diff --git a/addons/rescue_node/addon.go b/addons/rescue_node/addon.go index 936bf3ca8..46f386ab7 100644 --- a/addons/rescue_node/addon.go +++ b/addons/rescue_node/addon.go @@ -12,15 +12,10 @@ import ( "github.com/rocket-pool/smartnode/addons/rescue_node/pb" "github.com/rocket-pool/smartnode/shared/types/addons" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" ) const ( - colorReset string = "\033[0m" - colorRed string = "\033[31m" - colorGreen string = "\033[32m" - colorYellow string = "\033[33m" - colorBlue string = "\033[36m" - soloAuthValidity = 10 * time.Hour * 24 rpAuthValidity = 15 * time.Hour * 24 ) @@ -129,33 +124,33 @@ func (r *RescueNode) PrintStatusText(nodeAddr common.Address) { return } - fmt.Printf("%s=== Rescue Node Add-on Enabled ===%s\n", colorYellow, colorReset) + color.YellowPrintln("=== Rescue Node Add-on Enabled ===") // Check the Username usernameNodeAddr, err := r.getCredentialNodeId() if err != nil { - fmt.Printf("%s%v%s\n", colorRed, err, colorReset) + color.RedPrintln(err.Error()) } else { - fmt.Printf("Using a credential issued to %s%s%s.\n", colorBlue, usernameNodeAddr.String(), colorReset) + fmt.Printf("Using a credential issued to %s.\n", color.LightBlue(usernameNodeAddr.String())) if !bytes.Equal(usernameNodeAddr.Bytes(), nodeAddr.Bytes()) { - fmt.Printf("%s - WARNING: This does not match the Node Account!%s\n", colorYellow, colorReset) + color.YellowPrintln(" - WARNING: This does not match the Node Account!") } } credentialDetails, err := r.getCredentialDetails() if err != nil { - fmt.Printf("%s%v%s\n", colorRed, err, colorReset) + color.RedPrintln(err.Error()) } else { if credentialDetails.solo { - fmt.Printf("%s - WARNING: This credential was issued to a solo staker!%s\n", colorYellow, colorReset) + color.YellowPrintln(" - WARNING: This credential was issued to a solo staker!") } timeLeft := credentialDetails.GetTimeLeft().Truncate(time.Second) if timeLeft < 0 { - fmt.Printf("%sWARNING: This credential expired %s ago!%s\n", colorRed, timeLeft.String(), colorReset) + color.RedPrintf("WARNING: This credential expired %s ago!\n", timeLeft.String()) } else if timeLeft < time.Hour*24 { - fmt.Printf("%sWARNING: This credential expires in %s!%s\n", colorYellow, timeLeft.String(), colorReset) + color.YellowPrintf("WARNING: This credential expires in %s!\n", timeLeft.String()) } else { - fmt.Printf("%sThis credential expires in %s.%s\n", colorGreen, timeLeft.String(), colorReset) + color.GreenPrintf("This credential expires in %s.\n", timeLeft.String()) } } } diff --git a/rocketpool-cli/claims/claim-all.go b/rocketpool-cli/claims/claim-all.go index 649d4fe56..6a45f392f 100644 --- a/rocketpool-cli/claims/claim-all.go +++ b/rocketpool-cli/claims/claim-all.go @@ -15,19 +15,12 @@ import ( "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/rocket-pool/smartnode/shared/utils/math" "github.com/urfave/cli" ) -const ( - colorReset string = "\033[0m" - colorRed string = "\033[31m" - colorGreen string = "\033[32m" - colorYellow string = "\033[33m" - colorBlue string = "\033[36m" -) - // pendingClaim represents a single category of rewards that can be claimed. type pendingClaim struct { id int @@ -81,25 +74,27 @@ func claimAll(c *cli.Context, statusOnly bool) error { var periodicIntervalIndices []uint64 periodicRestakeResolved := false - fmt.Printf("%s============================================================%s\n", colorGreen, colorReset) - fmt.Printf("%s Available Rewards Summary %s\n", colorGreen, colorReset) - fmt.Printf("%s============================================================%s\n\n", colorGreen, colorReset) + color.GreenPrintln("============================================================") + color.GreenPrintln(" Available Rewards Summary ") + color.GreenPrintln("============================================================") + fmt.Println() // ================================================================ // 1. Megapool EL Rewards (distribute) // ================================================================ sectionID++ id := sectionID - fmt.Printf("%s--- [%d] Megapool Execution Layer Rewards ---%s\n", colorGreen, id, colorReset) + color.GreenPrintf("--- [%d] Megapool Execution Layer Rewards ---\n", id) canDistribute, err := rp.CanDistributeMegapool() if err != nil { - fmt.Printf(" %sCould not check megapool: %s%s\n\n", colorYellow, err, colorReset) + color.YellowPrintf(" Could not check megapool: %s\n", err) + fmt.Println() } else if !canDistribute.CanDistribute { if canDistribute.MegapoolNotDeployed { - fmt.Printf(" No megapool deployed.\n\n") + fmt.Println(" No megapool deployed.") } else if canDistribute.LastDistributionTime == 0 { - fmt.Printf(" No staking validators in the megapool.\n\n") + fmt.Println(" No staking validators in the megapool.") } else { reasons := []string{} if canDistribute.ExitingValidatorCount > 0 { @@ -109,26 +104,27 @@ func claimAll(c *cli.Context, statusOnly bool) error { reasons = append(reasons, fmt.Sprintf("%d validator(s) locked", canDistribute.LockedValidatorCount)) } if len(reasons) > 0 { - fmt.Printf(" Cannot distribute: %s\n\n", strings.Join(reasons, ", ")) + fmt.Printf(" Cannot distribute: %s\n", strings.Join(reasons, ", ")) } else { - fmt.Printf(" Cannot distribute at this time.\n\n") + fmt.Println(" Cannot distribute at this time.") } } + fmt.Println() } else { // Get the pending rewards breakdown pendingRewards, err := rp.CalculatePendingRewards() if err != nil { - fmt.Printf(" %sCould not calculate pending rewards: %s%s\n\n", colorYellow, err, colorReset) + color.YellowPrintf(" Could not calculate pending rewards: %s\n", err) + fmt.Println() } else { megapoolTotal := new(big.Int).Add(pendingRewards.RewardSplit.NodeRewards, pendingRewards.RefundValue) if megapoolTotal.Cmp(big.NewInt(0)) > 0 { fmt.Printf(" Node share: %.6f ETH\n", math.RoundDown(eth.WeiToEth(pendingRewards.RewardSplit.NodeRewards), 6)) if pendingRewards.RefundValue.Cmp(big.NewInt(0)) > 0 { fmt.Printf(" Refund value: %.6f ETH\n", math.RoundDown(eth.WeiToEth(pendingRewards.RefundValue), 6)) - fmt.Printf(" Total: %.6f ETH\n\n", math.RoundDown(eth.WeiToEth(megapoolTotal), 6)) - } else { - fmt.Println() + fmt.Printf(" Total: %.6f ETH\n", math.RoundDown(eth.WeiToEth(megapoolTotal), 6)) } + fmt.Println() totalEthWei.Add(totalEthWei, megapoolTotal) @@ -139,22 +135,23 @@ func claimAll(c *cli.Context, statusOnly bool) error { ethValue: megapoolTotal, gasInfo: gasInfo, execute: func() error { - fmt.Printf(" Submitting transaction...\n") + fmt.Println(" Submitting transaction...") response, err := rp.DistributeMegapool() if err != nil { return fmt.Errorf("transaction could not be submitted: %w", err) } - fmt.Printf(" Distributing megapool rewards...\n") + fmt.Println(" Distributing megapool rewards...") cliutils.PrintTransactionHash(rp, response.TxHash) if _, err = rp.WaitForTransaction(response.TxHash); err != nil { return fmt.Errorf("transaction was submitted but failed onchain: %w", err) } - fmt.Printf(" %sSuccessfully distributed megapool rewards.%s\n", colorGreen, colorReset) + color.GreenPrintln("Successfully distributed megapool rewards.") return nil }, }) } else { - fmt.Printf(" No pending rewards to distribute.\n\n") + fmt.Println(" No pending rewards to distribute.") + fmt.Println() } } } @@ -164,26 +161,31 @@ func claimAll(c *cli.Context, statusOnly bool) error { // ================================================================ sectionID++ feeDistID := sectionID - fmt.Printf("%s--- [%d] Fee Distributor ---%s\n", colorGreen, feeDistID, colorReset) + color.GreenPrintf("--- [%d] Fee Distributor ---\n", feeDistID) isInitResponse, err := rp.IsFeeDistributorInitialized() if err != nil { - fmt.Printf(" %sCould not check fee distributor: %s%s\n\n", colorYellow, err, colorReset) + color.YellowPrintf(" Could not check fee distributor: %s\n", err) + fmt.Println() } else if !isInitResponse.IsInitialized { - fmt.Printf(" Fee distributor not initialized. Run 'rocketpool node initialize-fee-distributor' first.\n\n") + fmt.Println(" Fee distributor not initialized. Run 'rocketpool node initialize-fee-distributor' first.") + fmt.Println() } else { canDistResp, err := rp.CanDistribute() if err != nil { - fmt.Printf(" %sCould not check fee distributor balance: %s%s\n\n", colorYellow, err, colorReset) + color.YellowPrintf(" Could not check fee distributor balance: %s\n", err) + fmt.Println() } else { balance := eth.WeiToEth(canDistResp.Balance) if balance == 0 { - fmt.Printf(" No balance in fee distributor.\n\n") + fmt.Println(" No balance in fee distributor.") + fmt.Println() } else { rEthShare := balance - canDistResp.NodeShare fmt.Printf(" Distributor balance: %.6f ETH\n", math.RoundDown(balance, 6)) fmt.Printf(" Your share: %.6f ETH\n", math.RoundDown(canDistResp.NodeShare, 6)) - fmt.Printf(" rETH stakers share: %.6f ETH\n\n", math.RoundDown(rEthShare, 6)) + fmt.Printf(" rETH stakers share: %.6f ETH\n", math.RoundDown(rEthShare, 6)) + fmt.Println() nodeShareWei := eth.EthToWei(canDistResp.NodeShare) totalEthWei.Add(totalEthWei, nodeShareWei) @@ -195,17 +197,17 @@ func claimAll(c *cli.Context, statusOnly bool) error { ethValue: nodeShareWei, gasInfo: gasInfo, execute: func() error { - fmt.Printf(" Submitting transaction...\n") + fmt.Println(" Submitting transaction...") response, err := rp.Distribute() if err != nil { return fmt.Errorf("transaction could not be submitted: %w", err) } - fmt.Printf(" Distributing fee distributor balance...\n") + fmt.Println(" Distributing fee distributor balance...") cliutils.PrintTransactionHash(rp, response.TxHash) if _, err = rp.WaitForTransaction(response.TxHash); err != nil { return fmt.Errorf("transaction was submitted but failed on-chain: %w", err) } - fmt.Printf(" %sSuccessfully distributed fee distributor balance.%s\n", colorGreen, colorReset) + color.GreenPrintln("Successfully distributed fee distributor balance.") return nil }, }) @@ -218,11 +220,12 @@ func claimAll(c *cli.Context, statusOnly bool) error { // ================================================================ sectionID++ minipoolID := sectionID - fmt.Printf("%s--- [%d] Minipool Balance Distribution ---%s\n", colorGreen, minipoolID, colorReset) + color.GreenPrintf("--- [%d] Minipool Balance Distribution ---\n", minipoolID) minipoolDetails, err := rp.GetDistributeBalanceDetails() if err != nil { - fmt.Printf(" %sCould not check minipool balances: %s%s\n\n", colorYellow, err, colorReset) + color.YellowPrintf(" Could not check minipool balances: %s\n", err) + fmt.Println() } else { eligibleMinipools := []api.MinipoolBalanceDistributionDetails{} for _, mp := range minipoolDetails.Details { @@ -232,7 +235,8 @@ func claimAll(c *cli.Context, statusOnly bool) error { } if len(eligibleMinipools) == 0 { - fmt.Printf(" No minipools eligible for balance distribution.\n\n") + fmt.Println(" No minipools eligible for balance distribution.") + fmt.Println() } else { // Sort by balance (highest first) sort.Slice(eligibleMinipools, func(i, j int) bool { @@ -266,7 +270,8 @@ func claimAll(c *cli.Context, statusOnly bool) error { mpTotalEth.Add(mpTotalEth, nodeAmount) } } - fmt.Printf(" Total from %d minipool(s): %.6f ETH\n\n", len(eligibleMinipools), math.RoundDown(eth.WeiToEth(mpTotalEth), 6)) + fmt.Printf(" Total from %d minipool(s): %.6f ETH\n", len(eligibleMinipools), math.RoundDown(eth.WeiToEth(mpTotalEth), 6)) + fmt.Println() totalEthWei.Add(totalEthWei, mpTotalEth) // Accumulate gas @@ -293,17 +298,17 @@ func claimAll(c *cli.Context, statusOnly bool) error { fmt.Printf(" Submitting transaction for minipool %s...\n", mp.Address.Hex()) response, err := rp.DistributeBalance(mp.Address) if err != nil { - fmt.Printf(" %sFailed to distribute minipool %s: %s%s\n", colorRed, mp.Address.Hex(), err, colorReset) + color.RedPrintf(" Failed to distribute minipool %s: %s\n", mp.Address.Hex(), err) failCount++ continue } fmt.Printf(" Distributing balance of minipool %s...\n", mp.Address.Hex()) cliutils.PrintTransactionHash(rp, response.TxHash) if _, err = rp.WaitForTransaction(response.TxHash); err != nil { - fmt.Printf(" %sTransaction failed for minipool %s: %s%s\n", colorRed, mp.Address.Hex(), err, colorReset) + color.RedPrintf(" Transaction failed for minipool %s: %s\n", mp.Address.Hex(), err) failCount++ } else { - fmt.Printf(" %sSuccessfully distributed balance of minipool %s.%s\n", colorGreen, mp.Address.Hex(), colorReset) + color.GreenPrintf("Successfully distributed balance of minipool %s.\n", mp.Address.Hex()) } } if failCount > 0 { @@ -320,13 +325,15 @@ func claimAll(c *cli.Context, statusOnly bool) error { // ================================================================ sectionID++ periodicID := sectionID - fmt.Printf("%s--- [%d] Periodic Rewards (RPL + ETH) ---%s\n", colorGreen, periodicID, colorReset) + color.GreenPrintf("--- [%d] Periodic Rewards (RPL + ETH) ---\n", periodicID) rewardsInfo, err := rp.GetRewardsInfo() if err != nil { - fmt.Printf(" %sCould not check periodic rewards: %s%s\n\n", colorYellow, err, colorReset) + color.YellowPrintf(" Could not check periodic rewards: %s\n", err) + fmt.Println() } else if !rewardsInfo.Registered { - fmt.Printf(" Node is not registered.\n\n") + fmt.Println(" Node is not registered.") + fmt.Println() } else { // Handle missing/invalid merkle trees missingIntervals := []int{} @@ -336,11 +343,11 @@ func claimAll(c *cli.Context, statusOnly bool) error { } } if len(missingIntervals) > 0 && !statusOnly { - fmt.Printf(" %sMissing or invalid Merkle tree files for intervals: %v%s\n", colorYellow, missingIntervals, colorReset) + color.YellowPrintf(" Missing or invalid Merkle tree files for intervals: %v\n", missingIntervals) if autoConfirm || prompt.Confirm(" Would you like to download the missing rewards tree files?") { cfg, _, err := rp.LoadConfig() if err != nil { - fmt.Printf(" %sCould not load config for tree download: %s%s\n", colorYellow, err, colorReset) + color.YellowPrintf(" Could not load config for tree download: %s\n", err) } else { for _, interval := range rewardsInfo.InvalidIntervals { if !interval.TreeFileExists || !interval.MerkleRootValid { @@ -356,7 +363,8 @@ func claimAll(c *cli.Context, statusOnly bool) error { // Reload rewards info rewardsInfo, err = rp.GetRewardsInfo() if err != nil { - fmt.Printf(" %sCould not reload rewards info: %s%s\n\n", colorYellow, err, colorReset) + color.YellowPrintf(" Could not reload rewards info: %s\n", err) + fmt.Println() } } } @@ -417,7 +425,7 @@ func claimAll(c *cli.Context, statusOnly bool) error { var gasInfo rocketpoolapi.GasInfo canClaim, canErr := rp.CanNodeClaimRewards(intervalIndices) if canErr != nil { - fmt.Printf(" %sWarning: could not estimate gas for periodic rewards: %s%s\n", colorYellow, canErr, colorReset) + color.YellowPrintf(" Warning: could not estimate gas for periodic rewards: %s\n", canErr) } else { gasInfo = canClaim.GasInfo } @@ -429,7 +437,7 @@ func claimAll(c *cli.Context, statusOnly bool) error { rplValue: prTotalRpl, gasInfo: gasInfo, execute: func() error { - fmt.Printf(" Submitting transaction...\n") + fmt.Println(" Submitting transaction...") var txHash common.Hash if periodicRestakeAmount == nil { response, err := rp.NodeClaimRewards(periodicIntervalIndices) @@ -444,15 +452,15 @@ func claimAll(c *cli.Context, statusOnly bool) error { } txHash = response.TxHash } - fmt.Printf(" Claiming periodic rewards...\n") + fmt.Println(" Claiming periodic rewards...") cliutils.PrintTransactionHash(rp, txHash) if _, err := rp.WaitForTransaction(txHash); err != nil { return fmt.Errorf("transaction was submitted but failed on-chain: %w", err) } if periodicRestakeAmount != nil { - fmt.Printf(" %sSuccessfully claimed rewards and restaked %.6f RPL.%s\n", colorGreen, eth.WeiToEth(periodicRestakeAmount), colorReset) + color.GreenPrintf("Successfully claimed rewards and restaked %.6f RPL.\n", eth.WeiToEth(periodicRestakeAmount)) } else { - fmt.Printf(" %sSuccessfully claimed periodic rewards.%s\n", colorGreen, colorReset) + color.GreenPrintln("Successfully claimed periodic rewards.") } return nil }, @@ -468,25 +476,30 @@ func claimAll(c *cli.Context, statusOnly bool) error { nodeStatus, err := rp.NodeStatus() if err != nil { sectionID++ - fmt.Printf("%s--- [%d] Unclaimed Rewards ---%s\n", colorGreen, sectionID, colorReset) - fmt.Printf(" %sCould not check node status: %s%s\n\n", colorYellow, err, colorReset) + color.GreenPrintf("--- [%d] Unclaimed Rewards ---\n", sectionID) + color.YellowPrintf(" Could not check node status: %s\n", err) + fmt.Println() sectionID++ - fmt.Printf("%s--- [%d] Credit Balance Withdrawal ---%s\n", colorGreen, sectionID, colorReset) - fmt.Printf(" %sCould not check node status: %s%s\n\n", colorYellow, err, colorReset) + color.GreenPrintf("--- [%d] Credit Balance Withdrawal ---\n", sectionID) + color.YellowPrintf(" Could not check node status: %s\n", err) + fmt.Println() sectionID++ - fmt.Printf("%s--- [%d] Staked ETH on Behalf Withdrawal ---%s\n", colorGreen, sectionID, colorReset) - fmt.Printf(" %sCould not check node status: %s%s\n\n", colorYellow, err, colorReset) + color.GreenPrintf("--- [%d] Staked ETH on Behalf Withdrawal ---\n", sectionID) + color.YellowPrintf(" Could not check node status: %s\n", err) + fmt.Println() } else { // --- Unclaimed Rewards --- sectionID++ unclaimedID := sectionID - fmt.Printf("%s--- [%d] Unclaimed Rewards ---%s\n", colorGreen, unclaimedID, colorReset) + color.GreenPrintf("--- [%d] Unclaimed Rewards ---\n", unclaimedID) if nodeStatus.UnclaimedRewards == nil || nodeStatus.UnclaimedRewards.Cmp(big.NewInt(0)) <= 0 { - fmt.Printf(" No unclaimed rewards.\n\n") + fmt.Println(" No unclaimed rewards.") + fmt.Println() } else { fmt.Printf(" Unclaimed rewards: %.6f ETH\n", math.RoundDown(eth.WeiToEth(nodeStatus.UnclaimedRewards), 6)) - fmt.Printf(" (Rewards distributed previously but not yet sent to withdrawal address)\n\n") + fmt.Println(" (Rewards distributed previously but not yet sent to withdrawal address)") + fmt.Println() totalEthWei.Add(totalEthWei, nodeStatus.UnclaimedRewards) nodeAddr := nodeStatus.AccountAddress @@ -494,9 +507,9 @@ func claimAll(c *cli.Context, statusOnly bool) error { var gasInfo rocketpoolapi.GasInfo canClaimOk := false if canErr != nil { - fmt.Printf(" %sWarning: could not estimate gas: %s%s\n", colorYellow, canErr, colorReset) + color.YellowPrintf(" Warning: could not estimate gas: %s\n", canErr) } else if !canClaim.CanClaim { - fmt.Printf(" %sCannot claim unclaimed rewards at this time.%s\n", colorYellow, colorReset) + color.YellowPrintln(" Cannot claim unclaimed rewards at this time.") } else { gasInfo = canClaim.GasInfo canClaimOk = true @@ -509,17 +522,17 @@ func claimAll(c *cli.Context, statusOnly bool) error { ethValue: nodeStatus.UnclaimedRewards, gasInfo: gasInfo, execute: func() error { - fmt.Printf(" Submitting transaction...\n") + fmt.Println(" Submitting transaction...") response, err := rp.ClaimUnclaimedRewards(nodeAddr) if err != nil { return fmt.Errorf("transaction could not be submitted: %w", err) } - fmt.Printf(" Claiming unclaimed rewards...\n") + fmt.Println(" Claiming unclaimed rewards...") cliutils.PrintTransactionHash(rp, response.TxHash) if _, err = rp.WaitForTransaction(response.TxHash); err != nil { return fmt.Errorf("transaction was submitted but failed on-chain: %w", err) } - fmt.Printf(" %sSuccessfully claimed unclaimed rewards.%s\n", colorGreen, colorReset) + color.GreenPrintln("Successfully claimed unclaimed rewards.") return nil }, }) @@ -529,13 +542,14 @@ func claimAll(c *cli.Context, statusOnly bool) error { // --- Credit Balance Withdrawal --- sectionID++ creditID := sectionID - fmt.Printf("%s--- [%d] Credit Balance Withdrawal ---%s\n", colorGreen, creditID, colorReset) + color.GreenPrintf("--- [%d] Credit Balance Withdrawal ---\n", creditID) if nodeStatus.CreditBalance == nil || nodeStatus.CreditBalance.Cmp(big.NewInt(0)) <= 0 { - fmt.Printf(" No credit balance available.\n\n") + fmt.Println(" No credit balance available.") + fmt.Println() } else { creditBalance := nodeStatus.CreditBalance - fmt.Printf(" Credit balance: %.6f ETH (the equivalent amount in rETH will be transferred to %s)\n\n", + fmt.Printf(" Credit balance: %.6f ETH (the equivalent amount in rETH will be transferred to %s)\n", math.RoundDown(eth.WeiToEth(creditBalance), 6), nodeStatus.PrimaryWithdrawalAddress) totalEthWei.Add(totalEthWei, creditBalance) @@ -543,12 +557,12 @@ func claimAll(c *cli.Context, statusOnly bool) error { var gasInfo rocketpoolapi.GasInfo canWithdrawOk := false if canErr != nil { - fmt.Printf(" %sWarning: could not estimate gas: %s%s\n", colorYellow, canErr, colorReset) + color.YellowPrintf(" Warning: could not estimate gas: %s\n", canErr) } else if !canWithdraw.CanWithdraw { if canWithdraw.InsufficientBalance { - fmt.Printf(" %sInsufficient credit balance.%s\n", colorYellow, colorReset) + color.YellowPrintln(" Insufficient credit balance.") } else { - fmt.Printf(" %sCannot withdraw credit at this time.%s\n", colorYellow, colorReset) + color.YellowPrintln(" Cannot withdraw credit at this time.") } } else { gasInfo = canWithdraw.GasInfo @@ -563,17 +577,17 @@ func claimAll(c *cli.Context, statusOnly bool) error { ethValue: withdrawAmount, gasInfo: gasInfo, execute: func() error { - fmt.Printf(" Submitting transaction...\n") + fmt.Println(" Submitting transaction...") response, err := rp.NodeWithdrawCredit(withdrawAmount) if err != nil { return fmt.Errorf("transaction could not be submitted: %w", err) } - fmt.Printf(" Withdrawing credit balance...\n") + fmt.Println(" Withdrawing credit balance...") cliutils.PrintTransactionHash(rp, response.TxHash) if _, err = rp.WaitForTransaction(response.TxHash); err != nil { return fmt.Errorf("transaction was submitted but failed on-chain: %w", err) } - fmt.Printf(" %sSuccessfully withdrew %.6f credit as rETH.%s\n", colorGreen, math.RoundDown(eth.WeiToEth(withdrawAmount), 6), colorReset) + color.GreenPrintf("Successfully withdrew %.6f credit as rETH.\n", math.RoundDown(eth.WeiToEth(withdrawAmount), 6)) return nil }, }) @@ -583,27 +597,29 @@ func claimAll(c *cli.Context, statusOnly bool) error { // --- Staked ETH on Behalf Withdrawal --- sectionID++ ethOnBehalfID := sectionID - fmt.Printf("%s--- [%d] Staked ETH on Behalf Withdrawal ---%s\n", colorGreen, ethOnBehalfID, colorReset) + color.GreenPrintf("--- [%d] Staked ETH on Behalf Withdrawal ---\n", ethOnBehalfID) if nodeStatus.EthOnBehalfBalance == nil || nodeStatus.EthOnBehalfBalance.Cmp(big.NewInt(0)) <= 0 { - fmt.Printf(" No ETH staked on behalf of the node.\n\n") + fmt.Println(" No ETH staked on behalf of the node.") + fmt.Println() } else { ethOnBehalf := nodeStatus.EthOnBehalfBalance - fmt.Printf(" Staked ETH on behalf: %.6f ETH\n\n", math.RoundDown(eth.WeiToEth(ethOnBehalf), 6)) + fmt.Printf(" Staked ETH on behalf: %.6f ETH\n", math.RoundDown(eth.WeiToEth(ethOnBehalf), 6)) + fmt.Println() totalEthWei.Add(totalEthWei, ethOnBehalf) canWithdraw, canErr := rp.CanNodeWithdrawEth(ethOnBehalf) var gasInfo rocketpoolapi.GasInfo canWithdrawOk := false if canErr != nil { - fmt.Printf(" %sWarning: could not estimate gas: %s%s\n", colorYellow, canErr, colorReset) + color.YellowPrintf(" Warning: could not estimate gas: %s\n", canErr) } else if !canWithdraw.CanWithdraw { if canWithdraw.InsufficientBalance { - fmt.Printf(" %sInsufficient staked ETH balance.%s\n", colorYellow, colorReset) + color.YellowPrintln(" Insufficient staked ETH balance.") } else if canWithdraw.HasDifferentWithdrawalAddress { - fmt.Printf(" %sCannot withdraw: primary withdrawal address is set and differs from the node address.%s\n", colorYellow, colorReset) + color.YellowPrintln(" Cannot withdraw: primary withdrawal address is set and differs from the node address.") } else { - fmt.Printf(" %sCannot withdraw staked ETH at this time.%s\n", colorYellow, colorReset) + color.YellowPrintln(" Cannot withdraw staked ETH at this time.") } } else { gasInfo = canWithdraw.GasInfo @@ -618,17 +634,17 @@ func claimAll(c *cli.Context, statusOnly bool) error { ethValue: withdrawAmount, gasInfo: gasInfo, execute: func() error { - fmt.Printf(" Submitting transaction...\n") + fmt.Println(" Submitting transaction...") response, err := rp.NodeWithdrawEth(withdrawAmount) if err != nil { return fmt.Errorf("transaction could not be submitted: %w", err) } - fmt.Printf(" Withdrawing staked ETH...\n") + fmt.Println(" Withdrawing staked ETH...") cliutils.PrintTransactionHash(rp, response.TxHash) if _, err = rp.WaitForTransaction(response.TxHash); err != nil { return fmt.Errorf("transaction was submitted but failed on-chain: %w", err) } - fmt.Printf(" %sSuccessfully withdrew %.6f staked ETH.%s\n", colorGreen, math.RoundDown(eth.WeiToEth(withdrawAmount), 6), colorReset) + color.GreenPrintf("Successfully withdrew %.6f staked ETH.\n", math.RoundDown(eth.WeiToEth(withdrawAmount), 6)) return nil }, }) @@ -641,13 +657,15 @@ func claimAll(c *cli.Context, statusOnly bool) error { // ================================================================ sectionID++ pdaoID := sectionID - fmt.Printf("%s--- [%d] PDAO Bond Claims ---%s\n", colorGreen, pdaoID, colorReset) + color.GreenPrintf("--- [%d] PDAO Bond Claims ---\n", pdaoID) bondsResponse, err := rp.PDAOGetClaimableBonds() if err != nil { - fmt.Printf(" %sCould not check PDAO bonds: %s%s\n\n", colorYellow, err, colorReset) + color.YellowPrintf(" Could not check PDAO bonds: %s\n", err) + fmt.Println() } else if len(bondsResponse.ClaimableBonds) == 0 { - fmt.Printf(" No claimable bonds or rewards.\n\n") + fmt.Println(" No claimable bonds or rewards.") + fmt.Println() } else { pdaoRplTotal := new(big.Int) for _, bond := range bondsResponse.ClaimableBonds { @@ -670,7 +688,7 @@ func claimAll(c *cli.Context, statusOnly bool) error { indices := getClaimIndicesForBond(bond) canResponse, canErr := rp.PDAOCanClaimBonds(bond.ProposalID, indices) if canErr != nil { - fmt.Printf(" %sWarning: could not estimate gas for proposal %d: %s%s\n", colorYellow, bond.ProposalID, canErr, colorReset) + color.YellowPrintf(" Warning: could not estimate gas for proposal %d: %s\n", bond.ProposalID, canErr) allCanClaim = false break } @@ -695,17 +713,17 @@ func claimAll(c *cli.Context, statusOnly bool) error { fmt.Printf(" Submitting transaction for proposal %d...\n", bond.ProposalID) response, err := rp.PDAOClaimBonds(bond.IsProposer, bond.ProposalID, indices) if err != nil { - fmt.Printf(" %sFailed to claim bonds from proposal %d: %s%s\n", colorRed, bond.ProposalID, err, colorReset) + color.RedPrintf(" Failed to claim bonds from proposal %d: %s\n", bond.ProposalID, err) failCount++ continue } fmt.Printf(" Claiming bonds from proposal %d...\n", bond.ProposalID) cliutils.PrintTransactionHash(rp, response.TxHash) if _, err = rp.WaitForTransaction(response.TxHash); err != nil { - fmt.Printf(" %sTransaction failed for proposal %d: %s%s\n", colorRed, bond.ProposalID, err, colorReset) + color.RedPrintf(" Transaction failed for proposal %d: %s\n", bond.ProposalID, err) failCount++ } else { - fmt.Printf(" %sSuccessfully claimed bonds from proposal %d.%s\n", colorGreen, bond.ProposalID, colorReset) + color.GreenPrintf("Successfully claimed bonds from proposal %d.\n", bond.ProposalID) } } if failCount > 0 { @@ -720,9 +738,9 @@ func claimAll(c *cli.Context, statusOnly bool) error { // ================================================================ // Summary // ================================================================ - fmt.Printf("%s============================================================%s\n", colorGreen, colorReset) - fmt.Printf("%s Totals %s\n", colorGreen, colorReset) - fmt.Printf("%s============================================================%s\n", colorGreen, colorReset) + color.GreenPrintf("============================================================\n") + color.GreenPrintf(" Totals \n") + color.GreenPrintf("============================================================\n") fmt.Printf(" ETH: %.6f\n", math.RoundDown(eth.WeiToEth(totalEthWei), 6)) fmt.Printf(" RPL: %.6f\n\n", math.RoundDown(eth.WeiToEth(totalRplWei), 6)) @@ -786,7 +804,8 @@ func claimAll(c *cli.Context, statusOnly bool) error { return nil } - fmt.Printf("\n%d claim(s) selected:\n", len(selectedClaims)) + fmt.Println() + fmt.Printf("%d claim(s) selected:\n", len(selectedClaims)) for i, claim := range selectedClaims { if v := claim.valueString(); v != "" { fmt.Printf(" %d. %s: %s\n", i+1, claim.name, v) @@ -839,6 +858,7 @@ func claimAll(c *cli.Context, statusOnly bool) error { } } } + fmt.Println() // Accumulate total gas for fee estimation var totalGasEst, totalGasSafe uint64 @@ -864,23 +884,25 @@ func claimAll(c *cli.Context, statusOnly bool) error { } // Execute selected claims - fmt.Printf("\n%sExecuting %d claim(s)...%s\n", colorBlue, len(selectedClaims), colorReset) + color.LightBluePrintf("Executing %d claim(s)...\n", len(selectedClaims)) successCount := 0 failCount := 0 skippedCount := 0 for i, claim := range selectedClaims { - fmt.Printf("\n%s[%d/%d] %s%s\n", colorBlue, i+1, len(selectedClaims), claim.name, colorReset) + fmt.Println() + color.LightBluePrintf("[%d/%d] %s\n", i+1, len(selectedClaims), claim.name) g.Assign(rp) err := claim.execute() if err != nil { failCount++ - fmt.Printf("\n %sERROR: %s%s\n", colorRed, err, colorReset) + fmt.Println() + color.RedPrintf(" ERROR: %s\n", err) // If there are more claims and we're not auto-confirming, ask whether to continue remaining := len(selectedClaims) - i - 1 if remaining > 0 { if autoConfirm { - fmt.Printf(" %sContinuing with remaining %d claim(s)...%s\n", colorYellow, remaining, colorReset) + color.YellowPrintf(" Continuing with remaining %d claim(s)...\n", remaining) } else { if !prompt.Confirm(" The above claim failed. Continue with the remaining %d claim(s)?", remaining) { skippedCount = remaining @@ -901,24 +923,24 @@ func claimAll(c *cli.Context, statusOnly bool) error { // Final summary fmt.Println() - fmt.Printf("============================================================\n") + fmt.Println("============================================================") if failCount == 0 && skippedCount == 0 { - fmt.Printf("%sAll %d claim(s) completed successfully.%s\n", colorGreen, successCount, colorReset) + color.GreenPrintf("All %d claim(s) completed successfully.\n", successCount) } else if successCount == 0 { - fmt.Printf("%sAll %d claim(s) failed.%s\n", colorRed, failCount, colorReset) + color.RedPrintf("All %d claim(s) failed.\n", failCount) if skippedCount > 0 { - fmt.Printf("%s%d claim(s) were skipped.%s\n", colorYellow, skippedCount, colorReset) + color.YellowPrintf("%d claim(s) were skipped.\n", skippedCount) } } else { - fmt.Printf("%s%d claim(s) succeeded%s, %s%d failed%s", - colorGreen, successCount, colorReset, - colorRed, failCount, colorReset) + color.GreenPrintf("%d claim(s) succeeded", successCount) + fmt.Printf(", ") + color.RedPrintf("%d claim(s) failed", failCount) if skippedCount > 0 { - fmt.Printf(", %s%d skipped%s", colorYellow, skippedCount, colorReset) + color.YellowPrintf("%d claim(s) were skipped", skippedCount) } fmt.Println(".") } - fmt.Printf("============================================================\n") + fmt.Println("============================================================") if failCount > 0 { return fmt.Errorf("%d of %d claim(s) failed", failCount, failCount+successCount) diff --git a/rocketpool-cli/megapool/deposit.go b/rocketpool-cli/megapool/deposit.go index c5ecd175b..36848b81d 100644 --- a/rocketpool-cli/megapool/deposit.go +++ b/rocketpool-cli/megapool/deposit.go @@ -13,17 +13,14 @@ import ( "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/rocket-pool/smartnode/shared/utils/math" ) // Config const ( - colorReset string = "\033[0m" - colorRed string = "\033[31m" - colorGreen string = "\033[32m" - colorYellow string = "\033[33m" - maxCount uint64 = 35 + maxCount uint64 = 35 ) func nodeMegapoolDeposit(c *cli.Context) error { @@ -138,7 +135,10 @@ func nodeMegapoolDeposit(c *cli.Context) error { fmt.Printf("The total bond requirement is %.2f ETH.\n", totalBondRequirementEth) fmt.Println() - if !(c.Bool("yes") || prompt.Confirm("%sNOTE: You are about to create %d new megapool validator(s), requiring a total of: %.2f ETH).%s\nWould you like to continue?", colorYellow, count, totalBondRequirementEth, colorReset)) { + if !(c.Bool("yes") || prompt.Confirm("%s%s", + color.YellowSprintf("NOTE: You are about to create %d new megapool validator(s), requiring a total of: %.2f ETH).\n", count, totalBondRequirementEth), + "Would you like to continue?", + )) { fmt.Println("Cancelled.") return nil } @@ -230,9 +230,10 @@ func nodeMegapoolDeposit(c *cli.Context) error { fmt.Printf("This deposit will use %.6f ETH from your credit balance plus ETH staked on your behalf and will not require any ETH from your node wallet.\n\n", eth.WeiToEth(usableCredit)) } } else { - fmt.Printf("%sNOTE: Your credit balance cannot currently be used to create a new megapool validator.\n"+ - "This may be because the deposit pool has insufficient ETH or the credit balance is not enough to cover any part of the deposit.%s\n"+ - "If you want to continue the deposit now, you will have to pay the full bond amount from your wallet.\n\n", colorYellow, colorReset) + color.YellowPrintln("NOTE: Your credit balance cannot currently be used to create a new megapool validator.") + color.YellowPrintln("This may be because the deposit pool has insufficient ETH or the credit balance is not enough to cover any part of the deposit.") + color.YellowPrintln("If you want to continue the deposit now, you will have to pay the full bond amount from your wallet.") + fmt.Println() } // Prompt for confirmation if !(c.Bool("yes") || prompt.Confirm("Would you like to continue?")) { @@ -243,24 +244,23 @@ func nodeMegapoolDeposit(c *cli.Context) error { } // Check to see if eth2 is synced - colorReset := "\033[0m" - colorRed := "\033[31m" - colorYellow := "\033[33m" syncResponse, err := rp.NodeSync() if err != nil { - fmt.Printf("%s**WARNING**: Can't verify the sync status of your consensus client.\nYOU WILL LOSE ETH if your megapool validator is activated before it is fully synced.\n"+ - "Reason: %s\n%s", colorRed, err, colorReset) + color.RedPrintln("**WARNING**: Can't verify the sync status of your consensus client.") + color.RedPrintln("YOU WILL LOSE ETH if your megapool validator is activated before it is fully synced.") + color.RedPrintf("Reason: %s\n", err) } else { if syncResponse.BcStatus.PrimaryClientStatus.IsSynced { - fmt.Printf("Your consensus client is synced, you may safely create a megapool validator.\n") + fmt.Println("Your consensus client is synced, you may safely create a megapool validator.") } else if syncResponse.BcStatus.FallbackEnabled { if syncResponse.BcStatus.FallbackClientStatus.IsSynced { - fmt.Printf("Your fallback consensus client is synced, you may safely create a megapool validator.\n") + fmt.Println("Your fallback consensus client is synced, you may safely create a megapool validator.") } else { - fmt.Printf("%s**WARNING**: neither your primary nor fallback consensus clients are fully synced.\nYOU WILL LOSE ETH if your megapool validator is activated before they are fully synced.\n%s", colorRed, colorReset) + color.RedPrintln("**WARNING**: neither your primary nor fallback consensus clients are fully synced.") + color.RedPrintln("YOU WILL LOSE ETH if your megapool validator is activated before they are fully synced.") } } else { - fmt.Printf("%s**WARNING**: your primary consensus client is either not fully synced or offline and you do not have a fallback client configured.\nYOU WILL LOSE ETH if your megapool validator is activated before it is fully synced.\n%s", colorRed, colorReset) + color.RedPrintln("**WARNING**: your primary consensus client is either not fully synced or offline and you do not have a fallback client configured.") } } @@ -272,13 +272,11 @@ func nodeMegapoolDeposit(c *cli.Context) error { // Prompt for confirmation - if !(c.Bool("yes") || prompt.Confirm( - "You are about to deposit %.6f ETH to create %d new megapool validator(s).\n"+ - "%sARE YOU SURE YOU WANT TO DO THIS? %s\n", + if !(c.Bool("yes") || prompt.Confirm("You are about to deposit %.6f ETH to create %d new megapool validator(s).\n%s", math.RoundDown(eth.WeiToEth(totalBondRequirement), 6), count, - colorYellow, - colorReset)) { + color.Yellow("ARE YOU SURE YOU WANT TO DO THIS?"), + )) { fmt.Println("Cancelled.") return nil } diff --git a/rocketpool-cli/megapool/exit-validator.go b/rocketpool-cli/megapool/exit-validator.go index 2c9643746..a8c48e0b5 100644 --- a/rocketpool-cli/megapool/exit-validator.go +++ b/rocketpool-cli/megapool/exit-validator.go @@ -6,6 +6,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/urfave/cli" ) @@ -76,11 +77,13 @@ func exitValidator(c *cli.Context) error { } // Show a warning message - fmt.Printf("%sNOTE:\n", colorYellow) - fmt.Println("You are about to exit a validator. This will tell each the validator to stop all activities on the Beacon Chain.") - fmt.Println("Please continue to run your validators until each one you've exited has been processed by the exit queue.\nYou can watch their progress on the https://beaconcha.in explorer.") - fmt.Println("Your funds will be locked on the Beacon Chain until they've been withdrawn, which will happen automatically (this may take a few days).") - fmt.Printf("Once your funds have been withdrawn, you can run `rocketpool megapool notify-validator-exit` to distribute them to your withdrawal address.\n\n%s", colorReset) + color.YellowPrintln("NOTE:") + color.YellowPrintln("You are about to exit a validator. This will tell each the validator to stop all activities on the Beacon Chain.") + color.YellowPrintln("Please continue to run your validators until each one you've exited has been processed by the exit queue.") + color.YellowPrintln("You can watch their progress on the https://beaconcha.in explorer.") + color.YellowPrintln("Your funds will be locked on the Beacon Chain until they've been withdrawn, which will happen automatically (this may take a few days).") + color.YellowPrintln("Once your funds have been withdrawn, you can run `rocketpool megapool notify-validator-exit` to distribute them to your withdrawal address.") + fmt.Println() // Prompt for confirmation if !(c.Bool("yes") || prompt.Confirm("Are you sure you want to EXIT validator id %d?", validatorId)) { diff --git a/rocketpool-cli/megapool/notify-final-balance.go b/rocketpool-cli/megapool/notify-final-balance.go index e9356b8fc..dd475f6bd 100644 --- a/rocketpool-cli/megapool/notify-final-balance.go +++ b/rocketpool-cli/megapool/notify-final-balance.go @@ -12,6 +12,7 @@ import ( "github.com/rocket-pool/smartnode/shared/types/api" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/urfave/cli" ) @@ -89,7 +90,7 @@ func notifyFinalBalance(c *cli.Context) error { } } - fmt.Printf("%sFetching the beacon state to craft a final balance proof. This process can take several minutes and is CPU and memory intensive.%s", colorYellow, colorReset) + color.YellowPrintln("Fetching the beacon state to craft a final balance proof. This process can take several minutes and is CPU and memory intensive.") fmt.Println() response, err := rp.CanNotifyFinalBalance(validatorId, slot) diff --git a/rocketpool-cli/megapool/status.go b/rocketpool-cli/megapool/status.go index cdb20d463..8fd1af54d 100644 --- a/rocketpool-cli/megapool/status.go +++ b/rocketpool-cli/megapool/status.go @@ -10,13 +10,13 @@ import ( "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/math" "github.com/urfave/cli" ) const ( - TimeFormat = "2006-01-02, 15:04 -0700 MST" - colorBlue string = "\033[36m" + TimeFormat = "2006-01-02, 15:04 -0700 MST" ) func getStatus(c *cli.Context) error { @@ -59,10 +59,10 @@ func getStatus(c *cli.Context) error { } // Address, express tickets, validator count - fmt.Printf("%s=== Megapool ===%s\n", colorGreen, colorReset) - fmt.Printf("The node has a megapool deployed at %s%s%s\n", colorBlue, status.Megapool.Address, colorReset) + color.GreenPrintln("=== Megapool ===") + fmt.Printf("The node has a megapool deployed at %s\n", color.LightBlue(status.Megapool.Address.String())) if status.Megapool.DelegateExpired { - fmt.Printf("%sThe megapool delegate is expired.%s\n", colorRed, colorReset) + color.RedPrintln("The megapool delegate is expired.") fmt.Println("Upgrade your megapool delegate using 'rocketpool megapool delegate-upgrade' to view the express ticket and validator count.") } else { fmt.Printf("The node has %d express ticket(s).\n", status.Megapool.NodeExpressTicketCount) @@ -71,19 +71,19 @@ func getStatus(c *cli.Context) error { fmt.Println() // Delegate addresses - fmt.Printf("%s=== Megapool Delegate ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Megapool Delegate ===") if status.Megapool.DelegateExpired { - fmt.Printf("%sThe megapool delegate is expired.%s\n", colorRed, colorReset) - fmt.Printf("The megapool is using an expired delegate at %s%s%s\n", colorBlue, status.Megapool.DelegateAddress, colorReset) - fmt.Printf("The megapool can be upgraded to delegate %s%s%s using 'rocketpool megapool delegate-upgrade'.\n", colorBlue, status.LatestDelegate, colorReset) + color.RedPrintln("The megapool delegate is expired.") + fmt.Printf("The megapool is using an expired delegate at %s\n", color.LightBlue(status.Megapool.DelegateAddress.String())) + fmt.Printf("The megapool can be upgraded to delegate %s using 'rocketpool megapool delegate-upgrade'.\n", color.LightBlue(status.LatestDelegate.String())) } else { if status.Megapool.EffectiveDelegateAddress == status.LatestDelegate { fmt.Println("The megapool is using the latest delegate.") } else { - fmt.Printf("The megapool is using an outdated delegate at %s%s%s\n", colorBlue, status.Megapool.DelegateAddress, colorReset) - fmt.Printf("The megapool can be upgraded to delegate %s%s%s using 'rocketpool megapool delegate-upgrade'.\n", colorBlue, status.LatestDelegate, colorReset) + fmt.Printf("The megapool is using an outdated delegate at %s\n", color.LightBlue(status.Megapool.DelegateAddress.String())) + fmt.Printf("The megapool can be upgraded to delegate %s using 'rocketpool megapool delegate-upgrade'.\n", color.LightBlue(status.LatestDelegate.String())) } - fmt.Printf("The megapool's effective delegate address is %s%s%s\n", colorBlue, status.Megapool.EffectiveDelegateAddress, colorReset) + fmt.Printf("The megapool's effective delegate address is %s\n", color.LightBlue(status.Megapool.EffectiveDelegateAddress.String())) } if status.Megapool.UseLatestDelegate { @@ -91,13 +91,13 @@ func getStatus(c *cli.Context) error { } else { fmt.Println("The megapool has automatic delegate upgrades disabled. You can toggle this setting using 'rocketpool megapool set-use-latest-delegate'.") if status.Megapool.DelegateExpiry > 0 { - fmt.Printf("Your current megapool delegate expires at %sblock %d%s.\n", colorBlue, status.Megapool.DelegateExpiry, colorReset) + fmt.Printf("Your current megapool delegate expires at %s.\n", color.LightBlueSprintf("block %d", status.Megapool.DelegateExpiry)) } } fmt.Println() // Balance and network commission - fmt.Printf("%s=== Megapool Balance ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Megapool Balance ===") if !status.Megapool.DelegateExpired { totalBond := new(big.Int).Mul(status.Megapool.NodeBond, big.NewInt(8)) rpBond := new(big.Int).Sub(totalBond, status.Megapool.NodeBond) @@ -172,7 +172,7 @@ func getValidatorStatus(c *cli.Context) error { // Return if delegate is expired if status.Megapool.DelegateExpired { - fmt.Printf("%sThe megapool delegate is expired.%s\n", colorRed, colorReset) + color.RedPrintln("The megapool delegate is expired.") fmt.Println("Upgrade your megapool delegate using 'rocketpool megapool delegate-upgrade' to view the validator info.") return nil } diff --git a/rocketpool-cli/minipool/close.go b/rocketpool-cli/minipool/close.go index 549a4c5e8..964e1da31 100644 --- a/rocketpool-cli/minipool/close.go +++ b/rocketpool-cli/minipool/close.go @@ -16,14 +16,11 @@ import ( "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/rocket-pool/smartnode/shared/utils/math" ) -const ( - colorBlue string = "\033[36m" -) - func closeMinipools(c *cli.Context) error { // Get RP client @@ -41,11 +38,11 @@ func closeMinipools(c *cli.Context) error { // Post a warning about express ticket provisioning if !details.ExpressTicketsProvisioned { - if !prompt.Confirm("%sWARNING: The node has unprovisioned express queue ticket(s). Closing minipool(s) without provisioning will reduce the number of express queue tickets the node is eligible for. Please enter `yes` if you've understood this message.%s`", colorRed, colorReset) { + if !prompt.ConfirmRed("WARNING: The node has unprovisioned express queue ticket(s). Closing minipool(s) without provisioning will reduce the number of express queue tickets the node is eligible for. Please enter `yes` if you've understood this message.") { fmt.Println("Cancelled.") return nil } - if c.Bool("yes") || prompt.Confirm("%sWould you like to provision express queue tickets for the node?%s", colorYellow, colorReset) { + if c.Bool("yes") || prompt.ConfirmYellow("Would you like to provision express queue tickets for the node?") { // Check if the node can provision express tickets canProvision, err := rp.CanProvisionExpressTickets() if err != nil { @@ -81,7 +78,8 @@ func closeMinipools(c *cli.Context) error { // Exit if the fee distributor hasn't been initialized yet if !details.IsFeeDistributorInitialized { - fmt.Println("Minipools cannot be closed until your fee distributor has been initialized.\nPlease run `rocketpool node initialize-fee-distributor` first, then return here to close your minipools.") + fmt.Println("Minipools cannot be closed until your fee distributor has been initialized.") + fmt.Println("Please run `rocketpool node initialize-fee-distributor` first, then return here to close your minipools.") return nil } @@ -117,25 +115,31 @@ func closeMinipools(c *cli.Context) error { // Print ineligible ones if len(unwithdrawnMinipools) > 0 { - fmt.Printf("%sNOTE: The following minipools have not had their full balances withdrawn from the Beacon Chain yet:\n", colorBlue) + color.LightBluePrintln("NOTE: The following minipools have not had their full balances withdrawn from the Beacon Chain yet:") for _, mp := range unwithdrawnMinipools { - fmt.Printf("\t%s\n", mp.Address) + color.LightBluePrintf("\t%s\n", mp.Address) } - fmt.Printf("\nTo close them, first run `rocketpool minipool exit` on them and wait until their balances have been withdrawn.%s\n\n", colorReset) + fmt.Println() + color.LightBluePrintln("To close them, first run `rocketpool minipool exit` on them and wait until their balances have been withdrawn.") + fmt.Println() } if len(versionTooLowMinipools) > 0 { - fmt.Printf("%sWARNING: The following minipools are using an old delegate and cannot be safely closed:\n", colorYellow) + color.YellowPrintln("WARNING: The following minipools are using an old delegate and cannot be safely closed:") for _, mp := range versionTooLowMinipools { - fmt.Printf("\t%s\n", mp.Address) + color.YellowPrintf("\t%s\n", mp.Address) } - fmt.Printf("\nPlease upgrade the delegate for these minipools using `rocketpool minipool delegate-upgrade` in order to close them.%s\n\n", colorReset) + fmt.Println() + color.YellowPrintln("Please upgrade the delegate for these minipools using `rocketpool minipool delegate-upgrade` in order to close them.") + fmt.Println() } if len(balanceLessThanRefundMinipools) > 0 { - fmt.Printf("%sWARNING: The following minipools have refunds larger than their current balances and cannot be closed at this time:\n", colorYellow) + color.YellowPrintln("WARNING: The following minipools have refunds larger than their current balances and cannot be closed at this time:") for _, mp := range balanceLessThanRefundMinipools { - fmt.Printf("\t%s\n", mp.Address) + color.YellowPrintf("\t%s\n", mp.Address) } - fmt.Printf("\nIf you have recently exited their validators from the Beacon Chain, please wait until their balances have been sent to the minipools before closing them.%s\n\n", colorReset) + fmt.Println() + color.YellowPrintln("If you have recently exited their validators from the Beacon Chain, please wait until their balances have been sent to the minipools before closing them.") + fmt.Println() } // Check for closable minipools @@ -207,12 +211,15 @@ func closeMinipools(c *cli.Context) error { // If there isn't enough eth to pay back rETH holders, warn that RPL and ETH will both be penalized if distributableBalance.Cmp(minipool.UserDepositBalance) < 0 { // Less than the user deposit balance, ETH + RPL will be slashed - fmt.Printf("%sWARNING: Minipool %s has a distributable balance of %.6f ETH which is lower than the amount borrowed from the staking pool (%.6f ETH).\nPlease visit the Rocket Pool Discord's #support channel (https://discord.gg/rocketpool) if you are not expecting this.%s\n", colorRed, minipool.Address.Hex(), math.RoundDown(eth.WeiToEth(distributableBalance), 6), math.RoundDown(eth.WeiToEth(minipool.UserDepositBalance), 6), colorReset) + color.RedPrintf("WARNING: Minipool %s has a distributable balance of %.6f ETH which is lower than the amount borrowed from the staking pool (%.6f ETH).\n", minipool.Address.Hex(), math.RoundDown(eth.WeiToEth(distributableBalance), 6), math.RoundDown(eth.WeiToEth(minipool.UserDepositBalance), 6)) + color.RedPrintln("Please visit the Rocket Pool Discord's #support channel (https://discord.gg/rocketpool) if you are not expecting this.") if !c.Bool("confirm-slashing") { - fmt.Printf("\n%sIf you are *sure* you want to close the minipool anyway, rerun this command with the `--confirm-slashing` flag. Doing so WILL RESULT in both your ETH bond and your RPL collateral being slashed.%s\n", colorRed, colorReset) + fmt.Println() + color.RedPrintln("If you are *sure* you want to close the minipool anyway, rerun this command with the `--confirm-slashing` flag. Doing so WILL RESULT in both your ETH bond and your RPL collateral being slashed.") return nil } - if !prompt.ConfirmWithIAgree("\n%sYou have the `--confirm-slashing` flag enabled. Closing this minipool WILL RESULT in the complete loss of your initial ETH bond and enough of your RPL stake to cover the losses to the staking pool. Please confirm you understand this and want to continue closing the minipool.%s", colorRed, colorReset) { + fmt.Println() + if !prompt.ConfirmWithIAgree("%s", color.Red("You have the `--confirm-slashing` flag enabled. Closing this minipool WILL RESULT in the complete loss of your initial ETH bond and enough of your RPL stake to cover the losses to the staking pool. Please confirm you understand this and want to continue closing the minipool.")) { fmt.Println("Cancelled.") return nil } @@ -222,7 +229,7 @@ func closeMinipools(c *cli.Context) error { if distributableBalance.Cmp(yellowThreshold) < 0 { // More than the user deposit balance but less than 31.5, ETH will be slashed with a red warning - if !prompt.ConfirmWithIAgree("%sWARNING: Minipool %s has a distributable balance of %.6f ETH. Closing it in this state WILL RESULT in a loss of ETH. You will only receive %.6f ETH back. Please confirm you understand this and want to continue closing the minipool.%s", colorRed, minipool.Address.Hex(), math.RoundDown(eth.WeiToEth(distributableBalance), 6), math.RoundDown(eth.WeiToEth(minipool.NodeShare), 6), colorReset) { + if !prompt.ConfirmWithIAgree("%s", color.RedSprintf("WARNING: Minipool %s has a distributable balance of %.6f ETH. Closing it in this state WILL RESULT in a loss of ETH. You will only receive %.6f ETH back. Please confirm you understand this and want to continue closing the minipool.", minipool.Address.Hex(), math.RoundDown(eth.WeiToEth(distributableBalance), 6), math.RoundDown(eth.WeiToEth(minipool.NodeShare), 6))) { fmt.Println("Cancelled.") return nil } @@ -231,7 +238,7 @@ func closeMinipools(c *cli.Context) error { } if distributableBalance.Cmp(thirtyTwo) < 0 { // More than 31.5 but less than 32, ETH will be slashed with a yellow warning - if !prompt.Confirm("%sWARNING: Minipool %s has a distributable balance of %.6f ETH. Closing it in this state WILL RESULT in a loss of ETH. You will only receive %.6f ETH back. Please confirm you understand this and want to continue closing the minipool.%s", colorYellow, minipool.Address.Hex(), math.RoundDown(eth.WeiToEth(distributableBalance), 6), math.RoundDown(eth.WeiToEth(minipool.NodeShare), 6), colorReset) { + if !prompt.ConfirmYellow("WARNING: Minipool %s has a distributable balance of %.6f ETH. Closing it in this state WILL RESULT in a loss of ETH. You will only receive %.6f ETH back. Please confirm you understand this and want to continue closing the minipool.", minipool.Address.Hex(), math.RoundDown(eth.WeiToEth(distributableBalance), 6), math.RoundDown(eth.WeiToEth(minipool.NodeShare), 6)) { fmt.Println("Cancelled.") return nil } diff --git a/rocketpool-cli/minipool/distribute.go b/rocketpool-cli/minipool/distribute.go index fd553288e..b42d29508 100644 --- a/rocketpool-cli/minipool/distribute.go +++ b/rocketpool-cli/minipool/distribute.go @@ -16,6 +16,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/rocket-pool/smartnode/shared/utils/math" ) @@ -65,25 +66,31 @@ func distributeBalance(c *cli.Context) error { // Print ineligible ones if len(versionTooLowMinipools) > 0 { - fmt.Printf("%sWARNING: The following minipools are using an old delegate and cannot have their rewards safely distributed:\n", colorYellow) + color.YellowPrintln("WARNING: The following minipools are using an old delegate and cannot have their rewards safely distributed:") for _, mp := range versionTooLowMinipools { - fmt.Printf("\t%s\n", mp.Address) + color.YellowPrintf("\t%s\n", mp.Address) } - fmt.Printf("\nPlease upgrade the delegate for these minipools using `rocketpool minipool delegate-upgrade` in order to distribute their ETH balances.%s\n\n", colorReset) + fmt.Println() + color.YellowPrintln("Please upgrade the delegate for these minipools using `rocketpool minipool delegate-upgrade` in order to distribute their ETH balances.") + fmt.Println() } if len(balanceLessThanRefundMinipools) > 0 { - fmt.Printf("%sWARNING: The following minipools have refunds larger than their current balances and cannot be distributed at this time:\n", colorYellow) + color.YellowPrintln("WARNING: The following minipools have refunds larger than their current balances and cannot be distributed at this time:") for _, mp := range balanceLessThanRefundMinipools { - fmt.Printf("\t%s\n", mp.Address) + color.YellowPrintf("\t%s\n", mp.Address) } - fmt.Printf("\nIf you have recently migrated these minipools from solo validators, please wait until enough rewards have been sent from the Beacon Chain to your minipools to cover your refund amounts.%s\n\n", colorReset) + fmt.Println() + color.YellowPrintln("If you have recently migrated these minipools from solo validators, please wait until enough rewards have been sent from the Beacon Chain to your minipools to cover your refund amounts.") + fmt.Println() } if len(balanceTooBigMinipools) > 0 { - fmt.Printf("%sWARNING: The following minipools have over 8 ETH in their balances (after accounting for refunds):\n", colorYellow) + color.YellowPrintln("WARNING: The following minipools have over 8 ETH in their balances (after accounting for refunds):") for _, mp := range balanceTooBigMinipools { - fmt.Printf("\t%s\n", mp.Address) + color.YellowPrintf("\t%s\n", mp.Address) } - fmt.Printf("\nDistributing these minipools will close them, effectively terminating them. If you're sure you want to do this, please use `rocketpool minipool close` on them instead.%s\n\n", colorReset) + fmt.Println() + color.YellowPrintln("Distributing these minipools will close them, effectively terminating them. If you're sure you want to do this, please use `rocketpool minipool close` on them instead.") + fmt.Println() } if len(eligibleMinipools) == 0 { diff --git a/rocketpool-cli/minipool/exit.go b/rocketpool-cli/minipool/exit.go index 784a9157d..2fef45907 100644 --- a/rocketpool-cli/minipool/exit.go +++ b/rocketpool-cli/minipool/exit.go @@ -10,6 +10,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" ) @@ -86,11 +87,13 @@ func exitMinipools(c *cli.Context) error { } // Show a warning message - fmt.Printf("%sNOTE:\n", colorYellow) - fmt.Println("You are about to exit your minipool. This will tell each one's validator to stop all activities on the Beacon Chain.") - fmt.Println("Please continue to run your validators until each one you've exited has been processed by the exit queue.\nYou can watch their progress on the https://beaconcha.in explorer.") - fmt.Println("Your funds will be locked on the Beacon Chain until they've been withdrawn, which will happen automatically (this may take a few days).") - fmt.Printf("Once your funds have been withdrawn, you can run `rocketpool minipool close` to distribute them to your withdrawal address and close the minipool.\n\n%s", colorReset) + color.YellowPrintln("NOTE:") + color.YellowPrintln("You are about to exit your minipool. This will tell each one's validator to stop all activities on the Beacon Chain.") + color.YellowPrintln("Please continue to run your validators until each one you've exited has been processed by the exit queue.") + color.YellowPrintln("You can watch their progress on the https://beaconcha.in explorer.") + color.YellowPrintln("Your funds will be locked on the Beacon Chain until they've been withdrawn, which will happen automatically (this may take a few days).") + color.YellowPrintln("Once your funds have been withdrawn, you can run `rocketpool minipool close` to distribute them to your withdrawal address and close the minipool.") + fmt.Println() // Prompt for confirmation if !(c.Bool("yes") || prompt.ConfirmWithIAgree("Are you sure you want to exit %d minipool(s)? This action cannot be undone!", len(selectedMinipools))) { diff --git a/rocketpool-cli/minipool/rescue-dissolved.go b/rocketpool-cli/minipool/rescue-dissolved.go index 92ce8d032..ab516af16 100644 --- a/rocketpool-cli/minipool/rescue-dissolved.go +++ b/rocketpool-cli/minipool/rescue-dissolved.go @@ -16,6 +16,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/rocket-pool/smartnode/shared/utils/math" ) @@ -86,25 +87,31 @@ func rescueDissolved(c *cli.Context) error { // Print ineligible ones if len(versionTooLowMinipools) > 0 { - fmt.Printf("%sWARNING: The following minipools are using an old delegate and cannot be safely rescued:\n", colorYellow) + color.YellowPrintf("WARNING: The following minipools are using an old delegate and cannot be safely rescued:\n") for _, mp := range versionTooLowMinipools { - fmt.Printf("\t%s\n", mp.Address) + color.YellowPrintf("\t%s\n", mp.Address) } - fmt.Printf("\nPlease upgrade the delegate for these minipools using `rocketpool minipool delegate-upgrade` before rescuing them.%s\n\n", colorReset) + fmt.Println() + color.YellowPrintln("Please upgrade the delegate for these minipools using `rocketpool minipool delegate-upgrade` before rescuing them.") + fmt.Println() } if len(balanceCompletedMinipools) > 0 { - fmt.Printf("%sNOTE: The following minipools already have 32 ETH or more deposited:\n", colorYellow) + color.YellowPrintf("NOTE: The following minipools already have 32 ETH or more deposited:\n") for _, mp := range balanceCompletedMinipools { - fmt.Printf("\t%s\n", mp.Address) + color.YellowPrintf("\t%s\n", mp.Address) } - fmt.Printf("\nThese minipools don't need to be rescued.%s\n\n", colorReset) + fmt.Println() + color.YellowPrintln("These minipools don't need to be rescued.") + fmt.Println() } if len(invalidBeaconStateMinipools) > 0 { - fmt.Printf("%sNOTE: The following minipools have an invalid state on the Beacon Chain (expected 'initialized_pending'):\n", colorYellow) + color.YellowPrintf("NOTE: The following minipools have an invalid state on the Beacon Chain (expected 'initialized_pending'):\n") for _, mp := range invalidBeaconStateMinipools { - fmt.Printf("\t%s (%s)\n", mp.Address, mp.BeaconState) + color.YellowPrintf("\t%s (%s)\n", mp.Address, mp.BeaconState) } - fmt.Printf("\nThese minipools cannot currently be rescued.%s\n\n", colorReset) + fmt.Println() + color.YellowPrintln("These minipools cannot currently be rescued.") + fmt.Println() } // Check for rescuable minipools @@ -113,7 +120,9 @@ func rescueDissolved(c *cli.Context) error { return nil } - fmt.Printf("%sNOTE: the amounts required for completion below use the validator balances according to the Beacon Chain.\nIf you have recently sent a rescue deposit to this minipool, please wait until it has been registered with the Beacon Chain for these remaining amounts to be accurate.%s\n\n", colorYellow, colorReset) + color.YellowPrintln("NOTE: the amounts required for completion below use the validator balances according to the Beacon Chain.") + color.YellowPrintln("If you have recently sent a rescue deposit to this minipool, please wait until it has been registered with the Beacon Chain for these remaining amounts to be accurate.") + fmt.Println() // Get selected minipools var selectedMinipool *api.MinipoolRescueDissolvedDetails @@ -223,7 +232,8 @@ func rescueDissolved(c *cli.Context) error { if _, err = rp.WaitForTransaction(response.TxHash); err != nil { return fmt.Errorf("Could not rescue minipool %s: %s.\n", selectedMinipool.Address.Hex(), err.Error()) } else { - fmt.Printf("Successfully deposited to minipool %s.\nPlease watch its status on a chain explorer such as https://beaconcha.in; it may take up to 24 hours for this deposit to be seen by the chain.\n", selectedMinipool.Address.Hex()) + fmt.Printf("Successfully deposited to minipool %s.\n", selectedMinipool.Address.Hex()) + fmt.Println("Please watch its status on a chain explorer such as https://beaconcha.in; it may take up to 24 hours for this deposit to be seen by the chain.") } // Return diff --git a/rocketpool-cli/minipool/status.go b/rocketpool-cli/minipool/status.go index a3bde6045..f20523ac3 100644 --- a/rocketpool-cli/minipool/status.go +++ b/rocketpool-cli/minipool/status.go @@ -12,14 +12,11 @@ import ( "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/hex" "github.com/rocket-pool/smartnode/shared/utils/math" ) -const colorReset string = "\033[0m" -const colorRed string = "\033[31m" -const colorYellow string = "\033[33m" - func getStatus(c *cli.Context) error { // Get RP client @@ -135,7 +132,7 @@ func getStatus(c *cli.Context) error { } if len(minipoolsPastDissolveNotificationThreshold) > 0 { - fmt.Printf("%sAttention! %d minipool(s) are close to being dissolved:\n%s", colorRed, len(minipoolsPastDissolveNotificationThreshold), colorReset) + color.RedPrintf("Attention! %d minipool(s) are close to being dissolved:\n", len(minipoolsPastDissolveNotificationThreshold)) for _, minipool := range minipoolsPastDissolveNotificationThreshold { fmt.Printf("- %s (%s until dissolve)\n", minipool.Address.Hex(), minipool.TimeUntilDissolve) } @@ -157,9 +154,9 @@ func printMinipoolDetails(minipool api.MinipoolDetails, latestDelegate common.Ad if minipool.Penalties == 0 { fmt.Println("Penalties: 0") } else if minipool.Penalties < 3 { - fmt.Printf("%sStrikes: %d%s\n", colorYellow, minipool.Penalties, colorReset) + color.YellowPrintf("Strikes: %d\n", minipool.Penalties) } else { - fmt.Printf("%sInfractions: %d%s\n", colorRed, minipool.Penalties, colorReset) + color.RedPrintf("Infractions: %d\n", minipool.Penalties) } fmt.Printf("Status: %s\n", minipool.Status.Status.String()) fmt.Printf("Status updated: %s\n", minipool.Status.StatusTime.Format(TimeFormat)) @@ -220,7 +217,7 @@ func printMinipoolDetails(minipool api.MinipoolDetails, latestDelegate common.Ad fmt.Printf("Effective delegate: %s\n", cliutils.GetPrettyAddress(minipool.EffectiveDelegate)) if minipool.EffectiveDelegate != latestDelegate { - fmt.Printf("%s*Minipool can be upgraded to delegate %s!%s\n", colorYellow, latestDelegate.Hex(), colorReset) + color.YellowPrintf("*Minipool can be upgraded to delegate %s!\n", latestDelegate.Hex()) } fmt.Printf("\n") diff --git a/rocketpool-cli/network/dao-proposals.go b/rocketpool-cli/network/dao-proposals.go index adb8b9061..bc335c817 100644 --- a/rocketpool-cli/network/dao-proposals.go +++ b/rocketpool-cli/network/dao-proposals.go @@ -10,6 +10,7 @@ import ( "github.com/rocket-pool/smartnode/bindings/utils/eth" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/urfave/cli" ) @@ -40,13 +41,12 @@ func getActiveDAOProposals(c *cli.Context) error { } // Voting status - fmt.Printf("%s=== Snapshot Voting ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Snapshot Voting ===") blankAddress := common.Address{} if snapshotProposalsResponse.SignallingAddress == blankAddress { fmt.Printf("The node does not currently have a snapshot signalling address set.\nTo learn more about snapshot signalling, please visit %s.\n", signallingAddressLink) } else { - fmt.Printf("The node has a signalling address of %s%s%s which can represent it when voting on Rocket Pool Snapshot governance proposals.", colorBlue, snapshotProposalsResponse.SignallingAddressFormatted, colorReset) - fmt.Println() + fmt.Println("The node has a signalling address of", color.LightBlue(snapshotProposalsResponse.SignallingAddressFormatted), "which can represent it when voting on Rocket Pool Snapshot governance proposals.") } voteCount := snapshotProposalsResponse.SnapshotResponse.VoteCount() @@ -120,12 +120,12 @@ func getActiveDAOProposals(c *cli.Context) error { votedChoices = fmt.Sprintf("%v", proposalVote.Choice) } - fmt.Printf("%s%s voted [%s] on this proposal\n%s", colorGreen, voter, votedChoices, colorReset) + color.GreenPrintf("%s voted [%s] on this proposal\n", voter, votedChoices) voted = true } } if !voted { - fmt.Printf("%sYou have NOT voted on this proposal yet\n%s", colorYellow, colorReset) + color.YellowPrintln("You have NOT voted on this proposal yet") } } @@ -133,7 +133,7 @@ func getActiveDAOProposals(c *cli.Context) error { fmt.Println() // Onchain Voting Status - fmt.Printf("%s=== Onchain Voting ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Onchain Voting ===") switch snapshotProposalsResponse.OnchainVotingDelegate { case blankAddress: @@ -141,7 +141,7 @@ func getActiveDAOProposals(c *cli.Context) error { case snapshotProposalsResponse.AccountAddress: fmt.Println("The node doesn't have a delegate, which means it can vote directly on onchain proposals. You can have another node represent you by running `rocketpool p svd
`.") default: - fmt.Printf("The node has a voting delegate of %s%s%s which can represent it when voting on Rocket Pool onchain governance proposals.\n", colorBlue, snapshotProposalsResponse.OnchainVotingDelegateFormatted, colorReset) + fmt.Println("The node has a voting delegate of", color.LightBlue(snapshotProposalsResponse.OnchainVotingDelegateFormatted), "which can represent it when voting on Rocket Pool onchain governance proposals.") } fmt.Printf("The node's local voting power: %.10f\n", eth.WeiToEth(snapshotProposalsResponse.VotingPower)) diff --git a/rocketpool-cli/network/generate-tree.go b/rocketpool-cli/network/generate-tree.go index 50b92e4a1..1a08ea33c 100644 --- a/rocketpool-cli/network/generate-tree.go +++ b/rocketpool-cli/network/generate-tree.go @@ -5,15 +5,12 @@ import ( "strconv" "github.com/rocket-pool/smartnode/shared/services/rocketpool" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/urfave/cli" ) const ( - colorReset string = "\033[0m" - colorGreen string = "\033[32m" - colorYellow string = "\033[33m" - signallingAddressLink string = "https://docs.rocketpool.net/pdao/participate#setting-your-snapshot-signalling-address" ) @@ -35,9 +32,16 @@ func generateRewardsTree(c *cli.Context) error { // Print archive node info archiveEcUrl := cfg.Smartnode.ArchiveECUrl.Value.(string) if archiveEcUrl == "" { - fmt.Printf("%sNOTE: in order to generate a Merkle rewards tree for a past rewards interval, you will likely need to have access to an Execution client with archival state.\nBy default, your Smart Node's Execution client will not provide this.\n\nPlease specify the URL of an archive-capable EC in the Smart Node section of the `rocketpool service config` Terminal UI.\nIf you need one, Alchemy provides a free service which you can use: https://www.alchemy.com/ethereum%s\n\n", colorYellow, colorReset) + color.YellowPrintln("NOTE: in order to generate a Merkle rewards tree for a past rewards interval, you will likely need to have access to an Execution client with archival state.") + color.YellowPrintln("By default, your Smart Node's Execution client will not provide this.") + fmt.Println() + color.YellowPrintln("Please specify the URL of an archive-capable EC in the Smart Node section of the `rocketpool service config` Terminal UI.") + fmt.Println() + color.YellowPrintln("If you need one, Alchemy provides a free service which you can use: https://www.alchemy.com/ethereum") + fmt.Println() } else { - fmt.Printf("%sYou have an archive EC specified at [%s]. This will be used for tree generation.%s\n\n", colorGreen, archiveEcUrl, colorReset) + color.GreenPrintln("You have an archive EC specified at [%s]. This will be used for tree generation.", archiveEcUrl) + fmt.Println() } // Get the index @@ -77,7 +81,8 @@ func generateRewardsTree(c *cli.Context) error { return err } - fmt.Printf("Your request to generate the rewards tree for interval %d has been applied, and your `watchtower` container will begin the process during its next duty check (typically 5 minutes).\nYou can follow its progress with %s`rocketpool service logs watchtower`%s.\n\n", index, colorGreen, colorReset) + fmt.Printf("Your request to generate the rewards tree for interval %d has been applied, and your `watchtower` container will begin the process during its next duty check (typically 5 minutes).\n", index) + fmt.Println("You can follow its progress with", color.Green("`rocketpool service logs watchtower`.")) if c.Bool("yes") || prompt.Confirm("Would you like to restart the watchtower container now, so it starts generating the file immediately?") { container := fmt.Sprintf("%s_watchtower", cfg.Smartnode.ProjectName.Value.(string)) diff --git a/rocketpool-cli/network/stats.go b/rocketpool-cli/network/stats.go index 4be549579..1273beb4c 100644 --- a/rocketpool-cli/network/stats.go +++ b/rocketpool-cli/network/stats.go @@ -6,10 +6,7 @@ import ( "github.com/urfave/cli" "github.com/rocket-pool/smartnode/shared/services/rocketpool" -) - -const ( - colorBlue string = "\033[36m" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" ) func getStats(c *cli.Context) error { @@ -36,13 +33,14 @@ func getStats(c *cli.Context) error { response.DissolvedMinipoolCount // Print & return - fmt.Printf("%s========== General Stats ==========%s\n", colorGreen, colorReset) + color.GreenPrintln("========== General Stats ==========") fmt.Printf("Total Value Locked: %f ETH\n", response.TotalValueLocked) fmt.Printf("Deposit Pool Balance: %f ETH\n", response.DepositPoolBalance) fmt.Printf("Minipool Queue Demand: %f ETH\n", response.MinipoolCapacity) - fmt.Printf("Deposit Pool ETH Used: %f%%\n\n", response.StakerUtilization*100) + fmt.Printf("Deposit Pool ETH Used: %f%%\n", response.StakerUtilization*100) + fmt.Println() - fmt.Printf("%s============== Nodes ==============%s\n", colorGreen, colorReset) + color.GreenPrintln("============== Nodes ==============") fmt.Printf("Current Commission Rate: %f%%\n", response.NodeFee*100) fmt.Printf("Node Count: %d\n", response.NodeCount) fmt.Printf("Active Minipools: %d\n", activeMinipools) @@ -51,9 +49,10 @@ func getStats(c *cli.Context) error { fmt.Printf(" Staking: %d\n", response.StakingMinipoolCount) fmt.Printf(" Withdrawable: %d\n", response.WithdrawableMinipoolCount) fmt.Printf(" Dissolved: %d\n", response.DissolvedMinipoolCount) - fmt.Printf("Finalized Minipools: %d\n\n", response.FinalizedMinipoolCount) + fmt.Printf("Finalized Minipools: %d\n", response.FinalizedMinipoolCount) + fmt.Println() - fmt.Printf("%s=========== Megapools ============%s\n", colorGreen, colorReset) + color.GreenPrintln("=========== Megapools ============") fmt.Printf("Megapool contracts deployed: %d\n", response.MegapoolContractCount) fmt.Printf("Total megapool validators: %d\n", response.MegapoolValidatorCount) fmt.Printf(" Staking: %d\n", response.MegapoolValidatorStakingCount) @@ -62,19 +61,22 @@ func getStats(c *cli.Context) error { fmt.Printf(" Exited: %d\n", response.MegapoolValidatorExitedCount) fmt.Printf(" Locked: %d\n", response.MegapoolValidatorLockedCount) fmt.Printf(" Exiting: %d\n", response.MegapoolValidatorExitingCount) - fmt.Printf(" Dissolved: %d\n\n", response.MegapoolValidatorDissolvedCount) + fmt.Printf(" Dissolved: %d\n", response.MegapoolValidatorDissolvedCount) + fmt.Println() - fmt.Printf("%s========== Smoothing Pool =========%s\n", colorGreen, colorReset) - fmt.Printf("Contract Address: %s%s%s\n", colorBlue, response.SmoothingPoolAddress.Hex(), colorReset) + color.GreenPrintln("========== Smoothing Pool ==========") + fmt.Printf("Contract Address: %s\n", color.LightBlue(response.SmoothingPoolAddress.Hex())) fmt.Printf("Nodes Opted in: %d\n", response.SmoothingPoolNodes) - fmt.Printf("Pending Balance: %f\n\n", response.SmoothingPoolBalance) + fmt.Printf("Pending Balance: %f\n", response.SmoothingPoolBalance) + fmt.Println() - fmt.Printf("%s============== Tokens =============%s\n", colorGreen, colorReset) + color.GreenPrintln("============== Tokens ==============") fmt.Printf("rETH Price (ETH / rETH): %f ETH\n", response.RethPrice) fmt.Printf("RPL Price (ETH / RPL): %f ETH\n", response.RplPrice) fmt.Printf("Total RPL staked: %f RPL\n", response.TotalRplStaked) fmt.Printf("Total Megapool RPL staked: %f RPL\n", response.TotalMegapoolRplStaked) fmt.Printf("Total Legacy RPL staked: %f RPL\n", response.TotalLegacyRplStaked) + fmt.Println() return nil diff --git a/rocketpool-cli/node/claim-rewards.go b/rocketpool-cli/node/claim-rewards.go index c012b488a..469311121 100644 --- a/rocketpool-cli/node/claim-rewards.go +++ b/rocketpool-cli/node/claim-rewards.go @@ -16,13 +16,10 @@ import ( "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" ) -const ( - colorBlue string = "\033[36m" -) - func nodeClaimRewards(c *cli.Context) error { // Get RP client @@ -33,7 +30,9 @@ func nodeClaimRewards(c *cli.Context) error { defer rp.Close() // Provide a notice - fmt.Printf("%sWelcome to the new rewards system!\nYou no longer need to claim rewards at each interval - you can simply let them accumulate and claim them whenever you want.\nHere you can see which intervals you haven't claimed yet, and how many rewards you earned during each one.%s\n", colorBlue, colorReset) + color.LightBluePrintln("Welcome to the new rewards system!") + color.LightBluePrintln("You no longer need to claim rewards at each interval - you can simply let them accumulate and claim them whenever you want.") + color.LightBluePrintln("Here you can see which intervals you haven't claimed yet, and how many rewards you earned during each one.") fmt.Println() // Get eligible intervals @@ -43,7 +42,7 @@ func nodeClaimRewards(c *cli.Context) error { } if !rewardsInfoResponse.Registered { - fmt.Printf("This node is not currently registered.\n") + fmt.Println("This node is not currently registered.") return nil } @@ -63,7 +62,7 @@ func nodeClaimRewards(c *cli.Context) error { // Download the Merkle trees for all unclaimed intervals that don't exist if len(missingIntervals) > 0 || len(invalidIntervals) > 0 { fmt.Println() - fmt.Printf("%sNOTE: If you would like to regenerate these tree files manually, please answer `n` to the prompt below and run `rocketpool network generate-rewards-tree` before claiming your rewards.%s\n", colorBlue, colorReset) + color.LightBluePrintln("NOTE: If you would like to regenerate these tree files manually, please answer `n` to the prompt below and run `rocketpool network generate-rewards-tree` before claiming your rewards.") if !prompt.Confirm("Would you like to download all missing rewards tree files now?") { fmt.Println("Cancelled.") return nil diff --git a/rocketpool-cli/node/claim-unclaimed-rewards.go b/rocketpool-cli/node/claim-unclaimed-rewards.go index 493a60d6a..fe2fdb0f7 100644 --- a/rocketpool-cli/node/claim-unclaimed-rewards.go +++ b/rocketpool-cli/node/claim-unclaimed-rewards.go @@ -8,6 +8,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/rocket-pool/smartnode/shared/utils/math" "github.com/urfave/cli" @@ -32,8 +33,8 @@ func claimUnclaimedRewards(c *cli.Context) error { fmt.Printf("The node's withdrawal address is %s\n", status.PrimaryWithdrawalAddress) if status.UnclaimedRewards != nil && status.UnclaimedRewards.Cmp(big.NewInt(0)) > 0 { fmt.Printf("You have %.6f ETH in unclaimed rewards.\n", math.RoundDown(eth.WeiToEth(status.UnclaimedRewards), 6)) - fmt.Printf("Your node %s%s%s's rewards were distributed, but the withdrawal address (at the time of distribution) was unable to accept ETH. ", - colorBlue, status.AccountAddress, colorReset) + fmt.Printf("Your node %s's rewards were distributed, but the withdrawal address (at the time of distribution) was unable to accept ETH. ", + color.LightBlue(status.AccountAddress.String())) fmt.Println("Before continuing, please use the command `rocketpool node set-primary-withdrawal-address` to configure an address that can accept ETH") } else { fmt.Println("You have no unclaimed rewards.") diff --git a/rocketpool-cli/node/primary-withdrawal-address.go b/rocketpool-cli/node/primary-withdrawal-address.go index 5ea1d21fc..39ce420e4 100644 --- a/rocketpool-cli/node/primary-withdrawal-address.go +++ b/rocketpool-cli/node/primary-withdrawal-address.go @@ -11,6 +11,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" ) @@ -41,9 +42,6 @@ func setPrimaryWithdrawalAddress(c *cli.Context, withdrawalAddressOrENS string) } // Print the "pending" disclaimer - colorReset := "\033[0m" - colorRed := "\033[31m" - colorYellow := "\033[33m" var confirm bool fmt.Println("You are about to change your primary withdrawal address. All future ETH rewards/refunds will be sent there.\nIf you haven't set your RPL withdrawal address, RPL rewards will be sent there as well.") if !c.Bool("force") { @@ -51,11 +49,13 @@ func setPrimaryWithdrawalAddress(c *cli.Context, withdrawalAddressOrENS string) fmt.Println("By default, this will put your new primary withdrawal address into a \"pending\" state.") fmt.Println("Rocket Pool will continue to use your old primary withdrawal address until you confirm that you own the new address via the Rocket Pool website.") fmt.Println("You will need to use a web3-compatible wallet (such as MetaMask) with your new address to confirm it.") - fmt.Printf("%sIf you cannot use such a wallet, or if you want to bypass this step and force Rocket Pool to use the new address immediately, please re-run this command with the \"--force\" flag.\n\n%s", colorYellow, colorReset) + color.YellowPrintln("If you cannot use such a wallet, or if you want to bypass this step and force Rocket Pool to use the new address immediately, please re-run this command with the \"--force\" flag.") + fmt.Println() } else { confirm = true - fmt.Printf("%sYou have specified the \"--force\" option, so your new address will take effect immediately.\n", colorRed) - fmt.Printf("Please ensure that you have the correct address - if you do not control the new address, you will not be able to change this once set!%s\n\n", colorReset) + color.RedPrintln("You have specified the \"--force\" option, so your new address will take effect immediately.") + color.RedPrintln("Please ensure that you have the correct address - if you do not control the new address, you will not be able to change this once set!") + fmt.Println() } // Check if the withdrawal address can be set diff --git a/rocketpool-cli/node/rewards.go b/rocketpool-cli/node/rewards.go index 7d45bd3e3..83a3b0113 100644 --- a/rocketpool-cli/node/rewards.go +++ b/rocketpool-cli/node/rewards.go @@ -11,6 +11,7 @@ import ( rprewards "github.com/rocket-pool/smartnode/shared/services/rewards" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" ) @@ -50,7 +51,7 @@ func getRewards(c *cli.Context) error { // Download the Merkle trees for all unclaimed intervals that don't exist if len(missingIntervals) > 0 || len(invalidIntervals) > 0 { fmt.Println() - fmt.Printf("%sNOTE: If you would like to regenerate these tree files manually, please answer `n` to the prompt below and run `rocketpool network generate-rewards-tree` before claiming your rewards.%s\n", colorBlue, colorReset) + color.LightBluePrintln("NOTE: If you would like to regenerate these tree files manually, please answer `n` to the prompt below and run `rocketpool network generate-rewards-tree` before claiming your rewards.") if !prompt.Confirm("Would you like to download all missing rewards tree files now?") { fmt.Println("Cancelled.") return nil diff --git a/rocketpool-cli/node/rpl-withdrawal-address.go b/rocketpool-cli/node/rpl-withdrawal-address.go index 714429789..542d71c88 100644 --- a/rocketpool-cli/node/rpl-withdrawal-address.go +++ b/rocketpool-cli/node/rpl-withdrawal-address.go @@ -12,6 +12,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" ) @@ -42,9 +43,6 @@ func setRPLWithdrawalAddress(c *cli.Context, withdrawalAddressOrENS string) erro } // Print the "pending" disclaimer - colorReset := "\033[0m" - colorRed := "\033[31m" - colorYellow := "\033[33m" var confirm bool fmt.Println("You are about to change your RPL withdrawal address. All future RPL rewards/refunds will be sent there.") if !c.Bool("force") { @@ -52,11 +50,13 @@ func setRPLWithdrawalAddress(c *cli.Context, withdrawalAddressOrENS string) erro fmt.Println("By default, this will put your new RPL withdrawal address into a \"pending\" state.") fmt.Println("Rocket Pool will continue to use your old RPL withdrawal address (or your primary withdrawal address if your RPL withdrawal address has not been set) until you confirm that you own the new address via the Rocket Pool website.") fmt.Println("You will need to use a web3-compatible wallet (such as MetaMask) with your new address to confirm it.") - fmt.Printf("%sIf you cannot use such a wallet, or if you want to bypass this step and force Rocket Pool to use the new address immediately, please re-run this command with the \"--force\" flag.\n\n%s", colorYellow, colorReset) + color.YellowPrintln("If you cannot use such a wallet, or if you want to bypass this step and force Rocket Pool to use the new address immediately, please re-run this command with the \"--force\" flag.") + fmt.Println() } else { confirm = true - fmt.Printf("%sYou have specified the \"--force\" option, so your new address will take effect immediately.\n", colorRed) - fmt.Printf("Please ensure that you have the correct address - if you do not control the new address, you will not be able to change this once set!%s\n\n", colorReset) + color.RedPrintln("You have specified the \"--force\" option, so your new address will take effect immediately.") + color.RedPrintln("Please ensure that you have the correct address - if you do not control the new address, you will not be able to change this once set!") + fmt.Println() } // Check if the withdrawal address can be set @@ -123,7 +123,7 @@ func setRPLWithdrawalAddress(c *cli.Context, withdrawalAddressOrENS string) erro // Prompt for confirmation if canResponse.RPLStake.Cmp(common.Big0) == 1 { - fmt.Printf("%sNOTE: You currently have %.6f RPL staked. Withdrawing it will *no longer* send it to your primary withdrawal address. It will be sent to the new RPL withdrawal address instead. Please verify you have control over that address before confirming this!%s\n", colorYellow, eth.WeiToEth(canResponse.RPLStake), colorReset) + color.YellowPrintf("NOTE: You currently have %.6f RPL staked. Withdrawing it will *no longer* send it to your primary withdrawal address. It will be sent to the new RPL withdrawal address instead. Please verify you have control over that address before confirming this!\n", eth.WeiToEth(canResponse.RPLStake)) } if !(c.Bool("yes") || prompt.Confirm("Are you sure you want to set your node's RPL withdrawal address to %s?", withdrawalAddressString)) { fmt.Println("Cancelled.") diff --git a/rocketpool-cli/node/send.go b/rocketpool-cli/node/send.go index 37766e2d8..eaab33cd1 100644 --- a/rocketpool-cli/node/send.go +++ b/rocketpool-cli/node/send.go @@ -10,6 +10,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" ) @@ -70,7 +71,8 @@ func nodeSend(c *cli.Context, amountRaw float64, sendAll bool, token string, toA fmt.Printf("Token name: %s\n", canSend.TokenName) fmt.Printf("Token symbol: %s\n", canSend.TokenSymbol) fmt.Printf("Node balance: %.8f %s\n\n", canSend.Balance, canSend.TokenSymbol) - fmt.Printf("%sWARNING: Please confirm that the above token is the one you intend to send before confirming below!%s\n\n", colorYellow, colorReset) + color.YellowPrintln("WARNING: Please confirm that the above token is the one you intend to send before confirming below!") + fmt.Println() if !(c.Bool("yes") || prompt.Confirm("Are you sure you want to send %.8f of %s to %s? This action cannot be undone!", amountRaw, tokenString, toAddressString)) { fmt.Println("Cancelled.") @@ -188,7 +190,8 @@ func nodeSendAll(c *cli.Context, rp *rocketpool.Client, token string, toAddress fmt.Printf("Token name: %s\n", canSend.TokenName) fmt.Printf("Token symbol: %s\n", canSend.TokenSymbol) fmt.Printf("Node balance: %.8f %s\n\n", canSend.Balance, canSend.TokenSymbol) - fmt.Printf("%sWARNING: Please confirm that the above token is the one you intend to send before confirming below!%s\n\n", colorYellow, colorReset) + color.YellowPrintln("WARNING: Please confirm that the above token is the one you intend to send before confirming below!") + fmt.Println() if !(c.Bool("yes") || prompt.Confirm("Are you sure you want to send all %.8f of %s to %s? This action cannot be undone!", amountRaw, tokenString, toAddressString)) { fmt.Println("Cancelled.") diff --git a/rocketpool-cli/node/smoothing-pool.go b/rocketpool-cli/node/smoothing-pool.go index a2913ae07..358eeab63 100644 --- a/rocketpool-cli/node/smoothing-pool.go +++ b/rocketpool-cli/node/smoothing-pool.go @@ -8,6 +8,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" ) @@ -56,7 +57,9 @@ func joinSmoothingPool(c *cli.Context) error { return err } - fmt.Printf("%sNOTE: This process will restart your node's validator client.\nYou may miss an attestation if you are currently scheduled to produce one.%s\n\n", colorYellow, colorReset) + color.YellowPrintln("NOTE: This process will restart your node's validator client.") + color.YellowPrintln("You may miss an attestation if you are currently scheduled to produce one.") + fmt.Println() // Prompt for confirmation if !(c.Bool("yes") || prompt.Confirm("Are you sure you want to join the Smoothing Pool?")) { @@ -147,7 +150,9 @@ func leaveSmoothingPool(c *cli.Context) error { // Log & return fmt.Println("Successfully left the Smoothing Pool.") - fmt.Printf("%sNOTE: Your validator client will restart to change its fee recipient back to your node's distributor once the next Epoch has been finalized.\nYou may miss an attestation when this happens (or multiple if you have Doppelganger Protection enabled); this is normal.%s\n", colorYellow, colorReset) + color.YellowPrintln("NOTE: Your validator client will restart to change its fee recipient back to your node's distributor once the next Epoch has been finalized.") + color.YellowPrintln("You may miss an attestation when this happens (or multiple if you have Doppelganger Protection enabled); this is normal.") + fmt.Println() return nil } diff --git a/rocketpool-cli/node/status.go b/rocketpool-cli/node/status.go index 6d6f0cd3a..06e83b23a 100644 --- a/rocketpool-cli/node/status.go +++ b/rocketpool-cli/node/status.go @@ -15,14 +15,11 @@ import ( "github.com/rocket-pool/smartnode/addons/rescue_node" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/math" ) const ( - colorReset string = "\033[0m" - colorRed string = "\033[31m" - colorGreen string = "\033[32m" - colorYellow string = "\033[33m" smoothingPoolLink string = "https://docs.rocketpool.net/upgrades/redstone/whats-new#smoothing-pool" signallingAddressLink string = "https://docs.rocketpool.net/pdao/participate#setting-your-snapshot-signalling-address" ) @@ -79,12 +76,10 @@ func getStatus(c *cli.Context) error { } // Account address & balances - fmt.Printf("%s=== Account and Balances ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Account and Balances ===") fmt.Printf( - "The node %s%s%s has a balance of %.6f ETH, %.6f RPL, and %.6f rETH.\n", - colorBlue, - status.AccountAddressFormatted, - colorReset, + "The node %s has a balance of %.6f ETH, %.6f RPL, and %.6f rETH.\n", + color.LightBlue(status.AccountAddressFormatted), math.RoundDown(eth.WeiToEth(status.AccountBalances.ETH), 6), math.RoundDown(eth.WeiToEth(status.AccountBalances.RPL), 6), math.RoundDown(eth.WeiToEth(status.AccountBalances.RETH), 6)) @@ -108,19 +103,15 @@ func getStatus(c *cli.Context) error { } fmt.Println() - fmt.Printf("%s=== Megapool ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Megapool ===") if status.MegapoolDeployed { - fmt.Printf("The node has a megapool deployed at %s%s%s.", colorBlue, status.MegapoolAddress.Hex(), colorReset) - fmt.Println() - fmt.Printf("The megapool has %d validators.", status.MegapoolActiveValidatorCount) - fmt.Println() + fmt.Printf("The node has a megapool deployed at %s.\n", color.LightBlue(status.MegapoolAddress.Hex())) + fmt.Printf("The megapool has %d validators.\n", status.MegapoolActiveValidatorCount) if status.MegapoolNodeDebt.Cmp(big.NewInt(0)) > 0 { - fmt.Printf("The megapool debt is %.6f ETH.", math.RoundDown(eth.WeiToEth(status.MegapoolNodeDebt), 6)) - fmt.Println() + fmt.Printf("The megapool debt is %.6f ETH.\n", math.RoundDown(eth.WeiToEth(status.MegapoolNodeDebt), 6)) } if status.MegapoolRefundValue.Cmp(big.NewInt(0)) > 0 { - fmt.Printf("The megapool refund value is %.6f ETH.", math.RoundDown(eth.WeiToEth(status.MegapoolRefundValue), 6)) - fmt.Println() + fmt.Printf("The megapool refund value is %.6f ETH.\n", math.RoundDown(eth.WeiToEth(status.MegapoolRefundValue), 6)) } } else { fmt.Println("The node does not have a megapool deployed yet.") @@ -129,14 +120,14 @@ func getStatus(c *cli.Context) error { if status.ExpressTicketsProvisioned { fmt.Printf("The node has %d express queue ticket(s).", status.ExpressTicketCount) } else { - fmt.Printf("%sThe node has unprovisioned express queue ticket(s). Please provision them using the `rocketpool node provision-express-tickets` command. You are eligible for %d express tickets.%s", colorYellow, status.ExpressTicketCount, colorReset) + color.YellowPrintf("The node has unprovisioned express queue ticket(s). Please provision them using the `rocketpool node provision-express-tickets` command. You are eligible for %d express tickets.", status.ExpressTicketCount) } fmt.Println() fmt.Println() // Penalties - fmt.Printf("%s=== Penalty Status ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Penalty Status ===") if len(status.PenalizedMinipools) > 0 { strikeMinipools := []common.Address{} infractionMinipools := []common.Address{} @@ -152,11 +143,10 @@ func getStatus(c *cli.Context) error { sort.Slice(strikeMinipools, func(i, j int) bool { // Sort them lexicographically return strikeMinipools[i].Hex() < strikeMinipools[j].Hex() }) - fmt.Printf("%sWARNING: The following minipools have been given strikes for cheating with an invalid fee recipient:\n", colorYellow) + color.YellowPrintln("WARNING: The following minipools have been given strikes for cheating with an invalid fee recipient:") for _, mp := range strikeMinipools { - fmt.Printf("\t%s: %d strikes\n", mp.Hex(), status.PenalizedMinipools[mp]) + color.YellowPrintf("\t%s: %d strikes\n", mp.Hex(), status.PenalizedMinipools[mp]) } - fmt.Println(colorReset) fmt.Println() } @@ -164,11 +154,10 @@ func getStatus(c *cli.Context) error { sort.Slice(infractionMinipools, func(i, j int) bool { // Sort them lexicographically return infractionMinipools[i].Hex() < infractionMinipools[j].Hex() }) - fmt.Printf("%sWARNING: The following minipools have been given infractions for cheating with an invalid fee recipient:\n", colorRed) + color.RedPrintln("WARNING: The following minipools have been given infractions for cheating with an invalid fee recipient:") for _, mp := range infractionMinipools { - fmt.Printf("\t%s: %d infractions\n", mp.Hex(), status.PenalizedMinipools[mp]-2) + color.RedPrintf("\t%s: %d infractions\n", mp.Hex(), status.PenalizedMinipools[mp]-2) } - fmt.Println(colorReset) fmt.Println() } } else { @@ -177,12 +166,13 @@ func getStatus(c *cli.Context) error { } // Signalling Status - fmt.Printf("%s=== Signalling on Snapshot ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Signalling on Snapshot ===") blankAddress := common.Address{} if status.SignallingAddress == blankAddress { - fmt.Printf("The node does not currently have a snapshot signalling address set.\nTo learn more about snapshot signalling, please visit %s.\n", signallingAddressLink) + fmt.Println("The node does not currently have a snapshot signalling address set.") + fmt.Printf("To learn more about snapshot signalling, please visit %s.\n", signallingAddressLink) } else { - fmt.Printf("The node has a signalling address of %s%s%s which can represent it when voting on Rocket Pool Snapshot governance proposals.\n", colorBlue, status.SignallingAddressFormatted, colorReset) + fmt.Println("The node has a signalling address of", color.LightBlue(status.SignallingAddressFormatted), "which can represent it when voting on Rocket Pool Snapshot governance proposals.") } if status.SnapshotResponse.Error != "" { @@ -206,14 +196,14 @@ func getStatus(c *cli.Context) error { } // Onchain voting status - fmt.Printf("%s=== Onchain Voting ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Onchain Voting ===") if status.OnchainVotingDelegate == blankAddress { fmt.Println("The node doesn't have a delegate, which means it can vote directly on onchain proposals after it initializes voting.") } else if status.OnchainVotingDelegate == status.AccountAddress { fmt.Println("The node doesn't have a delegate, which means it can vote directly on onchain proposals. You can have another node represent you by running `rocketpool p svd `.") } else { - fmt.Printf("The node has a voting delegate of %s%s%s which can represent it when voting on Rocket Pool onchain governance proposals.\n", colorBlue, status.OnchainVotingDelegateFormatted, colorReset) + fmt.Println("The node has a voting delegate of", color.LightBlue(status.OnchainVotingDelegateFormatted), "which can represent it when voting on Rocket Pool onchain governance proposals.") } if status.IsRPLLockingAllowed { fmt.Print("The node is allowed to lock RPL to create governance proposals/challenges.\n") @@ -228,80 +218,75 @@ func getStatus(c *cli.Context) error { fmt.Println("") // Primary withdrawal address & balances - fmt.Printf("%s=== Primary Withdrawal Address ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Primary Withdrawal Address ===") if !bytes.Equal(status.AccountAddress.Bytes(), status.PrimaryWithdrawalAddress.Bytes()) { fmt.Printf( - "The node's primary withdrawal address %s%s%s has a balance of %.6f ETH and %.6f RPL.\n", - colorBlue, - status.PrimaryWithdrawalAddressFormatted, - colorReset, + "The node's primary withdrawal address %s has a balance of %.6f ETH and %.6f RPL.\n", + color.LightBlue(status.PrimaryWithdrawalAddressFormatted), math.RoundDown(eth.WeiToEth(status.PrimaryWithdrawalBalances.ETH), 6), math.RoundDown(eth.WeiToEth(status.PrimaryWithdrawalBalances.RPL), 6)) } else { - fmt.Printf("%sThe node's primary withdrawal address has not been changed, so ETH rewards and minipool withdrawals will be sent to the node itself.\n", colorYellow) - fmt.Printf("Consider changing this to a cold wallet address that you control using the `set-withdrawal-address` command.\n%s", colorReset) + color.YellowPrintln("The node's primary withdrawal address has not been changed, so ETH rewards and minipool withdrawals will be sent to the node itself.") + color.YellowPrintln("Consider changing this to a cold wallet address that you control using the `set-withdrawal-address` command.") } fmt.Println("") if status.PendingPrimaryWithdrawalAddress.Hex() != blankAddress.Hex() { - fmt.Printf("%sThe node's primary withdrawal address has a pending change to %s which has not been confirmed yet.\n", colorYellow, status.PendingPrimaryWithdrawalAddressFormatted) - fmt.Printf("Please visit the Rocket Pool website with a web3-compatible wallet to complete this change.%s\n", colorReset) + color.YellowPrintf("The node's primary withdrawal address has a pending change to %s which has not been confirmed yet.\n", status.PendingPrimaryWithdrawalAddressFormatted) + color.YellowPrintln("Please visit the Rocket Pool website with a web3-compatible wallet to complete this change.") fmt.Println("") } // RPL withdrawal address & balances - fmt.Printf("%s=== RPL Withdrawal Address ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== RPL Withdrawal Address ===") if !status.IsRPLWithdrawalAddressSet { - fmt.Printf("The node's RPL withdrawal address has not been set. All RPL rewards will be sent to the primary withdrawal address.\n") + fmt.Println("The node's RPL withdrawal address has not been set. All RPL rewards will be sent to the primary withdrawal address.") } else if bytes.Equal(status.AccountAddress.Bytes(), status.RPLWithdrawalAddress.Bytes()) { - fmt.Printf("The node's RPL withdrawal address has been explicitly set to the node address itself (%s%s%s).\n", colorBlue, status.RPLWithdrawalAddressFormatted, colorReset) + fmt.Printf("The node's RPL withdrawal address has been explicitly set to the node address itself (%s).\n", color.LightBlue(status.RPLWithdrawalAddressFormatted)) } else if bytes.Equal(status.PrimaryWithdrawalAddress.Bytes(), status.RPLWithdrawalAddress.Bytes()) { - fmt.Printf("The node's RPL withdrawal address has been explicitly set to the primary withdrawal address (%s%s%s).\n", colorBlue, status.RPLWithdrawalAddressFormatted, colorReset) + fmt.Printf("The node's RPL withdrawal address has been explicitly set to the primary withdrawal address (%s).\n", color.LightBlue(status.RPLWithdrawalAddressFormatted)) } else { fmt.Printf( - "The node's RPL withdrawal address %s%s%s has a balance of %.6f ETH and %.6f RPL.\n", - colorBlue, - status.RPLWithdrawalAddressFormatted, - colorReset, + "The node's RPL withdrawal address %s has a balance of %.6f ETH and %.6f RPL.\n", + color.LightBlue(status.RPLWithdrawalAddressFormatted), math.RoundDown(eth.WeiToEth(status.RPLWithdrawalBalances.ETH), 6), math.RoundDown(eth.WeiToEth(status.RPLWithdrawalBalances.RPL), 6)) } fmt.Println("") if status.PendingRPLWithdrawalAddress.Hex() != blankAddress.Hex() { - fmt.Printf("%sThe node's RPL withdrawal address has a pending change to %s which has not been confirmed yet.\n", colorYellow, status.PendingRPLWithdrawalAddressFormatted) - fmt.Printf("Please visit the Rocket Pool website with a web3-compatible wallet to complete this change.%s\n", colorReset) + color.YellowPrintf("The node's RPL withdrawal address has a pending change to %s which has not been confirmed yet.\n", status.PendingRPLWithdrawalAddressFormatted) + color.YellowPrintln("Please visit the Rocket Pool website with a web3-compatible wallet to complete this change.") fmt.Println("") } // Fee distributor details - fmt.Printf("%s=== Fee Distributor and Smoothing Pool ===%s\n", colorGreen, colorReset) - fmt.Printf("The node's fee distributor %s%s%s has a balance of %.6f ETH.\n", colorBlue, status.FeeRecipientInfo.FeeDistributorAddress.Hex(), colorReset, math.RoundDown(eth.WeiToEth(status.FeeDistributorBalance), 6)) + color.GreenPrintln("=== Fee Distributor and Smoothing Pool ===") + fmt.Printf("The node's fee distributor %s has a balance of %.6f ETH.\n", color.LightBlue(status.FeeRecipientInfo.FeeDistributorAddress.Hex()), math.RoundDown(eth.WeiToEth(status.FeeDistributorBalance), 6)) if cfg.IsNativeMode && !status.FeeRecipientInfo.IsInSmoothingPool && !status.FeeRecipientInfo.IsInOptOutCooldown { - fmt.Printf("%sNOTE: You are in Native Mode; you MUST ensure that your Validator Client is using this address as its fee recipient!%s\n", colorYellow, colorReset) + color.YellowPrintln("NOTE: You are in Native Mode; you MUST ensure that your Validator Client is using this address as its fee recipient!") } if !status.IsFeeDistributorInitialized { - fmt.Printf("\n%sThe fee distributor hasn't been initialized yet. When you are able, please initialize it with `rocketpool node initialize-fee-distributor`.%s\n", colorYellow, colorReset) + fmt.Println() + color.YellowPrintln("The fee distributor hasn't been initialized yet. When you are able, please initialize it with `rocketpool node initialize-fee-distributor`.") } if status.FeeRecipientInfo.IsInSmoothingPool { fmt.Printf( - "The node is currently opted into the Smoothing Pool (%s%s%s).\n", - colorBlue, - status.FeeRecipientInfo.SmoothingPoolAddress.Hex(), - colorReset) + "The node is currently opted into the Smoothing Pool (%s).\n", + color.LightBlue(status.FeeRecipientInfo.SmoothingPoolAddress.Hex()), + ) if cfg.IsNativeMode { - fmt.Printf("%sNOTE: You are in Native Mode; you MUST ensure that your Validator Client is using this address as its fee recipient!%s\n", colorYellow, colorReset) + color.YellowPrintln("NOTE: You are in Native Mode; you MUST ensure that your Validator Client is using this address as its fee recipient!") } } else if status.FeeRecipientInfo.IsInOptOutCooldown { - fmt.Printf( - "The node is currently opting out of the Smoothing Pool, but cannot safely change its fee recipient yet.\nIt must remain the Smoothing Pool's address (%s%s%s) until the opt-out process is complete.\nIt can safely be changed once Epoch %d is finalized on the Beacon Chain.\n", - colorBlue, - status.FeeRecipientInfo.SmoothingPoolAddress.Hex(), - colorReset, - status.FeeRecipientInfo.OptOutEpoch) + fmt.Println("The node is currently opting out of the Smoothing Pool, but cannot safely change its fee recipient yet.") + fmt.Println("") + fmt.Printf("It must remain the Smoothing Pool's address (%s) until the opt-out process is complete.\n", color.LightBlue(status.FeeRecipientInfo.SmoothingPoolAddress.Hex())) + fmt.Printf("It can safely be changed once Epoch %d is finalized on the Beacon Chain.\n", status.FeeRecipientInfo.OptOutEpoch) if cfg.IsNativeMode { - fmt.Printf("%sNOTE: You are in Native Mode; you MUST ensure that your Validator Client is using this address as its fee recipient!%s\n", colorYellow, colorReset) + color.YellowPrintln("NOTE: You are in Native Mode; you MUST ensure that your Validator Client is using this address as its fee recipient!") } } else { - fmt.Printf("The node is not opted into the Smoothing Pool.\nTo learn more about the Smoothing Pool, please visit %s.\n", smoothingPoolLink) + fmt.Println("The node is not opted into the Smoothing Pool.") + fmt.Printf("To learn more about the Smoothing Pool, please visit %s.\n", smoothingPoolLink) // Count the number of 8 ETH, <10% commission minipools poolsWithMissingCommission := 0 leb16wei := new(big.Int) @@ -311,12 +296,8 @@ func getStatus(c *cli.Context) error { poolsWithMissingCommission++ } } - if poolsWithMissingCommission == 1 { - fmt.Printf("%sYou have %d minipool that would earn extra commission if you opted into the smoothing pool!%s\n", colorYellow, poolsWithMissingCommission, colorReset) - fmt.Println("See https://rpips.rocketpool.net/RPIPs/RPIP-62 for more information about bonus commission, or run `rocketpool node join-smoothing-pool` to opt in.") - } - if poolsWithMissingCommission > 1 { - fmt.Printf("%sYou have %d minipools that would earn extra commission if you opted into the smoothing pool!%s\n", colorYellow, poolsWithMissingCommission, colorReset) + if poolsWithMissingCommission > 0 { + color.YellowPrintf("You have %d minipool(s) that would earn extra commission if you opted into the smoothing pool!\n", poolsWithMissingCommission) fmt.Println("See https://rpips.rocketpool.net/RPIPs/RPIP-62 for more information about bonus commission, or run `rocketpool node join-smoothing-pool` to opt in.") } } @@ -324,7 +305,7 @@ func getStatus(c *cli.Context) error { fmt.Println() // RPL stake details - fmt.Printf("%s=== RPL Stake ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== RPL Stake ===") fmt.Println("NOTE: The following figures take *any pending bond reductions* into account.") fmt.Println() fmt.Printf("The node has a total stake of %.6f RPL.\n", math.RoundDown(eth.WeiToEth(status.TotalRplStake), 6)) @@ -383,7 +364,7 @@ func getStatus(c *cli.Context) error { if status.MinipoolCounts.Total > 0 { // Minipool details - fmt.Printf("%s=== Minipools ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Minipools ===") // Minipools fmt.Printf("The node has a total of %d active minipool(s):\n", status.MinipoolCounts.Total-status.MinipoolCounts.Finalised) @@ -422,7 +403,8 @@ func getStatus(c *cli.Context) error { } if status.Warning != "" { - fmt.Printf("\n%sWARNING: %s%s\n", colorRed, status.Warning, colorReset) + fmt.Println() + color.RedPrintln("WARNING: " + status.Warning) } // Return diff --git a/rocketpool-cli/node/sync.go b/rocketpool-cli/node/sync.go index e4aff91a2..471ca0490 100644 --- a/rocketpool-cli/node/sync.go +++ b/rocketpool-cli/node/sync.go @@ -10,6 +10,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" ) // Settings @@ -71,10 +72,9 @@ func getSyncProgress(c *cli.Context) error { return err } if !depositContractInfo.SufficientSync { - colorReset := "\033[0m" - colorYellow := "\033[33m" - fmt.Printf("%sYour execution client hasn't synced enough to determine if your execution and consensus clients are on the same network.\n", colorYellow) - fmt.Printf("To run this safety check, try again later when the execution client has made more sync progress.%s\n\n", colorReset) + color.YellowPrintln("Your execution client hasn't synced enough to determine if your execution and consensus clients are on the same network.") + color.YellowPrintln("To run this safety check, try again later when the execution client has made more sync progress.") + fmt.Println() } else if depositContractInfo.RPNetwork != depositContractInfo.BeaconNetwork || depositContractInfo.RPDepositContract != depositContractInfo.BeaconDepositContract { cliutils.PrintDepositMismatchError( diff --git a/rocketpool-cli/node/withdraw-rpl.go b/rocketpool-cli/node/withdraw-rpl.go index 4546742de..71e504d49 100644 --- a/rocketpool-cli/node/withdraw-rpl.go +++ b/rocketpool-cli/node/withdraw-rpl.go @@ -13,6 +13,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/rocket-pool/smartnode/shared/utils/math" ) @@ -49,7 +50,7 @@ func nodeWithdrawRpl(c *cli.Context) error { fmt.Println() fmt.Print("1. Request to unstake a certain RPL amount;") fmt.Println() - fmt.Printf("2. Wait for the unstaking period to end (currently %s%s%s), and then withdraw the RPL.", colorYellow, unstakingDurationString, colorReset) + fmt.Printf("2. Wait for the unstaking period to end (currently %s), and then withdraw the RPL.", color.Yellow(unstakingDurationString)) fmt.Println() fmt.Println() @@ -57,7 +58,7 @@ func nodeWithdrawRpl(c *cli.Context) error { fmt.Println() fmt.Printf("Your node currently has %.6f RPL locked on pDAO proposals.", math.RoundDown(eth.WeiToEth(status.NodeRPLLocked), 6)) fmt.Println() - fmt.Printf("Your node's RPL withdrawal address is %s%s%s.\n", colorBlue, status.RPLWithdrawalAddress.String(), colorReset) + fmt.Printf("Your node's RPL withdrawal address is %s.\n", color.LightBlue(status.RPLWithdrawalAddress.String())) fmt.Println() // Check if the node has unstaking RPL and if the unstaking period passed considering the last unstake time @@ -139,8 +140,8 @@ func nodeWithdrawRpl(c *cli.Context) error { // Inform users that the unstaking period will reset if they make another unstaking request if !cooldownPassed && hasUnstakingRPL { fmt.Printf("You have %.6f RPL currently unstaking until %s (%s from now).\n", math.RoundDown(eth.WeiToEth(status.UnstakingRPL), 6), unstakingPeriodEnd.Format(TimeFormat), timeUntilUnstakingPeriodEnd.String()) - fmt.Printf("%sRequesting to unstake additional RPL will reset the unstaking period.\n%s", colorYellow, colorReset) - fmt.Printf("%sThe unstaking period is %s.\n%s", colorYellow, unstakingDurationString, colorReset) + color.YellowPrintln("Requesting to unstake additional RPL will reset the unstaking period.") + color.YellowPrintf("The unstaking period is %s.\n", unstakingDurationString) if !prompt.Confirm("Are you sure you would like to continue?") { os.Exit(0) @@ -240,7 +241,7 @@ func nodeWithdrawRpl(c *cli.Context) error { return nil } fmt.Println("Unstaking legacy RPL follows the same 2-step process as unstaking megapool staked RPL.") - fmt.Printf("Unstaked legacy RPL can be withdrawn after an unstaking period of %s%s%s.\n", colorYellow, unstakingDurationString, colorReset) + fmt.Printf("Unstaked legacy RPL can be withdrawn after an unstaking period of %s.\n", color.Yellow(unstakingDurationString)) fmt.Println() // Get the maximum withdrawable amount based on constraints diff --git a/rocketpool-cli/pdao/set-allow-list.go b/rocketpool-cli/pdao/set-allow-list.go index 923848e08..162775bec 100644 --- a/rocketpool-cli/pdao/set-allow-list.go +++ b/rocketpool-cli/pdao/set-allow-list.go @@ -8,6 +8,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/gas" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/urfave/cli" ) @@ -58,9 +59,9 @@ func setAllowListedControllers(c *cli.Context) error { // Prompt for confirmation if addressListStr == "" { - fmt.Printf("%sYou are proposing to remove all allowlisted controllers%s\n", colorGreen, colorReset) + color.GreenPrintln("You are proposing to remove all allowlisted controllers") } else { - fmt.Printf("%sYou have selected propose %v as the allowlisted controllers%s\n", colorGreen, addressListStr, colorReset) + color.GreenPrintln("You have selected propose %v as the allowlisted controllers", addressListStr) } fmt.Println() diff --git a/rocketpool-cli/pdao/status.go b/rocketpool-cli/pdao/status.go index b879bee17..236253343 100644 --- a/rocketpool-cli/pdao/status.go +++ b/rocketpool-cli/pdao/status.go @@ -14,13 +14,11 @@ import ( "github.com/rocket-pool/smartnode/shared/services/rocketpool" "github.com/rocket-pool/smartnode/shared/types/api" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/math" ) const ( - colorBlue string = "\033[36m" - colorReset string = "\033[0m" - colorGreen string = "\033[32m" signallingAddressLink string = "https://docs.rocketpool.net/pdao/participate#setting-your-snapshot-signalling-address" challengeLink string = "https://docs.rocketpool.net/pdao#challenge-process" ) @@ -74,13 +72,12 @@ func getStatus(c *cli.Context) error { claimableBonds := claimableBondsResponse.ClaimableBonds // Signalling Status - fmt.Printf("%s=== Signalling on Snapshot ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Signalling on Snapshot ===") blankAddress := common.Address{} if response.SignallingAddress == blankAddress { fmt.Printf("The node does not currently have a snapshot signalling address set.\nTo learn more about snapshot signalling, please visit %s.\n", signallingAddressLink) } else { - fmt.Printf("The node has a signalling address of %s%s%s which can represent it when voting on Rocket Pool Snapshot governance proposals.", colorBlue, response.SignallingAddressFormatted, colorReset) - fmt.Println() + fmt.Printf("The node has a signalling address of %s which can represent it when voting on Rocket Pool Snapshot governance proposals.\n", color.LightBlue(response.SignallingAddressFormatted)) } if response.SnapshotResponse.Error != "" { @@ -97,14 +94,14 @@ func getStatus(c *cli.Context) error { } // Onchain Voting Status - fmt.Printf("%s=== Onchain Voting ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Onchain Voting ===") if response.OnchainVotingDelegate == blankAddress { fmt.Println("The node doesn't have a delegate, which means it can vote directly on onchain proposals after it initializes voting.") } else if response.OnchainVotingDelegate == response.AccountAddress { fmt.Println("The node doesn't have a delegate, which means it can vote directly on onchain proposals. You can have another node represent you by running `rocketpool p svd `.") } else { - fmt.Printf("The node has a voting delegate of %s%s%s which can represent it when voting on Rocket Pool onchain governance proposals.\n", colorBlue, response.OnchainVotingDelegateFormatted, colorReset) + fmt.Printf("The node has a voting delegate of %s which can represent it when voting on Rocket Pool onchain governance proposals.\n", color.LightBlue(response.OnchainVotingDelegateFormatted)) } fmt.Printf("The node's local voting power: %.10f\n", eth.WeiToEth(response.VotingPower)) @@ -118,7 +115,7 @@ func getStatus(c *cli.Context) error { fmt.Println("") // Claimable Bonds Status: - fmt.Printf("%s=== Claimable RPL Bonds ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Claimable RPL Bonds ===") if response.IsRPLLockingAllowed { fmt.Print("The node is allowed to lock RPL to create governance proposals/challenges.\n") if response.NodeRPLLocked.Cmp(big.NewInt(0)) != 0 { @@ -137,7 +134,7 @@ func getStatus(c *cli.Context) error { fmt.Println("") // Check if PDAO proposal checking duty is enabled - fmt.Printf("%s=== PDAO Proposal Checking Duty ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== PDAO Proposal Checking Duty ===") // Make sure the user opted into this duty if response.VerifyEnabled { fmt.Println("The node has PDAO proposal checking duties enabled. It will periodically check for proposals to challenge.") @@ -148,7 +145,7 @@ func getStatus(c *cli.Context) error { fmt.Println("") // Claimable Bonds Status: - fmt.Printf("%s=== Pending, Active and Succeeded Proposals ===%s\n", colorGreen, colorReset) + color.GreenPrintln("=== Pending, Active and Succeeded Proposals ===") // Get proposals by state stateProposals := map[string][]api.PDAOProposalWithNodeVoteDirection{} for _, proposal := range allProposals.Proposals { @@ -180,7 +177,8 @@ func getStatus(c *cli.Context) error { // Print message for Succeeded Proposals if stateName == "Succeeded" { succeededExists = true - fmt.Printf("%sThe following proposal(s) have succeeded and are waiting to be executed. Use `rocketpool pdao proposals execute` to execute.%s\n\n", colorBlue, colorReset) + color.LightBluePrintln("The following proposal(s) have succeeded and are waiting to be executed. Use `rocketpool pdao proposals execute` to execute.") + fmt.Println() } // Proposal state count diff --git a/rocketpool-cli/rocketpool-cli.go b/rocketpool-cli/rocketpool-cli.go index 2599fb2fd..c55005581 100644 --- a/rocketpool-cli/rocketpool-cli.go +++ b/rocketpool-cli/rocketpool-cli.go @@ -23,12 +23,11 @@ import ( "github.com/rocket-pool/smartnode/shared" "github.com/rocket-pool/smartnode/shared/services/rocketpool" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" ) const ( - colorReset string = "\033[0m" - colorYellow string = "\033[33m" - maxAlertItems int = 3 + maxAlertItems int = 3 ) // Run @@ -181,7 +180,7 @@ A special thanks to the Rocket Pool community for all their contributions. } if len(response.Alerts) > 0 { - fmt.Printf("\n%s=== Alerts ===%s\n", colorYellow, colorReset) + color.YellowPrintln("=== Alerts ===") for i, alert := range response.Alerts { fmt.Println(alert.ColorString()) if i == maxAlertItems-1 { diff --git a/rocketpool-cli/service/commands.go b/rocketpool-cli/service/commands.go index 2f0567029..5db8034ec 100644 --- a/rocketpool-cli/service/commands.go +++ b/rocketpool-cli/service/commands.go @@ -11,6 +11,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/config" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" ) // Creates CLI argument flags from the parameters of the configuration struct @@ -440,7 +441,7 @@ func RegisterCommands(app *cli.App, name string, aliases []string) { { Name: "resync-eth1", - Usage: fmt.Sprintf("%sDeletes the main ETH1 client's chain data and resyncs it from scratch. Only use this as a last resort!%s", colorRed, colorReset), + Usage: color.Red("Deletes the main ETH1 client's chain data and resyncs it from scratch. Only use this as a last resort!"), UsageText: "rocketpool service resync-eth1", Action: func(c *cli.Context) error { @@ -457,7 +458,7 @@ func RegisterCommands(app *cli.App, name string, aliases []string) { { Name: "resync-eth2", - Usage: fmt.Sprintf("%sDeletes the ETH2 client's chain data and resyncs it from scratch. Only use this as a last resort!%s", colorRed, colorReset), + Usage: color.Red("Deletes the ETH2 client's chain data and resyncs it from scratch. Only use this as a last resort!"), UsageText: "rocketpool service resync-eth2", Action: func(c *cli.Context) error { @@ -475,7 +476,7 @@ func RegisterCommands(app *cli.App, name string, aliases []string) { { Name: "terminate", Aliases: []string{"t"}, - Usage: fmt.Sprintf("%sDeletes all of the Rocket Pool Docker containers and volumes, including your ETH1 and ETH2 chain data and your Prometheus database (if metrics are enabled). Also removes your entire `.rocketpool` configuration folder, including your wallet, password, and validator keys. Only use this if you are cleaning up the Smart Node and want to start over!%s", colorRed, colorReset), + Usage: color.Red("Deletes all of the Rocket Pool Docker containers and volumes, including your ETH1 and ETH2 chain data and your Prometheus database (if metrics are enabled). Also removes your entire `.rocketpool` configuration folder, including your wallet, password, and validator keys. Only use this if you are cleaning up the Smart Node and want to start over!"), UsageText: "rocketpool service terminate [options]", Flags: []cli.Flag{ cli.BoolFlag{ diff --git a/rocketpool-cli/service/service.go b/rocketpool-cli/service/service.go index 42d77abcc..7876c0519 100644 --- a/rocketpool-cli/service/service.go +++ b/rocketpool-cli/service/service.go @@ -20,6 +20,7 @@ import ( "github.com/rocket-pool/smartnode/shared/services/rocketpool" cfgtypes "github.com/rocket-pool/smartnode/shared/types/config" cliutils "github.com/rocket-pool/smartnode/shared/utils/cli" + "github.com/rocket-pool/smartnode/shared/utils/cli/color" "github.com/rocket-pool/smartnode/shared/utils/cli/prompt" "github.com/shirou/gopsutil/v3/disk" ) @@ -44,13 +45,7 @@ const ( // Just ignore the version tag. dockerImageRegex string = "(?P