@@ -31,6 +31,7 @@ func handleRegisterValidator(cfg config.Config) {
3131
3232 moniker := defaultMoniker
3333 keyName := defaultKeyName
34+ var importMnemonic string // Will hold mnemonic if user chooses to import
3435
3536 v := validator .NewWith (validator.Options {
3637 BinPath : findPchaind (),
@@ -168,12 +169,19 @@ func handleRegisterValidator(cfg config.Config) {
168169 newName = strings .TrimSpace (newName )
169170 if newName != "" {
170171 keyName = newName
172+ // Check if new key name also exists, if not, show wallet choice
173+ if ! keyExists (cfg , keyName ) {
174+ importMnemonic = promptWalletChoice (reader )
175+ }
171176 } else {
172177 // User chose to reuse existing key
173178 fmt .Println ()
174179 fmt .Println (p .Colors .Success ("✓ Proceeding with existing key" ))
175180 fmt .Println ()
176181 }
182+ } else {
183+ // Key doesn't exist - prompt for wallet creation method
184+ importMnemonic = promptWalletChoice (reader )
177185 }
178186 fmt .Println ()
179187 }
@@ -206,11 +214,11 @@ func handleRegisterValidator(cfg config.Config) {
206214
207215 // Interactive mode - let user choose stake amount
208216 // Pass empty string to trigger the interactive stake selection prompt
209- runRegisterValidator (cfg , moniker , keyName , "" , commissionRate )
217+ runRegisterValidator (cfg , moniker , keyName , "" , commissionRate , importMnemonic )
210218 } else {
211219 // JSON mode or env vars set - use default/env amount
212220 commissionRate := getenvDefault ("COMMISSION_RATE" , "0.10" )
213- runRegisterValidator (cfg , moniker , keyName , defaultAmount , commissionRate )
221+ runRegisterValidator (cfg , moniker , keyName , defaultAmount , commissionRate , "" )
214222 }
215223}
216224
@@ -224,13 +232,68 @@ func keyExists(cfg config.Config, keyName string) bool {
224232 return err == nil
225233}
226234
235+ // promptWalletChoice prompts the user to choose between creating a new wallet or importing an existing one.
236+ // Returns the mnemonic if user chooses to import, empty string otherwise.
237+ func promptWalletChoice (reader * bufio.Reader ) string {
238+ p := ui .NewPrinter (flagOutput )
239+
240+ fmt .Println ()
241+ fmt .Println (p .Colors .Info ("Wallet Setup" ))
242+ fmt .Println (p .Colors .Separator (40 ))
243+ fmt .Println ()
244+ fmt .Println (" [1] Create new wallet (generates new recovery phrase)" )
245+ fmt .Println (" [2] Import existing wallet (use your recovery phrase)" )
246+ fmt .Println ()
247+ fmt .Print ("Choose option [1]: " )
248+
249+ choice , _ := reader .ReadString ('\n' )
250+ choice = strings .TrimSpace (choice )
251+
252+ if choice != "2" {
253+ // Default to creating new wallet
254+ return ""
255+ }
256+
257+ // User chose to import
258+ fmt .Println ()
259+ fmt .Println (p .Colors .Info ("Enter your recovery mnemonic phrase (12 or 24 words):" ))
260+ fmt .Println (p .Colors .Apply (p .Colors .Theme .Description , "(Words should be separated by spaces)" ))
261+ fmt .Println ()
262+ fmt .Print ("> " )
263+
264+ mnemonic , err := reader .ReadString ('\n' )
265+ if err != nil {
266+ fmt .Println (p .Colors .Error (fmt .Sprintf ("Error reading input: %v" , err )))
267+ return ""
268+ }
269+
270+ // Normalize the mnemonic
271+ mnemonic = strings .TrimSpace (mnemonic )
272+ mnemonic = strings .Join (strings .Fields (mnemonic ), " " ) // Normalize whitespace
273+ mnemonic = strings .ToLower (mnemonic ) // Convert to lowercase
274+
275+ // Validate mnemonic format
276+ if err := validator .ValidateMnemonic (mnemonic ); err != nil {
277+ fmt .Println ()
278+ fmt .Println (p .Colors .Error (fmt .Sprintf ("Invalid mnemonic: %v" , err )))
279+ fmt .Println ()
280+ return ""
281+ }
282+
283+ fmt .Println ()
284+ fmt .Println (p .Colors .Success ("✓ Mnemonic format validated" ))
285+
286+ return mnemonic
287+ }
288+
227289// runRegisterValidator performs the end-to-end registration flow:
228290// - verify node is not catching up
229- // - ensure key exists
291+ // - ensure key exists (or import from mnemonic)
230292// - wait for funding if necessary
231293// - submit create-validator transaction
232294// It prints text or JSON depending on --output.
233- func runRegisterValidator (cfg config.Config , moniker , keyName , amount , commissionRate string ) {
295+ // If importMnemonic is non-empty, the key will be imported from that mnemonic instead of creating a new one.
296+ func runRegisterValidator (cfg config.Config , moniker , keyName , amount , commissionRate , importMnemonic string ) {
234297 savedStdin := os .Stdin
235298 var tty * os.File
236299 if ! flagNonInteractive && ! term .IsTerminal (int (savedStdin .Fd ())) {
@@ -270,14 +333,40 @@ func runRegisterValidator(cfg config.Config, moniker, keyName, amount, commissio
270333 v := validator .NewWith (validator.Options {BinPath : findPchaind (), HomeDir : cfg .HomeDir , ChainID : cfg .ChainID , Keyring : cfg .KeyringBackend , GenesisDomain : cfg .GenesisDomain , Denom : cfg .Denom })
271334 ctx2 , cancel2 := context .WithTimeout (context .Background (), 20 * time .Second )
272335 defer cancel2 ()
273- keyInfo , err := v .EnsureKey (ctx2 , keyName )
274- if err != nil {
275- if flagOutput == "json" {
276- getPrinter ().JSON (map [string ]any {"ok" : false , "error" : err .Error ()})
277- } else {
278- fmt .Printf ("key error: %v\n " , err )
336+
337+ // Handle key creation or import based on importMnemonic
338+ var keyInfo validator.KeyInfo
339+ var err error
340+ var wasImported bool
341+
342+ if importMnemonic != "" {
343+ // Import key from mnemonic
344+ keyInfo , err = v .ImportKey (ctx2 , keyName , importMnemonic )
345+ wasImported = true
346+ if err != nil {
347+ if flagOutput == "json" {
348+ getPrinter ().JSON (map [string ]any {"ok" : false , "error" : err .Error ()})
349+ } else {
350+ p := ui .NewPrinter (flagOutput )
351+ fmt .Println ()
352+ fmt .Println (p .Colors .Error ("Failed to import wallet" ))
353+ fmt .Printf ("Error: %v\n \n " , err )
354+ fmt .Println (p .Colors .Info ("Please verify your mnemonic phrase and try again." ))
355+ fmt .Println ()
356+ }
357+ return
358+ }
359+ } else {
360+ // Create new key or use existing (original behavior)
361+ keyInfo , err = v .EnsureKey (ctx2 , keyName )
362+ if err != nil {
363+ if flagOutput == "json" {
364+ getPrinter ().JSON (map [string ]any {"ok" : false , "error" : err .Error ()})
365+ } else {
366+ fmt .Printf ("key error: %v\n " , err )
367+ }
368+ return
279369 }
280- return
281370 }
282371
283372 evmAddr , err := v .GetEVMAddress (ctx2 , keyInfo .Address )
@@ -288,16 +377,21 @@ func runRegisterValidator(cfg config.Config, moniker, keyName, amount, commissio
288377 p := ui .NewPrinter (flagOutput )
289378
290379 if flagOutput != "json" {
291- // Display mnemonic if this is a new key
380+ // Display appropriate message based on key creation method
292381 if keyInfo .Mnemonic != "" {
293- // Display mnemonic in prominent box
382+ // New key was created - display mnemonic in prominent box
294383 p .MnemonicBox (keyInfo .Mnemonic )
295384 fmt .Println ()
296385
297386 // Warning message in yellow
298387 fmt .Println (p .Colors .Warning ("**Important** Write this mnemonic phrase in a safe place." ))
299388 fmt .Println (p .Colors .Warning ("It is the only way to recover your account if you ever forget your password." ))
300389 fmt .Println ()
390+ } else if wasImported {
391+ // Key was imported from mnemonic - show success message
392+ fmt .Println (p .Colors .Success (fmt .Sprintf ("✓ Wallet imported successfully: %s" , keyInfo .Name )))
393+ fmt .Println (p .Colors .Apply (p .Colors .Theme .Description , " (Keep your recovery phrase safe - it controls this wallet)" ))
394+ fmt .Println ()
301395 } else {
302396 // Existing key - show clear status with reminder
303397 fmt .Println (p .Colors .Success (fmt .Sprintf ("✓ Using existing key: %s" , keyInfo .Name )))
0 commit comments