diff --git a/bindings/rewards/rewards.go b/bindings/rewards/rewards.go index 1e5264adb..051542e5c 100644 --- a/bindings/rewards/rewards.go +++ b/bindings/rewards/rewards.go @@ -818,6 +818,150 @@ func getMainnetIntervalRewardsEvent(rp *rocketpool.RocketPool, index uint64) (Re intervalEndTime = time.Unix(1746682539, 0) submissionTime = time.Unix(1746691523, 0) userETH.SetString("38934754761348367759", 10) + case 36: + treasuryRPL.SetString("22048941282103056576253", 10) + trustedNodeRPL.SetString("2004449207463914234114", 10) + nodeRPL.SetString("56124577808989598554500", 10) + executionBlock = big.NewInt(22636374) + consensusBlock = big.NewInt(11856479) + intervalsPassed = big.NewInt(1) + merkleRoot = common.HexToHash("0x704c7c2f6faeb97b20535a9a325e9ba573ec066ee579853b3e94b175114a8fd7") + intervalStartTime = time.Unix(1746682539, 0) + intervalEndTime = time.Unix(1749101739, 0) + submissionTime = time.Unix(1749113279, 0) + nodeETH.SetString("50039863506253385959", 10) + userETH.SetString("57244908435972245571", 10) + case 37: + treasuryRPL.SetString("22131620846710572152925", 10) + trustedNodeRPL.SetString("2011965531519142922898", 10) + nodeRPL.SetString("56335034882536001840574", 10) + nodeETH.SetString("45418705066756834715", 10) + executionBlock = big.NewInt(22836652) + consensusBlock = big.NewInt(12058079) + intervalsPassed = big.NewInt(1) + merkleRoot = common.HexToHash("0xf4db744c50762a7ae1b19ee26120a582207eb1c535614711aae4cc5c598e42cf") + intervalStartTime = time.Unix(1749101739, 0) + intervalEndTime = time.Unix(1751520939, 0) + submissionTime = time.Unix(1751532611, 0) + userETH.SetString("48318015461156177248", 10) + case 38: + treasuryRPL.SetString("22214610444816577609208", 10) + trustedNodeRPL.SetString("2019510040437870691659", 10) + nodeRPL.SetString("56546281132260379365673", 10) + nodeETH.SetString("40700127824685134045", 10) + executionBlock = big.NewInt(23037044) + consensusBlock = big.NewInt(12259679) + intervalsPassed = big.NewInt(1) + merkleRoot = common.HexToHash("0xf7809e999791fb6c73712c7d281624020ba773dc144fdfbb41c86b2e3cbb42fd") + intervalStartTime = time.Unix(1751520939, 0) + intervalEndTime = time.Unix(1753940139, 0) + submissionTime = time.Unix(1753948367, 0) + userETH.SetString("40528725716465757725", 10) + case 39: + treasuryRPL.SetString("22297911238990936999935", 10) + trustedNodeRPL.SetString("2027082839908266999911", 10) + nodeRPL.SetString("56758319517431475996754", 10) + nodeETH.SetString("34921409718655333629", 10) + executionBlock = big.NewInt(23237567) + consensusBlock = big.NewInt(12461279) + intervalsPassed = big.NewInt(1) + merkleRoot = common.HexToHash("0xec51162c8af79d86e4b8f6546ec0e1ec3b4172b12574893e938c4f5bdd408efe") + intervalStartTime = time.Unix(1753940139, 0) + intervalEndTime = time.Unix(1756359339, 0) + submissionTime = time.Unix(1756395407, 0) + userETH.SetString("31594532680318853963", 10) + case 40: + // {"status":"success","error":"","found":true,"index":"40","executionBlock":"23438005","consensusBlock":"12662879","merkleRoot":"0x2ad908aa47f988d09dd9884c8026e6213262e231bab0d5051eedb2bef6aefe1e","intervalsPassed":"1","treasuryRPL":"22381524396162942297937","trustedNodeRPL":["2034684036014812936091"],"nodeRPL":["56971153008414762209929"],"nodeETH":["24246940457789627532"],"userETH":"13888515305764133572","intervalStartTime":1756359339,"intervalEndTime":1758778539,"submissionTime":1758785783} + treasuryRPL.SetString("22381524396162942297937", 10) + trustedNodeRPL.SetString("2034684036014812936091", 10) + nodeRPL.SetString("56971153008414762209929", 10) + nodeETH.SetString("24246940457789627532", 10) + executionBlock = big.NewInt(23438005) + consensusBlock = big.NewInt(12662879) + intervalsPassed = big.NewInt(1) + merkleRoot = common.HexToHash("0x2ad908aa47f988d09dd9884c8026e6213262e231bab0d5051eedb2bef6aefe1e") + intervalStartTime = time.Unix(1756359339, 0) + intervalEndTime = time.Unix(1758778539, 0) + submissionTime = time.Unix(1758785783, 0) + userETH.SetString("13888515305764133572", 10) + case 41: + treasuryRPL.SetString("22465451087637660464639", 10) + trustedNodeRPL.SetString("2042313735239787314880", 10) + nodeRPL.SetString("57184784586714044816063", 10) + nodeETH.SetString("43676270282606810107", 10) + executionBlock = big.NewInt(23638162) + consensusBlock = big.NewInt(12864479) + intervalsPassed = big.NewInt(1) + merkleRoot = common.HexToHash("0xe8af8737484cfd3d3fd5106ef189333e92f9bbc2de0ac057f4c01c473b2ec958") + intervalStartTime = time.Unix(1758778539, 0) + intervalEndTime = time.Unix(1761197739, 0) + submissionTime = time.Unix(1761205211, 0) + userETH.SetString("47268078777290226308", 10) + case 42: + treasuryRPL.SetString("22549692489112341819332", 10) + trustedNodeRPL.SetString("2049972044464758347124", 10) + nodeRPL.SetString("57399217245013233718952", 10) + nodeETH.SetString("25631914712861974454", 10) + executionBlock = big.NewInt(23838215) + consensusBlock = big.NewInt(13066079) + intervalsPassed = big.NewInt(1) + merkleRoot = common.HexToHash("0x9f8c89596b48d377279bf703444a2e0cd5e98ac4cc0f5c490324c394c1ba3ce8") + intervalStartTime = time.Unix(1761197739, 0) + intervalEndTime = time.Unix(1763616939, 0) + submissionTime = time.Unix(1763642831, 0) + userETH.SetString("17561894202841328207", 10) + case 43: + treasuryRPL.SetString("22634249780692889936981", 10) + trustedNodeRPL.SetString("2057659070972080903284", 10) + nodeRPL.SetString("57614453987218265291225", 10) + nodeETH.SetString("18947687367286040081", 10) + executionBlock = big.NewInt(24037441) + consensusBlock = big.NewInt(13267679) + intervalsPassed = big.NewInt(1) + merkleRoot = common.HexToHash("0xa98c94badfa30d41fcde8a3c5ccdadadbe441e43883919567b2d45682e476ca3") + intervalStartTime = time.Unix(1763616939, 0) + intervalEndTime = time.Unix(1766036139, 0) + submissionTime = time.Unix(1766046599, 0) + userETH.SetString("8352566574518930988", 10) + case 44: + treasuryRPL.SetString("22719124146910393305039", 10) + trustedNodeRPL.SetString("2065374922446399391296", 10) + nodeRPL.SetString("57830497828499182955594", 10) + nodeETH.SetString("15367476931278801422", 10) + executionBlock = big.NewInt(24238059) + consensusBlock = big.NewInt(13469279) + intervalsPassed = big.NewInt(1) + merkleRoot = common.HexToHash("0x72ed38107b2b6c1a0469800af741c04adf5563aebe5a0ae5d77fb9b2354c64a5") + intervalStartTime = time.Unix(1766036139, 0) + intervalEndTime = time.Unix(1768455339, 0) + submissionTime = time.Unix(1768461851, 0) + userETH.SetString("4223474559072201135", 10) + case 45: + treasuryRPL.SetString("22804316776737718971210", 10) + trustedNodeRPL.SetString("2073119706976156270036", 10) + nodeRPL.SetString("58047351795332375560374", 10) + nodeETH.SetString("28550894583322805236", 10) + executionBlock = big.NewInt(24438645) + consensusBlock = big.NewInt(13670879) + intervalsPassed = big.NewInt(1) + merkleRoot = common.HexToHash("0x97dc8f589c86c3650a96568ab05c08a9e160aec7eb405e35ec2e62c6e1af559c") + intervalStartTime = time.Unix(1768455339, 0) + intervalEndTime = time.Unix(1770874539, 0) + submissionTime = time.Unix(1770894827, 0) + userETH.SetString("22178758316567204991", 10) + case 46: + treasuryRPL.SetString("22889828863606168413868", 10) + trustedNodeRPL.SetString("2080893533055106219373", 10) + nodeRPL.SetString("58265018925542974141956", 10) + nodeETH.SetString("19997007660595265346", 10) + executionBlock = big.NewInt(24639334) + consensusBlock = big.NewInt(13872479) + intervalsPassed = big.NewInt(1) + merkleRoot = common.HexToHash("0x8cb45bcefd498b8788e6291c4dc086694c85b495fb60e9d5b047e7b921929d48") + intervalStartTime = time.Unix(1770874539, 0) + intervalEndTime = time.Unix(1773293739, 0) + submissionTime = time.Unix(1773299819, 0) + userETH.SetString("12369109587853762785", 10) default: return RewardsEvent{}, false, nil } diff --git a/bindings/utils/eth/units.go b/bindings/utils/eth/units.go index 9d5407eb7..4b5ec450f 100644 --- a/bindings/utils/eth/units.go +++ b/bindings/utils/eth/units.go @@ -39,6 +39,9 @@ func EthToWei(eth float64) *big.Int { // Convert wei to gigawei func WeiToGwei(wei *big.Int) float64 { + if wei == nil { + return 0 + } var weiFloat big.Float var gwei big.Float weiFloat.SetInt(wei) diff --git a/bindings/utils/wait.go b/bindings/utils/wait.go index 7a9649c42..f68ae61d9 100644 --- a/bindings/utils/wait.go +++ b/bindings/utils/wait.go @@ -3,7 +3,6 @@ package utils import ( "context" "errors" - "fmt" "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -12,32 +11,30 @@ import ( "github.com/rocket-pool/smartnode/bindings/rocketpool" ) -// Wait for a transaction to get mined -func WaitForTransaction(client rocketpool.ExecutionClient, hash common.Hash) (*types.Receipt, error) { - +// Wait for a transaction to get included, respecting the provided context for cancellation. +// The transaction lookup retries indefinitely (with 1-second pauses) until found or ctx is done. +func WaitForTransactionWithContext(ctx context.Context, client rocketpool.ExecutionClient, hash common.Hash) (*types.Receipt, error) { var tx *types.Transaction - var err error - // Get the transaction from its hash, retrying for 30 sec if it wasn't found - for i := 0; i < 30; i++ { - if i == 29 { - return nil, fmt.Errorf("Transaction not found after 30 seconds.") + // Get the transaction from its hash, retrying until found or ctx is cancelled. + for { + var err error + tx, _, err = client.TransactionByHash(ctx, hash) + if err == nil { + break } - - tx, _, err = client.TransactionByHash(context.Background(), hash) - if err != nil { - if err.Error() == "not found" { - time.Sleep(1 * time.Second) - continue - } + if err.Error() != "not found" { return nil, err - } else { - break + } + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(1 * time.Second): } } // Wait for transaction to be mined - txReceipt, err := bind.WaitMined(context.Background(), client, tx) + txReceipt, err := bind.WaitMined(ctx, client, tx) if err != nil { return nil, err } @@ -47,6 +44,10 @@ func WaitForTransaction(client rocketpool.ExecutionClient, hash common.Hash) (*t return txReceipt, errors.New("Transaction failed with status 0") } - // Return return txReceipt, nil } + +// Wait for a transaction to get mined +func WaitForTransaction(client rocketpool.ExecutionClient, hash common.Hash) (*types.Receipt, error) { + return WaitForTransactionWithContext(context.Background(), client, hash) +} diff --git a/rocketpool-cli/megapool/commands.go b/rocketpool-cli/megapool/commands.go index 4ec3fcac1..cc4ba4a12 100644 --- a/rocketpool-cli/megapool/commands.go +++ b/rocketpool-cli/megapool/commands.go @@ -236,7 +236,7 @@ func RegisterCommands(app *cli.Command, name string, aliases []string) { Name: "yes", Usage: "Automatically confirm the action", }, - &cli.StringFlag{ + &cli.Uint64Flag{ Name: "validator-id", Usage: "The validator id to exit", }, @@ -277,7 +277,7 @@ func RegisterCommands(app *cli.Command, name string, aliases []string) { Name: "yes", Usage: "Automatically confirm the action", }, - &cli.StringFlag{ + &cli.Uint64Flag{ Name: "validator-id", Usage: "The validator id to exit", }, @@ -318,7 +318,7 @@ func RegisterCommands(app *cli.Command, name string, aliases []string) { Name: "yes", Usage: "Automatically confirm the action", }, - &cli.StringFlag{ + &cli.Uint64Flag{ Name: "validator-id", Usage: "The validator id for which the exit is being notified", }, @@ -359,7 +359,7 @@ func RegisterCommands(app *cli.Command, name string, aliases []string) { Name: "yes", Usage: "Automatically confirm the action", }, - &cli.StringFlag{ + &cli.Uint64Flag{ Name: "validator-id", Usage: "The validator id for which the final balance is being notified", }, @@ -409,33 +409,35 @@ func RegisterCommands(app *cli.Command, name string, aliases []string) { return distribute(c.Bool("yes")) }, }, - // Add set-use-latest-delegate command { Name: "set-use-latest-delegate", Aliases: []string{"l"}, Usage: "Set the megapool to always use the latest delegate", - UsageText: "rocketpool megapool set-use-latest-delegate use-latest-delegate", - + UsageText: "rocketpool megapool set-use-latest-delegate", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "use-latest-delegate", + Usage: "Enable (true) or disable (false) automatic using the latest delegate; omit to be prompted based on the current setting", + }, + &cli.BoolFlag{ + Name: "yes", + Usage: "Automatically confirm the action", + }, + }, Action: func(ctx context.Context, c *cli.Command) error { - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { + if err := cliutils.ValidateArgCount(c, 0); err != nil { return err } - useLatest, err := cliutils.ValidateBool("use-latest-delegate", c.Args().Get(0)) - if err != nil { - return err + var useLatest *bool + if c.IsSet("use-latest-delegate") { + val := c.Bool("use-latest-delegate") + useLatest = &val } return setUseLatestDelegateMegapool(useLatest, c.Bool("yes")) }, - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "yes", - Usage: "Automatically confirm the action", - }, - }, }, }, }) diff --git a/rocketpool-cli/megapool/delegate.go b/rocketpool-cli/megapool/delegate.go index ae86a7ee4..c5d4cb531 100644 --- a/rocketpool-cli/megapool/delegate.go +++ b/rocketpool-cli/megapool/delegate.go @@ -9,7 +9,7 @@ import ( "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" ) -func setUseLatestDelegateMegapool(setting bool, yes bool) error { +func setUseLatestDelegateMegapool(setting *bool, yes bool) error { // Get RP client rp, err := rocketpool.NewClient().WithReady() if err != nil { @@ -29,26 +29,49 @@ func setUseLatestDelegateMegapool(setting bool, yes bool) error { return nil } + // If no flag was provided, prompt the user based on the current setting + if setting == nil { + currentSetting := status.Megapool.UseLatestDelegate + var desired bool + if currentSetting { + fmt.Println("Your megapool currently has automatic delegate upgrades enabled.") + if !prompt.Confirm("Would you like to disable automatic delegate upgrades?") { + fmt.Println("No changes made.") + return nil + } + desired = false + } else { + fmt.Println("Your megapool currently has automatic delegate upgrades disabled.") + if !prompt.Confirm("Would you like to enable automatic delegate upgrades?") { + fmt.Println("No changes made.") + return nil + } + desired = true + } + setting = &desired + } + megapoolAddress := status.Megapool.Address // Print message we're updating the setting - if setting == true { + if *setting { fmt.Printf("Updating the use-latest-delegate setting for megapool %s to enabled...\n", megapoolAddress.Hex()) } else { fmt.Printf("Updating the use-latest-delegate setting for megapool %s to disabled...\n", megapoolAddress.Hex()) } // Get the gas estimate - canResponse, err := rp.CanSetUseLatestDelegateMegapool(megapoolAddress, setting) + canResponse, err := rp.CanSetUseLatestDelegateMegapool(megapoolAddress, *setting) if err != nil { return fmt.Errorf("error checking if megapool %s could have its use-latest-delegate flag changed: %w", megapoolAddress.Hex(), err) } - if canResponse.MatchesCurrentSetting == true { - if setting == true { + if canResponse.MatchesCurrentSetting { + if *setting { fmt.Printf("Could not enable use-latest-delegate on the node's megapool, the setting is already enabled.") } else { fmt.Printf("Could not disable use-latest-delegate on the node's megapool, the setting is already disabled.") } + fmt.Println() return nil } @@ -65,7 +88,7 @@ func setUseLatestDelegateMegapool(setting bool, yes bool) error { } // Update flag - response, err := rp.SetUseLatestDelegateMegapool(megapoolAddress, setting) + response, err := rp.SetUseLatestDelegateMegapool(megapoolAddress, *setting) if err != nil { fmt.Printf("Could not set use latest delegate for megapool %s: %s. \n", megapoolAddress.Hex(), err) return nil diff --git a/rocketpool-cli/rocketpool-cli.go b/rocketpool-cli/rocketpool-cli.go index d3edfcdab..e3d883697 100644 --- a/rocketpool-cli/rocketpool-cli.go +++ b/rocketpool-cli/rocketpool-cli.go @@ -202,6 +202,7 @@ func main() { } if len(response.Alerts) > 0 { + fmt.Println() color.YellowPrintln("=== Alerts ===") for i, alert := range response.Alerts { fmt.Println(alert.ColorString()) diff --git a/rocketpool/api/api.go b/rocketpool/api/api.go index a8dc437cf..778f64ec1 100644 --- a/rocketpool/api/api.go +++ b/rocketpool/api/api.go @@ -1,116 +1 @@ package api - -import ( - "context" - "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/rocket-pool/smartnode/rocketpool/api/upgrade" - "github.com/urfave/cli/v3" - - "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.Command, 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.Command, name string, aliases []string) { - - // CLI command - command := cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Run Rocket Pool API commands", - Commands: []*cli.Command{}, - } - - // Don't show help message for api errors because of JSON serialisation - command.OnUsageError = func(ctx context.Context, c *cli.Command, 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"}) - upgrade.RegisterSubcommands(&command, "upgrade", []string{"u"}) - debug.RegisterSubcommands(&command, "debug", []string{"d"}) - - // Append a general wait-for-transaction command to support async operations - command.Commands = append(command.Commands, &cli.Command{ - Name: "wait", - Aliases: []string{"t"}, - Usage: "Wait for a transaction to complete", - UsageText: "rocketpool api wait tx-hash", - Action: func(ctx context.Context, c *cli.Command) 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/bid-lot.go b/rocketpool/api/auction/bid-lot.go index 3aa9485d6..3dd57714c 100644 --- a/rocketpool/api/auction/bid-lot.go +++ b/rocketpool/api/auction/bid-lot.go @@ -1,9 +1,9 @@ package auction import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/auction" "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/urfave/cli/v3" @@ -11,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canBidOnLot(c *cli.Command, lotIndex uint64, amountWei *big.Int) (*api.CanBidOnLotResponse, error) { @@ -99,7 +98,7 @@ func canBidOnLot(c *cli.Command, lotIndex uint64, amountWei *big.Int) (*api.CanB } -func bidOnLot(c *cli.Command, lotIndex uint64, amountWei *big.Int) (*api.BidOnLotResponse, error) { +func bidOnLot(c *cli.Command, lotIndex uint64, amountWei *big.Int, opts *bind.TransactOpts) (*api.BidOnLotResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -108,10 +107,6 @@ func bidOnLot(c *cli.Command, lotIndex uint64, amountWei *big.Int) (*api.BidOnLo if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -120,19 +115,8 @@ func bidOnLot(c *cli.Command, lotIndex uint64, amountWei *big.Int) (*api.BidOnLo // Response response := api.BidOnLotResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } opts.Value = amountWei - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Bid on lot hash, err := auction.PlaceBid(rp, lotIndex, opts) if err != nil { diff --git a/rocketpool/api/auction/claim-lot.go b/rocketpool/api/auction/claim-lot.go index fa17fbe20..99c1a4778 100644 --- a/rocketpool/api/auction/claim-lot.go +++ b/rocketpool/api/auction/claim-lot.go @@ -1,16 +1,15 @@ package auction import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/auction" "github.com/urfave/cli/v3" "golang.org/x/sync/errgroup" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canClaimFromLot(c *cli.Command, lotIndex uint64) (*api.CanClaimFromLotResponse, error) { @@ -92,7 +91,7 @@ func canClaimFromLot(c *cli.Command, lotIndex uint64) (*api.CanClaimFromLotRespo } -func claimFromLot(c *cli.Command, lotIndex uint64) (*api.ClaimFromLotResponse, error) { +func claimFromLot(c *cli.Command, lotIndex uint64, opts *bind.TransactOpts) (*api.ClaimFromLotResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -101,10 +100,6 @@ func claimFromLot(c *cli.Command, lotIndex uint64) (*api.ClaimFromLotResponse, e if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -113,18 +108,6 @@ func claimFromLot(c *cli.Command, lotIndex uint64) (*api.ClaimFromLotResponse, e // Response response := api.ClaimFromLotResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Claim from lot hash, err := auction.ClaimBid(rp, lotIndex, opts) if err != nil { diff --git a/rocketpool/api/auction/commands.go b/rocketpool/api/auction/commands.go deleted file mode 100644 index 5f6b72d7d..000000000 --- a/rocketpool/api/auction/commands.go +++ /dev/null @@ -1,235 +0,0 @@ -package auction - -import ( - "context" - - "github.com/urfave/cli/v3" - - "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.Commands = append(command.Commands, &cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage Rocket Pool RPL auctions", - Commands: []*cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get RPL auction status", - UsageText: "rocketpool api auction status", - Action: func(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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/create-lot.go b/rocketpool/api/auction/create-lot.go index 9752f1d9a..63baceb3d 100644 --- a/rocketpool/api/auction/create-lot.go +++ b/rocketpool/api/auction/create-lot.go @@ -1,8 +1,7 @@ package auction import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/auction" "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/urfave/cli/v3" @@ -10,7 +9,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canCreateLot(c *cli.Command) (*api.CanCreateLotResponse, error) { @@ -79,7 +77,7 @@ func canCreateLot(c *cli.Command) (*api.CanCreateLotResponse, error) { } -func createLot(c *cli.Command) (*api.CreateLotResponse, error) { +func createLot(c *cli.Command, opts *bind.TransactOpts) (*api.CreateLotResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -88,10 +86,6 @@ func createLot(c *cli.Command) (*api.CreateLotResponse, error) { if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -100,18 +94,6 @@ func createLot(c *cli.Command) (*api.CreateLotResponse, error) { // Response response := api.CreateLotResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Create lot lotIndex, hash, err := auction.CreateLot(rp, opts) if err != nil { diff --git a/rocketpool/api/auction/recover-lot.go b/rocketpool/api/auction/recover-lot.go index 6fe548ac4..6c211bf3d 100644 --- a/rocketpool/api/auction/recover-lot.go +++ b/rocketpool/api/auction/recover-lot.go @@ -1,16 +1,15 @@ package auction import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/auction" "github.com/urfave/cli/v3" "golang.org/x/sync/errgroup" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canRecoverRplFromLot(c *cli.Command, lotIndex uint64) (*api.CanRecoverRPLFromLotResponse, error) { @@ -97,7 +96,7 @@ func canRecoverRplFromLot(c *cli.Command, lotIndex uint64) (*api.CanRecoverRPLFr } -func recoverRplFromLot(c *cli.Command, lotIndex uint64) (*api.RecoverRPLFromLotResponse, error) { +func recoverRplFromLot(c *cli.Command, lotIndex uint64, opts *bind.TransactOpts) (*api.RecoverRPLFromLotResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -106,10 +105,6 @@ func recoverRplFromLot(c *cli.Command, lotIndex uint64) (*api.RecoverRPLFromLotR if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -118,18 +113,6 @@ func recoverRplFromLot(c *cli.Command, lotIndex uint64) (*api.RecoverRPLFromLotR // Response response := api.RecoverRPLFromLotResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Recover unclaimed RPL from lot hash, err := auction.RecoverUnclaimedRPL(rp, lotIndex, opts) if err != nil { diff --git a/rocketpool/api/auction/routes.go b/rocketpool/api/auction/routes.go new file mode 100644 index 000000000..b5e0ab268 --- /dev/null +++ b/rocketpool/api/auction/routes.go @@ -0,0 +1,140 @@ +package auction + +import ( + "fmt" + "math/big" + "net/http" + "strconv" + + "github.com/urfave/cli/v3" + + "github.com/rocket-pool/smartnode/shared/services" + 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.Command) { + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := createLot(c, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := bidOnLot(c, lotIndex, amountWei, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := claimFromLot(c, lotIndex, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := recoverRplFromLot(c, lotIndex, opts) + 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 c1dddf7ef..000000000 --- a/rocketpool/api/debug/commands.go +++ /dev/null @@ -1,100 +0,0 @@ -package debug - -import ( - "context" - "fmt" - - "github.com/urfave/cli/v3" - - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Commands = append(command.Commands, &cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Debugging and troubleshooting commands", - Commands: []*cli.Command{ - - { - Name: "export-validators", - Aliases: []string{"x"}, - Usage: "Exports a TSV file of validators", - UsageText: "rocketpool api debug export-validators", - Action: func(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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/debug/rewards-event.go b/rocketpool/api/debug/rewards-event.go new file mode 100644 index 000000000..e059f3222 --- /dev/null +++ b/rocketpool/api/debug/rewards-event.go @@ -0,0 +1,61 @@ +package debug + +import ( + "github.com/urfave/cli/v3" + + "github.com/rocket-pool/smartnode/shared/services" + rprewards "github.com/rocket-pool/smartnode/shared/services/rewards" + "github.com/rocket-pool/smartnode/shared/types/api" +) + +func getRewardsEvent(c *cli.Command, interval uint64) (*api.RewardsEventResponse, error) { + if err := services.RequireRocketStorage(c); err != nil { + return nil, err + } + rp, err := services.GetRocketPool(c) + if err != nil { + return nil, err + } + cfg, err := services.GetConfig(c) + if err != nil { + return nil, err + } + + previousRewardsPoolAddresses := cfg.Smartnode.GetPreviousRewardsPoolAddresses() + rewardsClient := rprewards.NewRewardsExecutionClient(rp) + + event, err := rewardsClient.GetRewardSnapshotEvent(previousRewardsPoolAddresses, interval, nil) + if err != nil { + return nil, err + } + + response := api.RewardsEventResponse{ + Found: true, + } + + response.Index = event.Index.String() + response.ExecutionBlock = event.ExecutionBlock.String() + response.ConsensusBlock = event.ConsensusBlock.String() + response.MerkleRoot = event.MerkleRoot.Hex() + response.IntervalsPassed = event.IntervalsPassed.String() + response.TreasuryRPL = event.TreasuryRPL.String() + response.UserETH = event.UserETH.String() + response.IntervalStartTime = event.IntervalStartTime.Unix() + response.IntervalEndTime = event.IntervalEndTime.Unix() + response.SubmissionTime = event.SubmissionTime.Unix() + + response.TrustedNodeRPL = make([]string, len(event.TrustedNodeRPL)) + for i, v := range event.TrustedNodeRPL { + response.TrustedNodeRPL[i] = v.String() + } + response.NodeRPL = make([]string, len(event.NodeRPL)) + for i, v := range event.NodeRPL { + response.NodeRPL[i] = v.String() + } + response.NodeETH = make([]string, len(event.NodeETH)) + for i, v := range event.NodeETH { + response.NodeETH[i] = v.String() + } + + return &response, nil +} diff --git a/rocketpool/api/debug/routes.go b/rocketpool/api/debug/routes.go new file mode 100644 index 000000000..b97c8e761 --- /dev/null +++ b/rocketpool/api/debug/routes.go @@ -0,0 +1,28 @@ +package debug + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/urfave/cli/v3" + + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +func RegisterRoutes(mux *http.ServeMux, c *cli.Command) { + mux.HandleFunc("/api/debug/rewards-event", func(w http.ResponseWriter, r *http.Request) { + raw := r.URL.Query().Get("interval") + if raw == "" { + apiutils.WriteErrorResponse(w, &apiutils.BadRequestError{Err: fmt.Errorf("missing required query parameter: interval")}) + return + } + interval, err := strconv.ParseUint(raw, 10, 64) + if err != nil { + apiutils.WriteErrorResponse(w, &apiutils.BadRequestError{Err: fmt.Errorf("invalid interval: %w", err)}) + return + } + resp, err := getRewardsEvent(c, interval) + apiutils.WriteResponse(w, resp, err) + }) +} diff --git a/rocketpool/api/megapool/claim-refunds.go b/rocketpool/api/megapool/claim-refunds.go index 08c5d2c73..356876510 100644 --- a/rocketpool/api/megapool/claim-refunds.go +++ b/rocketpool/api/megapool/claim-refunds.go @@ -1,15 +1,14 @@ package megapool import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canClaimRefund(c *cli.Command) (*api.CanClaimRefundResponse, error) { @@ -83,7 +82,7 @@ func canClaimRefund(c *cli.Command) (*api.CanClaimRefundResponse, error) { } -func claimRefund(c *cli.Command) (*api.ClaimRefundResponse, error) { +func claimRefund(c *cli.Command, opts *bind.TransactOpts) (*api.ClaimRefundResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -119,18 +118,6 @@ func claimRefund(c *cli.Command) (*api.ClaimRefundResponse, error) { return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Dissolve hash, err := mp.ClaimRefund(opts) if err != nil { diff --git a/rocketpool/api/megapool/commands.go b/rocketpool/api/megapool/commands.go deleted file mode 100644 index fc224f6ca..000000000 --- a/rocketpool/api/megapool/commands.go +++ /dev/null @@ -1,730 +0,0 @@ -package megapool - -import ( - "context" - - "github.com/urfave/cli/v3" - - "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.Commands = append(command.Commands, &cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the node's megapool", - Commands: []*cli.Command{ - { - Name: "can-distribute-megapool", - Usage: "Check if can distribute megapool rewards", - UsageText: "rocketpool api node can-distribute-megapool", - Action: func(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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 use-latest-delegate", - Action: func(ctx context.Context, c *cli.Command) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - megapoolAddress, err := cliutils.ValidateAddress("megapool address", c.Args().Get(0)) - if err != nil { - return err - } - - useLatest, err := cliutils.ValidateBool("use-latest-delegate", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canSetUseLatestDelegate(c, megapoolAddress, useLatest)) - 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 use-latest-delegate", - Action: func(ctx context.Context, c *cli.Command) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 2); err != nil { - return err - } - megapoolAddress, err := cliutils.ValidateAddress("megapool address", c.Args().Get(0)) - if err != nil { - return err - } - - useLatest, err := cliutils.ValidateBool("use-latest-delegate", c.Args().Get(1)) - if err != nil { - return err - } - - // Run - api.PrintResponse(setUseLatestDelegate(c, megapoolAddress, useLatest)) - 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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/delegate.go b/rocketpool/api/megapool/delegate.go index 756dd3e54..19bcc04f6 100644 --- a/rocketpool/api/megapool/delegate.go +++ b/rocketpool/api/megapool/delegate.go @@ -3,11 +3,11 @@ package megapool import ( "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -49,16 +49,12 @@ func canDelegateUpgrade(c *cli.Command, megapoolAddress common.Address) (*api.Me return &response, nil } -func delegateUpgrade(c *cli.Command, megapoolAddress common.Address) (*api.MegapoolDelegateUpgradeResponse, error) { +func delegateUpgrade(c *cli.Command, megapoolAddress common.Address, opts *bind.TransactOpts) (*api.MegapoolDelegateUpgradeResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -73,18 +69,6 @@ func delegateUpgrade(c *cli.Command, megapoolAddress common.Address) (*api.Megap return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Upgrade hash, err := mega.DelegateUpgrade(opts) if err != nil { @@ -128,7 +112,7 @@ func getUseLatestDelegate(c *cli.Command, megapoolAddress common.Address) (*api. } -func canSetUseLatestDelegate(c *cli.Command, megapoolAddress common.Address, setting bool) (*api.MegapoolCanSetUseLatestDelegateResponse, error) { +func canSetUseLatestDelegate(c *cli.Command, megapoolAddress common.Address, useLatest bool) (*api.MegapoolCanSetUseLatestDelegateResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err @@ -156,7 +140,7 @@ func canSetUseLatestDelegate(c *cli.Command, megapoolAddress common.Address, set if err != nil { return nil, err } - if currentSetting == setting { + if currentSetting == useLatest { response.MatchesCurrentSetting = true return &response, nil } @@ -168,7 +152,7 @@ func canSetUseLatestDelegate(c *cli.Command, megapoolAddress common.Address, set return nil, err } - gasInfo, err := mega.EstimateSetUseLatestDelegateGas(setting, opts) + gasInfo, err := mega.EstimateSetUseLatestDelegateGas(useLatest, opts) if err == nil { response.GasInfo = gasInfo } @@ -178,15 +162,11 @@ func canSetUseLatestDelegate(c *cli.Command, megapoolAddress common.Address, set } -func setUseLatestDelegate(c *cli.Command, megapoolAddress common.Address, setting bool) (*api.MegapoolSetUseLatestDelegateResponse, error) { +func setUseLatestDelegate(c *cli.Command, megapoolAddress common.Address, useLatest bool, opts *bind.TransactOpts) (*api.MegapoolSetUseLatestDelegateResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -201,20 +181,8 @@ func setUseLatestDelegate(c *cli.Command, megapoolAddress common.Address, settin return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Set the new setting - hash, err := mega.SetUseLatestDelegate(setting, opts) + hash, err := mega.SetUseLatestDelegate(useLatest, opts) if err != nil { return nil, err } diff --git a/rocketpool/api/megapool/dissolve-validator.go b/rocketpool/api/megapool/dissolve-validator.go index 1384c45aa..de6d40e8b 100644 --- a/rocketpool/api/megapool/dissolve-validator.go +++ b/rocketpool/api/megapool/dissolve-validator.go @@ -1,12 +1,10 @@ package megapool import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -83,7 +81,7 @@ func canDissolveValidator(c *cli.Command, validatorId uint32) (*api.CanDissolveV } -func dissolveValidator(c *cli.Command, validatorId uint32) (*api.DissolveValidatorResponse, error) { +func dissolveValidator(c *cli.Command, validatorId uint32, opts *bind.TransactOpts) (*api.DissolveValidatorResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -119,18 +117,6 @@ func dissolveValidator(c *cli.Command, validatorId uint32) (*api.DissolveValidat return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Dissolve hash, err := mp.DissolveValidator(validatorId, opts) if err != nil { diff --git a/rocketpool/api/megapool/dissolve-with-proof.go b/rocketpool/api/megapool/dissolve-with-proof.go index 4e820a472..34f360f42 100644 --- a/rocketpool/api/megapool/dissolve-with-proof.go +++ b/rocketpool/api/megapool/dissolve-with-proof.go @@ -3,12 +3,12 @@ package megapool import ( "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -110,7 +110,7 @@ func canDissolveWithProof(c *cli.Command, validatorId uint32) (*api.CanDissolveW } -func dissolveWithProof(c *cli.Command, validatorId uint32) (*api.DissolveWithProofResponse, error) { +func dissolveWithProof(c *cli.Command, validatorId uint32, opts *bind.TransactOpts) (*api.DissolveWithProofResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -165,18 +165,6 @@ func dissolveWithProof(c *cli.Command, validatorId uint32) (*api.DissolveWithPro return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Dissolve tx, err := megapool.DissolveWithProof(rp, megapoolAddress, validatorId, slotTimestamp, validatorProof, slotProof, opts) if err != nil { diff --git a/rocketpool/api/megapool/distribute.go b/rocketpool/api/megapool/distribute.go index dee731e54..f2de45f57 100644 --- a/rocketpool/api/megapool/distribute.go +++ b/rocketpool/api/megapool/distribute.go @@ -1,12 +1,10 @@ package megapool import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -93,7 +91,7 @@ func canDistributeMegapool(c *cli.Command) (*api.CanDistributeMegapoolResponse, return &response, nil } -func distributeMegapool(c *cli.Command) (*api.DistributeMegapoolResponse, error) { +func distributeMegapool(c *cli.Command, opts *bind.TransactOpts) (*api.DistributeMegapoolResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err @@ -115,18 +113,6 @@ func distributeMegapool(c *cli.Command) (*api.DistributeMegapoolResponse, error) return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Response response := api.DistributeMegapoolResponse{} diff --git a/rocketpool/api/megapool/exit-queue.go b/rocketpool/api/megapool/exit-queue.go index 5b9da30ea..e69ce609b 100644 --- a/rocketpool/api/megapool/exit-queue.go +++ b/rocketpool/api/megapool/exit-queue.go @@ -1,12 +1,10 @@ package megapool import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -76,7 +74,7 @@ func canExitQueue(c *cli.Command, validatorIndex uint32) (*api.CanExitQueueRespo } -func exitQueue(c *cli.Command, validatorIndex uint32) (*api.ExitQueueResponse, error) { +func exitQueue(c *cli.Command, validatorIndex uint32, opts *bind.TransactOpts) (*api.ExitQueueResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -102,18 +100,6 @@ func exitQueue(c *cli.Command, validatorIndex uint32) (*api.ExitQueueResponse, e // Response response := api.ExitQueueResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Get the megapool address megapoolAddress, err := megapool.GetMegapoolExpectedAddress(rp, nodeAccount.Address, nil) if err != nil { diff --git a/rocketpool/api/megapool/notify-final-balance.go b/rocketpool/api/megapool/notify-final-balance.go index 993bed565..dc1cbacb9 100644 --- a/rocketpool/api/megapool/notify-final-balance.go +++ b/rocketpool/api/megapool/notify-final-balance.go @@ -4,12 +4,12 @@ import ( "fmt" "strconv" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -138,7 +138,7 @@ func canNotifyFinalBalance(c *cli.Command, validatorId uint32, withdrawalSlot ui } -func notifyFinalBalance(c *cli.Command, validatorId uint32, withdrawalSlot uint64) (*api.NotifyValidatorExitResponse, error) { +func notifyFinalBalance(c *cli.Command, validatorId uint32, withdrawalSlot uint64, opts *bind.TransactOpts) (*api.NotifyValidatorExitResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -195,12 +195,6 @@ func notifyFinalBalance(c *cli.Command, validatorId uint32, withdrawalSlot uint6 return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - validatorStatus, err := bc.GetValidatorStatus(types.ValidatorPubkey(validatorInfo.Pubkey), nil) if err != nil { return nil, fmt.Errorf("Error getting validator status from beacon chain: %w", err) @@ -255,12 +249,6 @@ func notifyFinalBalance(c *cli.Command, validatorId uint32, withdrawalSlot uint6 return nil, err } - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Notify the validator exit tx, err := megapool.NotifyFinalBalance(rp, megapoolAddress, validatorId, slotTimestamp, finalBalanceProof, validatorProof, slotProof, opts) if err != nil { diff --git a/rocketpool/api/megapool/notify-validator-exit.go b/rocketpool/api/megapool/notify-validator-exit.go index 77587104a..064bb3189 100644 --- a/rocketpool/api/megapool/notify-validator-exit.go +++ b/rocketpool/api/megapool/notify-validator-exit.go @@ -1,13 +1,11 @@ package megapool import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -89,7 +87,7 @@ func canNotifyValidatorExit(c *cli.Command, validatorId uint32) (*api.CanNotifyV } -func notifyValidatorExit(c *cli.Command, validatorId uint32) (*api.NotifyValidatorExitResponse, error) { +func notifyValidatorExit(c *cli.Command, validatorId uint32, opts *bind.TransactOpts) (*api.NotifyValidatorExitResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -148,18 +146,6 @@ func notifyValidatorExit(c *cli.Command, validatorId uint32) (*api.NotifyValidat return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Notify the validator exit tx, err := megapool.NotifyExit(rp, megapoolAddress, validatorId, slotTimetamp, validatorProof, slotProof, opts) if err != nil { diff --git a/rocketpool/api/megapool/reduce-bond.go b/rocketpool/api/megapool/reduce-bond.go index b5052056d..745b0ea4b 100644 --- a/rocketpool/api/megapool/reduce-bond.go +++ b/rocketpool/api/megapool/reduce-bond.go @@ -1,15 +1,14 @@ package megapool import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canReduceBond(c *cli.Command, amount *big.Int) (*api.CanReduceBondResponse, error) { @@ -90,7 +89,7 @@ func canReduceBond(c *cli.Command, amount *big.Int) (*api.CanReduceBondResponse, } -func reduceBond(c *cli.Command, amount *big.Int) (*api.ReduceBondResponse, error) { +func reduceBond(c *cli.Command, amount *big.Int, opts *bind.TransactOpts) (*api.ReduceBondResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -126,18 +125,6 @@ func reduceBond(c *cli.Command, amount *big.Int) (*api.ReduceBondResponse, error return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Reduce bond hash, err := mp.ReduceBond(amount, opts) if err != nil { diff --git a/rocketpool/api/megapool/repay-debt.go b/rocketpool/api/megapool/repay-debt.go index fe8487794..259880aae 100644 --- a/rocketpool/api/megapool/repay-debt.go +++ b/rocketpool/api/megapool/repay-debt.go @@ -5,12 +5,12 @@ import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canRepayDebt(c *cli.Command, amount *big.Int) (*api.CanRepayDebtResponse, error) { @@ -103,7 +103,7 @@ func canRepayDebt(c *cli.Command, amount *big.Int) (*api.CanRepayDebtResponse, e } -func repayDebt(c *cli.Command, amount *big.Int) (*api.RepayDebtResponse, error) { +func repayDebt(c *cli.Command, amount *big.Int, opts *bind.TransactOpts) (*api.RepayDebtResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -149,20 +149,8 @@ func repayDebt(c *cli.Command, amount *big.Int) (*api.RepayDebtResponse, error) return nil, fmt.Errorf("no debt to repay") } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - opts.Value = amount - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Repay debt hash, err := mp.RepayDebt(opts) if err != nil { diff --git a/rocketpool/api/megapool/routes.go b/rocketpool/api/megapool/routes.go new file mode 100644 index 000000000..679370a60 --- /dev/null +++ b/rocketpool/api/megapool/routes.go @@ -0,0 +1,391 @@ +package megapool + +import ( + "fmt" + "math/big" + "net/http" + "strconv" + + "github.com/ethereum/go-ethereum/common" + "github.com/urfave/cli/v3" + + "github.com/rocket-pool/smartnode/shared/services" + 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.Command) { + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := claimRefund(c, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := repayDebt(c, amountWei, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := reduceBond(c, amountWei, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := stake(c, validatorId, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := dissolveValidator(c, validatorId, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := dissolveWithProof(c, validatorId, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := notifyValidatorExit(c, validatorId, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := notifyFinalBalance(c, validatorId, slot, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := exitQueue(c, validatorIndex, opts) + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := distributeMegapool(c, opts) + 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")) + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := delegateUpgrade(c, address, opts) + 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")) + setLatest := r.URL.Query().Get("setLatest") == "true" + resp, err := canSetUseLatestDelegate(c, address, setLatest) + 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")) + setting := r.FormValue("setting") == "true" + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := setUseLatestDelegate(c, address, setting, opts) + 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/megapool/stake.go b/rocketpool/api/megapool/stake.go index c5a02ee31..290d0f237 100644 --- a/rocketpool/api/megapool/stake.go +++ b/rocketpool/api/megapool/stake.go @@ -1,16 +1,15 @@ package megapool import ( - "fmt" "strings" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canStake(c *cli.Command, validatorId uint64) (*api.CanStakeResponse, error) { @@ -115,7 +114,7 @@ func canStake(c *cli.Command, validatorId uint64) (*api.CanStakeResponse, error) } -func stake(c *cli.Command, validatorId uint64) (*api.StakeResponse, error) { +func stake(c *cli.Command, validatorId uint64, opts *bind.TransactOpts) (*api.StakeResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -170,18 +169,6 @@ func stake(c *cli.Command, validatorId uint64) (*api.StakeResponse, error) { return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Stake tx, err := megapool.Stake(rp, megapoolAddress, uint32(validatorId), slotTimestamp, validatorProof, slotProof, opts) if err != nil { diff --git a/rocketpool/api/minipool/close.go b/rocketpool/api/minipool/close.go index 43e4d6e3e..70f8e23cb 100644 --- a/rocketpool/api/minipool/close.go +++ b/rocketpool/api/minipool/close.go @@ -18,7 +18,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func getMinipoolCloseDetailsForNode(c *cli.Command) (*api.GetMinipoolCloseDetailsForNodeResponse, error) { @@ -307,16 +306,12 @@ func getMinipoolCloseDetails(rp *rocketpool.RocketPool, minipoolAddress common.A } -func closeMinipool(c *cli.Command, minipoolAddress common.Address) (*api.CloseMinipoolResponse, error) { +func closeMinipool(c *cli.Command, minipoolAddress common.Address, opts *bind.TransactOpts) (*api.CloseMinipoolResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -337,18 +332,6 @@ func closeMinipool(c *cli.Command, minipoolAddress common.Address) (*api.CloseMi return nil, fmt.Errorf("cannot create v3 binding for minipool %s, version %d", minipoolAddress.Hex(), mp.GetVersion()) } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Get some details var status types.MinipoolStatus var distributed bool diff --git a/rocketpool/api/minipool/commands.go b/rocketpool/api/minipool/commands.go deleted file mode 100644 index e133bacc9..000000000 --- a/rocketpool/api/minipool/commands.go +++ /dev/null @@ -1,596 +0,0 @@ -package minipool - -import ( - "context" - - "github.com/urfave/cli/v3" - - "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.Commands = append(command.Commands, &cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the node's minipools", - Commands: []*cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get a list of the node's minipools", - UsageText: "rocketpool api minipool status", - Action: func(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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: "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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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 - - }, - }, - }, - }) -} diff --git a/rocketpool/api/minipool/delegate.go b/rocketpool/api/minipool/delegate.go index 3c0850e22..39461ad3e 100644 --- a/rocketpool/api/minipool/delegate.go +++ b/rocketpool/api/minipool/delegate.go @@ -3,6 +3,7 @@ package minipool import ( "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/minipool" "github.com/rocket-pool/smartnode/bindings/rocketpool" @@ -11,7 +12,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canDelegateUpgrade(c *cli.Command, minipoolAddress common.Address) (*api.CanDelegateUpgradeResponse, error) { @@ -60,16 +60,12 @@ func canDelegateUpgrade(c *cli.Command, minipoolAddress common.Address) (*api.Ca } -func delegateUpgrade(c *cli.Command, minipoolAddress common.Address) (*api.DelegateUpgradeResponse, error) { +func delegateUpgrade(c *cli.Command, minipoolAddress common.Address, opts *bind.TransactOpts) (*api.DelegateUpgradeResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -84,18 +80,6 @@ func delegateUpgrade(c *cli.Command, minipoolAddress common.Address) (*api.Deleg return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Upgrade hash, err := mp.DelegateUpgrade(opts) if err != nil { @@ -174,17 +158,13 @@ func canSetUseLatestDelegate(c *cli.Command, minipoolAddress common.Address) (*a } -func setUseLatestDelegate(c *cli.Command, minipoolAddress common.Address) (*api.SetUseLatestDelegateResponse, error) { +func setUseLatestDelegate(c *cli.Command, minipoolAddress common.Address, opts *bind.TransactOpts) (*api.SetUseLatestDelegateResponse, error) { setting := true // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -225,18 +205,6 @@ func setUseLatestDelegate(c *cli.Command, minipoolAddress common.Address) (*api. } } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Set the new setting hash, err := mp.SetUseLatestDelegate(opts) if err != nil { diff --git a/rocketpool/api/minipool/dissolve.go b/rocketpool/api/minipool/dissolve.go index 8c2a6daf1..95fec9633 100644 --- a/rocketpool/api/minipool/dissolve.go +++ b/rocketpool/api/minipool/dissolve.go @@ -1,8 +1,7 @@ package minipool import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/minipool" "github.com/rocket-pool/smartnode/bindings/types" @@ -10,7 +9,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canDissolveMinipool(c *cli.Command, minipoolAddress common.Address) (*api.CanDissolveMinipoolResponse, error) { @@ -69,16 +67,12 @@ func canDissolveMinipool(c *cli.Command, minipoolAddress common.Address) (*api.C } -func dissolveMinipool(c *cli.Command, minipoolAddress common.Address) (*api.DissolveMinipoolResponse, error) { +func dissolveMinipool(c *cli.Command, minipoolAddress common.Address, opts *bind.TransactOpts) (*api.DissolveMinipoolResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -93,18 +87,6 @@ func dissolveMinipool(c *cli.Command, minipoolAddress common.Address) (*api.Diss return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Dissolve hash, err := mp.Dissolve(opts) if err != nil { diff --git a/rocketpool/api/minipool/distribute.go b/rocketpool/api/minipool/distribute.go index d6d8f9be7..3ea08a3e1 100644 --- a/rocketpool/api/minipool/distribute.go +++ b/rocketpool/api/minipool/distribute.go @@ -5,6 +5,7 @@ import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/minipool" "github.com/rocket-pool/smartnode/bindings/types" @@ -14,7 +15,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func getDistributeBalanceDetails(c *cli.Command) (*api.GetDistributeBalanceDetailsResponse, error) { @@ -191,16 +191,12 @@ func getDistributeBalanceDetails(c *cli.Command) (*api.GetDistributeBalanceDetai } -func distributeBalance(c *cli.Command, minipoolAddress common.Address) (*api.CloseMinipoolResponse, error) { +func distributeBalance(c *cli.Command, minipoolAddress common.Address, opts *bind.TransactOpts) (*api.CloseMinipoolResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -215,18 +211,6 @@ func distributeBalance(c *cli.Command, minipoolAddress common.Address) (*api.Clo return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Distribute the minipool's balance mpv3, success := minipool.GetMinipoolAsV3(mp) if !success { diff --git a/rocketpool/api/minipool/promote.go b/rocketpool/api/minipool/promote.go index 76b370f27..c9299f991 100644 --- a/rocketpool/api/minipool/promote.go +++ b/rocketpool/api/minipool/promote.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/minipool" "github.com/rocket-pool/smartnode/bindings/settings/trustednode" @@ -12,7 +13,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canPromoteMinipool(c *cli.Command, minipoolAddress common.Address) (*api.CanPromoteMinipoolResponse, error) { @@ -111,16 +111,12 @@ func canPromoteMinipool(c *cli.Command, minipoolAddress common.Address) (*api.Ca } -func promoteMinipool(c *cli.Command, minipoolAddress common.Address) (*api.StakeMinipoolResponse, error) { +func promoteMinipool(c *cli.Command, minipoolAddress common.Address, opts *bind.TransactOpts) (*api.StakeMinipoolResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -139,18 +135,6 @@ func promoteMinipool(c *cli.Command, minipoolAddress common.Address) (*api.Stake return nil, fmt.Errorf("cannot promte minipool %s because its delegate version is too low (v%d); please update the delegate to promote it", mp.GetAddress().Hex(), mp.GetVersion()) } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Promote hash, err := mpv3.Promote(opts) if err != nil { diff --git a/rocketpool/api/minipool/refund.go b/rocketpool/api/minipool/refund.go index ac9ec36ea..78f7ce15b 100644 --- a/rocketpool/api/minipool/refund.go +++ b/rocketpool/api/minipool/refund.go @@ -1,16 +1,15 @@ package minipool import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/minipool" "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canRefundMinipool(c *cli.Command, minipoolAddress common.Address) (*api.CanRefundMinipoolResponse, error) { @@ -69,16 +68,12 @@ func canRefundMinipool(c *cli.Command, minipoolAddress common.Address) (*api.Can } -func refundMinipool(c *cli.Command, minipoolAddress common.Address) (*api.RefundMinipoolResponse, error) { +func refundMinipool(c *cli.Command, minipoolAddress common.Address, opts *bind.TransactOpts) (*api.RefundMinipoolResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -93,18 +88,6 @@ func refundMinipool(c *cli.Command, minipoolAddress common.Address) (*api.Refund return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Refund hash, err := mp.Refund(opts) if err != nil { diff --git a/rocketpool/api/minipool/rescue-dissolved.go b/rocketpool/api/minipool/rescue-dissolved.go index 5a3898ee0..99b527d75 100644 --- a/rocketpool/api/minipool/rescue-dissolved.go +++ b/rocketpool/api/minipool/rescue-dissolved.go @@ -19,7 +19,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services/contracts" "github.com/rocket-pool/smartnode/shared/services/wallet" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/rocket-pool/smartnode/shared/utils/validator" ) @@ -281,7 +280,7 @@ func getDepositTx(rp *rocketpool.RocketPool, w wallet.Wallet, bc beacon.Client, } -func rescueDissolvedMinipool(c *cli.Command, minipoolAddress common.Address, amount *big.Int, submit bool) (*api.RescueDissolvedMinipoolResponse, error) { +func rescueDissolvedMinipool(c *cli.Command, minipoolAddress common.Address, amount *big.Int, submit bool, opts *bind.TransactOpts) (*api.RescueDissolvedMinipoolResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -303,19 +302,8 @@ func rescueDissolvedMinipool(c *cli.Command, minipoolAddress common.Address, amo // Response response := api.RescueDissolvedMinipoolResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } opts.Value = amount - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - opts.NoSend = !submit // Submit the rescue deposit diff --git a/rocketpool/api/minipool/routes.go b/rocketpool/api/minipool/routes.go new file mode 100644 index 000000000..32eb2c058 --- /dev/null +++ b/rocketpool/api/minipool/routes.go @@ -0,0 +1,355 @@ +package minipool + +import ( + "fmt" + "math/big" + "net/http" + + "github.com/ethereum/go-ethereum/common" + "github.com/urfave/cli/v3" + + "github.com/rocket-pool/smartnode/shared/services" + 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.Command) { + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := refundMinipool(c, addr, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := stakeMinipool(c, addr, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := promoteMinipool(c, addr, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := dissolveMinipool(c, addr, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := closeMinipool(c, addr, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := delegateUpgrade(c, addr, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := setUseLatestDelegate(c, addr, opts) + 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/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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := distributeBalance(c, addr, opts) + 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" + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := rescueDissolvedMinipool(c, addr, amount, submit, opts) + 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/minipool/stake.go b/rocketpool/api/minipool/stake.go index ea2bd2320..72a2337a9 100644 --- a/rocketpool/api/minipool/stake.go +++ b/rocketpool/api/minipool/stake.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/minipool" "github.com/rocket-pool/smartnode/bindings/settings/trustednode" @@ -13,7 +14,6 @@ import ( rptypes "github.com/rocket-pool/smartnode/bindings/types" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/rocket-pool/smartnode/shared/utils/validator" ) @@ -149,7 +149,7 @@ func canStakeMinipool(c *cli.Command, minipoolAddress common.Address) (*api.CanS } -func stakeMinipool(c *cli.Command, minipoolAddress common.Address) (*api.StakeMinipoolResponse, error) { +func stakeMinipool(c *cli.Command, minipoolAddress common.Address, opts *bind.TransactOpts) (*api.StakeMinipoolResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -177,18 +177,6 @@ func stakeMinipool(c *cli.Command, minipoolAddress common.Address) (*api.StakeMi return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Get eth2 config eth2Config, err := bc.GetEth2Config() if err != nil { diff --git a/rocketpool/api/network/commands.go b/rocketpool/api/network/commands.go deleted file mode 100644 index e0857bf68..000000000 --- a/rocketpool/api/network/commands.go +++ /dev/null @@ -1,203 +0,0 @@ -package network - -import ( - "context" - - "github.com/urfave/cli/v3" - - "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.Commands = append(command.Commands, &cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage Rocket Pool network parameters", - Commands: []*cli.Command{ - - { - Name: "node-fee", - Aliases: []string{"f"}, - Usage: "Get the current network node commission rate", - UsageText: "rocketpool api network node-fee", - Action: func(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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..bc07a5b61 --- /dev/null +++ b/rocketpool/api/network/routes.go @@ -0,0 +1,81 @@ +package network + +import ( + "net/http" + "strconv" + + "github.com/urfave/cli/v3" + + 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.Command) { + 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/burn.go b/rocketpool/api/node/burn.go index d253597c4..ae75e0632 100644 --- a/rocketpool/api/node/burn.go +++ b/rocketpool/api/node/burn.go @@ -1,16 +1,16 @@ package node import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/tokens" "github.com/urfave/cli/v3" "golang.org/x/sync/errgroup" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canNodeBurn(c *cli.Command, amountWei *big.Int, token string) (*api.CanNodeBurnResponse, error) { @@ -103,7 +103,7 @@ func canNodeBurn(c *cli.Command, amountWei *big.Int, token string) (*api.CanNode } -func nodeBurn(c *cli.Command, amountWei *big.Int, token string) (*api.NodeBurnResponse, error) { +func nodeBurn(c *cli.Command, amountWei *big.Int, token string, opts *bind.TransactOpts) (*api.NodeBurnResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -112,10 +112,6 @@ func nodeBurn(c *cli.Command, amountWei *big.Int, token string) (*api.NodeBurnRe if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -124,18 +120,6 @@ func nodeBurn(c *cli.Command, amountWei *big.Int, token string) (*api.NodeBurnRe // Response response := api.NodeBurnResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Handle token type switch token { case "reth": diff --git a/rocketpool/api/node/claim-rewards.go b/rocketpool/api/node/claim-rewards.go index dd433e0cd..d6a62ca60 100644 --- a/rocketpool/api/node/claim-rewards.go +++ b/rocketpool/api/node/claim-rewards.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli/v3" "golang.org/x/sync/errgroup" @@ -21,7 +22,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services/config" rprewards "github.com/rocket-pool/smartnode/shared/services/rewards" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" rputils "github.com/rocket-pool/smartnode/shared/utils/rp" ) @@ -240,7 +240,7 @@ func canClaimRewards(c *cli.Command, indicesString string) (*api.CanNodeClaimRew return &response, nil } -func claimRewards(c *cli.Command, indicesString string) (*api.NodeClaimRewardsResponse, error) { +func claimRewards(c *cli.Command, indicesString string, opts *bind.TransactOpts) (*api.NodeClaimRewardsResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -268,18 +268,6 @@ func claimRewards(c *cli.Command, indicesString string) (*api.NodeClaimRewardsRe return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Get the rewards claims, err := getRewardsForIntervals(rp, cfg, nodeAccount.Address, indicesString) if err != nil { @@ -345,7 +333,7 @@ func canClaimAndStakeRewards(c *cli.Command, indicesString string, stakeAmount * } -func claimAndStakeRewards(c *cli.Command, indicesString string, stakeAmount *big.Int) (*api.NodeClaimAndStakeRewardsResponse, error) { +func claimAndStakeRewards(c *cli.Command, indicesString string, stakeAmount *big.Int, opts *bind.TransactOpts) (*api.NodeClaimAndStakeRewardsResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -373,18 +361,6 @@ func claimAndStakeRewards(c *cli.Command, indicesString string, stakeAmount *big return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Get the rewards claims, err := getRewardsForIntervals(rp, cfg, nodeAccount.Address, indicesString) if err != nil { diff --git a/rocketpool/api/node/claim-rpl.go b/rocketpool/api/node/claim-rpl.go index a67828ac5..f813be4d8 100644 --- a/rocketpool/api/node/claim-rpl.go +++ b/rocketpool/api/node/claim-rpl.go @@ -4,12 +4,13 @@ import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/bindings/legacy/v1.0.0/rewards" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canNodeClaimRpl(c *cli.Command) (*api.CanNodeClaimRplResponse, error) { @@ -75,7 +76,7 @@ func canNodeClaimRpl(c *cli.Command) (*api.CanNodeClaimRplResponse, error) { return &response, nil } -func nodeClaimRpl(c *cli.Command) (*api.NodeClaimRplResponse, error) { +func nodeClaimRpl(c *cli.Command, opts *bind.TransactOpts) (*api.NodeClaimRplResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -84,10 +85,6 @@ func nodeClaimRpl(c *cli.Command) (*api.NodeClaimRplResponse, error) { if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -100,18 +97,6 @@ func nodeClaimRpl(c *cli.Command) (*api.NodeClaimRplResponse, error) { // Response response := api.NodeClaimRplResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Claim rewards legacyClaimNodeAddress := cfg.Smartnode.GetV100ClaimNodeAddress() hash, err := rewards.ClaimNodeRewards(rp, opts, &legacyClaimNodeAddress) diff --git a/rocketpool/api/node/claim-unclaimed-rewards.go b/rocketpool/api/node/claim-unclaimed-rewards.go index 81bb93ff3..cfb3fcf17 100644 --- a/rocketpool/api/node/claim-unclaimed-rewards.go +++ b/rocketpool/api/node/claim-unclaimed-rewards.go @@ -1,15 +1,13 @@ package node import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canClaimUnclaimedRewards(c *cli.Command, nodeAddress common.Address) (*api.CanClaimUnclaimedRewardsResponse, error) { @@ -53,16 +51,12 @@ func canClaimUnclaimedRewards(c *cli.Command, nodeAddress common.Address) (*api. } -func claimUnclaimedRewards(c *cli.Command, nodeAddress common.Address) (*api.ClaimUnclaimedRewardsResponse, error) { +func claimUnclaimedRewards(c *cli.Command, nodeAddress common.Address, opts *bind.TransactOpts) (*api.ClaimUnclaimedRewardsResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -71,18 +65,6 @@ func claimUnclaimedRewards(c *cli.Command, nodeAddress common.Address) (*api.Cla // Response response := api.ClaimUnclaimedRewardsResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Claim unclaimed rewards hash, err := node.ClaimUnclaimedRewards(rp, nodeAddress, opts) if err != nil { diff --git a/rocketpool/api/node/commands.go b/rocketpool/api/node/commands.go deleted file mode 100644 index 32965eeae..000000000 --- a/rocketpool/api/node/commands.go +++ /dev/null @@ -1,1820 +0,0 @@ -package node - -import ( - "context" - - "github.com/urfave/cli/v3" - - "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.Commands = append(command.Commands, &cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the node", - Commands: []*cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get the node's status", - UsageText: "rocketpool api node status", - Action: func(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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: "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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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/create-vacant-minipool.go b/rocketpool/api/node/create-vacant-minipool.go index f97a8b2f2..de75b8bbc 100644 --- a/rocketpool/api/node/create-vacant-minipool.go +++ b/rocketpool/api/node/create-vacant-minipool.go @@ -6,6 +6,8 @@ import ( "math/big" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/minipool" "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/bindings/settings/protocol" @@ -16,7 +18,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/types/api" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" "golang.org/x/sync/errgroup" ) @@ -153,7 +154,7 @@ func canCreateVacantMinipool(c *cli.Command, amountWei *big.Int, minNodeFee floa } -func createVacantMinipool(c *cli.Command, amountWei *big.Int, minNodeFee float64, salt *big.Int, pubkey rptypes.ValidatorPubkey) (*api.CreateVacantMinipoolResponse, error) { +func createVacantMinipool(c *cli.Command, amountWei *big.Int, minNodeFee float64, salt *big.Int, pubkey rptypes.ValidatorPubkey, opts *bind.TransactOpts) (*api.CreateVacantMinipoolResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -220,12 +221,6 @@ func createVacantMinipool(c *cli.Command, amountWei *big.Int, minNodeFee float64 scrubPeriod := time.Duration(scrubPeriodUnix) * time.Second response.ScrubPeriod = scrubPeriod - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - // Get the next minipool address and withdrawal credentials minipoolAddress, err := minipool.GetExpectedAddress(rp, nodeAccount.Address, salt, nil) if err != nil { @@ -256,12 +251,6 @@ func createVacantMinipool(c *cli.Command, amountWei *big.Int, minNodeFee float64 balanceWei := big.NewInt(0).SetUint64(validatorStatus.Balance) balanceWei.Mul(balanceWei, big.NewInt(1e9)) - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Create the minipool tx, err := node.CreateVacantMinipool(rp, amountWei, minNodeFee, pubkey, salt, minipoolAddress, balanceWei, opts) if err != nil { diff --git a/rocketpool/api/node/deposit.go b/rocketpool/api/node/deposit.go index 3c6e894f3..c3d5c9e5d 100644 --- a/rocketpool/api/node/deposit.go +++ b/rocketpool/api/node/deposit.go @@ -7,6 +7,7 @@ import ( "math/big" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing" "github.com/rocket-pool/smartnode/bindings/deposit" @@ -23,7 +24,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/beacon" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/rocket-pool/smartnode/shared/utils/validator" eth2types "github.com/wealdtech/go-eth2-types/v2" ) @@ -292,7 +292,7 @@ func canNodeDeposits(c *cli.Command, count uint64, amountWei *big.Int, minNodeFe } -func nodeDeposits(c *cli.Command, count uint64, amountWei *big.Int, minNodeFee float64, salt *big.Int, useCreditBalance bool, expressTicketsRequested int64, submit bool) (*api.NodeDepositsResponse, error) { +func nodeDeposits(c *cli.Command, count uint64, amountWei *big.Int, minNodeFee float64, salt *big.Int, useCreditBalance bool, expressTicketsRequested int64, submit bool, opts *bind.TransactOpts) (*api.NodeDepositsResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -369,12 +369,6 @@ func nodeDeposits(c *cli.Command, count uint64, amountWei *big.Int, minNodeFee f return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - // Set the value to the total amount needed// Get how much credit to use if useCreditBalance { remainingAmount := big.NewInt(0).Sub(amountWei, creditBalanceWei) @@ -474,12 +468,6 @@ func nodeDeposits(c *cli.Command, count uint64, amountWei *big.Int, minNodeFee f expressTicketsRequested-- } - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Do not send transaction unless requested opts.NoSend = !submit diff --git a/rocketpool/api/node/distributor.go b/rocketpool/api/node/distributor.go index dfb7e7f6c..0063db488 100644 --- a/rocketpool/api/node/distributor.go +++ b/rocketpool/api/node/distributor.go @@ -4,6 +4,8 @@ import ( "context" "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/urfave/cli/v3" @@ -11,7 +13,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func isFeeDistributorInitialized(c *cli.Command) (*api.NodeIsFeeDistributorInitializedResponse, error) { @@ -96,16 +97,12 @@ func getInitializeFeeDistributorGas(c *cli.Command) (*api.NodeInitializeFeeDistr } -func initializeFeeDistributor(c *cli.Command) (*api.NodeInitializeFeeDistributorResponse, error) { +func initializeFeeDistributor(c *cli.Command, opts *bind.TransactOpts) (*api.NodeInitializeFeeDistributorResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -114,17 +111,6 @@ func initializeFeeDistributor(c *cli.Command) (*api.NodeInitializeFeeDistributor // Response response := api.NodeInitializeFeeDistributorResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Initialize the fee distributor hash, err := node.InitializeFeeDistributor(rp, opts) if err != nil { @@ -215,7 +201,7 @@ func canDistribute(c *cli.Command) (*api.NodeCanDistributeResponse, error) { } -func distribute(c *cli.Command) (*api.NodeDistributeResponse, error) { +func distribute(c *cli.Command, opts *bind.TransactOpts) (*api.NodeDistributeResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { return nil, err @@ -253,18 +239,6 @@ func distribute(c *cli.Command) (*api.NodeDistributeResponse, error) { return nil, err } - // Get gas estimates - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - hash, err := distributor.Distribute(opts) if err != nil { return nil, err diff --git a/rocketpool/api/node/express-ticket.go b/rocketpool/api/node/express-ticket.go index c60d2368e..bcc1a6d2b 100644 --- a/rocketpool/api/node/express-ticket.go +++ b/rocketpool/api/node/express-ticket.go @@ -1,12 +1,11 @@ package node import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -116,7 +115,7 @@ func canProvisionExpressTickets(c *cli.Command) (*api.CanProvisionExpressTickets } -func provisionExpressTickets(c *cli.Command) (*api.ProvisionExpressTicketsResponse, error) { +func provisionExpressTickets(c *cli.Command, opts *bind.TransactOpts) (*api.ProvisionExpressTicketsResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -143,18 +142,6 @@ func provisionExpressTickets(c *cli.Command) (*api.ProvisionExpressTicketsRespon // Response response := api.ProvisionExpressTicketsResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("error checking for nonce override: %w", err) - } - // Provision express tickets hash, err := node.ProvisionExpressTickets(rp, nodeAccount.Address, opts) if err != nil { diff --git a/rocketpool/api/node/primary-withdrawal-address.go b/rocketpool/api/node/primary-withdrawal-address.go index 27e67e12a..046f0a20b 100644 --- a/rocketpool/api/node/primary-withdrawal-address.go +++ b/rocketpool/api/node/primary-withdrawal-address.go @@ -3,13 +3,13 @@ package node import ( "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/storage" "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canSetPrimaryWithdrawalAddress(c *cli.Command, withdrawalAddress common.Address, confirm bool) (*api.CanSetNodePrimaryWithdrawalAddressResponse, error) { @@ -54,7 +54,7 @@ func canSetPrimaryWithdrawalAddress(c *cli.Command, withdrawalAddress common.Add return &response, nil } -func setPrimaryWithdrawalAddress(c *cli.Command, withdrawalAddress common.Address, confirm bool) (*api.SetNodePrimaryWithdrawalAddressResponse, error) { +func setPrimaryWithdrawalAddress(c *cli.Command, withdrawalAddress common.Address, confirm bool, opts *bind.TransactOpts) (*api.SetNodePrimaryWithdrawalAddressResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -72,18 +72,6 @@ func setPrimaryWithdrawalAddress(c *cli.Command, withdrawalAddress common.Addres // Response response := api.SetNodePrimaryWithdrawalAddressResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Get the node's account nodeAccount, err := w.GetNodeAccount() if err != nil { @@ -160,7 +148,7 @@ func canConfirmPrimaryWithdrawalAddress(c *cli.Command) (*api.CanConfirmNodePrim return &response, nil } -func confirmPrimaryWithdrawalAddress(c *cli.Command) (*api.ConfirmNodePrimaryWithdrawalAddressResponse, error) { +func confirmPrimaryWithdrawalAddress(c *cli.Command, opts *bind.TransactOpts) (*api.ConfirmNodePrimaryWithdrawalAddressResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -178,18 +166,6 @@ func confirmPrimaryWithdrawalAddress(c *cli.Command) (*api.ConfirmNodePrimaryWit // Response response := api.ConfirmNodePrimaryWithdrawalAddressResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Get the node's account nodeAccount, err := w.GetNodeAccount() if err != nil { diff --git a/rocketpool/api/node/register.go b/rocketpool/api/node/register.go index ad24bfd0e..443a54e1c 100644 --- a/rocketpool/api/node/register.go +++ b/rocketpool/api/node/register.go @@ -1,7 +1,7 @@ package node import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/bindings/settings/protocol" @@ -10,7 +10,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canRegisterNode(c *cli.Command, timezoneLocation string) (*api.CanRegisterNodeResponse, error) { @@ -84,7 +83,7 @@ func canRegisterNode(c *cli.Command, timezoneLocation string) (*api.CanRegisterN } -func registerNode(c *cli.Command, timezoneLocation string) (*api.RegisterNodeResponse, error) { +func registerNode(c *cli.Command, timezoneLocation string, opts *bind.TransactOpts) (*api.RegisterNodeResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -93,10 +92,6 @@ func registerNode(c *cli.Command, timezoneLocation string) (*api.RegisterNodeRes if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -105,18 +100,6 @@ func registerNode(c *cli.Command, timezoneLocation string) (*api.RegisterNodeRes // Response response := api.RegisterNodeResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Register node hash, err := node.RegisterNode(rp, timezoneLocation, opts) if err != nil { diff --git a/rocketpool/api/node/routes.go b/rocketpool/api/node/routes.go new file mode 100644 index 000000000..b63af6851 --- /dev/null +++ b/rocketpool/api/node/routes.go @@ -0,0 +1,997 @@ +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/v3" + + "github.com/rocket-pool/smartnode/shared/services" + 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.Command) { + 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") + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := registerNode(c, tz, opts) + 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") + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := setTimezoneLocation(c, tz, opts) + 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" + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := setPrimaryWithdrawalAddress(c, addr, confirm, opts) + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := confirmPrimaryWithdrawalAddress(c, opts) + 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" + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := setRPLWithdrawalAddress(c, addr, confirm, opts) + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := confirmRPLWithdrawalAddress(c, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := approveFsRpl(c, amountWei, opts) + 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")) + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := waitForApprovalAndSwapFsRpl(c, amountWei, hash, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := swapRpl(c, amountWei, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := approveRpl(c, amountWei, opts) + 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")) + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := waitForApprovalAndStakeRpl(c, amountWei, hash, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := stakeRpl(c, amountWei, opts) + 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" + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := setRplLockAllowed(c, allowed, opts) + 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" + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := setStakeRplForAllowed(c, caller, allowed, opts) + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeWithdrawRpl(c, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeUnstakeLegacyRpl(c, amountWei, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeWithdrawRplv1_3_1(c, amountWei, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeUnstakeRpl(c, amountWei, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeWithdrawEth(c, amountWei, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeWithdrawCredit(c, amountWei, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + 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, opts) + 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")) + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeSend(c, amountRaw, token, to, opts) + 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")) + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeSendAllTokens(c, token, to, opts) + 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") + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeBurn(c, amountWei, token, opts) + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := nodeClaimRpl(c, opts) + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := initializeFeeDistributor(c, opts) + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := distribute(c, opts) + 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") + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := claimRewards(c, indices, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := claimAndStakeRewards(c, indices, stakeAmount, opts) + 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" + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := setSmoothingPoolStatus(c, status, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := createVacantMinipool(c, params.amountWei, params.minFee, params.salt, params.pubkey, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := sendMessage(c, addr, msgBytes, opts) + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := provisionExpressTickets(c, opts) + 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")) + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := claimUnclaimedRewards(c, nodeAddr, opts) + 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/node/rpl-withdrawal-address.go b/rocketpool/api/node/rpl-withdrawal-address.go index e42a28b55..1a91a99aa 100644 --- a/rocketpool/api/node/rpl-withdrawal-address.go +++ b/rocketpool/api/node/rpl-withdrawal-address.go @@ -1,9 +1,9 @@ package node import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/bindings/storage" @@ -12,7 +12,6 @@ import ( "golang.org/x/sync/errgroup" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canSetRPLWithdrawalAddress(c *cli.Command, withdrawalAddress common.Address, confirm bool) (*api.CanSetNodeRPLWithdrawalAddressResponse, error) { @@ -103,7 +102,7 @@ func canSetRPLWithdrawalAddress(c *cli.Command, withdrawalAddress common.Address return &response, nil } -func setRPLWithdrawalAddress(c *cli.Command, withdrawalAddress common.Address, confirm bool) (*api.SetNodeRPLWithdrawalAddressResponse, error) { +func setRPLWithdrawalAddress(c *cli.Command, withdrawalAddress common.Address, confirm bool, opts *bind.TransactOpts) (*api.SetNodeRPLWithdrawalAddressResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err @@ -120,18 +119,6 @@ func setRPLWithdrawalAddress(c *cli.Command, withdrawalAddress common.Address, c // Response response := api.SetNodeRPLWithdrawalAddressResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Get the node's account nodeAccount, err := w.GetNodeAccount() if err != nil { @@ -200,7 +187,7 @@ func canConfirmRPLWithdrawalAddress(c *cli.Command) (*api.CanConfirmNodeRPLWithd return &response, nil } -func confirmRPLWithdrawalAddress(c *cli.Command) (*api.ConfirmNodeRPLWithdrawalAddressResponse, error) { +func confirmRPLWithdrawalAddress(c *cli.Command, opts *bind.TransactOpts) (*api.ConfirmNodeRPLWithdrawalAddressResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err @@ -217,18 +204,6 @@ func confirmRPLWithdrawalAddress(c *cli.Command) (*api.ConfirmNodeRPLWithdrawalA // Response response := api.ConfirmNodeRPLWithdrawalAddressResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Get the node's account nodeAccount, err := w.GetNodeAccount() if err != nil { diff --git a/rocketpool/api/node/send-message.go b/rocketpool/api/node/send-message.go index 3d7a87da7..5d114e834 100644 --- a/rocketpool/api/node/send-message.go +++ b/rocketpool/api/node/send-message.go @@ -5,10 +5,10 @@ import ( "github.com/rocket-pool/smartnode/bindings/utils/eth" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -47,7 +47,7 @@ func canSendMessage(c *cli.Command, address common.Address, message []byte) (*ap } -func sendMessage(c *cli.Command, address common.Address, message []byte) (*api.NodeSendMessageResponse, error) { +func sendMessage(c *cli.Command, address common.Address, message []byte, opts *bind.TransactOpts) (*api.NodeSendMessageResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -65,18 +65,6 @@ func sendMessage(c *cli.Command, address common.Address, message []byte) (*api.N // Response response := api.NodeSendMessageResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Send the message hash, err := eth.SendTransaction(ec, address, w.GetChainID(), message, true, opts) if err != nil { diff --git a/rocketpool/api/node/send.go b/rocketpool/api/node/send.go index 24bea4a6e..663481176 100644 --- a/rocketpool/api/node/send.go +++ b/rocketpool/api/node/send.go @@ -7,6 +7,7 @@ import ( "math/big" "strings" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/tokens" "github.com/rocket-pool/smartnode/bindings/utils/eth" @@ -14,7 +15,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canNodeSend(c *cli.Command, amountRaw float64, token string, to common.Address) (*api.CanNodeSendResponse, error) { @@ -203,7 +203,7 @@ func canNodeSend(c *cli.Command, amountRaw float64, token string, to common.Addr } -func nodeSend(c *cli.Command, amountRaw float64, token string, to common.Address) (*api.NodeSendResponse, error) { +func nodeSend(c *cli.Command, amountRaw float64, token string, to common.Address, opts *bind.TransactOpts) (*api.NodeSendResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -225,18 +225,6 @@ func nodeSend(c *cli.Command, amountRaw float64, token string, to common.Address // Response response := api.NodeSendResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Handle explicit token addresses if strings.HasPrefix(token, "0x") { tokenAddress := common.HexToAddress(token) @@ -317,7 +305,7 @@ func nodeSend(c *cli.Command, amountRaw float64, token string, to common.Address // the recipient, using the exact *big.Int balance to avoid float64 rounding // errors that would cause "transfer amount exceeds balance" failures. // ETH is not supported here; use nodeSend with a pre-computed amount instead. -func nodeSendAllTokens(c *cli.Command, token string, to common.Address) (*api.NodeSendResponse, error) { +func nodeSendAllTokens(c *cli.Command, token string, to common.Address, opts *bind.TransactOpts) (*api.NodeSendResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -345,18 +333,6 @@ func nodeSendAllTokens(c *cli.Command, token string, to common.Address) (*api.No return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Handle explicit token addresses if strings.HasPrefix(token, "0x") { tokenAddress := common.HexToAddress(token) diff --git a/rocketpool/api/node/set-rpl-lock-allowed.go b/rocketpool/api/node/set-rpl-lock-allowed.go index 750bcdffc..35947dde8 100644 --- a/rocketpool/api/node/set-rpl-lock-allowed.go +++ b/rocketpool/api/node/set-rpl-lock-allowed.go @@ -1,14 +1,13 @@ package node import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/node" "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canSetRplLockAllowed(c *cli.Command, allowed bool) (*api.CanSetRplLockingAllowedResponse, error) { @@ -63,7 +62,7 @@ func canSetRplLockAllowed(c *cli.Command, allowed bool) (*api.CanSetRplLockingAl } -func setRplLockAllowed(c *cli.Command, allowed bool) (*api.SetRplLockingAllowedResponse, error) { +func setRplLockAllowed(c *cli.Command, allowed bool, opts *bind.TransactOpts) (*api.SetRplLockingAllowedResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -87,14 +86,6 @@ func setRplLockAllowed(c *cli.Command, allowed bool) (*api.SetRplLockingAllowedR response := api.SetRplLockingAllowedResponse{} // Stake RPL - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } hash, err := node.SetRPLLockingAllowed(rp, account.Address, allowed, opts) if err != nil { return nil, err diff --git a/rocketpool/api/node/set-stake-rpl-for-allowed.go b/rocketpool/api/node/set-stake-rpl-for-allowed.go index 562537a6b..ddf6fdb62 100644 --- a/rocketpool/api/node/set-stake-rpl-for-allowed.go +++ b/rocketpool/api/node/set-stake-rpl-for-allowed.go @@ -1,15 +1,13 @@ package node import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/node" "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canSetStakeRplForAllowed(c *cli.Command, caller common.Address, allowed bool) (*api.CanSetStakeRplForAllowedResponse, error) { @@ -47,16 +45,12 @@ func canSetStakeRplForAllowed(c *cli.Command, caller common.Address, allowed boo } -func setStakeRplForAllowed(c *cli.Command, caller common.Address, allowed bool) (*api.SetStakeRplForAllowedResponse, error) { +func setStakeRplForAllowed(c *cli.Command, caller common.Address, allowed bool, opts *bind.TransactOpts) (*api.SetStakeRplForAllowedResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -66,14 +60,6 @@ func setStakeRplForAllowed(c *cli.Command, caller common.Address, allowed bool) response := api.SetStakeRplForAllowedResponse{} // Stake RPL - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } hash, err := node.SetStakeRPLForAllowed(rp, caller, allowed, opts) if err != nil { return nil, err diff --git a/rocketpool/api/node/set-timezone.go b/rocketpool/api/node/set-timezone.go index 4ac633150..9a2a95105 100644 --- a/rocketpool/api/node/set-timezone.go +++ b/rocketpool/api/node/set-timezone.go @@ -1,15 +1,15 @@ package node import ( - "fmt" _ "time/tzdata" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canSetTimezoneLocation(c *cli.Command, timezoneLocation string) (*api.CanSetNodeTimezoneResponse, error) { @@ -45,16 +45,12 @@ func canSetTimezoneLocation(c *cli.Command, timezoneLocation string) (*api.CanSe } -func setTimezoneLocation(c *cli.Command, timezoneLocation string) (*api.SetNodeTimezoneResponse, error) { +func setTimezoneLocation(c *cli.Command, timezoneLocation string, opts *bind.TransactOpts) (*api.SetNodeTimezoneResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -63,18 +59,6 @@ func setTimezoneLocation(c *cli.Command, timezoneLocation string) (*api.SetNodeT // Response response := api.SetNodeTimezoneResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Set timezone location hash, err := node.SetTimezoneLocation(rp, timezoneLocation, opts) if err != nil { diff --git a/rocketpool/api/node/smoothing-pool.go b/rocketpool/api/node/smoothing-pool.go index c06e34ce7..a1bc9d6b7 100644 --- a/rocketpool/api/node/smoothing-pool.go +++ b/rocketpool/api/node/smoothing-pool.go @@ -4,13 +4,14 @@ import ( "context" "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/bindings/rewards" rocketpoolapi "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/rocket-pool/smartnode/shared/utils/validator" "github.com/urfave/cli/v3" ) @@ -112,7 +113,7 @@ func canSetSmoothingPoolStatus(c *cli.Command, status bool) (*api.CanSetSmoothin } -func setSmoothingPoolStatus(c *cli.Command, status bool) (*api.SetSmoothingPoolRegistrationStatusResponse, error) { +func setSmoothingPoolStatus(c *cli.Command, status bool, opts *bind.TransactOpts) (*api.SetSmoothingPoolRegistrationStatusResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -145,18 +146,6 @@ func setSmoothingPoolStatus(c *cli.Command, status bool) (*api.SetSmoothingPoolR // Response response := api.SetSmoothingPoolRegistrationStatusResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Get node account and distributor address nodeAccount, err := w.GetNodeAccount() if err != nil { diff --git a/rocketpool/api/node/stake-rpl.go b/rocketpool/api/node/stake-rpl.go index 35928b784..6ed4fc8a9 100644 --- a/rocketpool/api/node/stake-rpl.go +++ b/rocketpool/api/node/stake-rpl.go @@ -1,9 +1,9 @@ package node import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/node" @@ -13,7 +13,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canNodeStakeRpl(c *cli.Command, amountWei *big.Int) (*api.CanNodeStakeRplResponse, error) { @@ -144,16 +143,12 @@ func allowanceRpl(c *cli.Command) (*api.NodeStakeRplAllowanceResponse, error) { return &response, nil } -func approveRpl(c *cli.Command, amountWei *big.Int) (*api.NodeStakeRplApproveResponse, error) { +func approveRpl(c *cli.Command, amountWei *big.Int, opts *bind.TransactOpts) (*api.NodeStakeRplApproveResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -169,14 +164,6 @@ func approveRpl(c *cli.Command, amountWei *big.Int) (*api.NodeStakeRplApproveRes } // Approve RPL allowance - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } hash, err := tokens.ApproveRPL(rp, *rocketNodeStakingAddress, amountWei, opts) if err != nil { return nil, err @@ -189,7 +176,7 @@ func approveRpl(c *cli.Command, amountWei *big.Int) (*api.NodeStakeRplApproveRes } -func waitForApprovalAndStakeRpl(c *cli.Command, amountWei *big.Int, hash common.Hash) (*api.NodeStakeRplStakeResponse, error) { +func waitForApprovalAndStakeRpl(c *cli.Command, amountWei *big.Int, hash common.Hash, opts *bind.TransactOpts) (*api.NodeStakeRplStakeResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -207,20 +194,16 @@ func waitForApprovalAndStakeRpl(c *cli.Command, amountWei *big.Int, hash common. } // Perform the stake - return stakeRpl(c, amountWei) + return stakeRpl(c, amountWei, opts) } -func stakeRpl(c *cli.Command, amountWei *big.Int) (*api.NodeStakeRplStakeResponse, error) { +func stakeRpl(c *cli.Command, amountWei *big.Int, opts *bind.TransactOpts) (*api.NodeStakeRplStakeResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -230,14 +213,6 @@ func stakeRpl(c *cli.Command, amountWei *big.Int) (*api.NodeStakeRplStakeRespons response := api.NodeStakeRplStakeResponse{} // Stake RPL - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } hash, err := node.StakeRPL(rp, amountWei, opts) if err != nil { return nil, err diff --git a/rocketpool/api/node/swap-rpl.go b/rocketpool/api/node/swap-rpl.go index 03d5b42a5..74e2d2dbb 100644 --- a/rocketpool/api/node/swap-rpl.go +++ b/rocketpool/api/node/swap-rpl.go @@ -1,9 +1,9 @@ package node import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/tokens" "github.com/rocket-pool/smartnode/bindings/utils" @@ -11,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canNodeSwapRpl(c *cli.Command, amountWei *big.Int) (*api.CanNodeSwapRplResponse, error) { @@ -145,7 +144,7 @@ func getSwapApprovalGas(c *cli.Command, amountWei *big.Int) (*api.NodeSwapRplApp return &response, nil } -func approveFsRpl(c *cli.Command, amountWei *big.Int) (*api.NodeSwapRplApproveResponse, error) { +func approveFsRpl(c *cli.Command, amountWei *big.Int, opts *bind.TransactOpts) (*api.NodeSwapRplApproveResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -154,10 +153,6 @@ func approveFsRpl(c *cli.Command, amountWei *big.Int) (*api.NodeSwapRplApproveRe if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -173,14 +168,6 @@ func approveFsRpl(c *cli.Command, amountWei *big.Int) (*api.NodeSwapRplApproveRe } // Approve fixed-supply RPL allowance - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } if hash, err := tokens.ApproveFixedSupplyRPL(rp, *rocketTokenRPLAddress, amountWei, opts); err != nil { return nil, err } else { @@ -192,7 +179,7 @@ func approveFsRpl(c *cli.Command, amountWei *big.Int) (*api.NodeSwapRplApproveRe } -func waitForApprovalAndSwapFsRpl(c *cli.Command, amountWei *big.Int, hash common.Hash) (*api.NodeSwapRplSwapResponse, error) { +func waitForApprovalAndSwapFsRpl(c *cli.Command, amountWei *big.Int, hash common.Hash, opts *bind.TransactOpts) (*api.NodeSwapRplSwapResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -212,20 +199,16 @@ func waitForApprovalAndSwapFsRpl(c *cli.Command, amountWei *big.Int, hash common return nil, err } - return swapRpl(c, amountWei) + return swapRpl(c, amountWei, opts) } -func swapRpl(c *cli.Command, amountWei *big.Int) (*api.NodeSwapRplSwapResponse, error) { +func swapRpl(c *cli.Command, amountWei *big.Int, opts *bind.TransactOpts) (*api.NodeSwapRplSwapResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -235,14 +218,6 @@ func swapRpl(c *cli.Command, amountWei *big.Int) (*api.NodeSwapRplSwapResponse, response := api.NodeSwapRplSwapResponse{} // Swap fixed-supply RPL for RPL - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } if hash, err := tokens.SwapFixedSupplyRPLForRPL(rp, amountWei, opts); err != nil { return nil, err } else { diff --git a/rocketpool/api/node/unstake-rpl.go b/rocketpool/api/node/unstake-rpl.go index a5459bae6..4a2d0d542 100644 --- a/rocketpool/api/node/unstake-rpl.go +++ b/rocketpool/api/node/unstake-rpl.go @@ -1,9 +1,9 @@ package node import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/node" "github.com/urfave/cli/v3" @@ -11,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canNodeUnstakeRpl(c *cli.Command, amountWei *big.Int) (*api.CanNodeUnstakeRplResponse, error) { @@ -104,16 +103,12 @@ func canNodeUnstakeRpl(c *cli.Command, amountWei *big.Int) (*api.CanNodeUnstakeR } -func nodeUnstakeRpl(c *cli.Command, amountWei *big.Int) (*api.NodeUnstakeRplResponse, error) { +func nodeUnstakeRpl(c *cli.Command, amountWei *big.Int, opts *bind.TransactOpts) (*api.NodeUnstakeRplResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -122,17 +117,6 @@ func nodeUnstakeRpl(c *cli.Command, amountWei *big.Int) (*api.NodeUnstakeRplResp // Response response := api.NodeUnstakeRplResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } var hash common.Hash // Withdraw RPL hash, err = node.UnstakeRPL(rp, amountWei, opts) diff --git a/rocketpool/api/node/withdraw-credit.go b/rocketpool/api/node/withdraw-credit.go index 2808a3ed9..42c07e83b 100644 --- a/rocketpool/api/node/withdraw-credit.go +++ b/rocketpool/api/node/withdraw-credit.go @@ -1,16 +1,16 @@ package node import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/urfave/cli/v3" "golang.org/x/sync/errgroup" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canNodeWithdrawCredit(c *cli.Command, amountWei *big.Int) (*api.CanNodeWithdrawCreditResponse, error) { @@ -74,16 +74,12 @@ func canNodeWithdrawCredit(c *cli.Command, amountWei *big.Int) (*api.CanNodeWith } -func nodeWithdrawCredit(c *cli.Command, amountWei *big.Int) (*api.NodeWithdrawCreditResponse, error) { +func nodeWithdrawCredit(c *cli.Command, amountWei *big.Int, opts *bind.TransactOpts) (*api.NodeWithdrawCreditResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -92,18 +88,6 @@ func nodeWithdrawCredit(c *cli.Command, amountWei *big.Int) (*api.NodeWithdrawCr // Response response := api.NodeWithdrawCreditResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Withdraw credit tx, err := node.WithdrawCredit(rp, amountWei, opts) if err != nil { diff --git a/rocketpool/api/node/withdraw-eth.go b/rocketpool/api/node/withdraw-eth.go index ebe0ab628..f9dfd5f3a 100644 --- a/rocketpool/api/node/withdraw-eth.go +++ b/rocketpool/api/node/withdraw-eth.go @@ -1,16 +1,16 @@ package node import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/node" "github.com/urfave/cli/v3" "golang.org/x/sync/errgroup" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canNodeWithdrawEth(c *cli.Command, amountWei *big.Int) (*api.CanNodeWithdrawEthResponse, error) { @@ -84,7 +84,7 @@ func canNodeWithdrawEth(c *cli.Command, amountWei *big.Int) (*api.CanNodeWithdra } -func nodeWithdrawEth(c *cli.Command, amountWei *big.Int) (*api.NodeWithdrawRplResponse, error) { +func nodeWithdrawEth(c *cli.Command, amountWei *big.Int, opts *bind.TransactOpts) (*api.NodeWithdrawRplResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -102,24 +102,12 @@ func nodeWithdrawEth(c *cli.Command, amountWei *big.Int) (*api.NodeWithdrawRplRe // Response response := api.NodeWithdrawRplResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - // Get node account nodeAccount, err := w.GetNodeAccount() if err != nil { return nil, err } - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Withdraw ETH tx, err := node.WithdrawEth(rp, nodeAccount.Address, amountWei, opts) if err != nil { diff --git a/rocketpool/api/node/withdraw-legacy-rpl.go b/rocketpool/api/node/withdraw-legacy-rpl.go index dfcd519ce..d26d75316 100644 --- a/rocketpool/api/node/withdraw-legacy-rpl.go +++ b/rocketpool/api/node/withdraw-legacy-rpl.go @@ -1,9 +1,9 @@ package node import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/node" "github.com/urfave/cli/v3" @@ -11,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canNodeUnstakeLegacyRpl(c *cli.Command, amountWei *big.Int) (*api.CanNodeUnstakeLegacyRplResponse, error) { @@ -113,16 +112,12 @@ func canNodeUnstakeLegacyRpl(c *cli.Command, amountWei *big.Int) (*api.CanNodeUn } -func nodeUnstakeLegacyRpl(c *cli.Command, amountWei *big.Int) (*api.NodeUnstakeLegacyRplResponse, error) { +func nodeUnstakeLegacyRpl(c *cli.Command, amountWei *big.Int, opts *bind.TransactOpts) (*api.NodeUnstakeLegacyRplResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -131,17 +126,6 @@ func nodeUnstakeLegacyRpl(c *cli.Command, amountWei *big.Int) (*api.NodeUnstakeL // Response response := api.NodeUnstakeLegacyRplResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } var hash common.Hash // Unstake legacy RPL hash, err = node.UnstakeLegacyRPL(rp, amountWei, opts) diff --git a/rocketpool/api/node/withdraw-rpl.go b/rocketpool/api/node/withdraw-rpl.go index 0c2b666c5..3bd444c07 100644 --- a/rocketpool/api/node/withdraw-rpl.go +++ b/rocketpool/api/node/withdraw-rpl.go @@ -2,10 +2,10 @@ package node import ( "context" - "fmt" "math/big" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" node131 "github.com/rocket-pool/smartnode/bindings/legacy/v1.3.1/node" "github.com/rocket-pool/smartnode/bindings/node" @@ -15,7 +15,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canNodeWithdrawRpl(c *cli.Command) (*api.CanNodeWithdrawRplResponse, error) { @@ -129,16 +128,12 @@ func canNodeWithdrawRpl(c *cli.Command) (*api.CanNodeWithdrawRplResponse, error) } -func nodeWithdrawRpl(c *cli.Command) (*api.NodeWithdrawRplResponse, error) { +func nodeWithdrawRpl(c *cli.Command, opts *bind.TransactOpts) (*api.NodeWithdrawRplResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -147,17 +142,6 @@ func nodeWithdrawRpl(c *cli.Command) (*api.NodeWithdrawRplResponse, error) { // Response response := api.NodeWithdrawRplResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } var hash common.Hash // Withdraw RPL hash, err = node.WithdrawRPL(rp, opts) @@ -310,7 +294,7 @@ func canNodeWithdrawRplv1_3_1(c *cli.Command, amountWei *big.Int) (*api.CanNodeW } // Used if saturn is not deployed (v1.3.1) -func nodeWithdrawRplv1_3_1(c *cli.Command, amountWei *big.Int) (*api.NodeWithdrawRplResponse, error) { +func nodeWithdrawRplv1_3_1(c *cli.Command, amountWei *big.Int, opts *bind.TransactOpts) (*api.NodeWithdrawRplResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { @@ -334,17 +318,6 @@ func nodeWithdrawRplv1_3_1(c *cli.Command, amountWei *big.Int) (*api.NodeWithdra // Response response := api.NodeWithdrawRplResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } var hash common.Hash // Withdraw RPL hash, err = node131.WithdrawRPL(rp, nodeAccount.Address, amountWei, opts) diff --git a/rocketpool/api/odao/cancel-proposal.go b/rocketpool/api/odao/cancel-proposal.go index f57513e6c..9976f126c 100644 --- a/rocketpool/api/odao/cancel-proposal.go +++ b/rocketpool/api/odao/cancel-proposal.go @@ -2,7 +2,8 @@ package odao import ( "bytes" - "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/dao" "github.com/rocket-pool/smartnode/bindings/dao/trustednode" @@ -12,7 +13,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canCancelProposal(c *cli.Command, proposalId uint64) (*api.CanCancelTNDAOProposalResponse, error) { @@ -91,16 +91,12 @@ func canCancelProposal(c *cli.Command, proposalId uint64) (*api.CanCancelTNDAOPr } -func cancelProposal(c *cli.Command, proposalId uint64) (*api.CancelTNDAOProposalResponse, error) { +func cancelProposal(c *cli.Command, proposalId uint64, opts *bind.TransactOpts) (*api.CancelTNDAOProposalResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -109,18 +105,6 @@ func cancelProposal(c *cli.Command, proposalId uint64) (*api.CancelTNDAOProposal // Response response := api.CancelTNDAOProposalResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Cancel proposal hash, err := trustednode.CancelProposal(rp, proposalId, opts) if err != nil { diff --git a/rocketpool/api/odao/commands.go b/rocketpool/api/odao/commands.go deleted file mode 100644 index 92a35723f..000000000 --- a/rocketpool/api/odao/commands.go +++ /dev/null @@ -1,1140 +0,0 @@ -package odao - -import ( - "context" - - "github.com/urfave/cli/v3" - - "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.Commands = append(command.Commands, &cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the Rocket Pool oracle DAO", - Commands: []*cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get oracle DAO status", - UsageText: "rocketpool api odao status", - Action: func(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) error { - - // Run - api.PrintResponse(getMinipoolSettings(c)) - return nil - - }, - }, - }, - }) -} diff --git a/rocketpool/api/odao/execute-proposal.go b/rocketpool/api/odao/execute-proposal.go index db1278f81..14ac9d759 100644 --- a/rocketpool/api/odao/execute-proposal.go +++ b/rocketpool/api/odao/execute-proposal.go @@ -1,7 +1,7 @@ package odao import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/dao" "github.com/rocket-pool/smartnode/bindings/dao/trustednode" @@ -11,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canExecuteProposal(c *cli.Command, proposalId uint64) (*api.CanExecuteTNDAOProposalResponse, error) { @@ -20,13 +19,13 @@ func canExecuteProposal(c *cli.Command, proposalId uint64) (*api.CanExecuteTNDAO if err := services.RequireNodeWallet(c); err != nil { return nil, err } - if err := services.RequireRocketStorage(c); err != nil { - return nil, err - } w, err := services.GetWallet(c) if err != nil { return nil, err } + if err := services.RequireRocketStorage(c); err != nil { + return nil, err + } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -80,7 +79,7 @@ func canExecuteProposal(c *cli.Command, proposalId uint64) (*api.CanExecuteTNDAO } -func executeProposal(c *cli.Command, proposalId uint64) (*api.ExecuteTNDAOProposalResponse, error) { +func executeProposal(c *cli.Command, proposalId uint64, opts *bind.TransactOpts) (*api.ExecuteTNDAOProposalResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -89,10 +88,6 @@ func executeProposal(c *cli.Command, proposalId uint64) (*api.ExecuteTNDAOPropos if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -101,18 +96,6 @@ func executeProposal(c *cli.Command, proposalId uint64) (*api.ExecuteTNDAOPropos // Response response := api.ExecuteTNDAOProposalResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Cancel proposal hash, err := trustednode.ExecuteProposal(rp, proposalId, opts) if err != nil { diff --git a/rocketpool/api/odao/join.go b/rocketpool/api/odao/join.go index 996870fb9..f5ce226c9 100644 --- a/rocketpool/api/odao/join.go +++ b/rocketpool/api/odao/join.go @@ -1,9 +1,9 @@ package odao import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" tndao "github.com/rocket-pool/smartnode/bindings/dao/trustednode" tnsettings "github.com/rocket-pool/smartnode/bindings/settings/trustednode" @@ -14,7 +14,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canJoin(c *cli.Command) (*api.CanJoinTNDAOResponse, error) { @@ -118,16 +117,12 @@ func canJoin(c *cli.Command) (*api.CanJoinTNDAOResponse, error) { } -func approveRpl(c *cli.Command) (*api.JoinTNDAOApproveResponse, error) { +func approveRpl(c *cli.Command, opts *bind.TransactOpts) (*api.JoinTNDAOApproveResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -161,14 +156,6 @@ func approveRpl(c *cli.Command) (*api.JoinTNDAOApproveResponse, error) { } // Approve RPL allowance - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } hash, err := tokens.ApproveRPL(rp, *rocketDAONodeTrustedActionsAddress, rplBondAmount, opts) if err != nil { return nil, err @@ -181,16 +168,12 @@ func approveRpl(c *cli.Command) (*api.JoinTNDAOApproveResponse, error) { } -func waitForApprovalAndJoin(c *cli.Command, hash common.Hash) (*api.JoinTNDAOJoinResponse, error) { +func waitForApprovalAndJoin(c *cli.Command, hash common.Hash, opts *bind.TransactOpts) (*api.JoinTNDAOJoinResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -206,14 +189,6 @@ func waitForApprovalAndJoin(c *cli.Command, hash common.Hash) (*api.JoinTNDAOJoi response := api.JoinTNDAOJoinResponse{} // Join - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } joinHash, err := tndao.Join(rp, opts) if err != nil { return nil, err diff --git a/rocketpool/api/odao/leave.go b/rocketpool/api/odao/leave.go index 7b43a319b..868015609 100644 --- a/rocketpool/api/odao/leave.go +++ b/rocketpool/api/odao/leave.go @@ -1,8 +1,7 @@ package odao import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/dao/trustednode" "github.com/urfave/cli/v3" @@ -10,7 +9,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canLeave(c *cli.Command) (*api.CanLeaveTNDAOResponse, error) { @@ -80,16 +78,12 @@ func canLeave(c *cli.Command) (*api.CanLeaveTNDAOResponse, error) { } -func leave(c *cli.Command, bondRefundAddress common.Address) (*api.LeaveTNDAOResponse, error) { +func leave(c *cli.Command, bondRefundAddress common.Address, opts *bind.TransactOpts) (*api.LeaveTNDAOResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -98,18 +92,6 @@ func leave(c *cli.Command, bondRefundAddress common.Address) (*api.LeaveTNDAORes // Response response := api.LeaveTNDAOResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Leave hash, err := trustednode.Leave(rp, bondRefundAddress, opts) if err != nil { diff --git a/rocketpool/api/odao/penalise-megapool.go b/rocketpool/api/odao/penalise-megapool.go index df02e3604..a066cf56f 100644 --- a/rocketpool/api/odao/penalise-megapool.go +++ b/rocketpool/api/odao/penalise-megapool.go @@ -1,16 +1,15 @@ package odao import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/megapool" "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canPenaliseMegapool(c *cli.Command, megapoolAddress common.Address, block *big.Int, amount *big.Int) (*api.CanPenaliseMegapoolResponse, error) { @@ -46,6 +45,7 @@ func canPenaliseMegapool(c *cli.Command, megapoolAddress common.Address, block * if err != nil { return nil, err } + gasInfo, err := megapool.EstimatePenaliseGas(rp, megapoolAddress, block, amount, opts) if err != nil { return nil, err @@ -57,16 +57,12 @@ func canPenaliseMegapool(c *cli.Command, megapoolAddress common.Address, block * } -func penaliseMegapool(c *cli.Command, megapoolAddress common.Address, block *big.Int, amount *big.Int) (*api.PenaliseMegapoolResponse, error) { +func penaliseMegapool(c *cli.Command, megapoolAddress common.Address, block *big.Int, amount *big.Int, opts *bind.TransactOpts) (*api.PenaliseMegapoolResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -75,18 +71,6 @@ func penaliseMegapool(c *cli.Command, megapoolAddress common.Address, block *big // Response response := api.PenaliseMegapoolResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Repay debt hash, err := megapool.Penalise(rp, megapoolAddress, block, amount, opts) if err != nil { diff --git a/rocketpool/api/odao/propose-invite.go b/rocketpool/api/odao/propose-invite.go index 398afae5d..e1933f4ff 100644 --- a/rocketpool/api/odao/propose-invite.go +++ b/rocketpool/api/odao/propose-invite.go @@ -3,6 +3,7 @@ package odao import ( "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/dao/trustednode" "github.com/urfave/cli/v3" @@ -10,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canProposeInvite(c *cli.Command, memberAddress common.Address, memberId, memberUrl string) (*api.CanProposeTNDAOInviteResponse, error) { @@ -81,16 +81,12 @@ func canProposeInvite(c *cli.Command, memberAddress common.Address, memberId, me } -func proposeInvite(c *cli.Command, memberAddress common.Address, memberId, memberUrl string) (*api.ProposeTNDAOInviteResponse, error) { +func proposeInvite(c *cli.Command, memberAddress common.Address, memberId, memberUrl string, opts *bind.TransactOpts) (*api.ProposeTNDAOInviteResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -99,18 +95,6 @@ func proposeInvite(c *cli.Command, memberAddress common.Address, memberId, membe // Response response := api.ProposeTNDAOInviteResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal message := fmt.Sprintf("invite %s (%s)", memberId, memberUrl) proposalId, hash, err := trustednode.ProposeInviteMember(rp, message, memberAddress, memberId, memberUrl, opts) diff --git a/rocketpool/api/odao/propose-kick.go b/rocketpool/api/odao/propose-kick.go index 73d977d84..56ef6893f 100644 --- a/rocketpool/api/odao/propose-kick.go +++ b/rocketpool/api/odao/propose-kick.go @@ -4,6 +4,7 @@ import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/dao/trustednode" "github.com/rocket-pool/smartnode/bindings/utils/eth" @@ -12,7 +13,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/rocket-pool/smartnode/shared/utils/math" ) @@ -92,16 +92,12 @@ func canProposeKick(c *cli.Command, memberAddress common.Address, fineAmountWei } -func proposeKick(c *cli.Command, memberAddress common.Address, fineAmountWei *big.Int) (*api.ProposeTNDAOKickResponse, error) { +func proposeKick(c *cli.Command, memberAddress common.Address, fineAmountWei *big.Int, opts *bind.TransactOpts) (*api.ProposeTNDAOKickResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -132,18 +128,6 @@ func proposeKick(c *cli.Command, memberAddress common.Address, fineAmountWei *bi return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal message := fmt.Sprintf("kick %s (%s) with %.6f RPL fine", memberId, memberUrl, math.RoundDown(eth.WeiToEth(fineAmountWei), 6)) proposalId, hash, err := trustednode.ProposeKickMember(rp, message, memberAddress, fineAmountWei, opts) diff --git a/rocketpool/api/odao/propose-leave.go b/rocketpool/api/odao/propose-leave.go index 758bc442d..bd9c51d48 100644 --- a/rocketpool/api/odao/propose-leave.go +++ b/rocketpool/api/odao/propose-leave.go @@ -3,13 +3,14 @@ package odao import ( "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/dao/trustednode" "github.com/urfave/cli/v3" "golang.org/x/sync/errgroup" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canProposeLeave(c *cli.Command) (*api.CanProposeTNDAOLeaveResponse, error) { @@ -92,7 +93,7 @@ func canProposeLeave(c *cli.Command) (*api.CanProposeTNDAOLeaveResponse, error) } -func proposeLeave(c *cli.Command) (*api.ProposeTNDAOLeaveResponse, error) { +func proposeLeave(c *cli.Command, opts *bind.TransactOpts) (*api.ProposeTNDAOLeaveResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { @@ -138,18 +139,6 @@ func proposeLeave(c *cli.Command) (*api.ProposeTNDAOLeaveResponse, error) { return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal message := fmt.Sprintf("%s (%s) leaves", nodeMemberId, nodeMemberUrl) proposalId, hash, err := trustednode.ProposeMemberLeave(rp, message, nodeAccount.Address, opts) diff --git a/rocketpool/api/odao/propose-settings.go b/rocketpool/api/odao/propose-settings.go index 9d2c8b808..6a6479318 100644 --- a/rocketpool/api/odao/propose-settings.go +++ b/rocketpool/api/odao/propose-settings.go @@ -1,9 +1,10 @@ package odao import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/bindings/settings/trustednode" "github.com/urfave/cli/v3" @@ -11,7 +12,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/wallet" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canProposeSetting(c *cli.Command, w wallet.Wallet, rp *rocketpool.RocketPool) (*api.CanProposeTNDAOSettingResponse, error) { @@ -63,6 +63,7 @@ func canProposeSettingMembersQuorum(c *cli.Command, quorum float64) (*api.CanPro if err != nil { return nil, err } + gasInfo, err := trustednode.EstimateProposeQuorumGas(rp, quorum, opts) if err != nil { return nil, err @@ -73,16 +74,12 @@ func canProposeSettingMembersQuorum(c *cli.Command, quorum float64) (*api.CanPro } -func proposeSettingMembersQuorum(c *cli.Command, quorum float64) (*api.ProposeTNDAOSettingMembersQuorumResponse, error) { +func proposeSettingMembersQuorum(c *cli.Command, quorum float64, opts *bind.TransactOpts) (*api.ProposeTNDAOSettingMembersQuorumResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -91,18 +88,6 @@ func proposeSettingMembersQuorum(c *cli.Command, quorum float64) (*api.ProposeTN // Response response := api.ProposeTNDAOSettingMembersQuorumResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal proposalId, hash, err := trustednode.ProposeQuorum(rp, quorum, opts) if err != nil { @@ -141,6 +126,7 @@ func canProposeSettingMembersRplBond(c *cli.Command, bondAmountWei *big.Int) (*a if err != nil { return nil, err } + gasInfo, err := trustednode.EstimateProposeRPLBondGas(rp, bondAmountWei, opts) if err != nil { return nil, err @@ -151,16 +137,12 @@ func canProposeSettingMembersRplBond(c *cli.Command, bondAmountWei *big.Int) (*a } -func proposeSettingMembersRplBond(c *cli.Command, bondAmountWei *big.Int) (*api.ProposeTNDAOSettingMembersRplBondResponse, error) { +func proposeSettingMembersRplBond(c *cli.Command, bondAmountWei *big.Int, opts *bind.TransactOpts) (*api.ProposeTNDAOSettingMembersRplBondResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -169,18 +151,6 @@ func proposeSettingMembersRplBond(c *cli.Command, bondAmountWei *big.Int) (*api. // Response response := api.ProposeTNDAOSettingMembersRplBondResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal proposalId, hash, err := trustednode.ProposeRPLBond(rp, bondAmountWei, opts) if err != nil { @@ -219,6 +189,7 @@ func canProposeSettingMinipoolUnbondedMax(c *cli.Command, unbondedMinipoolMax ui if err != nil { return nil, err } + gasInfo, err := trustednode.EstimateProposeMinipoolUnbondedMaxGas(rp, unbondedMinipoolMax, opts) if err != nil { return nil, err @@ -229,16 +200,12 @@ func canProposeSettingMinipoolUnbondedMax(c *cli.Command, unbondedMinipoolMax ui } -func proposeSettingMinipoolUnbondedMax(c *cli.Command, unbondedMinipoolMax uint64) (*api.ProposeTNDAOSettingMinipoolUnbondedMaxResponse, error) { +func proposeSettingMinipoolUnbondedMax(c *cli.Command, unbondedMinipoolMax uint64, opts *bind.TransactOpts) (*api.ProposeTNDAOSettingMinipoolUnbondedMaxResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -247,18 +214,6 @@ func proposeSettingMinipoolUnbondedMax(c *cli.Command, unbondedMinipoolMax uint6 // Response response := api.ProposeTNDAOSettingMinipoolUnbondedMaxResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal proposalId, hash, err := trustednode.ProposeMinipoolUnbondedMax(rp, unbondedMinipoolMax, opts) if err != nil { @@ -297,6 +252,7 @@ func canProposeSettingProposalCooldown(c *cli.Command, proposalCooldownTimespan if err != nil { return nil, err } + gasInfo, err := trustednode.EstimateProposeProposalCooldownTimeGas(rp, proposalCooldownTimespan, opts) if err != nil { return nil, err @@ -307,16 +263,12 @@ func canProposeSettingProposalCooldown(c *cli.Command, proposalCooldownTimespan } -func proposeSettingProposalCooldown(c *cli.Command, proposalCooldownTimespan uint64) (*api.ProposeTNDAOSettingProposalCooldownResponse, error) { +func proposeSettingProposalCooldown(c *cli.Command, proposalCooldownTimespan uint64, opts *bind.TransactOpts) (*api.ProposeTNDAOSettingProposalCooldownResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -325,18 +277,6 @@ func proposeSettingProposalCooldown(c *cli.Command, proposalCooldownTimespan uin // Response response := api.ProposeTNDAOSettingProposalCooldownResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal proposalId, hash, err := trustednode.ProposeProposalCooldownTime(rp, proposalCooldownTimespan, opts) if err != nil { @@ -375,6 +315,7 @@ func canProposeSettingProposalVoteTimespan(c *cli.Command, proposalVoteTimespan if err != nil { return nil, err } + gasInfo, err := trustednode.EstimateProposeProposalVoteTimeGas(rp, proposalVoteTimespan, opts) if err != nil { return nil, err @@ -385,16 +326,12 @@ func canProposeSettingProposalVoteTimespan(c *cli.Command, proposalVoteTimespan } -func proposeSettingProposalVoteTimespan(c *cli.Command, proposalVoteTimespan uint64) (*api.ProposeTNDAOSettingProposalVoteTimespanResponse, error) { +func proposeSettingProposalVoteTimespan(c *cli.Command, proposalVoteTimespan uint64, opts *bind.TransactOpts) (*api.ProposeTNDAOSettingProposalVoteTimespanResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -403,18 +340,6 @@ func proposeSettingProposalVoteTimespan(c *cli.Command, proposalVoteTimespan uin // Response response := api.ProposeTNDAOSettingProposalVoteTimespanResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal proposalId, hash, err := trustednode.ProposeProposalVoteTime(rp, proposalVoteTimespan, opts) if err != nil { @@ -453,6 +378,7 @@ func canProposeSettingProposalVoteDelayTimespan(c *cli.Command, proposalDelayTim if err != nil { return nil, err } + gasInfo, err := trustednode.EstimateProposeProposalVoteDelayTimeGas(rp, proposalDelayTimespan, opts) if err != nil { return nil, err @@ -463,16 +389,12 @@ func canProposeSettingProposalVoteDelayTimespan(c *cli.Command, proposalDelayTim } -func proposeSettingProposalVoteDelayTimespan(c *cli.Command, proposalDelayTimespan uint64) (*api.ProposeTNDAOSettingProposalVoteDelayTimespanResponse, error) { +func proposeSettingProposalVoteDelayTimespan(c *cli.Command, proposalDelayTimespan uint64, opts *bind.TransactOpts) (*api.ProposeTNDAOSettingProposalVoteDelayTimespanResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -481,18 +403,6 @@ func proposeSettingProposalVoteDelayTimespan(c *cli.Command, proposalDelayTimesp // Response response := api.ProposeTNDAOSettingProposalVoteDelayTimespanResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal proposalId, hash, err := trustednode.ProposeProposalVoteDelayTime(rp, proposalDelayTimespan, opts) if err != nil { @@ -531,6 +441,7 @@ func canProposeSettingProposalExecuteTimespan(c *cli.Command, proposalExecuteTim if err != nil { return nil, err } + gasInfo, err := trustednode.EstimateProposeProposalExecuteTimeGas(rp, proposalExecuteTimespan, opts) if err != nil { return nil, err @@ -541,16 +452,12 @@ func canProposeSettingProposalExecuteTimespan(c *cli.Command, proposalExecuteTim } -func proposeSettingProposalExecuteTimespan(c *cli.Command, proposalExecuteTimespan uint64) (*api.ProposeTNDAOSettingProposalExecuteTimespanResponse, error) { +func proposeSettingProposalExecuteTimespan(c *cli.Command, proposalExecuteTimespan uint64, opts *bind.TransactOpts) (*api.ProposeTNDAOSettingProposalExecuteTimespanResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -559,18 +466,6 @@ func proposeSettingProposalExecuteTimespan(c *cli.Command, proposalExecuteTimesp // Response response := api.ProposeTNDAOSettingProposalExecuteTimespanResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal proposalId, hash, err := trustednode.ProposeProposalExecuteTime(rp, proposalExecuteTimespan, opts) if err != nil { @@ -609,6 +504,7 @@ func canProposeSettingProposalActionTimespan(c *cli.Command, proposalActionTimes if err != nil { return nil, err } + gasInfo, err := trustednode.EstimateProposeProposalActionTimeGas(rp, proposalActionTimespan, opts) if err != nil { return nil, err @@ -619,16 +515,12 @@ func canProposeSettingProposalActionTimespan(c *cli.Command, proposalActionTimes } -func proposeSettingProposalActionTimespan(c *cli.Command, proposalActionTimespan uint64) (*api.ProposeTNDAOSettingProposalActionTimespanResponse, error) { +func proposeSettingProposalActionTimespan(c *cli.Command, proposalActionTimespan uint64, opts *bind.TransactOpts) (*api.ProposeTNDAOSettingProposalActionTimespanResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -637,18 +529,6 @@ func proposeSettingProposalActionTimespan(c *cli.Command, proposalActionTimespan // Response response := api.ProposeTNDAOSettingProposalActionTimespanResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal proposalId, hash, err := trustednode.ProposeProposalActionTime(rp, proposalActionTimespan, opts) if err != nil { @@ -687,6 +567,7 @@ func canProposeSettingScrubPeriod(c *cli.Command, scrubPeriod uint64) (*api.CanP if err != nil { return nil, err } + gasInfo, err := trustednode.EstimateProposeScrubPeriodGas(rp, scrubPeriod, opts) if err != nil { return nil, err @@ -697,16 +578,12 @@ func canProposeSettingScrubPeriod(c *cli.Command, scrubPeriod uint64) (*api.CanP } -func proposeSettingScrubPeriod(c *cli.Command, scrubPeriod uint64) (*api.ProposeTNDAOSettingScrubPeriodResponse, error) { +func proposeSettingScrubPeriod(c *cli.Command, scrubPeriod uint64, opts *bind.TransactOpts) (*api.ProposeTNDAOSettingScrubPeriodResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -715,18 +592,6 @@ func proposeSettingScrubPeriod(c *cli.Command, scrubPeriod uint64) (*api.Propose // Response response := api.ProposeTNDAOSettingScrubPeriodResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal proposalId, hash, err := trustednode.ProposeScrubPeriod(rp, scrubPeriod, opts) if err != nil { @@ -765,6 +630,7 @@ func canProposeSettingPromotionScrubPeriod(c *cli.Command, promotionScrubPeriod if err != nil { return nil, err } + gasInfo, err := trustednode.EstimateProposePromotionScrubPeriodGas(rp, promotionScrubPeriod, opts) if err != nil { return nil, err @@ -775,16 +641,12 @@ func canProposeSettingPromotionScrubPeriod(c *cli.Command, promotionScrubPeriod } -func proposeSettingPromotionScrubPeriod(c *cli.Command, promotionScrubPeriod uint64) (*api.ProposeTNDAOSettingPromotionScrubPeriodResponse, error) { +func proposeSettingPromotionScrubPeriod(c *cli.Command, promotionScrubPeriod uint64, opts *bind.TransactOpts) (*api.ProposeTNDAOSettingPromotionScrubPeriodResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -793,18 +655,6 @@ func proposeSettingPromotionScrubPeriod(c *cli.Command, promotionScrubPeriod uin // Response response := api.ProposeTNDAOSettingPromotionScrubPeriodResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal proposalId, hash, err := trustednode.ProposePromotionScrubPeriod(rp, promotionScrubPeriod, opts) if err != nil { @@ -843,6 +693,7 @@ func canProposeSettingScrubPenaltyEnabled(c *cli.Command, enabled bool) (*api.Ca if err != nil { return nil, err } + gasInfo, err := trustednode.EstimateProposeScrubPenaltyEnabledGas(rp, enabled, opts) if err != nil { return nil, err @@ -853,16 +704,12 @@ func canProposeSettingScrubPenaltyEnabled(c *cli.Command, enabled bool) (*api.Ca } -func proposeSettingScrubPenaltyEnabled(c *cli.Command, enabled bool) (*api.ProposeTNDAOSettingScrubPeriodResponse, error) { +func proposeSettingScrubPenaltyEnabled(c *cli.Command, enabled bool, opts *bind.TransactOpts) (*api.ProposeTNDAOSettingScrubPeriodResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -871,18 +718,6 @@ func proposeSettingScrubPenaltyEnabled(c *cli.Command, enabled bool) (*api.Propo // Response response := api.ProposeTNDAOSettingScrubPeriodResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal proposalId, hash, err := trustednode.ProposeScrubPenaltyEnabled(rp, enabled, opts) if err != nil { @@ -921,6 +756,7 @@ func canProposeSettingBondReductionWindowStart(c *cli.Command, bondReductionWind if err != nil { return nil, err } + gasInfo, err := trustednode.EstimateProposeBondReductionWindowStartGas(rp, bondReductionWindowStart, opts) if err != nil { return nil, err @@ -931,16 +767,12 @@ func canProposeSettingBondReductionWindowStart(c *cli.Command, bondReductionWind } -func proposeSettingBondReductionWindowStart(c *cli.Command, bondReductionWindowStart uint64) (*api.ProposeTNDAOSettingScrubPeriodResponse, error) { +func proposeSettingBondReductionWindowStart(c *cli.Command, bondReductionWindowStart uint64, opts *bind.TransactOpts) (*api.ProposeTNDAOSettingScrubPeriodResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -949,18 +781,6 @@ func proposeSettingBondReductionWindowStart(c *cli.Command, bondReductionWindowS // Response response := api.ProposeTNDAOSettingScrubPeriodResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal proposalId, hash, err := trustednode.ProposeBondReductionWindowStart(rp, bondReductionWindowStart, opts) if err != nil { @@ -999,6 +819,7 @@ func canProposeSettingBondReductionWindowLength(c *cli.Command, bondReductionWin if err != nil { return nil, err } + gasInfo, err := trustednode.EstimateProposeBondReductionWindowLengthGas(rp, bondReductionWindowLength, opts) if err != nil { return nil, err @@ -1009,16 +830,12 @@ func canProposeSettingBondReductionWindowLength(c *cli.Command, bondReductionWin } -func proposeSettingBondReductionWindowLength(c *cli.Command, bondReductionWindowLength uint64) (*api.ProposeTNDAOSettingScrubPeriodResponse, error) { +func proposeSettingBondReductionWindowLength(c *cli.Command, bondReductionWindowLength uint64, opts *bind.TransactOpts) (*api.ProposeTNDAOSettingScrubPeriodResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -1027,18 +844,6 @@ func proposeSettingBondReductionWindowLength(c *cli.Command, bondReductionWindow // Response response := api.ProposeTNDAOSettingScrubPeriodResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal proposalId, hash, err := trustednode.ProposeBondReductionWindowLength(rp, bondReductionWindowLength, opts) if err != nil { diff --git a/rocketpool/api/odao/routes.go b/rocketpool/api/odao/routes.go new file mode 100644 index 000000000..55de36b6b --- /dev/null +++ b/rocketpool/api/odao/routes.go @@ -0,0 +1,691 @@ +package odao + +import ( + "fmt" + "math/big" + "net/http" + "strconv" + + "github.com/ethereum/go-ethereum/common" + "github.com/urfave/cli/v3" + + "github.com/rocket-pool/smartnode/shared/services" + 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.Command) { + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeInvite(c, addr, memberId, memberUrl, opts) + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeLeave(c, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeKick(c, addr, fine, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := cancelProposal(c, id, opts) + 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" + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := voteOnProposal(c, id, support, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := executeProposal(c, id, opts) + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := approveRpl(c, opts) + 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) + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := waitForApprovalAndJoin(c, hash, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := leave(c, common.HexToAddress(bondRefundStr), opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := penaliseMegapool(c, megapool, block, amount, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingMembersQuorum(c, quorum, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingMembersRplBond(c, bond, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingMinipoolUnbondedMax(c, max, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingProposalCooldown(c, val, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingProposalVoteTimespan(c, val, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingProposalVoteDelayTimespan(c, val, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingProposalExecuteTimespan(c, val, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingProposalActionTimespan(c, val, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingScrubPeriod(c, val, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingPromotionScrubPeriod(c, val, opts) + 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") + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingScrubPenaltyEnabled(c, enabledStr == "true", opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingBondReductionWindowStart(c, val, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSettingBondReductionWindowLength(c, val, opts) + 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/odao/vote-proposal.go b/rocketpool/api/odao/vote-proposal.go index b22b06579..e2860fe2d 100644 --- a/rocketpool/api/odao/vote-proposal.go +++ b/rocketpool/api/odao/vote-proposal.go @@ -1,7 +1,7 @@ package odao import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/dao" "github.com/rocket-pool/smartnode/bindings/dao/trustednode" @@ -11,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canVoteOnProposal(c *cli.Command, proposalId uint64) (*api.CanVoteOnTNDAOProposalResponse, error) { @@ -111,16 +110,12 @@ func canVoteOnProposal(c *cli.Command, proposalId uint64) (*api.CanVoteOnTNDAOPr } -func voteOnProposal(c *cli.Command, proposalId uint64, support bool) (*api.VoteOnTNDAOProposalResponse, error) { +func voteOnProposal(c *cli.Command, proposalId uint64, support bool, opts *bind.TransactOpts) (*api.VoteOnTNDAOProposalResponse, error) { // Get services if err := services.RequireNodeTrusted(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -129,18 +124,6 @@ func voteOnProposal(c *cli.Command, proposalId uint64, support bool) (*api.VoteO // Response response := api.VoteOnTNDAOProposalResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Vote on proposal hash, err := trustednode.VoteOnProposal(rp, proposalId, support, opts) if err != nil { diff --git a/rocketpool/api/pdao/claim-bonds.go b/rocketpool/api/pdao/claim-bonds.go index 228450038..a71015808 100644 --- a/rocketpool/api/pdao/claim-bonds.go +++ b/rocketpool/api/pdao/claim-bonds.go @@ -1,7 +1,7 @@ package pdao import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/dao/protocol" "github.com/rocket-pool/smartnode/bindings/rocketpool" @@ -11,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canClaimBonds(c *cli.Command, proposalId uint64, indices []uint64) (*api.PDAOCanClaimBondsResponse, error) { @@ -104,7 +103,7 @@ func canClaimBonds(c *cli.Command, proposalId uint64, indices []uint64) (*api.PD return &response, nil } -func claimBonds(c *cli.Command, isProposer bool, proposalId uint64, indices []uint64) (*api.PDAOClaimBondsResponse, error) { +func claimBonds(c *cli.Command, isProposer bool, proposalId uint64, indices []uint64, opts *bind.TransactOpts) (*api.PDAOClaimBondsResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { return nil, err @@ -112,10 +111,6 @@ func claimBonds(c *cli.Command, isProposer bool, proposalId uint64, indices []ui if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -124,18 +119,6 @@ func claimBonds(c *cli.Command, isProposer bool, proposalId uint64, indices []ui // Response response := api.PDAOClaimBondsResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Claim bonds if isProposer { response.TxHash, err = protocol.ClaimBondProposer(rp, proposalId, indices, opts) diff --git a/rocketpool/api/pdao/commands.go b/rocketpool/api/pdao/commands.go deleted file mode 100644 index ae5fc273d..000000000 --- a/rocketpool/api/pdao/commands.go +++ /dev/null @@ -1,1152 +0,0 @@ -package pdao - -import ( - "context" - "fmt" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/urfave/cli/v3" - - "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.Commands = append(command.Commands, &cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the Rocket Pool protocol DAO", - Commands: []*cli.Command{ - - { - Name: "proposals", - Aliases: []string{"p"}, - Usage: "Get the protocol DAO proposals", - UsageText: "rocketpool api pdao proposals", - Action: func(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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/defeat-proposal.go b/rocketpool/api/pdao/defeat-proposal.go index d3e6f601b..2d3a7ced1 100644 --- a/rocketpool/api/pdao/defeat-proposal.go +++ b/rocketpool/api/pdao/defeat-proposal.go @@ -1,9 +1,10 @@ package pdao import ( - "fmt" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" "github.com/rocket-pool/smartnode/bindings/types" "github.com/urfave/cli/v3" @@ -11,7 +12,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canDefeatProposal(c *cli.Command, proposalId uint64, index uint64) (*api.PDAOCanDefeatProposalResponse, error) { @@ -108,7 +108,7 @@ func canDefeatProposal(c *cli.Command, proposalId uint64, index uint64) (*api.PD return &response, nil } -func defeatProposal(c *cli.Command, proposalId uint64, index uint64) (*api.PDAODefeatProposalResponse, error) { +func defeatProposal(c *cli.Command, proposalId uint64, index uint64, opts *bind.TransactOpts) (*api.PDAODefeatProposalResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { return nil, err @@ -116,10 +116,6 @@ func defeatProposal(c *cli.Command, proposalId uint64, index uint64) (*api.PDAOD if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -128,18 +124,6 @@ func defeatProposal(c *cli.Command, proposalId uint64, index uint64) (*api.PDAOD // Response response := api.PDAODefeatProposalResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Execute proposal hash, err := protocol.DefeatProposal(rp, proposalId, index, opts) if err != nil { diff --git a/rocketpool/api/pdao/execute-proposal.go b/rocketpool/api/pdao/execute-proposal.go index 597db0881..a6680e556 100644 --- a/rocketpool/api/pdao/execute-proposal.go +++ b/rocketpool/api/pdao/execute-proposal.go @@ -1,7 +1,7 @@ package pdao import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/dao/protocol" rptypes "github.com/rocket-pool/smartnode/bindings/types" @@ -10,7 +10,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canExecuteProposal(c *cli.Command, proposalId uint64) (*api.CanExecutePDAOProposalResponse, error) { @@ -79,7 +78,7 @@ func canExecuteProposal(c *cli.Command, proposalId uint64) (*api.CanExecutePDAOP } -func executeProposal(c *cli.Command, proposalId uint64) (*api.ExecutePDAOProposalResponse, error) { +func executeProposal(c *cli.Command, proposalId uint64, opts *bind.TransactOpts) (*api.ExecutePDAOProposalResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -88,10 +87,6 @@ func executeProposal(c *cli.Command, proposalId uint64) (*api.ExecutePDAOProposa if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -100,18 +95,6 @@ func executeProposal(c *cli.Command, proposalId uint64) (*api.ExecutePDAOProposa // Response response := api.ExecutePDAOProposalResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Execute proposal hash, err := protocol.ExecuteProposal(rp, proposalId, opts) if err != nil { diff --git a/rocketpool/api/pdao/finalize-proposal.go b/rocketpool/api/pdao/finalize-proposal.go index 0ac0a805b..05f04a330 100644 --- a/rocketpool/api/pdao/finalize-proposal.go +++ b/rocketpool/api/pdao/finalize-proposal.go @@ -1,7 +1,7 @@ package pdao import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/dao/protocol" "github.com/rocket-pool/smartnode/bindings/types" @@ -10,7 +10,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canFinalizeProposal(c *cli.Command, proposalId uint64) (*api.PDAOCanFinalizeProposalResponse, error) { @@ -87,7 +86,7 @@ func canFinalizeProposal(c *cli.Command, proposalId uint64) (*api.PDAOCanFinaliz return &response, nil } -func finalizeProposal(c *cli.Command, proposalId uint64) (*api.PDAOFinalizeProposalResponse, error) { +func finalizeProposal(c *cli.Command, proposalId uint64, opts *bind.TransactOpts) (*api.PDAOFinalizeProposalResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { return nil, err @@ -95,10 +94,6 @@ func finalizeProposal(c *cli.Command, proposalId uint64) (*api.PDAOFinalizePropo if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -107,18 +102,6 @@ func finalizeProposal(c *cli.Command, proposalId uint64) (*api.PDAOFinalizePropo // Response response := api.PDAOFinalizeProposalResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Execute proposal hash, err := protocol.Finalize(rp, proposalId, opts) if err != nil { diff --git a/rocketpool/api/pdao/invite-security.go b/rocketpool/api/pdao/invite-security.go index 99c8846c0..896ef8c79 100644 --- a/rocketpool/api/pdao/invite-security.go +++ b/rocketpool/api/pdao/invite-security.go @@ -3,13 +3,13 @@ package pdao import ( "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/dao/protocol" "github.com/rocket-pool/smartnode/bindings/dao/security" "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" "golang.org/x/sync/errgroup" ) @@ -97,16 +97,12 @@ func canProposeInviteToSecurityCouncil(c *cli.Command, id string, address common return &response, nil } -func proposeInviteToSecurityCouncil(c *cli.Command, id string, address common.Address, blockNumber uint32) (*api.PDAOProposeInviteToSecurityCouncilResponse, error) { +func proposeInviteToSecurityCouncil(c *cli.Command, id string, address common.Address, blockNumber uint32, opts *bind.TransactOpts) (*api.PDAOProposeInviteToSecurityCouncilResponse, error) { // Get services cfg, err := services.GetConfig(c) if err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -119,18 +115,6 @@ func proposeInviteToSecurityCouncil(c *cli.Command, id string, address common.Ad // Response response := api.PDAOProposeInviteToSecurityCouncilResponse{} - // Get node account - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Propose message := fmt.Sprintf("invite %s (%s) to the security council", id, address.Hex()) pollard, err := getPollard(rp, cfg, bc, blockNumber) diff --git a/rocketpool/api/pdao/kick-multi-security.go b/rocketpool/api/pdao/kick-multi-security.go index 85c78d51f..365b99f7d 100644 --- a/rocketpool/api/pdao/kick-multi-security.go +++ b/rocketpool/api/pdao/kick-multi-security.go @@ -1,13 +1,11 @@ package pdao import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/dao/protocol" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -56,16 +54,12 @@ func canProposeKickMultiFromSecurityCouncil(c *cli.Command, addresses []common.A return &response, nil } -func proposeKickMultiFromSecurityCouncil(c *cli.Command, addresses []common.Address, blockNumber uint32) (*api.PDAOProposeKickMultiFromSecurityCouncilResponse, error) { +func proposeKickMultiFromSecurityCouncil(c *cli.Command, addresses []common.Address, blockNumber uint32, opts *bind.TransactOpts) (*api.PDAOProposeKickMultiFromSecurityCouncilResponse, error) { // Get services cfg, err := services.GetConfig(c) if err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -78,18 +72,6 @@ func proposeKickMultiFromSecurityCouncil(c *cli.Command, addresses []common.Addr // Response response := api.PDAOProposeKickMultiFromSecurityCouncilResponse{} - // Get node account - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Propose message := "kick multiple members from the security council" pollard, err := getPollard(rp, cfg, bc, blockNumber) diff --git a/rocketpool/api/pdao/kick-security.go b/rocketpool/api/pdao/kick-security.go index e846b4afa..4c0fcd257 100644 --- a/rocketpool/api/pdao/kick-security.go +++ b/rocketpool/api/pdao/kick-security.go @@ -3,12 +3,12 @@ package pdao import ( "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/dao/protocol" "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -81,16 +81,12 @@ func canProposeKickFromSecurityCouncil(c *cli.Command, address common.Address) ( return &response, nil } -func proposeKickFromSecurityCouncil(c *cli.Command, address common.Address, blockNumber uint32) (*api.PDAOProposeKickFromSecurityCouncilResponse, error) { +func proposeKickFromSecurityCouncil(c *cli.Command, address common.Address, blockNumber uint32, opts *bind.TransactOpts) (*api.PDAOProposeKickFromSecurityCouncilResponse, error) { // Get services cfg, err := services.GetConfig(c) if err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -103,18 +99,6 @@ func proposeKickFromSecurityCouncil(c *cli.Command, address common.Address, bloc // Response response := api.PDAOProposeKickFromSecurityCouncilResponse{} - // Get node account - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Propose message := fmt.Sprintf("kick %s from the security council", address.Hex()) pollard, err := getPollard(rp, cfg, bc, blockNumber) diff --git a/rocketpool/api/pdao/one-time-spend.go b/rocketpool/api/pdao/one-time-spend.go index 5eaac52a1..47c25259f 100644 --- a/rocketpool/api/pdao/one-time-spend.go +++ b/rocketpool/api/pdao/one-time-spend.go @@ -1,15 +1,14 @@ package pdao import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/dao/protocol" "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -81,16 +80,12 @@ func canProposeOneTimeSpend(c *cli.Command, invoiceID string, recipient common.A return &response, nil } -func proposeOneTimeSpend(c *cli.Command, invoiceID string, recipient common.Address, amount *big.Int, blockNumber uint32, customMessage string) (*api.PDAOProposeOneTimeSpendResponse, error) { +func proposeOneTimeSpend(c *cli.Command, invoiceID string, recipient common.Address, amount *big.Int, blockNumber uint32, customMessage string, opts *bind.TransactOpts) (*api.PDAOProposeOneTimeSpendResponse, error) { // Get services cfg, err := services.GetConfig(c) if err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -103,18 +98,6 @@ func proposeOneTimeSpend(c *cli.Command, invoiceID string, recipient common.Addr // Response response := api.PDAOProposeOneTimeSpendResponse{} - // Get node account - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Propose pollard, err := getPollard(rp, cfg, bc, blockNumber) if err != nil { diff --git a/rocketpool/api/pdao/override-vote.go b/rocketpool/api/pdao/override-vote.go index 6d73ffb78..33595e5a1 100644 --- a/rocketpool/api/pdao/override-vote.go +++ b/rocketpool/api/pdao/override-vote.go @@ -1,8 +1,7 @@ package pdao import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/dao/protocol" "github.com/rocket-pool/smartnode/bindings/network" @@ -12,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canOverrideVote(c *cli.Command, proposalId uint64, voteDirection types.VoteDirection) (*api.CanVoteOnPDAOProposalResponse, error) { @@ -106,12 +104,8 @@ func canOverrideVote(c *cli.Command, proposalId uint64, voteDirection types.Vote return &response, nil } -func overrideVote(c *cli.Command, proposalId uint64, voteDirection types.VoteDirection) (*api.VoteOnPDAOProposalResponse, error) { +func overrideVote(c *cli.Command, proposalId uint64, voteDirection types.VoteDirection, opts *bind.TransactOpts) (*api.VoteOnPDAOProposalResponse, error) { // Get services - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -120,18 +114,6 @@ func overrideVote(c *cli.Command, proposalId uint64, voteDirection types.VoteDir // Response response := api.VoteOnPDAOProposalResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Vote on proposal hash, err := protocol.OverrideVote(rp, proposalId, voteDirection, opts) if err != nil { diff --git a/rocketpool/api/pdao/percentages.go b/rocketpool/api/pdao/percentages.go index a35c621ec..4e8c7fdd9 100644 --- a/rocketpool/api/pdao/percentages.go +++ b/rocketpool/api/pdao/percentages.go @@ -4,13 +4,14 @@ import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/rocket-pool/smartnode/bindings/dao/protocol" rpnode "github.com/rocket-pool/smartnode/bindings/node" psettings "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -125,7 +126,7 @@ func canProposeRewardsPercentages(c *cli.Command, node *big.Int, odao *big.Int, return &response, nil } -func proposeRewardsPercentages(c *cli.Command, node *big.Int, odao *big.Int, pdao *big.Int, blockNumber uint32) (*api.PDAOProposeRewardsPercentagesResponse, error) { +func proposeRewardsPercentages(c *cli.Command, node *big.Int, odao *big.Int, pdao *big.Int, blockNumber uint32, opts *bind.TransactOpts) (*api.PDAOProposeRewardsPercentagesResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { return nil, err @@ -137,10 +138,6 @@ func proposeRewardsPercentages(c *cli.Command, node *big.Int, odao *big.Int, pda if err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -154,23 +151,12 @@ func proposeRewardsPercentages(c *cli.Command, node *big.Int, odao *big.Int, pda response := api.PDAOProposeRewardsPercentagesResponse{} // Get the account transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - // Decode the pollard pollard, err := getPollard(rp, cfg, bc, blockNumber) if err != nil { return nil, fmt.Errorf("error regenerating pollard: %w", err) } - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit the proposal proposalID, hash, err := protocol.ProposeSetRewardsPercentage(rp, "update RPL rewards distribution", odao, pdao, node, blockNumber, pollard, opts) if err != nil { diff --git a/rocketpool/api/pdao/propose-settings.go b/rocketpool/api/pdao/propose-settings.go index 76b65e5b2..ef42e6def 100644 --- a/rocketpool/api/pdao/propose-settings.go +++ b/rocketpool/api/pdao/propose-settings.go @@ -4,6 +4,7 @@ import ( "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" protocol131 "github.com/rocket-pool/smartnode/bindings/legacy/v1.3.1/protocol" @@ -13,7 +14,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" "golang.org/x/sync/errgroup" ) @@ -939,7 +939,7 @@ func canProposeSetting(c *cli.Command, contractName string, settingName string, } -func proposeSetting(c *cli.Command, contractName string, settingName string, value string, blockNumber uint32) (*api.ProposePDAOSettingResponse, error) { +func proposeSetting(c *cli.Command, contractName string, settingName string, value string, blockNumber uint32, opts *bind.TransactOpts) (*api.ProposePDAOSettingResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -952,10 +952,6 @@ func proposeSetting(c *cli.Command, contractName string, settingName string, val if err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -974,18 +970,6 @@ func proposeSetting(c *cli.Command, contractName string, settingName string, val return nil, fmt.Errorf("error regenerating pollard: %w", err) } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit the proposal var proposalID uint64 var hash common.Hash diff --git a/rocketpool/api/pdao/recurring-spend.go b/rocketpool/api/pdao/recurring-spend.go index 7138a8ce6..1430d1efb 100644 --- a/rocketpool/api/pdao/recurring-spend.go +++ b/rocketpool/api/pdao/recurring-spend.go @@ -1,16 +1,15 @@ package pdao import ( - "fmt" "math/big" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/dao/protocol" "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -82,16 +81,12 @@ func canProposeRecurringSpend(c *cli.Command, contractName string, recipient com return &response, nil } -func proposeRecurringSpend(c *cli.Command, contractName string, recipient common.Address, amountPerPeriod *big.Int, periodLength time.Duration, startTime time.Time, numberOfPeriods uint64, blockNumber uint32, customMessage string) (*api.PDAOProposeOneTimeSpendResponse, error) { +func proposeRecurringSpend(c *cli.Command, contractName string, recipient common.Address, amountPerPeriod *big.Int, periodLength time.Duration, startTime time.Time, numberOfPeriods uint64, blockNumber uint32, customMessage string, opts *bind.TransactOpts) (*api.PDAOProposeOneTimeSpendResponse, error) { // Get services cfg, err := services.GetConfig(c) if err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -104,18 +99,6 @@ func proposeRecurringSpend(c *cli.Command, contractName string, recipient common // Response response := api.PDAOProposeOneTimeSpendResponse{} - // Get node account - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Propose pollard, err := getPollard(rp, cfg, bc, blockNumber) diff --git a/rocketpool/api/pdao/replace-security.go b/rocketpool/api/pdao/replace-security.go index 68b492b40..047b2dd4e 100644 --- a/rocketpool/api/pdao/replace-security.go +++ b/rocketpool/api/pdao/replace-security.go @@ -3,13 +3,13 @@ package pdao import ( "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/dao/protocol" "github.com/rocket-pool/smartnode/bindings/dao/security" "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -88,16 +88,12 @@ func canProposeReplaceMemberOfSecurityCouncil(c *cli.Command, existingMemberAddr return &response, nil } -func proposeReplaceMemberOfSecurityCouncil(c *cli.Command, existingMemberAddress common.Address, newMemberID string, newMemberAddress common.Address, blockNumber uint32) (*api.PDAOProposeReplaceMemberOfSecurityCouncilResponse, error) { +func proposeReplaceMemberOfSecurityCouncil(c *cli.Command, existingMemberAddress common.Address, newMemberID string, newMemberAddress common.Address, blockNumber uint32, opts *bind.TransactOpts) (*api.PDAOProposeReplaceMemberOfSecurityCouncilResponse, error) { // Get services cfg, err := services.GetConfig(c) if err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -111,23 +107,12 @@ func proposeReplaceMemberOfSecurityCouncil(c *cli.Command, existingMemberAddress response := api.PDAOProposeReplaceMemberOfSecurityCouncilResponse{} // Get node account - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - // Get the existing member existingID, err := security.GetMemberID(rp, existingMemberAddress, nil) if err != nil { return nil, fmt.Errorf("error getting ID of existing member: %w", err) } - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Propose message := fmt.Sprintf("replace %s (%s) on the security council with %s (%s)", existingID, existingMemberAddress.Hex(), newMemberID, newMemberAddress.Hex()) pollard, err := getPollard(rp, cfg, bc, blockNumber) diff --git a/rocketpool/api/pdao/routes.go b/rocketpool/api/pdao/routes.go new file mode 100644 index 000000000..ae09560fd --- /dev/null +++ b/rocketpool/api/pdao/routes.go @@ -0,0 +1,703 @@ +package pdao + +import ( + "fmt" + "math/big" + "net/http" + "strconv" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/urfave/cli/v3" + + bindtypes "github.com/rocket-pool/smartnode/bindings/types" + "github.com/rocket-pool/smartnode/shared/services" + 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.Command) { + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := voteOnProposal(c, id, voteDir, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := overrideVote(c, id, voteDir, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := executeProposal(c, id, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSetting(c, contract, setting, value, blockNumber, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeRewardsPercentages(c, node, odaoAmt, pdaoAmt, blockNumber, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeOneTimeSpend(c, invoiceID, recipient, amount, blockNumber, customMessage, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeRecurringSpend(c, contractName, recipient, amountPerPeriod, periodLength, startTime, numberOfPeriods, blockNumber, customMessage, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeRecurringSpendUpdate(c, contractName, recipient, amountPerPeriod, periodLength, numberOfPeriods, blockNumber, customMessage, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeInviteToSecurityCouncil(c, id, addr, blockNumber, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeKickFromSecurityCouncil(c, addr, blockNumber, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeKickMultiFromSecurityCouncil(c, addresses, blockNumber, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeReplaceMemberOfSecurityCouncil(c, existing, newID, newAddr, blockNumber, opts) + 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" + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := claimBonds(c, isProposer, proposalID, indices, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := defeatProposal(c, id, index, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := finalizeProposal(c, id, opts) + 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")) + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := setVotingDelegate(c, addr, opts) + 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") + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := setSignallingAddress(c, addr, sig, opts) + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := clearSignallingAddress(c, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeAllowListedControllers(c, addresses, blockNumber, opts) + 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/pdao/set-allow-list.go b/rocketpool/api/pdao/set-allow-list.go index dde59a23a..dfc867104 100644 --- a/rocketpool/api/pdao/set-allow-list.go +++ b/rocketpool/api/pdao/set-allow-list.go @@ -1,14 +1,12 @@ package pdao import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -80,16 +78,12 @@ func canProposeAllowListedControllers(c *cli.Command, addressList []common.Addre return &response, nil } -func proposeAllowListedControllers(c *cli.Command, addressList []common.Address, blockNumber uint32) (*api.PDAOProposeAllowListedControllersResponse, error) { +func proposeAllowListedControllers(c *cli.Command, addressList []common.Address, blockNumber uint32, opts *bind.TransactOpts) (*api.PDAOProposeAllowListedControllersResponse, error) { // Get services cfg, err := services.GetConfig(c) if err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -102,18 +96,6 @@ func proposeAllowListedControllers(c *cli.Command, addressList []common.Address, // Response response := api.PDAOProposeAllowListedControllersResponse{} - // Get node account - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Propose pollard, err := getPollard(rp, cfg, bc, blockNumber) if err != nil { diff --git a/rocketpool/api/pdao/set-snapshot-address.go b/rocketpool/api/pdao/set-snapshot-address.go index 44809fa92..a0f1a677e 100644 --- a/rocketpool/api/pdao/set-snapshot-address.go +++ b/rocketpool/api/pdao/set-snapshot-address.go @@ -13,7 +13,6 @@ import ( "github.com/rocket-pool/smartnode/shared/types/api" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" apiutils "github.com/rocket-pool/smartnode/shared/utils/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -107,16 +106,12 @@ func canSetSignallingAddress(c *cli.Command, signallingAddress common.Address, s return &response, nil } -func setSignallingAddress(c *cli.Command, signallingAddress common.Address, signature string) (*api.PDAOSetSignallingAddressResponse, error) { +func setSignallingAddress(c *cli.Command, signallingAddress common.Address, signature string, opts *bind.TransactOpts) (*api.PDAOSetSignallingAddressResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } cfg, err := services.GetConfig(c) if err != nil { return nil, err @@ -138,18 +133,6 @@ func setSignallingAddress(c *cli.Command, signallingAddress common.Address, sign fmt.Println("Error parsing signature", err) } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Call SetSigner on RocketSignerRegistry tx, err := reg.SetSigner(opts, signallingAddress, sig.V, sig.R, sig.S) if err != nil { @@ -243,15 +226,11 @@ func canClearSignallingAddress(c *cli.Command) (*api.PDAOCanClearSignallingAddre return &response, nil } -func clearSignallingAddress(c *cli.Command) (*api.PDAOClearSignallingAddressResponse, error) { +func clearSignallingAddress(c *cli.Command, opts *bind.TransactOpts) (*api.PDAOClearSignallingAddressResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } cfg, err := services.GetConfig(c) if err != nil { return nil, err @@ -266,18 +245,6 @@ func clearSignallingAddress(c *cli.Command) (*api.PDAOClearSignallingAddressResp response := api.PDAOClearSignallingAddressResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Clear the signalling address tx, err := reg.ClearSigner(opts) if err != nil { diff --git a/rocketpool/api/pdao/update-recurring-spend.go b/rocketpool/api/pdao/update-recurring-spend.go index 60150ce9c..77a2916df 100644 --- a/rocketpool/api/pdao/update-recurring-spend.go +++ b/rocketpool/api/pdao/update-recurring-spend.go @@ -1,16 +1,15 @@ package pdao import ( - "fmt" "math/big" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/dao/protocol" "github.com/rocket-pool/smartnode/bindings/node" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -82,16 +81,12 @@ func canProposeRecurringSpendUpdate(c *cli.Command, contractName string, recipie return &response, nil } -func proposeRecurringSpendUpdate(c *cli.Command, contractName string, recipient common.Address, amountPerPeriod *big.Int, periodLength time.Duration, numberOfPeriods uint64, blockNumber uint32, customMessage string) (*api.PDAOProposeOneTimeSpendResponse, error) { +func proposeRecurringSpendUpdate(c *cli.Command, contractName string, recipient common.Address, amountPerPeriod *big.Int, periodLength time.Duration, numberOfPeriods uint64, blockNumber uint32, customMessage string, opts *bind.TransactOpts) (*api.PDAOProposeOneTimeSpendResponse, error) { // Get services cfg, err := services.GetConfig(c) if err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -104,18 +99,6 @@ func proposeRecurringSpendUpdate(c *cli.Command, contractName string, recipient // Response response := api.PDAOProposeOneTimeSpendResponse{} - // Get node account - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Propose pollard, err := getPollard(rp, cfg, bc, blockNumber) if err != nil { diff --git a/rocketpool/api/pdao/vote-proposal.go b/rocketpool/api/pdao/vote-proposal.go index e08b30f65..6cb3ad237 100644 --- a/rocketpool/api/pdao/vote-proposal.go +++ b/rocketpool/api/pdao/vote-proposal.go @@ -1,8 +1,7 @@ package pdao import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/dao/protocol" "github.com/rocket-pool/smartnode/bindings/types" @@ -12,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/proposals" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canVoteOnProposal(c *cli.Command, proposalId uint64, voteDirection types.VoteDirection) (*api.CanVoteOnPDAOProposalResponse, error) { @@ -120,7 +118,7 @@ func canVoteOnProposal(c *cli.Command, proposalId uint64, voteDirection types.Vo return &response, nil } -func voteOnProposal(c *cli.Command, proposalId uint64, voteDirection types.VoteDirection) (*api.VoteOnPDAOProposalResponse, error) { +func voteOnProposal(c *cli.Command, proposalId uint64, voteDirection types.VoteDirection, opts *bind.TransactOpts) (*api.VoteOnPDAOProposalResponse, error) { // Get services cfg, err := services.GetConfig(c) if err != nil { @@ -148,12 +146,6 @@ func voteOnProposal(c *cli.Command, proposalId uint64, voteDirection types.VoteD return nil, err } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - // Get the block used by the proposal proposalBlock, err := protocol.GetProposalBlock(rp, proposalId, nil) if err != nil { @@ -170,12 +162,6 @@ func voteOnProposal(c *cli.Command, proposalId uint64, voteDirection types.VoteD return nil, err } - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Vote on proposal hash, err := protocol.VoteOnProposal(rp, proposalId, voteDirection, totalDelegatedVP, nodeIndex, proof, opts) if err != nil { diff --git a/rocketpool/api/pdao/voting.go b/rocketpool/api/pdao/voting.go index 50eddf953..fabf4095f 100644 --- a/rocketpool/api/pdao/voting.go +++ b/rocketpool/api/pdao/voting.go @@ -1,15 +1,13 @@ package pdao import ( - "fmt" - + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/bindings/network" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func estimateSetVotingDelegateGas(c *cli.Command, address common.Address) (*api.PDAOCanSetVotingDelegateResponse, error) { @@ -48,7 +46,7 @@ func estimateSetVotingDelegateGas(c *cli.Command, address common.Address) (*api. } -func setVotingDelegate(c *cli.Command, address common.Address) (*api.PDAOSetVotingDelegateResponse, error) { +func setVotingDelegate(c *cli.Command, address common.Address, opts *bind.TransactOpts) (*api.PDAOSetVotingDelegateResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -58,26 +56,9 @@ func setVotingDelegate(c *cli.Command, address common.Address) (*api.PDAOSetVoti if err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } - // Response response := api.PDAOSetVotingDelegateResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Set the delegate tx, err := network.SetVotingDelegate(rp, address, opts) if err != nil { diff --git a/rocketpool/api/queue/assign-deposits.go b/rocketpool/api/queue/assign-deposits.go index 3344ee622..014e527a9 100644 --- a/rocketpool/api/queue/assign-deposits.go +++ b/rocketpool/api/queue/assign-deposits.go @@ -1,9 +1,9 @@ package queue import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/deposit" "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/urfave/cli/v3" @@ -11,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canAssignDeposits(c *cli.Command, max int64) (*api.CanAssignDepositsResponse, error) { @@ -70,7 +69,7 @@ func canAssignDeposits(c *cli.Command, max int64) (*api.CanAssignDepositsRespons } -func assignDeposits(c *cli.Command, max int64) (*api.AssignDepositsResponse, error) { +func assignDeposits(c *cli.Command, max int64, opts *bind.TransactOpts) (*api.AssignDepositsResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -79,10 +78,6 @@ func assignDeposits(c *cli.Command, max int64) (*api.AssignDepositsResponse, err if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -91,18 +86,6 @@ func assignDeposits(c *cli.Command, max int64) (*api.AssignDepositsResponse, err // Response response := api.AssignDepositsResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Assign deposits hash, err := deposit.AssignDeposits(rp, big.NewInt(max), opts) if err != nil { diff --git a/rocketpool/api/queue/commands.go b/rocketpool/api/queue/commands.go deleted file mode 100644 index bc9862546..000000000 --- a/rocketpool/api/queue/commands.go +++ /dev/null @@ -1,149 +0,0 @@ -package queue - -import ( - "context" - - "github.com/urfave/cli/v3" - - "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.Commands = append(command.Commands, &cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the Rocket Pool deposit queue", - Commands: []*cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get the deposit pool and minipool queue status", - UsageText: "rocketpool api queue status", - Action: func(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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/process.go b/rocketpool/api/queue/process.go index a37690101..2845a3ddf 100644 --- a/rocketpool/api/queue/process.go +++ b/rocketpool/api/queue/process.go @@ -1,9 +1,9 @@ package queue import ( - "fmt" "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/deposit" "github.com/rocket-pool/smartnode/bindings/settings/protocol" "github.com/urfave/cli/v3" @@ -11,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canProcessQueue(c *cli.Command, max int64) (*api.CanProcessQueueResponse, error) { @@ -71,7 +70,7 @@ func canProcessQueue(c *cli.Command, max int64) (*api.CanProcessQueueResponse, e } -func processQueue(c *cli.Command, max int64) (*api.ProcessQueueResponse, error) { +func processQueue(c *cli.Command, max int64, opts *bind.TransactOpts) (*api.ProcessQueueResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -80,10 +79,6 @@ func processQueue(c *cli.Command, max int64) (*api.ProcessQueueResponse, error) if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -92,18 +87,6 @@ func processQueue(c *cli.Command, max int64) (*api.ProcessQueueResponse, error) // Response response := api.ProcessQueueResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Process queue hash, err := deposit.AssignDeposits(rp, big.NewInt(max), opts) diff --git a/rocketpool/api/queue/routes.go b/rocketpool/api/queue/routes.go new file mode 100644 index 000000000..2a0e662d9 --- /dev/null +++ b/rocketpool/api/queue/routes.go @@ -0,0 +1,86 @@ +package queue + +import ( + "net/http" + "strconv" + + "github.com/urfave/cli/v3" + + "github.com/rocket-pool/smartnode/shared/services" + 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.Command) { + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := processQueue(c, int64(max), opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := assignDeposits(c, int64(max), opts) + 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/cancel-proposal.go b/rocketpool/api/security/cancel-proposal.go index 166bbadcf..f67c8c9b4 100644 --- a/rocketpool/api/security/cancel-proposal.go +++ b/rocketpool/api/security/cancel-proposal.go @@ -2,7 +2,8 @@ package security import ( "bytes" - "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/dao" "github.com/rocket-pool/smartnode/bindings/dao/security" @@ -12,7 +13,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canCancelProposal(c *cli.Command, proposalId uint64) (*api.SecurityCanCancelProposalResponse, error) { @@ -91,16 +91,12 @@ func canCancelProposal(c *cli.Command, proposalId uint64) (*api.SecurityCanCance } -func cancelProposal(c *cli.Command, proposalId uint64) (*api.SecurityCancelProposalResponse, error) { +func cancelProposal(c *cli.Command, proposalId uint64, opts *bind.TransactOpts) (*api.SecurityCancelProposalResponse, error) { // Get services if err := services.RequireNodeSecurityMember(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -109,18 +105,6 @@ func cancelProposal(c *cli.Command, proposalId uint64) (*api.SecurityCancelPropo // Response response := api.SecurityCancelProposalResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Cancel proposal hash, err := security.CancelProposal(rp, proposalId, opts) if err != nil { diff --git a/rocketpool/api/security/commands.go b/rocketpool/api/security/commands.go deleted file mode 100644 index b6037f8a2..000000000 --- a/rocketpool/api/security/commands.go +++ /dev/null @@ -1,419 +0,0 @@ -package security - -import ( - "context" - - "github.com/urfave/cli/v3" - - "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.Commands = append(command.Commands, &cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the Rocket Pool security council", - Commands: []*cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get security council status", - UsageText: "rocketpool api security status", - Action: func(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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/execute-proposal.go b/rocketpool/api/security/execute-proposal.go index 6a5194603..8042b6e14 100644 --- a/rocketpool/api/security/execute-proposal.go +++ b/rocketpool/api/security/execute-proposal.go @@ -1,7 +1,7 @@ package security import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/dao" "github.com/rocket-pool/smartnode/bindings/dao/security" @@ -11,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canExecuteProposal(c *cli.Command, proposalId uint64) (*api.SecurityCanExecuteProposalResponse, error) { @@ -80,7 +79,7 @@ func canExecuteProposal(c *cli.Command, proposalId uint64) (*api.SecurityCanExec } -func executeProposal(c *cli.Command, proposalId uint64) (*api.SecurityExecuteProposalResponse, error) { +func executeProposal(c *cli.Command, proposalId uint64, opts *bind.TransactOpts) (*api.SecurityExecuteProposalResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -89,10 +88,6 @@ func executeProposal(c *cli.Command, proposalId uint64) (*api.SecurityExecutePro if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -101,18 +96,6 @@ func executeProposal(c *cli.Command, proposalId uint64) (*api.SecurityExecutePro // Response response := api.SecurityExecuteProposalResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Cancel proposal hash, err := security.ExecuteProposal(rp, proposalId, opts) if err != nil { diff --git a/rocketpool/api/security/join.go b/rocketpool/api/security/join.go index 6e785a008..cd9422065 100644 --- a/rocketpool/api/security/join.go +++ b/rocketpool/api/security/join.go @@ -1,7 +1,7 @@ package security import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/dao/security" "github.com/urfave/cli/v3" @@ -9,7 +9,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canJoin(c *cli.Command) (*api.SecurityCanJoinResponse, error) { @@ -80,16 +79,12 @@ func canJoin(c *cli.Command) (*api.SecurityCanJoinResponse, error) { } -func join(c *cli.Command) (*api.SecurityJoinResponse, error) { +func join(c *cli.Command, opts *bind.TransactOpts) (*api.SecurityJoinResponse, error) { // Get services if err := services.RequireNodeRegistered(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -99,14 +94,6 @@ func join(c *cli.Command) (*api.SecurityJoinResponse, error) { response := api.SecurityJoinResponse{} // Join - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } hash, err := security.Join(rp, opts) if err != nil { return nil, err diff --git a/rocketpool/api/security/leave.go b/rocketpool/api/security/leave.go index 3ce339ba8..8db9737c1 100644 --- a/rocketpool/api/security/leave.go +++ b/rocketpool/api/security/leave.go @@ -1,7 +1,7 @@ package security import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/dao/security" "github.com/urfave/cli/v3" @@ -9,7 +9,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canLeave(c *cli.Command) (*api.SecurityCanLeaveResponse, error) { @@ -70,16 +69,12 @@ func canLeave(c *cli.Command) (*api.SecurityCanLeaveResponse, error) { } -func leave(c *cli.Command) (*api.SecurityLeaveResponse, error) { +func leave(c *cli.Command, opts *bind.TransactOpts) (*api.SecurityLeaveResponse, error) { // Get services if err := services.RequireNodeSecurityMember(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -88,18 +83,6 @@ func leave(c *cli.Command) (*api.SecurityLeaveResponse, error) { // Response response := api.SecurityLeaveResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Leave hash, err := security.Leave(rp, opts) if err != nil { diff --git a/rocketpool/api/security/propose-leave.go b/rocketpool/api/security/propose-leave.go index 35d5c2c34..bc4fe3061 100644 --- a/rocketpool/api/security/propose-leave.go +++ b/rocketpool/api/security/propose-leave.go @@ -1,14 +1,13 @@ package security import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/dao/security" "github.com/urfave/cli/v3" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canProposeLeave(c *cli.Command) (*api.SecurityCanProposeLeaveResponse, error) { @@ -64,16 +63,12 @@ func canProposeLeave(c *cli.Command) (*api.SecurityCanProposeLeaveResponse, erro } -func proposeLeave(c *cli.Command) (*api.SecurityProposeLeaveResponse, error) { +func proposeLeave(c *cli.Command, opts *bind.TransactOpts) (*api.SecurityProposeLeaveResponse, error) { // Get services if err := services.RequireNodeSecurityMember(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -82,18 +77,6 @@ func proposeLeave(c *cli.Command) (*api.SecurityProposeLeaveResponse, error) { // Response response := api.SecurityProposeLeaveResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit proposal hash, err := security.RequestLeave(rp, opts) if err != nil { diff --git a/rocketpool/api/security/propose-settings.go b/rocketpool/api/security/propose-settings.go index 405aeb7e8..3b44f2871 100644 --- a/rocketpool/api/security/propose-settings.go +++ b/rocketpool/api/security/propose-settings.go @@ -3,6 +3,7 @@ package security import ( "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/bindings/settings/protocol" @@ -10,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" - "github.com/rocket-pool/smartnode/shared/utils/eth1" "github.com/urfave/cli/v3" ) @@ -215,7 +215,7 @@ func canProposeSetting(c *cli.Command, contractName string, settingName string, } -func proposeSetting(c *cli.Command, contractName string, settingName string, value string) (*api.ProposePDAOSettingResponse, error) { +func proposeSetting(c *cli.Command, contractName string, settingName string, value string, opts *bind.TransactOpts) (*api.ProposePDAOSettingResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -224,10 +224,6 @@ func proposeSetting(c *cli.Command, contractName string, settingName string, val if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -236,18 +232,6 @@ func proposeSetting(c *cli.Command, contractName string, settingName string, val // Response response := api.ProposePDAOSettingResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Submit the proposal var proposalID uint64 var hash common.Hash diff --git a/rocketpool/api/security/routes.go b/rocketpool/api/security/routes.go new file mode 100644 index 000000000..2446a17ef --- /dev/null +++ b/rocketpool/api/security/routes.go @@ -0,0 +1,189 @@ +package security + +import ( + "net/http" + "strconv" + + "github.com/urfave/cli/v3" + + "github.com/rocket-pool/smartnode/shared/services" + 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.Command) { + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeLeave(c, opts) + 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") + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := proposeSetting(c, contractName, settingName, value, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := cancelProposal(c, id, opts) + 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" + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := voteOnProposal(c, id, support, opts) + 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 + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := executeProposal(c, id, opts) + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := join(c, opts) + 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) { + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := leave(c, opts) + 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/security/vote-proposal.go b/rocketpool/api/security/vote-proposal.go index 21fa354e2..54b097fbe 100644 --- a/rocketpool/api/security/vote-proposal.go +++ b/rocketpool/api/security/vote-proposal.go @@ -1,7 +1,7 @@ package security import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/dao" "github.com/rocket-pool/smartnode/bindings/dao/security" @@ -11,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canVoteOnProposal(c *cli.Command, proposalId uint64) (*api.SecurityCanVoteOnProposalResponse, error) { @@ -111,16 +110,12 @@ func canVoteOnProposal(c *cli.Command, proposalId uint64) (*api.SecurityCanVoteO } -func voteOnProposal(c *cli.Command, proposalId uint64, support bool) (*api.SecurityVoteOnProposalResponse, error) { +func voteOnProposal(c *cli.Command, proposalId uint64, support bool, opts *bind.TransactOpts) (*api.SecurityVoteOnProposalResponse, error) { // Get services if err := services.RequireNodeSecurityMember(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -129,18 +124,6 @@ func voteOnProposal(c *cli.Command, proposalId uint64, support bool) (*api.Secur // Response response := api.SecurityVoteOnProposalResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Vote on proposal hash, err := security.VoteOnProposal(rp, proposalId, support, opts) if err != nil { diff --git a/rocketpool/api/service/commands.go b/rocketpool/api/service/commands.go deleted file mode 100644 index fa2010e60..000000000 --- a/rocketpool/api/service/commands.go +++ /dev/null @@ -1,90 +0,0 @@ -package service - -import ( - "context" - - "github.com/urfave/cli/v3" - - "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.Commands = append(command.Commands, &cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the Rocket Pool deposit queue", - Commands: []*cli.Command{ - { - Name: "get-gas-price-from-latest-block", - Aliases: []string{"g"}, - Usage: "Get the gas price from the latest block", - UsageText: "rocketpool api gas get-gas-price-from-latest-block", - Action: func(ctx context.Context, c *cli.Command) error { - - // Run - api.PrintResponse(getGasPriceFromLatestBlock(c)) - return nil - - }, - }, - - { - 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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..ca9c2bbb7 --- /dev/null +++ b/rocketpool/api/service/routes.go @@ -0,0 +1,32 @@ +package service + +import ( + "net/http" + + "github.com/urfave/cli/v3" + + 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.Command) { + 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) + }) + + mux.HandleFunc("/api/service/get-gas-price-from-latest-block", func(w http.ResponseWriter, r *http.Request) { + resp, err := getGasPriceFromLatestBlock(c) + apiutils.WriteResponse(w, resp, err) + }) +} diff --git a/rocketpool/api/upgrade/commands.go b/rocketpool/api/upgrade/commands.go deleted file mode 100644 index 5e8868018..000000000 --- a/rocketpool/api/upgrade/commands.go +++ /dev/null @@ -1,74 +0,0 @@ -package upgrade - -import ( - "context" - - "github.com/rocket-pool/smartnode/shared/utils/api" - cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" - "github.com/urfave/cli/v3" -) - -// Register subcommands -func RegisterSubcommands(command *cli.Command, name string, aliases []string) { - command.Commands = append(command.Commands, &cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the Rocket Pool trusted node DAO upgrades", - Commands: []*cli.Command{ - { - Name: "get-upgrade-proposals", - Usage: "List the available upgrades", - UsageText: "rocketpool api upgrades get-upgrade-proposals", - Action: func(ctx context.Context, c *cli.Command) error { - // Run - api.PrintResponse(getUpgradeProposals(c)) - return nil - }, - }, - { - - Name: "can-execute-upgrade", - Usage: "Check whether the node can execute a proposal", - UsageText: "rocketpool api upgrades can-execute-upgrade upgrade-proposal-id", - Action: func(ctx context.Context, c *cli.Command) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - upgradeProposalId, err := cliutils.ValidatePositiveUint("upgrade proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(canExecuteUpgrade(c, upgradeProposalId)) - return nil - - }, - }, - { - Name: "execute-upgrade", - Aliases: []string{"x"}, - Usage: "Execute an upgrade", - UsageText: "rocketpool api upgrades execute-upgrade upgrade-proposal-id", - Action: func(ctx context.Context, c *cli.Command) error { - - // Validate args - if err := cliutils.ValidateArgCount(c, 1); err != nil { - return err - } - upgradeProposalId, err := cliutils.ValidatePositiveUint("upgrade proposal ID", c.Args().Get(0)) - if err != nil { - return err - } - - // Run - api.PrintResponse(executeUpgrade(c, upgradeProposalId)) - return nil - - }, - }, - }, - }) -} diff --git a/rocketpool/api/upgrade/execute-upgrade.go b/rocketpool/api/upgrade/execute-upgrade.go index 8c745e0b5..fde627447 100644 --- a/rocketpool/api/upgrade/execute-upgrade.go +++ b/rocketpool/api/upgrade/execute-upgrade.go @@ -1,7 +1,7 @@ package upgrade import ( - "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/dao/trustednode" "github.com/rocket-pool/smartnode/bindings/dao/upgrades" @@ -11,7 +11,6 @@ import ( "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" - "github.com/rocket-pool/smartnode/shared/utils/eth1" ) func canExecuteUpgrade(c *cli.Command, upgradeProposalId uint64) (*api.CanExecuteTNDAOUpgradeResponse, error) { @@ -94,7 +93,7 @@ func canExecuteUpgrade(c *cli.Command, upgradeProposalId uint64) (*api.CanExecut } -func executeUpgrade(c *cli.Command, upgradeProposalId uint64) (*api.ExecuteTNDAOUpgradeResponse, error) { +func executeUpgrade(c *cli.Command, upgradeProposalId uint64, opts *bind.TransactOpts) (*api.ExecuteTNDAOUpgradeResponse, error) { // Get services if err := services.RequireNodeWallet(c); err != nil { @@ -103,10 +102,6 @@ func executeUpgrade(c *cli.Command, upgradeProposalId uint64) (*api.ExecuteTNDAO if err := services.RequireRocketStorage(c); err != nil { return nil, err } - w, err := services.GetWallet(c) - if err != nil { - return nil, err - } rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -115,18 +110,6 @@ func executeUpgrade(c *cli.Command, upgradeProposalId uint64) (*api.ExecuteTNDAO // Response response := api.ExecuteTNDAOUpgradeResponse{} - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - - // Override the provided pending TX if requested - err = eth1.CheckForNonceOverride(c, opts) - if err != nil { - return nil, fmt.Errorf("Error checking for nonce override: %w", err) - } - // Execute upgrade hash, err := upgrades.ExecuteUpgrade(rp, upgradeProposalId, opts) if err != nil { diff --git a/rocketpool/api/upgrade/routes.go b/rocketpool/api/upgrade/routes.go new file mode 100644 index 000000000..4eb0912ce --- /dev/null +++ b/rocketpool/api/upgrade/routes.go @@ -0,0 +1,45 @@ +package upgrade + +import ( + "net/http" + "strconv" + + "github.com/urfave/cli/v3" + + "github.com/rocket-pool/smartnode/shared/services" + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" + cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" +) + +// RegisterRoutes registers the upgrade module's HTTP routes onto mux. +func RegisterRoutes(mux *http.ServeMux, c *cli.Command) { + mux.HandleFunc("/api/upgrade/get-upgrade-proposals", func(w http.ResponseWriter, r *http.Request) { + resp, err := getUpgradeProposals(c) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/upgrade/can-execute-upgrade", func(w http.ResponseWriter, r *http.Request) { + id, err := cliutils.ValidatePositiveUint("upgrade proposal ID", r.URL.Query().Get("id")) + if err != nil { + apiutils.WriteResponse(w, nil, err) + return + } + resp, err := canExecuteUpgrade(c, id) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/upgrade/execute-upgrade", func(w http.ResponseWriter, r *http.Request) { + id, err := strconv.ParseUint(r.URL.Query().Get("id"), 10, 64) + if err != nil { + apiutils.WriteResponse(w, nil, err) + return + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := executeUpgrade(c, id, opts) + 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..0dc873a80 --- /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/v3" + + "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.Command) { + 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.WaitForTransactionWithContext(r.Context(), 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 c1d209863..000000000 --- a/rocketpool/api/wallet/commands.go +++ /dev/null @@ -1,353 +0,0 @@ -package wallet - -import ( - "context" - - "github.com/urfave/cli/v3" - - "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.Commands = append(command.Commands, &cli.Command{ - Name: name, - Aliases: aliases, - Usage: "Manage the node wallet", - Commands: []*cli.Command{ - - { - Name: "status", - Aliases: []string{"s"}, - Usage: "Get the node wallet status", - UsageText: "rocketpool api wallet status", - Action: func(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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", - Aliases: []string{"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(ctx context.Context, c *cli.Command) 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", - Aliases: []string{"k"}, - Usage: "Recover the node wallet, but do not regenerate its validator keys", - }, - &cli.StringFlag{ - Name: "derivation-path", - Aliases: []string{"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.Uint64Flag{ - Name: "wallet-index", - Aliases: []string{"i"}, - Usage: "Specify the index to use with the derivation path when recovering your wallet", - Value: 0, - }, - }, - Action: func(ctx context.Context, c *cli.Command) 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", - Aliases: []string{"k"}, - Usage: "Recover the node wallet, but do not regenerate its validator keys", - }, - }, - Action: func(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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", - Aliases: []string{"k"}, - Usage: "Recover the node wallet, but do not regenerate its validator keys", - }, - &cli.StringFlag{ - Name: "derivation-path", - Aliases: []string{"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.Uint64Flag{ - Name: "wallet-index", - Aliases: []string{"i"}, - Usage: "Specify the index to use with the derivation path when recovering your wallet", - Value: 0, - }, - }, - Action: func(ctx context.Context, c *cli.Command) 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", - Aliases: []string{"k"}, - Usage: "Recover the node wallet, but do not regenerate its validator keys", - }, - }, - Action: func(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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(ctx context.Context, c *cli.Command) 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/ens-name.go b/rocketpool/api/wallet/ens-name.go index a36fb0308..6dc147ca7 100644 --- a/rocketpool/api/wallet/ens-name.go +++ b/rocketpool/api/wallet/ens-name.go @@ -3,6 +3,7 @@ package wallet import ( "fmt" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/types/api" @@ -16,7 +17,7 @@ const ( ) // Set a name to the node wallet's ENS reverse record. -func setEnsName(c *cli.Command, name string, onlyEstimateGas bool) (*api.SetEnsNameResponse, error) { +func setEnsName(c *cli.Command, name string, onlyEstimateGas bool, opts *bind.TransactOpts) (*api.SetEnsNameResponse, error) { rp, err := services.GetRocketPool(c) if err != nil { return nil, err @@ -54,12 +55,6 @@ func setEnsName(c *cli.Command, name string, onlyEstimateGas bool) (*api.SetEnsN return nil, fmt.Errorf("error: the ENS record already points to the name '%s'", name) } - // Get transactor - opts, err := w.GetNodeAccountTransactor() - if err != nil { - return nil, err - } - // If onlyEstimateGas is set, then don't send the tx, only simulates and returns the gas estimate opts.NoSend = onlyEstimateGas diff --git a/rocketpool/api/wallet/init.go b/rocketpool/api/wallet/init.go index 70bf5d4bf..19bd1167c 100644 --- a/rocketpool/api/wallet/init.go +++ b/rocketpool/api/wallet/init.go @@ -11,6 +11,10 @@ import ( ) func initWallet(c *cli.Command) (*api.InitWalletResponse, error) { + return initWalletWithPath(c, c.String("derivation-path")) +} + +func initWalletWithPath(c *cli.Command, derivationPath string) (*api.InitWalletResponse, error) { // Get services w, err := services.GetWallet(c) @@ -27,7 +31,7 @@ func initWallet(c *cli.Command) (*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 d7622732c..5bfee2262 100644 --- a/rocketpool/api/wallet/recover.go +++ b/rocketpool/api/wallet/recover.go @@ -19,6 +19,10 @@ const ( ) func recoverWallet(c *cli.Command, 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.Command, 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.Command, 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.Command, 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.Command, 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.Command, 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.Command, mnemonic string) (*api.RecoverWalletResponse, } func searchAndRecoverWallet(c *cli.Command, mnemonic string, address common.Address) (*api.SearchAndRecoverWalletResponse, error) { + return searchAndRecoverWalletWithParams(c, mnemonic, address, c.Bool("skip-validator-key-recovery")) +} + +func searchAndRecoverWalletWithParams(c *cli.Command, 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.Command, 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.Command, 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..3d8059935 --- /dev/null +++ b/rocketpool/api/wallet/routes.go @@ -0,0 +1,115 @@ +package wallet + +import ( + "net/http" + "strconv" + + "github.com/ethereum/go-ethereum/common" + "github.com/urfave/cli/v3" + + "github.com/rocket-pool/smartnode/shared/services" + 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.Command) { + 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") + } + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := setEnsName(c, name, true, opts) + apiutils.WriteResponse(w, resp, err) + }) + + mux.HandleFunc("/api/wallet/set-ens-name", func(w http.ResponseWriter, r *http.Request) { + name := r.FormValue("name") + opts, err := services.GetNodeAccountTransactorFromRequest(c, r) + if err != nil { + apiutils.WriteErrorResponse(w, err) + return + } + resp, err := setEnsName(c, name, false, opts) + apiutils.WriteResponse(w, resp, err) + }) +} diff --git a/rocketpool/api/wallet/test.go b/rocketpool/api/wallet/test.go index ce124c5cc..639e819aa 100644 --- a/rocketpool/api/wallet/test.go +++ b/rocketpool/api/wallet/test.go @@ -14,6 +14,10 @@ import ( ) func testRecoverWallet(c *cli.Command, 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.Command, 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.Command, 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.Command, 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.Command, 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.Command, 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.Command, mnemonic string) (*api.RecoverWalletRespo } func testSearchAndRecoverWallet(c *cli.Command, mnemonic string, address common.Address) (*api.SearchAndRecoverWalletResponse, error) { + return testSearchAndRecoverWalletWithParams(c, mnemonic, address, c.Bool("skip-validator-key-recovery")) +} + +func testSearchAndRecoverWalletWithParams(c *cli.Command, 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.Command, 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.Command, 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..f6a62831f --- /dev/null +++ b/rocketpool/node/http.go @@ -0,0 +1,84 @@ +package node + +import ( + "context" + "fmt" + "log" + "net/http" + "time" + + "github.com/urfave/cli/v3" + + "github.com/rocket-pool/smartnode/rocketpool/node/routes" + "github.com/rocket-pool/smartnode/shared/services/config" +) + +type httpServer struct { + server *http.Server + mux *http.ServeMux +} + +// statusRecorder wraps http.ResponseWriter to capture the written status code. +type statusRecorder struct { + http.ResponseWriter + status int +} + +func (r *statusRecorder) WriteHeader(code int) { + r.status = code + r.ResponseWriter.WriteHeader(code) +} + +// Write implements the ResponseWriter interface so we don't lose the original +// Write behavior when wrapping. +func (r *statusRecorder) Write(b []byte) (int, error) { + return r.ResponseWriter.Write(b) +} + +// loggingMiddleware logs method, path, status code, and elapsed time for every request. +func loggingMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + rec := &statusRecorder{ResponseWriter: w, status: http.StatusOK} + next.ServeHTTP(rec, r) + log.Printf("%s %s %d %s", r.Method, r.URL.Path, rec.status, time.Since(start)) + }) +} + +// 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.Command, 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 + } + + var host string + if !cfg.IsNativeMode { + // In Docker mode the server must bind to 0.0.0.0, so other containers can reach it. + 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: loggingMiddleware(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/metrics-exporter.go b/rocketpool/node/metrics-exporter.go index ae53576a1..f9fb13f30 100644 --- a/rocketpool/node/metrics-exporter.go +++ b/rocketpool/node/metrics-exporter.go @@ -1,6 +1,7 @@ package node import ( + "context" "fmt" "net/http" "os" @@ -16,7 +17,7 @@ import ( "github.com/urfave/cli/v3" ) -func runMetricsServer(c *cli.Command, logger log.ColorLogger, stateLocker *collectors.StateLocker) error { +func runMetricsServer(ctx context.Context, c *cli.Command, logger log.ColorLogger, stateLocker *collectors.StateLocker) error { // Get services cfg, err := services.GetConfig(c) @@ -105,8 +106,9 @@ func runMetricsServer(c *cli.Command, logger log.ColorLogger, stateLocker *colle } logger.Printlnf("Starting metrics exporter on %s:%d.", metricsAddress, metricsPort) metricsPath := "/metrics" - http.Handle(metricsPath, handler) - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + mux := http.NewServeMux() + mux.Handle(metricsPath, handler) + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(` Rocket Pool Metrics Exporter @@ -116,8 +118,17 @@ func runMetricsServer(c *cli.Command, logger log.ColorLogger, stateLocker *colle `, )) }) - err = http.ListenAndServe(fmt.Sprintf("%s:%d", metricsAddress, metricsPort), nil) - if err != nil { + srv := &http.Server{ + Addr: fmt.Sprintf("%s:%d", metricsAddress, metricsPort), + Handler: mux, + } + + go func() { + <-ctx.Done() + _ = srv.Shutdown(context.Background()) + }() + + if err = srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { return fmt.Errorf("Error running HTTP server: %w", err) } diff --git a/rocketpool/node/node.go b/rocketpool/node/node.go index ccf6c362f..3670d6163 100644 --- a/rocketpool/node/node.go +++ b/rocketpool/node/node.go @@ -7,8 +7,10 @@ import ( "math/big" "net/http" "os" + "os/signal" "path/filepath" "sync" + "syscall" "time" "github.com/ethereum/go-ethereum/common" @@ -85,16 +87,35 @@ func run(c *cli.Command) error { // Configure configureHTTP() + // Load config early so we can start the HTTP API server before blocking + // on wallet/service readiness. + cfg, err := services.GetConfig(c) + if err != nil { + return err + } + + // Print the current mode + if cfg.IsNativeMode { + fmt.Println("Starting node daemon in Native Mode.") + } else { + fmt.Println("Starting node daemon in Docker Mode.") + } + + // 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 immediately so the CLI can reach it while + // the daemon waits for the wallet and services to become ready. + startHTTP(ctx, c, cfg) + // Wait until the node wallet stored on disk is registered if err := services.WaitNodeRegistered(c, true); err != nil { return err } // Get services - cfg, err := services.GetConfig(c) - if err != nil { - return err - } rp, err := services.GetRocketPool(c) if err != nil { return err @@ -115,13 +136,6 @@ func run(c *cli.Command) error { fmt.Printf("Protocol version: %s\n", protocolVersion) - // Print the current mode - if cfg.IsNativeMode { - fmt.Println("Starting node daemon in Native Mode.") - } else { - fmt.Println("Starting node daemon in Docker Mode.") - } - nodeAccount, err := w.GetNodeAccount() if err != nil { return fmt.Errorf("error getting node account: %w", err) @@ -203,16 +217,26 @@ func run(c *cli.Command) error { // Run task loop go func() { + defer wg.Done() // we assume clients are synced on startup so that we don't send unnecessary alerts wasExecutionClientSynced := true wasBeaconClientSynced := true for { + // Exit if the process received SIGINT/SIGTERM + select { + case <-ctx.Done(): + return + default: + } + // Check the EC status err := services.WaitEthClientSynced(c, false) // Force refresh the primary / fallback EC status if err != nil { wasExecutionClientSynced = false errorLog.Printlnf("Execution client not synced: %s. Waiting for sync...", err.Error()) - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } continue } @@ -228,7 +252,9 @@ func run(c *cli.Command) error { // NOTE: if not synced, it returns an error - so there isn't necessarily an underlying issue wasBeaconClientSynced = false errorLog.Printlnf("Beacon client not synced: %s. Waiting for sync...", err.Error()) - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } continue } @@ -242,7 +268,9 @@ func run(c *cli.Command) error { newProtocolVersion, err := utils.GetCurrentVersion(rp, nil) if err != nil { errorLog.Println(err) - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } continue } if newProtocolVersion.Compare(protocolVersion) != 0 { @@ -255,7 +283,9 @@ func run(c *cli.Command) error { state, err := updateNetworkState(m, &updateLog, nodeAccount.Address) if err != nil { errorLog.Println(err) - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } continue } stateLocker.UpdateState(state) @@ -264,7 +294,9 @@ func run(c *cli.Command) error { if err := manageFeeRecipient.run(state); err != nil { errorLog.Println(err) } - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } // Run the defend challenge exit task if err := defendChallengeExit.run(state); err != nil { @@ -275,20 +307,26 @@ func run(c *cli.Command) error { if err := downloadRewardsTrees.run(state); err != nil { errorLog.Println(err) } - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } // Run the pDAO proposal defender if err := defendPdaoProps.run(state); err != nil { errorLog.Println(err) } - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } // Run the pDAO proposal verifier if verifyPdaoProps != nil { if err := verifyPdaoProps.run(state); err != nil { errorLog.Println(err) } - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } } // Run the megapool prestake check @@ -296,63 +334,79 @@ func run(c *cli.Command) error { if err := prestakeMegapoolValidator.run(state); err != nil { errorLog.Println(err) } - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } } // Run the megapool stake check if err := stakeMegapoolValidators.run(state); err != nil { errorLog.Println(err) } - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } // Run the megapool notify validator exit check if err := notifyValidatorExit.run(state); err != nil { errorLog.Println(err) } - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } // Run the megapool notify final balance check if err := notifyFinalBalance.run(state); err != nil { errorLog.Println(err) } - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } // Run the megapool provision express ticket check if err := provisionExpressTickets.run(state); err != nil { errorLog.Println(err) } - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } // Run the balance distribution check if err := distributeMinipools.run(state); err != nil { errorLog.Println(err) } - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } // Run the set use latest delegate check if err := setUseLatestDelegate.run(state); err != nil { errorLog.Println(err) } - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } // Run the port connectivity check if err := checkPorts.Run(); err != nil { errorLog.Println(err) } - time.Sleep(taskCooldown) + if !sleepWithContext(ctx, taskCooldown) { + return + } - time.Sleep(tasksInterval) + if !sleepWithContext(ctx, tasksInterval) { + return + } } - wg.Done() }() // Run metrics loop go func() { - err := runMetricsServer(c, log.NewColorLogger(MetricsColor), stateLocker) - if err != nil { + defer wg.Done() + if err := runMetricsServer(ctx, c, log.NewColorLogger(MetricsColor), stateLocker); err != nil { errorLog.Println(err) } - wg.Done() }() // Wait for both threads to stop @@ -360,6 +414,16 @@ func run(c *cli.Command) error { return nil } +// sleepWithContext sleeps for d or until ctx is cancelled, returning false if cancelled. +func sleepWithContext(ctx context.Context, d time.Duration) bool { + select { + case <-ctx.Done(): + return false + case <-time.After(d): + return true + } +} + // Configure HTTP transport settings func configureHTTP() { // The daemon makes a large number of concurrent RPC requests to the Eth1 client diff --git a/rocketpool/node/routes/routes.go b/rocketpool/node/routes/routes.go new file mode 100644 index 000000000..fdbb6940a --- /dev/null +++ b/rocketpool/node/routes/routes.go @@ -0,0 +1,53 @@ +package routes + +import ( + "net/http" + + "github.com/urfave/cli/v3" + + apiroutes "github.com/rocket-pool/smartnode/rocketpool/api" + auctionroutes "github.com/rocket-pool/smartnode/rocketpool/api/auction" + debugroutes "github.com/rocket-pool/smartnode/rocketpool/api/debug" + 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" + upgraderoutes "github.com/rocket-pool/smartnode/rocketpool/api/upgrade" + walletroutes "github.com/rocket-pool/smartnode/rocketpool/api/wallet" + apiutils "github.com/rocket-pool/smartnode/shared/utils/api" +) + +// RegisterRoutes registers all HTTP API routes onto mux. +// Each migration branch adds additional module registrations here. +func RegisterRoutes(mux *http.ServeMux, c *cli.Command) { + 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) + debugroutes.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) + upgraderoutes.RegisterRoutes(mux, c) + walletroutes.RegisterRoutes(mux, c) + + // Catch-all: any path not matched by a specific route gets a JSON 404. + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + apiutils.WriteErrorResponse(w, &apiutils.NotFoundError{Path: r.URL.Path}) + }) + +} diff --git a/rocketpool/rocketpool.go b/rocketpool/rocketpool.go index 9cdadceb3..f2edbf96c 100644 --- a/rocketpool/rocketpool.go +++ b/rocketpool/rocketpool.go @@ -7,11 +7,9 @@ import ( cli "github.com/urfave/cli/v3" - "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" ) @@ -53,10 +51,6 @@ func main() { Aliases: []string{"l"}, Usage: "Desired gas limit", }, - &cli.StringFlag{ - Name: "nonce", - Usage: "Use this flag to explicitly specify the nonce that this transaction should use, so it can override an existing 'stuck' transaction", - }, &cli.StringFlag{ Name: "metricsAddress", Aliases: []string{"m"}, @@ -83,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(ctx context.Context, c *cli.Command) (context.Context, error) { - commandName = c.Args().First() - return ctx, nil - } - // Run application if err := app.Run(context.Background(), 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 cc9df1387..fa098cb39 100644 --- a/shared/services/config/rocket-pool-config.go +++ b/shared/services/config/rocket-pool-config.go @@ -1254,6 +1254,11 @@ func (cfg *RocketPoolConfig) GetECStopSignal() (string, error) { return "", fmt.Errorf("Unknown Execution Client %s", string(cfg.ExecutionClient.Value.(config.ExecutionClient))) } +func (cfg *RocketPoolConfig) GetNodeOpenPorts() string { + 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..f4d92a2d5 100644 --- a/shared/services/config/smartnode-config.go +++ b/shared/services/config/smartnode-config.go @@ -114,6 +114,9 @@ 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"` + /////////////////////////// // Non-editable settings // /////////////////////////// @@ -413,6 +416,17 @@ 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, + }, + txWatchUrl: map[config.Network]string{ config.Network_Mainnet: "https://etherscan.io/tx", config.Network_Devnet: "https://hoodi.etherscan.io/tx", @@ -642,6 +656,7 @@ func (cfg *SmartnodeConfig) GetParameters() []*config.Parameter { &cfg.ArchiveECUrl, &cfg.WatchtowerMaxFeeOverride, &cfg.WatchtowerPrioFeeOverride, + &cfg.APIPort, } } diff --git a/shared/services/ec-manager.go b/shared/services/ec-manager.go index a6fc89def..931ca7576 100644 --- a/shared/services/ec-manager.go +++ b/shared/services/ec-manager.go @@ -409,13 +409,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 @@ -427,8 +435,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/gas/gas.go b/shared/services/gas/gas.go index 146f06094..b90340be5 100644 --- a/shared/services/gas/gas.go +++ b/shared/services/gas/gas.go @@ -180,7 +180,7 @@ func GetHeadlessMaxFeeWeiWithLatestBlock(cfg *config.RocketPoolConfig, rp *rocke func handleEtherscanGasPrices(gasSuggestion etherscan.GasFeeSuggestion, gasInfo rocketpool.GasInfo, priorityFee float64, gasLimit uint64) float64 { fastGwei := gasSuggestion.FastGwei + priorityFee - fastEth := gasSuggestion.FastGwei / eth.WeiPerGwei + fastEth := fastGwei / eth.WeiPerGwei var fastLowLimit float64 var fastHighLimit float64 @@ -193,7 +193,7 @@ func handleEtherscanGasPrices(gasSuggestion etherscan.GasFeeSuggestion, gasInfo } standardGwei := gasSuggestion.StandardGwei + priorityFee - standardEth := gasSuggestion.StandardGwei / eth.WeiPerGwei + standardEth := standardGwei / eth.WeiPerGwei var standardLowLimit float64 var standardHighLimit float64 @@ -206,7 +206,7 @@ func handleEtherscanGasPrices(gasSuggestion etherscan.GasFeeSuggestion, gasInfo } slowGwei := gasSuggestion.SlowGwei + priorityFee - slowEth := gasSuggestion.SlowGwei / eth.WeiPerGwei + slowEth := slowGwei / eth.WeiPerGwei var slowLowLimit float64 var slowHighLimit float64 @@ -229,7 +229,7 @@ func handleEtherscanGasPrices(gasSuggestion etherscan.GasFeeSuggestion, gasInfo color.LightBluePrintln("+====================================================+") fmt.Println() - fmt.Printf("These prices include a maximum priority fee of %.3f gwei.\n", priorityFee) + fmt.Printf("These prices include a maximum priority fee of %.4f gwei.\n", priorityFee) for { desiredPrice := prompt.Prompt( diff --git a/shared/services/requirements.go b/shared/services/requirements.go index cad320a2a..8a9646131 100644 --- a/shared/services/requirements.go +++ b/shared/services/requirements.go @@ -264,7 +264,9 @@ func getRocketStorageLoaded(c *cli.Command) (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.Command, 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.Command, 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/rewards/execution-client.go b/shared/services/rewards/execution-client.go index 081ef50de..a494b71f6 100644 --- a/shared/services/rewards/execution-client.go +++ b/shared/services/rewards/execution-client.go @@ -65,8 +65,8 @@ func (client *defaultRewardsExecutionClient) GetRewardsEvent(index uint64, rocke TrustedNodeRPL: eventHouston.TrustedNodeRPL, SubmissionTime: eventHouston.SubmissionTime, NodeRPL: eventHouston.NodeRPL, - NodeETH: rewardsEvent.NodeETH, - UserETH: rewardsEvent.UserETH, + NodeETH: eventHouston.NodeETH, + UserETH: eventHouston.UserETH, } return found, event, nil diff --git a/shared/services/rocketpool/api.go b/shared/services/rocketpool/api.go index 42ccddd01..ccc80b24e 100644 --- a/shared/services/rocketpool/api.go +++ b/shared/services/rocketpool/api.go @@ -1,16 +1,18 @@ package rocketpool import ( + "context" "fmt" + "net/url" "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" "github.com/rocket-pool/smartnode/shared/types/api" ) -// Wait for a transaction +// Wait for a transaction — no timeout; blocks until the tx is included or the caller cancels. func (c *Client) WaitForTransaction(txHash common.Hash) (api.APIResponse, error) { - responseBytes, err := c.callAPI(fmt.Sprintf("wait %s", txHash.String())) + responseBytes, err := c.callHTTPAPICtx(context.Background(), "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 b99bd878b..51299916d 100644 --- a/shared/services/rocketpool/client.go +++ b/shared/services/rocketpool/client.go @@ -2,22 +2,27 @@ package rocketpool import ( "bufio" + "bytes" + "context" "errors" "fmt" "io" "io/fs" "math" "math/big" + "net/http" + "net/url" "os" "os/exec" "path/filepath" "slices" + "strconv" "strings" + "sync" "time" "github.com/fatih/color" "github.com/goccy/go-json" - "golang.org/x/crypto/ssh" "github.com/alessio/shellescape" "github.com/blang/semver/v4" @@ -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" @@ -78,13 +80,12 @@ type Globals struct { // Rocket Pool client type Client struct { - globals Globals - client *ssh.Client - originalMaxFee float64 - originalMaxPrioFee float64 - originalGasLimit uint64 - ignoreSyncCheck bool - forceFallbacks bool + globals Globals + + // 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 { @@ -113,7 +114,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 } @@ -132,7 +132,6 @@ func checkClientStatus(rp *Client) (bool, error) { clicolor.YellowPrintf("\tPrimary EC status: %s\n", primaryEcStatus) clicolor.YellowPrintf("\tPrimary CC status: %s\n", primaryBcStatus) fmt.Println() - rp.SetClientStatusFlags(true, true) return true, nil } @@ -157,12 +156,7 @@ func NewClient() *Client { // Return client client := &Client{ - globals: Defaults, - originalMaxFee: Defaults.MaxFee, - originalMaxPrioFee: Defaults.MaxPrioFee, - originalGasLimit: Defaults.GasLimit, - forceFallbacks: false, - ignoreSyncCheck: false, + globals: Defaults, } return client @@ -196,18 +190,8 @@ func (c *Client) WithReady() (*Client, error) { return c, nil } -// Close client remote connection -func (c *Client) Close() { - if c == nil { - return - } - - if c.client == nil { - return - } - - _ = c.client.Close() -} +// Close is a no-op retained for interface compatibility. +func (c *Client) Close() {} func (c *Client) ConfigPath() string { return c.globals.ConfigPath @@ -538,40 +522,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.globals.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.globals.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. @@ -1030,11 +1003,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) @@ -1150,7 +1118,6 @@ func (c *Client) deployTemplates(cfg *config.RocketPoolConfig, rocketpoolDir str // These containers always run toDeploy := []string{ - config.ApiContainerName, config.NodeContainerName, config.WatchtowerContainerName, config.ValidatorContainerName, @@ -1264,154 +1231,106 @@ 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.globals.DaemonPath == "" { - containerName, err := c.getAPIContainerName() - 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.globals.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.globals.DaemonPath == "" { - envArgs := "" - for key, value := range envVars { - os.Setenv(key, shellescape.Quote(value)) - envArgs += fmt.Sprintf("-e %s ", key) - } - 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 %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)) + return } - cmd = fmt.Sprintf("%s %s --settings %s %s %s %s %s api %s", - envArgs, - c.globals.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) + port, ok := cfg.Smartnode.APIPort.Value.(uint16) + if !ok || port == 0 { + return } - } - - ignoreSyncCheckFlag := "" - if c.ignoreSyncCheck { - ignoreSyncCheckFlag = "--ignore-sync-check" - } - forceFallbacksFlag := "" - if c.forceFallbacks { - forceFallbacksFlag = "--force-fallbacks" - } + c.apiURL = fmt.Sprintf("http://127.0.0.1:%d", port) + }) + return c.apiURL +} - return ignoreSyncCheckFlag, forceFallbacksFlag, args +// 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 +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) } -func (c *Client) runApiCall(cmd string) ([]byte, error) { - if c.globals.DebugPrint { - fmt.Println("To API:") - fmt.Println(cmd) +// 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)") } - output, err := c.readOutput(cmd) + target := base + path - if c.globals.DebugPrint { - if output != nil { - fmt.Println("API Out:") - fmt.Println(string(output)) + var req *http.Request + var err error + switch method { + case http.MethodGet: + if len(params) > 0 { + target += "?" + params.Encode() } - if err != nil { - fmt.Println("API Err:") - fmt.Println(err.Error()) + req, err = http.NewRequestWithContext(ctx, http.MethodGet, target, nil) + case http.MethodPost: + if params == nil { + params = url.Values{} } + // Propagate the gas settings + if c.globals.MaxFee > 0 { + params.Set("maxFee", strconv.FormatFloat(c.globals.MaxFee, 'f', -1, 64)) + } + if c.globals.MaxPrioFee > 0 { + params.Set("maxPrioFee", strconv.FormatFloat(c.globals.MaxPrioFee, 'f', -1, 64)) + } + if c.globals.GasLimit > 0 { + params.Set("gasLimit", strconv.FormatFloat(float64(c.globals.GasLimit), 'f', 0, 64)) + } + if c.globals.CustomNonce != nil { + params.Set("nonce", c.globals.CustomNonce.String()) + } + 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) + } + if err != nil { + return nil, fmt.Errorf("error building HTTP request for %s %s: %w", method, path, err) } - // Reset the gas settings after the call - c.globals.MaxFee = c.originalMaxFee - c.globals.MaxPrioFee = c.originalMaxPrioFee - c.globals.GasLimit = c.originalGasLimit - - return output, err -} + if c.globals.DebugPrint { + fmt.Printf("HTTP API: %s %s\n", method, target) + } -// Get the API container name -func (c *Client) getAPIContainerName() (string, error) { - cfg, _, err := c.LoadConfig() + resp, err := http.DefaultClient.Do(req) if err != nil { - return "", err + return nil, fmt.Errorf("error calling HTTP API %s %s: %w", method, path, err) } - if cfg.Smartnode.ProjectName.Value == "" { - return "", errors.New("Rocket Pool docker project name not set") + defer resp.Body.Close() + + responseBytes, err := io.ReadAll(resp.Body) + if err != nil { + 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.globals.MaxFee) - opts += fmt.Sprintf("--maxPrioFee %f ", c.globals.MaxPrioFee) - opts += fmt.Sprintf("--gasLimit %d ", c.globals.GasLimit) - return opts -} + if c.globals.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.globals.CustomNonce != nil { - nonce = fmt.Sprintf("--nonce %s", c.globals.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/command.go b/shared/services/rocketpool/command.go index 508d23e4a..1a9a56577 100644 --- a/shared/services/rocketpool/command.go +++ b/shared/services/rocketpool/command.go @@ -3,128 +3,44 @@ package rocketpool import ( "io" "os/exec" - - "golang.org/x/crypto/ssh" ) -// A command to be executed either locally or remotely +// A command to be executed locally type command struct { cmd *exec.Cmd - session *ssh.Session cmdText string } -// Create a command to be run by the Rocket Pool client func (c *Client) newCommand(cmdText string) (*command, error) { - if c.client == nil { - return &command{ - cmd: exec.Command("sh", "-c", cmdText), - cmdText: cmdText, - }, nil - } - - session, err := c.client.NewSession() - if err != nil { - return nil, err - } return &command{ - session: session, + cmd: exec.Command("sh", "-c", cmdText), cmdText: cmdText, }, nil } -// Close the command session -func (c *command) Close() error { - if c.session != nil { - return c.session.Close() - } - return nil -} +func (c *command) Close() error { return nil } -// Run the command -func (c *command) Run() error { - if c.cmd != nil { - return c.cmd.Run() - } +func (c *command) Run() error { return c.cmd.Run() } - return c.session.Run(c.cmdText) -} +func (c *command) Start() error { return c.cmd.Start() } -// Start executes the command. Don't forget to call Wait -func (c *command) Start() error { - if c.cmd != nil { - return c.cmd.Start() - } +func (c *command) Wait() error { return c.cmd.Wait() } - return c.session.Start(c.cmdText) -} +func (c *command) SetStdin(r io.Reader) { c.cmd.Stdin = r } +func (c *command) SetStdout(w io.Writer) { c.cmd.Stdout = w } +func (c *command) SetStderr(w io.Writer) { c.cmd.Stderr = w } -// Wait for the command to exit -func (c *command) Wait() error { - if c.cmd != nil { - return c.cmd.Wait() - } +func (c *command) Output() ([]byte, error) { return c.cmd.Output() } - return c.session.Wait() -} +func (c *command) StdoutPipe() (io.Reader, error) { return c.cmd.StdoutPipe() } -func (c *command) SetStdin(r io.Reader) { - if c.cmd != nil { - c.cmd.Stdin = r - } else { - c.session.Stdin = r - } -} +func (c *command) StderrPipe() (io.Reader, error) { return c.cmd.StderrPipe() } -func (c *command) SetStdout(w io.Writer) { - if c.cmd != nil { - c.cmd.Stdout = w - } else { - c.session.Stdout = w - } -} - -func (c *command) SetStderr(w io.Writer) { - if c.cmd != nil { - c.cmd.Stderr = w - } else { - c.session.Stderr = w - } -} - -// Run the command and return its output -func (c *command) Output() ([]byte, error) { - if c.cmd != nil { - return c.cmd.Output() - } - - return c.session.Output(c.cmdText) -} - -// Get a pipe to the command's stdout -func (c *command) StdoutPipe() (io.Reader, error) { - if c.cmd != nil { - return c.cmd.StdoutPipe() - } - return c.session.StdoutPipe() -} - -// Get a pipe to the command's stderr -func (c *command) StderrPipe() (io.Reader, error) { - if c.cmd != nil { - return c.cmd.StderrPipe() - } - - return c.session.StderrPipe() -} - -// OutputPipes pipes for stdout and stderr func (c *command) OutputPipes() (io.Reader, io.Reader, error) { cmdOut, err := c.StdoutPipe() if err != nil { return nil, nil, err } cmdErr, err := c.StderrPipe() - return cmdOut, cmdErr, err } diff --git a/shared/services/rocketpool/gas.go b/shared/services/rocketpool/gas.go index 789c03c33..96dad910e 100644 --- a/shared/services/rocketpool/gas.go +++ b/shared/services/rocketpool/gas.go @@ -17,7 +17,7 @@ func (rp *Client) PrintMultiTxWarning() { // Get the gas price from the latest block func (c *Client) GetGasPriceFromLatestBlock() (api.GasPriceFromLatestBlockResponse, error) { - responseBytes, err := c.callAPI("service get-gas-price-from-latest-block") + responseBytes, err := c.callHTTPAPI("GET", "/api/service/get-gas-price-from-latest-block", nil) if err != nil { return api.GasPriceFromLatestBlockResponse{}, fmt.Errorf("Could not get gas price from latest block: %w", err) } diff --git a/shared/services/rocketpool/megapool.go b/shared/services/rocketpool/megapool.go index 021886ac1..1fafe52cd 100644 --- a/shared/services/rocketpool/megapool.go +++ b/shared/services/rocketpool/megapool.go @@ -3,6 +3,8 @@ package rocketpool import ( "fmt" "math/big" + "net/url" + "strconv" "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" @@ -11,7 +13,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 +28,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 +47,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 +63,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 +81,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 +97,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 +113,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 +129,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 +145,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 +161,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 +175,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 +193,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 +207,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 +257,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 +271,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 +287,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 +357,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 +375,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 +391,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 +407,7 @@ func (c *Client) DelegateUpgradeMegapool(address common.Address) (api.MegapoolDe // Get the megapool's use-latest-delegate 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) } @@ -379,8 +422,8 @@ func (c *Client) GetUseLatestDelegate(address common.Address) (api.MegapoolGetUs } // Check whether a megapool can have its use-latest-delegate 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)) +func (c *Client) CanSetUseLatestDelegateMegapool(address common.Address, useLatest bool) (api.MegapoolCanSetUseLatestDelegateResponse, error) { + responseBytes, err := c.callHTTPAPI("GET", "/api/megapool/can-set-use-latest-delegate", url.Values{"address": {address.Hex()}, "setLatest": {strconv.FormatBool(useLatest)}}) if err != nil { return api.MegapoolCanSetUseLatestDelegateResponse{}, fmt.Errorf("Could not get can set use latest delegate for megapool status: %w", err) } @@ -396,7 +439,14 @@ func (c *Client) CanSetUseLatestDelegateMegapool(address common.Address, setting // Change a megapool's use-latest-delegate 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 +462,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 +478,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 +494,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 +508,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 +521,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 +542,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 +558,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 +571,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 54e6bcbca..ac4329fd2 100644 --- a/shared/services/rocketpool/minipool.go +++ b/shared/services/rocketpool/minipool.go @@ -3,6 +3,8 @@ package rocketpool import ( "fmt" "math/big" + "net/url" + "strconv" "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" @@ -12,7 +14,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 +60,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 +76,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 +92,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 +108,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 +124,7 @@ func (c *Client) StakeMinipool(address common.Address) (api.StakeMinipoolRespons // 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) } @@ -138,7 +140,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) } @@ -154,7 +156,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) } @@ -170,7 +172,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) } @@ -186,7 +188,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) } @@ -202,7 +204,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) } @@ -218,7 +220,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) } @@ -234,7 +236,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) } @@ -249,8 +251,9 @@ 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())) +func (c *Client) CanSetUseLatestDelegateMinipool(address common.Address, setLatest bool) (api.CanSetUseLatestDelegateResponse, error) { + // pass setLatest as well + responseBytes, err := c.callHTTPAPI("GET", "/api/minipool/can-set-use-latest-delegate", url.Values{"address": {address.Hex()}, "setLatest": {strconv.FormatBool(setLatest)}}) if err != nil { return api.CanSetUseLatestDelegateResponse{}, fmt.Errorf("Could not get can set use latest delegate for minipool status: %w", err) } @@ -266,7 +269,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) } @@ -282,7 +285,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) } @@ -298,7 +304,7 @@ func (c *Client) GetVanityArtifacts(depositAmount *big.Int, nodeAddress string) // 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) } @@ -314,7 +320,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) } @@ -330,7 +336,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) } @@ -346,7 +355,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) } @@ -362,7 +374,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) } @@ -378,7 +393,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) } @@ -394,7 +409,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) } 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 d425e4b40..f54aab1e2 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" @@ -16,7 +19,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) } @@ -54,9 +57,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) } @@ -72,7 +79,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) } @@ -88,7 +95,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) } @@ -104,7 +111,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) } @@ -120,7 +130,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) } @@ -136,7 +149,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) } @@ -152,7 +165,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) } @@ -168,7 +181,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) } @@ -184,7 +200,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) } @@ -200,7 +219,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) } @@ -216,7 +235,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) } @@ -232,7 +251,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) } @@ -248,7 +267,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) } @@ -264,7 +283,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) } @@ -280,7 +299,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) } @@ -296,7 +315,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) } @@ -312,7 +331,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) } @@ -328,7 +350,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) } @@ -344,7 +366,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("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) } @@ -360,7 +382,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) } @@ -376,7 +398,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) } @@ -392,7 +414,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) } @@ -408,7 +430,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) } @@ -424,7 +449,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) } @@ -440,7 +465,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("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) } @@ -456,7 +481,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) } @@ -472,7 +497,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) } @@ -488,7 +513,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) } @@ -504,7 +532,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) } @@ -520,7 +551,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("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) } @@ -536,7 +567,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("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) } @@ -552,7 +583,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) } @@ -568,7 +599,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) } @@ -585,7 +616,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) } @@ -602,7 +633,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) } @@ -618,7 +649,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) } @@ -634,7 +665,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) } @@ -650,7 +681,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) } @@ -666,7 +697,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) } @@ -682,7 +713,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) } @@ -698,7 +729,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) } @@ -714,7 +745,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) } @@ -730,7 +767,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) } @@ -746,7 +791,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) } @@ -762,7 +811,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) } @@ -779,7 +832,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) } @@ -795,7 +851,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) } @@ -811,7 +870,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) } @@ -827,7 +889,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) } @@ -843,7 +905,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) } @@ -859,7 +921,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) } @@ -875,7 +937,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) } @@ -891,7 +953,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) } @@ -907,7 +969,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) } @@ -923,7 +985,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) } @@ -939,7 +1001,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) } @@ -955,7 +1017,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) } @@ -971,7 +1033,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) } @@ -987,7 +1049,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) } @@ -1003,11 +1065,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) } @@ -1023,11 +1085,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) } @@ -1043,11 +1105,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) } @@ -1063,11 +1128,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) } @@ -1083,7 +1151,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) } @@ -1099,7 +1167,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) } @@ -1115,7 +1183,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) } @@ -1130,7 +1198,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) } @@ -1143,8 +1211,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) } @@ -1160,9 +1229,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) } @@ -1179,7 +1246,7 @@ func (c *Client) SignMessage(message string) (api.NodeSignResponse, error) { // 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) } @@ -1195,7 +1262,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) } @@ -1211,7 +1278,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) } @@ -1227,7 +1297,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) } @@ -1243,7 +1316,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) } @@ -1259,7 +1332,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) } @@ -1274,7 +1347,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) } @@ -1289,7 +1362,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) } @@ -1305,7 +1378,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) } @@ -1321,7 +1394,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) } @@ -1337,7 +1410,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 37934877b..725a9690c 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("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("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("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/upgrades.go b/shared/services/rocketpool/upgrades.go index c2bdfe3b4..3c8491f34 100644 --- a/shared/services/rocketpool/upgrades.go +++ b/shared/services/rocketpool/upgrades.go @@ -2,6 +2,7 @@ package rocketpool import ( "fmt" + "net/url" "strconv" "github.com/goccy/go-json" @@ -10,7 +11,7 @@ import ( // Get upgrade proposals func (c *Client) TNDAOUpgradeProposals() (api.TNDAOGetUpgradeProposalsResponse, error) { - responseBytes, err := c.callAPI("upgrade get-upgrade-proposals") + responseBytes, err := c.callHTTPAPI("GET", "/api/upgrade/get-upgrade-proposals", nil) if err != nil { return api.TNDAOGetUpgradeProposalsResponse{}, fmt.Errorf("Could not get upgrade proposals: %w", err) } @@ -26,7 +27,7 @@ func (c *Client) TNDAOUpgradeProposals() (api.TNDAOGetUpgradeProposalsResponse, // Check whether the node can execute a proposal func (c *Client) CanExecuteUpgradeProposal(proposalId uint64) (api.CanExecuteUpgradeProposalResponse, error) { - responseBytes, err := c.callAPI("upgrade can-execute-upgrade", strconv.FormatUint(proposalId, 10)) + responseBytes, err := c.callHTTPAPI("GET", "/api/upgrade/can-execute-upgrade", url.Values{"id": {strconv.FormatUint(proposalId, 10)}}) if err != nil { return api.CanExecuteUpgradeProposalResponse{}, fmt.Errorf("Could not check whether the node can execute upgrade proposal: %w", err) } @@ -42,7 +43,7 @@ func (c *Client) CanExecuteUpgradeProposal(proposalId uint64) (api.CanExecuteUpg // Execute a proposal func (c *Client) ExecuteUpgradeProposal(proposalId uint64) (api.ExecuteUpgradeProposalResponse, error) { - responseBytes, err := c.callAPI("upgrade execute-upgrade", strconv.FormatUint(proposalId, 10)) + responseBytes, err := c.callHTTPAPI("POST", "/api/upgrade/execute-upgrade", url.Values{"id": {strconv.FormatUint(proposalId, 10)}}) if err != nil { return api.ExecuteUpgradeProposalResponse{}, fmt.Errorf("Could not execute upgrade proposal: %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/services/services.go b/shared/services/services.go index 365d5e8d9..17619af2d 100644 --- a/shared/services/services.go +++ b/shared/services/services.go @@ -3,11 +3,14 @@ package services import ( "fmt" "math/big" + "net/http" "os" "path/filepath" + "strconv" "sync" "github.com/docker/docker/client" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/rocket-pool/smartnode/bindings/rocketpool" @@ -86,6 +89,44 @@ func GetWallet(c *cli.Command) (wallet.Wallet, error) { return getWallet(c, cfg, pm, am, false) } +// GetNodeAccountTransactorFromRequest returns a transactor for the node +// account, with gas settings overridden by values sent in the HTTP request +// body (maxFee, maxPrioFee, gasLimit fields in Gwei / uint64). This ensures +// the fee cap and tip cap the user selected interactively in the CLI are +// honoured by the daemon, which otherwise only knows about the values in the +// config file. +func GetNodeAccountTransactorFromRequest(c *cli.Command, r *http.Request) (*bind.TransactOpts, error) { + w, err := GetWallet(c) + if err != nil { + return nil, err + } + opts, err := w.GetNodeAccountTransactor() + if err != nil { + return nil, err + } + if maxFeeStr := r.FormValue("maxFee"); maxFeeStr != "" { + if maxFeeGwei, parseErr := strconv.ParseFloat(maxFeeStr, 64); parseErr == nil && maxFeeGwei > 0 { + opts.GasFeeCap = eth.GweiToWei(maxFeeGwei) + } + } + if maxPrioFeeStr := r.FormValue("maxPrioFee"); maxPrioFeeStr != "" { + if maxPrioFeeGwei, parseErr := strconv.ParseFloat(maxPrioFeeStr, 64); parseErr == nil && maxPrioFeeGwei > 0 { + opts.GasTipCap = eth.GweiToWei(maxPrioFeeGwei) + } + } + if gasLimitStr := r.FormValue("gasLimit"); gasLimitStr != "" { + if gasLimit, parseErr := strconv.ParseUint(gasLimitStr, 10, 64); parseErr == nil { + opts.GasLimit = gasLimit + } + } + if nonceStr := r.FormValue("nonce"); nonceStr != "" { + if nonce, ok := new(big.Int).SetString(nonceStr, 0); ok { + opts.Nonce = nonce + } + } + return opts, nil +} + func GetHdWallet(c *cli.Command) (wallet.Wallet, error) { cfg, err := getConfig(c) if err != nil { diff --git a/shared/types/api/debug.go b/shared/types/api/debug.go new file mode 100644 index 000000000..a38621b9d --- /dev/null +++ b/shared/types/api/debug.go @@ -0,0 +1,20 @@ +package api + +type RewardsEventResponse struct { + Status string `json:"status"` + Error string `json:"error"` + Found bool `json:"found"` + Index string `json:"index"` + ExecutionBlock string `json:"executionBlock"` + ConsensusBlock string `json:"consensusBlock"` + MerkleRoot string `json:"merkleRoot"` + IntervalsPassed string `json:"intervalsPassed"` + TreasuryRPL string `json:"treasuryRPL"` + TrustedNodeRPL []string `json:"trustedNodeRPL"` + NodeRPL []string `json:"nodeRPL"` + NodeETH []string `json:"nodeETH"` + UserETH string `json:"userETH"` + IntervalStartTime int64 `json:"intervalStartTime"` + IntervalEndTime int64 `json:"intervalEndTime"` + SubmissionTime int64 `json:"submissionTime"` +} 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..6a3bc9a84 --- /dev/null +++ b/shared/utils/api/http.go @@ -0,0 +1,87 @@ +package api + +import ( + "errors" + "fmt" + "net/http" + "reflect" + + "github.com/goccy/go-json" + + "github.com/rocket-pool/smartnode/shared/types/api" +) + +// BadRequestError signals that the caller supplied invalid input. +// WriteResponse maps it to HTTP 400 rather than 500. +type BadRequestError struct{ Err error } + +func (e *BadRequestError) Error() string { return e.Err.Error() } +func (e *BadRequestError) Unwrap() error { return e.Err } + +// NotFoundError signals that the requested resource or route does not exist. +// WriteResponse maps it to HTTP 404. +type NotFoundError struct{ Path string } + +func (e *NotFoundError) Error() string { return fmt.Sprintf("not found: %s", e.Path) } + +// 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. +// On error it writes 400 for BadRequestError and 500 for everything else. +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 + } + + statusCode := http.StatusOK + if ef.String() != "" { + var br *BadRequestError + var nf *NotFoundError + switch { + case errors.As(responseError, &br): + statusCode = http.StatusBadRequest + case errors.As(responseError, &nf): + statusCode = http.StatusNotFound + default: + statusCode = http.StatusInternalServerError + } + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(statusCode) + _, _ = w.Write(append(responseBytes, '\n')) +} + +// 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/utils/eth1/eth1.go b/shared/utils/eth1/eth1.go index 3f0260ee1..c7edf738a 100644 --- a/shared/utils/eth1/eth1.go +++ b/shared/utils/eth1/eth1.go @@ -1,7 +1,6 @@ package eth1 import ( - "context" "fmt" "math/big" @@ -11,55 +10,8 @@ import ( "github.com/rocket-pool/smartnode/bindings/rocketpool" "github.com/rocket-pool/smartnode/shared/services" "github.com/rocket-pool/smartnode/shared/services/config" - "github.com/urfave/cli/v3" ) -// Sets the nonce of the provided transaction options to the latest nonce if requested -func CheckForNonceOverride(c *cli.Command, opts *bind.TransactOpts) error { - - customNonceString := c.Root().String("nonce") - if customNonceString != "" { - customNonce, success := big.NewInt(0).SetString(customNonceString, 0) - if !success { - return fmt.Errorf("Invalid nonce: %s", customNonceString) - } - - // Do a sanity check to make sure the provided nonce is for a pending transaction - // otherwise the user is burning gas for no reason - ec, err := services.GetEthClient(c) - if err != nil { - return fmt.Errorf("Could not retrieve ETH1 client: %w", err) - } - - // Make sure it's not higher than the next available nonce - nextNonceUint, err := ec.PendingNonceAt(context.Background(), opts.From) - if err != nil { - return fmt.Errorf("Could not get next available nonce: %w", err) - } - - nextNonce := big.NewInt(0).SetUint64(nextNonceUint) - if customNonce.Cmp(nextNonce) == 1 { - return fmt.Errorf("Can't use nonce %s because it's greater than the next available nonce (%d).", customNonceString, nextNonceUint) - } - - // Make sure the nonce hasn't already been included in a block - latestProposedNonceUint, err := ec.NonceAt(context.Background(), opts.From, nil) - if err != nil { - return fmt.Errorf("Could not get latest nonce: %w", err) - } - - latestProposedNonce := big.NewInt(0).SetUint64(latestProposedNonceUint) - if customNonce.Cmp(latestProposedNonce) == -1 { - return fmt.Errorf("Can't use nonce %s because it has already been included in a block.", customNonceString) - } - - // It points to a pending transaction, so this is a valid thing to do - opts.Nonce = customNonce - } - return nil - -} - // Determines if the primary EC can be used for historical queries, or if the Archive EC is required func GetBestApiClient(primary *rocketpool.RocketPool, cfg *config.RocketPoolConfig, printMessage func(string), blockNumber *big.Int) (*rocketpool.RocketPool, error) { diff --git a/shared/version.txt b/shared/version.txt index bb13bef55..734375f89 100644 --- a/shared/version.txt +++ b/shared/version.txt @@ -1 +1 @@ -1.19.5-dev \ No newline at end of file +1.20.0-dev