diff --git a/pkg/operator/config/create.go b/pkg/operator/config/create.go index 9a4dc55..c10dc23 100644 --- a/pkg/operator/config/create.go +++ b/pkg/operator/config/create.go @@ -167,22 +167,6 @@ func promptOperatorInfo(config *types.OperatorConfigNew, p utils.Prompter) (type } config.EthRPCUrl = rpcUrl - // Prompt for ecdsa key path - ecdsaKeyPath, err := p.InputString("Enter your ecdsa key path:", "", "", - func(s string) error { - _, err := os.Stat(s) - if os.IsNotExist(err) { - return err - } - return nil - }, - ) - - if err != nil { - return types.OperatorConfigNew{}, err - } - config.PrivateKeyStorePath = ecdsaKeyPath - // Prompt for network & set chainId chainId, err := p.Select("Select your network:", []string{"mainnet", "holesky", "local"}) if err != nil { @@ -201,7 +185,93 @@ func promptOperatorInfo(config *types.OperatorConfigNew, p utils.Prompter) (type config.ELDelegationManagerAddress = utils.ChainMetadataMap[utils.LocalChainId].ELDelegationManagerAddress } - config.SignerType = types.LocalKeystoreSigner + // Prompt for signer type + signerType, err := p.Select("Select your signer type:", []string{"local_keystore", "fireblocks"}) + if err != nil { + return types.OperatorConfigNew{}, err + } + + switch signerType { + case "local_keystore": + config.SignerType = types.LocalKeystoreSigner + // Prompt for ecdsa key path + ecdsaKeyPath, err := p.InputString("Enter your ecdsa key path:", "", "", + func(s string) error { + _, err := os.Stat(s) + if os.IsNotExist(err) { + return err + } + return nil + }, + ) + + if err != nil { + return types.OperatorConfigNew{}, err + } + config.PrivateKeyStorePath = ecdsaKeyPath + case "fireblocks": + config.SignerType = types.FireBlocksSigner + // Prompt for fireblocks API key + apiKey, err := p.InputString("Enter your fireblocks api key:", "", "", + func(s string) error { + if len(s) == 0 { + return errors.New("fireblocks API key should not be empty") + } + return nil + }, + ) + if err != nil { + return types.OperatorConfigNew{}, err + } + config.FireblocksConfig.APIKey = apiKey + + // Prompt for fireblocks base url + baseUrl, err := p.InputString("Enter your fireblocks base url:", "https://api.fireblocks.io/", "", + func(s string) error { + if len(s) == 0 { + return errors.New("base url should not be empty") + } + return nil + }, + ) + if err != nil { + return types.OperatorConfigNew{}, err + } + config.FireblocksConfig.BaseUrl = baseUrl + + // Prompt for fireblocks vault account name + vaultAccountName, err := p.InputString("Enter the name of fireblocks vault:", "", "", + func(s string) error { + if len(s) == 0 { + return errors.New("vault account name should not be empty") + } + return nil + }, + ) + if err != nil { + return types.OperatorConfigNew{}, err + } + config.FireblocksConfig.VaultAccountName = vaultAccountName + + // Prompt for fireblocks API timeout + timeout, err := p.InputInteger("Enter the timeout for fireblocks API (in seconds):", "3", "", + func(i int64) error { + if i <= 0 { + return errors.New("timeout should be greater than 0") + } + return nil + }, + ) + if err != nil { + return types.OperatorConfigNew{}, err + } + config.FireblocksConfig.Timeout = timeout + + // ask to fill in secret key + config.FireblocksConfig.SecretKey = "" + default: + return types.OperatorConfigNew{}, fmt.Errorf("unknown signer type %s", signerType) + } return *config, nil } diff --git a/pkg/utils/mocks/prompter.go b/pkg/utils/mocks/prompter.go index 3424958..aa0e458 100644 --- a/pkg/utils/mocks/prompter.go +++ b/pkg/utils/mocks/prompter.go @@ -68,6 +68,21 @@ func (mr *MockPrompterMockRecorder) InputHiddenString(arg0, arg1, arg2 any) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InputHiddenString", reflect.TypeOf((*MockPrompter)(nil).InputHiddenString), arg0, arg1, arg2) } +// InputInteger mocks base method. +func (m *MockPrompter) InputInteger(arg0, arg1, arg2 string, arg3 func(int64) error) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InputInteger", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// InputInteger indicates an expected call of InputInteger. +func (mr *MockPrompterMockRecorder) InputInteger(arg0, arg1, arg2, arg3 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InputInteger", reflect.TypeOf((*MockPrompter)(nil).InputInteger), arg0, arg1, arg2, arg3) +} + // InputString mocks base method. func (m *MockPrompter) InputString(arg0, arg1, arg2 string, arg3 func(string) error) (string, error) { m.ctrl.T.Helper() diff --git a/pkg/utils/prompter.go b/pkg/utils/prompter.go index 72692c0..bc9e8ac 100644 --- a/pkg/utils/prompter.go +++ b/pkg/utils/prompter.go @@ -1,11 +1,17 @@ package utils -import "github.com/AlecAivazis/survey/v2" +import ( + "fmt" + "strconv" + + "github.com/AlecAivazis/survey/v2" +) // Prompter is an interface for prompting the user for input. type Prompter interface { Select(prompt string, options []string) (string, error) InputString(prompt, defValue, help string, validator func(string) error) (string, error) + InputInteger(prompt, defValue, help string, validator func(int64) error) (int64, error) Confirm(prompt string) (bool, error) InputHiddenString(prompt, help string, validator func(string) error) (string, error) } @@ -46,6 +52,27 @@ func (p *prompter) InputString(prompt, defValue, help string, validator func(str return result, err } +func (p *prompter) InputInteger(prompt, defValue, help string, validator func(int64) error) (int64, error) { + var result int64 + i := &survey.Input{ + Message: prompt, + Default: defValue, + Help: help, + } + + err := survey.AskOne(i, &result, survey.WithValidator(func(ans interface{}) error { + atoi, err := strconv.Atoi(ans.(string)) + if err != nil { + return fmt.Errorf("invalid integer with err: %s", err.Error()) + } + if err := validator(int64(atoi)); err != nil { + return err + } + return nil + })) + return result, err +} + // Confirm prompts the user to confirm an action with a yes/no question. func (p *prompter) Confirm(prompt string) (bool, error) { result := false