@ethersphere/swarm-cli
Version:
CLI tool for Bee
136 lines (113 loc) • 4.51 kB
text/typescript
import { Wallet } from '@ethereumjs/wallet'
import { readFileSync } from 'fs'
import { Argument, LeafCommand, Option } from 'furious-commander'
import { IdentityType } from '../../service/identity/types'
import { expectFile, getFieldOrNull, isPrivateKey, normalizePrivateKey } from '../../utils'
import { CommandLineError } from '../../utils/error'
import { Message } from '../../utils/message'
import { createAndRunSpinner } from '../../utils/spinner'
import { RootCommand } from '../root-command'
import { VerbosityLevel } from '../root-command/command-log'
export class Import extends RootCommand implements LeafCommand {
public readonly name = 'import'
public readonly description = 'Import private key or V3 wallet as a new identity'
public resource!: string
public identityName!: string
public password!: string
public async run(): Promise<void> {
super.init()
if (this.commandConfig.config.identities[this.identityName]) {
throw new CommandLineError(Message.identityNameConflict(this.identityName))
}
if (isPrivateKey(this.resource)) {
await this.runImportOnPrivateKey()
} else {
expectFile(this.resource)
this.resource = readFileSync(this.resource, 'utf-8')
if (isPrivateKey(this.resource)) {
await this.runImportOnPrivateKey()
} else {
if (!this.password) {
this.console.log(Message.optionNotDefined('password'))
this.password = await this.console.askForPassword(Message.existingV3Password())
}
const spinner = createAndRunSpinner('Decrypting V3 wallet...', this.verbosity)
try {
const wallet: Wallet = await this.decryptV3Wallet(this.resource)
spinner.text = 'Importing V3 wallet...'
await this.saveWallet(wallet)
} finally {
spinner.stop()
}
this.console.log(`V3 Wallet imported as identity '${this.identityName}' successfully`)
}
}
}
private async runImportOnPrivateKey(): Promise<void> {
if (await this.shouldConvertToV3Wallet()) {
await this.convertPrivateKeyToV3Wallet()
} else {
const data = {
wallet: {
privateKey: this.resource,
},
identityType: IdentityType.simple,
}
if (!this.commandConfig.saveIdentity(this.identityName, data)) {
throw new CommandLineError(Message.identityNameConflictOption(this.identityName))
}
this.console.log(`Private key imported as identity '${this.identityName}' successfully`)
}
}
private async decryptV3Wallet(data: string): Promise<Wallet> {
try {
const wallet: Wallet = await Wallet.fromV3(data, this.password)
return wallet
} catch (error: unknown) {
const message: string = getFieldOrNull(error, 'message') || 'unknown error'
throw new CommandLineError(`Failed to decrypt wallet: ${message}`)
}
}
private async convertPrivateKeyToV3Wallet(): Promise<void> {
if (!this.password) {
this.console.log(Message.optionNotDefined('password'))
this.password = await this.console.askForPasswordWithConfirmation(
Message.newV3Password(),
Message.newV3PasswordConfirmation(),
)
}
const wallet = Wallet.fromPrivateKey(Buffer.from(normalizePrivateKey(this.resource), 'hex'))
await this.saveWallet(wallet)
this.console.log(`V3 Wallet imported as identity '${this.identityName}' successfully`)
}
private async saveWallet(wallet: Wallet): Promise<void> {
const data = {
wallet: await wallet.toV3(this.password),
identityType: IdentityType.v3,
}
if (!this.commandConfig.saveIdentity(this.identityName, data)) {
throw new CommandLineError(Message.identityNameConflict(this.identityName))
}
}
private async shouldConvertToV3Wallet(): Promise<boolean> {
if (this.yes) {
return false
}
if (this.password) {
return true
}
if (this.verbosity !== VerbosityLevel.Quiet) {
const answer = await this.console.confirmAndDelete('Convert private key to a secure V3 wallet?')
return answer
}
return false
}
}