diff --git a/rocketpool/api/api.go b/rocketpool/api/api.go index 789c1a5b1..778f64ec1 100644 --- a/rocketpool/api/api.go +++ b/rocketpool/api/api.go @@ -1,113 +1 @@ package api - -import ( - "net/http" - - "github.com/ethereum/go-ethereum/common" - "github.com/rocket-pool/smartnode/rocketpool/api/debug" - "github.com/rocket-pool/smartnode/rocketpool/api/megapool" - "github.com/rocket-pool/smartnode/rocketpool/api/pdao" - "github.com/rocket-pool/smartnode/rocketpool/api/security" - "github.com/urfave/cli" - - "github.com/rocket-pool/smartnode/bindings/utils" - "github.com/rocket-pool/smartnode/rocketpool/api/auction" - "github.com/rocket-pool/smartnode/rocketpool/api/minipool" - "github.com/rocket-pool/smartnode/rocketpool/api/network" - "github.com/rocket-pool/smartnode/rocketpool/api/node" - "github.com/rocket-pool/smartnode/rocketpool/api/odao" - "github.com/rocket-pool/smartnode/rocketpool/api/queue" - apiservice "github.com/rocket-pool/smartnode/rocketpool/api/service" - "github.com/rocket-pool/smartnode/rocketpool/api/wallet" - "github.com/rocket-pool/smartnode/shared/services" - apitypes "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -const ( - MaxConcurrentEth1Requests = 200 -) - -// Waits for an auction transaction -func waitForTransaction(c *cli.Context, hash common.Hash) (*apitypes.APIResponse, error) { - - rp, err := services.GetRocketPool(c) - if err != nil { - return nil, err - } - - // Response - response := apitypes.APIResponse{} - _, err = utils.WaitForTransaction(rp.Client, hash) - if err != nil { - return nil, err - } - - // Return response - return &response, nil - -} - -// Register commands -func RegisterCommands(app *cli.App, name string, aliases []string) { - - // CLI command - command := cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Run Rocket Pool API commands", - Subcommands: []cli.Command{}, - } - - // Don't show help message for api errors because of JSON serialisation - command.OnUsageError = func(context *cli.Context, err error, isSubcommand bool) error { - return err - } - - // Register subcommands - auction.RegisterSubcommands(&command, "auction", []string{"a"}) - megapool.RegisterSubcommands(&command, "megapool", []string{"g"}) - minipool.RegisterSubcommands(&command, "minipool", []string{"m"}) - megapool.RegisterSubcommands(&command, "megapool", []string{"g"}) - network.RegisterSubcommands(&command, "network", []string{"e"}) - node.RegisterSubcommands(&command, "node", []string{"n"}) - odao.RegisterSubcommands(&command, "odao", []string{"o"}) - pdao.RegisterSubcommands(&command, "pdao", []string{"p"}) - queue.RegisterSubcommands(&command, "queue", []string{"q"}) - security.RegisterSubcommands(&command, "security", []string{"c"}) - apiservice.RegisterSubcommands(&command, "service", []string{"s"}) - wallet.RegisterSubcommands(&command, "wallet", []string{"w"}) - debug.RegisterSubcommands(&command, "debug", []string{"d"}) - - // Append a general wait-for-transaction command to support async operations - command.Subcommands = append(command.Subcommands, cli.Command{ - Name: "wait", - Aliases: []string{"t"}, - Usage: "Wait for a transaction to complete", - UsageText: "rocketpool api wait tx-hash", - Action: func(c *cli.Context) error { - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - hash, err := cliutils.ValidateTxHash("tx-hash", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(waitForTransaction(c, hash)) - return nil - }, - }) - - // Register CLI command - app.Commands = append(app.Commands, command) - - // The daemon makes a large number of concurrent RPC requests to the Eth1 client - // The HTTP transport is set to cache connections for future re-use equal to the maximum expected number of concurrent requests - // This prevents issues related to memory consumption and address allowance from repeatedly opening and closing connections - http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = MaxConcurrentEth1Requests - -} diff --git a/rocketpool/api/auction/commands.go b/rocketpool/api/auction/commands.go deleted file mode 100644 index ef2e11ca4..000000000 --- a/rocketpool/api/auction/commands.go +++ /dev/null @@ -1,233 +0,0 @@ -package auction - -import ( - "github.com/urfave/cli" - - "github.com/rocket-pool/smartnode/shared/utils/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Subcommands = append(command.Subcommands, cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage Rocket Pool RPL auctions", - Subcommands: []cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get RPL auction status", - UsageText: "rocketpool api auction status", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getStatus(c)) - return nil - - }, - }, - - { - Name: "lots", - Aliases: []string{"l"}, - Usage: "Get RPL lots for auction", - UsageText: "rocketpool api auction lots", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getLots(c)) - return nil - - }, - }, - - { - Name: "can-create-lot", - Usage: "Check whether the node can create a new lot", - UsageText: "rocketpool api auction can-create-lot", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canCreateLot(c)) - return nil - - }, - }, - { - Name: "create-lot", - Aliases: []string{"t"}, - Usage: "Create a new lot", - UsageText: "rocketpool api auction create-lot", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(createLot(c)) - return nil - - }, - }, - - { - Name: "can-bid-lot", - Usage: "Check whether the node can bid on a lot", - UsageText: "rocketpool api auction can-bid-lot lot-id amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - lotIndex, err := cliutils.ValidateUint("lot ID", c.Args().Get(0)) - if err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("bid amount", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canBidOnLot(c, lotIndex, amountWei)) - return nil - - }, - }, - { - Name: "bid-lot", - Aliases: []string{"b"}, - Usage: "Bid on a lot", - UsageText: "rocketpool api auction bid-lot lot-id amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - lotIndex, err := cliutils.ValidateUint("lot ID", c.Args().Get(0)) - if err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("bid amount", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(bidOnLot(c, lotIndex, amountWei)) - return nil - - }, - }, - - { - Name: "can-claim-lot", - Usage: "Check whether the node can claim RPL from a lot", - UsageText: "rocketpool api auction can-claim-lot lot-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - lotIndex, err := cliutils.ValidateUint("lot ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canClaimFromLot(c, lotIndex)) - return nil - - }, - }, - { - Name: "claim-lot", - Aliases: []string{"c"}, - Usage: "Claim RPL from a lot", - UsageText: "rocketpool api auction claim-lot lot-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - lotIndex, err := cliutils.ValidateUint("lot ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(claimFromLot(c, lotIndex)) - return nil - - }, - }, - - { - Name: "can-recover-lot", - Usage: "Check whether the node can recover unclaimed RPL from a lot", - UsageText: "rocketpool api auction can-recover-lot lot-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - lotIndex, err := cliutils.ValidateUint("lot ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canRecoverRplFromLot(c, lotIndex)) - return nil - - }, - }, - { - Name: "recover-lot", - Aliases: []string{"r"}, - Usage: "Recover unclaimed RPL from a lot (returning it to the auction contract)", - UsageText: "rocketpool api auction recover-lot lot-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - lotIndex, err := cliutils.ValidateUint("lot ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(recoverRplFromLot(c, lotIndex)) - return nil - - }, - }, - }, - }) -} diff --git a/rocketpool/api/auction/routes.go b/rocketpool/api/auction/routes.go new file mode 100644 index 000000000..4e4139b15 --- /dev/null +++ b/rocketpool/api/auction/routes.go @@ -0,0 +1,119 @@ +package auction + +import ( + "fmt" + "math/big" + "net/http" + "strconv" + + "github.com/urfave/cli" + + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +// RegisterRoutes registers the auction module's HTTP routes onto mux. +func RegisterRoutes(mux *http.ServeMux, c *cli.Context) { + mux.HandleFunc("/api/auction/status", func(w http.ResponseWriter, r *http.Request) { + resp, err := getStatus(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/auction/lots", func(w http.ResponseWriter, r *http.Request) { + resp, err := getLots(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/auction/can-create-lot", func(w http.ResponseWriter, r *http.Request) { + resp, err := canCreateLot(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/auction/create-lot", func(w http.ResponseWriter, r *http.Request) { + resp, err := createLot(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/auction/can-bid-lot", func(w http.ResponseWriter, r *http.Request) { + lotIndex, amountWei, err := parseLotIndexAndAmount(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canBidOnLot(c, lotIndex, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/auction/bid-lot", func(w http.ResponseWriter, r *http.Request) { + lotIndex, amountWei, err := parseLotIndexAndAmount(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := bidOnLot(c, lotIndex, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/auction/can-claim-lot", func(w http.ResponseWriter, r *http.Request) { + lotIndex, err := parseLotIndex(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canClaimFromLot(c, lotIndex) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/auction/claim-lot", func(w http.ResponseWriter, r *http.Request) { + lotIndex, err := parseLotIndex(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := claimFromLot(c, lotIndex) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/auction/can-recover-lot", func(w http.ResponseWriter, r *http.Request) { + lotIndex, err := parseLotIndex(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canRecoverRplFromLot(c, lotIndex) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/auction/recover-lot", func(w http.ResponseWriter, r *http.Request) { + lotIndex, err := parseLotIndex(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := recoverRplFromLot(c, lotIndex) + apiutils.WriteResponse(w, resp, err) + }) +} + +func parseLotIndex(r *http.Request) (uint64, error) { + raw := r.URL.Query().Get("lotIndex") + if raw == "" { + raw = r.FormValue("lotIndex") + } + return strconv.ParseUint(raw, 10, 64) +} + +func parseLotIndexAndAmount(r *http.Request) (uint64, *big.Int, error) { + lotIndex, err := parseLotIndex(r) + if err != nil { + return 0, nil, err + } + raw := r.URL.Query().Get("amountWei") + if raw == "" { + raw = r.FormValue("amountWei") + } + amountWei, ok := new(big.Int).SetString(raw, 10) + if !ok { + return 0, nil, fmt.Errorf("invalid amountWei: %s", raw) + } + return lotIndex, amountWei, nil +} diff --git a/rocketpool/api/debug/commands.go b/rocketpool/api/debug/commands.go deleted file mode 100644 index 66adc64dc..000000000 --- a/rocketpool/api/debug/commands.go +++ /dev/null @@ -1,99 +0,0 @@ -package debug - -import ( - "fmt" - - "github.com/urfave/cli" - - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Subcommands = append(command.Subcommands, cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Debugging and troubleshooting commands", - Subcommands: []cli.Command{ - - { - Name: "export-validators", - Aliases: []string{"x"}, - Usage: "Exports a TSV file of validators", - UsageText: "rocketpool api debug export-validators", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Export TSV of validators - if err := ExportValidators(c); err != nil { - fmt.Printf("An error occurred: %s\n", err) - } - return nil - - }, - }, - { - Name: "get-beacon-state", - Aliases: []string{"b"}, - Usage: "Returns the beacon state for a given slot number", - UsageText: "rocketpool api debug get-beacon-state slot-number validator-index", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - - slotNumber, err := cliutils.ValidatePositiveUint("slot number", c.Args().Get(0)) - if err != nil { - return err - } - - validatorIndex, err := cliutils.ValidatePositiveUint("validator index", c.Args().Get(1)) - if err != nil { - return err - } - - if err := getBeaconStateForSlot(c, slotNumber, validatorIndex); err != nil { - fmt.Printf("An error occurred: %s\n", err) - } - return nil - - }, - }, - { - Name: "get-withdrawal-proof", - Aliases: []string{"w"}, - Usage: "Returns a withdrawal proof for a given validator index and given slot, for the withdrawal most recent to that slot", - UsageText: "rocketpool api debug get-withdrawal-proof slot-number validator-index", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - - slotNumber, err := cliutils.ValidatePositiveUint("slot number", c.Args().Get(0)) - if err != nil { - return err - } - - validatorIndex, err := cliutils.ValidatePositiveUint("validator index", c.Args().Get(1)) - if err != nil { - return err - } - - if err := getWithdrawalProofForSlot(c, slotNumber, validatorIndex); err != nil { - fmt.Printf("An error occurred: %s\n", err) - } - return nil - - }, - }, - }, - }) -} diff --git a/rocketpool/api/megapool/commands.go b/rocketpool/api/megapool/commands.go deleted file mode 100644 index e87ed778b..000000000 --- a/rocketpool/api/megapool/commands.go +++ /dev/null @@ -1,718 +0,0 @@ -package megapool - -import ( - "github.com/urfave/cli" - - "github.com/rocket-pool/smartnode/shared/utils/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Subcommands = append(command.Subcommands, cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the node's megapool", - Subcommands: []cli.Command{ - { - Name: "can-distribute-megapool", - Usage: "Check if can distribute megapool rewards", - UsageText: "rocketpool api node can-distribute-megapool", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canDistributeMegapool(c)) - return nil - - }, - }, - - { - Name: "distribute-megapool", - Usage: "Distribute megapool rewards", - UsageText: "rocketpool api node distribute-megapool", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(distributeMegapool(c)) - return nil - - }, - }, - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get the node's megapool status", - UsageText: "rocketpool api megapool status finalized-state", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Get finalized state - finalizedState, err := cliutils.ValidateBool("finalized-state", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(getStatus(c, finalizedState)) - return nil - - }, - }, - { - Name: "validator-map-and-balances", - Aliases: []string{"gvm"}, - Usage: "Get a map of the node's validators and beacon balances", - UsageText: "rocketpool api megapool validator-map-and-balances", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getValidatorMapAndBalances(c)) - return nil - - }, - }, - { - Name: "can-repay-debt", - Usage: "Check if we can repay the megapool debt", - UsageText: "rocketpool api megapool can-repay-debt amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Get amount - amount, err := cliutils.ValidatePositiveWeiAmount("amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canRepayDebt(c, amount)) - return nil - - }, - }, - { - Name: "repay-debt", - Aliases: []string{"rd"}, - Usage: "Repay the megapool debt", - UsageText: "rocketpool api megapool repay-debt amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Get amount - amount, err := cliutils.ValidatePositiveWeiAmount("amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(repayDebt(c, amount)) - return nil - - }, - }, - { - Name: "can-reduce-bond", - Usage: "Check if we can reduce the megapool bond", - UsageText: "rocketpool api megapool can-reduce-bond amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Get amount - amount, err := cliutils.ValidatePositiveWeiAmount("amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canReduceBond(c, amount)) - return nil - - }, - }, - { - Name: "reduce-bond", - Aliases: []string{"rb"}, - Usage: "Reduce the megapool bond", - UsageText: "rocketpool api megapool reduce-bond amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Get amount - amount, err := cliutils.ValidatePositiveWeiAmount("amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(reduceBond(c, amount)) - return nil - - }, - }, - { - Name: "can-claim-refund", - Usage: "Check if we can claim a megapool refund", - UsageText: "rocketpool api megapool can-claim-refund", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canClaimRefund(c)) - return nil - - }, - }, - { - Name: "claim-refund", - Aliases: []string{"cr"}, - Usage: "Claim a megapool refund", - UsageText: "rocketpool api megapool claim-refund", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(claimRefund(c)) - return nil - - }, - }, - { - Name: "can-stake", - Usage: "Check if we can stake a megapool validator", - UsageText: "rocketpool api megapool can-stake validator-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Get the validatorId - validatorId, err := cliutils.ValidateUint("validatorId", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canStake(c, validatorId)) - return nil - - }, - }, - { - Name: "stake", - Aliases: []string{"st"}, - Usage: "Stake a megapool validator", - UsageText: "rocketpool api megapool stake validator-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Get validatorId - validatorId, err := cliutils.ValidateUint("validatorId", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(stake(c, validatorId)) - return nil - - }, - }, - { - Name: "can-exit-queue", - Usage: "Check whether the node can exit the megapool queue", - UsageText: "rocketpool api megapool can-exit-queue validator-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Check the validator-id - validatorId, err := cliutils.ValidateUint32("validatorId", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canExitQueue(c, validatorId)) - return nil - - }, - }, - { - Name: "exit-queue", - Usage: "Exit the megapool queue", - UsageText: "rocketpool api megapool exit-queue validator-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Check the validatorId - validatorId, err := cliutils.ValidateUint32("validatorId", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(exitQueue(c, validatorId)) - return nil - - }, - }, - { - Name: "can-dissolve-validator", - Usage: "Check if we can dissolve a megapool validator", - UsageText: "rocketpool api megapool can-dissolve-validator validator-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Get the validatorId - validatorId, err := cliutils.ValidateUint32("validatorId", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canDissolveValidator(c, validatorId)) - return nil - - }, - }, - { - Name: "dissolve-validator", - Aliases: []string{"dv"}, - Usage: "Dissolve a megapool validator", - UsageText: "rocketpool api megapool dissolve-validator validator-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Get validatorId - validatorId, err := cliutils.ValidateUint32("validatorId", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(dissolveValidator(c, validatorId)) - return nil - - }, - }, - { - Name: "can-exit-validator", - Usage: "Check if we can exit a megapool validator", - UsageText: "rocketpool api megapool can-exit-validator validator-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Get the validatorId - validatorId, err := cliutils.ValidateUint32("validatorId", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canExitValidator(c, validatorId)) - return nil - - }, - }, - { - Name: "exit-validator", - Aliases: []string{"ev"}, - Usage: "Exit a megapool validator", - UsageText: "rocketpool api megapool exit-validator validator-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Get validatorId - validatorId, err := cliutils.ValidateUint32("validatorId", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(exitValidator(c, validatorId)) - return nil - - }, - }, - { - Name: "can-notify-validator-exit", - Usage: "Check if we can notify the exit of a megapool validator", - UsageText: "rocketpool api megapool can-notify-validator-exit validator-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Get the validatorId - validatorId, err := cliutils.ValidateUint32("validatorId", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canNotifyValidatorExit(c, validatorId)) - return nil - - }, - }, - { - Name: "notify-validator-exit", - Aliases: []string{"ev"}, - Usage: "Notify a megapool validator exit", - UsageText: "rocketpool api megapool notify-validator-exit validator-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Get validatorId - validatorId, err := cliutils.ValidateUint32("validatorId", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(notifyValidatorExit(c, validatorId)) - return nil - - }, - }, - { - Name: "can-notify-final-balance", - Usage: "Check if we can notify the final balance of a megapool validator", - UsageText: "rocketpool api megapool can-notify-final-balance validator-id slot", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - - // Get the validatorId - validatorId, err := cliutils.ValidateUint32("validatorId", c.Args().Get(0)) - if err != nil { - return err - } - - // Get slot - slot, err := cliutils.ValidateUint("slot", c.Args().Get(1)) - if err != nil { - return err - } - // Run - api.PrintResponse(canNotifyFinalBalance(c, validatorId, slot)) - return nil - - }, - }, - { - Name: "notify-final-balance", - Aliases: []string{"ev"}, - Usage: "Notify a megapool validator final balance", - UsageText: "rocketpool api megapool notify-final-balance validator-id slot", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - - // Get validatorId - validatorId, err := cliutils.ValidateUint32("validatorId", c.Args().Get(0)) - if err != nil { - return err - } - - // Get slot - slot, err := cliutils.ValidateUint("slot", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(notifyFinalBalance(c, validatorId, slot)) - return nil - - }, - }, - { - Name: "get-use-latest-delegate", - Usage: "Gets the current setting of the 'always use latest delegate' toggle", - UsageText: "rocketpool api megapool get-use-latest-delegate megapool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - megapoolAddress, err := cliutils.ValidateAddress("megapool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(getUseLatestDelegate(c, megapoolAddress)) - return nil - - }, - }, - { - Name: "can-set-use-latest-delegate", - Usage: "Check whether the 'always use latest delegate' toggle can be set", - UsageText: "rocketpool api megapool can-set-use-latest-delegate megapool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - megapoolAddress, err := cliutils.ValidateAddress("megapool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canSetUseLatestDelegate(c, megapoolAddress)) - return nil - - }, - }, - { - Name: "set-use-latest-delegate", - Usage: "Set to ignore the megapool's current delegate, and always use the latest delegate instead", - UsageText: "rocketpool api megapool set-use-latest-delegate", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - megapoolAddress, err := cliutils.ValidateAddress("megapool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(setUseLatestDelegate(c, megapoolAddress)) - return nil - - }, - }, - { - Name: "get-delegate", - Usage: "Gets the address of the current delegate contract used by the megapool", - UsageText: "rocketpool api megapool get-delegate megapool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - megapoolAddress, err := cliutils.ValidateAddress("megapool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(getDelegate(c, megapoolAddress)) - return nil - - }, - }, - { - Name: "get-effective-delegate", - Usage: "Gets the address of the effective delegate contract used by the megapool, which takes the UseLatestDelegate setting into account", - UsageText: "rocketpool api megapool get-effective-delegate megapool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - megapoolAddress, err := cliutils.ValidateAddress("megapool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(getEffectiveDelegate(c, megapoolAddress)) - return nil - - }, - }, - { - Name: "can-delegate-upgrade", - Usage: "Check whether the megapool delegate can be upgraded", - UsageText: "rocketpool api megapool can-delegate-upgrade megapool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - megapoolAddress, err := cliutils.ValidateAddress("megapool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canDelegateUpgrade(c, megapoolAddress)) - return nil - - }, - }, - { - Name: "delegate-upgrade", - Usage: "Upgrade this megapool to the latest network delegate contract", - UsageText: "rocketpool api megapool delegate-upgrade megapool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - megapoolAddress, err := cliutils.ValidateAddress("megapool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(delegateUpgrade(c, megapoolAddress)) - return nil - - }, - }, - { - Name: "calculate-rewards", - Usage: "Calculate the rewards split given an eth amount", - UsageText: "rocketpool api megapool calculate-rewards amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - // Get amount - amount, err := cliutils.ValidatePositiveWeiAmount("amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(calculateRewards(c, amount)) - return nil - - }, - }, - { - Name: "pending-rewards", - Usage: "Calculate the pending rewards split", - UsageText: "rocketpool api megapool pending-rewards", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - // Run - api.PrintResponse(calculatePendingRewards(c)) - return nil - - }, - }, - { - Name: "get-new-validator-bond-requirement", - Usage: "Get the bond amount required for the megapool's next validator", - UsageText: "rocketpool api megapool get-new-validator-bond-requirement", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - // Run - api.PrintResponse(getNewValidatorBondRequirement(c)) - return nil - - }, - }, - }, - }) -} diff --git a/rocketpool/api/megapool/routes.go b/rocketpool/api/megapool/routes.go new file mode 100644 index 000000000..457a5c4fe --- /dev/null +++ b/rocketpool/api/megapool/routes.go @@ -0,0 +1,328 @@ +package megapool + +import ( + "fmt" + "math/big" + "net/http" + "strconv" + + "github.com/ethereum/go-ethereum/common" + "github.com/urfave/cli" + + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +// RegisterRoutes registers the megapool module's HTTP routes onto mux. +func RegisterRoutes(mux *http.ServeMux, c *cli.Context) { + mux.HandleFunc("/api/megapool/status", func(w http.ResponseWriter, r *http.Request) { + finalizedState := r.URL.Query().Get("finalizedState") == "true" + resp, err := getStatus(c, finalizedState) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/validator-map-and-balances", func(w http.ResponseWriter, r *http.Request) { + resp, err := getValidatorMapAndBalances(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/can-claim-refund", func(w http.ResponseWriter, r *http.Request) { + resp, err := canClaimRefund(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/claim-refund", func(w http.ResponseWriter, r *http.Request) { + resp, err := claimRefund(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/can-repay-debt", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canRepayDebt(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/repay-debt", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := repayDebt(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/can-reduce-bond", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canReduceBond(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/reduce-bond", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := reduceBond(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/can-stake", func(w http.ResponseWriter, r *http.Request) { + validatorId, err := parseUint64(r, "validatorId") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canStake(c, validatorId) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/stake", func(w http.ResponseWriter, r *http.Request) { + validatorId, err := parseUint64(r, "validatorId") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := stake(c, validatorId) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/can-dissolve-validator", func(w http.ResponseWriter, r *http.Request) { + validatorId, err := parseUint32(r, "validatorId") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canDissolveValidator(c, validatorId) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/dissolve-validator", func(w http.ResponseWriter, r *http.Request) { + validatorId, err := parseUint32(r, "validatorId") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := dissolveValidator(c, validatorId) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/can-dissolve-with-proof", func(w http.ResponseWriter, r *http.Request) { + validatorId, err := parseUint32(r, "validatorId") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canDissolveWithProof(c, validatorId) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/dissolve-with-proof", func(w http.ResponseWriter, r *http.Request) { + validatorId, err := parseUint32(r, "validatorId") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := dissolveWithProof(c, validatorId) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/can-exit-validator", func(w http.ResponseWriter, r *http.Request) { + validatorId, err := parseUint32(r, "validatorId") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canExitValidator(c, validatorId) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/exit-validator", func(w http.ResponseWriter, r *http.Request) { + validatorId, err := parseUint32(r, "validatorId") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := exitValidator(c, validatorId) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/can-notify-validator-exit", func(w http.ResponseWriter, r *http.Request) { + validatorId, err := parseUint32(r, "validatorId") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canNotifyValidatorExit(c, validatorId) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/notify-validator-exit", func(w http.ResponseWriter, r *http.Request) { + validatorId, err := parseUint32(r, "validatorId") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := notifyValidatorExit(c, validatorId) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/can-notify-final-balance", func(w http.ResponseWriter, r *http.Request) { + validatorId, err := parseUint32(r, "validatorId") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + slot, err := parseUint64(r, "slot") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canNotifyFinalBalance(c, validatorId, slot) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/notify-final-balance", func(w http.ResponseWriter, r *http.Request) { + validatorId, err := parseUint32(r, "validatorId") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + slot, err := parseUint64(r, "slot") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := notifyFinalBalance(c, validatorId, slot) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/can-exit-queue", func(w http.ResponseWriter, r *http.Request) { + validatorIndex, err := parseUint32(r, "validatorIndex") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canExitQueue(c, validatorIndex) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/exit-queue", func(w http.ResponseWriter, r *http.Request) { + validatorIndex, err := parseUint32(r, "validatorIndex") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := exitQueue(c, validatorIndex) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/can-distribute", func(w http.ResponseWriter, r *http.Request) { + resp, err := canDistributeMegapool(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/distribute", func(w http.ResponseWriter, r *http.Request) { + resp, err := distributeMegapool(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/get-new-validator-bond-requirement", func(w http.ResponseWriter, r *http.Request) { + resp, err := getNewValidatorBondRequirement(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/pending-rewards", func(w http.ResponseWriter, r *http.Request) { + resp, err := calculatePendingRewards(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/calculate-rewards", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := calculateRewards(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/get-use-latest-delegate", func(w http.ResponseWriter, r *http.Request) { + address := common.HexToAddress(r.URL.Query().Get("address")) + resp, err := getUseLatestDelegate(c, address) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/can-delegate-upgrade", func(w http.ResponseWriter, r *http.Request) { + address := common.HexToAddress(r.URL.Query().Get("address")) + resp, err := canDelegateUpgrade(c, address) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/delegate-upgrade", func(w http.ResponseWriter, r *http.Request) { + address := common.HexToAddress(r.FormValue("address")) + resp, err := delegateUpgrade(c, address) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/can-set-use-latest-delegate", func(w http.ResponseWriter, r *http.Request) { + address := common.HexToAddress(r.URL.Query().Get("address")) + resp, err := canSetUseLatestDelegate(c, address) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/set-use-latest-delegate", func(w http.ResponseWriter, r *http.Request) { + address := common.HexToAddress(r.FormValue("address")) + resp, err := setUseLatestDelegate(c, address) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/get-delegate", func(w http.ResponseWriter, r *http.Request) { + address := common.HexToAddress(r.URL.Query().Get("address")) + resp, err := getDelegate(c, address) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/megapool/get-effective-delegate", func(w http.ResponseWriter, r *http.Request) { + address := common.HexToAddress(r.URL.Query().Get("address")) + resp, err := getEffectiveDelegate(c, address) + apiutils.WriteResponse(w, resp, err) + }) +} + +func parseUint64(r *http.Request, name string) (uint64, error) { + raw := r.URL.Query().Get(name) + if raw == "" { + raw = r.FormValue(name) + } + return strconv.ParseUint(raw, 10, 64) +} + +func parseUint32(r *http.Request, name string) (uint32, error) { + raw := r.URL.Query().Get(name) + if raw == "" { + raw = r.FormValue(name) + } + v, err := strconv.ParseUint(raw, 10, 32) + return uint32(v), err +} + +func parseBigInt(r *http.Request, name string) (*big.Int, error) { + raw := r.URL.Query().Get(name) + if raw == "" { + raw = r.FormValue(name) + } + v, ok := new(big.Int).SetString(raw, 10) + if !ok { + return nil, fmt.Errorf("invalid %s: %s", name, raw) + } + return v, nil +} diff --git a/rocketpool/api/minipool/commands.go b/rocketpool/api/minipool/commands.go deleted file mode 100644 index ea12090f1..000000000 --- a/rocketpool/api/minipool/commands.go +++ /dev/null @@ -1,704 +0,0 @@ -package minipool - -import ( - "github.com/urfave/cli" - - "github.com/rocket-pool/smartnode/shared/utils/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Subcommands = append(command.Subcommands, cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the node's minipools", - Subcommands: []cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get a list of the node's minipools", - UsageText: "rocketpool api minipool status", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getStatus(c)) - return nil - - }, - }, - - { - Name: "can-stake", - Usage: "Check whether the minipool is ready to be staked, moving from prelaunch to staking status", - UsageText: "rocketpool api minipool can-stake minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canStakeMinipool(c, minipoolAddress)) - return nil - - }, - }, - { - Name: "stake", - Aliases: []string{"t"}, - Usage: "Stake the minipool, moving it from prelaunch to staking status", - UsageText: "rocketpool api minipool stake minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(stakeMinipool(c, minipoolAddress)) - return nil - - }, - }, - - { - Name: "can-promote", - Usage: "Check whether a vacant minipool is ready to be promoted", - UsageText: "rocketpool api minipool can-promote minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canPromoteMinipool(c, minipoolAddress)) - return nil - - }, - }, - { - Name: "promote", - Usage: "Promote a vacant minipool", - UsageText: "rocketpool api minipool promote minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(promoteMinipool(c, minipoolAddress)) - return nil - - }, - }, - - { - Name: "can-refund", - Usage: "Check whether the node can refund ETH from the minipool", - UsageText: "rocketpool api minipool can-refund minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canRefundMinipool(c, minipoolAddress)) - return nil - - }, - }, - { - Name: "refund", - Aliases: []string{"r"}, - Usage: "Refund ETH belonging to the node from a minipool", - UsageText: "rocketpool api minipool refund minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(refundMinipool(c, minipoolAddress)) - return nil - - }, - }, - - { - Name: "can-dissolve", - Usage: "Check whether the minipool can be dissolved", - UsageText: "rocketpool api minipool can-dissolve minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canDissolveMinipool(c, minipoolAddress)) - return nil - - }, - }, - { - Name: "dissolve", - Aliases: []string{"d"}, - Usage: "Dissolve an initialized or prelaunch minipool", - UsageText: "rocketpool api minipool dissolve minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(dissolveMinipool(c, minipoolAddress)) - return nil - - }, - }, - - { - Name: "can-exit", - Usage: "Check whether the minipool can be exited from the beacon chain", - UsageText: "rocketpool api minipool can-exit minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canExitMinipool(c, minipoolAddress)) - return nil - - }, - }, - { - Name: "exit", - Aliases: []string{"e"}, - Usage: "Exit a staking minipool from the beacon chain", - UsageText: "rocketpool api minipool exit minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(exitMinipool(c, minipoolAddress)) - return nil - - }, - }, - - { - Name: "get-minipool-close-details-for-node", - Usage: "Check all of the node's minipools for closure eligibility, and return the details of the closeable ones", - UsageText: "rocketpool api minipool get-minipool-close-details-for-node", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getMinipoolCloseDetailsForNode(c)) - return nil - - }, - }, - { - Name: "close", - Aliases: []string{"c"}, - Usage: "Withdraw balance from a dissolved minipool and close it", - UsageText: "rocketpool api minipool close minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(closeMinipool(c, minipoolAddress)) - return nil - - }, - }, - - { - Name: "can-delegate-upgrade", - Usage: "Check whether the minipool delegate can be upgraded", - UsageText: "rocketpool api minipool can-delegate-upgrade minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canDelegateUpgrade(c, minipoolAddress)) - return nil - - }, - }, - { - Name: "delegate-upgrade", - Usage: "Upgrade this minipool to the latest network delegate contract", - UsageText: "rocketpool api minipool delegate-upgrade minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(delegateUpgrade(c, minipoolAddress)) - return nil - - }, - }, - - { - Name: "get-use-latest-delegate", - Usage: "Gets the current setting of the 'always use latest delegate' toggle", - UsageText: "rocketpool api minipool get-use-latest-delegate minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(getUseLatestDelegate(c, minipoolAddress)) - return nil - - }, - }, - - { - Name: "get-delegate", - Usage: "Gets the address of the current delegate contract used by the minipool", - UsageText: "rocketpool api minipool get-delegate minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(getDelegate(c, minipoolAddress)) - return nil - - }, - }, - - { - Name: "get-effective-delegate", - Usage: "Gets the address of the effective delegate contract used by the minipool, which takes the UseLatestDelegate setting into account", - UsageText: "rocketpool api minipool get-effective-delegate minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(getEffectiveDelegate(c, minipoolAddress)) - return nil - - }, - }, - - { - Name: "get-vanity-artifacts", - Aliases: []string{"v"}, - Usage: "Gets the data necessary to search for vanity minipool addresses", - UsageText: "rocketpool api minipool get-vanity-artifacts deposit node-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - depositAmount, err := cliutils.ValidatePositiveWeiAmount("deposit amount", c.Args().Get(0)) - if err != nil { - return err - } - nodeAddressStr := c.Args().Get(1) - - // Run - api.PrintResponse(getVanityArtifacts(c, depositAmount, nodeAddressStr)) - return nil - - }, - }, - - { - Name: "can-begin-reduce-bond-amount", - Usage: "Check whether the minipool can begin the bond reduction process", - UsageText: "rocketpool api minipool can-begin-reduce-bond-amount minipool-address new-bond-amount-wei", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - newBondAmountWei, err := cliutils.ValidateWeiAmount("new bond amount", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canBeginReduceBondAmount(c, minipoolAddress, newBondAmountWei)) - return nil - - }, - }, - { - Name: "begin-reduce-bond-amount", - Usage: "Begin the bond reduction process for a minipool", - UsageText: "rocketpool api minipool begin-reduce-bond-amount minipool-address new-bond-amount-wei", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - newBondAmountWei, err := cliutils.ValidateWeiAmount("new bond amount", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(beginReduceBondAmount(c, minipoolAddress, newBondAmountWei)) - return nil - - }, - }, - - { - Name: "can-reduce-bond-amount", - Usage: "Check if a minipool's bond can be reduced", - UsageText: "rocketpool api minipool can-reduce-bond-amount minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canReduceBondAmount(c, minipoolAddress)) - return nil - - }, - }, - { - Name: "reduce-bond-amount", - Usage: "Reduce a minipool's bond", - UsageText: "rocketpool api minipool reduce-bond-amount minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(reduceBondAmount(c, minipoolAddress)) - return nil - - }, - }, - - { - Name: "get-distribute-balance-details", - Usage: "Get the balance distribution details for all of the node's minipools", - UsageText: "rocketpool api minipool get-distribute-balance-details", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getDistributeBalanceDetails(c)) - return nil - - }, - }, - { - Name: "distribute-balance", - Usage: "Distribute a minipool's ETH balance", - UsageText: "rocketpool api minipool distribute-balance minipool-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(distributeBalance(c, minipoolAddress)) - return nil - - }, - }, - - { - Name: "import-key", - Usage: "Import a validator private key for a vacant minipool", - UsageText: "rocketpool api minipool import-key minipool-address mnemonic", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - mnemonic, err := cliutils.ValidateWalletMnemonic("mnemonic", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(importKey(c, minipoolAddress, mnemonic)) - return nil - - }, - }, - - { - Name: "can-change-withdrawal-creds", - Usage: "Check whether a solo validator's withdrawal credentials can be changed to a minipool address", - UsageText: "rocketpool api minipool can-change-withdrawal-creds minipool-address mnemonic", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - mnemonic, err := cliutils.ValidateWalletMnemonic("mnemonic", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canChangeWithdrawalCreds(c, minipoolAddress, mnemonic)) - return nil - - }, - }, - { - Name: "change-withdrawal-creds", - Usage: "Change a solo validator's withdrawal credentials to a minipool address", - UsageText: "rocketpool api minipool change-withdrawal-creds minipool-address mnemonic", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - mnemonic, err := cliutils.ValidateWalletMnemonic("mnemonic", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(changeWithdrawalCreds(c, minipoolAddress, mnemonic)) - return nil - - }, - }, - - { - Name: "get-rescue-dissolved-details-for-node", - Usage: "Check all of the node's minipools for rescue eligibility, and return the details of the rescuable ones", - UsageText: "rocketpool api minipool get-rescue-dissolved-details-for-node", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getMinipoolRescueDissolvedDetailsForNode(c)) - return nil - - }, - }, - - { - Name: "rescue-dissolved", - Usage: "Rescue a dissolved minipool by depositing ETH for it to the Beacon deposit contract", - UsageText: "rocketpool api minipool rescue-dissolved minipool-address deposit-amount submit", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - minipoolAddress, err := cliutils.ValidateAddress("minipool address", c.Args().Get(0)) - if err != nil { - return err - } - depositAmount, err := cliutils.ValidateBigInt("deposit amount", c.Args().Get(1)) - if err != nil { - return err - } - submit, err := cliutils.ValidateBool("submit", c.Args().Get(2)) - if err != nil { - return err - } - - // Run - api.PrintResponse(rescueDissolvedMinipool(c, minipoolAddress, depositAmount, submit)) - return nil - - }, - }, - { - Name: "get-bond-reduction-enabled", - Usage: "Check whether bond reduction is enabled", - UsageText: "rocketpool api minipool get-bond-reduction-enabled", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getBondReductionEnabled(c)) - return nil - }, - }, - }, - }) -} diff --git a/rocketpool/api/minipool/routes.go b/rocketpool/api/minipool/routes.go new file mode 100644 index 000000000..8edd3217a --- /dev/null +++ b/rocketpool/api/minipool/routes.go @@ -0,0 +1,365 @@ +package minipool + +import ( + "fmt" + "math/big" + "net/http" + + "github.com/ethereum/go-ethereum/common" + "github.com/urfave/cli" + + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +// RegisterRoutes registers the minipool module's HTTP routes onto mux. +func RegisterRoutes(mux *http.ServeMux, c *cli.Context) { + mux.HandleFunc("/api/minipool/status", func(w http.ResponseWriter, r *http.Request) { + resp, err := getStatus(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/can-refund", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canRefundMinipool(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/refund", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := refundMinipool(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/can-stake", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canStakeMinipool(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/stake", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := stakeMinipool(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/can-promote", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canPromoteMinipool(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/promote", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := promoteMinipool(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/can-dissolve", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canDissolveMinipool(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/dissolve", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := dissolveMinipool(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/can-exit", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canExitMinipool(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/exit", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := exitMinipool(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/get-minipool-close-details-for-node", func(w http.ResponseWriter, r *http.Request) { + resp, err := getMinipoolCloseDetailsForNode(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/close", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := closeMinipool(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/can-delegate-upgrade", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canDelegateUpgrade(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/delegate-upgrade", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := delegateUpgrade(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/can-set-use-latest-delegate", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canSetUseLatestDelegate(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/set-use-latest-delegate", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := setUseLatestDelegate(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/get-use-latest-delegate", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := getUseLatestDelegate(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/get-delegate", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := getDelegate(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/get-effective-delegate", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := getEffectiveDelegate(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/get-previous-delegate", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := getPreviousDelegate(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/get-vanity-artifacts", func(w http.ResponseWriter, r *http.Request) { + depositAmountStr := r.URL.Query().Get("depositAmount") + depositAmount, ok := new(big.Int).SetString(depositAmountStr, 10) + if !ok { + apiutils.WriteErrorResponse(w, fmt.Errorf("invalid depositAmount: %s", depositAmountStr)) + return + } + nodeAddressStr := r.URL.Query().Get("nodeAddress") + resp, err := getVanityArtifacts(c, depositAmount, nodeAddressStr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/can-begin-reduce-bond-amount", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + amountStr := r.URL.Query().Get("newBondAmountWei") + amount, ok := new(big.Int).SetString(amountStr, 10) + if !ok { + apiutils.WriteErrorResponse(w, fmt.Errorf("invalid newBondAmountWei: %s", amountStr)) + return + } + resp, err := canBeginReduceBondAmount(c, addr, amount) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/begin-reduce-bond-amount", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + amountStr := r.FormValue("newBondAmountWei") + amount, ok := new(big.Int).SetString(amountStr, 10) + if !ok { + apiutils.WriteErrorResponse(w, fmt.Errorf("invalid newBondAmountWei: %s", amountStr)) + return + } + resp, err := beginReduceBondAmount(c, addr, amount) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/can-reduce-bond-amount", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canReduceBondAmount(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/reduce-bond-amount", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := reduceBondAmount(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/get-distribute-balance-details", func(w http.ResponseWriter, r *http.Request) { + resp, err := getDistributeBalanceDetails(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/distribute-balance", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := distributeBalance(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/import-key", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + mnemonic := r.FormValue("mnemonic") + resp, err := importKey(c, addr, mnemonic) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/can-change-withdrawal-creds", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + mnemonic := r.URL.Query().Get("mnemonic") + resp, err := canChangeWithdrawalCreds(c, addr, mnemonic) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/change-withdrawal-creds", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + mnemonic := r.FormValue("mnemonic") + resp, err := changeWithdrawalCreds(c, addr, mnemonic) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/get-rescue-dissolved-details-for-node", func(w http.ResponseWriter, r *http.Request) { + resp, err := getMinipoolRescueDissolvedDetailsForNode(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/rescue-dissolved", func(w http.ResponseWriter, r *http.Request) { + addr, err := parseAddress(r, "address") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + amountStr := r.FormValue("amount") + amount, ok := new(big.Int).SetString(amountStr, 10) + if !ok { + apiutils.WriteErrorResponse(w, fmt.Errorf("invalid amount: %s", amountStr)) + return + } + submit := r.FormValue("submit") == "true" + resp, err := rescueDissolvedMinipool(c, addr, amount, submit) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/minipool/get-bond-reduction-enabled", func(w http.ResponseWriter, r *http.Request) { + resp, err := getBondReductionEnabled(c) + apiutils.WriteResponse(w, resp, err) + }) +} + +func parseAddress(r *http.Request, name string) (common.Address, error) { + raw := r.URL.Query().Get(name) + if raw == "" { + raw = r.FormValue(name) + } + if raw == "" { + return common.Address{}, fmt.Errorf("missing required parameter: %s", name) + } + return common.HexToAddress(raw), nil +} diff --git a/rocketpool/api/network/commands.go b/rocketpool/api/network/commands.go deleted file mode 100644 index 4fefa5f4f..000000000 --- a/rocketpool/api/network/commands.go +++ /dev/null @@ -1,201 +0,0 @@ -package network - -import ( - "github.com/urfave/cli" - - "github.com/rocket-pool/smartnode/shared/utils/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Subcommands = append(command.Subcommands, cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage Rocket Pool network parameters", - Subcommands: []cli.Command{ - - { - Name: "node-fee", - Aliases: []string{"f"}, - Usage: "Get the current network node commission rate", - UsageText: "rocketpool api network node-fee", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getNodeFee(c)) - return nil - - }, - }, - - { - Name: "rpl-price", - Aliases: []string{"p"}, - Usage: "Get the current network RPL price in ETH", - UsageText: "rocketpool api network rpl-price", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getRplPrice(c)) - return nil - - }, - }, - - { - Name: "stats", - Aliases: []string{"s"}, - Usage: "Get stats about the Rocket Pool network and its tokens", - UsageText: "rocketpool api network stats", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getStats(c)) - return nil - - }, - }, - - { - Name: "timezone-map", - Aliases: []string{"t"}, - Usage: "Get the table of node operators by timezone", - UsageText: "rocketpool api network stats", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getTimezones(c)) - return nil - - }, - }, - - { - Name: "can-generate-rewards-tree", - Usage: "Check if the rewards tree for the provided interval can be generated", - UsageText: "rocketpool api network can-generate-rewards-tree index", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - index, err := cliutils.ValidateUint("index", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canGenerateRewardsTree(c, index)) - return nil - - }, - }, - - { - Name: "generate-rewards-tree", - Usage: "Set a request marker for the watchtower to generate the rewards tree for the given interval", - UsageText: "rocketpool api network generate-rewards-tree index", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - index, err := cliutils.ValidateUint("index", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(generateRewardsTree(c, index)) - return nil - - }, - }, - - { - Name: "dao-proposals", - Aliases: []string{"d"}, - Usage: "Get the currently active DAO proposals", - UsageText: "rocketpool api network dao-proposals", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getActiveDAOProposals(c)) - return nil - - }, - }, - - { - Name: "download-rewards-file", - Aliases: []string{"drf"}, - Usage: "Download a rewards info file from IPFS for the given interval", - UsageText: "rocketpool api service download-rewards-file interval", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - interval, err := cliutils.ValidateUint("interval", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(downloadRewardsFile(c, interval)) - return nil - - }, - }, - { - Name: "latest-delegate", - Usage: "Get the address of the latest minipool delegate contract.", - UsageText: "rocketpool api network latest-delegate", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getLatestDelegate(c)) - return nil - - }, - }, - }, - }) -} diff --git a/rocketpool/api/network/routes.go b/rocketpool/api/network/routes.go new file mode 100644 index 000000000..8ec082ab3 --- /dev/null +++ b/rocketpool/api/network/routes.go @@ -0,0 +1,81 @@ +package network + +import ( + "net/http" + "strconv" + + "github.com/urfave/cli" + + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +// RegisterRoutes registers the network module's HTTP routes onto mux. +func RegisterRoutes(mux *http.ServeMux, c *cli.Context) { + mux.HandleFunc("/api/network/node-fee", func(w http.ResponseWriter, r *http.Request) { + resp, err := getNodeFee(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/network/rpl-price", func(w http.ResponseWriter, r *http.Request) { + resp, err := getRplPrice(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/network/stats", func(w http.ResponseWriter, r *http.Request) { + resp, err := getStats(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/network/timezone-map", func(w http.ResponseWriter, r *http.Request) { + resp, err := getTimezones(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/network/can-generate-rewards-tree", func(w http.ResponseWriter, r *http.Request) { + index, err := parseUint64Param(r, "index") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canGenerateRewardsTree(c, index) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/network/generate-rewards-tree", func(w http.ResponseWriter, r *http.Request) { + index, err := parseUint64Param(r, "index") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := generateRewardsTree(c, index) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/network/dao-proposals", func(w http.ResponseWriter, r *http.Request) { + resp, err := getActiveDAOProposals(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/network/download-rewards-file", func(w http.ResponseWriter, r *http.Request) { + interval, err := parseUint64Param(r, "interval") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := downloadRewardsFile(c, interval) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/network/latest-delegate", func(w http.ResponseWriter, r *http.Request) { + resp, err := getLatestDelegate(c) + apiutils.WriteResponse(w, resp, err) + }) +} + +func parseUint64Param(r *http.Request, name string) (uint64, error) { + raw := r.URL.Query().Get(name) + if raw == "" { + raw = r.FormValue(name) + } + return strconv.ParseUint(raw, 10, 64) +} diff --git a/rocketpool/api/node/commands.go b/rocketpool/api/node/commands.go deleted file mode 100644 index 111907a8f..000000000 --- a/rocketpool/api/node/commands.go +++ /dev/null @@ -1,1885 +0,0 @@ -package node - -import ( - "github.com/urfave/cli" - - "github.com/rocket-pool/smartnode/shared/utils/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Subcommands = append(command.Subcommands, cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the node", - Subcommands: []cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get the node's status", - UsageText: "rocketpool api node status", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getStatus(c)) - return nil - - }, - }, - - { - Name: "alerts", - Aliases: []string{"al"}, - Usage: "Get active alerts from Alertmanager", - UsageText: "rocketpool api node alerts", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getAlerts(c)) - return nil - - }, - }, - - { - Name: "sync", - Aliases: []string{"y"}, - Usage: "Get the sync progress of the eth1 and eth2 clients", - UsageText: "rocketpool api node sync", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getSyncProgress(c)) - return nil - - }, - }, - - { - Name: "can-register", - Usage: "Check whether the node can be registered with Rocket Pool", - UsageText: "rocketpool api node can-register timezone-location", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - timezoneLocation, err := cliutils.ValidateTimezoneLocation("timezone location", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canRegisterNode(c, timezoneLocation)) - return nil - - }, - }, - { - Name: "register", - Aliases: []string{"r"}, - Usage: "Register the node with Rocket Pool", - UsageText: "rocketpool api node register timezone-location", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - timezoneLocation, err := cliutils.ValidateTimezoneLocation("timezone location", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(registerNode(c, timezoneLocation)) - return nil - - }, - }, - - { - Name: "can-set-primary-withdrawal-address", - Usage: "Checks if the node can set its primary withdrawal address", - UsageText: "rocketpool api node can-set-primary-withdrawal-address address confirm", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - withdrawalAddress, err := cliutils.ValidateAddress("withdrawal address", c.Args().Get(0)) - if err != nil { - return err - } - - confirm, err := cliutils.ValidateBool("confirm", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canSetPrimaryWithdrawalAddress(c, withdrawalAddress, confirm)) - return nil - - }, - }, - { - Name: "set-primary-withdrawal-address", - Usage: "Set the node's primary withdrawal address", - UsageText: "rocketpool api node set-primary-withdrawal-address address confirm", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - withdrawalAddress, err := cliutils.ValidateAddress("withdrawal address", c.Args().Get(0)) - if err != nil { - return err - } - - confirm, err := cliutils.ValidateBool("confirm", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(setPrimaryWithdrawalAddress(c, withdrawalAddress, confirm)) - return nil - - }, - }, - - { - Name: "can-confirm-primary-withdrawal-address", - Usage: "Checks if the node can confirm its primary withdrawal address", - UsageText: "rocketpool api node can-confirm-primary-withdrawal-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canConfirmPrimaryWithdrawalAddress(c)) - return nil - - }, - }, - { - Name: "confirm-primary-withdrawal-address", - Usage: "Confirms the node's primary withdrawal address if it was set back to the node address", - UsageText: "rocketpool api node confirm-primary-withdrawal-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(confirmPrimaryWithdrawalAddress(c)) - return nil - - }, - }, - - { - Name: "can-set-rpl-withdrawal-address", - Usage: "Checks if the node can set its RPL withdrawal address", - UsageText: "rocketpool api node can-set-rpl-withdrawal-address address confirm", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - withdrawalAddress, err := cliutils.ValidateAddress("withdrawal address", c.Args().Get(0)) - if err != nil { - return err - } - - confirm, err := cliutils.ValidateBool("confirm", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canSetRPLWithdrawalAddress(c, withdrawalAddress, confirm)) - return nil - - }, - }, - { - Name: "set-rpl-withdrawal-address", - Usage: "Set the node's RPL withdrawal address", - UsageText: "rocketpool api node set-rpl-withdrawal-address address confirm", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - withdrawalAddress, err := cliutils.ValidateAddress("withdrawal address", c.Args().Get(0)) - if err != nil { - return err - } - - confirm, err := cliutils.ValidateBool("confirm", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(setRPLWithdrawalAddress(c, withdrawalAddress, confirm)) - return nil - - }, - }, - - { - Name: "can-confirm-rpl-withdrawal-address", - Usage: "Checks if the node can confirm its RPL withdrawal address", - UsageText: "rocketpool api node can-confirm-rpl-withdrawal-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canConfirmRPLWithdrawalAddress(c)) - return nil - - }, - }, - { - Name: "confirm-rpl-withdrawal-address", - Usage: "Confirms the node's RPL withdrawal address if it was set back to the node address", - UsageText: "rocketpool api node confirm-rpl-withdrawal-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(confirmRPLWithdrawalAddress(c)) - return nil - - }, - }, - - { - Name: "can-set-timezone", - Usage: "Checks if the node can set its timezone location", - UsageText: "rocketpool api node can-set-timezone timezone-location", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - timezoneLocation, err := cliutils.ValidateTimezoneLocation("timezone location", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canSetTimezoneLocation(c, timezoneLocation)) - return nil - - }, - }, - { - Name: "set-timezone", - Aliases: []string{"t"}, - Usage: "Set the node's timezone location", - UsageText: "rocketpool api node set-timezone timezone-location", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - timezoneLocation, err := cliutils.ValidateTimezoneLocation("timezone location", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(setTimezoneLocation(c, timezoneLocation)) - return nil - - }, - }, - - { - Name: "can-swap-rpl", - Usage: "Check whether the node can swap old RPL for new RPL", - UsageText: "rocketpool api node can-swap-rpl amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("swap amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canNodeSwapRpl(c, amountWei)) - return nil - - }, - }, - { - Name: "swap-rpl-approve-rpl", - Aliases: []string{"p1"}, - Usage: "Approve fixed-supply RPL for swapping to new RPL", - UsageText: "rocketpool api node swap-rpl-approve-rpl amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("swap amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(approveFsRpl(c, amountWei)) - return nil - - }, - }, - { - Name: "wait-and-swap-rpl", - Aliases: []string{"p2"}, - Usage: "Swap old RPL for new RPL, waiting for the approval TX hash to be included in a block first", - UsageText: "rocketpool api node wait-and-swap-rpl amount tx-hash", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("swap amount", c.Args().Get(0)) - if err != nil { - return err - } - hash, err := cliutils.ValidateTxHash("swap amount", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(waitForApprovalAndSwapFsRpl(c, amountWei, hash)) - return nil - - }, - }, - { - Name: "get-swap-rpl-approval-gas", - Usage: "Estimate the gas cost of legacy RPL interaction approval", - UsageText: "rocketpool api node get-swap-rpl-approval-gas", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("approve amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(getSwapApprovalGas(c, amountWei)) - return nil - - }, - }, - { - Name: "swap-rpl-allowance", - Usage: "Get the node's legacy RPL allowance for new RPL contract", - UsageText: "rocketpool api node swap-allowance-rpl", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(allowanceFsRpl(c)) - return nil - - }, - }, - { - Name: "swap-rpl", - Aliases: []string{"p3"}, - Usage: "Swap old RPL for new RPL", - UsageText: "rocketpool api node swap-rpl amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("swap amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(swapRpl(c, amountWei)) - return nil - - }, - }, - - { - Name: "can-stake-rpl", - Usage: "Check whether the node can stake RPL", - UsageText: "rocketpool api node can-stake-rpl amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("stake amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canNodeStakeRpl(c, amountWei)) - return nil - - }, - }, - { - Name: "stake-rpl-approve-rpl", - Aliases: []string{"k1"}, - Usage: "Approve RPL for staking against the node", - UsageText: "rocketpool api node stake-rpl-approve-rpl amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("stake amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(approveRpl(c, amountWei)) - return nil - - }, - }, - { - Name: "wait-and-stake-rpl", - Aliases: []string{"k2"}, - Usage: "Stake RPL against the node, waiting for approval tx-hash to be included in a block first", - UsageText: "rocketpool api node wait-and-stake-rpl amount tx-hash", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("stake amount", c.Args().Get(0)) - if err != nil { - return err - } - hash, err := cliutils.ValidateTxHash("tx-hash", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(waitForApprovalAndStakeRpl(c, amountWei, hash)) - return nil - - }, - }, - { - Name: "get-stake-rpl-approval-gas", - Usage: "Estimate the gas cost of new RPL interaction approval", - UsageText: "rocketpool api node get-stake-rpl-approval-gas", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("approve amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(getStakeApprovalGas(c, amountWei)) - return nil - - }, - }, - { - Name: "stake-rpl-allowance", - Usage: "Get the node's RPL allowance for the staking contract", - UsageText: "rocketpool api node stake-allowance-rpl", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(allowanceRpl(c)) - return nil - - }, - }, - { - Name: "stake-rpl", - Aliases: []string{"k3"}, - Usage: "Stake RPL against the node", - UsageText: "rocketpool api node stake-rpl amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("stake amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(stakeRpl(c, amountWei)) - return nil - - }, - }, - { - Name: "can-set-rpl-locking-allowed", - Usage: "Check whether the node can set the RPL lock allowed status", - UsageText: "rocketpool api node can-set-rpl-locking-allowed caller allowed", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - allowedString := c.Args().Get(0) - allowed, err := cliutils.ValidateBool("allowed", allowedString) - if err != nil { - return err - } - - // Run - api.PrintResponse(canSetRplLockAllowed(c, allowed)) - return nil - - }, - }, - - { - Name: "set-rpl-locking-allowed", - Usage: "Sets the node RPL locking allowed status", - UsageText: "rocketpool api node set-rpl-locking-allowed caller allowed", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - allowedString := c.Args().Get(0) - allowed, err := cliutils.ValidateBool("allowed", allowedString) - if err != nil { - return err - } - - // Run - api.PrintResponse(setRplLockAllowed(c, allowed)) - return nil - - }, - }, - - { - Name: "can-set-stake-rpl-for-allowed", - Usage: "Check whether the node can set allowed status for an address to stake RPL on behalf of themself", - UsageText: "rocketpool api node can-set-stake-rpl-for-allowed caller allowed", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - - callerAddressString := c.Args().Get(0) - callerAddress, err := cliutils.ValidateAddress("caller", callerAddressString) - if err != nil { - return err - } - - allowedString := c.Args().Get(1) - allowed, err := cliutils.ValidateBool("allowed", allowedString) - if err != nil { - return err - } - - // Run - api.PrintResponse(canSetStakeRplForAllowed(c, callerAddress, allowed)) - return nil - - }, - }, - { - Name: "set-stake-rpl-for-allowed", - Aliases: []string{"kf"}, - Usage: "Sets the allowed status for an address to stake RPL on behalf of your node", - UsageText: "rocketpool api node set-stake-rpl-for-allowed caller allowed", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - - callerAddressString := c.Args().Get(0) - callerAddress, err := cliutils.ValidateAddress("caller", callerAddressString) - if err != nil { - return err - } - - allowedString := c.Args().Get(1) - allowed, err := cliutils.ValidateBool("allowed", allowedString) - if err != nil { - return err - } - - // Run - api.PrintResponse(setStakeRplForAllowed(c, callerAddress, allowed)) - - return nil - }, - }, - { - Name: "can-withdraw-credit", - Usage: "Check whether the node can withdraw credit", - UsageText: "rocketpool api node can-withdraw-credit amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("withdrawal amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canNodeWithdrawCredit(c, amountWei)) - return nil - - }, - }, - { - Name: "withdraw-credit", - Aliases: []string{"wc"}, - Usage: "Withdraw credit from the node", - UsageText: "rocketpool api node withdraw-credit amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("withdrawal amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(nodeWithdrawCredit(c, amountWei)) - return nil - - }, - }, - { - Name: "can-withdraw-eth", - Usage: "Check whether the node can withdraw ETH staked on its behalf", - UsageText: "rocketpool api node can-withdraw-eth amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("withdrawal amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canNodeWithdrawEth(c, amountWei)) - return nil - - }, - }, - { - Name: "withdraw-eth", - Aliases: []string{"i"}, - Usage: "Withdraw ETH staked on behalf of the node", - UsageText: "rocketpool api node withdraw-eth amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("withdrawal amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(nodeWithdrawEth(c, amountWei)) - return nil - - }, - }, - { - Name: "can-unstake-legacy-rpl", - Usage: "Check whether the node can withdraw legacy staked RPL", - UsageText: "rocketpool api node can-withdraw-legacy-rpl amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("unstake amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canNodeUnstakeLegacyRpl(c, amountWei)) - return nil - - }, - }, - { - Name: "unstake-legacy-rpl", - Aliases: []string{"l"}, - Usage: "Unstake legacy RPL staked against the node", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("unstake amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(nodeUnstakeLegacyRpl(c, amountWei)) - return nil - }, - }, - { - Name: "can-withdraw-rpl", - Usage: "Check whether the node can withdraw staked RPL", - UsageText: "rocketpool api node can-withdraw-rpl", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canNodeWithdrawRpl(c)) - return nil - - }, - }, - { - Name: "withdraw-rpl", - Aliases: []string{"w"}, - Usage: "Withdraw RPL staked against the node", - UsageText: "rocketpool api node withdraw-rpl", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(nodeWithdrawRpl(c)) - return nil - - }, - }, - { - Name: "can-withdraw-rpl-v131", - Usage: "Check whether the node can withdraw staked RPL", - UsageText: "rocketpool api node can-withdraw-rpl-v131 amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("withdrawal amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canNodeWithdrawRplv1_3_1(c, amountWei)) - return nil - - }, - }, - { - Name: "withdraw-rpl-v131", - Aliases: []string{"w"}, - Usage: "Withdraw RPL staked against the node", - UsageText: "rocketpool api node withdraw-rpl-v131 amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("withdrawal amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(nodeWithdrawRplv1_3_1(c, amountWei)) - return nil - - }, - }, - { - Name: "can-unstake-rpl", - Usage: "Check whether the node can unstake RPL", - UsageText: "rocketpool api node can-unstake-rpl amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - amountWei, err := cliutils.ValidatePositiveWeiAmount("unstake amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canNodeUnstakeRpl(c, amountWei)) - return nil - - }, - }, - { - Name: "unstake-rpl", - Aliases: []string{"u"}, - Usage: "Unstake RPL from the node", - UsageText: "rocketpool api node withdraw-rpl amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - amountWei, err := cliutils.ValidatePositiveWeiAmount("unstake amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(nodeUnstakeRpl(c, amountWei)) - return nil - - }, - }, - - { - Name: "can-deposit", - 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 express-tickets count", - Action: func(c *cli.Context) error { - - // Validate args - 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 - } - - minNodeFee, err := cliutils.ValidateFraction("minimum node fee", c.Args().Get(1)) - if err != nil { - return err - } - salt, err := cliutils.ValidateBigInt("salt", c.Args().Get(2)) - if err != nil { - return err - } - - expressTickets, err := cliutils.ValidateUint("express-tickets", 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(canNodeDeposits(c, count, amountWei, minNodeFee, salt, int64(expressTickets))) - 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). Optionally specify count to make multiple deposits.", - UsageText: "rocketpool api node deposit amount min-node-fee salt use-credit-balance express-tickets submit count", - Action: func(c *cli.Context) error { - - // Validate args - 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 - } - - minNodeFee, err := cliutils.ValidateFraction("minimum node fee", c.Args().Get(1)) - if err != nil { - return err - } - - salt, err := cliutils.ValidateBigInt("salt", c.Args().Get(2)) - if err != nil { - return err - } - - useCreditBalanceString := c.Args().Get(3) - useCreditBalance, err := cliutils.ValidateBool("use-credit-balance", useCreditBalanceString) - if err != nil { - return err - } - - expressTickets, err := cliutils.ValidateUint("express-tickets", c.Args().Get(4)) - if err != nil { - return err - } - submit, err := cliutils.ValidateBool("submit", c.Args().Get(5)) - if err != nil { - return err - } - - // Check if count is provided - count, err := cliutils.ValidateUint("count", c.Args().Get(6)) - if err != nil { - return err - } - - // Run - response, err := nodeDeposits(c, count, amountWei, minNodeFee, salt, useCreditBalance, int64(expressTickets), submit) - - api.PrintResponse(response, err) - return nil - - }, - }, - - { - Name: "can-send", - Usage: "Check whether the node can send ETH or tokens to an address", - UsageText: "rocketpool api node can-send amount token to", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - amountRaw, err := cliutils.ValidateEthAmount("send amount", c.Args().Get(0)) - if err != nil { - return err - } - token, err := cliutils.ValidateTokenType("token type", c.Args().Get(1)) - if err != nil { - return err - } - toAddress, err := cliutils.ValidateAddress("to address", c.Args().Get(2)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canNodeSend(c, amountRaw, token, toAddress)) - return nil - - }, - }, - { - Name: "send", - Aliases: []string{"n"}, - Usage: "Send ETH or tokens from the node account to an address", - UsageText: "rocketpool api node send amount token to", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - amountRaw, err := cliutils.ValidatePositiveEthAmount("send amount", c.Args().Get(0)) - if err != nil { - return err - } - token, err := cliutils.ValidateTokenType("token type", c.Args().Get(1)) - if err != nil { - return err - } - toAddress, err := cliutils.ValidateAddress("to address", c.Args().Get(2)) - if err != nil { - return err - } - - // Run - api.PrintResponse(nodeSend(c, amountRaw, token, toAddress)) - return nil - - }, - }, - { - Name: "send-all", - Usage: "Send the entire token balance from the node account to an address (avoids float64 rounding errors)", - UsageText: "rocketpool api node send-all token to", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - token, err := cliutils.ValidateTokenType("token type", c.Args().Get(0)) - if err != nil { - return err - } - toAddress, err := cliutils.ValidateAddress("to address", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(nodeSendAllTokens(c, token, toAddress)) - return nil - - }, - }, - - { - Name: "can-burn", - Usage: "Check whether the node can burn tokens for ETH", - UsageText: "rocketpool api node can-burn amount token", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("burn amount", c.Args().Get(0)) - if err != nil { - return err - } - token, err := cliutils.ValidateBurnableTokenType("token type", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canNodeBurn(c, amountWei, token)) - return nil - - }, - }, - { - Name: "burn", - Aliases: []string{"b"}, - Usage: "Burn tokens for ETH", - UsageText: "rocketpool api node burn amount token", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("burn amount", c.Args().Get(0)) - if err != nil { - return err - } - token, err := cliutils.ValidateBurnableTokenType("token type", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(nodeBurn(c, amountWei, token)) - return nil - - }, - }, - - { - Name: "can-claim-rpl-rewards", - Usage: "Check whether the node has RPL rewards available to claim", - UsageText: "rocketpool api node can-claim-rpl-rewards", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canNodeClaimRpl(c)) - return nil - - }, - }, - { - Name: "claim-rpl-rewards", - Usage: "Claim available RPL rewards", - UsageText: "rocketpool api node claim-rpl-rewards", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(nodeClaimRpl(c)) - return nil - - }, - }, - - { - Name: "rewards", - Usage: "Get RPL rewards info", - UsageText: "rocketpool api node rewards", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getRewards(c)) - return nil - - }, - }, - - { - Name: "deposit-contract-info", - Usage: "Get information about the deposit contract specified by Rocket Pool and the Beacon Chain client", - UsageText: "rocketpool api node deposit-contract-info", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getDepositContractInfo(c)) - return nil - - }, - }, - - { - Name: "sign", - Usage: "Signs a transaction with the node's private key. The TX must be serialized as a hex string.", - UsageText: "rocketpool api node sign tx", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - data := c.Args().Get(0) - - // Run - api.PrintResponse(sign(c, data)) - return nil - - }, - }, - - { - Name: "sign-message", - Usage: "Signs an arbitrary message with the node's private key.", - UsageText: "rocketpool api node sign-message 'message'", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - message := c.Args().Get(0) - - // Run - api.PrintResponse(signMessage(c, message)) - return nil - - }, - }, - - { - Name: "is-fee-distributor-initialized", - Usage: "Check if the fee distributor contract for this node is initialized and deployed", - UsageText: "rocketpool api node is-fee-distributor-initialized", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(isFeeDistributorInitialized(c)) - return nil - - }, - }, - { - Name: "get-initialize-fee-distributor-gas", - Usage: "Estimate the cost of initializing the fee distributor", - UsageText: "rocketpool api node get-initialize-fee-distributor-gas", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getInitializeFeeDistributorGas(c)) - return nil - }, - }, - - { - Name: "initialize-fee-distributor", - Usage: "Initialize and deploy the fee distributor contract for this node", - UsageText: "rocketpool api node initialize-fee-distributor", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(initializeFeeDistributor(c)) - return nil - - }, - }, - - { - Name: "can-distribute", - Usage: "Check if distributing ETH from the node's fee distributor is possible", - UsageText: "rocketpool api node can-distribute", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canDistribute(c)) - return nil - - }, - }, - { - Name: "distribute", - Usage: "Distribute ETH from the node's fee distributor", - UsageText: "rocketpool api node distribute", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(distribute(c)) - return nil - - }, - }, - { - Name: "claim-rpl-rewards", - Usage: "Claim available RPL rewards", - UsageText: "rocketpool api node claim-rpl-rewards", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(nodeClaimRpl(c)) - return nil - - }, - }, - - { - Name: "get-rewards-info", - Usage: "Get info about your eligible rewards periods, including balances and Merkle proofs", - UsageText: "rocketpool api node get-rewards-info", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getRewardsInfo(c)) - return nil - - }, - }, - { - Name: "can-claim-rewards", - Usage: "Check if the rewards for the given intervals can be claimed", - UsageText: "rocketpool api node can-claim-rewards 0,1,2,5,6", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - indicesString := c.Args().Get(0) - - // Run - api.PrintResponse(canClaimRewards(c, indicesString)) - return nil - - }, - }, - { - Name: "claim-rewards", - Usage: "Claim rewards for the given reward intervals", - UsageText: "rocketpool api node claim-rewards 0,1,2,5,6", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - indicesString := c.Args().Get(0) - - // Run - api.PrintResponse(claimRewards(c, indicesString)) - return nil - - }, - }, - { - Name: "can-claim-and-stake-rewards", - Usage: "Check if the rewards for the given intervals can be claimed, and RPL restaked automatically", - UsageText: "rocketpool api node can-claim-and-stake-rewards 0,1,2,5,6 amount-to-restake", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - indicesString := c.Args().Get(0) - - stakeAmount, err := cliutils.ValidateBigInt("stakeAmount", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canClaimAndStakeRewards(c, indicesString, stakeAmount)) - return nil - - }, - }, - { - Name: "claim-and-stake-rewards", - Usage: "Claim rewards for the given reward intervals and restake RPL automatically", - UsageText: "rocketpool api node claim-and-stake-rewards 0,1,2,5,6 amount-to-restake", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - indicesString := c.Args().Get(0) - - stakeAmount, err := cliutils.ValidateBigInt("stakeAmount", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(claimAndStakeRewards(c, indicesString, stakeAmount)) - return nil - - }, - }, - - { - Name: "get-smoothing-pool-registration-status", - Usage: "Check whether or not the node is opted into the Smoothing Pool", - UsageText: "rocketpool api node get-smoothing-pool-registration-status", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getSmoothingPoolRegistrationStatus(c)) - return nil - - }, - }, - { - Name: "can-set-smoothing-pool-status", - Usage: "Check if the node's Smoothing Pool status can be changed", - UsageText: "rocketpool api node can-set-smoothing-pool-status status", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - status, err := cliutils.ValidateBool("status", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canSetSmoothingPoolStatus(c, status)) - return nil - - }, - }, - { - Name: "set-smoothing-pool-status", - Usage: "Sets the node's Smoothing Pool opt-in status", - UsageText: "rocketpool api node set-smoothing-pool-status status", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - status, err := cliutils.ValidateBool("status", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(setSmoothingPoolStatus(c, status)) - return nil - - }, - }, - { - Name: "resolve-ens-name", - Usage: "Resolve an ENS name", - UsageText: "rocketpool api node resolve-ens-name name", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Run - api.PrintResponse(resolveEnsName(c, c.Args().Get(0))) - return nil - - }, - }, - { - Name: "reverse-resolve-ens-name", - Usage: "Reverse resolve an address to an ENS name", - UsageText: "rocketpool api node reverse-resolve-ens-name address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - address, err := cliutils.ValidateAddress("address", c.Args().Get(0)) - if err != nil { - return err - } - // Run - api.PrintResponse(reverseResolveEnsName(c, address)) - return nil - - }, - }, - - { - Name: "can-create-vacant-minipool", - Usage: "Check whether a vacant minipool can be created for solo staker migration", - UsageText: "rocketpool api node can-create-vacant-minipool amount min-fee salt pubkey", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 4); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("deposit amount", c.Args().Get(0)) - if err != nil { - return err - } - minNodeFee, err := cliutils.ValidateFraction("minimum node fee", c.Args().Get(1)) - if err != nil { - return err - } - salt, err := cliutils.ValidateBigInt("salt", c.Args().Get(2)) - if err != nil { - return err - } - pubkey, err := cliutils.ValidatePubkey("pubkey", c.Args().Get(3)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canCreateVacantMinipool(c, amountWei, minNodeFee, salt, pubkey)) - return nil - - }, - }, - { - Name: "create-vacant-minipool", - Usage: "Create a vacant minipool, which can be used to migrate a solo staker", - UsageText: "rocketpool api node create-vacant-minipool amount min-fee salt pubkey", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 4); err != nil { - return err - } - amountWei, err := cliutils.ValidatePositiveWeiAmount("deposit amount", c.Args().Get(0)) - if err != nil { - return err - } - minNodeFee, err := cliutils.ValidateFraction("minimum node fee", c.Args().Get(1)) - if err != nil { - return err - } - salt, err := cliutils.ValidateBigInt("salt", c.Args().Get(2)) - if err != nil { - return err - } - pubkey, err := cliutils.ValidatePubkey("pubkey", c.Args().Get(3)) - if err != nil { - return err - } - - // Run - api.PrintResponse(createVacantMinipool(c, amountWei, minNodeFee, salt, pubkey)) - return nil - - }, - }, - - { - Name: "check-collateral", - Usage: "Check if the node is above the minimum collateralization threshold, including pending bond reductions", - UsageText: "rocketpool api node check-collateral", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(checkCollateral(c)) - return nil - - }, - }, - - { - Name: "get-eth-balance", - Usage: "Get the ETH balance of the node address", - UsageText: "rocketpool api node get-eth-balance", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getNodeEthBalance(c)) - return nil - - }, - }, - - { - Name: "can-send-message", - Usage: "Estimates the gas for sending a zero-value message with a payload", - UsageText: "rocketpool api node can-send-message address message", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - address, err := cliutils.ValidateAddress("address", c.Args().Get(0)) - if err != nil { - return err - } - message, err := cliutils.ValidateByteArray("message", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canSendMessage(c, address, message)) - return nil - - }, - }, - { - Name: "send-message", - Usage: "Sends a zero-value message with a payload", - UsageText: "rocketpool api node send-message address message", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - address, err := cliutils.ValidateAddress("address", c.Args().Get(0)) - if err != nil { - return err - } - message, err := cliutils.ValidateByteArray("message", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(sendMessage(c, address, message)) - return nil - - }, - }, - { - Name: "get-express-ticket-count", - Usage: "Get the number of express tickets available for the node", - UsageText: "rocketpool api node get-express-ticket-count", - Action: func(c *cli.Context) error { - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - api.PrintResponse(getExpressTicketCount(c)) - return nil - }, - }, - { - Name: "can-claim-unclaimed-rewards", - Usage: "Check if any unclaimed rewards can be sent to the node's withdrawal address", - UsageText: "rocketpool api node can-claim-unclaimed-rewards address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - // Get amount - nodeAddress, err := cliutils.ValidateAddress("address", c.Args().Get(0)) - if err != nil { - return err - } - // Run - api.PrintResponse(canClaimUnclaimedRewards(c, nodeAddress)) - return nil - - }, - }, - { - Name: "claim-unclaimed-rewards", - Usage: "Send unclaimed rewards to the node's withdrawal address", - UsageText: "rocketpool api node claim-unclaimed-rewards address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - // Get amount - nodeAddress, err := cliutils.ValidateAddress("address", c.Args().Get(0)) - if err != nil { - return err - } - // Run - api.PrintResponse(canClaimUnclaimedRewards(c, nodeAddress)) - return nil - - }, - }, - { - Name: "get-express-tickets-provisioned", - Usage: "Get the number of express tickets provisioned for the node", - UsageText: "rocketpool api node get-express-tickets-provisioned", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getExpressTicketsProvisioned(c)) - return nil - - }, - }, - { - Name: "can-provision-express-tickets", - Usage: "Check if the node's express tickets can be provisioned", - UsageText: "rocketpool api node can-provision-express-tickets", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canProvisionExpressTickets(c)) - return nil - - }, - }, - { - Name: "provision-express-tickets", - Usage: "Provision the node's express tickets", - UsageText: "rocketpool api node provision-express-tickets", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(provisionExpressTickets(c)) - return nil - - }, - }, - { - Name: "get-bond-requirement", - Usage: "Get the bond requirement for a validator", - UsageText: "rocketpool api node get-bond-requirement num-validators", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - numValidators, err := cliutils.ValidateUint("num-validators", c.Args().Get(0)) - if err != nil { - return err - } - // Run - api.PrintResponse(getBondRequirement(c, numValidators)) - return nil - - }, - }, - }, - }) -} diff --git a/rocketpool/api/node/routes.go b/rocketpool/api/node/routes.go new file mode 100644 index 000000000..ea0edfc1d --- /dev/null +++ b/rocketpool/api/node/routes.go @@ -0,0 +1,826 @@ +package node + +import ( + "encoding/hex" + "fmt" + "math/big" + "net/http" + "strconv" + + "github.com/ethereum/go-ethereum/common" + rptypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/urfave/cli" + + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +// RegisterRoutes registers the node module's HTTP routes onto mux. +func RegisterRoutes(mux *http.ServeMux, c *cli.Context) { + mux.HandleFunc("/api/node/status", func(w http.ResponseWriter, r *http.Request) { + resp, err := getStatus(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/alerts", func(w http.ResponseWriter, r *http.Request) { + resp, err := getAlerts(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/sync", func(w http.ResponseWriter, r *http.Request) { + resp, err := getSyncProgress(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/get-eth-balance", func(w http.ResponseWriter, r *http.Request) { + resp, err := getNodeEthBalance(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/check-collateral", func(w http.ResponseWriter, r *http.Request) { + resp, err := checkCollateral(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/rewards", func(w http.ResponseWriter, r *http.Request) { + resp, err := getRewards(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/deposit-contract-info", func(w http.ResponseWriter, r *http.Request) { + resp, err := getDepositContractInfo(c) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Register --- + + mux.HandleFunc("/api/node/can-register", func(w http.ResponseWriter, r *http.Request) { + tz := r.URL.Query().Get("timezoneLocation") + resp, err := canRegisterNode(c, tz) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/register", func(w http.ResponseWriter, r *http.Request) { + tz := r.FormValue("timezoneLocation") + resp, err := registerNode(c, tz) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Timezone --- + + mux.HandleFunc("/api/node/can-set-timezone", func(w http.ResponseWriter, r *http.Request) { + tz := r.URL.Query().Get("timezoneLocation") + resp, err := canSetTimezoneLocation(c, tz) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/set-timezone", func(w http.ResponseWriter, r *http.Request) { + tz := r.FormValue("timezoneLocation") + resp, err := setTimezoneLocation(c, tz) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Primary withdrawal address --- + + mux.HandleFunc("/api/node/can-set-primary-withdrawal-address", func(w http.ResponseWriter, r *http.Request) { + addr := common.HexToAddress(r.URL.Query().Get("address")) + confirm := r.URL.Query().Get("confirm") == "true" + resp, err := canSetPrimaryWithdrawalAddress(c, addr, confirm) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/set-primary-withdrawal-address", func(w http.ResponseWriter, r *http.Request) { + addr := common.HexToAddress(r.FormValue("address")) + confirm := r.FormValue("confirm") == "true" + resp, err := setPrimaryWithdrawalAddress(c, addr, confirm) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-confirm-primary-withdrawal-address", func(w http.ResponseWriter, r *http.Request) { + resp, err := canConfirmPrimaryWithdrawalAddress(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/confirm-primary-withdrawal-address", func(w http.ResponseWriter, r *http.Request) { + resp, err := confirmPrimaryWithdrawalAddress(c) + apiutils.WriteResponse(w, resp, err) + }) + + // --- RPL withdrawal address --- + + mux.HandleFunc("/api/node/can-set-rpl-withdrawal-address", func(w http.ResponseWriter, r *http.Request) { + addr := common.HexToAddress(r.URL.Query().Get("address")) + confirm := r.URL.Query().Get("confirm") == "true" + resp, err := canSetRPLWithdrawalAddress(c, addr, confirm) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/set-rpl-withdrawal-address", func(w http.ResponseWriter, r *http.Request) { + addr := common.HexToAddress(r.FormValue("address")) + confirm := r.FormValue("confirm") == "true" + resp, err := setRPLWithdrawalAddress(c, addr, confirm) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-confirm-rpl-withdrawal-address", func(w http.ResponseWriter, r *http.Request) { + resp, err := canConfirmRPLWithdrawalAddress(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/confirm-rpl-withdrawal-address", func(w http.ResponseWriter, r *http.Request) { + resp, err := confirmRPLWithdrawalAddress(c) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Swap RPL --- + + mux.HandleFunc("/api/node/swap-rpl-allowance", func(w http.ResponseWriter, r *http.Request) { + resp, err := allowanceFsRpl(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-swap-rpl", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canNodeSwapRpl(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/get-swap-rpl-approval-gas", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := getSwapApprovalGas(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/swap-rpl-approve-rpl", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := approveFsRpl(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/wait-and-swap-rpl", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + hash := common.HexToHash(r.FormValue("approvalTxHash")) + resp, err := waitForApprovalAndSwapFsRpl(c, amountWei, hash) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/swap-rpl", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := swapRpl(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Stake RPL --- + + mux.HandleFunc("/api/node/stake-rpl-allowance", func(w http.ResponseWriter, r *http.Request) { + resp, err := allowanceRpl(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-stake-rpl", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canNodeStakeRpl(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/get-stake-rpl-approval-gas", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := getStakeApprovalGas(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/stake-rpl-approve-rpl", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := approveRpl(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/wait-and-stake-rpl", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + hash := common.HexToHash(r.FormValue("approvalTxHash")) + resp, err := waitForApprovalAndStakeRpl(c, amountWei, hash) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/stake-rpl", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := stakeRpl(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + // --- RPL locking --- + + mux.HandleFunc("/api/node/can-set-rpl-locking-allowed", func(w http.ResponseWriter, r *http.Request) { + allowed := r.URL.Query().Get("allowed") == "true" + resp, err := canSetRplLockAllowed(c, allowed) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/set-rpl-locking-allowed", func(w http.ResponseWriter, r *http.Request) { + allowed := r.FormValue("allowed") == "true" + resp, err := setRplLockAllowed(c, allowed) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Stake RPL for allowed --- + + mux.HandleFunc("/api/node/can-set-stake-rpl-for-allowed", func(w http.ResponseWriter, r *http.Request) { + caller := common.HexToAddress(r.URL.Query().Get("caller")) + allowed := r.URL.Query().Get("allowed") == "true" + resp, err := canSetStakeRplForAllowed(c, caller, allowed) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/set-stake-rpl-for-allowed", func(w http.ResponseWriter, r *http.Request) { + caller := common.HexToAddress(r.FormValue("caller")) + allowed := r.FormValue("allowed") == "true" + resp, err := setStakeRplForAllowed(c, caller, allowed) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Withdraw RPL --- + + mux.HandleFunc("/api/node/can-withdraw-rpl", func(w http.ResponseWriter, r *http.Request) { + resp, err := canNodeWithdrawRpl(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/withdraw-rpl", func(w http.ResponseWriter, r *http.Request) { + resp, err := nodeWithdrawRpl(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-unstake-legacy-rpl", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canNodeUnstakeLegacyRpl(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/unstake-legacy-rpl", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeUnstakeLegacyRpl(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-withdraw-rpl-v131", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canNodeWithdrawRplv1_3_1(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/withdraw-rpl-v131", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeWithdrawRplv1_3_1(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-unstake-rpl", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canNodeUnstakeRpl(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/unstake-rpl", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeUnstakeRpl(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Withdraw ETH / credit --- + + mux.HandleFunc("/api/node/can-withdraw-eth", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canNodeWithdrawEth(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/withdraw-eth", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeWithdrawEth(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-withdraw-credit", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canNodeWithdrawCredit(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/withdraw-credit", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeWithdrawCredit(c, amountWei) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Deposit --- + + mux.HandleFunc("/api/node/can-deposit", func(w http.ResponseWriter, r *http.Request) { + params, err := parseDepositParams(r, false) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canNodeDeposits(c, params.count, params.amountWei, params.minFee, params.salt, params.expressTickets) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/deposit", func(w http.ResponseWriter, r *http.Request) { + params, err := parseDepositParams(r, true) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeDeposits(c, params.count, params.amountWei, params.minFee, params.salt, params.useCreditBalance, params.expressTickets, params.submit) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Send / burn --- + + mux.HandleFunc("/api/node/can-send", func(w http.ResponseWriter, r *http.Request) { + amountRaw, err := parseNodeFloat64(r, "amountRaw") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + token := r.URL.Query().Get("token") + to := common.HexToAddress(r.URL.Query().Get("to")) + resp, err := canNodeSend(c, amountRaw, token, to) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/send", func(w http.ResponseWriter, r *http.Request) { + amountRaw, err := parseNodeFloat64(r, "amountRaw") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + token := r.FormValue("token") + to := common.HexToAddress(r.FormValue("to")) + resp, err := nodeSend(c, amountRaw, token, to) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/send-all", func(w http.ResponseWriter, r *http.Request) { + token := r.FormValue("token") + to := common.HexToAddress(r.FormValue("to")) + resp, err := nodeSendAllTokens(c, token, to) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-burn", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + token := r.URL.Query().Get("token") + resp, err := canNodeBurn(c, amountWei, token) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/burn", func(w http.ResponseWriter, r *http.Request) { + amountWei, err := parseNodeBigInt(r, "amountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + token := r.FormValue("token") + resp, err := nodeBurn(c, amountWei, token) + apiutils.WriteResponse(w, resp, err) + }) + + // --- RPL claim --- + + mux.HandleFunc("/api/node/can-claim-rpl-rewards", func(w http.ResponseWriter, r *http.Request) { + resp, err := canNodeClaimRpl(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/claim-rpl-rewards", func(w http.ResponseWriter, r *http.Request) { + resp, err := nodeClaimRpl(c) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Fee distributor --- + + mux.HandleFunc("/api/node/is-fee-distributor-initialized", func(w http.ResponseWriter, r *http.Request) { + resp, err := isFeeDistributorInitialized(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/get-initialize-fee-distributor-gas", func(w http.ResponseWriter, r *http.Request) { + resp, err := getInitializeFeeDistributorGas(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/initialize-fee-distributor", func(w http.ResponseWriter, r *http.Request) { + resp, err := initializeFeeDistributor(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-distribute", func(w http.ResponseWriter, r *http.Request) { + resp, err := canDistribute(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/distribute", func(w http.ResponseWriter, r *http.Request) { + resp, err := distribute(c) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Interval rewards --- + + mux.HandleFunc("/api/node/get-rewards-info", func(w http.ResponseWriter, r *http.Request) { + resp, err := getRewardsInfo(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-claim-rewards", func(w http.ResponseWriter, r *http.Request) { + indices := r.URL.Query().Get("indices") + resp, err := canClaimRewards(c, indices) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/claim-rewards", func(w http.ResponseWriter, r *http.Request) { + indices := r.FormValue("indices") + resp, err := claimRewards(c, indices) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-claim-and-stake-rewards", func(w http.ResponseWriter, r *http.Request) { + indices := r.URL.Query().Get("indices") + stakeAmount, err := parseNodeBigInt(r, "stakeAmount") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canClaimAndStakeRewards(c, indices, stakeAmount) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/claim-and-stake-rewards", func(w http.ResponseWriter, r *http.Request) { + indices := r.FormValue("indices") + stakeAmount, err := parseNodeBigInt(r, "stakeAmount") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := claimAndStakeRewards(c, indices, stakeAmount) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Smoothing pool --- + + mux.HandleFunc("/api/node/get-smoothing-pool-registration-status", func(w http.ResponseWriter, r *http.Request) { + resp, err := getSmoothingPoolRegistrationStatus(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-set-smoothing-pool-status", func(w http.ResponseWriter, r *http.Request) { + status := r.URL.Query().Get("status") == "true" + resp, err := canSetSmoothingPoolStatus(c, status) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/set-smoothing-pool-status", func(w http.ResponseWriter, r *http.Request) { + status := r.FormValue("status") == "true" + resp, err := setSmoothingPoolStatus(c, status) + apiutils.WriteResponse(w, resp, err) + }) + + // --- ENS --- + + mux.HandleFunc("/api/node/resolve-ens-name", func(w http.ResponseWriter, r *http.Request) { + name := r.URL.Query().Get("name") + resp, err := resolveEnsName(c, name) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/reverse-resolve-ens-name", func(w http.ResponseWriter, r *http.Request) { + addr := common.HexToAddress(r.URL.Query().Get("address")) + resp, err := reverseResolveEnsName(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Sign --- + + mux.HandleFunc("/api/node/sign-message", func(w http.ResponseWriter, r *http.Request) { + message := r.FormValue("message") + resp, err := signMessage(c, message) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/sign", func(w http.ResponseWriter, r *http.Request) { + serializedTx := r.FormValue("serializedTx") + resp, err := sign(c, serializedTx) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Vacant minipool --- + + mux.HandleFunc("/api/node/can-create-vacant-minipool", func(w http.ResponseWriter, r *http.Request) { + params, err := parseVacantMinipoolParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canCreateVacantMinipool(c, params.amountWei, params.minFee, params.salt, params.pubkey) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/create-vacant-minipool", func(w http.ResponseWriter, r *http.Request) { + params, err := parseVacantMinipoolParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := createVacantMinipool(c, params.amountWei, params.minFee, params.salt, params.pubkey) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Send message --- + + mux.HandleFunc("/api/node/can-send-message", func(w http.ResponseWriter, r *http.Request) { + addr := common.HexToAddress(r.URL.Query().Get("address")) + msgBytes, err := hex.DecodeString(r.URL.Query().Get("message")) + if err != nil { + apiutils.WriteErrorResponse(w, fmt.Errorf("invalid message hex: %w", err)) + return + } + resp, err := canSendMessage(c, addr, msgBytes) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/send-message", func(w http.ResponseWriter, r *http.Request) { + addr := common.HexToAddress(r.FormValue("address")) + msgBytes, err := hex.DecodeString(r.FormValue("message")) + if err != nil { + apiutils.WriteErrorResponse(w, fmt.Errorf("invalid message hex: %w", err)) + return + } + resp, err := sendMessage(c, addr, msgBytes) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Express tickets --- + + mux.HandleFunc("/api/node/get-express-ticket-count", func(w http.ResponseWriter, r *http.Request) { + resp, err := getExpressTicketCount(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/get-express-tickets-provisioned", func(w http.ResponseWriter, r *http.Request) { + resp, err := getExpressTicketsProvisioned(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/can-provision-express-tickets", func(w http.ResponseWriter, r *http.Request) { + resp, err := canProvisionExpressTickets(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/provision-express-tickets", func(w http.ResponseWriter, r *http.Request) { + resp, err := provisionExpressTickets(c) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Unclaimed rewards --- + + mux.HandleFunc("/api/node/can-claim-unclaimed-rewards", func(w http.ResponseWriter, r *http.Request) { + nodeAddr := common.HexToAddress(r.URL.Query().Get("nodeAddress")) + resp, err := canClaimUnclaimedRewards(c, nodeAddr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/node/claim-unclaimed-rewards", func(w http.ResponseWriter, r *http.Request) { + nodeAddr := common.HexToAddress(r.FormValue("nodeAddress")) + resp, err := claimUnclaimedRewards(c, nodeAddr) + apiutils.WriteResponse(w, resp, err) + }) + + // --- Bond requirement --- + + mux.HandleFunc("/api/node/get-bond-requirement", func(w http.ResponseWriter, r *http.Request) { + numValidators, err := strconv.ParseUint(r.URL.Query().Get("numValidators"), 10, 64) + if err != nil { + apiutils.WriteErrorResponse(w, fmt.Errorf("invalid numValidators: %w", err)) + return + } + resp, err := getBondRequirement(c, numValidators) + apiutils.WriteResponse(w, resp, err) + }) +} + +// --- Helper types and functions --- + +type depositParams struct { + count uint64 + amountWei *big.Int + minFee float64 + salt *big.Int + expressTickets int64 + useCreditBalance bool + submit bool +} + +func parseDepositParams(r *http.Request, includeExecuteParams bool) (depositParams, error) { + var p depositParams + var err error + + p.amountWei, err = parseNodeBigInt(r, "amountWei") + if err != nil { + return p, fmt.Errorf("invalid amountWei: %w", err) + } + + minFeeStr := r.URL.Query().Get("minFee") + if minFeeStr == "" { + minFeeStr = r.FormValue("minFee") + } + p.minFee, err = strconv.ParseFloat(minFeeStr, 64) + if err != nil { + return p, fmt.Errorf("invalid minFee: %w", err) + } + + p.salt, err = parseNodeBigInt(r, "salt") + if err != nil { + return p, fmt.Errorf("invalid salt: %w", err) + } + + expressStr := r.URL.Query().Get("expressTickets") + if expressStr == "" { + expressStr = r.FormValue("expressTickets") + } + p.expressTickets, err = strconv.ParseInt(expressStr, 10, 64) + if err != nil { + return p, fmt.Errorf("invalid expressTickets: %w", err) + } + + countStr := r.URL.Query().Get("count") + if countStr == "" { + countStr = r.FormValue("count") + } + p.count, err = strconv.ParseUint(countStr, 10, 64) + if err != nil { + return p, fmt.Errorf("invalid count: %w", err) + } + + if includeExecuteParams { + p.useCreditBalance = r.FormValue("useCreditBalance") == "true" + p.submit = r.FormValue("submit") == "true" + } + + return p, nil +} + +type vacantMinipoolParams struct { + amountWei *big.Int + minFee float64 + salt *big.Int + pubkey rptypes.ValidatorPubkey +} + +func parseVacantMinipoolParams(r *http.Request) (vacantMinipoolParams, error) { + var p vacantMinipoolParams + var err error + + raw := r.URL.Query().Get("amountWei") + if raw == "" { + raw = r.FormValue("amountWei") + } + p.amountWei, _ = new(big.Int).SetString(raw, 10) + if p.amountWei == nil { + return p, fmt.Errorf("invalid amountWei: %s", raw) + } + + minFeeStr := r.URL.Query().Get("minFee") + if minFeeStr == "" { + minFeeStr = r.FormValue("minFee") + } + p.minFee, err = strconv.ParseFloat(minFeeStr, 64) + if err != nil { + return p, fmt.Errorf("invalid minFee: %w", err) + } + + saltStr := r.URL.Query().Get("salt") + if saltStr == "" { + saltStr = r.FormValue("salt") + } + p.salt, _ = new(big.Int).SetString(saltStr, 10) + if p.salt == nil { + return p, fmt.Errorf("invalid salt: %s", saltStr) + } + + pubkeyStr := r.URL.Query().Get("pubkey") + if pubkeyStr == "" { + pubkeyStr = r.FormValue("pubkey") + } + pubkeyBytes, err := hex.DecodeString(pubkeyStr) + if err != nil { + return p, fmt.Errorf("invalid pubkey hex: %w", err) + } + if len(pubkeyBytes) != len(p.pubkey) { + return p, fmt.Errorf("pubkey must be %d bytes, got %d", len(p.pubkey), len(pubkeyBytes)) + } + copy(p.pubkey[:], pubkeyBytes) + + return p, nil +} + +func parseNodeBigInt(r *http.Request, name string) (*big.Int, error) { + raw := r.URL.Query().Get(name) + if raw == "" { + raw = r.FormValue(name) + } + v, ok := new(big.Int).SetString(raw, 10) + if !ok { + return nil, fmt.Errorf("invalid %s: %s", name, raw) + } + return v, nil +} + +func parseNodeFloat64(r *http.Request, name string) (float64, error) { + raw := r.URL.Query().Get(name) + if raw == "" { + raw = r.FormValue(name) + } + return strconv.ParseFloat(raw, 64) +} diff --git a/rocketpool/api/odao/commands.go b/rocketpool/api/odao/commands.go deleted file mode 100644 index 4f5460941..000000000 --- a/rocketpool/api/odao/commands.go +++ /dev/null @@ -1,1138 +0,0 @@ -package odao - -import ( - "github.com/urfave/cli" - - "github.com/rocket-pool/smartnode/shared/utils/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Subcommands = append(command.Subcommands, cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the Rocket Pool oracle DAO", - Subcommands: []cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get oracle DAO status", - UsageText: "rocketpool api odao status", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getStatus(c)) - return nil - - }, - }, - - { - Name: "members", - Aliases: []string{"m"}, - Usage: "Get the oracle DAO members", - UsageText: "rocketpool api odao members", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getMembers(c)) - return nil - - }, - }, - - { - Name: "proposals", - Aliases: []string{"p"}, - Usage: "Get the oracle DAO proposals", - UsageText: "rocketpool api odao proposals", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getProposals(c)) - return nil - - }, - }, - - { - Name: "can-penalise-megapool", - Aliases: []string{"cpm"}, - Usage: "Checks whether we can penalise a megapool", - UsageText: "rocketpool api odao can-penalise-megapool megapool-address block amount", - Action: func(c *cli.Context) error { - - // Validate args - var err error - if err = cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - megapoolAddress, err := cliutils.ValidateAddress("megapool address", c.Args().Get(0)) - if err != nil { - return err - } - - block, err := cliutils.ValidateBigInt("block", c.Args().Get(1)) - if err != nil { - return err - } - - amount, err := cliutils.ValidateBigInt("amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canPenaliseMegapool(c, megapoolAddress, block, amount)) - return nil - - }, - }, - - { - Name: "penalise-megapool", - Aliases: []string{"pm"}, - Usage: "Penalise a megapool", - UsageText: "rocketpool api odao penalise-megapool megapool-address block amount", - Action: func(c *cli.Context) error { - - // Validate args - var err error - if err = cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - megapoolAddress, err := cliutils.ValidateAddress("megapool address", c.Args().Get(0)) - if err != nil { - return err - } - - block, err := cliutils.ValidateBigInt("block", c.Args().Get(1)) - if err != nil { - return err - } - - amount, err := cliutils.ValidateBigInt("amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(penaliseMegapool(c, megapoolAddress, block, amount)) - return nil - - }, - }, - - { - Name: "proposal-details", - Aliases: []string{"d"}, - Usage: "Get details of a proposal", - UsageText: "rocketpool api odao proposal-details proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - var err error - if err = cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - id, err := cliutils.ValidateUint("proposal-id", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(getProposal(c, id)) - return nil - - }, - }, - - { - Name: "can-propose-invite", - Usage: "Check whether the node can propose inviting a new member", - UsageText: "rocketpool api odao can-propose-invite member-address member-id member-url", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - memberAddress, err := cliutils.ValidateAddress("member address", c.Args().Get(0)) - if err != nil { - return err - } - memberId, err := cliutils.ValidateDAOMemberID("member ID", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeInvite(c, memberAddress, memberId, c.Args().Get(2))) - return nil - - }, - }, - { - Name: "propose-invite", - Aliases: []string{"i"}, - Usage: "Propose inviting a new member", - UsageText: "rocketpool api odao propose-invite member-address member-id member-url", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - memberAddress, err := cliutils.ValidateAddress("member address", c.Args().Get(0)) - if err != nil { - return err - } - memberId, err := cliutils.ValidateDAOMemberID("member ID", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeInvite(c, memberAddress, memberId, c.Args().Get(2))) - return nil - - }, - }, - - { - Name: "can-propose-leave", - Usage: "Check whether the node can propose leaving the oracle DAO", - UsageText: "rocketpool api odao can-propose-leave", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canProposeLeave(c)) - return nil - - }, - }, - { - Name: "propose-leave", - Aliases: []string{"l"}, - Usage: "Propose leaving the oracle DAO", - UsageText: "rocketpool api odao propose-leave", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(proposeLeave(c)) - return nil - - }, - }, - - { - Name: "can-propose-kick", - Usage: "Check whether the node can propose kicking a member", - UsageText: "rocketpool api odao can-propose-kick member-address fine-amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - memberAddress, err := cliutils.ValidateAddress("member address", c.Args().Get(0)) - if err != nil { - return err - } - fineAmountWei, err := cliutils.ValidatePositiveOrZeroWeiAmount("fine amount", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeKick(c, memberAddress, fineAmountWei)) - return nil - - }, - }, - { - Name: "propose-kick", - Aliases: []string{"k"}, - Usage: "Propose kicking a member", - UsageText: "rocketpool api odao propose-kick member-address fine-amount", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - memberAddress, err := cliutils.ValidateAddress("member address", c.Args().Get(0)) - if err != nil { - return err - } - fineAmountWei, err := cliutils.ValidatePositiveOrZeroWeiAmount("fine amount", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeKick(c, memberAddress, fineAmountWei)) - return nil - - }, - }, - - { - Name: "can-cancel-proposal", - Usage: "Check whether the node can cancel a proposal", - UsageText: "rocketpool api odao can-cancel-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canCancelProposal(c, proposalId)) - return nil - - }, - }, - { - Name: "cancel-proposal", - Aliases: []string{"c"}, - Usage: "Cancel a proposal made by the node", - UsageText: "rocketpool api odao cancel-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(cancelProposal(c, proposalId)) - return nil - - }, - }, - - { - Name: "can-vote-proposal", - Usage: "Check whether the node can vote on a proposal", - UsageText: "rocketpool api odao can-vote-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canVoteOnProposal(c, proposalId)) - return nil - - }, - }, - { - Name: "vote-proposal", - Aliases: []string{"v"}, - Usage: "Vote on a proposal", - UsageText: "rocketpool api odao vote-proposal proposal-id support", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - support, err := cliutils.ValidateBool("support", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(voteOnProposal(c, proposalId, support)) - return nil - - }, - }, - - { - Name: "can-execute-proposal", - Usage: "Check whether the node can execute a proposal", - UsageText: "rocketpool api odao can-execute-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canExecuteProposal(c, proposalId)) - return nil - - }, - }, - { - Name: "execute-proposal", - Aliases: []string{"x"}, - Usage: "Execute a proposal", - UsageText: "rocketpool api odao execute-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(executeProposal(c, proposalId)) - return nil - - }, - }, - - { - Name: "can-join", - Usage: "Check whether the node can join the oracle DAO", - UsageText: "rocketpool api odao can-join", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canJoin(c)) - return nil - - }, - }, - { - Name: "join-approve-rpl", - Aliases: []string{"j1"}, - Usage: "Approves the RPL bond transfer prior to join the oracle DAO", - UsageText: "rocketpool api odao join-approve-rpl", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(approveRpl(c)) - return nil - - }, - }, - { - Name: "join", - Aliases: []string{"j2"}, - Usage: "Join the oracle DAO (requires an executed invite proposal)", - UsageText: "rocketpool api odao join tx-hash", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - hash, err := cliutils.ValidateTxHash("tx-hash", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(waitForApprovalAndJoin(c, hash)) - return nil - - }, - }, - - { - Name: "can-leave", - Usage: "Check whether the node can leave the oracle DAO", - UsageText: "rocketpool api odao can-leave", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canLeave(c)) - return nil - - }, - }, - { - Name: "leave", - Aliases: []string{"e"}, - Usage: "Leave the oracle DAO (requires an executed leave proposal)", - UsageText: "rocketpool api odao leave bond-refund-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - bondRefundAddress, err := cliutils.ValidateAddress("bond refund address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(leave(c, bondRefundAddress)) - return nil - - }, - }, - - { - Name: "can-propose-members-quorum", - Usage: "Check whether the node can propose the members.quorum setting", - UsageText: "rocketpool api odao can-propose-members-quorum value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - quorum, err := cliutils.ValidateFraction("quorum", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeSettingMembersQuorum(c, quorum)) - return nil - - }, - }, - { - Name: "propose-members-quorum", - Usage: "Propose updating the members.quorum setting", - UsageText: "rocketpool api odao propose-members-quorum value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - quorum, err := cliutils.ValidateFraction("quorum", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSettingMembersQuorum(c, quorum)) - return nil - - }, - }, - - { - Name: "can-propose-members-rplbond", - Usage: "Check whether the node can propose the members.rplbond setting", - UsageText: "rocketpool api odao can-propose-members-rplbond value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - bondAmountWei, err := cliutils.ValidateWeiAmount("RPL bond amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeSettingMembersRplBond(c, bondAmountWei)) - return nil - - }, - }, - { - Name: "propose-members-rplbond", - Usage: "Propose updating the members.rplbond setting", - UsageText: "rocketpool api odao propose-members-rplbond value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - bondAmountWei, err := cliutils.ValidateWeiAmount("RPL bond amount", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSettingMembersRplBond(c, bondAmountWei)) - return nil - - }, - }, - - { - Name: "can-propose-members-minipool-unbonded-max", - Usage: "Check whether the node can propose the members.minipool.unbonded.max setting", - UsageText: "rocketpool api odao can-propose-members-minipool-unbonded-max value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - unbondedMinipoolMax, err := cliutils.ValidateUint("maximum unbonded minipool count", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeSettingMinipoolUnbondedMax(c, unbondedMinipoolMax)) - return nil - - }, - }, - { - Name: "propose-members-minipool-unbonded-max", - Usage: "Propose updating the members.minipool.unbonded.max setting", - UsageText: "rocketpool api odao propose-members-minipool-unbonded-max value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - unbondedMinipoolMax, err := cliutils.ValidateUint("maximum unbonded minipool count", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSettingMinipoolUnbondedMax(c, unbondedMinipoolMax)) - return nil - - }, - }, - - { - Name: "can-propose-proposal-cooldown", - Usage: "Check whether the node can propose the proposal.cooldown setting", - UsageText: "rocketpool api odao can-propose-proposal-cooldown value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalCooldownBlocks, err := cliutils.ValidateUint("proposal cooldown period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeSettingProposalCooldown(c, proposalCooldownBlocks)) - return nil - - }, - }, - { - Name: "propose-proposal-cooldown", - Usage: "Propose updating the proposal.cooldown setting", - UsageText: "rocketpool api odao propose-proposal-cooldown value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalCooldownBlocks, err := cliutils.ValidateUint("proposal cooldown period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSettingProposalCooldown(c, proposalCooldownBlocks)) - return nil - - }, - }, - - { - Name: "can-propose-proposal-vote-timespan", - Usage: "Check whether the node can propose the proposal.vote.time setting", - UsageText: "rocketpool api odao can-propose-proposal-vote-timespan value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalVoteTimespan, err := cliutils.ValidateUint("proposal voting period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeSettingProposalVoteTimespan(c, proposalVoteTimespan)) - return nil - - }, - }, - { - Name: "propose-proposal-vote-timespan", - Usage: "Propose updating the proposal.vote.time setting", - UsageText: "rocketpool api odao propose-proposal-vote-timespan value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalVoteTimespan, err := cliutils.ValidateUint("proposal voting period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSettingProposalVoteTimespan(c, proposalVoteTimespan)) - return nil - - }, - }, - - { - Name: "can-propose-proposal-vote-delay-timespan", - Usage: "Check whether the node can propose the proposal.vote.delay.time setting", - UsageText: "rocketpool api odao can-propose-proposal-vote-delay-timespan value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalDelayTimespan, err := cliutils.ValidateUint("proposal delay period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeSettingProposalVoteDelayTimespan(c, proposalDelayTimespan)) - return nil - - }, - }, - { - Name: "propose-proposal-vote-delay-timespan", - Usage: "Propose updating the proposal.vote.delay.time setting", - UsageText: "rocketpool api odao propose-proposal-vote-delay-timespan value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalDelayTimespan, err := cliutils.ValidateUint("proposal delay period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSettingProposalVoteDelayTimespan(c, proposalDelayTimespan)) - return nil - - }, - }, - - { - Name: "can-propose-proposal-execute-timespan", - Usage: "Check whether the node can propose the proposal.execute.time setting", - UsageText: "rocketpool api odao can-propose-proposal-execute-timespan value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalExecuteTimespan, err := cliutils.ValidateUint("proposal execution period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeSettingProposalExecuteTimespan(c, proposalExecuteTimespan)) - return nil - - }, - }, - { - Name: "propose-proposal-execute-timespan", - Usage: "Propose updating the proposal.execute.time setting", - UsageText: "rocketpool api odao propose-proposal-execute-timespan value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalExecuteTimespan, err := cliutils.ValidateUint("proposal execution period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSettingProposalExecuteTimespan(c, proposalExecuteTimespan)) - return nil - - }, - }, - - { - Name: "can-propose-proposal-action-timespan", - Usage: "Check whether the node can propose the proposal.action.time setting", - UsageText: "rocketpool api odao can-propose-proposal-action-timespan value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalActionTimespan, err := cliutils.ValidateUint("proposal action period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeSettingProposalActionTimespan(c, proposalActionTimespan)) - return nil - - }, - }, - { - Name: "propose-proposal-action-timespan", - Usage: "Propose updating the proposal.action.time setting", - UsageText: "rocketpool api odao propose-proposal-action-timespan value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalActionTimespan, err := cliutils.ValidateUint("proposal action period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSettingProposalActionTimespan(c, proposalActionTimespan)) - return nil - - }, - }, - - { - Name: "can-propose-scrub-period", - Usage: "Check whether the node can propose the minipool.scrub.period setting", - UsageText: "rocketpool api odao can-propose-scrub-period value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - scrubPeriod, err := cliutils.ValidateUint("scrub period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeSettingScrubPeriod(c, scrubPeriod)) - return nil - - }, - }, - { - Name: "propose-scrub-period", - Usage: "Propose updating the minipool.scrub.period setting", - UsageText: "rocketpool api odao propose-scrub-period value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - scrubPeriod, err := cliutils.ValidateUint("scrub period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSettingScrubPeriod(c, scrubPeriod)) - return nil - - }, - }, - - { - Name: "can-propose-promotion-scrub-period", - Usage: "Check whether the node can propose the minipool.promotion.scrub.period setting", - UsageText: "rocketpool api odao can-propose-promotion-scrub-period value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - scrubPeriod, err := cliutils.ValidateUint("promotion scrub period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeSettingPromotionScrubPeriod(c, scrubPeriod)) - return nil - - }, - }, - { - Name: "propose-promotion-scrub-period", - Usage: "Propose updating the minipool.promotion.scrub.period setting", - UsageText: "rocketpool api odao propose-promotion-scrub-period value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - scrubPeriod, err := cliutils.ValidateUint("promotion scrub period", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSettingPromotionScrubPeriod(c, scrubPeriod)) - return nil - - }, - }, - - { - Name: "can-propose-scrub-penalty-enabled", - Usage: "Check whether the node can propose the minipool.scrub.penalty.enabled setting", - UsageText: "rocketpool api odao can-propose-scrub-penalty-enabled value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - enabled, err := cliutils.ValidateBool("scrub penalty enabled", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeSettingScrubPenaltyEnabled(c, enabled)) - return nil - - }, - }, - { - Name: "propose-scrub-penalty-enabled", - Usage: "Propose updating the minipool.scrub.penalty.enabled setting", - UsageText: "rocketpool api odao propose-scrub-penalty-enabled value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - enabled, err := cliutils.ValidateBool("scrub penalty enabled", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSettingScrubPenaltyEnabled(c, enabled)) - return nil - - }, - }, - - { - Name: "can-propose-bond-reduction-window-start", - Usage: "Check whether the node can propose the minipool.bond.reduction.window.start setting", - UsageText: "rocketpool api odao can-propose-bond-reduction-window-start value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - windowStart, err := cliutils.ValidateUint("window start", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeSettingBondReductionWindowStart(c, windowStart)) - return nil - - }, - }, - { - Name: "propose-bond-reduction-window-start", - Usage: "Propose updating the minipool.bond.reduction.window.start setting", - UsageText: "rocketpool api odao propose-bond-reduction-window-start value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - windowStart, err := cliutils.ValidateUint("window start", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSettingBondReductionWindowStart(c, windowStart)) - return nil - - }, - }, - - { - Name: "can-propose-bond-reduction-window-length", - Usage: "Check whether the node can propose the minipool.bond.reduction.window.length setting", - UsageText: "rocketpool api odao can-propose-bond-reduction-window-length value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - windowLength, err := cliutils.ValidateUint("window length", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeSettingBondReductionWindowLength(c, windowLength)) - return nil - - }, - }, - { - Name: "propose-bond-reduction-window-length", - Usage: "Propose updating the minipool.bond.reduction.window.length setting", - UsageText: "rocketpool api odao propose-bond-reduction-window-length value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - windowLength, err := cliutils.ValidateUint("window length", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSettingBondReductionWindowLength(c, windowLength)) - return nil - - }, - }, - - { - Name: "get-member-settings", - Usage: "Get the ODAO settings related to ODAO members", - UsageText: "rocketpool api odao get-member-settings", - Action: func(c *cli.Context) error { - - // Run - api.PrintResponse(getMemberSettings(c)) - return nil - - }, - }, - { - Name: "get-proposal-settings", - Usage: "Get the ODAO settings related to ODAO proposals", - UsageText: "rocketpool api odao get-proposal-settings", - Action: func(c *cli.Context) error { - - // Run - api.PrintResponse(getProposalSettings(c)) - return nil - - }, - }, - { - Name: "get-minipool-settings", - Usage: "Get the ODAO settings related to minipools", - UsageText: "rocketpool api odao get-minipool-settings", - Action: func(c *cli.Context) error { - - // Run - api.PrintResponse(getMinipoolSettings(c)) - return nil - - }, - }, - }, - }) -} diff --git a/rocketpool/api/odao/routes.go b/rocketpool/api/odao/routes.go new file mode 100644 index 000000000..52bc38f24 --- /dev/null +++ b/rocketpool/api/odao/routes.go @@ -0,0 +1,575 @@ +package odao + +import ( + "fmt" + "math/big" + "net/http" + "strconv" + + "github.com/ethereum/go-ethereum/common" + "github.com/urfave/cli" + + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +// RegisterRoutes registers the odao module's HTTP routes onto mux. +func RegisterRoutes(mux *http.ServeMux, c *cli.Context) { + mux.HandleFunc("/api/odao/status", func(w http.ResponseWriter, r *http.Request) { + resp, err := getStatus(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/members", func(w http.ResponseWriter, r *http.Request) { + resp, err := getMembers(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/proposals", func(w http.ResponseWriter, r *http.Request) { + resp, err := getProposals(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/proposal-details", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := getProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-invite", func(w http.ResponseWriter, r *http.Request) { + addr, memberId, memberUrl, err := parseInviteParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeInvite(c, addr, memberId, memberUrl) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-invite", func(w http.ResponseWriter, r *http.Request) { + addr, memberId, memberUrl, err := parseInviteParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeInvite(c, addr, memberId, memberUrl) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-leave", func(w http.ResponseWriter, r *http.Request) { + resp, err := canProposeLeave(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-leave", func(w http.ResponseWriter, r *http.Request) { + resp, err := proposeLeave(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-kick", func(w http.ResponseWriter, r *http.Request) { + addr, fine, err := parseKickParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeKick(c, addr, fine) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-kick", func(w http.ResponseWriter, r *http.Request) { + addr, fine, err := parseKickParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeKick(c, addr, fine) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-cancel-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canCancelProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/cancel-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := cancelProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-vote-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canVoteOnProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/vote-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + supportStr := r.FormValue("support") + support := supportStr == "true" + resp, err := voteOnProposal(c, id, support) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-execute-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canExecuteProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/execute-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := executeProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-join", func(w http.ResponseWriter, r *http.Request) { + resp, err := canJoin(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/join-approve-rpl", func(w http.ResponseWriter, r *http.Request) { + resp, err := approveRpl(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/join", func(w http.ResponseWriter, r *http.Request) { + hashStr := r.FormValue("approvalTxHash") + if hashStr == "" { + apiutils.WriteErrorResponse(w, fmt.Errorf("missing required parameter: approvalTxHash")) + return + } + hash := common.HexToHash(hashStr) + resp, err := waitForApprovalAndJoin(c, hash) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-leave", func(w http.ResponseWriter, r *http.Request) { + resp, err := canLeave(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/leave", func(w http.ResponseWriter, r *http.Request) { + bondRefundStr := r.FormValue("bondRefundAddress") + if bondRefundStr == "" { + apiutils.WriteErrorResponse(w, fmt.Errorf("missing required parameter: bondRefundAddress")) + return + } + resp, err := leave(c, common.HexToAddress(bondRefundStr)) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/get-member-settings", func(w http.ResponseWriter, r *http.Request) { + resp, err := getMemberSettings(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/get-proposal-settings", func(w http.ResponseWriter, r *http.Request) { + resp, err := getProposalSettings(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/get-minipool-settings", func(w http.ResponseWriter, r *http.Request) { + resp, err := getMinipoolSettings(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-penalise-megapool", func(w http.ResponseWriter, r *http.Request) { + megapool, block, amount, err := parsePenaliseParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canPenaliseMegapool(c, megapool, block, amount) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/penalise-megapool", func(w http.ResponseWriter, r *http.Request) { + megapool, block, amount, err := parsePenaliseParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := penaliseMegapool(c, megapool, block, amount) + apiutils.WriteResponse(w, resp, err) + }) + + // propose-settings endpoints + mux.HandleFunc("/api/odao/can-propose-members-quorum", func(w http.ResponseWriter, r *http.Request) { + quorum, err := parseFloat64(r, "quorum") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeSettingMembersQuorum(c, quorum) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-members-quorum", func(w http.ResponseWriter, r *http.Request) { + quorum, err := parseFloat64(r, "quorum") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingMembersQuorum(c, quorum) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-members-rplbond", func(w http.ResponseWriter, r *http.Request) { + bond, err := parseBigInt(r, "bondAmountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeSettingMembersRplBond(c, bond) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-members-rplbond", func(w http.ResponseWriter, r *http.Request) { + bond, err := parseBigInt(r, "bondAmountWei") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingMembersRplBond(c, bond) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-members-minipool-unbonded-max", func(w http.ResponseWriter, r *http.Request) { + max, err := parseUint64(r, "max") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeSettingMinipoolUnbondedMax(c, max) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-members-minipool-unbonded-max", func(w http.ResponseWriter, r *http.Request) { + max, err := parseUint64(r, "max") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingMinipoolUnbondedMax(c, max) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-proposal-cooldown", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeSettingProposalCooldown(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-proposal-cooldown", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingProposalCooldown(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-proposal-vote-timespan", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeSettingProposalVoteTimespan(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-proposal-vote-timespan", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingProposalVoteTimespan(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-proposal-vote-delay-timespan", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeSettingProposalVoteDelayTimespan(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-proposal-vote-delay-timespan", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingProposalVoteDelayTimespan(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-proposal-execute-timespan", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeSettingProposalExecuteTimespan(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-proposal-execute-timespan", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingProposalExecuteTimespan(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-proposal-action-timespan", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeSettingProposalActionTimespan(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-proposal-action-timespan", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingProposalActionTimespan(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-scrub-period", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeSettingScrubPeriod(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-scrub-period", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingScrubPeriod(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-promotion-scrub-period", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeSettingPromotionScrubPeriod(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-promotion-scrub-period", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingPromotionScrubPeriod(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-scrub-penalty-enabled", func(w http.ResponseWriter, r *http.Request) { + enabledStr := r.URL.Query().Get("enabled") + resp, err := canProposeSettingScrubPenaltyEnabled(c, enabledStr == "true") + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-scrub-penalty-enabled", func(w http.ResponseWriter, r *http.Request) { + enabledStr := r.FormValue("enabled") + resp, err := proposeSettingScrubPenaltyEnabled(c, enabledStr == "true") + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-bond-reduction-window-start", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeSettingBondReductionWindowStart(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-bond-reduction-window-start", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingBondReductionWindowStart(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/can-propose-bond-reduction-window-length", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeSettingBondReductionWindowLength(c, val) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/odao/propose-bond-reduction-window-length", func(w http.ResponseWriter, r *http.Request) { + val, err := parseUint64(r, "value") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingBondReductionWindowLength(c, val) + apiutils.WriteResponse(w, resp, err) + }) +} + +func parseUint64(r *http.Request, name string) (uint64, error) { + raw := r.URL.Query().Get(name) + if raw == "" { + raw = r.FormValue(name) + } + val, err := strconv.ParseUint(raw, 10, 64) + if err != nil { + return 0, fmt.Errorf("invalid %s: %s", name, raw) + } + return val, nil +} + +func parseFloat64(r *http.Request, name string) (float64, error) { + raw := r.URL.Query().Get(name) + if raw == "" { + raw = r.FormValue(name) + } + val, err := strconv.ParseFloat(raw, 64) + if err != nil { + return 0, fmt.Errorf("invalid %s: %s", name, raw) + } + return val, nil +} + +func parseBigInt(r *http.Request, name string) (*big.Int, error) { + raw := r.URL.Query().Get(name) + if raw == "" { + raw = r.FormValue(name) + } + val, ok := new(big.Int).SetString(raw, 10) + if !ok { + return nil, fmt.Errorf("invalid %s: %s", name, raw) + } + return val, nil +} + +func parseInviteParams(r *http.Request) (common.Address, string, string, error) { + addrStr := r.URL.Query().Get("address") + if addrStr == "" { + addrStr = r.FormValue("address") + } + if addrStr == "" { + return common.Address{}, "", "", fmt.Errorf("missing required parameter: address") + } + memberId := r.URL.Query().Get("memberId") + if memberId == "" { + memberId = r.FormValue("memberId") + } + memberUrl := r.URL.Query().Get("memberUrl") + if memberUrl == "" { + memberUrl = r.FormValue("memberUrl") + } + return common.HexToAddress(addrStr), memberId, memberUrl, nil +} + +func parseKickParams(r *http.Request) (common.Address, *big.Int, error) { + addrStr := r.URL.Query().Get("address") + if addrStr == "" { + addrStr = r.FormValue("address") + } + if addrStr == "" { + return common.Address{}, nil, fmt.Errorf("missing required parameter: address") + } + fineStr := r.URL.Query().Get("fineAmountWei") + if fineStr == "" { + fineStr = r.FormValue("fineAmountWei") + } + fine, ok := new(big.Int).SetString(fineStr, 10) + if !ok { + return common.Address{}, nil, fmt.Errorf("invalid fineAmountWei: %s", fineStr) + } + return common.HexToAddress(addrStr), fine, nil +} + +func parsePenaliseParams(r *http.Request) (common.Address, *big.Int, *big.Int, error) { + addrStr := r.URL.Query().Get("megapoolAddress") + if addrStr == "" { + addrStr = r.FormValue("megapoolAddress") + } + blockStr := r.URL.Query().Get("block") + if blockStr == "" { + blockStr = r.FormValue("block") + } + amountStr := r.URL.Query().Get("amountWei") + if amountStr == "" { + amountStr = r.FormValue("amountWei") + } + block, ok := new(big.Int).SetString(blockStr, 10) + if !ok { + return common.Address{}, nil, nil, fmt.Errorf("invalid block: %s", blockStr) + } + amount, ok := new(big.Int).SetString(amountStr, 10) + if !ok { + return common.Address{}, nil, nil, fmt.Errorf("invalid amountWei: %s", amountStr) + } + return common.HexToAddress(addrStr), block, amount, nil +} diff --git a/rocketpool/api/pdao/commands.go b/rocketpool/api/pdao/commands.go deleted file mode 100644 index e51180145..000000000 --- a/rocketpool/api/pdao/commands.go +++ /dev/null @@ -1,1151 +0,0 @@ -package pdao - -import ( - "fmt" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/urfave/cli" - - "github.com/rocket-pool/smartnode/shared/utils/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Subcommands = append(command.Subcommands, cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the Rocket Pool protocol DAO", - Subcommands: []cli.Command{ - - { - Name: "proposals", - Aliases: []string{"p"}, - Usage: "Get the protocol DAO proposals", - UsageText: "rocketpool api pdao proposals", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getProposals(c)) - return nil - - }, - }, - - { - Name: "proposal-details", - Aliases: []string{"d"}, - Usage: "Get details of a proposal", - UsageText: "rocketpool api pdao proposal-details proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - var err error - if err = cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - id, err := cliutils.ValidateUint("proposal-id", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(getProposal(c, id)) - return nil - - }, - }, - - { - Name: "can-vote-proposal", - Usage: "Check whether the node can vote on a proposal", - UsageText: "rocketpool api pdao can-vote-proposal proposal-id vote-direction", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - voteDir, err := cliutils.ValidateVoteDirection("vote direction", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canVoteOnProposal(c, proposalId, voteDir)) - return nil - - }, - }, - { - Name: "vote-proposal", - Aliases: []string{"v"}, - Usage: "Vote on a proposal", - UsageText: "rocketpool api pdao vote-proposal proposal-id vote-direction", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - voteDir, err := cliutils.ValidateVoteDirection("vote direction", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(voteOnProposal(c, proposalId, voteDir)) - return nil - - }, - }, - - { - Name: "can-override-vote", - Usage: "Check whether the node can override their delegate's vote on a proposal", - UsageText: "rocketpool api pdao can-override-vote proposal-id vote-direction", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - voteDir, err := cliutils.ValidateVoteDirection("vote direction", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canOverrideVote(c, proposalId, voteDir)) - return nil - - }, - }, - { - Name: "override-vote", - Usage: "Override the vote of the node's delegate on a proposal", - UsageText: "rocketpool api pdao override-vote proposal-id vote-direction", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - voteDir, err := cliutils.ValidateVoteDirection("vote direction", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(overrideVote(c, proposalId, voteDir)) - return nil - - }, - }, - - { - Name: "can-execute-proposal", - Usage: "Check whether the node can execute a proposal", - UsageText: "rocketpool api pdao can-execute-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canExecuteProposal(c, proposalId)) - return nil - - }, - }, - { - Name: "execute-proposal", - Aliases: []string{"x"}, - Usage: "Execute a proposal", - UsageText: "rocketpool api pdao execute-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(executeProposal(c, proposalId)) - return nil - - }, - }, - - { - Name: "get-settings", - Usage: "Get the Protocol DAO settings", - UsageText: "rocketpool api pdao get-member-settings", - Action: func(c *cli.Context) error { - - // Run - api.PrintResponse(getSettings(c)) - return nil - - }, - }, - - { - Name: "can-propose-setting", - Usage: "Check whether the node can propose a PDAO setting", - UsageText: "rocketpool api pdao can-propose-setting contract-name setting-name value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - contractName := c.Args().Get(0) - settingName := c.Args().Get(1) - value := c.Args().Get(2) - - // Run - api.PrintResponse(canProposeSetting(c, contractName, settingName, value)) - return nil - - }, - }, - { - Name: "propose-setting", - Usage: "Propose updating a PDAO setting (use can-propose-setting to get the pollard)", - UsageText: "rocketpool api pdao propose-setting contract-name setting-name value block-number", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 4); err != nil { - return err - } - contractName := c.Args().Get(0) - settingName := c.Args().Get(1) - value := c.Args().Get(2) - blockNumber, err := cliutils.ValidatePositiveUint32("block-number", c.Args().Get(3)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeSetting(c, contractName, settingName, value, blockNumber)) - return nil - - }, - }, - - { - Name: "get-rewards-percentages", - Usage: "Get the allocation percentages of RPL rewards for the Oracle DAO, the Protocol DAO, and the node operators", - UsageText: "rocketpool api pdao get-rewards-percentages", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getRewardsPercentages(c)) - return nil - - }, - }, - { - Name: "can-propose-rewards-percentages", - Usage: "Check whether the node can propose new RPL rewards allocation percentages for the Oracle DAO, the Protocol DAO, and the node operators", - UsageText: "rocketpool api pdao can-propose-rewards-percentages node odao pdao", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - node, err := cliutils.ValidateBigInt("node", c.Args().Get(0)) - if err != nil { - return err - } - odao, err := cliutils.ValidateBigInt("odao", c.Args().Get(1)) - if err != nil { - return err - } - pdao, err := cliutils.ValidateBigInt("pdao", c.Args().Get(2)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeRewardsPercentages(c, node, odao, pdao)) - return nil - - }, - }, - { - Name: "propose-rewards-percentages", - Usage: "Propose new RPL rewards allocation percentages for the Oracle DAO, the Protocol DAO, and the node operators", - UsageText: "rocketpool api pdao propose-rewards-percentages node odao pdao block-number", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 4); err != nil { - return err - } - node, err := cliutils.ValidateBigInt("node", c.Args().Get(0)) - if err != nil { - return err - } - odao, err := cliutils.ValidateBigInt("odao", c.Args().Get(1)) - if err != nil { - return err - } - pdao, err := cliutils.ValidateBigInt("pdao", c.Args().Get(2)) - if err != nil { - return err - } - blockNumber, err := cliutils.ValidateUint32("blockNumber", c.Args().Get(3)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeRewardsPercentages(c, node, odao, pdao, blockNumber)) - return nil - - }, - }, - - { - Name: "can-propose-one-time-spend", - Usage: "Check whether the node can propose a one-time spend of the Protocol DAO's treasury", - UsageText: "rocketpool api pdao can-propose-one-time-spend invoice-id recipient amount custom-message", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 4); err != nil { - return err - } - invoiceID := c.Args().Get(0) - recipient, err := cliutils.ValidateAddress("recipient", c.Args().Get(1)) - if err != nil { - return err - } - amount, err := cliutils.ValidateBigInt("amount", c.Args().Get(2)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeOneTimeSpend(c, invoiceID, recipient, amount, c.Args().Get(3))) - return nil - - }, - }, - { - Name: "propose-one-time-spend", - Usage: "Propose a one-time spend of the Protocol DAO's treasury", - UsageText: "rocketpool api pdao propose-one-time-spend invoice-id recipient amount block-number custom-message", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 5); err != nil { - return err - } - invoiceID := c.Args().Get(0) - recipient, err := cliutils.ValidateAddress("recipient", c.Args().Get(1)) - if err != nil { - return err - } - amount, err := cliutils.ValidateBigInt("amount", c.Args().Get(2)) - if err != nil { - return err - } - blockNumber, err := cliutils.ValidateUint32("blockNumber", c.Args().Get(3)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeOneTimeSpend(c, invoiceID, recipient, amount, blockNumber, c.Args().Get(4))) - return nil - - }, - }, - - { - Name: "can-propose-recurring-spend", - Usage: "Check whether the node can propose a recurring spend of the Protocol DAO's treasury", - UsageText: "rocketpool api pdao can-propose-recurring-spend contract-name recipient amount-per-period period-length start-time number-of-periods custom-message", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 7); err != nil { - return err - } - contractName := c.Args().Get(0) - recipient, err := cliutils.ValidateAddress("recipient", c.Args().Get(1)) - if err != nil { - return err - } - amountPerPeriod, err := cliutils.ValidateBigInt("amount-per-period", c.Args().Get(2)) - if err != nil { - return err - } - periodLength, err := cliutils.ValidateDuration("period-length", c.Args().Get(3)) - if err != nil { - return err - } - startTime, err := cliutils.ValidatePositiveUint("start-time", c.Args().Get(4)) - if err != nil { - return err - } - numberOfPeriods, err := cliutils.ValidatePositiveUint("number-of-periods", c.Args().Get(5)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeRecurringSpend(c, contractName, recipient, amountPerPeriod, periodLength, time.Unix(int64(startTime), 0), numberOfPeriods, c.Args().Get(6))) - return nil - - }, - }, - { - Name: "propose-recurring-spend", - Usage: "Propose a recurring spend of the Protocol DAO's treasury", - UsageText: "rocketpool api pdao propose-recurring-spend contract-name recipient amount-per-period period-length start-time number-of-periods block-number custom-message", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 8); err != nil { - return err - } - contractName := c.Args().Get(0) - recipient, err := cliutils.ValidateAddress("recipient", c.Args().Get(1)) - if err != nil { - return err - } - amountPerPeriod, err := cliutils.ValidateBigInt("amount-per-period", c.Args().Get(2)) - if err != nil { - return err - } - periodLength, err := cliutils.ValidateDuration("period-length", c.Args().Get(3)) - if err != nil { - return err - } - startTime, err := cliutils.ValidatePositiveUint("start-time", c.Args().Get(4)) - if err != nil { - return err - } - numberOfPeriods, err := cliutils.ValidatePositiveUint("number-of-periods", c.Args().Get(5)) - if err != nil { - return err - } - blockNumber, err := cliutils.ValidateUint32("blockNumber", c.Args().Get(6)) - if err != nil { - return err - } - // Run - api.PrintResponse(proposeRecurringSpend(c, contractName, recipient, amountPerPeriod, periodLength, time.Unix(int64(startTime), 0), numberOfPeriods, blockNumber, c.Args().Get(7))) - return nil - - }, - }, - - { - Name: "can-propose-recurring-spend-update", - Usage: "Check whether the node can propose an update to an existing recurring spend plan", - UsageText: "rocketpool api pdao can-propose-recurring-spend-update contract-name recipient amount-per-period period-length number-of-periods custom-message", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 6); err != nil { - return err - } - contractName := c.Args().Get(0) - recipient, err := cliutils.ValidateAddress("recipient", c.Args().Get(1)) - if err != nil { - return err - } - amountPerPeriod, err := cliutils.ValidateBigInt("amount-per-period", c.Args().Get(2)) - if err != nil { - return err - } - periodLength, err := cliutils.ValidateDuration("period-length", c.Args().Get(3)) - if err != nil { - return err - } - numberOfPeriods, err := cliutils.ValidatePositiveUint("number-of-periods", c.Args().Get(4)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeRecurringSpendUpdate(c, contractName, recipient, amountPerPeriod, periodLength, numberOfPeriods, c.Args().Get(5))) - return nil - - }, - }, - { - Name: "propose-recurring-spend-update", - Usage: "Propose an update to an existing recurring spend plan", - UsageText: "rocketpool api pdao propose-recurring-spend-update contract-name recipient amount-per-period period-length number-of-periods block-number custom-message", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 7); err != nil { - return err - } - contractName := c.Args().Get(0) - recipient, err := cliutils.ValidateAddress("recipient", c.Args().Get(1)) - if err != nil { - return err - } - amountPerPeriod, err := cliutils.ValidateBigInt("amount-per-period", c.Args().Get(2)) - if err != nil { - return err - } - periodLength, err := cliutils.ValidateDuration("period-length", c.Args().Get(3)) - if err != nil { - return err - } - numberOfPeriods, err := cliutils.ValidatePositiveUint("number-of-periods", c.Args().Get(4)) - if err != nil { - return err - } - blockNumber, err := cliutils.ValidateUint32("blockNumber", c.Args().Get(5)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeRecurringSpendUpdate(c, contractName, recipient, amountPerPeriod, periodLength, numberOfPeriods, blockNumber, c.Args().Get(6))) - return nil - - }, - }, - - { - Name: "can-propose-invite-to-security-council", - Usage: "Check whether the node can invite someone to the security council", - UsageText: "rocketpool api pdao can-propose-invite-to-security-council id address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - id := c.Args().Get(0) - address, err := cliutils.ValidateAddress("address", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeInviteToSecurityCouncil(c, id, address)) - return nil - - }, - }, - { - Name: "propose-invite-to-security-council", - Usage: "Propose inviting someone to the security council", - UsageText: "rocketpool api pdao propose-invite-to-security-council id address block-number", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - id := c.Args().Get(0) - address, err := cliutils.ValidateAddress("address", c.Args().Get(1)) - if err != nil { - return err - } - blockNumber, err := cliutils.ValidateUint32("blockNumber", c.Args().Get(2)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeInviteToSecurityCouncil(c, id, address, blockNumber)) - return nil - - }, - }, - - { - Name: "can-propose-kick-from-security-council", - Usage: "Check whether the node can kick someone from the security council", - UsageText: "rocketpool api pdao can-propose-kick-from-security-council address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - address, err := cliutils.ValidateAddress("address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeKickFromSecurityCouncil(c, address)) - return nil - - }, - }, - { - Name: "propose-kick-from-security-council", - Usage: "Propose kicking someone from the security council", - UsageText: "rocketpool api pdao propose-kick-from-security-council address block-number", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - address, err := cliutils.ValidateAddress("address", c.Args().Get(0)) - if err != nil { - return err - } - blockNumber, err := cliutils.ValidateUint32("blockNumber", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeKickFromSecurityCouncil(c, address, blockNumber)) - return nil - - }, - }, - - { - Name: "can-propose-kick-multi-from-security-council", - Usage: "Check whether the node can kick multiple members from the security council", - UsageText: "rocketpool api pdao can-propose-kick-multi-from-security-council addresses", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - addresses, err := cliutils.ValidateAddresses("address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeKickMultiFromSecurityCouncil(c, addresses)) - return nil - - }, - }, - { - Name: "propose-kick-multi-from-security-council", - Usage: "Propose kicking multiple members from the security council", - UsageText: "rocketpool api pdao propose-kick-multi-from-security-council addresses block-number", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - addresses, err := cliutils.ValidateAddresses("addresses", c.Args().Get(0)) - if err != nil { - return err - } - blockNumber, err := cliutils.ValidateUint32("blockNumber", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeKickMultiFromSecurityCouncil(c, addresses, blockNumber)) - return nil - - }, - }, - - { - Name: "can-propose-replace-member-of-security-council", - Usage: "Check whether the node can propose replacing someone on the security council with another member", - UsageText: "rocketpool api pdao can-propose-replace-member-of-security-council existing-address new-id new-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - existingAddress, err := cliutils.ValidateAddress("existingAddress", c.Args().Get(0)) - if err != nil { - return err - } - newID := c.Args().Get(1) - newAddress, err := cliutils.ValidateAddress("newAddress", c.Args().Get(2)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProposeReplaceMemberOfSecurityCouncil(c, existingAddress, newID, newAddress)) - return nil - - }, - }, - { - Name: "propose-replace-member-of-security-council", - Usage: "Propose replacing someone on the security council with another member", - UsageText: "rocketpool api pdao propose-replace-member-of-security-council existing-address new-id new-address block-number", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 4); err != nil { - return err - } - existingAddress, err := cliutils.ValidateAddress("existingAddress", c.Args().Get(0)) - if err != nil { - return err - } - newID := c.Args().Get(1) - newAddress, err := cliutils.ValidateAddress("newAddress", c.Args().Get(2)) - if err != nil { - return err - } - blockNumber, err := cliutils.ValidateUint32("blockNumber", c.Args().Get(3)) - if err != nil { - return err - } - - // Run - api.PrintResponse(proposeReplaceMemberOfSecurityCouncil(c, existingAddress, newID, newAddress, blockNumber)) - return nil - - }, - }, - - { - Name: "get-claimable-bonds", - Usage: "Get the list of proposals with claimable / rewardable bonds, and the relevant indices for each one", - UsageText: "rocketpool api pdao get-claimable-bonds", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getClaimableBonds(c)) - return nil - - }, - }, - - { - Name: "can-claim-bonds", - Usage: "Check whether the node can claim the bonds and/or rewards from a proposal", - UsageText: "rocketpool api pdao can-claim-bonds proposal-id tree-node-indices", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - indices, err := cliutils.ValidatePositiveUints("indices", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canClaimBonds(c, proposalId, indices)) - return nil - - }, - }, - { - Name: "claim-bonds", - Usage: "Claim the bonds and/or rewards from a proposal", - UsageText: "rocketpool api pdao claim-bonds is-proposer proposal-id tree-node-indice", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - isProposer, err := cliutils.ValidateBool("is-proposer", c.Args().Get(0)) - if err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal-id", c.Args().Get(1)) - if err != nil { - return err - } - indices, err := cliutils.ValidatePositiveUints("indices", c.Args().Get(2)) - if err != nil { - return err - } - - // Run - api.PrintResponse(claimBonds(c, isProposer, proposalId, indices)) - return nil - - }, - }, - - { - Name: "can-defeat-proposal", - Usage: "Check whether a proposal can be defeated with the provided tree index", - UsageText: "rocketpool api pdao can-defeat-proposal proposal-id challenged-index", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal-id", c.Args().Get(0)) - if err != nil { - return err - } - index, err := cliutils.ValidatePositiveUint("challenged-index", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canDefeatProposal(c, proposalId, index)) - return nil - - }, - }, - { - Name: "defeat-proposal", - Usage: "Defeat a proposal if it still has a challenge after voting has started", - UsageText: "rocketpool api pdao defeat-proposal proposal-id challenged-index", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal-id", c.Args().Get(0)) - if err != nil { - return err - } - index, err := cliutils.ValidatePositiveUint("challenged-index", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(defeatProposal(c, proposalId, index)) - return nil - - }, - }, - - { - Name: "can-finalize-proposal", - Usage: "Check whether a proposal can be finalized after being vetoed", - UsageText: "rocketpool api pdao can-finalize-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal-id", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canFinalizeProposal(c, proposalId)) - return nil - - }, - }, - { - Name: "finalize-proposal", - Usage: "Finalize a proposal if it's been vetoed by burning the proposer's bond", - UsageText: "rocketpool api pdao finalize-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal-id", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(finalizeProposal(c, proposalId)) - return nil - - }, - }, - { - Name: "estimate-set-voting-delegate-gas", - Usage: "Estimate the gas required to set an on-chain voting delegate", - UsageText: "rocketpool api pdao estimate-set-voting-delegate-gas address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - delegate, err := cliutils.ValidateAddress("delegate", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(estimateSetVotingDelegateGas(c, delegate)) - return nil - - }, - }, - { - Name: "set-voting-delegate", - Usage: "Set an on-chain voting delegate for the node", - UsageText: "rocketpool api pdao set-voting-delegate address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - delegate, err := cliutils.ValidateAddress("delegate", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(setVotingDelegate(c, delegate)) - return nil - - }, - }, - { - Name: "get-current-voting-delegate", - Usage: "Get the current on-chain voting delegate for the node", - UsageText: "rocketpool api pdao get-current-voting-delegate", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getCurrentVotingDelegate(c)) - return nil - - }, - }, - { - Name: "status", - Usage: "Get the pdao status", - UsageText: "rocketpool api pdao status", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getStatus(c)) - return nil - - }, - }, - { - Name: "can-set-signalling-address", - Usage: "Checks if signalling address can be set.", - UsageText: "rocketpool api pdao can-set-signalling-address signalling-address signature", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - signallingAddress, err := cliutils.ValidateAddress("signalling-address", c.Args().Get(0)) - if err != nil { - return err - } - signature, err := cliutils.ValidateSignature("signature", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canSetSignallingAddress(c, signallingAddress, signature)) - return nil - - }, - }, - { - Name: "set-signalling-address", - Usage: "Set the signalling address for the node", - UsageText: "rocketpool api pdao set-signalling-address signalling-address signature", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - signallingAddress, err := cliutils.ValidateAddress("signalling-address", c.Args().Get(0)) - if err != nil { - return err - } - signature, err := cliutils.ValidateSignature("signature", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(setSignallingAddress(c, signallingAddress, signature)) - return nil - - }, - }, - { - Name: "can-clear-signalling-address", - Usage: "Checks if the signalling address can be cleared.", - UsageText: "rocketpool api pdao can-clear-signalling-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - // Run - api.PrintResponse(canClearSignallingAddress(c)) - return nil - - }, - }, - { - Name: "clear-signalling-address", - Usage: "Clear the signalling delegate address for the node", - UsageText: "rocketpool api pdao clear-signalling-address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - // Run - api.PrintResponse(clearSignallingAddress(c)) - return nil - - }, - }, - { - Name: "can-propose-allow-listed-controllers", - Usage: "Check whether the node can propose a list of addresses that can update commission share parameters", - UsageText: "rocketpool api pdao can-propose-allow-listed-controllers addresses", - Action: func(c *cli.Context) error { - - // Validate args - if c.NArg() > 2 { - return fmt.Errorf("Incorrect argument count;") - } - - var addressList []common.Address - var err error - if c.NArg() != 0 { - addressList, err = cliutils.ValidateAddresses("addressList", c.Args().Get(0)) - if err != nil { - return err - } - } - // Run - api.PrintResponse(canProposeAllowListedControllers(c, addressList)) - return nil - - }, - }, - { - Name: "propose-allow-listed-controllers", - Usage: "Propose a list of addresses that can update commission share parameters", - UsageText: "rocketpool api pdao propose-allow-listed-controllers addresses block-number", - Action: func(c *cli.Context) error { - - // Validate args - if c.NArg() > 3 || c.NArg() < 1 { - return fmt.Errorf("Incorrect argument count;") - } - - var addressList []common.Address - var blockNumber uint32 - var err error - - // Handles a case where an empty addressList is passed, to propose an empty addressList - if c.NArg() == 1 { - blockNumber, err = cliutils.ValidateUint32("blockNumber", c.Args().Get(0)) - if err != nil { - return err - } - } else { - addressList, err = cliutils.ValidateAddresses("addressList", c.Args().Get(0)) - if err != nil { - return err - } - blockNumber, err = cliutils.ValidateUint32("blockNumber", c.Args().Get(1)) - if err != nil { - return err - } - } - - // Run - api.PrintResponse(proposeAllowListedControllers(c, addressList, blockNumber)) - return nil - - }, - }, - }, - }) -} diff --git a/rocketpool/api/pdao/routes.go b/rocketpool/api/pdao/routes.go new file mode 100644 index 000000000..6825c5d10 --- /dev/null +++ b/rocketpool/api/pdao/routes.go @@ -0,0 +1,607 @@ +package pdao + +import ( + "fmt" + "math/big" + "net/http" + "strconv" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/urfave/cli" + + bindtypes "github.com/rocket-pool/smartnode/bindings/types" + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" + cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" +) + +// RegisterRoutes registers the pdao module's HTTP routes onto mux. +func RegisterRoutes(mux *http.ServeMux, c *cli.Context) { + mux.HandleFunc("/api/pdao/status", func(w http.ResponseWriter, r *http.Request) { + resp, err := getStatus(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/proposals", func(w http.ResponseWriter, r *http.Request) { + resp, err := getProposals(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/proposal-details", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64Param(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := getProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-vote-proposal", func(w http.ResponseWriter, r *http.Request) { + id, voteDir, err := parseProposalVoteParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canVoteOnProposal(c, id, voteDir) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/vote-proposal", func(w http.ResponseWriter, r *http.Request) { + id, voteDir, err := parseProposalVoteParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := voteOnProposal(c, id, voteDir) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-override-vote", func(w http.ResponseWriter, r *http.Request) { + id, voteDir, err := parseProposalVoteParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canOverrideVote(c, id, voteDir) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/override-vote", func(w http.ResponseWriter, r *http.Request) { + id, voteDir, err := parseProposalVoteParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := overrideVote(c, id, voteDir) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-execute-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64Param(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canExecuteProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/execute-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64Param(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := executeProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/get-settings", func(w http.ResponseWriter, r *http.Request) { + resp, err := getSettings(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-propose-setting", func(w http.ResponseWriter, r *http.Request) { + contract := paramVal(r, "contract") + setting := paramVal(r, "setting") + value := paramVal(r, "value") + resp, err := canProposeSetting(c, contract, setting, value) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/propose-setting", func(w http.ResponseWriter, r *http.Request) { + contract := paramVal(r, "contract") + setting := paramVal(r, "setting") + value := paramVal(r, "value") + blockNumber, err := parseUint32Param(r, "blockNumber") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSetting(c, contract, setting, value, blockNumber) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/get-rewards-percentages", func(w http.ResponseWriter, r *http.Request) { + resp, err := getRewardsPercentages(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-propose-rewards-percentages", func(w http.ResponseWriter, r *http.Request) { + node, odaoAmt, pdaoAmt, err := parseRewardPercentages(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeRewardsPercentages(c, node, odaoAmt, pdaoAmt) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/propose-rewards-percentages", func(w http.ResponseWriter, r *http.Request) { + node, odaoAmt, pdaoAmt, err := parseRewardPercentages(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + blockNumber, err := parseUint32Param(r, "blockNumber") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeRewardsPercentages(c, node, odaoAmt, pdaoAmt, blockNumber) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-propose-one-time-spend", func(w http.ResponseWriter, r *http.Request) { + invoiceID, recipient, amount, customMessage, err := parseOneTimeSpendParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeOneTimeSpend(c, invoiceID, recipient, amount, customMessage) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/propose-one-time-spend", func(w http.ResponseWriter, r *http.Request) { + invoiceID, recipient, amount, customMessage, err := parseOneTimeSpendParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + blockNumber, err := parseUint32Param(r, "blockNumber") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeOneTimeSpend(c, invoiceID, recipient, amount, blockNumber, customMessage) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-propose-recurring-spend", func(w http.ResponseWriter, r *http.Request) { + contractName, recipient, amountPerPeriod, periodLength, startTime, numberOfPeriods, customMessage, err := parseRecurringSpendParams(r, false) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeRecurringSpend(c, contractName, recipient, amountPerPeriod, periodLength, startTime, numberOfPeriods, customMessage) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/propose-recurring-spend", func(w http.ResponseWriter, r *http.Request) { + contractName, recipient, amountPerPeriod, periodLength, startTime, numberOfPeriods, customMessage, err := parseRecurringSpendParams(r, false) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + blockNumber, err := parseUint32Param(r, "blockNumber") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeRecurringSpend(c, contractName, recipient, amountPerPeriod, periodLength, startTime, numberOfPeriods, blockNumber, customMessage) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-propose-recurring-spend-update", func(w http.ResponseWriter, r *http.Request) { + contractName, recipient, amountPerPeriod, periodLength, _, numberOfPeriods, customMessage, err := parseRecurringSpendParams(r, true) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeRecurringSpendUpdate(c, contractName, recipient, amountPerPeriod, periodLength, numberOfPeriods, customMessage) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/propose-recurring-spend-update", func(w http.ResponseWriter, r *http.Request) { + contractName, recipient, amountPerPeriod, periodLength, _, numberOfPeriods, customMessage, err := parseRecurringSpendParams(r, true) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + blockNumber, err := parseUint32Param(r, "blockNumber") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeRecurringSpendUpdate(c, contractName, recipient, amountPerPeriod, periodLength, numberOfPeriods, blockNumber, customMessage) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-propose-invite-to-security-council", func(w http.ResponseWriter, r *http.Request) { + id := paramVal(r, "id") + addr := common.HexToAddress(paramVal(r, "address")) + resp, err := canProposeInviteToSecurityCouncil(c, id, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/propose-invite-to-security-council", func(w http.ResponseWriter, r *http.Request) { + id := paramVal(r, "id") + addr := common.HexToAddress(paramVal(r, "address")) + blockNumber, err := parseUint32Param(r, "blockNumber") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeInviteToSecurityCouncil(c, id, addr, blockNumber) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-propose-kick-from-security-council", func(w http.ResponseWriter, r *http.Request) { + addr := common.HexToAddress(paramVal(r, "address")) + resp, err := canProposeKickFromSecurityCouncil(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/propose-kick-from-security-council", func(w http.ResponseWriter, r *http.Request) { + addr := common.HexToAddress(paramVal(r, "address")) + blockNumber, err := parseUint32Param(r, "blockNumber") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeKickFromSecurityCouncil(c, addr, blockNumber) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-propose-kick-multi-from-security-council", func(w http.ResponseWriter, r *http.Request) { + addresses, err := parseAddressList(r, "addresses") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProposeKickMultiFromSecurityCouncil(c, addresses) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/propose-kick-multi-from-security-council", func(w http.ResponseWriter, r *http.Request) { + addresses, err := parseAddressList(r, "addresses") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + blockNumber, err := parseUint32Param(r, "blockNumber") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeKickMultiFromSecurityCouncil(c, addresses, blockNumber) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-propose-replace-member-of-security-council", func(w http.ResponseWriter, r *http.Request) { + existing := common.HexToAddress(paramVal(r, "existingAddress")) + newID := paramVal(r, "newId") + newAddr := common.HexToAddress(paramVal(r, "newAddress")) + resp, err := canProposeReplaceMemberOfSecurityCouncil(c, existing, newID, newAddr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/propose-replace-member-of-security-council", func(w http.ResponseWriter, r *http.Request) { + existing := common.HexToAddress(paramVal(r, "existingAddress")) + newID := paramVal(r, "newId") + newAddr := common.HexToAddress(paramVal(r, "newAddress")) + blockNumber, err := parseUint32Param(r, "blockNumber") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeReplaceMemberOfSecurityCouncil(c, existing, newID, newAddr, blockNumber) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/get-claimable-bonds", func(w http.ResponseWriter, r *http.Request) { + resp, err := getClaimableBonds(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-claim-bonds", func(w http.ResponseWriter, r *http.Request) { + proposalID, indices, err := parseClaimBondsParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canClaimBonds(c, proposalID, indices) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/claim-bonds", func(w http.ResponseWriter, r *http.Request) { + proposalID, indices, err := parseClaimBondsParams(r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + isProposer := paramVal(r, "isProposer") == "true" + resp, err := claimBonds(c, isProposer, proposalID, indices) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-defeat-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64Param(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + index, err := parseUint64Param(r, "index") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canDefeatProposal(c, id, index) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/defeat-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64Param(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + index, err := parseUint64Param(r, "index") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := defeatProposal(c, id, index) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-finalize-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64Param(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canFinalizeProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/finalize-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64Param(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := finalizeProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/estimate-set-voting-delegate-gas", func(w http.ResponseWriter, r *http.Request) { + addr := common.HexToAddress(paramVal(r, "address")) + resp, err := estimateSetVotingDelegateGas(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/set-voting-delegate", func(w http.ResponseWriter, r *http.Request) { + addr := common.HexToAddress(paramVal(r, "address")) + resp, err := setVotingDelegate(c, addr) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/get-current-voting-delegate", func(w http.ResponseWriter, r *http.Request) { + resp, err := getCurrentVotingDelegate(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-set-signalling-address", func(w http.ResponseWriter, r *http.Request) { + addr := common.HexToAddress(paramVal(r, "address")) + sig := paramVal(r, "signature") + resp, err := canSetSignallingAddress(c, addr, sig) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/set-signalling-address", func(w http.ResponseWriter, r *http.Request) { + addr := common.HexToAddress(paramVal(r, "address")) + sig := paramVal(r, "signature") + resp, err := setSignallingAddress(c, addr, sig) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-clear-signalling-address", func(w http.ResponseWriter, r *http.Request) { + resp, err := canClearSignallingAddress(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/clear-signalling-address", func(w http.ResponseWriter, r *http.Request) { + resp, err := clearSignallingAddress(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/can-propose-allow-listed-controllers", func(w http.ResponseWriter, r *http.Request) { + addressList := paramVal(r, "addressList") + addresses, err := parseAddressList(r, "addressList") + if err != nil { + // Fall back to the raw comma-separated string if address parsing fails + addresses = parseRawAddressList(addressList) + } + resp, err := canProposeAllowListedControllers(c, addresses) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/pdao/propose-allow-listed-controllers", func(w http.ResponseWriter, r *http.Request) { + addressList := paramVal(r, "addressList") + addresses, err := parseAddressList(r, "addressList") + if err != nil { + addresses = parseRawAddressList(addressList) + } + blockNumber, err := parseUint32Param(r, "blockNumber") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeAllowListedControllers(c, addresses, blockNumber) + apiutils.WriteResponse(w, resp, err) + }) +} + +func paramVal(r *http.Request, name string) string { + v := r.URL.Query().Get(name) + if v == "" { + v = r.FormValue(name) + } + return v +} + +func parseUint64Param(r *http.Request, name string) (uint64, error) { + raw := paramVal(r, name) + val, err := strconv.ParseUint(raw, 10, 64) + if err != nil { + return 0, fmt.Errorf("invalid %s: %s", name, raw) + } + return val, nil +} + +func parseUint32Param(r *http.Request, name string) (uint32, error) { + raw := paramVal(r, name) + val, err := strconv.ParseUint(raw, 10, 32) + if err != nil { + return 0, fmt.Errorf("invalid %s: %s", name, raw) + } + return uint32(val), nil +} + +func parseProposalVoteParams(r *http.Request) (uint64, bindtypes.VoteDirection, error) { + id, err := parseUint64Param(r, "id") + if err != nil { + return 0, 0, err + } + dirStr := paramVal(r, "voteDirection") + dir, err := cliutils.ValidateVoteDirection("voteDirection", dirStr) + if err != nil { + return 0, 0, err + } + return id, dir, nil +} + +func parseRewardPercentages(r *http.Request) (*big.Int, *big.Int, *big.Int, error) { + nodeStr := paramVal(r, "node") + odaoStr := paramVal(r, "odao") + pdaoStr := paramVal(r, "pdao") + + node, ok := new(big.Int).SetString(nodeStr, 10) + if !ok { + return nil, nil, nil, fmt.Errorf("invalid node percentage: %s", nodeStr) + } + odaoAmt, ok := new(big.Int).SetString(odaoStr, 10) + if !ok { + return nil, nil, nil, fmt.Errorf("invalid odao percentage: %s", odaoStr) + } + pdaoAmt, ok := new(big.Int).SetString(pdaoStr, 10) + if !ok { + return nil, nil, nil, fmt.Errorf("invalid pdao percentage: %s", pdaoStr) + } + return node, odaoAmt, pdaoAmt, nil +} + +func parseOneTimeSpendParams(r *http.Request) (string, common.Address, *big.Int, string, error) { + invoiceID := paramVal(r, "invoiceId") + recipient := common.HexToAddress(paramVal(r, "recipient")) + amountStr := paramVal(r, "amount") + amount, ok := new(big.Int).SetString(amountStr, 10) + if !ok { + return "", common.Address{}, nil, "", fmt.Errorf("invalid amount: %s", amountStr) + } + customMessage := paramVal(r, "customMessage") + return invoiceID, recipient, amount, customMessage, nil +} + +// parseRecurringSpendParams parses recurring spend parameters. +// If skipStartTime is true, the startTime is omitted (for update operations). +func parseRecurringSpendParams(r *http.Request, skipStartTime bool) (string, common.Address, *big.Int, time.Duration, time.Time, uint64, string, error) { + contractName := paramVal(r, "contractName") + recipient := common.HexToAddress(paramVal(r, "recipient")) + + amountStr := paramVal(r, "amountPerPeriod") + amount, ok := new(big.Int).SetString(amountStr, 10) + if !ok { + return "", common.Address{}, nil, 0, time.Time{}, 0, "", fmt.Errorf("invalid amountPerPeriod: %s", amountStr) + } + + periodLengthStr := paramVal(r, "periodLength") + periodLength, err := time.ParseDuration(periodLengthStr) + if err != nil { + return "", common.Address{}, nil, 0, time.Time{}, 0, "", fmt.Errorf("invalid periodLength: %s", periodLengthStr) + } + + var startTime time.Time + if !skipStartTime { + startTimeStr := paramVal(r, "startTime") + startTimeUnix, err := strconv.ParseInt(startTimeStr, 10, 64) + if err != nil { + return "", common.Address{}, nil, 0, time.Time{}, 0, "", fmt.Errorf("invalid startTime: %s", startTimeStr) + } + startTime = time.Unix(startTimeUnix, 0) + } + + numberOfPeriodsStr := paramVal(r, "numberOfPeriods") + numberOfPeriods, err := strconv.ParseUint(numberOfPeriodsStr, 10, 64) + if err != nil { + return "", common.Address{}, nil, 0, time.Time{}, 0, "", fmt.Errorf("invalid numberOfPeriods: %s", numberOfPeriodsStr) + } + + customMessage := paramVal(r, "customMessage") + return contractName, recipient, amount, periodLength, startTime, numberOfPeriods, customMessage, nil +} + +func parseAddressList(r *http.Request, name string) ([]common.Address, error) { + raw := paramVal(r, name) + if raw == "" { + return nil, fmt.Errorf("missing required parameter: %s", name) + } + return parseRawAddressList(raw), nil +} + +func parseRawAddressList(raw string) []common.Address { + parts := strings.Split(raw, ",") + addresses := make([]common.Address, 0, len(parts)) + for _, p := range parts { + p = strings.TrimSpace(p) + if p != "" { + addresses = append(addresses, common.HexToAddress(p)) + } + } + return addresses +} + +func parseClaimBondsParams(r *http.Request) (uint64, []uint64, error) { + proposalID, err := parseUint64Param(r, "proposalId") + if err != nil { + return 0, nil, err + } + indicesStr := paramVal(r, "indices") + parts := strings.Split(indicesStr, ",") + indices := make([]uint64, 0, len(parts)) + for _, p := range parts { + p = strings.TrimSpace(p) + if p == "" { + continue + } + idx, err := strconv.ParseUint(p, 10, 64) + if err != nil { + return 0, nil, fmt.Errorf("invalid index: %s", p) + } + indices = append(indices, idx) + } + return proposalID, indices, nil +} diff --git a/rocketpool/api/queue/commands.go b/rocketpool/api/queue/commands.go deleted file mode 100644 index 078ed76e7..000000000 --- a/rocketpool/api/queue/commands.go +++ /dev/null @@ -1,147 +0,0 @@ -package queue - -import ( - "github.com/urfave/cli" - - "github.com/rocket-pool/smartnode/shared/utils/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Subcommands = append(command.Subcommands, cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the Rocket Pool deposit queue", - Subcommands: []cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get the deposit pool and minipool queue status", - UsageText: "rocketpool api queue status", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getStatus(c)) - return nil - - }, - }, - - { - Name: "can-process", - Usage: "Check whether the deposit pool can be processed", - UsageText: "rocketpool api queue can-process max-validators", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - max, err := cliutils.ValidatePositiveUint32("max-validators", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canProcessQueue(c, int64(max))) - return nil - - }, - }, - { - Name: "process", - Aliases: []string{"p"}, - Usage: "Process the deposit pool", - UsageText: "rocketpool api queue process max-validators", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - max, err := cliutils.ValidatePositiveUint32("max-validators", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(processQueue(c, int64(max))) - return nil - - }, - }, - { - Name: "get-queue-details", - Usage: "Gets queue details.", - UsageText: "rocketpool api queue get-queue-details", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getQueueDetails(c)) - return nil - - }, - }, - - { - Name: "can-assign-deposits", - Usage: "Check whether deposits can be assigned", - UsageText: "rocketpool api queue can-assign-deposits max", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - max, err := cliutils.ValidatePositiveUint32("max", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canAssignDeposits(c, int64(max))) - return nil - - }, - }, - { - Name: "assign-deposits", - Aliases: []string{"ad"}, - Usage: "Assign deposits to queued validators", - UsageText: "rocketpool api queue assign-deposits max", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - max, err := cliutils.ValidatePositiveUint32("max", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(assignDeposits(c, int64(max))) - return nil - - }, - }, - }, - }) -} diff --git a/rocketpool/api/queue/routes.go b/rocketpool/api/queue/routes.go new file mode 100644 index 000000000..bb15ddcce --- /dev/null +++ b/rocketpool/api/queue/routes.go @@ -0,0 +1,75 @@ +package queue + +import ( + "net/http" + "strconv" + + "github.com/urfave/cli" + + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +// RegisterRoutes registers the queue module's HTTP routes onto mux. +func RegisterRoutes(mux *http.ServeMux, c *cli.Context) { + mux.HandleFunc("/api/queue/status", func(w http.ResponseWriter, r *http.Request) { + resp, err := getStatus(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/queue/can-process", func(w http.ResponseWriter, r *http.Request) { + max, err := parseUint32Param(r, "max") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canProcessQueue(c, int64(max)) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/queue/process", func(w http.ResponseWriter, r *http.Request) { + max, err := parseUint32Param(r, "max") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := processQueue(c, int64(max)) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/queue/get-queue-details", func(w http.ResponseWriter, r *http.Request) { + resp, err := getQueueDetails(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/queue/can-assign-deposits", func(w http.ResponseWriter, r *http.Request) { + max, err := parseUint32Param(r, "max") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canAssignDeposits(c, int64(max)) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/queue/assign-deposits", func(w http.ResponseWriter, r *http.Request) { + max, err := parseUint32Param(r, "max") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := assignDeposits(c, int64(max)) + apiutils.WriteResponse(w, resp, err) + }) +} + +func parseUint32Param(r *http.Request, name string) (uint32, error) { + raw := r.URL.Query().Get(name) + if raw == "" { + raw = r.FormValue(name) + } + v, err := strconv.ParseUint(raw, 10, 32) + if err != nil { + return 0, err + } + return uint32(v), nil +} diff --git a/rocketpool/api/security/commands.go b/rocketpool/api/security/commands.go deleted file mode 100644 index bb6e7a456..000000000 --- a/rocketpool/api/security/commands.go +++ /dev/null @@ -1,417 +0,0 @@ -package security - -import ( - "github.com/urfave/cli" - - "github.com/rocket-pool/smartnode/shared/utils/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Subcommands = append(command.Subcommands, cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the Rocket Pool security council", - Subcommands: []cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get security council status", - UsageText: "rocketpool api security status", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getStatus(c)) - return nil - - }, - }, - { - Name: "members", - Aliases: []string{"m"}, - Usage: "Get the security council members", - UsageText: "rocketpool api security members", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getMembers(c)) - return nil - - }, - }, - - { - Name: "proposals", - Aliases: []string{"p"}, - Usage: "Get the security council proposals", - UsageText: "rocketpool api security proposals", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getProposals(c)) - return nil - - }, - }, - - { - Name: "proposal-details", - Aliases: []string{"d"}, - Usage: "Get details of a proposal", - UsageText: "rocketpool api security proposal-details proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - var err error - if err = cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - id, err := cliutils.ValidateUint("proposal-id", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(getProposal(c, id)) - return nil - - }, - }, - { - Name: "can-propose-leave", - Usage: "Check whether the node can propose leaving the security council", - UsageText: "rocketpool api security can-propose-leave", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canProposeLeave(c)) - return nil - - }, - }, - { - Name: "propose-leave", - Aliases: []string{"l"}, - Usage: "Propose leaving the security council", - UsageText: "rocketpool api security propose-leave", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(proposeLeave(c)) - return nil - - }, - }, - { - Name: "can-leave", - Usage: "Check whether the node can leave the security council", - UsageText: "rocketpool api security can-leave", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canLeave(c)) - return nil - - }, - }, - { - Name: "leave", - Aliases: []string{"l"}, - Usage: "Leave the security council", - UsageText: "rocketpool api security propose-leave", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(leave(c)) - return nil - - }, - }, - { - Name: "can-cancel-proposal", - Usage: "Check whether the node can cancel a proposal", - UsageText: "rocketpool api security can-cancel-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canCancelProposal(c, proposalId)) - return nil - - }, - }, - { - Name: "cancel-proposal", - Aliases: []string{"c"}, - Usage: "Cancel a proposal made by the node", - UsageText: "rocketpool api security cancel-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(cancelProposal(c, proposalId)) - return nil - - }, - }, - - { - Name: "can-vote-proposal", - Usage: "Check whether the node can vote on a proposal", - UsageText: "rocketpool api security can-vote-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canVoteOnProposal(c, proposalId)) - return nil - - }, - }, - { - Name: "vote-proposal", - Aliases: []string{"v"}, - Usage: "Vote on a proposal", - UsageText: "rocketpool api security vote-proposal proposal-id support", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - support, err := cliutils.ValidateBool("support", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(voteOnProposal(c, proposalId, support)) - return nil - - }, - }, - - { - Name: "can-execute-proposal", - Usage: "Check whether the node can execute a proposal", - UsageText: "rocketpool api security can-execute-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canExecuteProposal(c, proposalId)) - return nil - - }, - }, - { - Name: "execute-proposal", - Aliases: []string{"x"}, - Usage: "Execute a proposal", - UsageText: "rocketpool api security execute-proposal proposal-id", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - proposalId, err := cliutils.ValidatePositiveUint("proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(executeProposal(c, proposalId)) - return nil - - }, - }, - - { - Name: "can-join", - Usage: "Check whether the node can join the security council", - UsageText: "rocketpool api security can-join", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canJoin(c)) - return nil - - }, - }, - { - Name: "join", - Aliases: []string{"j"}, - Usage: "Join the security council (requires an executed invite proposal)", - UsageText: "rocketpool api security join", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(join(c)) - return nil - - }, - }, - - { - Name: "can-leave", - Usage: "Check whether the node can leave the security council", - UsageText: "rocketpool api security can-leave", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(canLeave(c)) - return nil - - }, - }, - { - Name: "leave", - Aliases: []string{"e"}, - Usage: "Leave the security council (requires an executed leave proposal)", - UsageText: "rocketpool api security leave", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(leave(c)) - return nil - - }, - }, - - { - Name: "can-propose-setting", - Usage: "Check whether the node can propose a PDAO setting", - UsageText: "rocketpool api security can-propose-setting contract-name setting-name value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - contractName := c.Args().Get(0) - settingName := c.Args().Get(1) - value := c.Args().Get(2) - - // Run - api.PrintResponse(canProposeSetting(c, contractName, settingName, value)) - return nil - - }, - }, - { - Name: "propose-setting", - Usage: "Propose updating a PDAO setting", - UsageText: "rocketpool api security propose-setting contract-name setting-name value", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 3); err != nil { - return err - } - contractName := c.Args().Get(0) - settingName := c.Args().Get(1) - value := c.Args().Get(2) - - // Run - api.PrintResponse(proposeSetting(c, contractName, settingName, value)) - return nil - - }, - }, - }, - }) -} diff --git a/rocketpool/api/security/routes.go b/rocketpool/api/security/routes.go new file mode 100644 index 000000000..b82e3d787 --- /dev/null +++ b/rocketpool/api/security/routes.go @@ -0,0 +1,153 @@ +package security + +import ( + "net/http" + "strconv" + + "github.com/urfave/cli" + + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +// RegisterRoutes registers the security module's HTTP routes onto mux. +func RegisterRoutes(mux *http.ServeMux, c *cli.Context) { + mux.HandleFunc("/api/security/status", func(w http.ResponseWriter, r *http.Request) { + resp, err := getStatus(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/members", func(w http.ResponseWriter, r *http.Request) { + resp, err := getMembers(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/proposals", func(w http.ResponseWriter, r *http.Request) { + resp, err := getProposals(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/proposal-details", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := getProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/can-propose-leave", func(w http.ResponseWriter, r *http.Request) { + resp, err := canProposeLeave(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/propose-leave", func(w http.ResponseWriter, r *http.Request) { + resp, err := proposeLeave(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/can-propose-setting", func(w http.ResponseWriter, r *http.Request) { + contractName := r.URL.Query().Get("contractName") + settingName := r.URL.Query().Get("settingName") + value := r.URL.Query().Get("value") + resp, err := canProposeSetting(c, contractName, settingName, value) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/propose-setting", func(w http.ResponseWriter, r *http.Request) { + contractName := r.FormValue("contractName") + settingName := r.FormValue("settingName") + value := r.FormValue("value") + resp, err := proposeSetting(c, contractName, settingName, value) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/can-cancel-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canCancelProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/cancel-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := cancelProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/can-vote-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canVoteOnProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/vote-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + support := r.FormValue("support") == "true" + resp, err := voteOnProposal(c, id, support) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/can-execute-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := canExecuteProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/execute-proposal", func(w http.ResponseWriter, r *http.Request) { + id, err := parseUint64(r, "id") + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := executeProposal(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/can-join", func(w http.ResponseWriter, r *http.Request) { + resp, err := canJoin(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/join", func(w http.ResponseWriter, r *http.Request) { + resp, err := join(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/can-leave", func(w http.ResponseWriter, r *http.Request) { + resp, err := canLeave(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/security/leave", func(w http.ResponseWriter, r *http.Request) { + resp, err := leave(c) + apiutils.WriteResponse(w, resp, err) + }) +} + +func parseUint64(r *http.Request, name string) (uint64, error) { + raw := r.URL.Query().Get(name) + if raw == "" { + raw = r.FormValue(name) + } + return strconv.ParseUint(raw, 10, 64) +} diff --git a/rocketpool/api/service/commands.go b/rocketpool/api/service/commands.go deleted file mode 100644 index 790d787d9..000000000 --- a/rocketpool/api/service/commands.go +++ /dev/null @@ -1,74 +0,0 @@ -package service - -import ( - "github.com/urfave/cli" - - "github.com/rocket-pool/smartnode/shared/utils/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Subcommands = append(command.Subcommands, cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the Rocket Pool deposit queue", - Subcommands: []cli.Command{ - { - Name: "terminate-data-folder", - Aliases: []string{"t"}, - Usage: "Deletes the data folder including the wallet file, password file, and all validator keys - don't use this unless you have a very good reason to do it (such as switching from a Testnet to Mainnet)", - UsageText: "rocketpool api service terminate-data-folder", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(terminateDataFolder(c)) - return nil - - }, - }, - - { - Name: "get-client-status", - Aliases: []string{"g"}, - Usage: "Gets the status of the configured Execution and Beacon clients", - UsageText: "rocketpool api service get-client-status", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getClientStatus(c)) - return nil - - }, - }, - - { - Name: "restart-vc", - Usage: "Restarts the validator client", - UsageText: "rocketpool api service restart-vc", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(restartVc(c)) - return nil - - }, - }, - }, - }) -} diff --git a/rocketpool/api/service/routes.go b/rocketpool/api/service/routes.go new file mode 100644 index 000000000..7c49913bf --- /dev/null +++ b/rocketpool/api/service/routes.go @@ -0,0 +1,27 @@ +package service + +import ( + "net/http" + + "github.com/urfave/cli" + + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +// RegisterRoutes registers the service module's HTTP routes onto mux. +func RegisterRoutes(mux *http.ServeMux, c *cli.Context) { + mux.HandleFunc("/api/service/get-client-status", func(w http.ResponseWriter, r *http.Request) { + resp, err := getClientStatus(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/service/restart-vc", func(w http.ResponseWriter, r *http.Request) { + resp, err := restartVc(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/service/terminate-data-folder", func(w http.ResponseWriter, r *http.Request) { + resp, err := terminateDataFolder(c) + apiutils.WriteResponse(w, resp, err) + }) +} diff --git a/rocketpool/api/version.go b/rocketpool/api/version.go new file mode 100644 index 000000000..832e68741 --- /dev/null +++ b/rocketpool/api/version.go @@ -0,0 +1,22 @@ +package api + +import ( + "net/http" + + "github.com/rocket-pool/smartnode/shared" + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +type VersionResponse struct { + Status string `json:"status"` + Error string `json:"error"` + Version string `json:"version"` +} + +// RegisterVersionRoute registers the /api/version endpoint on mux. +func RegisterVersionRoute(mux *http.ServeMux) { + mux.HandleFunc("/api/version", func(w http.ResponseWriter, r *http.Request) { + response := VersionResponse{Version: shared.RocketPoolVersion()} + apiutils.WriteResponse(w, &response, nil) + }) +} diff --git a/rocketpool/api/wait.go b/rocketpool/api/wait.go new file mode 100644 index 000000000..5e5dd6207 --- /dev/null +++ b/rocketpool/api/wait.go @@ -0,0 +1,29 @@ +package api + +import ( + "net/http" + + "github.com/ethereum/go-ethereum/common" + "github.com/urfave/cli" + + "github.com/rocket-pool/smartnode/bindings/utils" + "github.com/rocket-pool/smartnode/shared/services" + apitypes "github.com/rocket-pool/smartnode/shared/types/api" + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +// RegisterWaitRoute registers the /api/wait endpoint on mux. +// It waits for a transaction hash to be mined. +func RegisterWaitRoute(mux *http.ServeMux, c *cli.Context) { + mux.HandleFunc("/api/wait", func(w http.ResponseWriter, r *http.Request) { + hash := common.HexToHash(r.URL.Query().Get("txHash")) + rp, err := services.GetRocketPool(c) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + response := apitypes.APIResponse{} + _, err = utils.WaitForTransaction(rp.Client, hash) + apiutils.WriteResponse(w, &response, err) + }) +} diff --git a/rocketpool/api/wallet/commands.go b/rocketpool/api/wallet/commands.go deleted file mode 100644 index 691986006..000000000 --- a/rocketpool/api/wallet/commands.go +++ /dev/null @@ -1,342 +0,0 @@ -package wallet - -import ( - "github.com/urfave/cli" - - "github.com/rocket-pool/smartnode/shared/utils/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Subcommands = append(command.Subcommands, cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the node wallet", - Subcommands: []cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get the node wallet status", - UsageText: "rocketpool api wallet status", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(getStatus(c)) - return nil - - }, - }, - - { - Name: "set-password", - Aliases: []string{"p"}, - Usage: "Set the node wallet password", - UsageText: "rocketpool api wallet set-password password", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - password, err := cliutils.ValidateNodePassword("wallet password", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(setPassword(c, password)) - return nil - - }, - }, - - { - Name: "init", - Aliases: []string{"i"}, - Usage: "Initialize the node wallet", - UsageText: "rocketpool api wallet init", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "derivation-path, d", - Usage: "Specify the derivation path for the wallet.\nOmit this flag (or leave it blank) for the default of \"m/44'/60'/0'/0/%d\" (where %d is the index).\nSet this to \"ledgerLive\" to use Ledger Live's path of \"m/44'/60'/%d/0/0\".\nSet this to \"mew\" to use MyEtherWallet's path of \"m/44'/60'/0'/%d\".\nFor custom paths, simply enter them here.", - }, - }, - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(initWallet(c)) - return nil - - }, - }, - - { - Name: "recover", - Aliases: []string{"r"}, - Usage: "Recover a node wallet from a mnemonic phrase", - UsageText: "rocketpool api wallet recover mnemonic", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "skip-validator-key-recovery, k", - Usage: "Recover the node wallet, but do not regenerate its validator keys", - }, - cli.StringFlag{ - Name: "derivation-path, d", - Usage: "Specify the derivation path for the wallet.\nOmit this flag (or leave it blank) for the default of \"m/44'/60'/0'/0/%d\" (where %d is the index).\nSet this to \"ledgerLive\" to use Ledger Live's path of \"m/44'/60'/%d/0/0\".\nSet this to \"mew\" to use MyEtherWallet's path of \"m/44'/60'/0'/%d\".\nFor custom paths, simply enter them here.", - }, - cli.UintFlag{ - Name: "wallet-index, i", - Usage: "Specify the index to use with the derivation path when recovering your wallet", - Value: 0, - }, - }, - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - mnemonic, err := cliutils.ValidateWalletMnemonic("mnemonic", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(recoverWallet(c, mnemonic)) - return nil - - }, - }, - - { - Name: "search-and-recover", - Aliases: []string{"r"}, - Usage: "Search for and recover a node wallet's derivation key and index using a mnemonic phrase and a well-known address.", - UsageText: "rocketpool api wallet search-and-recover mnemonic address", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "skip-validator-key-recovery, k", - Usage: "Recover the node wallet, but do not regenerate its validator keys", - }, - }, - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - mnemonic, err := cliutils.ValidateWalletMnemonic("mnemonic", c.Args().Get(0)) - if err != nil { - return err - } - address, err := cliutils.ValidateAddress("address", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(searchAndRecoverWallet(c, mnemonic, address)) - return nil - - }, - }, - - { - Name: "rebuild", - Aliases: []string{"b"}, - Usage: "Rebuild validator keystores from derived keys", - UsageText: "rocketpool api wallet rebuild", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(rebuildWallet(c)) - return nil - - }, - }, - - { - Name: "test-recovery", - Aliases: []string{"r"}, - Usage: "Test recovery of a node wallet and its validator keys without actually saving the recovered files", - UsageText: "rocketpool api wallet test-recovery mnemonic", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "skip-validator-key-recovery, k", - Usage: "Recover the node wallet, but do not regenerate its validator keys", - }, - cli.StringFlag{ - Name: "derivation-path, d", - Usage: "Specify the derivation path for the wallet.\nOmit this flag (or leave it blank) for the default of \"m/44'/60'/0'/0/%d\" (where %d is the index).\nSet this to \"ledgerLive\" to use Ledger Live's path of \"m/44'/60'/%d/0/0\".\nSet this to \"mew\" to use MyEtherWallet's path of \"m/44'/60'/0'/%d\".\nFor custom paths, simply enter them here.", - }, - cli.UintFlag{ - Name: "wallet-index, i", - Usage: "Specify the index to use with the derivation path when recovering your wallet", - Value: 0, - }, - }, - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - mnemonic, err := cliutils.ValidateWalletMnemonic("mnemonic", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(testRecoverWallet(c, mnemonic)) - return nil - - }, - }, - - { - Name: "test-search-and-recover", - Aliases: []string{"r"}, - Usage: "Test searching for and recovery of a node wallet's derivation key, index, and validator keys using a mnemonic phrase and a well-known address.", - UsageText: "rocketpool api wallet test-search-and-recover mnemonic address", - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "skip-validator-key-recovery, k", - Usage: "Recover the node wallet, but do not regenerate its validator keys", - }, - }, - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - mnemonic, err := cliutils.ValidateWalletMnemonic("mnemonic", c.Args().Get(0)) - if err != nil { - return err - } - address, err := cliutils.ValidateAddress("address", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(testSearchAndRecoverWallet(c, mnemonic, address)) - return nil - - }, - }, - - { - Name: "export", - Aliases: []string{"e"}, - Usage: "Export the node wallet in JSON format", - UsageText: "rocketpool api wallet export", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(exportWallet(c)) - return nil - - }, - }, - - { - Name: "estimate-gas-set-ens-name", - Usage: "Estimate the gas required to set the name for the node wallet's ENS reverse record", - UsageText: "rocketpool api node estimate-gas-set-ens-name name", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Run - api.PrintResponse(setEnsName(c, c.Args().Get(0), true)) - return nil - - }, - }, - - { - Name: "set-ens-name", - Usage: "Set a name to the node wallet's ENS reverse record", - UsageText: "rocketpool api node set-ens-name name", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - - // Run - api.PrintResponse(setEnsName(c, c.Args().Get(0), false)) - return nil - - }, - }, - - { - Name: "masquerade", - Usage: "Change your node's effective address to a different one. Your node will not be able to submit transactions or sign messages since you don't have the corresponding wallet's private key.", - UsageText: "rocketpool api wallet masquerade address", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - address, err := cliutils.ValidateAddress("address", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(masquerade(c, address)) - return nil - - }, - }, - - { - Name: "end-masquerade", - Usage: "End a masquerade, restoring your node's effective address back to your wallet address if one is loaded.", - UsageText: "rocketpool api wallet end-masquerade", - Action: func(c *cli.Context) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 0); err != nil { - return err - } - - // Run - api.PrintResponse(endMasquerade(c)) - return nil - - }, - }, - }, - }) -} diff --git a/rocketpool/api/wallet/init.go b/rocketpool/api/wallet/init.go index d11799a8c..997fdfaa2 100644 --- a/rocketpool/api/wallet/init.go +++ b/rocketpool/api/wallet/init.go @@ -11,6 +11,10 @@ import ( ) func initWallet(c *cli.Context) (*api.InitWalletResponse, error) { + return initWalletWithPath(c, c.String("derivation-path")) +} + +func initWalletWithPath(c *cli.Context, derivationPath string) (*api.InitWalletResponse, error) { // Get services w, err := services.GetWallet(c) @@ -27,7 +31,7 @@ func initWallet(c *cli.Context) (*api.InitWalletResponse, error) { } // Get the derivation path - path := c.String("derivation-path") + path := derivationPath switch path { case "": path = wallet.DefaultNodeKeyPath diff --git a/rocketpool/api/wallet/recover.go b/rocketpool/api/wallet/recover.go index 7714819f8..65041d1cc 100644 --- a/rocketpool/api/wallet/recover.go +++ b/rocketpool/api/wallet/recover.go @@ -19,6 +19,10 @@ const ( ) func recoverWallet(c *cli.Context, mnemonic string) (*api.RecoverWalletResponse, error) { + return recoverWalletWithParams(c, mnemonic, c.Bool("skip-validator-key-recovery"), c.String("derivation-path"), c.Uint("wallet-index")) +} + +func recoverWalletWithParams(c *cli.Context, mnemonic string, skipValidatorKeyRecovery bool, derivationPath string, walletIndex uint) (*api.RecoverWalletResponse, error) { // Get services w, err := services.GetWallet(c) @@ -26,7 +30,7 @@ func recoverWallet(c *cli.Context, mnemonic string) (*api.RecoverWalletResponse, return nil, err } var rp *rocketpool.RocketPool - if !c.Bool("skip-validator-key-recovery") { + if !skipValidatorKeyRecovery { if err := services.RequireRocketStorage(c); err != nil { return nil, err } @@ -49,7 +53,7 @@ func recoverWallet(c *cli.Context, mnemonic string) (*api.RecoverWalletResponse, } // Get the derivation path - path := c.String("derivation-path") + path := derivationPath switch path { case "": path = wallet.DefaultNodeKeyPath @@ -59,9 +63,6 @@ func recoverWallet(c *cli.Context, mnemonic string) (*api.RecoverWalletResponse, path = wallet.MyEtherWalletNodeKeyPath } - // Get the wallet index - walletIndex := c.Uint("wallet-index") - // Recover wallet if err := w.Recover(path, walletIndex, mnemonic); err != nil { return nil, err @@ -74,7 +75,7 @@ func recoverWallet(c *cli.Context, mnemonic string) (*api.RecoverWalletResponse, } response.AccountAddress = nodeAccount.Address - if !c.Bool("skip-validator-key-recovery") { + if !skipValidatorKeyRecovery { response.ValidatorKeys, err = walletutils.RecoverNodeKeys(c, rp, bc, nodeAccount.Address, w, false) if err != nil { return nil, err @@ -92,6 +93,10 @@ func recoverWallet(c *cli.Context, mnemonic string) (*api.RecoverWalletResponse, } func searchAndRecoverWallet(c *cli.Context, mnemonic string, address common.Address) (*api.SearchAndRecoverWalletResponse, error) { + return searchAndRecoverWalletWithParams(c, mnemonic, address, c.Bool("skip-validator-key-recovery")) +} + +func searchAndRecoverWalletWithParams(c *cli.Context, mnemonic string, address common.Address, skipValidatorKeyRecovery bool) (*api.SearchAndRecoverWalletResponse, error) { // Get services w, err := services.GetWallet(c) @@ -99,7 +104,7 @@ func searchAndRecoverWallet(c *cli.Context, mnemonic string, address common.Addr return nil, err } var rp *rocketpool.RocketPool - if !c.Bool("skip-validator-key-recovery") { + if !skipValidatorKeyRecovery { if err := services.RequireRocketStorage(c); err != nil { return nil, err } @@ -173,7 +178,7 @@ func searchAndRecoverWallet(c *cli.Context, mnemonic string, address common.Addr } response.AccountAddress = nodeAccount.Address - if !c.Bool("skip-validator-key-recovery") { + if !skipValidatorKeyRecovery { response.ValidatorKeys, err = walletutils.RecoverNodeKeys(c, rp, bc, nodeAccount.Address, w, false) if err != nil { return nil, err diff --git a/rocketpool/api/wallet/routes.go b/rocketpool/api/wallet/routes.go new file mode 100644 index 000000000..63c86645e --- /dev/null +++ b/rocketpool/api/wallet/routes.go @@ -0,0 +1,104 @@ +package wallet + +import ( + "net/http" + "strconv" + + "github.com/ethereum/go-ethereum/common" + "github.com/urfave/cli" + + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +// RegisterRoutes registers the wallet module's HTTP routes onto mux. +func RegisterRoutes(mux *http.ServeMux, c *cli.Context) { + mux.HandleFunc("/api/wallet/status", func(w http.ResponseWriter, r *http.Request) { + resp, err := getStatus(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/wallet/set-password", func(w http.ResponseWriter, r *http.Request) { + password := r.FormValue("password") + resp, err := setPassword(c, password) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/wallet/init", func(w http.ResponseWriter, r *http.Request) { + derivationPath := r.URL.Query().Get("derivationPath") + if derivationPath == "" { + derivationPath = r.FormValue("derivationPath") + } + resp, err := initWalletWithPath(c, derivationPath) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/wallet/recover", func(w http.ResponseWriter, r *http.Request) { + mnemonic := r.FormValue("mnemonic") + skipRecovery := r.FormValue("skipValidatorKeyRecovery") == "true" + derivationPath := r.FormValue("derivationPath") + walletIndex, _ := strconv.ParseUint(r.FormValue("walletIndex"), 10, 64) + resp, err := recoverWalletWithParams(c, mnemonic, skipRecovery, derivationPath, uint(walletIndex)) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/wallet/search-and-recover", func(w http.ResponseWriter, r *http.Request) { + mnemonic := r.FormValue("mnemonic") + address := common.HexToAddress(r.FormValue("address")) + skipRecovery := r.FormValue("skipValidatorKeyRecovery") == "true" + resp, err := searchAndRecoverWalletWithParams(c, mnemonic, address, skipRecovery) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/wallet/test-recover", func(w http.ResponseWriter, r *http.Request) { + mnemonic := r.FormValue("mnemonic") + skipRecovery := r.FormValue("skipValidatorKeyRecovery") == "true" + derivationPath := r.FormValue("derivationPath") + walletIndex, _ := strconv.ParseUint(r.FormValue("walletIndex"), 10, 64) + resp, err := testRecoverWalletWithParams(c, mnemonic, skipRecovery, derivationPath, uint(walletIndex)) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/wallet/test-search-and-recover", func(w http.ResponseWriter, r *http.Request) { + mnemonic := r.FormValue("mnemonic") + address := common.HexToAddress(r.FormValue("address")) + skipRecovery := r.FormValue("skipValidatorKeyRecovery") == "true" + resp, err := testSearchAndRecoverWalletWithParams(c, mnemonic, address, skipRecovery) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/wallet/rebuild", func(w http.ResponseWriter, r *http.Request) { + resp, err := rebuildWallet(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/wallet/export", func(w http.ResponseWriter, r *http.Request) { + resp, err := exportWallet(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/wallet/masquerade", func(w http.ResponseWriter, r *http.Request) { + address := common.HexToAddress(r.FormValue("address")) + resp, err := masquerade(c, address) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/wallet/end-masquerade", func(w http.ResponseWriter, r *http.Request) { + resp, err := endMasquerade(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/wallet/estimate-gas-set-ens-name", func(w http.ResponseWriter, r *http.Request) { + name := r.URL.Query().Get("name") + if name == "" { + name = r.FormValue("name") + } + resp, err := setEnsName(c, name, true) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/wallet/set-ens-name", func(w http.ResponseWriter, r *http.Request) { + name := r.FormValue("name") + resp, err := setEnsName(c, name, false) + apiutils.WriteResponse(w, resp, err) + }) +} diff --git a/rocketpool/api/wallet/test.go b/rocketpool/api/wallet/test.go index 1b8db6b7e..65c06505c 100644 --- a/rocketpool/api/wallet/test.go +++ b/rocketpool/api/wallet/test.go @@ -14,6 +14,10 @@ import ( ) func testRecoverWallet(c *cli.Context, mnemonic string) (*api.RecoverWalletResponse, error) { + return testRecoverWalletWithParams(c, mnemonic, c.Bool("skip-validator-key-recovery"), c.String("derivation-path"), c.Uint("wallet-index")) +} + +func testRecoverWalletWithParams(c *cli.Context, mnemonic string, skipValidatorKeyRecovery bool, derivationPath string, walletIndex uint) (*api.RecoverWalletResponse, error) { // Get services cfg, err := services.GetConfig(c) @@ -21,7 +25,7 @@ func testRecoverWallet(c *cli.Context, mnemonic string) (*api.RecoverWalletRespo return nil, err } var rp *rocketpool.RocketPool - if !c.Bool("skip-validator-key-recovery") { + if !skipValidatorKeyRecovery { if err := services.RequireRocketStorage(c); err != nil { return nil, err } @@ -47,7 +51,7 @@ func testRecoverWallet(c *cli.Context, mnemonic string) (*api.RecoverWalletRespo response := api.RecoverWalletResponse{} // Get the derivation path - path := c.String("derivation-path") + path := derivationPath switch path { case "": path = wallet.DefaultNodeKeyPath @@ -57,9 +61,6 @@ func testRecoverWallet(c *cli.Context, mnemonic string) (*api.RecoverWalletRespo path = wallet.MyEtherWalletNodeKeyPath } - // Get the wallet index - walletIndex := c.Uint("wallet-index") - // Recover wallet if err := w.TestRecovery(path, walletIndex, mnemonic); err != nil { return nil, err @@ -72,7 +73,7 @@ func testRecoverWallet(c *cli.Context, mnemonic string) (*api.RecoverWalletRespo } response.AccountAddress = nodeAccount.Address - if !c.Bool("skip-validator-key-recovery") { + if !skipValidatorKeyRecovery { response.ValidatorKeys, err = walletutils.RecoverNodeKeys(c, rp, bc, nodeAccount.Address, w, true) if err != nil { return nil, err @@ -85,6 +86,10 @@ func testRecoverWallet(c *cli.Context, mnemonic string) (*api.RecoverWalletRespo } func testSearchAndRecoverWallet(c *cli.Context, mnemonic string, address common.Address) (*api.SearchAndRecoverWalletResponse, error) { + return testSearchAndRecoverWalletWithParams(c, mnemonic, address, c.Bool("skip-validator-key-recovery")) +} + +func testSearchAndRecoverWalletWithParams(c *cli.Context, mnemonic string, address common.Address, skipValidatorKeyRecovery bool) (*api.SearchAndRecoverWalletResponse, error) { // Get services cfg, err := services.GetConfig(c) @@ -92,7 +97,7 @@ func testSearchAndRecoverWallet(c *cli.Context, mnemonic string, address common. return nil, err } var rp *rocketpool.RocketPool - if !c.Bool("skip-validator-key-recovery") { + if !skipValidatorKeyRecovery { if err := services.RequireRocketStorage(c); err != nil { return nil, err } @@ -169,7 +174,7 @@ func testSearchAndRecoverWallet(c *cli.Context, mnemonic string, address common. } response.AccountAddress = nodeAccount.Address - if !c.Bool("skip-validator-key-recovery") { + if !skipValidatorKeyRecovery { response.ValidatorKeys, err = walletutils.RecoverNodeKeys(c, rp, bc, nodeAccount.Address, w, true) if err != nil { return nil, err diff --git a/rocketpool/node/http.go b/rocketpool/node/http.go new file mode 100644 index 000000000..ca4a09e07 --- /dev/null +++ b/rocketpool/node/http.go @@ -0,0 +1,64 @@ +package node + +import ( + "context" + "fmt" + "log" + "net/http" + + "github.com/urfave/cli" + + "github.com/rocket-pool/smartnode/rocketpool/node/routes" + "github.com/rocket-pool/smartnode/shared/services/config" + cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" +) + +type httpServer struct { + server *http.Server + mux *http.ServeMux +} + +// startHTTP starts the node's HTTP API server and returns immediately. +// The server runs in the background for the lifetime of the process. +func startHTTP(ctx context.Context, c *cli.Context, cfg *config.RocketPoolConfig) { + port, ok := cfg.Smartnode.APIPort.Value.(uint16) + if !ok || port == 0 { + log.Println("Warning: APIPort not configured, HTTP API server will not start.") + return + } + + // In Docker mode the server must bind to 0.0.0.0 so the port mapping in + // node.tmpl makes it accessible from the host. In native mode we respect + // the OpenAPIPort setting: Closed → 127.0.0.1, OpenLocalhost → 0.0.0.0. + var host string + if !cfg.IsNativeMode { + host = "0.0.0.0" + } else { + portMode, _ := cfg.Smartnode.OpenAPIPort.Value.(cfgtypes.RPCMode) + if portMode == cfgtypes.RPC_OpenLocalhost { + host = "0.0.0.0" + } else { + host = "127.0.0.1" + } + } + + mux := http.NewServeMux() + routes.RegisterRoutes(mux, c) + + srv := &http.Server{ + Addr: fmt.Sprintf("%s:%d", host, port), + Handler: mux, + } + + go func() { + log.Printf("Node HTTP API server listening on %s:%d\n", host, port) + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Printf("Node HTTP API server error: %v\n", err) + } + }() + + go func() { + <-ctx.Done() + _ = srv.Shutdown(context.Background()) + }() +} diff --git a/rocketpool/node/node.go b/rocketpool/node/node.go index 741c4af24..1c1734c04 100644 --- a/rocketpool/node/node.go +++ b/rocketpool/node/node.go @@ -1,13 +1,16 @@ package node import ( + "context" _ "embed" "fmt" "math/big" "net/http" "os" + "os/signal" "path/filepath" "sync" + "syscall" "time" "github.com/ethereum/go-ethereum/common" @@ -125,6 +128,15 @@ func run(c *cli.Context) error { errorLog := log.NewColorLogger(ErrorColor) updateLog := log.NewColorLogger(UpdateColor) + // Create a context that is cancelled on SIGINT/SIGTERM so the HTTP server + // and other background goroutines can shut down gracefully. + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer cancel() + + // Start the HTTP API server. It runs in the background for the lifetime + // of the daemon and serves all migrated API endpoints. + startHTTP(ctx, c, cfg) + // Create the state manager m := state.NewNetworkStateManager(rp, cfg.Smartnode.GetStateManagerContracts(), bc, &updateLog) stateLocker := collectors.NewStateLocker() diff --git a/rocketpool/node/routes/routes.go b/rocketpool/node/routes/routes.go new file mode 100644 index 000000000..af15954c4 --- /dev/null +++ b/rocketpool/node/routes/routes.go @@ -0,0 +1,42 @@ +package routes + +import ( + "net/http" + + "github.com/urfave/cli" + + apiroutes "github.com/rocket-pool/smartnode/rocketpool/api" + auctionroutes "github.com/rocket-pool/smartnode/rocketpool/api/auction" + megapoolroutes "github.com/rocket-pool/smartnode/rocketpool/api/megapool" + minipoolroutes "github.com/rocket-pool/smartnode/rocketpool/api/minipool" + networkroutes "github.com/rocket-pool/smartnode/rocketpool/api/network" + noderoutes "github.com/rocket-pool/smartnode/rocketpool/api/node" + odaoroutes "github.com/rocket-pool/smartnode/rocketpool/api/odao" + pdaoroutes "github.com/rocket-pool/smartnode/rocketpool/api/pdao" + queueroutes "github.com/rocket-pool/smartnode/rocketpool/api/queue" + securityroutes "github.com/rocket-pool/smartnode/rocketpool/api/security" + serviceroutes "github.com/rocket-pool/smartnode/rocketpool/api/service" + walletroutes "github.com/rocket-pool/smartnode/rocketpool/api/wallet" +) + +// RegisterRoutes registers all HTTP API routes onto mux. +// Each migration branch adds additional module registrations here. +func RegisterRoutes(mux *http.ServeMux, c *cli.Context) { + mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + apiroutes.RegisterVersionRoute(mux) + apiroutes.RegisterWaitRoute(mux, c) + auctionroutes.RegisterRoutes(mux, c) + megapoolroutes.RegisterRoutes(mux, c) + minipoolroutes.RegisterRoutes(mux, c) + networkroutes.RegisterRoutes(mux, c) + noderoutes.RegisterRoutes(mux, c) + odaoroutes.RegisterRoutes(mux, c) + pdaoroutes.RegisterRoutes(mux, c) + queueroutes.RegisterRoutes(mux, c) + securityroutes.RegisterRoutes(mux, c) + serviceroutes.RegisterRoutes(mux, c) + walletroutes.RegisterRoutes(mux, c) +} diff --git a/rocketpool/rocketpool.go b/rocketpool/rocketpool.go index a71e48a95..3cd41e8ad 100644 --- a/rocketpool/rocketpool.go +++ b/rocketpool/rocketpool.go @@ -6,11 +6,9 @@ import ( "github.com/urfave/cli" - "github.com/rocket-pool/smartnode/rocketpool/api" "github.com/rocket-pool/smartnode/rocketpool/node" "github.com/rocket-pool/smartnode/rocketpool/watchtower" "github.com/rocket-pool/smartnode/shared" - apiutils "github.com/rocket-pool/smartnode/shared/utils/api" blsversionpin "github.com/herumi/bls-eth-go-binary/bls" ) @@ -79,25 +77,13 @@ func main() { } // Register commands - api.RegisterCommands(app, "api", []string{"a"}) node.RegisterCommands(app, "node", []string{"n"}) watchtower.RegisterCommands(app, "watchtower", []string{"w"}) - // Get command being run - var commandName string - app.Before = func(c *cli.Context) error { - commandName = c.Args().First() - return nil - } - // Run application if err := app.Run(os.Args); err != nil { - if commandName == "api" { - apiutils.PrintErrorResponse(err) - } else { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } + fmt.Fprintln(os.Stderr, err) + os.Exit(1) } } diff --git a/shared/services/config/rocket-pool-config.go b/shared/services/config/rocket-pool-config.go index 552f835b5..6b6757f23 100644 --- a/shared/services/config/rocket-pool-config.go +++ b/shared/services/config/rocket-pool-config.go @@ -1254,6 +1254,19 @@ func (cfg *RocketPoolConfig) GetECStopSignal() (string, error) { return "", fmt.Errorf("Unknown Execution Client %s", string(cfg.ExecutionClient.Value.(config.ExecutionClient))) } +// Gets the port mapping string for the node API webserver. +// In Docker mode this always returns "127.0.0.1:PORT:PORT/tcp" so the CLI on +// the host can reach the server. In native mode Docker is not involved so an +// empty string is returned. +// Used by text/template to format node.tmpl. +func (cfg *RocketPoolConfig) GetNodeOpenPorts() string { + if cfg.IsNativeMode { + return "" + } + port := cfg.Smartnode.APIPort.Value.(uint16) + return fmt.Sprintf("\"127.0.0.1:%d:%d/tcp\"", port, port) +} + // Gets the stop signal of the ec container // Used by text/template to format eth1.yml func (cfg *RocketPoolConfig) GetECOpenAPIPorts() string { diff --git a/shared/services/config/smartnode-config.go b/shared/services/config/smartnode-config.go index 7c2803df1..bda308ece 100644 --- a/shared/services/config/smartnode-config.go +++ b/shared/services/config/smartnode-config.go @@ -114,6 +114,12 @@ type SmartnodeConfig struct { // Delay for automatic queue assignment AutoAssignmentDelay config.Parameter `yaml:"autoAssignmentDelay,omitempty"` + // Port for the node's HTTP API webserver + APIPort config.Parameter `yaml:"apiPort,omitempty"` + + // Whether to expose the node's API port to the local network + OpenAPIPort config.Parameter `yaml:"openAPIPort,omitempty"` + /////////////////////////// // Non-editable settings // /////////////////////////// @@ -413,6 +419,29 @@ func NewSmartnodeConfig(cfg *RocketPoolConfig) *SmartnodeConfig { OverwriteOnUpgrade: true, }, + APIPort: config.Parameter{ + ID: "apiPort", + Name: "API Port", + Description: "The port your Smartnode's HTTP API server should listen on.", + Type: config.ParameterType_Uint16, + Default: map[config.Network]interface{}{config.Network_All: uint16(8280)}, + AffectsContainers: []config.ContainerID{config.ContainerID_Node}, + CanBeBlank: false, + OverwriteOnUpgrade: false, + }, + + OpenAPIPort: config.Parameter{ + ID: "openAPIPort", + Name: "Expose API Port", + Description: "Expose the API port to other processes on your machine. For security reasons, this port can only be exposed to localhost. It is recommended to keep this setting on Closed unless you have a specific reason to expose it.", + Type: config.ParameterType_Choice, + Default: map[config.Network]interface{}{config.Network_All: config.RPC_Closed}, + AffectsContainers: []config.ContainerID{config.ContainerID_Node}, + CanBeBlank: false, + OverwriteOnUpgrade: false, + Options: config.RestrictedPortModes(), + }, + txWatchUrl: map[config.Network]string{ config.Network_Mainnet: "https://etherscan.io/tx", config.Network_Devnet: "https://hoodi.etherscan.io/tx", @@ -642,6 +671,8 @@ func (cfg *SmartnodeConfig) GetParameters() []*config.Parameter { &cfg.ArchiveECUrl, &cfg.WatchtowerMaxFeeOverride, &cfg.WatchtowerPrioFeeOverride, + &cfg.APIPort, + &cfg.OpenAPIPort, } } diff --git a/shared/services/ec-manager.go b/shared/services/ec-manager.go index cc454f311..536f1f242 100644 --- a/shared/services/ec-manager.go +++ b/shared/services/ec-manager.go @@ -410,13 +410,21 @@ func getNetworkNameFromId(networkId uint) string { } +// ecStatusTimeout is the per-call deadline used when probing an EC for its +// network ID, sync progress, or latest block. 10 seconds is long enough to +// tolerate transient load on a healthy client while still returning quickly +// when the client is unresponsive. +const ecStatusTimeout = 10 * time.Second + // Check the client status func checkEcStatus(client *ethClient) api.ClientStatus { status := api.ClientStatus{} // Get the NetworkId - networkId, err := client.NetworkID(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), ecStatusTimeout) + networkId, err := client.NetworkID(ctx) + cancel() if err != nil { status.Error = fmt.Sprintf("Sync progress check failed with [%s]", err.Error()) status.IsSynced = false @@ -428,8 +436,10 @@ func checkEcStatus(client *ethClient) api.ClientStatus { status.NetworkId = uint(networkId.Uint64()) } - // Get the fallback's sync progress - progress, err := client.SyncProgress(context.Background()) + // Get the sync progress + ctx, cancel = context.WithTimeout(context.Background(), ecStatusTimeout) + progress, err := client.SyncProgress(ctx) + cancel() if err != nil { status.Error = fmt.Sprintf("Sync progress check failed with [%s]", err.Error()) status.IsSynced = false diff --git a/shared/services/requirements.go b/shared/services/requirements.go index 328cfd9c8..1413b9c4e 100644 --- a/shared/services/requirements.go +++ b/shared/services/requirements.go @@ -264,7 +264,9 @@ func getRocketStorageLoaded(c *cli.Context) (bool, error) { if err != nil { return false, err } - code, err := ec.CodeAt(context.Background(), common.HexToAddress(cfg.Smartnode.GetStorageAddress()), nil) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + code, err := ec.CodeAt(ctx, common.HexToAddress(cfg.Smartnode.GetStorageAddress()), nil) if err != nil { return false, err } @@ -477,7 +479,9 @@ func waitEthClientSynced(c *cli.Context, verbose bool, timeout int64) (bool, err } // Get sync progress - progress, err := clientToCheck.SyncProgress(context.Background()) + pollCtx, pollCancel := context.WithTimeout(context.Background(), 10*time.Second) + progress, err := clientToCheck.SyncProgress(pollCtx) + pollCancel() if err != nil { return false, err } @@ -595,7 +599,9 @@ func waitBeaconClientSynced(c *cli.Context, verbose bool, timeout int64) (bool, // Confirm the EC's latest block is within the threshold of the current system clock func IsSyncWithinThreshold(ec rocketpool.ExecutionClient) (bool, time.Time, error) { - t, err := ec.LatestBlockTime(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + t, err := ec.LatestBlockTime(ctx) if err != nil { return false, time.Time{}, err } diff --git a/shared/services/rocketpool/api.go b/shared/services/rocketpool/api.go index 42ccddd01..d2a633272 100644 --- a/shared/services/rocketpool/api.go +++ b/shared/services/rocketpool/api.go @@ -2,6 +2,7 @@ package rocketpool import ( "fmt" + "net/url" "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" @@ -10,7 +11,7 @@ import ( // Wait for a transaction func (c *Client) WaitForTransaction(txHash common.Hash) (api.APIResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("wait %s", txHash.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/wait", url.Values{"txHash": {txHash.Hex()}}) if err != nil { return api.APIResponse{}, fmt.Errorf("Error waiting for tx: %w", err) } diff --git a/shared/services/rocketpool/assets/install/templates/api.tmpl b/shared/services/rocketpool/assets/install/templates/api.tmpl deleted file mode 100644 index 648d17d9d..000000000 --- a/shared/services/rocketpool/assets/install/templates/api.tmpl +++ /dev/null @@ -1,30 +0,0 @@ -# Autogenerated - DO NOT MODIFY THIS FILE DIRECTLY -# If you want to overwrite some of these values with your own customizations, -# please add them to `override/api.yml`. -# -# See https://docs.docker.com/compose/extends/#adding-and-overriding-configuration -# for more information on overriding specific parameters of docker-compose files. - -services: - api: - image: {{.Smartnode.GetSmartnodeContainerTag}} - container_name: {{.Smartnode.ProjectName}}_api - restart: unless-stopped - stop_signal: SIGKILL - stop_grace_period: 1s - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - {{.RocketPoolDirectory}}:/.rocketpool - - {{.Smartnode.DataPath}}:/.rocketpool/data - networks: - - net - entrypoint: /bin/sleep - command: "infinity" - cap_drop: - - all - cap_add: - - dac_override - security_opt: - - no-new-privileges -networks: - net: diff --git a/shared/services/rocketpool/assets/install/templates/node.tmpl b/shared/services/rocketpool/assets/install/templates/node.tmpl index 3f564e086..6d6fb5992 100644 --- a/shared/services/rocketpool/assets/install/templates/node.tmpl +++ b/shared/services/rocketpool/assets/install/templates/node.tmpl @@ -11,6 +11,7 @@ services: container_name: {{.Smartnode.ProjectName}}_node restart: unless-stopped tty: true + ports: [{{.GetNodeOpenPorts}}] volumes: - /var/run/docker.sock:/var/run/docker.sock - {{.RocketPoolDirectory}}:/.rocketpool diff --git a/shared/services/rocketpool/auction.go b/shared/services/rocketpool/auction.go index f22714e35..12f0aa5c5 100644 --- a/shared/services/rocketpool/auction.go +++ b/shared/services/rocketpool/auction.go @@ -3,6 +3,7 @@ package rocketpool import ( "fmt" "math/big" + "net/url" "github.com/goccy/go-json" "github.com/rocket-pool/smartnode/shared/types/api" @@ -10,7 +11,7 @@ import ( // Get RPL auction status func (c *Client) AuctionStatus() (api.AuctionStatusResponse, error) { - responseBytes, err := c.callAPI("auction status") + responseBytes, err := c.callHTTPAPI("GET", "/api/auction/status", nil) if err != nil { return api.AuctionStatusResponse{}, fmt.Errorf("Could not get auction status: %w", err) } @@ -35,7 +36,7 @@ func (c *Client) AuctionStatus() (api.AuctionStatusResponse, error) { // Get RPL lots for auction func (c *Client) AuctionLots() (api.AuctionLotsResponse, error) { - responseBytes, err := c.callAPI("auction lots") + responseBytes, err := c.callHTTPAPI("GET", "/api/auction/lots", nil) if err != nil { return api.AuctionLotsResponse{}, fmt.Errorf("Could not get auction lots: %w", err) } @@ -84,7 +85,7 @@ func (c *Client) AuctionLots() (api.AuctionLotsResponse, error) { // Check whether the node can create a new lot func (c *Client) CanCreateLot() (api.CanCreateLotResponse, error) { - responseBytes, err := c.callAPI("auction can-create-lot") + responseBytes, err := c.callHTTPAPI("GET", "/api/auction/can-create-lot", nil) if err != nil { return api.CanCreateLotResponse{}, fmt.Errorf("Could not get can create lot status: %w", err) } @@ -100,7 +101,7 @@ func (c *Client) CanCreateLot() (api.CanCreateLotResponse, error) { // Create a new lot func (c *Client) CreateLot() (api.CreateLotResponse, error) { - responseBytes, err := c.callAPI("auction create-lot") + responseBytes, err := c.callHTTPAPI("POST", "/api/auction/create-lot", nil) if err != nil { return api.CreateLotResponse{}, fmt.Errorf("Could not create lot: %w", err) } @@ -116,7 +117,10 @@ func (c *Client) CreateLot() (api.CreateLotResponse, error) { // Check whether the node can bid on a lot func (c *Client) CanBidOnLot(lotIndex uint64, amountWei *big.Int) (api.CanBidOnLotResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("auction can-bid-lot %d %s", lotIndex, amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/auction/can-bid-lot", url.Values{ + "lotIndex": {fmt.Sprintf("%d", lotIndex)}, + "amountWei": {amountWei.String()}, + }) if err != nil { return api.CanBidOnLotResponse{}, fmt.Errorf("Could not get can bid on lot status: %w", err) } @@ -132,7 +136,10 @@ func (c *Client) CanBidOnLot(lotIndex uint64, amountWei *big.Int) (api.CanBidOnL // Bid on a lot func (c *Client) BidOnLot(lotIndex uint64, amountWei *big.Int) (api.BidOnLotResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("auction bid-lot %d %s", lotIndex, amountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/auction/bid-lot", url.Values{ + "lotIndex": {fmt.Sprintf("%d", lotIndex)}, + "amountWei": {amountWei.String()}, + }) if err != nil { return api.BidOnLotResponse{}, fmt.Errorf("Could not bid on lot: %w", err) } @@ -148,7 +155,9 @@ func (c *Client) BidOnLot(lotIndex uint64, amountWei *big.Int) (api.BidOnLotResp // Check whether the node can claim RPL from a lot func (c *Client) CanClaimFromLot(lotIndex uint64) (api.CanClaimFromLotResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("auction can-claim-lot %d", lotIndex)) + responseBytes, err := c.callHTTPAPI("GET", "/api/auction/can-claim-lot", url.Values{ + "lotIndex": {fmt.Sprintf("%d", lotIndex)}, + }) if err != nil { return api.CanClaimFromLotResponse{}, fmt.Errorf("Could not get can claim RPL from lot status: %w", err) } @@ -164,7 +173,9 @@ func (c *Client) CanClaimFromLot(lotIndex uint64) (api.CanClaimFromLotResponse, // Claim RPL from a lot func (c *Client) ClaimFromLot(lotIndex uint64) (api.ClaimFromLotResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("auction claim-lot %d", lotIndex)) + responseBytes, err := c.callHTTPAPI("POST", "/api/auction/claim-lot", url.Values{ + "lotIndex": {fmt.Sprintf("%d", lotIndex)}, + }) if err != nil { return api.ClaimFromLotResponse{}, fmt.Errorf("Could not claim RPL from lot: %w", err) } @@ -180,7 +191,9 @@ func (c *Client) ClaimFromLot(lotIndex uint64) (api.ClaimFromLotResponse, error) // Check whether the node can recover unclaimed RPL from a lot func (c *Client) CanRecoverUnclaimedRPLFromLot(lotIndex uint64) (api.CanRecoverRPLFromLotResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("auction can-recover-lot %d", lotIndex)) + responseBytes, err := c.callHTTPAPI("GET", "/api/auction/can-recover-lot", url.Values{ + "lotIndex": {fmt.Sprintf("%d", lotIndex)}, + }) if err != nil { return api.CanRecoverRPLFromLotResponse{}, fmt.Errorf("Could not get can recover unclaimed RPL from lot status: %w", err) } @@ -196,7 +209,9 @@ func (c *Client) CanRecoverUnclaimedRPLFromLot(lotIndex uint64) (api.CanRecoverR // Recover unclaimed RPL from a lot (returning it to the auction contract) func (c *Client) RecoverUnclaimedRPLFromLot(lotIndex uint64) (api.RecoverRPLFromLotResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("auction recover-lot %d", lotIndex)) + responseBytes, err := c.callHTTPAPI("POST", "/api/auction/recover-lot", url.Values{ + "lotIndex": {fmt.Sprintf("%d", lotIndex)}, + }) if err != nil { return api.RecoverRPLFromLotResponse{}, fmt.Errorf("Could not recover unclaimed RPL from lot: %w", err) } diff --git a/shared/services/rocketpool/client.go b/shared/services/rocketpool/client.go index e7eba56fa..fc1fe07c4 100644 --- a/shared/services/rocketpool/client.go +++ b/shared/services/rocketpool/client.go @@ -2,17 +2,22 @@ package rocketpool import ( "bufio" + "bytes" + "context" "errors" "fmt" "io" "io/fs" "math" "math/big" + "net/http" + "net/url" "os" "os/exec" "path/filepath" "slices" "strings" + "sync" "time" "github.com/fatih/color" @@ -39,9 +44,6 @@ const ( PrometheusConfigTemplate string = "prometheus.tmpl" PrometheusFile string = "prometheus.yml" - APIContainerSuffix string = "_api" - APIBinPath string = "/go/bin/rocketpool" - templatesDir string = "templates" overrideDir string = "override" runtimeDir string = "runtime" @@ -68,17 +70,17 @@ func SyncRatioToPercent(in float64) float64 { type Client struct { configPath string daemonPath string - maxFee float64 - maxPrioFee float64 - gasLimit uint64 - customNonce *big.Int - client *ssh.Client - originalMaxFee float64 - originalMaxPrioFee float64 - originalGasLimit uint64 - debugPrint bool - ignoreSyncCheck bool - forceFallbacks bool + maxFee float64 + maxPrioFee float64 + gasLimit uint64 + customNonce *big.Int + client *ssh.Client + debugPrint bool + + // apiURL is the base URL for the node's HTTP API server. + // It is derived lazily from config on first use. + apiURL string + apiURLOnce sync.Once } func getClientStatusString(clientStatus api.ClientStatus) string { @@ -107,7 +109,6 @@ func checkClientStatus(rp *Client) (bool, error) { // Primary EC and CC are good if ecMgrStatus.PrimaryClientStatus.IsSynced && bcMgrStatus.PrimaryClientStatus.IsSynced { - rp.SetClientStatusFlags(true, false) return true, nil } @@ -123,7 +124,6 @@ func checkClientStatus(rp *Client) (bool, error) { // Fallback EC and CC are good if ecMgrStatus.FallbackClientStatus.IsSynced && bcMgrStatus.FallbackClientStatus.IsSynced { fmt.Printf("%sNOTE: primary clients are not ready, using fallback clients...\n\tPrimary EC status: %s\n\tPrimary CC status: %s%s\n\n", colorYellow, primaryEcStatus, primaryBcStatus, colorReset) - rp.SetClientStatusFlags(true, true) return true, nil } @@ -144,17 +144,12 @@ func NewClientFromCtx(c *cli.Context) *Client { // Return client client := &Client{ - configPath: os.ExpandEnv(c.GlobalString("config-path")), - daemonPath: os.ExpandEnv(c.GlobalString("daemon-path")), - maxFee: c.GlobalFloat64("maxFee"), - maxPrioFee: c.GlobalFloat64("maxPrioFee"), - gasLimit: c.GlobalUint64("gasLimit"), - originalMaxFee: c.GlobalFloat64("maxFee"), - originalMaxPrioFee: c.GlobalFloat64("maxPrioFee"), - originalGasLimit: c.GlobalUint64("gasLimit"), - debugPrint: c.GlobalBool("debug"), - forceFallbacks: false, - ignoreSyncCheck: false, + configPath: os.ExpandEnv(c.GlobalString("config-path")), + daemonPath: os.ExpandEnv(c.GlobalString("daemon-path")), + maxFee: c.GlobalFloat64("maxFee"), + maxPrioFee: c.GlobalFloat64("maxPrioFee"), + gasLimit: c.GlobalUint64("gasLimit"), + debugPrint: c.GlobalBool("debug"), } if nonce, ok := c.App.Metadata["nonce"]; ok { @@ -534,40 +529,29 @@ func (c *Client) PrintServiceCompose(composeFiles []string) error { // Get the Rocket Pool service version func (c *Client) GetServiceVersion() (string, error) { - - // Get service container version output - var cmd string - if c.daemonPath == "" { - containerName, err := c.getAPIContainerName() - if err != nil { - return "", err - } - cmd = fmt.Sprintf("docker exec %s %s --version", shellescape.Quote(containerName), shellescape.Quote(APIBinPath)) - } else { - cmd = fmt.Sprintf("%s --version", shellescape.Quote(c.daemonPath)) + type versionResponse struct { + Status string `json:"status"` + Error string `json:"error"` + Version string `json:"version"` } - versionBytes, err := c.readOutput(cmd) + + responseBytes, err := c.callHTTPAPI("GET", "/api/version", nil) if err != nil { return "", fmt.Errorf("Could not get Rocket Pool service version: %w", err) } - - // Get the version string - outputString := string(versionBytes) - elements := strings.Fields(outputString) // Split on whitespace - if len(elements) < 1 { - return "", fmt.Errorf("Could not parse Rocket Pool service version number from output '%s'", outputString) + var response versionResponse + if err := json.Unmarshal(responseBytes, &response); err != nil { + return "", fmt.Errorf("Could not decode Rocket Pool service version response: %w", err) + } + if response.Error != "" { + return "", fmt.Errorf("Could not get Rocket Pool service version: %s", response.Error) } - versionString := elements[len(elements)-1] - // Make sure it's a semantic version - version, err := semver.Make(versionString) + version, err := semver.Make(response.Version) if err != nil { - return "", fmt.Errorf("Could not parse Rocket Pool service version number from output '%s': %w", outputString, err) + return "", fmt.Errorf("Could not parse Rocket Pool service version number '%s': %w", response.Version, err) } - - // Return the parsed semantic version (extra safety) return version.String(), nil - } // Increments the custom nonce parameter. @@ -1026,11 +1010,6 @@ func (c *Client) AssignGasSettings(maxFee float64, maxPrioFee float64, gasLimit } // Set the flags for ignoring EC and CC sync checks and forcing fallbacks to prevent unnecessary duplication of effort by the API during CLI commands -func (c *Client) SetClientStatusFlags(ignoreSyncCheck bool, forceFallbacks bool) { - c.ignoreSyncCheck = ignoreSyncCheck - c.forceFallbacks = forceFallbacks -} - func (c *Client) checkIfCommandExists(command string) (bool, error) { // Run `type` to check for existence cmd := fmt.Sprintf("type %s", command) @@ -1146,7 +1125,6 @@ func (c *Client) deployTemplates(cfg *config.RocketPoolConfig, rocketpoolDir str // These containers always run toDeploy := []string{ - config.ApiContainerName, config.NodeContainerName, config.WatchtowerContainerName, config.ValidatorContainerName, @@ -1260,154 +1238,91 @@ func (c *Client) composeAddons(cfg *config.RocketPoolConfig, rocketpoolDir strin } -// Call the Rocket Pool API -func (c *Client) callAPI(args string, otherArgs ...string) ([]byte, error) { - // Sanitize and parse the args - ignoreSyncCheckFlag, forceFallbackECFlag, args := c.getApiCallArgs(args, otherArgs...) - - // Create the command to run - var cmd string - if c.daemonPath == "" { - containerName, err := c.getAPIContainerName() +// getAPIURL returns the base URL for the node's HTTP API server, e.g. +// "http://127.0.0.1:8280". The result is derived from config and cached. +func (c *Client) getAPIURL() string { + c.apiURLOnce.Do(func() { + cfg, _, err := c.LoadConfig() if err != nil { - return []byte{}, err - } - cmd = fmt.Sprintf("docker exec %s %s %s %s %s %s api %s", shellescape.Quote(containerName), shellescape.Quote(APIBinPath), ignoreSyncCheckFlag, forceFallbackECFlag, c.getGasOpts(), c.getCustomNonce(), args) - } else { - cmd = fmt.Sprintf("%s --settings %s %s %s %s %s api %s", - c.daemonPath, - shellescape.Quote(fmt.Sprintf("%s/%s", c.configPath, SettingsFile)), - ignoreSyncCheckFlag, - forceFallbackECFlag, - c.getGasOpts(), - c.getCustomNonce(), - args) - } - - // Run the command - return c.runApiCall(cmd) -} - -// Call the Rocket Pool API with some custom environment variables -func (c *Client) callAPIWithEnvVars(envVars map[string]string, args string, otherArgs ...string) ([]byte, error) { - // Sanitize and parse the args - ignoreSyncCheckFlag, forceFallbackECFlag, args := c.getApiCallArgs(args, otherArgs...) - - // Create the command to run - var cmd string - if c.daemonPath == "" { - envArgs := "" - for key, value := range envVars { - os.Setenv(key, shellescape.Quote(value)) - envArgs += fmt.Sprintf("-e %s ", key) + return } - containerName, err := c.getAPIContainerName() - if err != nil { - return []byte{}, err + port, ok := cfg.Smartnode.APIPort.Value.(uint16) + if !ok || port == 0 { + return } - cmd = fmt.Sprintf("docker exec %s %s %s %s %s %s %s api %s", envArgs, shellescape.Quote(containerName), shellescape.Quote(APIBinPath), ignoreSyncCheckFlag, forceFallbackECFlag, c.getGasOpts(), c.getCustomNonce(), args) - } else { - envArgs := "" - for key, value := range envVars { - envArgs += fmt.Sprintf("%s=%s ", key, shellescape.Quote(value)) + c.apiURL = fmt.Sprintf("http://127.0.0.1:%d", port) + }) + return c.apiURL +} + +// callHTTPAPI calls the node's HTTP API server with a 5-minute safety timeout. +// method is "GET" or "POST". +// path is the URL path, e.g. "/api/node/status". +// params are appended as query string parameters for GET or as a form body for POST. +// The response body is returned as-is; callers unmarshal it the same way +// they currently unmarshal the output of callAPI. +func (c *Client) callHTTPAPI(method, path string, params url.Values) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + return c.callHTTPAPICtx(ctx, method, path, params) +} + +// callHTTPAPICtx is the context-aware core of callHTTPAPI. Use it directly +// when a tighter deadline is required (e.g. optional/informational requests +// that must not block the user). +func (c *Client) callHTTPAPICtx(ctx context.Context, method, path string, params url.Values) ([]byte, error) { + base := c.getAPIURL() + if base == "" { + return nil, fmt.Errorf("node HTTP API URL is not configured (APIPort may be 0)") + } + + target := base + path + + var req *http.Request + var err error + switch method { + case http.MethodGet: + if len(params) > 0 { + target += "?" + params.Encode() } - cmd = fmt.Sprintf("%s %s --settings %s %s %s %s %s api %s", - envArgs, - c.daemonPath, - shellescape.Quote(fmt.Sprintf("%s/%s", c.configPath, SettingsFile)), - ignoreSyncCheckFlag, - forceFallbackECFlag, - c.getGasOpts(), - c.getCustomNonce(), - args) - } - - // Run the command - return c.runApiCall(cmd) -} - -func (c *Client) getApiCallArgs(args string, otherArgs ...string) (string, string, string) { - // Sanitize arguments - var sanitizedArgs []string - for arg := range strings.FieldsSeq(args) { - sanitizedArg := shellescape.Quote(arg) - sanitizedArgs = append(sanitizedArgs, sanitizedArg) - } - args = strings.Join(sanitizedArgs, " ") - if len(otherArgs) > 0 { - for _, arg := range otherArgs { - sanitizedArg := shellescape.Quote(arg) - args += fmt.Sprintf(" %s", sanitizedArg) + req, err = http.NewRequestWithContext(ctx, http.MethodGet, target, nil) + case http.MethodPost: + body := []byte(params.Encode()) + req, err = http.NewRequestWithContext(ctx, http.MethodPost, target, bytes.NewReader(body)) + if err == nil { + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") } + default: + return nil, fmt.Errorf("unsupported HTTP method: %s", method) } - - ignoreSyncCheckFlag := "" - if c.ignoreSyncCheck { - ignoreSyncCheckFlag = "--ignore-sync-check" - } - forceFallbacksFlag := "" - if c.forceFallbacks { - forceFallbacksFlag = "--force-fallbacks" + if err != nil { + return nil, fmt.Errorf("error building HTTP request for %s %s: %w", method, path, err) } - return ignoreSyncCheckFlag, forceFallbacksFlag, args -} - -func (c *Client) runApiCall(cmd string) ([]byte, error) { if c.debugPrint { - fmt.Println("To API:") - fmt.Println(cmd) + fmt.Printf("HTTP API: %s %s\n", method, target) } - output, err := c.readOutput(cmd) - - if c.debugPrint { - if output != nil { - fmt.Println("API Out:") - fmt.Println(string(output)) - } - if err != nil { - fmt.Println("API Err:") - fmt.Println(err.Error()) - } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, fmt.Errorf("error calling HTTP API %s %s: %w", method, path, err) } + defer resp.Body.Close() - // Reset the gas settings after the call - c.maxFee = c.originalMaxFee - c.maxPrioFee = c.originalMaxPrioFee - c.gasLimit = c.originalGasLimit - - return output, err -} - -// Get the API container name -func (c *Client) getAPIContainerName() (string, error) { - cfg, _, err := c.LoadConfig() + responseBytes, err := io.ReadAll(resp.Body) if err != nil { - return "", err - } - if cfg.Smartnode.ProjectName.Value == "" { - return "", errors.New("Rocket Pool docker project name not set") + return nil, fmt.Errorf("error reading HTTP API response for %s %s: %w", method, path, err) } - return cfg.Smartnode.ProjectName.Value.(string) + APIContainerSuffix, nil -} -// Get gas price & limit flags -func (c *Client) getGasOpts() string { - var opts string - opts += fmt.Sprintf("--maxFee %f ", c.maxFee) - opts += fmt.Sprintf("--maxPrioFee %f ", c.maxPrioFee) - opts += fmt.Sprintf("--gasLimit %d ", c.gasLimit) - return opts -} + if c.debugPrint { + fmt.Printf("HTTP API response (%d): %s\n", resp.StatusCode, string(responseBytes)) + } -func (c *Client) getCustomNonce() string { - // Set the custom nonce - nonce := "" - if c.customNonce != nil { - nonce = fmt.Sprintf("--nonce %s", c.customNonce.String()) + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP API %s %s returned status %d: %s", method, path, resp.StatusCode, string(responseBytes)) } - return nonce + + return responseBytes, nil } // Run a command and print its output diff --git a/shared/services/rocketpool/megapool.go b/shared/services/rocketpool/megapool.go index 37d296070..d7a7f97de 100644 --- a/shared/services/rocketpool/megapool.go +++ b/shared/services/rocketpool/megapool.go @@ -3,6 +3,7 @@ package rocketpool import ( "fmt" "math/big" + "net/url" "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" @@ -11,7 +12,11 @@ import ( // Get megapool status func (c *Client) MegapoolStatus(finalizedState bool) (api.MegapoolStatusResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool status %t", finalizedState)) + finalizedStr := "false" + if finalizedState { + finalizedStr = "true" + } + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/status", url.Values{"finalizedState": {finalizedStr}}) if err != nil { return api.MegapoolStatusResponse{}, fmt.Errorf("Could not get megapool status: %w", err) } @@ -22,13 +27,12 @@ func (c *Client) MegapoolStatus(finalizedState bool) (api.MegapoolStatusResponse if response.Error != "" { return api.MegapoolStatusResponse{}, fmt.Errorf("Could not get megapool status: %s", response.Error) } - return response, nil } // Get a map of the node's validators and beacon balances func (c *Client) GetValidatorMapAndBalances() (api.MegapoolValidatorMapAndRewardsResponse, error) { - responseBytes, err := c.callAPI("megapool validator-map-and-balances") + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/validator-map-and-balances", nil) if err != nil { return api.MegapoolValidatorMapAndRewardsResponse{}, fmt.Errorf("Could not get megapool validator-map-and-balances: %w", err) } @@ -42,9 +46,9 @@ func (c *Client) GetValidatorMapAndBalances() (api.MegapoolValidatorMapAndReward return response, nil } -// Check whether the node can repay megapool debt +// Check whether the node can claim a megapool refund func (c *Client) CanClaimMegapoolRefund() (api.CanClaimRefundResponse, error) { - responseBytes, err := c.callAPI("megapool can-claim-refund") + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-claim-refund", nil) if err != nil { return api.CanClaimRefundResponse{}, fmt.Errorf("Could not get can claim refund status: %w", err) } @@ -58,9 +62,9 @@ func (c *Client) CanClaimMegapoolRefund() (api.CanClaimRefundResponse, error) { return response, nil } -// Repay megapool debt +// Claim megapool refund func (c *Client) ClaimMegapoolRefund() (api.ClaimRefundResponse, error) { - responseBytes, err := c.callAPI("megapool claim-refund") + responseBytes, err := c.callHTTPAPI("POST", "/api/megapool/claim-refund", nil) if err != nil { return api.ClaimRefundResponse{}, fmt.Errorf("Could not claim refund: %w", err) } @@ -76,7 +80,7 @@ func (c *Client) ClaimMegapoolRefund() (api.ClaimRefundResponse, error) { // Check whether the node can repay megapool debt func (c *Client) CanRepayDebt(amountWei *big.Int) (api.CanRepayDebtResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool can-repay-debt %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-repay-debt", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.CanRepayDebtResponse{}, fmt.Errorf("Could not get can repay debt status: %w", err) } @@ -92,7 +96,7 @@ func (c *Client) CanRepayDebt(amountWei *big.Int) (api.CanRepayDebtResponse, err // Repay megapool debt func (c *Client) RepayDebt(amountWei *big.Int) (api.RepayDebtResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool repay-debt %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/megapool/repay-debt", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.RepayDebtResponse{}, fmt.Errorf("Could not repay megapool debt: %w", err) } @@ -108,7 +112,7 @@ func (c *Client) RepayDebt(amountWei *big.Int) (api.RepayDebtResponse, error) { // Check whether the node can reduce the megapool bond func (c *Client) CanReduceBond(amountWei *big.Int) (api.CanReduceBondResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool can-reduce-bond %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-reduce-bond", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.CanReduceBondResponse{}, fmt.Errorf("Could not get can reduce bond status: %w", err) } @@ -124,7 +128,7 @@ func (c *Client) CanReduceBond(amountWei *big.Int) (api.CanReduceBondResponse, e // Reduce megapool bond func (c *Client) ReduceBond(amountWei *big.Int) (api.ReduceBondResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool reduce-bond %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/megapool/reduce-bond", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.ReduceBondResponse{}, fmt.Errorf("Could not reduce bond: %w", err) } @@ -140,7 +144,7 @@ func (c *Client) ReduceBond(amountWei *big.Int) (api.ReduceBondResponse, error) // Check whether the node can stake a megapool validator func (c *Client) CanStake(validatorId uint64) (api.CanStakeResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool can-stake %d", validatorId)) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-stake", url.Values{"validatorId": {fmt.Sprintf("%d", validatorId)}}) if err != nil { return api.CanStakeResponse{}, fmt.Errorf("Could not get can stake status: %w", err) } @@ -156,7 +160,7 @@ func (c *Client) CanStake(validatorId uint64) (api.CanStakeResponse, error) { // Stake a megapool validator func (c *Client) Stake(validatorId uint64) (api.StakeResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool stake %d", validatorId)) + responseBytes, err := c.callHTTPAPI("POST", "/api/megapool/stake", url.Values{"validatorId": {fmt.Sprintf("%d", validatorId)}}) if err != nil { return api.StakeResponse{}, fmt.Errorf("Could not stake megapool validator: %w", err) } @@ -170,9 +174,9 @@ func (c *Client) Stake(validatorId uint64) (api.StakeResponse, error) { return response, nil } -// Check whether the megapool validator can be disoolved +// Check whether a megapool validator can be dissolved func (c *Client) CanDissolveValidator(validatorId uint64) (api.CanDissolveValidatorResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool can-dissolve-validator %d", validatorId)) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-dissolve-validator", url.Values{"validatorId": {fmt.Sprintf("%d", validatorId)}}) if err != nil { return api.CanDissolveValidatorResponse{}, fmt.Errorf("Could not get can dissolve validator status: %w", err) } @@ -188,7 +192,7 @@ func (c *Client) CanDissolveValidator(validatorId uint64) (api.CanDissolveValida // Dissolve a megapool validator func (c *Client) DissolveValidator(validatorId uint64) (api.DissolveValidatorResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool dissolve-validator %d", validatorId)) + responseBytes, err := c.callHTTPAPI("POST", "/api/megapool/dissolve-validator", url.Values{"validatorId": {fmt.Sprintf("%d", validatorId)}}) if err != nil { return api.DissolveValidatorResponse{}, fmt.Errorf("Could not dissolve megapool validator: %w", err) } @@ -202,9 +206,41 @@ func (c *Client) DissolveValidator(validatorId uint64) (api.DissolveValidatorRes return response, nil } -// Check whether the megapool validator can be exited +// Check whether a megapool validator can be dissolved with proof +func (c *Client) CanDissolveWithProof(validatorId uint64) (api.CanDissolveWithProofResponse, error) { + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-dissolve-with-proof", url.Values{"validatorId": {fmt.Sprintf("%d", validatorId)}}) + if err != nil { + return api.CanDissolveWithProofResponse{}, fmt.Errorf("Could not get can dissolve-with-proof status: %w", err) + } + var response api.CanDissolveWithProofResponse + if err := json.Unmarshal(responseBytes, &response); err != nil { + return api.CanDissolveWithProofResponse{}, fmt.Errorf("Could not decode can dissolve-with-proof response: %w", err) + } + if response.Error != "" { + return api.CanDissolveWithProofResponse{}, fmt.Errorf("Could not get can dissolve-with-proof status: %s", response.Error) + } + return response, nil +} + +// Dissolve a megapool validator with proof +func (c *Client) DissolveWithProof(validatorId uint64) (api.DissolveWithProofResponse, error) { + responseBytes, err := c.callHTTPAPI("POST", "/api/megapool/dissolve-with-proof", url.Values{"validatorId": {fmt.Sprintf("%d", validatorId)}}) + if err != nil { + return api.DissolveWithProofResponse{}, fmt.Errorf("Could not dissolve megapool validator with proof: %w", err) + } + var response api.DissolveWithProofResponse + if err := json.Unmarshal(responseBytes, &response); err != nil { + return api.DissolveWithProofResponse{}, fmt.Errorf("Could not decode dissolve-with-proof response: %w", err) + } + if response.Error != "" { + return api.DissolveWithProofResponse{}, fmt.Errorf("Could not dissolve megapool validator with proof: %s", response.Error) + } + return response, nil +} + +// Check whether a megapool validator can be exited func (c *Client) CanExitValidator(validatorId uint64) (api.CanExitValidatorResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool can-exit-validator %d", validatorId)) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-exit-validator", url.Values{"validatorId": {fmt.Sprintf("%d", validatorId)}}) if err != nil { return api.CanExitValidatorResponse{}, fmt.Errorf("Could not get can exit validator status: %w", err) } @@ -220,7 +256,7 @@ func (c *Client) CanExitValidator(validatorId uint64) (api.CanExitValidatorRespo // Exit a megapool validator func (c *Client) ExitValidator(validatorId uint64) (api.ExitValidatorResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool exit-validator %d", validatorId)) + responseBytes, err := c.callHTTPAPI("POST", "/api/megapool/exit-validator", url.Values{"validatorId": {fmt.Sprintf("%d", validatorId)}}) if err != nil { return api.ExitValidatorResponse{}, fmt.Errorf("Could not exit megapool validator: %w", err) } @@ -234,15 +270,15 @@ func (c *Client) ExitValidator(validatorId uint64) (api.ExitValidatorResponse, e return response, nil } -// Check whether we can notify a validator exit +// Check whether the node can notify validator exit func (c *Client) CanNotifyValidatorExit(validatorId uint64) (api.CanNotifyValidatorExitResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool can-notify-validator-exit %d", validatorId)) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-notify-validator-exit", url.Values{"validatorId": {fmt.Sprintf("%d", validatorId)}}) if err != nil { return api.CanNotifyValidatorExitResponse{}, fmt.Errorf("Could not get can notify validator exit status: %w", err) } var response api.CanNotifyValidatorExitResponse if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.CanNotifyValidatorExitResponse{}, fmt.Errorf("Could not decode can notify-validator-exit response: %w", err) + return api.CanNotifyValidatorExitResponse{}, fmt.Errorf("Could not decode can notify validator exit response: %w", err) } if response.Error != "" { return api.CanNotifyValidatorExitResponse{}, fmt.Errorf("Could not get can notify validator exit status: %s", response.Error) @@ -250,57 +286,63 @@ func (c *Client) CanNotifyValidatorExit(validatorId uint64) (api.CanNotifyValida return response, nil } -// Notify exit of a megapool validator +// Notify the megapool that a validator has exited func (c *Client) NotifyValidatorExit(validatorId uint64) (api.NotifyValidatorExitResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool notify-validator-exit %d", validatorId)) + responseBytes, err := c.callHTTPAPI("POST", "/api/megapool/notify-validator-exit", url.Values{"validatorId": {fmt.Sprintf("%d", validatorId)}}) if err != nil { return api.NotifyValidatorExitResponse{}, fmt.Errorf("Could not notify validator exit: %w", err) } var response api.NotifyValidatorExitResponse if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.NotifyValidatorExitResponse{}, fmt.Errorf("Could not decode notify-validator-exit response: %w", err) + return api.NotifyValidatorExitResponse{}, fmt.Errorf("Could not decode notify validator exit response: %w", err) } if response.Error != "" { - return api.NotifyValidatorExitResponse{}, fmt.Errorf("Could not get notify-validator-exit status: %s", response.Error) + return api.NotifyValidatorExitResponse{}, fmt.Errorf("Could not notify validator exit: %s", response.Error) } return response, nil } -// Check whether we can notify a validator's final balance +// Check whether the node can notify final balance func (c *Client) CanNotifyFinalBalance(validatorId uint64, slot uint64) (api.CanNotifyFinalBalanceResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool can-notify-final-balance %d %d", validatorId, slot)) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-notify-final-balance", url.Values{ + "validatorId": {fmt.Sprintf("%d", validatorId)}, + "slot": {fmt.Sprintf("%d", slot)}, + }) if err != nil { - return api.CanNotifyFinalBalanceResponse{}, fmt.Errorf("Could not get can notify validator final balance status: %w", err) + return api.CanNotifyFinalBalanceResponse{}, fmt.Errorf("Could not get can notify final balance status: %w", err) } var response api.CanNotifyFinalBalanceResponse if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.CanNotifyFinalBalanceResponse{}, fmt.Errorf("Could not decode can notify-final-balance response: %w", err) + return api.CanNotifyFinalBalanceResponse{}, fmt.Errorf("Could not decode can notify final balance response: %w", err) } if response.Error != "" { - return api.CanNotifyFinalBalanceResponse{}, fmt.Errorf("Could not get can notify validator final balance status: %s", response.Error) + return api.CanNotifyFinalBalanceResponse{}, fmt.Errorf("Could not get can notify final balance status: %s", response.Error) } return response, nil } -// Notify final balance of a megapool validator +// Notify the megapool of a validator's final balance func (c *Client) NotifyFinalBalance(validatorId uint64, slot uint64) (api.NotifyFinalBalanceResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool notify-final-balance %d %d", validatorId, slot)) + responseBytes, err := c.callHTTPAPI("POST", "/api/megapool/notify-final-balance", url.Values{ + "validatorId": {fmt.Sprintf("%d", validatorId)}, + "slot": {fmt.Sprintf("%d", slot)}, + }) if err != nil { return api.NotifyFinalBalanceResponse{}, fmt.Errorf("Could not notify final balance: %w", err) } var response api.NotifyFinalBalanceResponse if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.NotifyFinalBalanceResponse{}, fmt.Errorf("Could not decode notify-final-balance response: %w", err) + return api.NotifyFinalBalanceResponse{}, fmt.Errorf("Could not decode notify final balance response: %w", err) } if response.Error != "" { - return api.NotifyFinalBalanceResponse{}, fmt.Errorf("Could not get notify-final-balance status: %s", response.Error) + return api.NotifyFinalBalanceResponse{}, fmt.Errorf("Could not notify final balance: %s", response.Error) } return response, nil } -// Check whether the node can exit the megapool queue +// Check whether the node can exit the validator queue func (c *Client) CanExitQueue(validatorIndex uint32) (api.CanExitQueueResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool can-exit-queue %d", validatorIndex)) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-exit-queue", url.Values{"validatorIndex": {fmt.Sprintf("%d", validatorIndex)}}) if err != nil { return api.CanExitQueueResponse{}, fmt.Errorf("Could not get can exit queue status: %w", err) } @@ -314,9 +356,9 @@ func (c *Client) CanExitQueue(validatorIndex uint32) (api.CanExitQueueResponse, return response, nil } -// Exit the megapool queue +// Exit the validator queue func (c *Client) ExitQueue(validatorIndex uint32) (api.ExitQueueResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool exit-queue %d", validatorIndex)) + responseBytes, err := c.callHTTPAPI("POST", "/api/megapool/exit-queue", url.Values{"validatorIndex": {fmt.Sprintf("%d", validatorIndex)}}) if err != nil { return api.ExitQueueResponse{}, fmt.Errorf("Could not exit queue: %w", err) } @@ -332,7 +374,7 @@ func (c *Client) ExitQueue(validatorIndex uint32) (api.ExitQueueResponse, error) // Get the gas info for a megapool delegate upgrade func (c *Client) CanDelegateUpgradeMegapool(address common.Address) (api.MegapoolCanDelegateUpgradeResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool can-delegate-upgrade %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-delegate-upgrade", url.Values{"address": {address.Hex()}}) if err != nil { return api.MegapoolCanDelegateUpgradeResponse{}, fmt.Errorf("Could not get can delegate upgrade megapool status: %w", err) } @@ -348,7 +390,7 @@ func (c *Client) CanDelegateUpgradeMegapool(address common.Address) (api.Megapoo // Upgrade the megapool delegate func (c *Client) DelegateUpgradeMegapool(address common.Address) (api.MegapoolDelegateUpgradeResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool delegate-upgrade %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/megapool/delegate-upgrade", url.Values{"address": {address.Hex()}}) if err != nil { return api.MegapoolDelegateUpgradeResponse{}, fmt.Errorf("Could not upgrade megapool delegate: %w", err) } @@ -364,7 +406,7 @@ func (c *Client) DelegateUpgradeMegapool(address common.Address) (api.MegapoolDe // Get the megapool's auto-upgrade setting func (c *Client) GetUseLatestDelegate(address common.Address) (api.MegapoolGetUseLatestDelegateResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool get-use-latest-delegate %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/get-use-latest-delegate", url.Values{"address": {address.Hex()}}) if err != nil { return api.MegapoolGetUseLatestDelegateResponse{}, fmt.Errorf("Could not get use latest delegate for megapool: %w", err) } @@ -380,7 +422,7 @@ func (c *Client) GetUseLatestDelegate(address common.Address) (api.MegapoolGetUs // Check whether a megapool can have its auto-upgrade setting changed func (c *Client) CanSetUseLatestDelegateMegapool(address common.Address, setting bool) (api.MegapoolCanSetUseLatestDelegateResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool can-set-use-latest-delegate %s %t", address.Hex(), setting)) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-set-use-latest-delegate", url.Values{"address": {address.Hex()}}) if err != nil { return api.MegapoolCanSetUseLatestDelegateResponse{}, fmt.Errorf("Could not get can set use latest delegate for megapool status: %w", err) } @@ -396,7 +438,14 @@ func (c *Client) CanSetUseLatestDelegateMegapool(address common.Address, setting // Change a megapool's auto-upgrade setting func (c *Client) SetUseLatestDelegateMegapool(address common.Address, setting bool) (api.MegapoolSetUseLatestDelegateResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool set-use-latest-delegate %s %t", address.Hex(), setting)) + settingStr := "false" + if setting { + settingStr = "true" + } + responseBytes, err := c.callHTTPAPI("POST", "/api/megapool/set-use-latest-delegate", url.Values{ + "address": {address.Hex()}, + "setting": {settingStr}, + }) if err != nil { return api.MegapoolSetUseLatestDelegateResponse{}, fmt.Errorf("Could not set use latest delegate for megapool: %w", err) } @@ -412,7 +461,7 @@ func (c *Client) SetUseLatestDelegateMegapool(address common.Address, setting bo // Get the megapool's delegate address func (c *Client) GetDelegate(address common.Address) (api.MegapoolGetDelegateResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool get-delegate %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/get-delegate", url.Values{"address": {address.Hex()}}) if err != nil { return api.MegapoolGetDelegateResponse{}, fmt.Errorf("Could get delegate for megapool: %w", err) } @@ -428,7 +477,7 @@ func (c *Client) GetDelegate(address common.Address) (api.MegapoolGetDelegateRes // Get the megapool's effective delegate address func (c *Client) GetEffectiveDelegate(address common.Address) (api.MegapoolGetEffectiveDelegateResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool get-effective-delegate %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/get-effective-delegate", url.Values{"address": {address.Hex()}}) if err != nil { return api.MegapoolGetEffectiveDelegateResponse{}, fmt.Errorf("Could get effective delegate for megapool: %w", err) } @@ -444,7 +493,7 @@ func (c *Client) GetEffectiveDelegate(address common.Address) (api.MegapoolGetEf // Calculate the megapool pending rewards func (c *Client) CalculatePendingRewards() (api.MegapoolRewardSplitResponse, error) { - responseBytes, err := c.callAPI("megapool pending-rewards") + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/pending-rewards", nil) if err != nil { return api.MegapoolRewardSplitResponse{}, fmt.Errorf("Could not get pending rewards: %w", err) } @@ -458,9 +507,9 @@ func (c *Client) CalculatePendingRewards() (api.MegapoolRewardSplitResponse, err return response, nil } -// Calculate Rewards split given an arbitrary amount +// Calculate rewards split given an arbitrary amount func (c *Client) CalculateRewards(amountWei *big.Int) (api.MegapoolRewardSplitResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("megapool calculate-rewards %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/calculate-rewards", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.MegapoolRewardSplitResponse{}, fmt.Errorf("Could not calculate rewards: %w", err) } @@ -471,13 +520,12 @@ func (c *Client) CalculateRewards(amountWei *big.Int) (api.MegapoolRewardSplitRe if response.Error != "" { return api.MegapoolRewardSplitResponse{}, fmt.Errorf("Could not get calculate rewards: %s", response.Error) } - return response, nil } // Check if the node can distribute megapool rewards func (c *Client) CanDistributeMegapool() (api.CanDistributeMegapoolResponse, error) { - responseBytes, err := c.callAPI("megapool can-distribute-megapool") + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-distribute", nil) if err != nil { return api.CanDistributeMegapoolResponse{}, fmt.Errorf("Could not get can-distribute-megapool response: %w", err) } @@ -493,7 +541,7 @@ func (c *Client) CanDistributeMegapool() (api.CanDistributeMegapoolResponse, err // Distribute megapool rewards func (c *Client) DistributeMegapool() (api.DistributeMegapoolResponse, error) { - responseBytes, err := c.callAPI("megapool distribute-megapool") + responseBytes, err := c.callHTTPAPI("POST", "/api/megapool/distribute", nil) if err != nil { return api.DistributeMegapoolResponse{}, fmt.Errorf("Could not get distribute-megapool response: %w", err) } @@ -509,7 +557,7 @@ func (c *Client) DistributeMegapool() (api.DistributeMegapoolResponse, error) { // Get the bond amount required for the megapool's next validator func (c *Client) GetNewValidatorBondRequirement() (api.GetNewValidatorBondRequirementResponse, error) { - responseBytes, err := c.callAPI("megapool get-new-validator-bond-requirement") + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/get-new-validator-bond-requirement", nil) if err != nil { return api.GetNewValidatorBondRequirementResponse{}, fmt.Errorf("Could not get new validator bond requirement: %w", err) } @@ -522,3 +570,7 @@ func (c *Client) GetNewValidatorBondRequirement() (api.GetNewValidatorBondRequir } return response, nil } + +// DissolveWithProof and CanDissolveWithProof client methods added above. +// CanDissolveWithProof / DissolveWithProof (also known as DissolveWithProof) are +// already implemented above. diff --git a/shared/services/rocketpool/minipool.go b/shared/services/rocketpool/minipool.go index bca3f72b6..f3b0f8b3e 100644 --- a/shared/services/rocketpool/minipool.go +++ b/shared/services/rocketpool/minipool.go @@ -3,6 +3,7 @@ package rocketpool import ( "fmt" "math/big" + "net/url" "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" @@ -12,7 +13,7 @@ import ( // Get minipool status func (c *Client) MinipoolStatus() (api.MinipoolStatusResponse, error) { - responseBytes, err := c.callAPI("minipool status") + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/status", nil) if err != nil { return api.MinipoolStatusResponse{}, fmt.Errorf("Could not get minipool status: %w", err) } @@ -58,7 +59,7 @@ func (c *Client) MinipoolStatus() (api.MinipoolStatusResponse, error) { // Check whether a minipool is eligible for a refund func (c *Client) CanRefundMinipool(address common.Address) (api.CanRefundMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool can-refund %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/can-refund", url.Values{"address": {address.Hex()}}) if err != nil { return api.CanRefundMinipoolResponse{}, fmt.Errorf("Could not get can refund minipool status: %w", err) } @@ -74,7 +75,7 @@ func (c *Client) CanRefundMinipool(address common.Address) (api.CanRefundMinipoo // Refund ETH from a minipool func (c *Client) RefundMinipool(address common.Address) (api.RefundMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool refund %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/refund", url.Values{"address": {address.Hex()}}) if err != nil { return api.RefundMinipoolResponse{}, fmt.Errorf("Could not refund minipool: %w", err) } @@ -90,7 +91,7 @@ func (c *Client) RefundMinipool(address common.Address) (api.RefundMinipoolRespo // Check whether a minipool is eligible for staking func (c *Client) CanStakeMinipool(address common.Address) (api.CanStakeMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool can-stake %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/can-stake", url.Values{"address": {address.Hex()}}) if err != nil { return api.CanStakeMinipoolResponse{}, fmt.Errorf("Could not get can stake minipool status: %w", err) } @@ -106,7 +107,7 @@ func (c *Client) CanStakeMinipool(address common.Address) (api.CanStakeMinipoolR // Stake a minipool func (c *Client) StakeMinipool(address common.Address) (api.StakeMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool stake %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/stake", url.Values{"address": {address.Hex()}}) if err != nil { return api.StakeMinipoolResponse{}, fmt.Errorf("Could not stake minipool: %w", err) } @@ -122,7 +123,7 @@ func (c *Client) StakeMinipool(address common.Address) (api.StakeMinipoolRespons // Check whether a minipool is eligible for promotion func (c *Client) CanPromoteMinipool(address common.Address) (api.CanPromoteMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool can-promote %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/can-promote", url.Values{"address": {address.Hex()}}) if err != nil { return api.CanPromoteMinipoolResponse{}, fmt.Errorf("Could not get can promote minipool status: %w", err) } @@ -138,7 +139,7 @@ func (c *Client) CanPromoteMinipool(address common.Address) (api.CanPromoteMinip // Promote a minipool func (c *Client) PromoteMinipool(address common.Address) (api.PromoteMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool promote %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/promote", url.Values{"address": {address.Hex()}}) if err != nil { return api.PromoteMinipoolResponse{}, fmt.Errorf("Could not promote minipool: %w", err) } @@ -154,7 +155,7 @@ func (c *Client) PromoteMinipool(address common.Address) (api.PromoteMinipoolRes // Check whether a minipool can be dissolved func (c *Client) CanDissolveMinipool(address common.Address) (api.CanDissolveMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool can-dissolve %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/can-dissolve", url.Values{"address": {address.Hex()}}) if err != nil { return api.CanDissolveMinipoolResponse{}, fmt.Errorf("Could not get can dissolve minipool status: %w", err) } @@ -170,7 +171,7 @@ func (c *Client) CanDissolveMinipool(address common.Address) (api.CanDissolveMin // Dissolve a minipool func (c *Client) DissolveMinipool(address common.Address) (api.DissolveMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool dissolve %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/dissolve", url.Values{"address": {address.Hex()}}) if err != nil { return api.DissolveMinipoolResponse{}, fmt.Errorf("Could not dissolve minipool: %w", err) } @@ -186,7 +187,7 @@ func (c *Client) DissolveMinipool(address common.Address) (api.DissolveMinipoolR // Check whether a minipool can be exited func (c *Client) CanExitMinipool(address common.Address) (api.CanExitMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool can-exit %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/can-exit", url.Values{"address": {address.Hex()}}) if err != nil { return api.CanExitMinipoolResponse{}, fmt.Errorf("Could not get can exit minipool status: %w", err) } @@ -202,7 +203,7 @@ func (c *Client) CanExitMinipool(address common.Address) (api.CanExitMinipoolRes // Exit a minipool func (c *Client) ExitMinipool(address common.Address) (api.ExitMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool exit %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/exit", url.Values{"address": {address.Hex()}}) if err != nil { return api.ExitMinipoolResponse{}, fmt.Errorf("Could not exit minipool: %w", err) } @@ -218,7 +219,7 @@ func (c *Client) ExitMinipool(address common.Address) (api.ExitMinipoolResponse, // Check all of the node's minipools for closure eligibility, and return the details of the closeable ones func (c *Client) GetMinipoolCloseDetailsForNode() (api.GetMinipoolCloseDetailsForNodeResponse, error) { - responseBytes, err := c.callAPI("minipool get-minipool-close-details-for-node") + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/get-minipool-close-details-for-node", nil) if err != nil { return api.GetMinipoolCloseDetailsForNodeResponse{}, fmt.Errorf("Could not get get-minipool-close-details-for-node status: %w", err) } @@ -234,7 +235,7 @@ func (c *Client) GetMinipoolCloseDetailsForNode() (api.GetMinipoolCloseDetailsFo // Close a minipool func (c *Client) CloseMinipool(address common.Address) (api.CloseMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool close %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/close", url.Values{"address": {address.Hex()}}) if err != nil { return api.CloseMinipoolResponse{}, fmt.Errorf("Could not close minipool: %w", err) } @@ -250,7 +251,7 @@ func (c *Client) CloseMinipool(address common.Address) (api.CloseMinipoolRespons // Check whether a minipool can have its delegate upgraded func (c *Client) CanDelegateUpgradeMinipool(address common.Address) (api.CanDelegateUpgradeResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool can-delegate-upgrade %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/can-delegate-upgrade", url.Values{"address": {address.Hex()}}) if err != nil { return api.CanDelegateUpgradeResponse{}, fmt.Errorf("Could not get can delegate upgrade minipool status: %w", err) } @@ -266,7 +267,7 @@ func (c *Client) CanDelegateUpgradeMinipool(address common.Address) (api.CanDele // Upgrade a minipool delegate func (c *Client) DelegateUpgradeMinipool(address common.Address) (api.DelegateUpgradeResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool delegate-upgrade %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/delegate-upgrade", url.Values{"address": {address.Hex()}}) if err != nil { return api.DelegateUpgradeResponse{}, fmt.Errorf("Could not upgrade delegate for minipool: %w", err) } @@ -282,7 +283,7 @@ func (c *Client) DelegateUpgradeMinipool(address common.Address) (api.DelegateUp // Check whether a minipool can have its auto-upgrade setting changed func (c *Client) CanSetUseLatestDelegateMinipool(address common.Address) (api.CanSetUseLatestDelegateResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool can-set-use-latest-delegate %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/can-set-use-latest-delegate", url.Values{"address": {address.Hex()}}) if err != nil { return api.CanSetUseLatestDelegateResponse{}, fmt.Errorf("Could not get can set use latest delegate for minipool status: %w", err) } @@ -298,7 +299,7 @@ func (c *Client) CanSetUseLatestDelegateMinipool(address common.Address) (api.Ca // Change a minipool's auto-upgrade setting func (c *Client) SetUseLatestDelegateMinipool(address common.Address) (api.SetUseLatestDelegateResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool set-use-latest-delegate %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/set-use-latest-delegate", url.Values{"address": {address.Hex()}}) if err != nil { return api.SetUseLatestDelegateResponse{}, fmt.Errorf("Could not set use latest delegate for minipool: %w", err) } @@ -314,7 +315,10 @@ func (c *Client) SetUseLatestDelegateMinipool(address common.Address) (api.SetUs // Get the artifacts necessary for vanity address searching func (c *Client) GetVanityArtifacts(depositAmount *big.Int, nodeAddress string) (api.GetVanityArtifactsResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool get-vanity-artifacts %s %s", depositAmount.String(), nodeAddress)) + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/get-vanity-artifacts", url.Values{ + "depositAmount": {depositAmount.String()}, + "nodeAddress": {nodeAddress}, + }) if err != nil { return api.GetVanityArtifactsResponse{}, fmt.Errorf("Could not get vanity artifacts: %w", err) } @@ -330,7 +334,10 @@ func (c *Client) GetVanityArtifacts(depositAmount *big.Int, nodeAddress string) // Check whether the minipool can begin the bond reduction process func (c *Client) CanBeginReduceBondAmount(address common.Address, newBondAmountWei *big.Int) (api.CanBeginReduceBondAmountResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool can-begin-reduce-bond-amount %s %s", address.Hex(), newBondAmountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/can-begin-reduce-bond-amount", url.Values{ + "address": {address.Hex()}, + "newBondAmountWei": {newBondAmountWei.String()}, + }) if err != nil { return api.CanBeginReduceBondAmountResponse{}, fmt.Errorf("Could not get can begin reduce bond amount status: %w", err) } @@ -346,7 +353,10 @@ func (c *Client) CanBeginReduceBondAmount(address common.Address, newBondAmountW // Begin the bond reduction process for a minipool func (c *Client) BeginReduceBondAmount(address common.Address, newBondAmountWei *big.Int) (api.BeginReduceBondAmountResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool begin-reduce-bond-amount %s %s", address.Hex(), newBondAmountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/begin-reduce-bond-amount", url.Values{ + "address": {address.Hex()}, + "newBondAmountWei": {newBondAmountWei.String()}, + }) if err != nil { return api.BeginReduceBondAmountResponse{}, fmt.Errorf("Could not begin reduce bond amount: %w", err) } @@ -362,7 +372,7 @@ func (c *Client) BeginReduceBondAmount(address common.Address, newBondAmountWei // Check if a minipool's bond can be reduced func (c *Client) CanReduceBondAmount(address common.Address) (api.CanReduceBondAmountResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool can-reduce-bond-amount %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/can-reduce-bond-amount", url.Values{"address": {address.Hex()}}) if err != nil { return api.CanReduceBondAmountResponse{}, fmt.Errorf("Could not get can reduce bond amount status: %w", err) } @@ -378,7 +388,7 @@ func (c *Client) CanReduceBondAmount(address common.Address) (api.CanReduceBondA // Reduce a minipool's bond func (c *Client) ReduceBondAmount(address common.Address) (api.ReduceBondAmountResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool reduce-bond-amount %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/reduce-bond-amount", url.Values{"address": {address.Hex()}}) if err != nil { return api.ReduceBondAmountResponse{}, fmt.Errorf("Could not reduce bond amount: %w", err) } @@ -394,7 +404,7 @@ func (c *Client) ReduceBondAmount(address common.Address) (api.ReduceBondAmountR // Get the balance distribution details for all of the node's minipools func (c *Client) GetDistributeBalanceDetails() (api.GetDistributeBalanceDetailsResponse, error) { - responseBytes, err := c.callAPI("minipool get-distribute-balance-details") + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/get-distribute-balance-details", nil) if err != nil { return api.GetDistributeBalanceDetailsResponse{}, fmt.Errorf("Could not get distribute balance details: %w", err) } @@ -410,7 +420,7 @@ func (c *Client) GetDistributeBalanceDetails() (api.GetDistributeBalanceDetailsR // Distribute a minipool's ETH balance func (c *Client) DistributeBalance(address common.Address) (api.DistributeBalanceResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool distribute-balance %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/distribute-balance", url.Values{"address": {address.Hex()}}) if err != nil { return api.DistributeBalanceResponse{}, fmt.Errorf("Could not get distribute balance status: %w", err) } @@ -426,7 +436,10 @@ func (c *Client) DistributeBalance(address common.Address) (api.DistributeBalanc // Import a validator private key for a vacant minipool func (c *Client) ImportKey(address common.Address, mnemonic string) (api.ChangeWithdrawalCredentialsResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool import-key %s", address.Hex()), mnemonic) + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/import-key", url.Values{ + "address": {address.Hex()}, + "mnemonic": {mnemonic}, + }) if err != nil { return api.ChangeWithdrawalCredentialsResponse{}, fmt.Errorf("Could not import validator key: %w", err) } @@ -442,7 +455,10 @@ func (c *Client) ImportKey(address common.Address, mnemonic string) (api.ChangeW // Check whether a solo validator's withdrawal creds can be migrated to a minipool address func (c *Client) CanChangeWithdrawalCredentials(address common.Address, mnemonic string) (api.CanChangeWithdrawalCredentialsResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool can-change-withdrawal-creds %s", address.Hex()), mnemonic) + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/can-change-withdrawal-creds", url.Values{ + "address": {address.Hex()}, + "mnemonic": {mnemonic}, + }) if err != nil { return api.CanChangeWithdrawalCredentialsResponse{}, fmt.Errorf("Could not get can-change-withdrawal-creds status: %w", err) } @@ -458,7 +474,10 @@ func (c *Client) CanChangeWithdrawalCredentials(address common.Address, mnemonic // Migrate a solo validator's withdrawal creds to a minipool address func (c *Client) ChangeWithdrawalCredentials(address common.Address, mnemonic string) (api.ChangeWithdrawalCredentialsResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool change-withdrawal-creds %s", address.Hex()), mnemonic) + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/change-withdrawal-creds", url.Values{ + "address": {address.Hex()}, + "mnemonic": {mnemonic}, + }) if err != nil { return api.ChangeWithdrawalCredentialsResponse{}, fmt.Errorf("Could not change withdrawal creds: %w", err) } @@ -474,7 +493,7 @@ func (c *Client) ChangeWithdrawalCredentials(address common.Address, mnemonic st // Check all of the node's minipools for rescue eligibility, and return the details of the rescuable ones func (c *Client) GetMinipoolRescueDissolvedDetailsForNode() (api.GetMinipoolRescueDissolvedDetailsForNodeResponse, error) { - responseBytes, err := c.callAPI("minipool get-rescue-dissolved-details-for-node") + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/get-rescue-dissolved-details-for-node", nil) if err != nil { return api.GetMinipoolRescueDissolvedDetailsForNodeResponse{}, fmt.Errorf("Could not get get-minipool-rescue-dissolved-details-for-node status: %w", err) } @@ -490,7 +509,15 @@ func (c *Client) GetMinipoolRescueDissolvedDetailsForNode() (api.GetMinipoolResc // Rescue a dissolved minipool by depositing ETH for it to the Beacon deposit contract func (c *Client) RescueDissolvedMinipool(address common.Address, amount *big.Int, submit bool) (api.RescueDissolvedMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("minipool rescue-dissolved %s %s %t", address.Hex(), amount.String(), submit)) + submitStr := "false" + if submit { + submitStr = "true" + } + responseBytes, err := c.callHTTPAPI("POST", "/api/minipool/rescue-dissolved", url.Values{ + "address": {address.Hex()}, + "amount": {amount.String()}, + "submit": {submitStr}, + }) if err != nil { return api.RescueDissolvedMinipoolResponse{}, fmt.Errorf("Could not rescue dissolved minipool: %w", err) } @@ -505,7 +532,7 @@ func (c *Client) RescueDissolvedMinipool(address common.Address, amount *big.Int } func (c *Client) GetBondReductionEnabled() (api.GetBondReductionEnabledResponse, error) { - responseBytes, err := c.callAPI("minipool get-bond-reduction-enabled") + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/get-bond-reduction-enabled", nil) if err != nil { return api.GetBondReductionEnabledResponse{}, fmt.Errorf("Could not get bond reduction enabled status: %w", err) } diff --git a/shared/services/rocketpool/network.go b/shared/services/rocketpool/network.go index d72e30ca8..175462a35 100644 --- a/shared/services/rocketpool/network.go +++ b/shared/services/rocketpool/network.go @@ -3,6 +3,7 @@ package rocketpool import ( "fmt" "math/big" + "net/url" "github.com/goccy/go-json" "github.com/rocket-pool/smartnode/shared/types/api" @@ -10,7 +11,7 @@ import ( // Get network node fee func (c *Client) NodeFee() (api.NodeFeeResponse, error) { - responseBytes, err := c.callAPI("network node-fee") + responseBytes, err := c.callHTTPAPI("GET", "/api/network/node-fee", nil) if err != nil { return api.NodeFeeResponse{}, fmt.Errorf("Could not get network node fee: %w", err) } @@ -26,7 +27,7 @@ func (c *Client) NodeFee() (api.NodeFeeResponse, error) { // Get network RPL price func (c *Client) RplPrice() (api.RplPriceResponse, error) { - responseBytes, err := c.callAPI("network rpl-price") + responseBytes, err := c.callHTTPAPI("GET", "/api/network/rpl-price", nil) if err != nil { return api.RplPriceResponse{}, fmt.Errorf("Could not get network RPL price: %w", err) } @@ -45,7 +46,7 @@ func (c *Client) RplPrice() (api.RplPriceResponse, error) { // Get network stats func (c *Client) NetworkStats() (api.NetworkStatsResponse, error) { - responseBytes, err := c.callAPI("network stats") + responseBytes, err := c.callHTTPAPI("GET", "/api/network/stats", nil) if err != nil { return api.NetworkStatsResponse{}, fmt.Errorf("Could not get network stats: %w", err) } @@ -61,7 +62,7 @@ func (c *Client) NetworkStats() (api.NetworkStatsResponse, error) { // Get the timezone map func (c *Client) TimezoneMap() (api.NetworkTimezonesResponse, error) { - responseBytes, err := c.callAPI("network timezone-map") + responseBytes, err := c.callHTTPAPI("GET", "/api/network/timezone-map", nil) if err != nil { return api.NetworkTimezonesResponse{}, fmt.Errorf("Could not get network timezone map: %w", err) } @@ -77,7 +78,7 @@ func (c *Client) TimezoneMap() (api.NetworkTimezonesResponse, error) { // Check if the rewards tree for the provided interval can be generated func (c *Client) CanGenerateRewardsTree(index uint64) (api.CanNetworkGenerateRewardsTreeResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("network can-generate-rewards-tree %d", index)) + responseBytes, err := c.callHTTPAPI("GET", "/api/network/can-generate-rewards-tree", url.Values{"index": {fmt.Sprintf("%d", index)}}) if err != nil { return api.CanNetworkGenerateRewardsTreeResponse{}, fmt.Errorf("Could not check rewards tree generation status: %w", err) } @@ -93,7 +94,7 @@ func (c *Client) CanGenerateRewardsTree(index uint64) (api.CanNetworkGenerateRew // Set a request marker for the watchtower to generate the rewards tree for the given interval func (c *Client) GenerateRewardsTree(index uint64) (api.NetworkGenerateRewardsTreeResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("network generate-rewards-tree %d", index)) + responseBytes, err := c.callHTTPAPI("POST", "/api/network/generate-rewards-tree", url.Values{"index": {fmt.Sprintf("%d", index)}}) if err != nil { return api.NetworkGenerateRewardsTreeResponse{}, fmt.Errorf("Could not initialize rewards tree generation: %w", err) } @@ -109,7 +110,7 @@ func (c *Client) GenerateRewardsTree(index uint64) (api.NetworkGenerateRewardsTr // GetActiveDAOProposals fetches information about active DAO proposals func (c *Client) GetActiveDAOProposals() (api.NetworkDAOProposalsResponse, error) { - responseBytes, err := c.callAPI("network dao-proposals") + responseBytes, err := c.callHTTPAPI("GET", "/api/network/dao-proposals", nil) if err != nil { return api.NetworkDAOProposalsResponse{}, fmt.Errorf("could not request active DAO proposals: %w", err) } @@ -125,7 +126,7 @@ func (c *Client) GetActiveDAOProposals() (api.NetworkDAOProposalsResponse, error // Download a rewards info file from IPFS for the given interval func (c *Client) DownloadRewardsFile(interval uint64) (api.DownloadRewardsFileResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("network download-rewards-file %d", interval)) + responseBytes, err := c.callHTTPAPI("POST", "/api/network/download-rewards-file", url.Values{"interval": {fmt.Sprintf("%d", interval)}}) if err != nil { return api.DownloadRewardsFileResponse{}, fmt.Errorf("could not download rewards file: %w", err) } @@ -141,7 +142,7 @@ func (c *Client) DownloadRewardsFile(interval uint64) (api.DownloadRewardsFileRe // Get the address of the latest minipool delegate contract func (c *Client) GetLatestDelegate() (api.GetLatestDelegateResponse, error) { - responseBytes, err := c.callAPI("network latest-delegate") + responseBytes, err := c.callHTTPAPI("GET", "/api/network/latest-delegate", nil) if err != nil { return api.GetLatestDelegateResponse{}, fmt.Errorf("could not get latest delegate: %w", err) } diff --git a/shared/services/rocketpool/node.go b/shared/services/rocketpool/node.go index c42278bb2..18771005e 100644 --- a/shared/services/rocketpool/node.go +++ b/shared/services/rocketpool/node.go @@ -1,11 +1,14 @@ package rocketpool import ( + "context" "encoding/hex" "fmt" "math/big" + "net/url" "strconv" "strings" + "time" "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" @@ -17,7 +20,7 @@ import ( // Get node status func (c *Client) NodeStatus() (api.NodeStatusResponse, error) { - responseBytes, err := c.callAPI("node status") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/status", nil) if err != nil { return api.NodeStatusResponse{}, fmt.Errorf("Could not get node status: %w", err) } @@ -55,9 +58,13 @@ func (c *Client) NodeStatus() (api.NodeStatusResponse, error) { return response, nil } -// Get active alerts from Alertmanager +// Get active alerts from Alertmanager. +// Uses a short 2-second timeout: alerts are informational and displayed after +// every command, so they must never block the user if the daemon is not yet up. func (c *Client) NodeAlerts() (api.NodeAlertsResponse, error) { - responseBytes, err := c.callAPI("node alerts") + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + responseBytes, err := c.callHTTPAPICtx(ctx, "GET", "/api/node/alerts", nil) if err != nil { return api.NodeAlertsResponse{}, fmt.Errorf("could not get node alerts: %w", err) } @@ -73,7 +80,7 @@ func (c *Client) NodeAlerts() (api.NodeAlertsResponse, error) { // Check whether the node can be registered func (c *Client) CanRegisterNode(timezoneLocation string) (api.CanRegisterNodeResponse, error) { - responseBytes, err := c.callAPI("node can-register", timezoneLocation) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-register", url.Values{"timezoneLocation": {timezoneLocation}}) if err != nil { return api.CanRegisterNodeResponse{}, fmt.Errorf("Could not get can register node status: %w", err) } @@ -89,7 +96,7 @@ func (c *Client) CanRegisterNode(timezoneLocation string) (api.CanRegisterNodeRe // Register the node func (c *Client) RegisterNode(timezoneLocation string) (api.RegisterNodeResponse, error) { - responseBytes, err := c.callAPI("node register", timezoneLocation) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/register", url.Values{"timezoneLocation": {timezoneLocation}}) if err != nil { return api.RegisterNodeResponse{}, fmt.Errorf("Could not register node: %w", err) } @@ -105,7 +112,10 @@ func (c *Client) RegisterNode(timezoneLocation string) (api.RegisterNodeResponse // Checks if the node's primary withdrawal address can be set func (c *Client) CanSetNodePrimaryWithdrawalAddress(withdrawalAddress common.Address, confirm bool) (api.CanSetNodePrimaryWithdrawalAddressResponse, error) { - responseBytes, err := c.callAPI("node can-set-primary-withdrawal-address", withdrawalAddress.Hex(), strconv.FormatBool(confirm)) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-set-primary-withdrawal-address", url.Values{ + "address": {withdrawalAddress.Hex()}, + "confirm": {strconv.FormatBool(confirm)}, + }) if err != nil { return api.CanSetNodePrimaryWithdrawalAddressResponse{}, fmt.Errorf("Could not get can set node primary withdrawal address: %w", err) } @@ -121,7 +131,10 @@ func (c *Client) CanSetNodePrimaryWithdrawalAddress(withdrawalAddress common.Add // Set the node's primary withdrawal address func (c *Client) SetNodePrimaryWithdrawalAddress(withdrawalAddress common.Address, confirm bool) (api.SetNodePrimaryWithdrawalAddressResponse, error) { - responseBytes, err := c.callAPI("node set-primary-withdrawal-address", withdrawalAddress.Hex(), strconv.FormatBool(confirm)) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/set-primary-withdrawal-address", url.Values{ + "address": {withdrawalAddress.Hex()}, + "confirm": {strconv.FormatBool(confirm)}, + }) if err != nil { return api.SetNodePrimaryWithdrawalAddressResponse{}, fmt.Errorf("Could not set node primary withdrawal address: %w", err) } @@ -137,7 +150,7 @@ func (c *Client) SetNodePrimaryWithdrawalAddress(withdrawalAddress common.Addres // Checks if the node's primary withdrawal address can be confirmed func (c *Client) CanConfirmNodePrimaryWithdrawalAddress() (api.CanSetNodePrimaryWithdrawalAddressResponse, error) { - responseBytes, err := c.callAPI("node can-confirm-primary-withdrawal-address") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-confirm-primary-withdrawal-address", nil) if err != nil { return api.CanSetNodePrimaryWithdrawalAddressResponse{}, fmt.Errorf("Could not get can confirm node primary withdrawal address: %w", err) } @@ -153,7 +166,7 @@ func (c *Client) CanConfirmNodePrimaryWithdrawalAddress() (api.CanSetNodePrimary // Confirm the node's primary withdrawal address func (c *Client) ConfirmNodePrimaryWithdrawalAddress() (api.SetNodePrimaryWithdrawalAddressResponse, error) { - responseBytes, err := c.callAPI("node confirm-primary-withdrawal-address") + responseBytes, err := c.callHTTPAPI("POST", "/api/node/confirm-primary-withdrawal-address", nil) if err != nil { return api.SetNodePrimaryWithdrawalAddressResponse{}, fmt.Errorf("Could not confirm node primary withdrawal address: %w", err) } @@ -169,7 +182,10 @@ func (c *Client) ConfirmNodePrimaryWithdrawalAddress() (api.SetNodePrimaryWithdr // Checks if the node's RPL withdrawal address can be set func (c *Client) CanSetNodeRPLWithdrawalAddress(withdrawalAddress common.Address, confirm bool) (api.CanSetNodeRPLWithdrawalAddressResponse, error) { - responseBytes, err := c.callAPI("node can-set-rpl-withdrawal-address", withdrawalAddress.Hex(), strconv.FormatBool(confirm)) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-set-rpl-withdrawal-address", url.Values{ + "address": {withdrawalAddress.Hex()}, + "confirm": {strconv.FormatBool(confirm)}, + }) if err != nil { return api.CanSetNodeRPLWithdrawalAddressResponse{}, fmt.Errorf("Could not get can set node RPL withdrawal address: %w", err) } @@ -185,7 +201,10 @@ func (c *Client) CanSetNodeRPLWithdrawalAddress(withdrawalAddress common.Address // Set the node's RPL withdrawal address func (c *Client) SetNodeRPLWithdrawalAddress(withdrawalAddress common.Address, confirm bool) (api.SetNodeRPLWithdrawalAddressResponse, error) { - responseBytes, err := c.callAPI("node set-rpl-withdrawal-address", withdrawalAddress.Hex(), strconv.FormatBool(confirm)) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/set-rpl-withdrawal-address", url.Values{ + "address": {withdrawalAddress.Hex()}, + "confirm": {strconv.FormatBool(confirm)}, + }) if err != nil { return api.SetNodeRPLWithdrawalAddressResponse{}, fmt.Errorf("Could not set node RPL withdrawal address: %w", err) } @@ -201,7 +220,7 @@ func (c *Client) SetNodeRPLWithdrawalAddress(withdrawalAddress common.Address, c // Checks if the node's RPL withdrawal address can be confirmed func (c *Client) CanConfirmNodeRPLWithdrawalAddress() (api.CanSetNodeRPLWithdrawalAddressResponse, error) { - responseBytes, err := c.callAPI("node can-confirm-rpl-withdrawal-address") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-confirm-rpl-withdrawal-address", nil) if err != nil { return api.CanSetNodeRPLWithdrawalAddressResponse{}, fmt.Errorf("Could not get can confirm node RPL withdrawal address: %w", err) } @@ -217,7 +236,7 @@ func (c *Client) CanConfirmNodeRPLWithdrawalAddress() (api.CanSetNodeRPLWithdraw // Confirm the node's RPL withdrawal address func (c *Client) ConfirmNodeRPLWithdrawalAddress() (api.SetNodeRPLWithdrawalAddressResponse, error) { - responseBytes, err := c.callAPI("node confirm-rpl-withdrawal-address") + responseBytes, err := c.callHTTPAPI("POST", "/api/node/confirm-rpl-withdrawal-address", nil) if err != nil { return api.SetNodeRPLWithdrawalAddressResponse{}, fmt.Errorf("Could not confirm node RPL withdrawal address: %w", err) } @@ -233,7 +252,7 @@ func (c *Client) ConfirmNodeRPLWithdrawalAddress() (api.SetNodeRPLWithdrawalAddr // Checks if the node's timezone location can be set func (c *Client) CanSetNodeTimezone(timezoneLocation string) (api.CanSetNodeTimezoneResponse, error) { - responseBytes, err := c.callAPI("node can-set-timezone", timezoneLocation) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-set-timezone", url.Values{"timezoneLocation": {timezoneLocation}}) if err != nil { return api.CanSetNodeTimezoneResponse{}, fmt.Errorf("Could not get can set node timezone: %w", err) } @@ -249,7 +268,7 @@ func (c *Client) CanSetNodeTimezone(timezoneLocation string) (api.CanSetNodeTime // Set the node's timezone location func (c *Client) SetNodeTimezone(timezoneLocation string) (api.SetNodeTimezoneResponse, error) { - responseBytes, err := c.callAPI("node set-timezone", timezoneLocation) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/set-timezone", url.Values{"timezoneLocation": {timezoneLocation}}) if err != nil { return api.SetNodeTimezoneResponse{}, fmt.Errorf("Could not set node timezone: %w", err) } @@ -265,7 +284,7 @@ func (c *Client) SetNodeTimezone(timezoneLocation string) (api.SetNodeTimezoneRe // Check whether the node can swap RPL tokens func (c *Client) CanNodeSwapRpl(amountWei *big.Int) (api.CanNodeSwapRplResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-swap-rpl %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-swap-rpl", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.CanNodeSwapRplResponse{}, fmt.Errorf("Could not get can node swap RPL status: %w", err) } @@ -281,7 +300,7 @@ func (c *Client) CanNodeSwapRpl(amountWei *big.Int) (api.CanNodeSwapRplResponse, // Get the gas estimate for approving legacy RPL interaction func (c *Client) NodeSwapRplApprovalGas(amountWei *big.Int) (api.NodeSwapRplApproveGasResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node get-swap-rpl-approval-gas %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/get-swap-rpl-approval-gas", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.NodeSwapRplApproveGasResponse{}, fmt.Errorf("Could not get old RPL approval gas: %w", err) } @@ -297,7 +316,7 @@ func (c *Client) NodeSwapRplApprovalGas(amountWei *big.Int) (api.NodeSwapRplAppr // Approves old RPL for a token swap func (c *Client) NodeSwapRplApprove(amountWei *big.Int) (api.NodeSwapRplApproveResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node swap-rpl-approve-rpl %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/swap-rpl-approve-rpl", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.NodeSwapRplApproveResponse{}, fmt.Errorf("Could not approve old RPL: %w", err) } @@ -313,7 +332,10 @@ func (c *Client) NodeSwapRplApprove(amountWei *big.Int) (api.NodeSwapRplApproveR // Swap node's old RPL tokens for new RPL tokens, waiting for the approval to be included in a block first func (c *Client) NodeWaitAndSwapRpl(amountWei *big.Int, approvalTxHash common.Hash) (api.NodeSwapRplSwapResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node wait-and-swap-rpl %s %s", amountWei.String(), approvalTxHash.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/wait-and-swap-rpl", url.Values{ + "amountWei": {amountWei.String()}, + "approvalTxHash": {approvalTxHash.Hex()}, + }) if err != nil { return api.NodeSwapRplSwapResponse{}, fmt.Errorf("Could not swap node's RPL tokens: %w", err) } @@ -329,7 +351,7 @@ func (c *Client) NodeWaitAndSwapRpl(amountWei *big.Int, approvalTxHash common.Ha // Swap node's old RPL tokens for new RPL tokens func (c *Client) NodeSwapRpl(amountWei *big.Int) (api.NodeSwapRplSwapResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node swap-rpl %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/swap-rpl", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.NodeSwapRplSwapResponse{}, fmt.Errorf("Could not swap node's RPL tokens: %w", err) } @@ -345,7 +367,7 @@ func (c *Client) NodeSwapRpl(amountWei *big.Int) (api.NodeSwapRplSwapResponse, e // Get a node's legacy RPL allowance for swapping on the new RPL contract func (c *Client) GetNodeSwapRplAllowance() (api.NodeSwapRplAllowanceResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node swap-rpl-allowance")) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/swap-rpl-allowance", nil) if err != nil { return api.NodeSwapRplAllowanceResponse{}, fmt.Errorf("Could not get node swap RPL allowance: %w", err) } @@ -361,7 +383,7 @@ func (c *Client) GetNodeSwapRplAllowance() (api.NodeSwapRplAllowanceResponse, er // Check whether the node can stake RPL func (c *Client) CanNodeStakeRpl(amountWei *big.Int) (api.CanNodeStakeRplResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-stake-rpl %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-stake-rpl", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.CanNodeStakeRplResponse{}, fmt.Errorf("Could not get can node stake RPL status: %w", err) } @@ -377,7 +399,7 @@ func (c *Client) CanNodeStakeRpl(amountWei *big.Int) (api.CanNodeStakeRplRespons // Get the gas estimate for approving new RPL interaction func (c *Client) NodeStakeRplApprovalGas(amountWei *big.Int) (api.NodeStakeRplApproveGasResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node get-stake-rpl-approval-gas %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/get-stake-rpl-approval-gas", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.NodeStakeRplApproveGasResponse{}, fmt.Errorf("Could not get new RPL approval gas: %w", err) } @@ -393,7 +415,7 @@ func (c *Client) NodeStakeRplApprovalGas(amountWei *big.Int) (api.NodeStakeRplAp // Approve RPL for staking against the node func (c *Client) NodeStakeRplApprove(amountWei *big.Int) (api.NodeStakeRplApproveResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node stake-rpl-approve-rpl %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/stake-rpl-approve-rpl", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.NodeStakeRplApproveResponse{}, fmt.Errorf("Could not approve RPL for staking: %w", err) } @@ -409,7 +431,10 @@ func (c *Client) NodeStakeRplApprove(amountWei *big.Int) (api.NodeStakeRplApprov // Stake RPL against the node waiting for approvalTxHash to be included in a block first func (c *Client) NodeWaitAndStakeRpl(amountWei *big.Int, approvalTxHash common.Hash) (api.NodeStakeRplStakeResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node wait-and-stake-rpl %s %s", amountWei.String(), approvalTxHash.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/wait-and-stake-rpl", url.Values{ + "amountWei": {amountWei.String()}, + "approvalTxHash": {approvalTxHash.Hex()}, + }) if err != nil { return api.NodeStakeRplStakeResponse{}, fmt.Errorf("Could not stake node RPL: %w", err) } @@ -425,7 +450,7 @@ func (c *Client) NodeWaitAndStakeRpl(amountWei *big.Int, approvalTxHash common.H // Stake RPL against the node func (c *Client) NodeStakeRpl(amountWei *big.Int) (api.NodeStakeRplStakeResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node stake-rpl %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/stake-rpl", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.NodeStakeRplStakeResponse{}, fmt.Errorf("Could not stake node RPL: %w", err) } @@ -441,7 +466,7 @@ func (c *Client) NodeStakeRpl(amountWei *big.Int) (api.NodeStakeRplStakeResponse // Get a node's RPL allowance for the staking contract func (c *Client) GetNodeStakeRplAllowance() (api.NodeStakeRplAllowanceResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node stake-rpl-allowance")) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/stake-rpl-allowance", nil) if err != nil { return api.NodeStakeRplAllowanceResponse{}, fmt.Errorf("Could not get node stake RPL allowance: %w", err) } @@ -457,7 +482,7 @@ func (c *Client) GetNodeStakeRplAllowance() (api.NodeStakeRplAllowanceResponse, // Checks if the node operator can set RPL locking allowed func (c *Client) CanSetRPLLockingAllowed(allowed bool) (api.CanSetRplLockingAllowedResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-set-rpl-locking-allowed %t", allowed)) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-set-rpl-locking-allowed", url.Values{"allowed": {strconv.FormatBool(allowed)}}) if err != nil { return api.CanSetRplLockingAllowedResponse{}, fmt.Errorf("Could not get can set RPL locking allowed: %w", err) } @@ -473,7 +498,7 @@ func (c *Client) CanSetRPLLockingAllowed(allowed bool) (api.CanSetRplLockingAllo // Sets the allow state for the node to lock RPL func (c *Client) SetRPLLockingAllowed(allowed bool) (api.SetRplLockingAllowedResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node set-rpl-locking-allowed %t", allowed)) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/set-rpl-locking-allowed", url.Values{"allowed": {strconv.FormatBool(allowed)}}) if err != nil { return api.SetRplLockingAllowedResponse{}, fmt.Errorf("Could not set RPL locking allowed: %w", err) } @@ -489,7 +514,10 @@ func (c *Client) SetRPLLockingAllowed(allowed bool) (api.SetRplLockingAllowedRes // Checks if the node operator can set RPL stake for allowed func (c *Client) CanSetStakeRPLForAllowed(caller common.Address, allowed bool) (api.CanSetStakeRplForAllowedResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-set-stake-rpl-for-allowed %s %t", caller.Hex(), allowed)) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-set-stake-rpl-for-allowed", url.Values{ + "caller": {caller.Hex()}, + "allowed": {strconv.FormatBool(allowed)}, + }) if err != nil { return api.CanSetStakeRplForAllowedResponse{}, fmt.Errorf("Could not get can set stake RPL for allowed: %w", err) } @@ -505,7 +533,10 @@ func (c *Client) CanSetStakeRPLForAllowed(caller common.Address, allowed bool) ( // Sets the allow state of another address staking on behalf of the node func (c *Client) SetStakeRPLForAllowed(caller common.Address, allowed bool) (api.SetStakeRplForAllowedResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node set-stake-rpl-for-allowed %s %t", caller.Hex(), allowed)) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/set-stake-rpl-for-allowed", url.Values{ + "caller": {caller.Hex()}, + "allowed": {strconv.FormatBool(allowed)}, + }) if err != nil { return api.SetStakeRplForAllowedResponse{}, fmt.Errorf("Could not set stake RPL for allowed: %w", err) } @@ -521,7 +552,7 @@ func (c *Client) SetStakeRPLForAllowed(caller common.Address, allowed bool) (api // Check whether the node can withdraw RPL func (c *Client) CanNodeWithdrawRpl() (api.CanNodeWithdrawRplResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-withdraw-rpl")) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-withdraw-rpl", nil) if err != nil { return api.CanNodeWithdrawRplResponse{}, fmt.Errorf("Could not get can node withdraw RPL status: %w", err) } @@ -537,7 +568,7 @@ func (c *Client) CanNodeWithdrawRpl() (api.CanNodeWithdrawRplResponse, error) { // Withdraw RPL staked against the node func (c *Client) NodeWithdrawRpl() (api.NodeWithdrawRplResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node withdraw-rpl")) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/withdraw-rpl", nil) if err != nil { return api.NodeWithdrawRplResponse{}, fmt.Errorf("Could not withdraw node RPL: %w", err) } @@ -553,7 +584,7 @@ func (c *Client) NodeWithdrawRpl() (api.NodeWithdrawRplResponse, error) { // Check whether the node can unstake legacy RPL func (c *Client) CanNodeUnstakeLegacyRpl(amountWei *big.Int) (api.CanNodeUnstakeLegacyRplResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-unstake-legacy-rpl %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-unstake-legacy-rpl", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.CanNodeUnstakeLegacyRplResponse{}, fmt.Errorf("Could not get can node unstake legacy RPL status: %w", err) } @@ -569,7 +600,7 @@ func (c *Client) CanNodeUnstakeLegacyRpl(amountWei *big.Int) (api.CanNodeUnstake // Unstake legacy RPL staked against the node func (c *Client) NodeUnstakeLegacyRpl(amountWei *big.Int) (api.NodeUnstakeLegacyRplResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node unstake-legacy-rpl %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/unstake-legacy-rpl", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.NodeUnstakeLegacyRplResponse{}, fmt.Errorf("Could not unstake node legacy RPL: %w", err) } @@ -586,7 +617,7 @@ func (c *Client) NodeUnstakeLegacyRpl(amountWei *big.Int) (api.NodeUnstakeLegacy // Check whether the node can withdraw RPL // Used if saturn is not deployed (v1.3.1) func (c *Client) CanNodeWithdrawRplV1_3_1(amountWei *big.Int) (api.CanNodeWithdrawRplv1_3_1Response, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-withdraw-rpl-v131 %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-withdraw-rpl-v131", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.CanNodeWithdrawRplv1_3_1Response{}, fmt.Errorf("Could not get can node withdraw RPL status: %w", err) } @@ -603,7 +634,7 @@ func (c *Client) CanNodeWithdrawRplV1_3_1(amountWei *big.Int) (api.CanNodeWithdr // Withdraw RPL staked against the node // Used if saturn is not deployed (v1.3.1) func (c *Client) NodeWithdrawRplV1_3_1(amountWei *big.Int) (api.NodeWithdrawRplResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node withdraw-rpl-v131 %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/withdraw-rpl-v131", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.NodeWithdrawRplResponse{}, fmt.Errorf("Could not withdraw node RPL: %w", err) } @@ -619,7 +650,7 @@ func (c *Client) NodeWithdrawRplV1_3_1(amountWei *big.Int) (api.NodeWithdrawRplR // Check whether the node can unstake RPL func (c *Client) CanNodeUnstakeRpl(amountWei *big.Int) (api.CanNodeUnstakeRplResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-unstake-rpl %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-unstake-rpl", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.CanNodeUnstakeRplResponse{}, fmt.Errorf("Could not get can node unstake RPL status: %w", err) } @@ -635,7 +666,7 @@ func (c *Client) CanNodeUnstakeRpl(amountWei *big.Int) (api.CanNodeUnstakeRplRes // Unstake RPL staked against the node func (c *Client) NodeUnstakeRpl(amountWei *big.Int) (api.NodeUnstakeRplResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node unstake-rpl %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/unstake-rpl", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.NodeUnstakeRplResponse{}, fmt.Errorf("Could not unstake node RPL: %w", err) } @@ -651,7 +682,7 @@ func (c *Client) NodeUnstakeRpl(amountWei *big.Int) (api.NodeUnstakeRplResponse, // Check whether we can withdraw ETH staked on behalf of the node func (c *Client) CanNodeWithdrawEth(amountWei *big.Int) (api.CanNodeWithdrawEthResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-withdraw-eth %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-withdraw-eth", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.CanNodeWithdrawEthResponse{}, fmt.Errorf("Could not get can node withdraw ETH status: %w", err) } @@ -667,7 +698,7 @@ func (c *Client) CanNodeWithdrawEth(amountWei *big.Int) (api.CanNodeWithdrawEthR // Withdraw ETH staked on behalf of the node func (c *Client) NodeWithdrawEth(amountWei *big.Int) (api.NodeWithdrawEthResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node withdraw-eth %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/withdraw-eth", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.NodeWithdrawEthResponse{}, fmt.Errorf("Could not withdraw node ETH: %w", err) } @@ -683,7 +714,7 @@ func (c *Client) NodeWithdrawEth(amountWei *big.Int) (api.NodeWithdrawEthRespons // Check whether we can withdraw credit from the node func (c *Client) CanNodeWithdrawCredit(amountWei *big.Int) (api.CanNodeWithdrawCreditResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-withdraw-credit %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-withdraw-credit", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.CanNodeWithdrawCreditResponse{}, fmt.Errorf("Could not get can node withdraw credit status: %w", err) } @@ -699,7 +730,7 @@ func (c *Client) CanNodeWithdrawCredit(amountWei *big.Int) (api.CanNodeWithdrawC // Withdraw credit from the node as rETH func (c *Client) NodeWithdrawCredit(amountWei *big.Int) (api.NodeWithdrawCreditResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node withdraw-credit %s", amountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/withdraw-credit", url.Values{"amountWei": {amountWei.String()}}) if err != nil { return api.NodeWithdrawCreditResponse{}, fmt.Errorf("Could not withdraw credit: %w", err) } @@ -715,7 +746,13 @@ func (c *Client) NodeWithdrawCredit(amountWei *big.Int) (api.NodeWithdrawCreditR // Check whether the node can make multiple deposits func (c *Client) CanNodeDeposits(count uint64, amountWei *big.Int, minFee float64, salt *big.Int, expressTickets uint64) (api.CanNodeDepositsResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-deposit %s %f %s %d %d", amountWei.String(), minFee, salt.String(), expressTickets, count)) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-deposit", url.Values{ + "count": {strconv.FormatUint(count, 10)}, + "amountWei": {amountWei.String()}, + "minFee": {strconv.FormatFloat(minFee, 'f', -1, 64)}, + "salt": {salt.String()}, + "expressTickets": {strconv.FormatUint(expressTickets, 10)}, + }) if err != nil { return api.CanNodeDepositsResponse{}, fmt.Errorf("Could not get can node deposits status: %w", err) } @@ -731,7 +768,15 @@ func (c *Client) CanNodeDeposits(count uint64, amountWei *big.Int, minFee float6 // Make multiple node deposits func (c *Client) NodeDeposits(count uint64, amountWei *big.Int, minFee float64, salt *big.Int, useCreditBalance bool, expressTickets uint64, submit bool) (api.NodeDepositsResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node deposit %s %f %s %t %d %t %d", amountWei.String(), minFee, salt.String(), useCreditBalance, expressTickets, submit, count)) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/deposit", url.Values{ + "count": {strconv.FormatUint(count, 10)}, + "amountWei": {amountWei.String()}, + "minFee": {strconv.FormatFloat(minFee, 'f', -1, 64)}, + "salt": {salt.String()}, + "expressTickets": {strconv.FormatUint(expressTickets, 10)}, + "useCreditBalance": {strconv.FormatBool(useCreditBalance)}, + "submit": {strconv.FormatBool(submit)}, + }) if err != nil { return api.NodeDepositsResponse{}, fmt.Errorf("Could not make node deposits: %w", err) } @@ -747,7 +792,11 @@ func (c *Client) NodeDeposits(count uint64, amountWei *big.Int, minFee float64, // Check whether the node can send tokens func (c *Client) CanNodeSend(amountRaw float64, token string, toAddress common.Address) (api.CanNodeSendResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-send %.10f %s %s", amountRaw, token, toAddress.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-send", url.Values{ + "amountRaw": {strconv.FormatFloat(amountRaw, 'f', 10, 64)}, + "token": {token}, + "to": {toAddress.Hex()}, + }) if err != nil { return api.CanNodeSendResponse{}, fmt.Errorf("Could not get can node send status: %w", err) } @@ -763,7 +812,11 @@ func (c *Client) CanNodeSend(amountRaw float64, token string, toAddress common.A // Send tokens from the node to an address func (c *Client) NodeSend(amountRaw float64, token string, toAddress common.Address) (api.NodeSendResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node send %.10f %s %s", amountRaw, token, toAddress.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/send", url.Values{ + "amountRaw": {strconv.FormatFloat(amountRaw, 'f', 10, 64)}, + "token": {token}, + "to": {toAddress.Hex()}, + }) if err != nil { return api.NodeSendResponse{}, fmt.Errorf("Could not send tokens from node: %w", err) } @@ -780,7 +833,10 @@ func (c *Client) NodeSend(amountRaw float64, token string, toAddress common.Addr // Send all tokens of the given type from the node to an address. // Uses the exact on-chain *big.Int balance to avoid float64 rounding errors. func (c *Client) NodeSendAll(token string, toAddress common.Address) (api.NodeSendResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node send-all %s %s", token, toAddress.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/send-all", url.Values{ + "token": {token}, + "to": {toAddress.Hex()}, + }) if err != nil { return api.NodeSendResponse{}, fmt.Errorf("Could not send tokens from node: %w", err) } @@ -796,7 +852,10 @@ func (c *Client) NodeSendAll(token string, toAddress common.Address) (api.NodeSe // Check whether the node can burn tokens func (c *Client) CanNodeBurn(amountWei *big.Int, token string) (api.CanNodeBurnResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-burn %s %s", amountWei.String(), token)) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-burn", url.Values{ + "amountWei": {amountWei.String()}, + "token": {token}, + }) if err != nil { return api.CanNodeBurnResponse{}, fmt.Errorf("Could not get can node burn status: %w", err) } @@ -812,7 +871,10 @@ func (c *Client) CanNodeBurn(amountWei *big.Int, token string) (api.CanNodeBurnR // Burn tokens owned by the node for ETH func (c *Client) NodeBurn(amountWei *big.Int, token string) (api.NodeBurnResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node burn %s %s", amountWei.String(), token)) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/burn", url.Values{ + "amountWei": {amountWei.String()}, + "token": {token}, + }) if err != nil { return api.NodeBurnResponse{}, fmt.Errorf("Could not burn tokens owned by node: %w", err) } @@ -828,7 +890,7 @@ func (c *Client) NodeBurn(amountWei *big.Int, token string) (api.NodeBurnRespons // Get node sync progress func (c *Client) NodeSync() (api.NodeSyncProgressResponse, error) { - responseBytes, err := c.callAPI("node sync") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/sync", nil) if err != nil { return api.NodeSyncProgressResponse{}, fmt.Errorf("Could not get node sync: %w", err) } @@ -844,7 +906,7 @@ func (c *Client) NodeSync() (api.NodeSyncProgressResponse, error) { // Check whether the node has RPL rewards available to claim func (c *Client) CanNodeClaimRpl() (api.CanNodeClaimRplResponse, error) { - responseBytes, err := c.callAPI("node can-claim-rpl-rewards") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-claim-rpl-rewards", nil) if err != nil { return api.CanNodeClaimRplResponse{}, fmt.Errorf("Could not get can node claim rpl rewards status: %w", err) } @@ -860,7 +922,7 @@ func (c *Client) CanNodeClaimRpl() (api.CanNodeClaimRplResponse, error) { // Claim available RPL rewards func (c *Client) NodeClaimRpl() (api.NodeClaimRplResponse, error) { - responseBytes, err := c.callAPI("node claim-rpl-rewards") + responseBytes, err := c.callHTTPAPI("POST", "/api/node/claim-rpl-rewards", nil) if err != nil { return api.NodeClaimRplResponse{}, fmt.Errorf("Could not claim rpl rewards: %w", err) } @@ -876,7 +938,7 @@ func (c *Client) NodeClaimRpl() (api.NodeClaimRplResponse, error) { // Get node RPL rewards status func (c *Client) NodeRewards() (api.NodeRewardsResponse, error) { - responseBytes, err := c.callAPI("node rewards") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/rewards", nil) if err != nil { return api.NodeRewardsResponse{}, fmt.Errorf("Could not get node rewards: %w", err) } @@ -892,7 +954,7 @@ func (c *Client) NodeRewards() (api.NodeRewardsResponse, error) { // Get the deposit contract info for Rocket Pool and the Beacon Client func (c *Client) DepositContractInfo() (api.DepositContractInfoResponse, error) { - responseBytes, err := c.callAPI("node deposit-contract-info") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/deposit-contract-info", nil) if err != nil { return api.DepositContractInfoResponse{}, fmt.Errorf("Could not get deposit contract info: %w", err) } @@ -908,7 +970,7 @@ func (c *Client) DepositContractInfo() (api.DepositContractInfoResponse, error) // Get the initialization status of the fee distributor contract func (c *Client) IsFeeDistributorInitialized() (api.NodeIsFeeDistributorInitializedResponse, error) { - responseBytes, err := c.callAPI("node is-fee-distributor-initialized") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/is-fee-distributor-initialized", nil) if err != nil { return api.NodeIsFeeDistributorInitializedResponse{}, fmt.Errorf("Could not get fee distributor initialization status: %w", err) } @@ -924,7 +986,7 @@ func (c *Client) IsFeeDistributorInitialized() (api.NodeIsFeeDistributorInitiali // Get the gas cost for initializing the fee distributor contract func (c *Client) GetInitializeFeeDistributorGas() (api.NodeInitializeFeeDistributorGasResponse, error) { - responseBytes, err := c.callAPI("node get-initialize-fee-distributor-gas") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/get-initialize-fee-distributor-gas", nil) if err != nil { return api.NodeInitializeFeeDistributorGasResponse{}, fmt.Errorf("Could not get initialize fee distributor gas: %w", err) } @@ -940,7 +1002,7 @@ func (c *Client) GetInitializeFeeDistributorGas() (api.NodeInitializeFeeDistribu // Initialize the fee distributor contract func (c *Client) InitializeFeeDistributor() (api.NodeInitializeFeeDistributorResponse, error) { - responseBytes, err := c.callAPI("node initialize-fee-distributor") + responseBytes, err := c.callHTTPAPI("POST", "/api/node/initialize-fee-distributor", nil) if err != nil { return api.NodeInitializeFeeDistributorResponse{}, fmt.Errorf("Could not initialize fee distributor: %w", err) } @@ -956,7 +1018,7 @@ func (c *Client) InitializeFeeDistributor() (api.NodeInitializeFeeDistributorRes // Check if distributing ETH from the node's fee distributor is possible func (c *Client) CanDistribute() (api.NodeCanDistributeResponse, error) { - responseBytes, err := c.callAPI("node can-distribute") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-distribute", nil) if err != nil { return api.NodeCanDistributeResponse{}, fmt.Errorf("Could not get can distribute: %w", err) } @@ -972,7 +1034,7 @@ func (c *Client) CanDistribute() (api.NodeCanDistributeResponse, error) { // Distribute ETH from the node's fee distributor func (c *Client) Distribute() (api.NodeDistributeResponse, error) { - responseBytes, err := c.callAPI("node distribute") + responseBytes, err := c.callHTTPAPI("POST", "/api/node/distribute", nil) if err != nil { return api.NodeDistributeResponse{}, fmt.Errorf("Could not distribute ETH: %w", err) } @@ -988,7 +1050,7 @@ func (c *Client) Distribute() (api.NodeDistributeResponse, error) { // Get info about your eligible rewards periods, including balances and Merkle proofs func (c *Client) GetRewardsInfo() (api.NodeGetRewardsInfoResponse, error) { - responseBytes, err := c.callAPI("node get-rewards-info") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/get-rewards-info", nil) if err != nil { return api.NodeGetRewardsInfoResponse{}, fmt.Errorf("Could not get rewards info: %w", err) } @@ -1004,11 +1066,11 @@ func (c *Client) GetRewardsInfo() (api.NodeGetRewardsInfoResponse, error) { // Check if the rewards for the given intervals can be claimed func (c *Client) CanNodeClaimRewards(indices []uint64) (api.CanNodeClaimRewardsResponse, error) { - indexStrings := []string{} - for _, index := range indices { - indexStrings = append(indexStrings, fmt.Sprint(index)) + indexStrings := make([]string, len(indices)) + for i, idx := range indices { + indexStrings[i] = strconv.FormatUint(idx, 10) } - responseBytes, err := c.callAPI("node can-claim-rewards", strings.Join(indexStrings, ",")) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-claim-rewards", url.Values{"indices": {strings.Join(indexStrings, ",")}}) if err != nil { return api.CanNodeClaimRewardsResponse{}, fmt.Errorf("Could not check if can claim rewards: %w", err) } @@ -1024,11 +1086,11 @@ func (c *Client) CanNodeClaimRewards(indices []uint64) (api.CanNodeClaimRewardsR // Claim rewards for the given reward intervals func (c *Client) NodeClaimRewards(indices []uint64) (api.NodeClaimRewardsResponse, error) { - indexStrings := []string{} - for _, index := range indices { - indexStrings = append(indexStrings, fmt.Sprint(index)) + indexStrings := make([]string, len(indices)) + for i, idx := range indices { + indexStrings[i] = strconv.FormatUint(idx, 10) } - responseBytes, err := c.callAPI("node claim-rewards", strings.Join(indexStrings, ",")) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/claim-rewards", url.Values{"indices": {strings.Join(indexStrings, ",")}}) if err != nil { return api.NodeClaimRewardsResponse{}, fmt.Errorf("Could not claim rewards: %w", err) } @@ -1044,11 +1106,14 @@ func (c *Client) NodeClaimRewards(indices []uint64) (api.NodeClaimRewardsRespons // Check if the rewards for the given intervals can be claimed, and RPL restaked automatically func (c *Client) CanNodeClaimAndStakeRewards(indices []uint64, stakeAmountWei *big.Int) (api.CanNodeClaimAndStakeRewardsResponse, error) { - indexStrings := []string{} - for _, index := range indices { - indexStrings = append(indexStrings, fmt.Sprint(index)) + indexStrings := make([]string, len(indices)) + for i, idx := range indices { + indexStrings[i] = strconv.FormatUint(idx, 10) } - responseBytes, err := c.callAPI("node can-claim-and-stake-rewards", strings.Join(indexStrings, ","), stakeAmountWei.String()) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-claim-and-stake-rewards", url.Values{ + "indices": {strings.Join(indexStrings, ",")}, + "stakeAmount": {stakeAmountWei.String()}, + }) if err != nil { return api.CanNodeClaimAndStakeRewardsResponse{}, fmt.Errorf("Could not check if can claim and stake rewards: %w", err) } @@ -1064,11 +1129,14 @@ func (c *Client) CanNodeClaimAndStakeRewards(indices []uint64, stakeAmountWei *b // Claim rewards for the given reward intervals and restake RPL automatically func (c *Client) NodeClaimAndStakeRewards(indices []uint64, stakeAmountWei *big.Int) (api.NodeClaimAndStakeRewardsResponse, error) { - indexStrings := []string{} - for _, index := range indices { - indexStrings = append(indexStrings, fmt.Sprint(index)) + indexStrings := make([]string, len(indices)) + for i, idx := range indices { + indexStrings[i] = strconv.FormatUint(idx, 10) } - responseBytes, err := c.callAPI("node claim-and-stake-rewards", strings.Join(indexStrings, ","), stakeAmountWei.String()) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/claim-and-stake-rewards", url.Values{ + "indices": {strings.Join(indexStrings, ",")}, + "stakeAmount": {stakeAmountWei.String()}, + }) if err != nil { return api.NodeClaimAndStakeRewardsResponse{}, fmt.Errorf("Could not claim and stake rewards: %w", err) } @@ -1084,7 +1152,7 @@ func (c *Client) NodeClaimAndStakeRewards(indices []uint64, stakeAmountWei *big. // Check whether or not the node is opted into the Smoothing Pool func (c *Client) NodeGetSmoothingPoolRegistrationStatus() (api.GetSmoothingPoolRegistrationStatusResponse, error) { - responseBytes, err := c.callAPI("node get-smoothing-pool-registration-status") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/get-smoothing-pool-registration-status", nil) if err != nil { return api.GetSmoothingPoolRegistrationStatusResponse{}, fmt.Errorf("Could not get smoothing pool registration status: %w", err) } @@ -1100,7 +1168,7 @@ func (c *Client) NodeGetSmoothingPoolRegistrationStatus() (api.GetSmoothingPoolR // Check if the node's Smoothing Pool status can be changed func (c *Client) CanNodeSetSmoothingPoolStatus(status bool) (api.CanSetSmoothingPoolRegistrationStatusResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-set-smoothing-pool-status %t", status)) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-set-smoothing-pool-status", url.Values{"status": {strconv.FormatBool(status)}}) if err != nil { return api.CanSetSmoothingPoolRegistrationStatusResponse{}, fmt.Errorf("Could not get can-set-smoothing-pool-status: %w", err) } @@ -1116,7 +1184,7 @@ func (c *Client) CanNodeSetSmoothingPoolStatus(status bool) (api.CanSetSmoothing // Sets the node's Smoothing Pool opt-in status func (c *Client) NodeSetSmoothingPoolStatus(status bool) (api.SetSmoothingPoolRegistrationStatusResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node set-smoothing-pool-status %t", status)) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/set-smoothing-pool-status", url.Values{"status": {strconv.FormatBool(status)}}) if err != nil { return api.SetSmoothingPoolRegistrationStatusResponse{}, fmt.Errorf("Could not set smoothing pool status: %w", err) } @@ -1131,7 +1199,7 @@ func (c *Client) NodeSetSmoothingPoolStatus(status bool) (api.SetSmoothingPoolRe } func (c *Client) ResolveEnsName(name string) (api.ResolveEnsNameResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node resolve-ens-name %s", name)) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/resolve-ens-name", url.Values{"name": {name}}) if err != nil { return api.ResolveEnsNameResponse{}, fmt.Errorf("Could not resolve ENS name: %w", err) } @@ -1144,8 +1212,9 @@ func (c *Client) ResolveEnsName(name string) (api.ResolveEnsNameResponse, error) } return response, nil } + func (c *Client) ReverseResolveEnsName(name string) (api.ResolveEnsNameResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node reverse-resolve-ens-name %s", name)) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/reverse-resolve-ens-name", url.Values{"address": {name}}) if err != nil { return api.ResolveEnsNameResponse{}, fmt.Errorf("Could not reverse resolve ENS name: %w", err) } @@ -1161,9 +1230,7 @@ func (c *Client) ReverseResolveEnsName(name string) (api.ResolveEnsNameResponse, // Use the node private key to sign an arbitrary message func (c *Client) SignMessage(message string) (api.NodeSignResponse, error) { - // Ignore sync status so we can sign messages even without ready clients - c.ignoreSyncCheck = true - responseBytes, err := c.callAPI("node sign-message", message) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/sign-message", url.Values{"message": {message}}) if err != nil { return api.NodeSignResponse{}, fmt.Errorf("Could not sign message: %w", err) } @@ -1180,7 +1247,12 @@ func (c *Client) SignMessage(message string) (api.NodeSignResponse, error) { // Check whether a vacant minipool can be created for solo staker migration func (c *Client) CanCreateVacantMinipool(amountWei *big.Int, minFee float64, salt *big.Int, pubkey types.ValidatorPubkey) (api.CanCreateVacantMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-create-vacant-minipool %s %f %s %s", amountWei.String(), minFee, salt.String(), pubkey.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-create-vacant-minipool", url.Values{ + "amountWei": {amountWei.String()}, + "minFee": {strconv.FormatFloat(minFee, 'f', -1, 64)}, + "salt": {salt.String()}, + "pubkey": {pubkey.Hex()}, + }) if err != nil { return api.CanCreateVacantMinipoolResponse{}, fmt.Errorf("Could not get can create vacant minipool status: %w", err) } @@ -1196,7 +1268,12 @@ func (c *Client) CanCreateVacantMinipool(amountWei *big.Int, minFee float64, sal // Create a vacant minipool, which can be used to migrate a solo staker func (c *Client) CreateVacantMinipool(amountWei *big.Int, minFee float64, salt *big.Int, pubkey types.ValidatorPubkey) (api.CreateVacantMinipoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node create-vacant-minipool %s %f %s %s", amountWei.String(), minFee, salt.String(), pubkey.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/create-vacant-minipool", url.Values{ + "amountWei": {amountWei.String()}, + "minFee": {strconv.FormatFloat(minFee, 'f', -1, 64)}, + "salt": {salt.String()}, + "pubkey": {pubkey.Hex()}, + }) if err != nil { return api.CreateVacantMinipoolResponse{}, fmt.Errorf("Could not get create vacant minipool status: %w", err) } @@ -1212,7 +1289,7 @@ func (c *Client) CreateVacantMinipool(amountWei *big.Int, minFee float64, salt * // Get the node's collateral info, including pending bond reductions func (c *Client) CheckCollateral() (api.CheckCollateralResponse, error) { - responseBytes, err := c.callAPI("node check-collateral") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/check-collateral", nil) if err != nil { return api.CheckCollateralResponse{}, fmt.Errorf("Could not get check-collateral status: %w", err) } @@ -1228,7 +1305,7 @@ func (c *Client) CheckCollateral() (api.CheckCollateralResponse, error) { // Get the ETH balance of the node address func (c *Client) GetEthBalance() (api.NodeEthBalanceResponse, error) { - responseBytes, err := c.callAPI("node get-eth-balance") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/get-eth-balance", nil) if err != nil { return api.NodeEthBalanceResponse{}, fmt.Errorf("Could not get get-eth-balance status: %w", err) } @@ -1244,7 +1321,10 @@ func (c *Client) GetEthBalance() (api.NodeEthBalanceResponse, error) { // Estimates the gas for sending a zero-value message with a payload func (c *Client) CanSendMessage(address common.Address, message []byte) (api.CanNodeSendMessageResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-send-message %s %s", address.Hex(), hex.EncodeToString(message))) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-send-message", url.Values{ + "address": {address.Hex()}, + "message": {hex.EncodeToString(message)}, + }) if err != nil { return api.CanNodeSendMessageResponse{}, fmt.Errorf("Could not get can-send-message response: %w", err) } @@ -1260,7 +1340,10 @@ func (c *Client) CanSendMessage(address common.Address, message []byte) (api.Can // Sends a zero-value message with a payload func (c *Client) SendMessage(address common.Address, message []byte) (api.NodeSendMessageResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node send-message %s %s", address.Hex(), hex.EncodeToString(message))) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/send-message", url.Values{ + "address": {address.Hex()}, + "message": {hex.EncodeToString(message)}, + }) if err != nil { return api.NodeSendMessageResponse{}, fmt.Errorf("Could not get send-message response: %w", err) } @@ -1276,7 +1359,7 @@ func (c *Client) SendMessage(address common.Address, message []byte) (api.NodeSe // Get the number of express tickets available for the node func (c *Client) GetExpressTicketCount() (api.GetExpressTicketCountResponse, error) { - responseBytes, err := c.callAPI("node get-express-ticket-count") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/get-express-ticket-count", nil) if err != nil { return api.GetExpressTicketCountResponse{}, fmt.Errorf("Could not get express ticket count: %w", err) } @@ -1292,7 +1375,7 @@ func (c *Client) GetExpressTicketCount() (api.GetExpressTicketCountResponse, err // Check if the node's express tickets have been provisioned func (c *Client) GetExpressTicketsProvisioned() (api.GetExpressTicketsProvisionedResponse, error) { - responseBytes, err := c.callAPI("node get-express-tickets-provisioned") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/get-express-tickets-provisioned", nil) if err != nil { return api.GetExpressTicketsProvisionedResponse{}, fmt.Errorf("Could not get express tickets provisioned: %w", err) } @@ -1307,7 +1390,7 @@ func (c *Client) GetExpressTicketsProvisioned() (api.GetExpressTicketsProvisione } func (c *Client) CanProvisionExpressTickets() (api.CanProvisionExpressTicketsResponse, error) { - responseBytes, err := c.callAPI("node can-provision-express-tickets") + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-provision-express-tickets", nil) if err != nil { return api.CanProvisionExpressTicketsResponse{}, fmt.Errorf("Could not get can-provision-express-tickets response: %w", err) } @@ -1322,7 +1405,7 @@ func (c *Client) CanProvisionExpressTickets() (api.CanProvisionExpressTicketsRes } func (c *Client) ProvisionExpressTickets() (api.ProvisionExpressTicketsResponse, error) { - responseBytes, err := c.callAPI("node provision-express-tickets") + responseBytes, err := c.callHTTPAPI("POST", "/api/node/provision-express-tickets", nil) if err != nil { return api.ProvisionExpressTicketsResponse{}, fmt.Errorf("Could not get provision-express-tickets response: %w", err) } @@ -1338,7 +1421,7 @@ func (c *Client) ProvisionExpressTickets() (api.ProvisionExpressTicketsResponse, // Check whether the node can claim unclaimed rewards func (c *Client) CanClaimUnclaimedRewards(nodeAddress common.Address) (api.CanClaimUnclaimedRewardsResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node can-claim-unclaimed-rewards %s", nodeAddress.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/can-claim-unclaimed-rewards", url.Values{"nodeAddress": {nodeAddress.Hex()}}) if err != nil { return api.CanClaimUnclaimedRewardsResponse{}, fmt.Errorf("Could not get can-claim-unclaimed-rewards response: %w", err) } @@ -1354,7 +1437,7 @@ func (c *Client) CanClaimUnclaimedRewards(nodeAddress common.Address) (api.CanCl // Send unclaimed rewards to a node operator's withdrawal address func (c *Client) ClaimUnclaimedRewards(nodeAddress common.Address) (api.ClaimUnclaimedRewardsResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node claim-unclaimed-rewards %s", nodeAddress.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/node/claim-unclaimed-rewards", url.Values{"nodeAddress": {nodeAddress.Hex()}}) if err != nil { return api.ClaimUnclaimedRewardsResponse{}, fmt.Errorf("Could not get claim-unclaimed-rewards response: %w", err) } @@ -1370,7 +1453,7 @@ func (c *Client) ClaimUnclaimedRewards(nodeAddress common.Address) (api.ClaimUnc // Get the bond requirement for a number of validators func (c *Client) GetBondRequirement(numValidators uint64) (api.GetBondRequirementResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("node get-bond-requirement %d", numValidators)) + responseBytes, err := c.callHTTPAPI("GET", "/api/node/get-bond-requirement", url.Values{"numValidators": {strconv.FormatUint(numValidators, 10)}}) if err != nil { return api.GetBondRequirementResponse{}, fmt.Errorf("Could not get get-bond-requirement response: %w", err) } diff --git a/shared/services/rocketpool/odao.go b/shared/services/rocketpool/odao.go index cffdfa48e..b600d02ec 100644 --- a/shared/services/rocketpool/odao.go +++ b/shared/services/rocketpool/odao.go @@ -3,6 +3,7 @@ package rocketpool import ( "fmt" "math/big" + "net/url" "strconv" "github.com/ethereum/go-ethereum/common" @@ -13,7 +14,7 @@ import ( // Get oracle DAO status func (c *Client) TNDAOStatus() (api.TNDAOStatusResponse, error) { - responseBytes, err := c.callAPI("odao status") + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/status", nil) if err != nil { return api.TNDAOStatusResponse{}, fmt.Errorf("Could not get oracle DAO status: %w", err) } @@ -29,7 +30,7 @@ func (c *Client) TNDAOStatus() (api.TNDAOStatusResponse, error) { // Get oracle DAO members func (c *Client) TNDAOMembers() (api.TNDAOMembersResponse, error) { - responseBytes, err := c.callAPI("odao members") + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/members", nil) if err != nil { return api.TNDAOMembersResponse{}, fmt.Errorf("Could not get oracle DAO members: %w", err) } @@ -51,7 +52,7 @@ func (c *Client) TNDAOMembers() (api.TNDAOMembersResponse, error) { // Get oracle DAO proposals func (c *Client) TNDAOProposals() (api.TNDAOProposalsResponse, error) { - responseBytes, err := c.callAPI("odao proposals") + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/proposals", nil) if err != nil { return api.TNDAOProposalsResponse{}, fmt.Errorf("Could not get oracle DAO proposals: %w", err) } @@ -67,7 +68,7 @@ func (c *Client) TNDAOProposals() (api.TNDAOProposalsResponse, error) { // Get a single oracle DAO proposal func (c *Client) TNDAOProposal(id uint64) (api.TNDAOProposalResponse, error) { - responseBytes, err := c.callAPI("odao proposal-details", strconv.FormatUint(id, 10)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/proposal-details", url.Values{"id": {strconv.FormatUint(id, 10)}}) if err != nil { return api.TNDAOProposalResponse{}, fmt.Errorf("Could not get oracle DAO proposal: %w", err) } @@ -83,7 +84,11 @@ func (c *Client) TNDAOProposal(id uint64) (api.TNDAOProposalResponse, error) { // Check whether the node can propose inviting a new member func (c *Client) CanProposeInviteToTNDAO(memberAddress common.Address, memberId, memberUrl string) (api.CanProposeTNDAOInviteResponse, error) { - responseBytes, err := c.callAPI("odao can-propose-invite", memberAddress.Hex(), memberId, memberUrl) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-invite", url.Values{ + "address": {memberAddress.Hex()}, + "memberId": {memberId}, + "memberUrl": {memberUrl}, + }) if err != nil { return api.CanProposeTNDAOInviteResponse{}, fmt.Errorf("Could not get can propose oracle DAO invite status: %w", err) } @@ -99,7 +104,11 @@ func (c *Client) CanProposeInviteToTNDAO(memberAddress common.Address, memberId, // Propose inviting a new member func (c *Client) ProposeInviteToTNDAO(memberAddress common.Address, memberId, memberUrl string) (api.ProposeTNDAOInviteResponse, error) { - responseBytes, err := c.callAPI("odao propose-invite", memberAddress.Hex(), memberId, memberUrl) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-invite", url.Values{ + "address": {memberAddress.Hex()}, + "memberId": {memberId}, + "memberUrl": {memberUrl}, + }) if err != nil { return api.ProposeTNDAOInviteResponse{}, fmt.Errorf("Could not propose oracle DAO invite: %w", err) } @@ -115,7 +124,7 @@ func (c *Client) ProposeInviteToTNDAO(memberAddress common.Address, memberId, me // Check whether the node can propose leaving the oracle DAO func (c *Client) CanProposeLeaveTNDAO() (api.CanProposeTNDAOLeaveResponse, error) { - responseBytes, err := c.callAPI("odao can-propose-leave") + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-leave", nil) if err != nil { return api.CanProposeTNDAOLeaveResponse{}, fmt.Errorf("Could not get can propose leaving oracle DAO status: %w", err) } @@ -131,7 +140,7 @@ func (c *Client) CanProposeLeaveTNDAO() (api.CanProposeTNDAOLeaveResponse, error // Propose leaving the oracle DAO func (c *Client) ProposeLeaveTNDAO() (api.ProposeTNDAOLeaveResponse, error) { - responseBytes, err := c.callAPI("odao propose-leave") + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-leave", nil) if err != nil { return api.ProposeTNDAOLeaveResponse{}, fmt.Errorf("Could not propose leaving oracle DAO: %w", err) } @@ -145,41 +154,12 @@ func (c *Client) ProposeLeaveTNDAO() (api.ProposeTNDAOLeaveResponse, error) { return response, nil } -// Check whether the node can propose replacing its position with a new member -func (c *Client) CanProposeReplaceTNDAOMember(memberAddress common.Address, memberId, memberUrl string) (api.CanProposeTNDAOReplaceResponse, error) { - responseBytes, err := c.callAPI("odao can-propose-replace", memberAddress.Hex(), memberId, memberUrl) - if err != nil { - return api.CanProposeTNDAOReplaceResponse{}, fmt.Errorf("Could not get can propose replacing oracle DAO member status: %w", err) - } - var response api.CanProposeTNDAOReplaceResponse - if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.CanProposeTNDAOReplaceResponse{}, fmt.Errorf("Could not decode can propose replacing oracle DAO member response: %w", err) - } - if response.Error != "" { - return api.CanProposeTNDAOReplaceResponse{}, fmt.Errorf("Could not get can propose replacing oracle DAO member status: %s", response.Error) - } - return response, nil -} - -// Propose replacing the node's position with a new member -func (c *Client) ProposeReplaceTNDAOMember(memberAddress common.Address, memberId, memberUrl string) (api.ProposeTNDAOReplaceResponse, error) { - responseBytes, err := c.callAPI("odao propose-replace", memberAddress.Hex(), memberId, memberUrl) - if err != nil { - return api.ProposeTNDAOReplaceResponse{}, fmt.Errorf("Could not propose replacing oracle DAO member: %w", err) - } - var response api.ProposeTNDAOReplaceResponse - if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.ProposeTNDAOReplaceResponse{}, fmt.Errorf("Could not decode propose replacing oracle DAO member response: %w", err) - } - if response.Error != "" { - return api.ProposeTNDAOReplaceResponse{}, fmt.Errorf("Could not propose replacing oracle DAO member: %s", response.Error) - } - return response, nil -} - // Check whether the node can propose kicking a member func (c *Client) CanProposeKickFromTNDAO(memberAddress common.Address, fineAmountWei *big.Int) (api.CanProposeTNDAOKickResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-kick %s %s", memberAddress.Hex(), fineAmountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-kick", url.Values{ + "address": {memberAddress.Hex()}, + "fineAmountWei": {fineAmountWei.String()}, + }) if err != nil { return api.CanProposeTNDAOKickResponse{}, fmt.Errorf("Could not get can propose kicking oracle DAO member status: %w", err) } @@ -195,7 +175,10 @@ func (c *Client) CanProposeKickFromTNDAO(memberAddress common.Address, fineAmoun // Propose kicking a member func (c *Client) ProposeKickFromTNDAO(memberAddress common.Address, fineAmountWei *big.Int) (api.ProposeTNDAOKickResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-kick %s %s", memberAddress.Hex(), fineAmountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-kick", url.Values{ + "address": {memberAddress.Hex()}, + "fineAmountWei": {fineAmountWei.String()}, + }) if err != nil { return api.ProposeTNDAOKickResponse{}, fmt.Errorf("Could not propose kicking oracle DAO member: %w", err) } @@ -211,7 +194,7 @@ func (c *Client) ProposeKickFromTNDAO(memberAddress common.Address, fineAmountWe // Check whether the node can cancel a proposal func (c *Client) CanCancelTNDAOProposal(proposalId uint64) (api.CanCancelTNDAOProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-cancel-proposal %d", proposalId)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-cancel-proposal", url.Values{"id": {strconv.FormatUint(proposalId, 10)}}) if err != nil { return api.CanCancelTNDAOProposalResponse{}, fmt.Errorf("Could not get can cancel oracle DAO proposal status: %w", err) } @@ -227,7 +210,7 @@ func (c *Client) CanCancelTNDAOProposal(proposalId uint64) (api.CanCancelTNDAOPr // Cancel a proposal made by the node func (c *Client) CancelTNDAOProposal(proposalId uint64) (api.CancelTNDAOProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao cancel-proposal %d", proposalId)) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/cancel-proposal", url.Values{"id": {strconv.FormatUint(proposalId, 10)}}) if err != nil { return api.CancelTNDAOProposalResponse{}, fmt.Errorf("Could not cancel oracle DAO proposal: %w", err) } @@ -243,7 +226,7 @@ func (c *Client) CancelTNDAOProposal(proposalId uint64) (api.CancelTNDAOProposal // Check whether the node can vote on a proposal func (c *Client) CanVoteOnTNDAOProposal(proposalId uint64) (api.CanVoteOnTNDAOProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-vote-proposal %d", proposalId)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-vote-proposal", url.Values{"id": {strconv.FormatUint(proposalId, 10)}}) if err != nil { return api.CanVoteOnTNDAOProposalResponse{}, fmt.Errorf("Could not get can vote on oracle DAO proposal status: %w", err) } @@ -259,7 +242,14 @@ func (c *Client) CanVoteOnTNDAOProposal(proposalId uint64) (api.CanVoteOnTNDAOPr // Vote on a proposal func (c *Client) VoteOnTNDAOProposal(proposalId uint64, support bool) (api.VoteOnTNDAOProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao vote-proposal %d %t", proposalId, support)) + supportStr := "false" + if support { + supportStr = "true" + } + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/vote-proposal", url.Values{ + "id": {strconv.FormatUint(proposalId, 10)}, + "support": {supportStr}, + }) if err != nil { return api.VoteOnTNDAOProposalResponse{}, fmt.Errorf("Could not vote on oracle DAO proposal: %w", err) } @@ -275,7 +265,7 @@ func (c *Client) VoteOnTNDAOProposal(proposalId uint64, support bool) (api.VoteO // Check whether the node can execute a proposal func (c *Client) CanExecuteTNDAOProposal(proposalId uint64) (api.CanExecuteTNDAOProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-execute-proposal %d", proposalId)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-execute-proposal", url.Values{"id": {strconv.FormatUint(proposalId, 10)}}) if err != nil { return api.CanExecuteTNDAOProposalResponse{}, fmt.Errorf("Could not get can execute oracle DAO proposal status: %w", err) } @@ -291,7 +281,7 @@ func (c *Client) CanExecuteTNDAOProposal(proposalId uint64) (api.CanExecuteTNDAO // Execute a proposal func (c *Client) ExecuteTNDAOProposal(proposalId uint64) (api.ExecuteTNDAOProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao execute-proposal %d", proposalId)) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/execute-proposal", url.Values{"id": {strconv.FormatUint(proposalId, 10)}}) if err != nil { return api.ExecuteTNDAOProposalResponse{}, fmt.Errorf("Could not execute oracle DAO proposal: %w", err) } @@ -307,7 +297,7 @@ func (c *Client) ExecuteTNDAOProposal(proposalId uint64) (api.ExecuteTNDAOPropos // Check whether the node can join the oracle DAO func (c *Client) CanJoinTNDAO() (api.CanJoinTNDAOResponse, error) { - responseBytes, err := c.callAPI("odao can-join") + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-join", nil) if err != nil { return api.CanJoinTNDAOResponse{}, fmt.Errorf("Could not get can join oracle DAO status: %w", err) } @@ -321,9 +311,9 @@ func (c *Client) CanJoinTNDAO() (api.CanJoinTNDAOResponse, error) { return response, nil } -// Join the oracle DAO (requires an executed invite proposal) +// Approve RPL for joining the oracle DAO func (c *Client) ApproveRPLToJoinTNDAO() (api.JoinTNDAOApproveResponse, error) { - responseBytes, err := c.callAPI("odao join-approve-rpl") + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/join-approve-rpl", nil) if err != nil { return api.JoinTNDAOApproveResponse{}, fmt.Errorf("Could not approve RPL for joining oracle DAO: %w", err) } @@ -339,7 +329,7 @@ func (c *Client) ApproveRPLToJoinTNDAO() (api.JoinTNDAOApproveResponse, error) { // Join the oracle DAO (requires an executed invite proposal) func (c *Client) JoinTNDAO(approvalTxHash common.Hash) (api.JoinTNDAOJoinResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao join %s", approvalTxHash.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/join", url.Values{"approvalTxHash": {approvalTxHash.String()}}) if err != nil { return api.JoinTNDAOJoinResponse{}, fmt.Errorf("Could not join oracle DAO: %w", err) } @@ -355,7 +345,7 @@ func (c *Client) JoinTNDAO(approvalTxHash common.Hash) (api.JoinTNDAOJoinRespons // Check whether the node can leave the oracle DAO func (c *Client) CanLeaveTNDAO() (api.CanLeaveTNDAOResponse, error) { - responseBytes, err := c.callAPI("odao can-leave") + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-leave", nil) if err != nil { return api.CanLeaveTNDAOResponse{}, fmt.Errorf("Could not get can leave oracle DAO status: %w", err) } @@ -371,7 +361,7 @@ func (c *Client) CanLeaveTNDAO() (api.CanLeaveTNDAOResponse, error) { // Leave the oracle DAO (requires an executed leave proposal) func (c *Client) LeaveTNDAO(bondRefundAddress common.Address) (api.LeaveTNDAOResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao leave %s", bondRefundAddress.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/leave", url.Values{"bondRefundAddress": {bondRefundAddress.Hex()}}) if err != nil { return api.LeaveTNDAOResponse{}, fmt.Errorf("Could not leave oracle DAO: %w", err) } @@ -385,55 +375,8 @@ func (c *Client) LeaveTNDAO(bondRefundAddress common.Address) (api.LeaveTNDAORes return response, nil } -// Check whether the node can replace its position in the oracle DAO -func (c *Client) CanReplaceTNDAOMember() (api.CanReplaceTNDAOPositionResponse, error) { - responseBytes, err := c.callAPI("odao can-replace") - if err != nil { - return api.CanReplaceTNDAOPositionResponse{}, fmt.Errorf("Could not get can replace oracle DAO member status: %w", err) - } - var response api.CanReplaceTNDAOPositionResponse - if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.CanReplaceTNDAOPositionResponse{}, fmt.Errorf("Could not decode can replace oracle DAO member response: %w", err) - } - if response.Error != "" { - return api.CanReplaceTNDAOPositionResponse{}, fmt.Errorf("Could not get can replace oracle DAO member status: %s", response.Error) - } - return response, nil -} - -// Replace the node's position in the oracle DAO (requires an executed replace proposal) -func (c *Client) ReplaceTNDAOMember() (api.ReplaceTNDAOPositionResponse, error) { - responseBytes, err := c.callAPI("odao replace") - if err != nil { - return api.ReplaceTNDAOPositionResponse{}, fmt.Errorf("Could not replace oracle DAO member: %w", err) - } - var response api.ReplaceTNDAOPositionResponse - if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.ReplaceTNDAOPositionResponse{}, fmt.Errorf("Could not decode replace oracle DAO member response: %w", err) - } - if response.Error != "" { - return api.ReplaceTNDAOPositionResponse{}, fmt.Errorf("Could not replace oracle DAO member: %s", response.Error) - } - return response, nil -} - -// Check whether the node can propose a setting update -func (c *Client) CanProposeTNDAOSetting() (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI("odao can-propose-setting") - if err != nil { - return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting status: %w", err) - } - var response api.CanProposeTNDAOSettingResponse - if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not decode can propose setting response: %w", err) - } - if response.Error != "" { - return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting status: %s", response.Error) - } - return response, nil -} func (c *Client) CanProposeTNDAOSettingMembersQuorum(quorum float64) (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-members-quorum %f", quorum)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-members-quorum", url.Values{"quorum": {strconv.FormatFloat(quorum, 'f', -1, 64)}}) if err != nil { return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting members.quorum: %w", err) } @@ -446,8 +389,9 @@ func (c *Client) CanProposeTNDAOSettingMembersQuorum(quorum float64) (api.CanPro } return response, nil } + func (c *Client) CanProposeTNDAOSettingMembersRplBond(bondAmountWei *big.Int) (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-members-rplbond %s", bondAmountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-members-rplbond", url.Values{"bondAmountWei": {bondAmountWei.String()}}) if err != nil { return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting members.rplbond: %w", err) } @@ -460,8 +404,9 @@ func (c *Client) CanProposeTNDAOSettingMembersRplBond(bondAmountWei *big.Int) (a } return response, nil } + func (c *Client) CanProposeTNDAOSettingMinipoolUnbondedMax(unbondedMinipoolMax uint64) (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-members-minipool-unbonded-max %d", unbondedMinipoolMax)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-members-minipool-unbonded-max", url.Values{"max": {strconv.FormatUint(unbondedMinipoolMax, 10)}}) if err != nil { return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting members.minipool.unbonded.max: %w", err) } @@ -474,8 +419,9 @@ func (c *Client) CanProposeTNDAOSettingMinipoolUnbondedMax(unbondedMinipoolMax u } return response, nil } + func (c *Client) CanProposeTNDAOSettingProposalCooldown(proposalCooldownTimespan uint64) (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-proposal-cooldown %d", proposalCooldownTimespan)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-proposal-cooldown", url.Values{"value": {strconv.FormatUint(proposalCooldownTimespan, 10)}}) if err != nil { return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting proposal.cooldown.time: %w", err) } @@ -488,8 +434,9 @@ func (c *Client) CanProposeTNDAOSettingProposalCooldown(proposalCooldownTimespan } return response, nil } + func (c *Client) CanProposeTNDAOSettingProposalVoteTimespan(proposalVoteTimespan uint64) (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-proposal-vote-timespan %d", proposalVoteTimespan)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-proposal-vote-timespan", url.Values{"value": {strconv.FormatUint(proposalVoteTimespan, 10)}}) if err != nil { return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting proposal.vote.time: %w", err) } @@ -502,8 +449,9 @@ func (c *Client) CanProposeTNDAOSettingProposalVoteTimespan(proposalVoteTimespan } return response, nil } + func (c *Client) CanProposeTNDAOSettingProposalVoteDelayTimespan(proposalDelayTimespan uint64) (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-proposal-vote-delay-timespan %d", proposalDelayTimespan)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-proposal-vote-delay-timespan", url.Values{"value": {strconv.FormatUint(proposalDelayTimespan, 10)}}) if err != nil { return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting proposal.vote.delay.time: %w", err) } @@ -516,8 +464,9 @@ func (c *Client) CanProposeTNDAOSettingProposalVoteDelayTimespan(proposalDelayTi } return response, nil } + func (c *Client) CanProposeTNDAOSettingProposalExecuteTimespan(proposalExecuteTimespan uint64) (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-proposal-execute-timespan %d", proposalExecuteTimespan)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-proposal-execute-timespan", url.Values{"value": {strconv.FormatUint(proposalExecuteTimespan, 10)}}) if err != nil { return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting proposal.execute.time: %w", err) } @@ -530,8 +479,9 @@ func (c *Client) CanProposeTNDAOSettingProposalExecuteTimespan(proposalExecuteTi } return response, nil } + func (c *Client) CanProposeTNDAOSettingProposalActionTimespan(proposalActionTimespan uint64) (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-proposal-action-timespan %d", proposalActionTimespan)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-proposal-action-timespan", url.Values{"value": {strconv.FormatUint(proposalActionTimespan, 10)}}) if err != nil { return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting proposal.action.time: %w", err) } @@ -544,8 +494,9 @@ func (c *Client) CanProposeTNDAOSettingProposalActionTimespan(proposalActionTime } return response, nil } + func (c *Client) CanProposeTNDAOSettingScrubPeriod(scrubPeriod uint64) (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-scrub-period %d", scrubPeriod)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-scrub-period", url.Values{"value": {strconv.FormatUint(scrubPeriod, 10)}}) if err != nil { return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting minipool.scrub.period: %w", err) } @@ -558,8 +509,9 @@ func (c *Client) CanProposeTNDAOSettingScrubPeriod(scrubPeriod uint64) (api.CanP } return response, nil } + func (c *Client) CanProposeTNDAOSettingPromotionScrubPeriod(scrubPeriod uint64) (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-promotion-scrub-period %d", scrubPeriod)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-promotion-scrub-period", url.Values{"value": {strconv.FormatUint(scrubPeriod, 10)}}) if err != nil { return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting minipool.promotion.scrub.period: %w", err) } @@ -572,8 +524,13 @@ func (c *Client) CanProposeTNDAOSettingPromotionScrubPeriod(scrubPeriod uint64) } return response, nil } + func (c *Client) CanProposeTNDAOSettingScrubPenaltyEnabled(enabled bool) (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-scrub-penalty-enabled %t", enabled)) + enabledStr := "false" + if enabled { + enabledStr = "true" + } + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-scrub-penalty-enabled", url.Values{"enabled": {enabledStr}}) if err != nil { return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting minipool.scrub.penalty.enabled: %w", err) } @@ -586,8 +543,9 @@ func (c *Client) CanProposeTNDAOSettingScrubPenaltyEnabled(enabled bool) (api.Ca } return response, nil } + func (c *Client) CanProposeTNDAOSettingBondReductionWindowStart(windowStart uint64) (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-bond-reduction-window-start %d", windowStart)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-bond-reduction-window-start", url.Values{"value": {strconv.FormatUint(windowStart, 10)}}) if err != nil { return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting minipool.bond.reduction.window.start: %w", err) } @@ -600,8 +558,9 @@ func (c *Client) CanProposeTNDAOSettingBondReductionWindowStart(windowStart uint } return response, nil } + func (c *Client) CanProposeTNDAOSettingBondReductionWindowLength(windowLength uint64) (api.CanProposeTNDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-propose-bond-reduction-window-length %d", windowLength)) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-propose-bond-reduction-window-length", url.Values{"value": {strconv.FormatUint(windowLength, 10)}}) if err != nil { return api.CanProposeTNDAOSettingResponse{}, fmt.Errorf("Could not get can propose setting minipool.bond.reduction.window.length: %w", err) } @@ -617,7 +576,7 @@ func (c *Client) CanProposeTNDAOSettingBondReductionWindowLength(windowLength ui // Propose a setting update func (c *Client) ProposeTNDAOSettingMembersQuorum(quorum float64) (api.ProposeTNDAOSettingMembersQuorumResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-members-quorum %f", quorum)) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-members-quorum", url.Values{"quorum": {strconv.FormatFloat(quorum, 'f', -1, 64)}}) if err != nil { return api.ProposeTNDAOSettingMembersQuorumResponse{}, fmt.Errorf("Could not propose oracle DAO setting members.quorum: %w", err) } @@ -630,8 +589,9 @@ func (c *Client) ProposeTNDAOSettingMembersQuorum(quorum float64) (api.ProposeTN } return response, nil } + func (c *Client) ProposeTNDAOSettingMembersRplBond(bondAmountWei *big.Int) (api.ProposeTNDAOSettingMembersRplBondResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-members-rplbond %s", bondAmountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-members-rplbond", url.Values{"bondAmountWei": {bondAmountWei.String()}}) if err != nil { return api.ProposeTNDAOSettingMembersRplBondResponse{}, fmt.Errorf("Could not propose oracle DAO setting members.rplbond: %w", err) } @@ -644,8 +604,9 @@ func (c *Client) ProposeTNDAOSettingMembersRplBond(bondAmountWei *big.Int) (api. } return response, nil } + func (c *Client) ProposeTNDAOSettingMinipoolUnbondedMax(unbondedMinipoolMax uint64) (api.ProposeTNDAOSettingMinipoolUnbondedMaxResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-members-minipool-unbonded-max %d", unbondedMinipoolMax)) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-members-minipool-unbonded-max", url.Values{"max": {strconv.FormatUint(unbondedMinipoolMax, 10)}}) if err != nil { return api.ProposeTNDAOSettingMinipoolUnbondedMaxResponse{}, fmt.Errorf("Could not propose oracle DAO setting members.minipool.unbonded.max: %w", err) } @@ -658,8 +619,9 @@ func (c *Client) ProposeTNDAOSettingMinipoolUnbondedMax(unbondedMinipoolMax uint } return response, nil } + func (c *Client) ProposeTNDAOSettingProposalCooldown(proposalCooldownTimespan uint64) (api.ProposeTNDAOSettingProposalCooldownResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-proposal-cooldown %d", proposalCooldownTimespan)) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-proposal-cooldown", url.Values{"value": {strconv.FormatUint(proposalCooldownTimespan, 10)}}) if err != nil { return api.ProposeTNDAOSettingProposalCooldownResponse{}, fmt.Errorf("Could not propose oracle DAO setting proposal.cooldown.time: %w", err) } @@ -672,8 +634,9 @@ func (c *Client) ProposeTNDAOSettingProposalCooldown(proposalCooldownTimespan ui } return response, nil } + func (c *Client) ProposeTNDAOSettingProposalVoteTimespan(proposalVoteTimespan uint64) (api.ProposeTNDAOSettingProposalVoteTimespanResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-proposal-vote-timespan %d", proposalVoteTimespan)) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-proposal-vote-timespan", url.Values{"value": {strconv.FormatUint(proposalVoteTimespan, 10)}}) if err != nil { return api.ProposeTNDAOSettingProposalVoteTimespanResponse{}, fmt.Errorf("Could not propose oracle DAO setting proposal.vote.time: %w", err) } @@ -686,8 +649,9 @@ func (c *Client) ProposeTNDAOSettingProposalVoteTimespan(proposalVoteTimespan ui } return response, nil } + func (c *Client) ProposeTNDAOSettingProposalVoteDelayTimespan(proposalDelayTimespan uint64) (api.ProposeTNDAOSettingProposalVoteDelayTimespanResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-proposal-vote-delay-timespan %d", proposalDelayTimespan)) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-proposal-vote-delay-timespan", url.Values{"value": {strconv.FormatUint(proposalDelayTimespan, 10)}}) if err != nil { return api.ProposeTNDAOSettingProposalVoteDelayTimespanResponse{}, fmt.Errorf("Could not propose oracle DAO setting proposal.vote.delay.time: %w", err) } @@ -700,8 +664,9 @@ func (c *Client) ProposeTNDAOSettingProposalVoteDelayTimespan(proposalDelayTimes } return response, nil } + func (c *Client) ProposeTNDAOSettingProposalExecuteTimespan(proposalExecuteTimespan uint64) (api.ProposeTNDAOSettingProposalExecuteTimespanResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-proposal-execute-timespan %d", proposalExecuteTimespan)) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-proposal-execute-timespan", url.Values{"value": {strconv.FormatUint(proposalExecuteTimespan, 10)}}) if err != nil { return api.ProposeTNDAOSettingProposalExecuteTimespanResponse{}, fmt.Errorf("Could not propose oracle DAO setting proposal.execute.time: %w", err) } @@ -714,8 +679,9 @@ func (c *Client) ProposeTNDAOSettingProposalExecuteTimespan(proposalExecuteTimes } return response, nil } + func (c *Client) ProposeTNDAOSettingProposalActionTimespan(proposalActionTimespan uint64) (api.ProposeTNDAOSettingProposalActionTimespanResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-proposal-action-timespan %d", proposalActionTimespan)) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-proposal-action-timespan", url.Values{"value": {strconv.FormatUint(proposalActionTimespan, 10)}}) if err != nil { return api.ProposeTNDAOSettingProposalActionTimespanResponse{}, fmt.Errorf("Could not propose oracle DAO setting proposal.action.time: %w", err) } @@ -728,8 +694,9 @@ func (c *Client) ProposeTNDAOSettingProposalActionTimespan(proposalActionTimespa } return response, nil } + func (c *Client) ProposeTNDAOSettingScrubPeriod(scrubPeriod uint64) (api.ProposeTNDAOSettingScrubPeriodResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-scrub-period %d", scrubPeriod)) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-scrub-period", url.Values{"value": {strconv.FormatUint(scrubPeriod, 10)}}) if err != nil { return api.ProposeTNDAOSettingScrubPeriodResponse{}, fmt.Errorf("Could not propose oracle DAO setting minipool.scrub.period: %w", err) } @@ -742,8 +709,9 @@ func (c *Client) ProposeTNDAOSettingScrubPeriod(scrubPeriod uint64) (api.Propose } return response, nil } + func (c *Client) ProposeTNDAOSettingPromotionScrubPeriod(scrubPeriod uint64) (api.ProposeTNDAOSettingPromotionScrubPeriodResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-promotion-scrub-period %d", scrubPeriod)) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-promotion-scrub-period", url.Values{"value": {strconv.FormatUint(scrubPeriod, 10)}}) if err != nil { return api.ProposeTNDAOSettingPromotionScrubPeriodResponse{}, fmt.Errorf("Could not propose oracle DAO setting minipool.promotion.scrub.period: %w", err) } @@ -756,8 +724,13 @@ func (c *Client) ProposeTNDAOSettingPromotionScrubPeriod(scrubPeriod uint64) (ap } return response, nil } + func (c *Client) ProposeTNDAOSettingScrubPenaltyEnabled(enabled bool) (api.ProposeTNDAOSettingScrubPenaltyEnabledResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-scrub-penalty-enabled %t", enabled)) + enabledStr := "false" + if enabled { + enabledStr = "true" + } + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-scrub-penalty-enabled", url.Values{"enabled": {enabledStr}}) if err != nil { return api.ProposeTNDAOSettingScrubPenaltyEnabledResponse{}, fmt.Errorf("Could not propose oracle DAO setting minipool.scrub.penalty.enabled: %w", err) } @@ -770,8 +743,9 @@ func (c *Client) ProposeTNDAOSettingScrubPenaltyEnabled(enabled bool) (api.Propo } return response, nil } + func (c *Client) ProposeTNDAOSettingBondReductionWindowStart(windowStart uint64) (api.ProposeTNDAOSettingBondReductionWindowStartResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-bond-reduction-window-start %d", windowStart)) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-bond-reduction-window-start", url.Values{"value": {strconv.FormatUint(windowStart, 10)}}) if err != nil { return api.ProposeTNDAOSettingBondReductionWindowStartResponse{}, fmt.Errorf("Could not propose oracle DAO setting minipool.bond.reduction.window.start: %w", err) } @@ -784,8 +758,9 @@ func (c *Client) ProposeTNDAOSettingBondReductionWindowStart(windowStart uint64) } return response, nil } + func (c *Client) ProposeTNDAOSettingBondReductionWindowLength(windowLength uint64) (api.ProposeTNDAOSettingBondReductionWindowLengthResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao propose-bond-reduction-window-length %d", windowLength)) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/propose-bond-reduction-window-length", url.Values{"value": {strconv.FormatUint(windowLength, 10)}}) if err != nil { return api.ProposeTNDAOSettingBondReductionWindowLengthResponse{}, fmt.Errorf("Could not propose oracle DAO setting minipool.bond.reduction.window.length: %w", err) } @@ -801,7 +776,7 @@ func (c *Client) ProposeTNDAOSettingBondReductionWindowLength(windowLength uint6 // Get the member settings func (c *Client) GetTNDAOMemberSettings() (api.GetTNDAOMemberSettingsResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao get-member-settings")) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/get-member-settings", nil) if err != nil { return api.GetTNDAOMemberSettingsResponse{}, fmt.Errorf("Could not get oracle DAO member settings: %w", err) } @@ -823,7 +798,7 @@ func (c *Client) GetTNDAOMemberSettings() (api.GetTNDAOMemberSettingsResponse, e // Get the proposal settings func (c *Client) GetTNDAOProposalSettings() (api.GetTNDAOProposalSettingsResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao get-proposal-settings")) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/get-proposal-settings", nil) if err != nil { return api.GetTNDAOProposalSettingsResponse{}, fmt.Errorf("Could not get oracle DAO proposal settings: %w", err) } @@ -837,9 +812,9 @@ func (c *Client) GetTNDAOProposalSettings() (api.GetTNDAOProposalSettingsRespons return response, nil } -// Get the proposal settings +// Get the minipool settings func (c *Client) GetTNDAOMinipoolSettings() (api.GetTNDAOMinipoolSettingsResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao get-minipool-settings")) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/get-minipool-settings", nil) if err != nil { return api.GetTNDAOMinipoolSettingsResponse{}, fmt.Errorf("Could not get oracle DAO minipool settings: %w", err) } @@ -855,7 +830,11 @@ func (c *Client) GetTNDAOMinipoolSettings() (api.GetTNDAOMinipoolSettingsRespons // Check whether the node can penalise a megapool func (c *Client) CanPenaliseMegapool(megapoolAddress common.Address, block *big.Int, amountWei *big.Int) (api.CanPenaliseMegapoolResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao can-penalise-megapool %s %s %s", megapoolAddress.String(), block.String(), amountWei.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/odao/can-penalise-megapool", url.Values{ + "megapoolAddress": {megapoolAddress.Hex()}, + "block": {block.String()}, + "amountWei": {amountWei.String()}, + }) if err != nil { return api.CanPenaliseMegapoolResponse{}, fmt.Errorf("Could not get can penalise megapool status: %w", err) } @@ -871,16 +850,20 @@ func (c *Client) CanPenaliseMegapool(megapoolAddress common.Address, block *big. // Penalise a megapool func (c *Client) PenaliseMegapool(megapoolAddress common.Address, block *big.Int, amountWei *big.Int) (api.RepayDebtResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("odao penalise-megapool %s %s %s", megapoolAddress.String(), block.String(), amountWei.String())) + responseBytes, err := c.callHTTPAPI("POST", "/api/odao/penalise-megapool", url.Values{ + "megapoolAddress": {megapoolAddress.Hex()}, + "block": {block.String()}, + "amountWei": {amountWei.String()}, + }) if err != nil { - return api.RepayDebtResponse{}, fmt.Errorf("Could not penalise megapool : %w", err) + return api.RepayDebtResponse{}, fmt.Errorf("Could not penalise megapool: %w", err) } var response api.RepayDebtResponse if err := json.Unmarshal(responseBytes, &response); err != nil { return api.RepayDebtResponse{}, fmt.Errorf("Could not decode penalise megapool response: %w", err) } if response.Error != "" { - return api.RepayDebtResponse{}, fmt.Errorf("Could not penalise megapool : %s", response.Error) + return api.RepayDebtResponse{}, fmt.Errorf("Could not penalise megapool: %s", response.Error) } return response, nil } diff --git a/shared/services/rocketpool/pdao.go b/shared/services/rocketpool/pdao.go index 2f5eed4d3..7854690ff 100644 --- a/shared/services/rocketpool/pdao.go +++ b/shared/services/rocketpool/pdao.go @@ -3,6 +3,8 @@ package rocketpool import ( "fmt" "math/big" + "net/url" + "strconv" "strings" "time" @@ -29,7 +31,7 @@ func getVoteDirectionString(direction types.VoteDirection) string { // Get protocol DAO proposals func (c *Client) PDAOProposals() (api.PDAOProposalsResponse, error) { - responseBytes, err := c.callAPI("pdao proposals") + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/proposals", nil) if err != nil { return api.PDAOProposalsResponse{}, fmt.Errorf("Could not get protocol DAO proposals: %w", err) } @@ -45,7 +47,7 @@ func (c *Client) PDAOProposals() (api.PDAOProposalsResponse, error) { // Get protocol DAO proposal details func (c *Client) PDAOProposalDetails(proposalID uint64) (api.PDAOProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao proposal-details %d", proposalID)) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/proposal-details", url.Values{"id": {strconv.FormatUint(proposalID, 10)}}) if err != nil { return api.PDAOProposalResponse{}, fmt.Errorf("Could not get protocol DAO proposal: %w", err) } @@ -61,7 +63,10 @@ func (c *Client) PDAOProposalDetails(proposalID uint64) (api.PDAOProposalRespons // Check whether the node can vote on a proposal func (c *Client) PDAOCanVoteProposal(proposalID uint64, voteDirection types.VoteDirection) (api.CanVoteOnPDAOProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-vote-proposal %d %s", proposalID, getVoteDirectionString(voteDirection))) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-vote-proposal", url.Values{ + "id": {strconv.FormatUint(proposalID, 10)}, + "voteDirection": {getVoteDirectionString(voteDirection)}, + }) if err != nil { return api.CanVoteOnPDAOProposalResponse{}, fmt.Errorf("Could not get protocol DAO can-vote-proposal: %w", err) } @@ -77,7 +82,10 @@ func (c *Client) PDAOCanVoteProposal(proposalID uint64, voteDirection types.Vote // Vote on a proposal func (c *Client) PDAOVoteProposal(proposalID uint64, voteDirection types.VoteDirection) (api.VoteOnPDAOProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao vote-proposal %d %s", proposalID, getVoteDirectionString(voteDirection))) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/vote-proposal", url.Values{ + "id": {strconv.FormatUint(proposalID, 10)}, + "voteDirection": {getVoteDirectionString(voteDirection)}, + }) if err != nil { return api.VoteOnPDAOProposalResponse{}, fmt.Errorf("Could not get protocol DAO vote-proposal: %w", err) } @@ -93,7 +101,10 @@ func (c *Client) PDAOVoteProposal(proposalID uint64, voteDirection types.VoteDir // Check whether the node can override the delegate's vote on a proposal func (c *Client) PDAOCanOverrideVote(proposalID uint64, voteDirection types.VoteDirection) (api.CanVoteOnPDAOProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-override-vote %d %s", proposalID, getVoteDirectionString(voteDirection))) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-override-vote", url.Values{ + "id": {strconv.FormatUint(proposalID, 10)}, + "voteDirection": {getVoteDirectionString(voteDirection)}, + }) if err != nil { return api.CanVoteOnPDAOProposalResponse{}, fmt.Errorf("Could not get protocol DAO can-override-vote: %w", err) } @@ -109,7 +120,10 @@ func (c *Client) PDAOCanOverrideVote(proposalID uint64, voteDirection types.Vote // Override the delegate's vote on a proposal func (c *Client) PDAOOverrideVote(proposalID uint64, voteDirection types.VoteDirection) (api.VoteOnPDAOProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao override-vote %d %s", proposalID, getVoteDirectionString(voteDirection))) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/override-vote", url.Values{ + "id": {strconv.FormatUint(proposalID, 10)}, + "voteDirection": {getVoteDirectionString(voteDirection)}, + }) if err != nil { return api.VoteOnPDAOProposalResponse{}, fmt.Errorf("Could not get protocol DAO override-vote: %w", err) } @@ -125,7 +139,7 @@ func (c *Client) PDAOOverrideVote(proposalID uint64, voteDirection types.VoteDir // Check whether the node can execute a proposal func (c *Client) PDAOCanExecuteProposal(proposalID uint64) (api.CanExecutePDAOProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-execute-proposal %d", proposalID)) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-execute-proposal", url.Values{"id": {strconv.FormatUint(proposalID, 10)}}) if err != nil { return api.CanExecutePDAOProposalResponse{}, fmt.Errorf("Could not get protocol DAO can-execute-proposal: %w", err) } @@ -141,7 +155,7 @@ func (c *Client) PDAOCanExecuteProposal(proposalID uint64) (api.CanExecutePDAOPr // Execute a proposal func (c *Client) PDAOExecuteProposal(proposalID uint64) (api.ExecutePDAOProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao execute-proposal %d", proposalID)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/execute-proposal", url.Values{"id": {strconv.FormatUint(proposalID, 10)}}) if err != nil { return api.ExecutePDAOProposalResponse{}, fmt.Errorf("Could not get protocol DAO execute-proposal: %w", err) } @@ -157,7 +171,7 @@ func (c *Client) PDAOExecuteProposal(proposalID uint64) (api.ExecutePDAOProposal // Get protocol DAO settings func (c *Client) PDAOGetSettings() (api.GetPDAOSettingsResponse, error) { - responseBytes, err := c.callAPI("pdao get-settings") + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/get-settings", nil) if err != nil { return api.GetPDAOSettingsResponse{}, fmt.Errorf("Could not get protocol DAO get-settings: %w", err) } @@ -173,7 +187,11 @@ func (c *Client) PDAOGetSettings() (api.GetPDAOSettingsResponse, error) { // Check whether the node can propose updating a PDAO setting func (c *Client) PDAOCanProposeSetting(contract string, setting string, value string) (api.CanProposePDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-propose-setting %s %s %s", contract, setting, value)) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-propose-setting", url.Values{ + "contract": {contract}, + "setting": {setting}, + "value": {value}, + }) if err != nil { return api.CanProposePDAOSettingResponse{}, fmt.Errorf("Could not get protocol DAO can-propose-setting: %w", err) } @@ -187,9 +205,14 @@ func (c *Client) PDAOCanProposeSetting(contract string, setting string, value st return response, nil } -// Propose updating a PDAO setting (use can-propose-setting to get the pollard) +// Propose updating a PDAO setting func (c *Client) PDAOProposeSetting(contract string, setting string, value string, blockNumber uint32) (api.ProposePDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao propose-setting %s %s %s %d", contract, setting, value, blockNumber)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/propose-setting", url.Values{ + "contract": {contract}, + "setting": {setting}, + "value": {value}, + "blockNumber": {strconv.FormatUint(uint64(blockNumber), 10)}, + }) if err != nil { return api.ProposePDAOSettingResponse{}, fmt.Errorf("Could not get protocol DAO propose-setting: %w", err) } @@ -203,9 +226,9 @@ func (c *Client) PDAOProposeSetting(contract string, setting string, value strin return response, nil } -// Get the allocation percentages of RPL rewards for the Oracle DAO, the Protocol DAO, and the node operators +// Get the allocation percentages of RPL rewards func (c *Client) PDAOGetRewardsPercentages() (api.PDAOGetRewardsPercentagesResponse, error) { - responseBytes, err := c.callAPI("pdao get-rewards-percentages") + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/get-rewards-percentages", nil) if err != nil { return api.PDAOGetRewardsPercentagesResponse{}, fmt.Errorf("Could not get protocol DAO get-rewards-percentages: %w", err) } @@ -219,9 +242,13 @@ func (c *Client) PDAOGetRewardsPercentages() (api.PDAOGetRewardsPercentagesRespo return response, nil } -// Check whether the node can propose new RPL rewards allocation percentages for the Oracle DAO, the Protocol DAO, and the node operators +// Check whether the node can propose new RPL rewards allocation percentages func (c *Client) PDAOCanProposeRewardsPercentages(node *big.Int, odao *big.Int, pdao *big.Int) (api.PDAOCanProposeRewardsPercentagesResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-propose-rewards-percentages %s %s %s", node.String(), odao.String(), pdao.String())) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-propose-rewards-percentages", url.Values{ + "node": {node.String()}, + "odao": {odao.String()}, + "pdao": {pdao.String()}, + }) if err != nil { return api.PDAOCanProposeRewardsPercentagesResponse{}, fmt.Errorf("Could not get protocol DAO can-propose-rewards-percentages: %w", err) } @@ -235,9 +262,14 @@ func (c *Client) PDAOCanProposeRewardsPercentages(node *big.Int, odao *big.Int, return response, nil } -// Propose new RPL rewards allocation percentages for the Oracle DAO, the Protocol DAO, and the node operators +// Propose new RPL rewards allocation percentages func (c *Client) PDAOProposeRewardsPercentages(node *big.Int, odao *big.Int, pdao *big.Int, blockNumber uint32) (api.ProposePDAOSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao propose-rewards-percentages %s %s %s %d", node, odao, pdao, blockNumber)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/propose-rewards-percentages", url.Values{ + "node": {node.String()}, + "odao": {odao.String()}, + "pdao": {pdao.String()}, + "blockNumber": {strconv.FormatUint(uint64(blockNumber), 10)}, + }) if err != nil { return api.ProposePDAOSettingResponse{}, fmt.Errorf("Could not get protocol DAO propose-rewards-percentages: %w", err) } @@ -253,7 +285,12 @@ func (c *Client) PDAOProposeRewardsPercentages(node *big.Int, odao *big.Int, pda // Check whether the node can propose a one-time spend of the Protocol DAO's treasury func (c *Client) PDAOCanProposeOneTimeSpend(invoiceID string, recipient common.Address, amount *big.Int, customMessage string) (api.PDAOCanProposeOneTimeSpendResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-propose-one-time-spend %s %s %s %s", invoiceID, recipient.Hex(), amount.String(), customMessage)) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-propose-one-time-spend", url.Values{ + "invoiceId": {invoiceID}, + "recipient": {recipient.Hex()}, + "amount": {amount.String()}, + "customMessage": {customMessage}, + }) if err != nil { return api.PDAOCanProposeOneTimeSpendResponse{}, fmt.Errorf("Could not get protocol DAO can-propose-one-time-spend: %w", err) } @@ -269,7 +306,13 @@ func (c *Client) PDAOCanProposeOneTimeSpend(invoiceID string, recipient common.A // Propose a one-time spend of the Protocol DAO's treasury func (c *Client) PDAOProposeOneTimeSpend(invoiceID string, recipient common.Address, amount *big.Int, blockNumber uint32, customMessage string) (api.PDAOProposeOneTimeSpendResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao propose-one-time-spend %s %s %s %d %s", invoiceID, recipient.Hex(), amount.String(), blockNumber, customMessage)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/propose-one-time-spend", url.Values{ + "invoiceId": {invoiceID}, + "recipient": {recipient.Hex()}, + "amount": {amount.String()}, + "blockNumber": {strconv.FormatUint(uint64(blockNumber), 10)}, + "customMessage": {customMessage}, + }) if err != nil { return api.PDAOProposeOneTimeSpendResponse{}, fmt.Errorf("Could not get protocol DAO propose-one-time-spend: %w", err) } @@ -285,7 +328,15 @@ func (c *Client) PDAOProposeOneTimeSpend(invoiceID string, recipient common.Addr // Check whether the node can propose a recurring spend of the Protocol DAO's treasury func (c *Client) PDAOCanProposeRecurringSpend(contractName string, recipient common.Address, amountPerPeriod *big.Int, periodLength time.Duration, startTime time.Time, numberOfPeriods uint64, customMessage string) (api.PDAOCanProposeRecurringSpendResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-propose-recurring-spend %s %s %s %s %d %d %s", contractName, recipient.Hex(), amountPerPeriod.String(), periodLength.String(), startTime.Unix(), numberOfPeriods, customMessage)) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-propose-recurring-spend", url.Values{ + "contractName": {contractName}, + "recipient": {recipient.Hex()}, + "amountPerPeriod": {amountPerPeriod.String()}, + "periodLength": {periodLength.String()}, + "startTime": {strconv.FormatInt(startTime.Unix(), 10)}, + "numberOfPeriods": {strconv.FormatUint(numberOfPeriods, 10)}, + "customMessage": {customMessage}, + }) if err != nil { return api.PDAOCanProposeRecurringSpendResponse{}, fmt.Errorf("Could not get protocol DAO can-propose-recurring-spend: %w", err) } @@ -301,7 +352,16 @@ func (c *Client) PDAOCanProposeRecurringSpend(contractName string, recipient com // Propose a recurring spend of the Protocol DAO's treasury func (c *Client) PDAOProposeRecurringSpend(contractName string, recipient common.Address, amountPerPeriod *big.Int, periodLength time.Duration, startTime time.Time, numberOfPeriods uint64, blockNumber uint32, customMessage string) (api.PDAOProposeRecurringSpendResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao propose-recurring-spend %s %s %s %s %d %d %d %s", contractName, recipient.Hex(), amountPerPeriod.String(), periodLength.String(), startTime.Unix(), numberOfPeriods, blockNumber, customMessage)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/propose-recurring-spend", url.Values{ + "contractName": {contractName}, + "recipient": {recipient.Hex()}, + "amountPerPeriod": {amountPerPeriod.String()}, + "periodLength": {periodLength.String()}, + "startTime": {strconv.FormatInt(startTime.Unix(), 10)}, + "numberOfPeriods": {strconv.FormatUint(numberOfPeriods, 10)}, + "blockNumber": {strconv.FormatUint(uint64(blockNumber), 10)}, + "customMessage": {customMessage}, + }) if err != nil { return api.PDAOProposeRecurringSpendResponse{}, fmt.Errorf("Could not get protocol DAO propose-recurring-spend: %w", err) } @@ -317,7 +377,14 @@ func (c *Client) PDAOProposeRecurringSpend(contractName string, recipient common // Check whether the node can propose an update to an existing recurring spend plan func (c *Client) PDAOCanProposeRecurringSpendUpdate(contractName string, recipient common.Address, amountPerPeriod *big.Int, periodLength time.Duration, numberOfPeriods uint64, customMessage string) (api.PDAOCanProposeRecurringSpendUpdateResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-propose-recurring-spend-update %s %s %s %s %d %s", contractName, recipient.Hex(), amountPerPeriod.String(), periodLength.String(), numberOfPeriods, customMessage)) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-propose-recurring-spend-update", url.Values{ + "contractName": {contractName}, + "recipient": {recipient.Hex()}, + "amountPerPeriod": {amountPerPeriod.String()}, + "periodLength": {periodLength.String()}, + "numberOfPeriods": {strconv.FormatUint(numberOfPeriods, 10)}, + "customMessage": {customMessage}, + }) if err != nil { return api.PDAOCanProposeRecurringSpendUpdateResponse{}, fmt.Errorf("Could not get protocol DAO can-propose-recurring-spend-update: %w", err) } @@ -333,7 +400,15 @@ func (c *Client) PDAOCanProposeRecurringSpendUpdate(contractName string, recipie // Propose an update to an existing recurring spend plan func (c *Client) PDAOProposeRecurringSpendUpdate(contractName string, recipient common.Address, amountPerPeriod *big.Int, periodLength time.Duration, numberOfPeriods uint64, blockNumber uint32, customMessage string) (api.PDAOProposeRecurringSpendUpdateResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao propose-recurring-spend-update %s %s %s %s %d %d %s", contractName, recipient.Hex(), amountPerPeriod.String(), periodLength.String(), numberOfPeriods, blockNumber, customMessage)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/propose-recurring-spend-update", url.Values{ + "contractName": {contractName}, + "recipient": {recipient.Hex()}, + "amountPerPeriod": {amountPerPeriod.String()}, + "periodLength": {periodLength.String()}, + "numberOfPeriods": {strconv.FormatUint(numberOfPeriods, 10)}, + "blockNumber": {strconv.FormatUint(uint64(blockNumber), 10)}, + "customMessage": {customMessage}, + }) if err != nil { return api.PDAOProposeRecurringSpendUpdateResponse{}, fmt.Errorf("Could not get protocol DAO propose-recurring-spend-update: %w", err) } @@ -349,7 +424,10 @@ func (c *Client) PDAOProposeRecurringSpendUpdate(contractName string, recipient // Check whether the node can invite someone to the security council func (c *Client) PDAOCanProposeInviteToSecurityCouncil(id string, address common.Address) (api.PDAOCanProposeInviteToSecurityCouncilResponse, error) { - responseBytes, err := c.callAPI("pdao can-propose-invite-to-security-council", id, address.Hex()) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-propose-invite-to-security-council", url.Values{ + "id": {id}, + "address": {address.Hex()}, + }) if err != nil { return api.PDAOCanProposeInviteToSecurityCouncilResponse{}, fmt.Errorf("Could not get protocol DAO can-propose-invite-to-security-council: %w", err) } @@ -365,7 +443,11 @@ func (c *Client) PDAOCanProposeInviteToSecurityCouncil(id string, address common // Propose inviting someone to the security council func (c *Client) PDAOProposeInviteToSecurityCouncil(id string, address common.Address, blockNumber uint32) (api.PDAOProposeInviteToSecurityCouncilResponse, error) { - responseBytes, err := c.callAPI("pdao propose-invite-to-security-council", id, address.Hex(), fmt.Sprint(blockNumber)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/propose-invite-to-security-council", url.Values{ + "id": {id}, + "address": {address.Hex()}, + "blockNumber": {strconv.FormatUint(uint64(blockNumber), 10)}, + }) if err != nil { return api.PDAOProposeInviteToSecurityCouncilResponse{}, fmt.Errorf("Could not get protocol DAO propose-invite-to-security-council: %w", err) } @@ -381,7 +463,7 @@ func (c *Client) PDAOProposeInviteToSecurityCouncil(id string, address common.Ad // Check whether the node can kick someone from the security council func (c *Client) PDAOCanProposeKickFromSecurityCouncil(address common.Address) (api.PDAOCanProposeKickFromSecurityCouncilResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-propose-kick-from-security-council %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-propose-kick-from-security-council", url.Values{"address": {address.Hex()}}) if err != nil { return api.PDAOCanProposeKickFromSecurityCouncilResponse{}, fmt.Errorf("Could not get protocol DAO can-propose-kick-from-security-council: %w", err) } @@ -397,7 +479,10 @@ func (c *Client) PDAOCanProposeKickFromSecurityCouncil(address common.Address) ( // Propose kicking someone from the security council func (c *Client) PDAOProposeKickFromSecurityCouncil(address common.Address, blockNumber uint32) (api.PDAOProposeKickFromSecurityCouncilResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao propose-kick-from-security-council %s %d", address.Hex(), blockNumber)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/propose-kick-from-security-council", url.Values{ + "address": {address.Hex()}, + "blockNumber": {strconv.FormatUint(uint64(blockNumber), 10)}, + }) if err != nil { return api.PDAOProposeKickFromSecurityCouncilResponse{}, fmt.Errorf("Could not get protocol DAO propose-kick-from-security-council: %w", err) } @@ -417,8 +502,7 @@ func (c *Client) PDAOCanProposeKickMultiFromSecurityCouncil(addresses []common.A for i, address := range addresses { addressStrings[i] = address.Hex() } - - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-propose-kick-multi-from-security-council %s", strings.Join(addressStrings, ","))) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-propose-kick-multi-from-security-council", url.Values{"addresses": {strings.Join(addressStrings, ",")}}) if err != nil { return api.PDAOCanProposeKickMultiFromSecurityCouncilResponse{}, fmt.Errorf("Could not get protocol DAO can-propose-kick-multi-from-security-council: %w", err) } @@ -438,8 +522,10 @@ func (c *Client) PDAOProposeKickMultiFromSecurityCouncil(addresses []common.Addr for i, address := range addresses { addressStrings[i] = address.Hex() } - - responseBytes, err := c.callAPI(fmt.Sprintf("pdao propose-kick-multi-from-security-council %s %d", strings.Join(addressStrings, ","), blockNumber)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/propose-kick-multi-from-security-council", url.Values{ + "addresses": {strings.Join(addressStrings, ",")}, + "blockNumber": {strconv.FormatUint(uint64(blockNumber), 10)}, + }) if err != nil { return api.PDAOProposeKickMultiFromSecurityCouncilResponse{}, fmt.Errorf("Could not get protocol DAO propose-kick-multi-from-security-council: %w", err) } @@ -453,9 +539,13 @@ func (c *Client) PDAOProposeKickMultiFromSecurityCouncil(addresses []common.Addr return response, nil } -// Check whether the node can propose replacing someone on the security council with another member +// Check whether the node can propose replacing someone on the security council func (c *Client) PDAOCanProposeReplaceMemberOfSecurityCouncil(existingAddress common.Address, newID string, newAddress common.Address) (api.PDAOCanProposeReplaceMemberOfSecurityCouncilResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-propose-replace-member-of-security-council %s", existingAddress.Hex()), newID, newAddress.Hex()) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-propose-replace-member-of-security-council", url.Values{ + "existingAddress": {existingAddress.Hex()}, + "newId": {newID}, + "newAddress": {newAddress.Hex()}, + }) if err != nil { return api.PDAOCanProposeReplaceMemberOfSecurityCouncilResponse{}, fmt.Errorf("Could not get protocol DAO can-propose-replace-member-of-security-council: %w", err) } @@ -469,9 +559,14 @@ func (c *Client) PDAOCanProposeReplaceMemberOfSecurityCouncil(existingAddress co return response, nil } -// Propose replacing someone on the security council with another member +// Propose replacing someone on the security council func (c *Client) PDAOProposeReplaceMemberOfSecurityCouncil(existingAddress common.Address, newID string, newAddress common.Address, blockNumber uint32) (api.PDAOProposeReplaceMemberOfSecurityCouncilResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao propose-replace-member-of-security-council %s", existingAddress.Hex()), newID, newAddress.Hex(), fmt.Sprint(blockNumber)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/propose-replace-member-of-security-council", url.Values{ + "existingAddress": {existingAddress.Hex()}, + "newId": {newID}, + "newAddress": {newAddress.Hex()}, + "blockNumber": {strconv.FormatUint(uint64(blockNumber), 10)}, + }) if err != nil { return api.PDAOProposeReplaceMemberOfSecurityCouncilResponse{}, fmt.Errorf("Could not get protocol DAO propose-replace-member-of-security-council: %w", err) } @@ -485,9 +580,9 @@ func (c *Client) PDAOProposeReplaceMemberOfSecurityCouncil(existingAddress commo return response, nil } -// Get the list of proposals with claimable / rewardable bonds, and the relevant indices for each one +// Get the list of proposals with claimable / rewardable bonds func (c *Client) PDAOGetClaimableBonds() (api.PDAOGetClaimableBondsResponse, error) { - responseBytes, err := c.callAPI("pdao get-claimable-bonds") + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/get-claimable-bonds", nil) if err != nil { return api.PDAOGetClaimableBondsResponse{}, fmt.Errorf("Could not get protocol DAO get-claimable-bonds: %w", err) } @@ -505,10 +600,12 @@ func (c *Client) PDAOGetClaimableBonds() (api.PDAOGetClaimableBondsResponse, err func (c *Client) PDAOCanClaimBonds(proposalID uint64, indices []uint64) (api.PDAOCanClaimBondsResponse, error) { indicesStrings := make([]string, len(indices)) for i, index := range indices { - indicesStrings[i] = fmt.Sprint(index) + indicesStrings[i] = strconv.FormatUint(index, 10) } - - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-claim-bonds %d %s", proposalID, strings.Join(indicesStrings, ","))) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-claim-bonds", url.Values{ + "proposalId": {strconv.FormatUint(proposalID, 10)}, + "indices": {strings.Join(indicesStrings, ",")}, + }) if err != nil { return api.PDAOCanClaimBondsResponse{}, fmt.Errorf("Could not get protocol DAO can-claim-bonds: %w", err) } @@ -526,10 +623,17 @@ func (c *Client) PDAOCanClaimBonds(proposalID uint64, indices []uint64) (api.PDA func (c *Client) PDAOClaimBonds(isProposer bool, proposalID uint64, indices []uint64) (api.PDAOClaimBondsResponse, error) { indicesStrings := make([]string, len(indices)) for i, index := range indices { - indicesStrings[i] = fmt.Sprint(index) + indicesStrings[i] = strconv.FormatUint(index, 10) } - - responseBytes, err := c.callAPI(fmt.Sprintf("pdao claim-bonds %t %d %s", isProposer, proposalID, strings.Join(indicesStrings, ","))) + isProposerStr := "false" + if isProposer { + isProposerStr = "true" + } + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/claim-bonds", url.Values{ + "isProposer": {isProposerStr}, + "proposalId": {strconv.FormatUint(proposalID, 10)}, + "indices": {strings.Join(indicesStrings, ",")}, + }) if err != nil { return api.PDAOClaimBondsResponse{}, fmt.Errorf("Could not get protocol DAO claim-bonds: %w", err) } @@ -545,7 +649,10 @@ func (c *Client) PDAOClaimBonds(isProposer bool, proposalID uint64, indices []ui // Check whether the node can defeat a proposal func (c *Client) PDAOCanDefeatProposal(proposalID uint64, index uint64) (api.PDAOCanDefeatProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-defeat-proposal %d %d", proposalID, index)) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-defeat-proposal", url.Values{ + "id": {strconv.FormatUint(proposalID, 10)}, + "index": {strconv.FormatUint(index, 10)}, + }) if err != nil { return api.PDAOCanDefeatProposalResponse{}, fmt.Errorf("Could not get protocol DAO can-defeat-proposal: %w", err) } @@ -561,7 +668,10 @@ func (c *Client) PDAOCanDefeatProposal(proposalID uint64, index uint64) (api.PDA // Defeat a proposal func (c *Client) PDAODefeatProposal(proposalID uint64, index uint64) (api.PDAODefeatProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao defeat-proposal %d %d", proposalID, index)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/defeat-proposal", url.Values{ + "id": {strconv.FormatUint(proposalID, 10)}, + "index": {strconv.FormatUint(index, 10)}, + }) if err != nil { return api.PDAODefeatProposalResponse{}, fmt.Errorf("Could not get protocol DAO defeat-proposal: %w", err) } @@ -577,7 +687,7 @@ func (c *Client) PDAODefeatProposal(proposalID uint64, index uint64) (api.PDAODe // Check whether the node can finalize a proposal func (c *Client) PDAOCanFinalizeProposal(proposalID uint64) (api.PDAOCanFinalizeProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-finalize-proposal %d", proposalID)) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-finalize-proposal", url.Values{"id": {strconv.FormatUint(proposalID, 10)}}) if err != nil { return api.PDAOCanFinalizeProposalResponse{}, fmt.Errorf("Could not get protocol DAO can-finalize-proposal: %w", err) } @@ -593,7 +703,7 @@ func (c *Client) PDAOCanFinalizeProposal(proposalID uint64) (api.PDAOCanFinalize // Finalize a proposal func (c *Client) PDAOFinalizeProposal(proposalID uint64) (api.PDAOFinalizeProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao finalize-proposal %d", proposalID)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/finalize-proposal", url.Values{"id": {strconv.FormatUint(proposalID, 10)}}) if err != nil { return api.PDAOFinalizeProposalResponse{}, fmt.Errorf("Could not get protocol DAO finalize-proposal: %w", err) } @@ -609,7 +719,7 @@ func (c *Client) PDAOFinalizeProposal(proposalID uint64) (api.PDAOFinalizePropos // EstimateSetVotingDelegateGas estimates the gas required to set an on-chain voting delegate func (c *Client) EstimateSetVotingDelegateGas(address common.Address) (api.PDAOCanSetVotingDelegateResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao estimate-set-voting-delegate-gas %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/estimate-set-voting-delegate-gas", url.Values{"address": {address.Hex()}}) if err != nil { return api.PDAOCanSetVotingDelegateResponse{}, fmt.Errorf("could not call estimate-set-voting-delegate-gas: %w", err) } @@ -623,9 +733,9 @@ func (c *Client) EstimateSetVotingDelegateGas(address common.Address) (api.PDAOC return response, nil } -// SetVotingDelegate set an on-chain voting delegate for the node +// SetVotingDelegate sets an on-chain voting delegate for the node func (c *Client) SetVotingDelegate(address common.Address) (api.PDAOSetVotingDelegateResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao set-voting-delegate %s", address.Hex())) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/set-voting-delegate", url.Values{"address": {address.Hex()}}) if err != nil { return api.PDAOSetVotingDelegateResponse{}, fmt.Errorf("could not call set-voting-delegate: %w", err) } @@ -641,7 +751,7 @@ func (c *Client) SetVotingDelegate(address common.Address) (api.PDAOSetVotingDel // GetCurrentVotingDelegate gets the node current on-chain voting delegate func (c *Client) GetCurrentVotingDelegate() (api.PDAOCurrentVotingDelegateResponse, error) { - responseBytes, err := c.callAPI("pdao get-current-voting-delegate") + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/get-current-voting-delegate", nil) if err != nil { return api.PDAOCurrentVotingDelegateResponse{}, fmt.Errorf("could not request get-current-voting-delegate: %w", err) } @@ -657,7 +767,10 @@ func (c *Client) GetCurrentVotingDelegate() (api.PDAOCurrentVotingDelegateRespon // CanSetSignallingAddress fetches gas info and if a node can set the signalling address func (c *Client) CanSetSignallingAddress(signallingAddress common.Address, signature string) (api.PDAOCanSetSignallingAddressResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-set-signalling-address %s %s", signallingAddress.Hex(), signature)) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-set-signalling-address", url.Values{ + "address": {signallingAddress.Hex()}, + "signature": {signature}, + }) if err != nil { return api.PDAOCanSetSignallingAddressResponse{}, fmt.Errorf("could not call can-set-signalling-address: %w", err) } @@ -673,7 +786,10 @@ func (c *Client) CanSetSignallingAddress(signallingAddress common.Address, signa // SetSignallingAddress sets the node's signalling address func (c *Client) SetSignallingAddress(signallingAddress common.Address, signature string) (api.PDAOSetSignallingAddressResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao set-signalling-address %s %s", signallingAddress.Hex(), signature)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/set-signalling-address", url.Values{ + "address": {signallingAddress.Hex()}, + "signature": {signature}, + }) if err != nil { return api.PDAOSetSignallingAddressResponse{}, fmt.Errorf("could not call set-signalling-address: %w", err) } @@ -689,7 +805,7 @@ func (c *Client) SetSignallingAddress(signallingAddress common.Address, signatur // CanClearSignallingAddress fetches gas info and if a node can clear a signalling address func (c *Client) CanClearSignallingAddress() (api.PDAOCanClearSignallingAddressResponse, error) { - responseBytes, err := c.callAPI("pdao can-clear-signalling-address") + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-clear-signalling-address", nil) if err != nil { return api.PDAOCanClearSignallingAddressResponse{}, fmt.Errorf("could not call can-clear-signalling-address: %w", err) } @@ -703,9 +819,9 @@ func (c *Client) CanClearSignallingAddress() (api.PDAOCanClearSignallingAddressR return response, nil } -// ClearSignallingAddress sets the node's signalling address +// ClearSignallingAddress clears the node's signalling address func (c *Client) ClearSignallingAddress() (api.PDAOSetSignallingAddressResponse, error) { - responseBytes, err := c.callAPI("pdao clear-signalling-address") + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/clear-signalling-address", nil) if err != nil { return api.PDAOSetSignallingAddressResponse{}, fmt.Errorf("could not call clear-signalling-address: %w", err) } @@ -721,7 +837,7 @@ func (c *Client) ClearSignallingAddress() (api.PDAOSetSignallingAddressResponse, // Check whether the node can propose a list of addresses that can update commission share parameters func (c *Client) PDAOCanProposeAllowListedControllers(addressList string) (api.PDAOACanProposeAllowListedControllersResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao can-propose-allow-listed-controllers %s", addressList)) + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/can-propose-allow-listed-controllers", url.Values{"addressList": {addressList}}) if err != nil { return api.PDAOACanProposeAllowListedControllersResponse{}, fmt.Errorf("Could not get protocol DAO can-propose-allow-listed-controllers: %w", err) } @@ -737,7 +853,10 @@ func (c *Client) PDAOCanProposeAllowListedControllers(addressList string) (api.P // Propose a list of addresses that can update commission share parameters func (c *Client) PDAOProposeAllowListedControllers(addressList string, blockNumber uint32) (api.PDAOProposeAllowListedControllersResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("pdao propose-allow-listed-controllers %s %d", addressList, blockNumber)) + responseBytes, err := c.callHTTPAPI("POST", "/api/pdao/propose-allow-listed-controllers", url.Values{ + "addressList": {addressList}, + "blockNumber": {strconv.FormatUint(uint64(blockNumber), 10)}, + }) if err != nil { return api.PDAOProposeAllowListedControllersResponse{}, fmt.Errorf("Could not get protocol DAO propose-allow-listed-controllers: %w", err) } @@ -753,7 +872,7 @@ func (c *Client) PDAOProposeAllowListedControllers(addressList string, blockNumb // Get PDAO Status func (c *Client) PDAOStatus() (api.PDAOStatusResponse, error) { - responseBytes, err := c.callAPI("pdao status") + responseBytes, err := c.callHTTPAPI("GET", "/api/pdao/status", nil) if err != nil { return api.PDAOStatusResponse{}, fmt.Errorf("could not call get pdao status: %w", err) } diff --git a/shared/services/rocketpool/queue.go b/shared/services/rocketpool/queue.go index 4ae9e1246..d62ee04af 100644 --- a/shared/services/rocketpool/queue.go +++ b/shared/services/rocketpool/queue.go @@ -3,6 +3,7 @@ package rocketpool import ( "fmt" "math/big" + "net/url" "github.com/goccy/go-json" @@ -11,7 +12,7 @@ import ( // Get queue status func (c *Client) QueueStatus() (api.QueueStatusResponse, error) { - responseBytes, err := c.callAPI("queue status") + responseBytes, err := c.callHTTPAPI("GET", "/api/queue/status", nil) if err != nil { return api.QueueStatusResponse{}, fmt.Errorf("Could not get queue status: %w", err) } @@ -33,7 +34,7 @@ func (c *Client) QueueStatus() (api.QueueStatusResponse, error) { // Check whether the queue can be processed func (c *Client) CanProcessQueue(max uint32) (api.CanProcessQueueResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("queue can-process %d", max)) + responseBytes, err := c.callHTTPAPI("GET", "/api/queue/can-process", url.Values{"max": {fmt.Sprintf("%d", max)}}) if err != nil { return api.CanProcessQueueResponse{}, fmt.Errorf("Could not get can process queue status: %w", err) } @@ -49,7 +50,7 @@ func (c *Client) CanProcessQueue(max uint32) (api.CanProcessQueueResponse, error // Process the queue func (c *Client) ProcessQueue(max uint32) (api.ProcessQueueResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("queue process %d", max)) + responseBytes, err := c.callHTTPAPI("POST", "/api/queue/process", url.Values{"max": {fmt.Sprintf("%d", max)}}) if err != nil { return api.ProcessQueueResponse{}, fmt.Errorf("Could not process queue: %w", err) } @@ -65,7 +66,7 @@ func (c *Client) ProcessQueue(max uint32) (api.ProcessQueueResponse, error) { // Check whether deposits can be assigned func (c *Client) CanAssignDeposits(max uint32) (api.CanAssignDepositsResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("queue can-assign-deposits %d", max)) + responseBytes, err := c.callHTTPAPI("GET", "/api/queue/can-assign-deposits", url.Values{"max": {fmt.Sprintf("%d", max)}}) if err != nil { return api.CanAssignDepositsResponse{}, fmt.Errorf("Could not get can assign deposits status: %w", err) } @@ -81,7 +82,7 @@ func (c *Client) CanAssignDeposits(max uint32) (api.CanAssignDepositsResponse, e // Assign deposits to queued validators func (c *Client) AssignDeposits(max uint32) (api.AssignDepositsResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("queue assign-deposits %d", max)) + responseBytes, err := c.callHTTPAPI("POST", "/api/queue/assign-deposits", url.Values{"max": {fmt.Sprintf("%d", max)}}) if err != nil { return api.AssignDepositsResponse{}, fmt.Errorf("Could not assign deposits: %w", err) } @@ -96,7 +97,7 @@ func (c *Client) AssignDeposits(max uint32) (api.AssignDepositsResponse, error) } func (c *Client) GetQueueDetails() (api.GetQueueDetailsResponse, error) { - responseBytes, err := c.callAPI("queue get-queue-details") + responseBytes, err := c.callHTTPAPI("GET", "/api/queue/get-queue-details", nil) if err != nil { return api.GetQueueDetailsResponse{}, fmt.Errorf("Could not get total queue length: %w", err) } diff --git a/shared/services/rocketpool/security.go b/shared/services/rocketpool/security.go index 18a5a34bf..47bfa84e7 100644 --- a/shared/services/rocketpool/security.go +++ b/shared/services/rocketpool/security.go @@ -2,16 +2,15 @@ package rocketpool import ( "fmt" - "strings" + "net/url" - "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" "github.com/rocket-pool/smartnode/shared/types/api" ) // Get security council status func (c *Client) SecurityStatus() (api.SecurityStatusResponse, error) { - responseBytes, err := c.callAPI("security status") + responseBytes, err := c.callHTTPAPI("GET", "/api/security/status", nil) if err != nil { return api.SecurityStatusResponse{}, fmt.Errorf("Could not get security council status: %w", err) } @@ -27,7 +26,7 @@ func (c *Client) SecurityStatus() (api.SecurityStatusResponse, error) { // Get the security council members func (c *Client) SecurityMembers() (api.SecurityMembersResponse, error) { - responseBytes, err := c.callAPI("security members") + responseBytes, err := c.callHTTPAPI("GET", "/api/security/members", nil) if err != nil { return api.SecurityMembersResponse{}, fmt.Errorf("Could not get security council members: %w", err) } @@ -43,7 +42,7 @@ func (c *Client) SecurityMembers() (api.SecurityMembersResponse, error) { // Get the security council proposals func (c *Client) SecurityProposals() (api.SecurityProposalsResponse, error) { - responseBytes, err := c.callAPI("security proposals") + responseBytes, err := c.callHTTPAPI("GET", "/api/security/proposals", nil) if err != nil { return api.SecurityProposalsResponse{}, fmt.Errorf("Could not get security council proposals: %w", err) } @@ -59,7 +58,7 @@ func (c *Client) SecurityProposals() (api.SecurityProposalsResponse, error) { // Get details of a proposal func (c *Client) SecurityProposal(id uint64) (api.SecurityProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("security proposal-details %d", id)) + responseBytes, err := c.callHTTPAPI("GET", "/api/security/proposal-details", url.Values{"id": {fmt.Sprintf("%d", id)}}) if err != nil { return api.SecurityProposalResponse{}, fmt.Errorf("Could not get security council proposal: %w", err) } @@ -73,41 +72,9 @@ func (c *Client) SecurityProposal(id uint64) (api.SecurityProposalResponse, erro return response, nil } -// Check whether the node can propose inviting a new member -func (c *Client) SecurityCanProposeInvite(memberId string, memberAddress common.Address) (api.SecurityCanProposeInviteResponse, error) { - responseBytes, err := c.callAPI("security can-propose-invite", memberId, memberAddress.Hex()) - if err != nil { - return api.SecurityCanProposeInviteResponse{}, fmt.Errorf("Could not get security-can-propose-invite status: %w", err) - } - var response api.SecurityCanProposeInviteResponse - if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.SecurityCanProposeInviteResponse{}, fmt.Errorf("Could not decode security-can-propose-invite response: %w", err) - } - if response.Error != "" { - return api.SecurityCanProposeInviteResponse{}, fmt.Errorf("Could not get security-can-propose-invite status: %s", response.Error) - } - return response, nil -} - -// Propose inviting a new member -func (c *Client) SecurityProposeInvite(memberId string, memberAddress common.Address) (api.SecurityProposeInviteResponse, error) { - responseBytes, err := c.callAPI("security propose-invite", memberId, memberAddress.Hex()) - if err != nil { - return api.SecurityProposeInviteResponse{}, fmt.Errorf("Could not propose security council invite: %w", err) - } - var response api.SecurityProposeInviteResponse - if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.SecurityProposeInviteResponse{}, fmt.Errorf("Could not decode propose security council invite response: %w", err) - } - if response.Error != "" { - return api.SecurityProposeInviteResponse{}, fmt.Errorf("Could not propose security council invite: %s", response.Error) - } - return response, nil -} - // Check whether the node can propose to leave the security council func (c *Client) SecurityProposeLeave() (api.SecurityProposeLeaveResponse, error) { - responseBytes, err := c.callAPI("security propose-leave") + responseBytes, err := c.callHTTPAPI("POST", "/api/security/propose-leave", nil) if err != nil { return api.SecurityProposeLeaveResponse{}, fmt.Errorf("Could not get security-propose-leave status: %w", err) } @@ -123,7 +90,7 @@ func (c *Client) SecurityProposeLeave() (api.SecurityProposeLeaveResponse, error // Check whether the node can propose leaving the security council func (c *Client) SecurityCanProposeLeave() (api.SecurityCanProposeLeaveResponse, error) { - responseBytes, err := c.callAPI("security can-propose-leave") + responseBytes, err := c.callHTTPAPI("GET", "/api/security/can-propose-leave", nil) if err != nil { return api.SecurityCanProposeLeaveResponse{}, fmt.Errorf("Could not get security-can-propose-leave status: %w", err) } @@ -137,115 +104,9 @@ func (c *Client) SecurityCanProposeLeave() (api.SecurityCanProposeLeaveResponse, return response, nil } -// Check whether the node can propose kicking a member -func (c *Client) SecurityCanProposeKick(memberAddress common.Address) (api.SecurityCanProposeKickResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("security can-propose-kick %s", memberAddress.Hex())) - if err != nil { - return api.SecurityCanProposeKickResponse{}, fmt.Errorf("Could not get security-can-propose-kick status: %w", err) - } - var response api.SecurityCanProposeKickResponse - if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.SecurityCanProposeKickResponse{}, fmt.Errorf("Could not decode security-can-propose-kick response: %w", err) - } - if response.Error != "" { - return api.SecurityCanProposeKickResponse{}, fmt.Errorf("Could not get security-can-propose-kick status: %s", response.Error) - } - return response, nil -} - -// Propose kicking a member -func (c *Client) SecurityProposeKick(memberAddress common.Address) (api.SecurityProposeKickResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("security propose-kick %s", memberAddress.Hex())) - if err != nil { - return api.SecurityProposeKickResponse{}, fmt.Errorf("Could not propose kicking security council member: %w", err) - } - var response api.SecurityProposeKickResponse - if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.SecurityProposeKickResponse{}, fmt.Errorf("Could not decode propose kicking security council member response: %w", err) - } - if response.Error != "" { - return api.SecurityProposeKickResponse{}, fmt.Errorf("Could not propose kicking security council member: %s", response.Error) - } - return response, nil -} - -// Check whether the node can propose kicking multiple members -func (c *Client) SecurityCanProposeKickMulti(addresses []common.Address) (api.SecurityCanProposeKickMultiResponse, error) { - addressStrings := make([]string, len(addresses)) - for i, address := range addresses { - addressStrings[i] = address.Hex() - } - - responseBytes, err := c.callAPI(fmt.Sprintf("security can-propose-kick-multi %s", strings.Join(addressStrings, ","))) - if err != nil { - return api.SecurityCanProposeKickMultiResponse{}, fmt.Errorf("Could not get security-can-propose-kick-multi status: %w", err) - } - var response api.SecurityCanProposeKickMultiResponse - if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.SecurityCanProposeKickMultiResponse{}, fmt.Errorf("Could not decode security-can-propose-kick-multi response: %w", err) - } - if response.Error != "" { - return api.SecurityCanProposeKickMultiResponse{}, fmt.Errorf("Could not get security-can-propose-kick-multi status: %s", response.Error) - } - return response, nil -} - -// Propose kicking multiple members -func (c *Client) SecurityProposeKickMulti(addresses []common.Address) (api.SecurityProposeKickMultiResponse, error) { - addressStrings := make([]string, len(addresses)) - for i, address := range addresses { - addressStrings[i] = address.Hex() - } - - responseBytes, err := c.callAPI(fmt.Sprintf("security propose-kick-multi %s", strings.Join(addressStrings, ","))) - if err != nil { - return api.SecurityProposeKickMultiResponse{}, fmt.Errorf("Could not propose kicking multiple security council members: %w", err) - } - var response api.SecurityProposeKickMultiResponse - if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.SecurityProposeKickMultiResponse{}, fmt.Errorf("Could not decode propose kicking multiple security council members response: %w", err) - } - if response.Error != "" { - return api.SecurityProposeKickMultiResponse{}, fmt.Errorf("Could not propose kicking multiple security council members: %s", response.Error) - } - return response, nil -} - -// Check whether the node can propose replacing someone on the security council with another member -func (c *Client) SecurityCanProposeReplace(existingAddress common.Address, newID string, newAddress common.Address) (api.SecurityCanProposeReplaceResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("security can-propose-replace-member %s", existingAddress.Hex()), newID, newAddress.Hex()) - if err != nil { - return api.SecurityCanProposeReplaceResponse{}, fmt.Errorf("Could not get security-can-propose-replace status: %w", err) - } - var response api.SecurityCanProposeReplaceResponse - if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.SecurityCanProposeReplaceResponse{}, fmt.Errorf("Could not decode protocol DAO can-propose-replace-member-of-security-council response: %w", err) - } - if response.Error != "" { - return api.SecurityCanProposeReplaceResponse{}, fmt.Errorf("Could not get security-can-propose-replace status: %s", response.Error) - } - return response, nil -} - -// Propose replacing someone on the security council with another member -func (c *Client) SecurityProposeReplace(existingAddress common.Address, newID string, newAddress common.Address) (api.SecurityProposeReplaceResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("security propose-replace-member %s", existingAddress.Hex()), newID, newAddress.Hex()) - if err != nil { - return api.SecurityProposeReplaceResponse{}, fmt.Errorf("Could not propose replacement of security council member: %w", err) - } - var response api.SecurityProposeReplaceResponse - if err := json.Unmarshal(responseBytes, &response); err != nil { - return api.SecurityProposeReplaceResponse{}, fmt.Errorf("Could not decode propose replacement of security council member response: %w", err) - } - if response.Error != "" { - return api.SecurityProposeReplaceResponse{}, fmt.Errorf("Could not propose replacement of security council member: %s", response.Error) - } - return response, nil -} - // Check whether the node can cancel a proposal func (c *Client) SecurityCanCancelProposal(proposalId uint64) (api.SecurityCanCancelProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("security can-cancel-proposal %d", proposalId)) + responseBytes, err := c.callHTTPAPI("GET", "/api/security/can-cancel-proposal", url.Values{"id": {fmt.Sprintf("%d", proposalId)}}) if err != nil { return api.SecurityCanCancelProposalResponse{}, fmt.Errorf("Could not get security-can-cancel-proposal status: %w", err) } @@ -261,7 +122,7 @@ func (c *Client) SecurityCanCancelProposal(proposalId uint64) (api.SecurityCanCa // Cancel a proposal made by the node func (c *Client) SecurityCancelProposal(proposalId uint64) (api.SecurityCancelProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("security cancel-proposal %d", proposalId)) + responseBytes, err := c.callHTTPAPI("POST", "/api/security/cancel-proposal", url.Values{"id": {fmt.Sprintf("%d", proposalId)}}) if err != nil { return api.SecurityCancelProposalResponse{}, fmt.Errorf("Could not cancel security council proposal: %w", err) } @@ -277,7 +138,7 @@ func (c *Client) SecurityCancelProposal(proposalId uint64) (api.SecurityCancelPr // Check whether the node can vote on a proposal func (c *Client) SecurityCanVoteOnProposal(proposalId uint64) (api.SecurityCanVoteOnProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("security can-vote-proposal %d", proposalId)) + responseBytes, err := c.callHTTPAPI("GET", "/api/security/can-vote-proposal", url.Values{"id": {fmt.Sprintf("%d", proposalId)}}) if err != nil { return api.SecurityCanVoteOnProposalResponse{}, fmt.Errorf("Could not get security-can-vote-on-proposal status: %w", err) } @@ -293,7 +154,14 @@ func (c *Client) SecurityCanVoteOnProposal(proposalId uint64) (api.SecurityCanVo // Vote on a proposal func (c *Client) SecurityVoteOnProposal(proposalId uint64, support bool) (api.SecurityVoteOnProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("security vote-proposal %d %t", proposalId, support)) + supportStr := "false" + if support { + supportStr = "true" + } + responseBytes, err := c.callHTTPAPI("POST", "/api/security/vote-proposal", url.Values{ + "id": {fmt.Sprintf("%d", proposalId)}, + "support": {supportStr}, + }) if err != nil { return api.SecurityVoteOnProposalResponse{}, fmt.Errorf("Could not vote on security council proposal: %w", err) } @@ -309,7 +177,7 @@ func (c *Client) SecurityVoteOnProposal(proposalId uint64, support bool) (api.Se // Check whether the node can execute a proposal func (c *Client) SecurityCanExecuteProposal(proposalId uint64) (api.SecurityCanExecuteProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("security can-execute-proposal %d", proposalId)) + responseBytes, err := c.callHTTPAPI("GET", "/api/security/can-execute-proposal", url.Values{"id": {fmt.Sprintf("%d", proposalId)}}) if err != nil { return api.SecurityCanExecuteProposalResponse{}, fmt.Errorf("Could not get security-can-execute-proposal status: %w", err) } @@ -325,7 +193,7 @@ func (c *Client) SecurityCanExecuteProposal(proposalId uint64) (api.SecurityCanE // Execute a proposal func (c *Client) SecurityExecuteProposal(proposalId uint64) (api.SecurityExecuteProposalResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("security execute-proposal %d", proposalId)) + responseBytes, err := c.callHTTPAPI("POST", "/api/security/execute-proposal", url.Values{"id": {fmt.Sprintf("%d", proposalId)}}) if err != nil { return api.SecurityExecuteProposalResponse{}, fmt.Errorf("Could not execute security council proposal: %w", err) } @@ -341,7 +209,7 @@ func (c *Client) SecurityExecuteProposal(proposalId uint64) (api.SecurityExecute // Check whether the node can join the security council func (c *Client) SecurityCanJoin() (api.SecurityCanJoinResponse, error) { - responseBytes, err := c.callAPI("security can-join") + responseBytes, err := c.callHTTPAPI("GET", "/api/security/can-join", nil) if err != nil { return api.SecurityCanJoinResponse{}, fmt.Errorf("Could not get security-can-join status: %w", err) } @@ -357,7 +225,7 @@ func (c *Client) SecurityCanJoin() (api.SecurityCanJoinResponse, error) { // Join the security council (requires an executed invite proposal) func (c *Client) SecurityJoin() (api.SecurityJoinResponse, error) { - responseBytes, err := c.callAPI("security join") + responseBytes, err := c.callHTTPAPI("POST", "/api/security/join", nil) if err != nil { return api.SecurityJoinResponse{}, fmt.Errorf("Could not join security council: %w", err) } @@ -373,7 +241,7 @@ func (c *Client) SecurityJoin() (api.SecurityJoinResponse, error) { // Check whether the node can leave the security council func (c *Client) SecurityCanLeave() (api.SecurityCanLeaveResponse, error) { - responseBytes, err := c.callAPI("security can-leave") + responseBytes, err := c.callHTTPAPI("GET", "/api/security/can-leave", nil) if err != nil { return api.SecurityCanLeaveResponse{}, fmt.Errorf("Could not get security-can-leave status: %w", err) } @@ -389,7 +257,7 @@ func (c *Client) SecurityCanLeave() (api.SecurityCanLeaveResponse, error) { // Leave the security council (requires an executed leave proposal) func (c *Client) SecurityLeave() (api.SecurityLeaveResponse, error) { - responseBytes, err := c.callAPI("security leave") + responseBytes, err := c.callHTTPAPI("POST", "/api/security/leave", nil) if err != nil { return api.SecurityLeaveResponse{}, fmt.Errorf("Could not leave security council: %w", err) } @@ -405,7 +273,11 @@ func (c *Client) SecurityLeave() (api.SecurityLeaveResponse, error) { // Check whether the node can propose updating a PDAO setting func (c *Client) SecurityCanProposeSetting(contract string, setting string, value string) (api.SecurityCanProposeSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("security can-propose-setting %s %s %s", contract, setting, value)) + responseBytes, err := c.callHTTPAPI("GET", "/api/security/can-propose-setting", url.Values{ + "contractName": {contract}, + "settingName": {setting}, + "value": {value}, + }) if err != nil { return api.SecurityCanProposeSettingResponse{}, fmt.Errorf("Could not get security-can-propose-setting: %w", err) } @@ -421,7 +293,11 @@ func (c *Client) SecurityCanProposeSetting(contract string, setting string, valu // Propose updating a PDAO setting func (c *Client) SecurityProposeSetting(contract string, setting string, value string) (api.SecurityProposeSettingResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("security propose-setting %s %s %s", contract, setting, value)) + responseBytes, err := c.callHTTPAPI("POST", "/api/security/propose-setting", url.Values{ + "contractName": {contract}, + "settingName": {setting}, + "value": {value}, + }) if err != nil { return api.SecurityProposeSettingResponse{}, fmt.Errorf("Could not propose security council setting: %w", err) } diff --git a/shared/services/rocketpool/service.go b/shared/services/rocketpool/service.go index dd91b1455..1adc4c182 100644 --- a/shared/services/rocketpool/service.go +++ b/shared/services/rocketpool/service.go @@ -11,7 +11,7 @@ import ( // Deletes the data folder including the wallet file, password file, and all validator keys. // Don't use this unless you have a very good reason to do it (such as switching from a Testnet to Mainnet). func (c *Client) TerminateDataFolder() (api.TerminateDataFolderResponse, error) { - responseBytes, err := c.callAPI("service terminate-data-folder") + responseBytes, err := c.callHTTPAPI("POST", "/api/service/terminate-data-folder", nil) if err != nil { return api.TerminateDataFolderResponse{}, fmt.Errorf("Could not delete data folder: %w", err) } @@ -27,7 +27,7 @@ func (c *Client) TerminateDataFolder() (api.TerminateDataFolderResponse, error) // Gets the status of the configured Execution and Beacon clients func (c *Client) GetClientStatus() (api.ClientStatusResponse, error) { - responseBytes, err := c.callAPI("service get-client-status") + responseBytes, err := c.callHTTPAPI("GET", "/api/service/get-client-status", nil) if err != nil { return api.ClientStatusResponse{}, fmt.Errorf("Could not get client status: %w", err) } @@ -43,7 +43,7 @@ func (c *Client) GetClientStatus() (api.ClientStatusResponse, error) { // Restarts the Validator client func (c *Client) RestartVc() (api.RestartVcResponse, error) { - responseBytes, err := c.callAPI("service restart-vc") + responseBytes, err := c.callHTTPAPI("POST", "/api/service/restart-vc", nil) if err != nil { return api.RestartVcResponse{}, fmt.Errorf("Could not get restart-vc status: %w", err) } diff --git a/shared/services/rocketpool/wallet.go b/shared/services/rocketpool/wallet.go index a82a60e62..f21075725 100644 --- a/shared/services/rocketpool/wallet.go +++ b/shared/services/rocketpool/wallet.go @@ -2,6 +2,7 @@ package rocketpool import ( "fmt" + "net/url" "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" @@ -11,7 +12,7 @@ import ( // Get wallet status func (c *Client) WalletStatus() (api.WalletStatusResponse, error) { - responseBytes, err := c.callAPI("wallet status") + responseBytes, err := c.callHTTPAPI("GET", "/api/wallet/status", nil) if err != nil { return api.WalletStatusResponse{}, fmt.Errorf("Could not get wallet status: %w", err) } @@ -27,7 +28,7 @@ func (c *Client) WalletStatus() (api.WalletStatusResponse, error) { // Set wallet password func (c *Client) SetPassword(password string) (api.SetPasswordResponse, error) { - responseBytes, err := c.callAPI("wallet set-password", password) + responseBytes, err := c.callHTTPAPI("POST", "/api/wallet/set-password", url.Values{"password": {password}}) if err != nil { return api.SetPasswordResponse{}, fmt.Errorf("Could not set wallet password: %w", err) } @@ -43,7 +44,7 @@ func (c *Client) SetPassword(password string) (api.SetPasswordResponse, error) { // Initialize wallet func (c *Client) InitWallet(derivationPath string) (api.InitWalletResponse, error) { - responseBytes, err := c.callAPI("wallet init --derivation-path", derivationPath) + responseBytes, err := c.callHTTPAPI("POST", "/api/wallet/init", url.Values{"derivationPath": {derivationPath}}) if err != nil { return api.InitWalletResponse{}, fmt.Errorf("Could not initialize wallet: %w", err) } @@ -59,16 +60,16 @@ func (c *Client) InitWallet(derivationPath string) (api.InitWalletResponse, erro // Recover wallet func (c *Client) RecoverWallet(mnemonic string, skipValidatorKeyRecovery bool, derivationPath string, walletIndex uint) (api.RecoverWalletResponse, error) { - command := "wallet recover " + skipStr := "false" if skipValidatorKeyRecovery { - command += "--skip-validator-key-recovery " - } - if walletIndex != 0 { - command += fmt.Sprintf("--wallet-index %d ", walletIndex) - } - command += "--derivation-path" - - responseBytes, err := c.callAPI(command, derivationPath, mnemonic) + skipStr = "true" + } + responseBytes, err := c.callHTTPAPI("POST", "/api/wallet/recover", url.Values{ + "mnemonic": {mnemonic}, + "skipValidatorKeyRecovery": {skipStr}, + "derivationPath": {derivationPath}, + "walletIndex": {fmt.Sprintf("%d", walletIndex)}, + }) if err != nil { return api.RecoverWalletResponse{}, fmt.Errorf("Could not recover wallet: %w", err) } @@ -84,12 +85,15 @@ func (c *Client) RecoverWallet(mnemonic string, skipValidatorKeyRecovery bool, d // Search and recover wallet func (c *Client) SearchAndRecoverWallet(mnemonic string, address common.Address, skipValidatorKeyRecovery bool) (api.SearchAndRecoverWalletResponse, error) { - command := "wallet search-and-recover " + skipStr := "false" if skipValidatorKeyRecovery { - command += "--skip-validator-key-recovery " + skipStr = "true" } - - responseBytes, err := c.callAPI(command, mnemonic, address.Hex()) + responseBytes, err := c.callHTTPAPI("POST", "/api/wallet/search-and-recover", url.Values{ + "mnemonic": {mnemonic}, + "address": {address.Hex()}, + "skipValidatorKeyRecovery": {skipStr}, + }) if err != nil { return api.SearchAndRecoverWalletResponse{}, fmt.Errorf("Could not search and recover wallet: %w", err) } @@ -103,18 +107,18 @@ func (c *Client) SearchAndRecoverWallet(mnemonic string, address common.Address, return response, nil } -// Recover wallet +// Recover wallet (test, no save) func (c *Client) TestRecoverWallet(mnemonic string, skipValidatorKeyRecovery bool, derivationPath string, walletIndex uint) (api.RecoverWalletResponse, error) { - command := "wallet test-recovery " + skipStr := "false" if skipValidatorKeyRecovery { - command += "--skip-validator-key-recovery " - } - if walletIndex != 0 { - command += fmt.Sprintf("--wallet-index %d ", walletIndex) - } - command += "--derivation-path" - - responseBytes, err := c.callAPI(command, derivationPath, mnemonic) + skipStr = "true" + } + responseBytes, err := c.callHTTPAPI("POST", "/api/wallet/test-recover", url.Values{ + "mnemonic": {mnemonic}, + "skipValidatorKeyRecovery": {skipStr}, + "derivationPath": {derivationPath}, + "walletIndex": {fmt.Sprintf("%d", walletIndex)}, + }) if err != nil { return api.RecoverWalletResponse{}, fmt.Errorf("Could not test recover wallet: %w", err) } @@ -128,14 +132,17 @@ func (c *Client) TestRecoverWallet(mnemonic string, skipValidatorKeyRecovery boo return response, nil } -// Search and recover wallet +// Search and recover wallet (test, no save) func (c *Client) TestSearchAndRecoverWallet(mnemonic string, address common.Address, skipValidatorKeyRecovery bool) (api.SearchAndRecoverWalletResponse, error) { - command := "wallet test-search-and-recover " + skipStr := "false" if skipValidatorKeyRecovery { - command += "--skip-validator-key-recovery " + skipStr = "true" } - - responseBytes, err := c.callAPI(command, mnemonic, address.Hex()) + responseBytes, err := c.callHTTPAPI("POST", "/api/wallet/test-search-and-recover", url.Values{ + "mnemonic": {mnemonic}, + "address": {address.Hex()}, + "skipValidatorKeyRecovery": {skipStr}, + }) if err != nil { return api.SearchAndRecoverWalletResponse{}, fmt.Errorf("Could not test search and recover wallet: %w", err) } @@ -151,7 +158,7 @@ func (c *Client) TestSearchAndRecoverWallet(mnemonic string, address common.Addr // Rebuild wallet func (c *Client) RebuildWallet() (api.RebuildWalletResponse, error) { - responseBytes, err := c.callAPI("wallet rebuild") + responseBytes, err := c.callHTTPAPI("POST", "/api/wallet/rebuild", nil) if err != nil { return api.RebuildWalletResponse{}, fmt.Errorf("Could not rebuild wallet: %w", err) } @@ -167,7 +174,7 @@ func (c *Client) RebuildWallet() (api.RebuildWalletResponse, error) { // Estimate the gas required to set an ENS reverse record to a name func (c *Client) EstimateGasSetEnsName(name string) (api.SetEnsNameResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("wallet estimate-gas-set-ens-name %s", name)) + responseBytes, err := c.callHTTPAPI("GET", "/api/wallet/estimate-gas-set-ens-name", url.Values{"name": {name}}) if err != nil { return api.SetEnsNameResponse{}, fmt.Errorf("Could not get estimate-gas-set-ens-name response: %w", err) } @@ -183,7 +190,7 @@ func (c *Client) EstimateGasSetEnsName(name string) (api.SetEnsNameResponse, err // Set an ENS reverse record to a name func (c *Client) SetEnsName(name string) (api.SetEnsNameResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("wallet set-ens-name %s", name)) + responseBytes, err := c.callHTTPAPI("POST", "/api/wallet/set-ens-name", url.Values{"name": {name}}) if err != nil { return api.SetEnsNameResponse{}, fmt.Errorf("Could not update ENS record: %w", err) } @@ -199,7 +206,7 @@ func (c *Client) SetEnsName(name string) (api.SetEnsNameResponse, error) { // Export wallet func (c *Client) ExportWallet() (api.ExportWalletResponse, error) { - responseBytes, err := c.callAPI("wallet export") + responseBytes, err := c.callHTTPAPI("GET", "/api/wallet/export", nil) if err != nil { return api.ExportWalletResponse{}, fmt.Errorf("Could not export wallet: %w", err) } @@ -215,7 +222,7 @@ func (c *Client) ExportWallet() (api.ExportWalletResponse, error) { // Set the node address to an arbitrary address func (c *Client) Masquerade(address common.Address) (api.MasqueradeResponse, error) { - responseBytes, err := c.callAPI("wallet masquerade", address.Hex()) + responseBytes, err := c.callHTTPAPI("POST", "/api/wallet/masquerade", url.Values{"address": {address.Hex()}}) if err != nil { return api.MasqueradeResponse{}, fmt.Errorf("Could not masquerade wallet: %w", err) } @@ -231,7 +238,7 @@ func (c *Client) Masquerade(address common.Address) (api.MasqueradeResponse, err // Delete the address file, ending a masquerade func (c *Client) EndMasquerade() (api.EndMasqueradeResponse, error) { - responseBytes, err := c.callAPI("wallet end-masquerade") + responseBytes, err := c.callHTTPAPI("POST", "/api/wallet/end-masquerade", nil) if err != nil { return api.EndMasqueradeResponse{}, fmt.Errorf("Could not end masquerade: %w", err) } diff --git a/shared/types/config/port-modes.go b/shared/types/config/port-modes.go index 936474fde..603d81da3 100644 --- a/shared/types/config/port-modes.go +++ b/shared/types/config/port-modes.go @@ -55,3 +55,17 @@ func PortModes(warningOverride string) []ParameterOption { Value: RPC_OpenExternal, }} } + +// RestrictedPortModes returns port mode options limited to Closed or Localhost only. +// Used for ports that must never be exposed externally (e.g. the node API). +func RestrictedPortModes() []ParameterOption { + return []ParameterOption{{ + Name: "Closed", + Description: "Do not allow connections to the port", + Value: RPC_Closed, + }, { + Name: "Open to Localhost", + Description: "Allow connections from this host only", + Value: RPC_OpenLocalhost, + }} +} diff --git a/shared/utils/api/http.go b/shared/utils/api/http.go new file mode 100644 index 000000000..8fa1a8e9b --- /dev/null +++ b/shared/utils/api/http.go @@ -0,0 +1,59 @@ +package api + +import ( + "errors" + "fmt" + "net/http" + "reflect" + + "github.com/goccy/go-json" + + "github.com/rocket-pool/smartnode/shared/types/api" +) + +// WriteResponse serialises response as JSON and writes it to w. +// response must be a pointer to a struct with string fields named Status and Error. +func WriteResponse(w http.ResponseWriter, response interface{}, responseError error) { + r := reflect.ValueOf(response) + if !(r.Kind() == reflect.Ptr && r.Type().Elem().Kind() == reflect.Struct) { + WriteErrorResponse(w, errors.New("invalid API response")) + return + } + + if r.IsNil() { + response = reflect.New(r.Type().Elem()).Interface() + r = reflect.ValueOf(response) + } + + sf := r.Elem().FieldByName("Status") + ef := r.Elem().FieldByName("Error") + if !(sf.IsValid() && sf.CanSet() && sf.Kind() == reflect.String && + ef.IsValid() && ef.CanSet() && ef.Kind() == reflect.String) { + WriteErrorResponse(w, errors.New("invalid API response")) + return + } + + if responseError != nil { + ef.SetString(responseError.Error()) + } + if ef.String() == "" { + sf.SetString("success") + } else { + sf.SetString("error") + } + + responseBytes, err := json.Marshal(response) + if err != nil { + WriteErrorResponse(w, fmt.Errorf("could not encode API response: %w", err)) + return + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write(responseBytes) +} + +// WriteErrorResponse writes a generic error response to w. +func WriteErrorResponse(w http.ResponseWriter, err error) { + WriteResponse(w, &api.APIResponse{}, err) +} diff --git a/shared/utils/api/response.go b/shared/utils/api/response.go index 022b3e0bf..b7b6fd512 100644 --- a/shared/utils/api/response.go +++ b/shared/utils/api/response.go @@ -1,72 +1,9 @@ package api -import ( - "errors" - "fmt" - "math/big" - "reflect" - - "github.com/goccy/go-json" - - "github.com/rocket-pool/smartnode/shared/types/api" -) +import "math/big" func ZeroIfNil(in **big.Int) { if *in == nil { *in = big.NewInt(0) } } - -// Print an API response -// response must be a pointer to a struct type with Error and Status string fields -func PrintResponse(response interface{}, responseError error) { - - // Check response type - r := reflect.ValueOf(response) - if !(r.Kind() == reflect.Ptr && r.Type().Elem().Kind() == reflect.Struct) { - PrintErrorResponse(errors.New("Invalid API response")) - return - } - - // Create zero response value if nil - if r.IsNil() { - response = reflect.New(r.Type().Elem()).Interface() - r = reflect.ValueOf(response) - } - - // Get and check response fields - sf := r.Elem().FieldByName("Status") - ef := r.Elem().FieldByName("Error") - if !(sf.IsValid() && sf.CanSet() && sf.Kind() == reflect.String && ef.IsValid() && ef.CanSet() && ef.Kind() == reflect.String) { - PrintErrorResponse(errors.New("Invalid API response")) - return - } - - // Populate error - if responseError != nil { - ef.SetString(responseError.Error()) - } - - // Set status - if ef.String() == "" { - sf.SetString("success") - } else { - sf.SetString("error") - } - - // Encode - responseBytes, err := json.Marshal(response) - if err != nil { - PrintErrorResponse(fmt.Errorf("Could not encode API response: %w", err)) - return - } - - // Print - fmt.Println(string(responseBytes)) - -} - -// Print an API error response -func PrintErrorResponse(err error) { - PrintResponse(&api.APIResponse{}, err) -} diff --git a/shared/version.txt b/shared/version.txt index 3585aa310..c8b9f0839 100644 --- a/shared/version.txt +++ b/shared/version.txt @@ -1 +1 @@ -1.19.2-dev \ No newline at end of file +1.20.0-dev \ No newline at end of file