icewallet
Version:
Cold storage enabled command line bitcoin wallet based on bitpay's bitcore
218 lines (207 loc) • 6.97 kB
text/typescript
import fs = require('fs');
let unit = require('bitcore-lib').Unit;
let intl = require('intl');
import inquirer = require('inquirer')
import {PublicWalletService} from '../Services/PublicWalletService'
import {PublicWalletInfo} from '../Models/PublicWalletInfo'
import IceWallet from './IceWallet'
export default class IceWalletPublic extends IceWallet {
wallet:PublicWalletService;
createNewWallet(callback:(err:any,wallet:PublicWalletService) => void){
inquirer.prompt([
{
name:'password1',
type:'password',
message:'create a password',
validate:(password) => {if(!password) return 'Password required'; else return true}
},
{
name:'password2',
type:'password',
message:'retype password',
validate:(password) => {if(!password) return 'Password required'; else return true}
}])
.then((passwords:any) => {
if(passwords['password1'] != passwords['password2']){
return callback('Passwords dont match', null);
}
let password = passwords['password1'];
try {
var info = new PublicWalletInfo();
var wallet = new PublicWalletService(info, password.toString());
}
catch(err){
return callback('Could not create wallet, make sure you typed the xpub correctly',null)
}
console.log('sucessfully created wallet');
return callback(null,wallet);
})
}
addAccount(callback:(err:any) => void):void {
inquirer.prompt([
{
name:'xpub',
message:'Enter the BIP32 xub key for your wallet account',
default:null,
},
{
name:'name',
message:'Give the account a name',
default:null,
},
])
.then((answers:any) => {
this.wallet.walletInfo.addAccount(answers['xpub'].toString(), answers['name'].toString());
return callback(null);
})
}
loadWalletFromInfo(callback:(err:any,PublicWalletService:PublicWalletService) => void){
inquirer.prompt({
name:'password',
type:'password',
message:'enter your password to open the wallet',
})
.then((answers:any) => {
let password = answers['password'].toString();
console.log('loading and decrypting wallet info from' + this.pathToWalletInfo);
console.log('this might take a minute');
fs.readFile(this.pathToWalletInfo, 'hex', (err, data) => {
if (err){
return callback(err,null);
}
PublicWalletService.openWallet(password, data, (err, info, wallet) => {
if (err){
return callback(err, null);
}
return callback(null,wallet);
})
})
})
}
displayAccountMenu(){
class Choices {
[key: string]: string;
initiateWithdraw = 'Initiate Withdraw';
completeWithdraw = 'Complete Withdraw';
showBalace = 'Show Balance';
showXpub = 'Show xpub';
update = 'Update';
nextUnusedIndexes = 'Show next Unused Indexes';
backToMain = 'Back To Main Menu';
saveAndQuit = 'Save and Quit (dont quit any other way)';
}
let choices = new Choices();
console.log('----------' + this.wallet.selectedAccount.name + '----------');
inquirer.prompt([
{
name:'choice',
type:'list',
message:'Choose an option',
choices: Object.keys(choices).map<string>((choice) => choices[choice].toString()),
}])
.then((answers:any) => {
let choice = answers['choice'];
let done = (err:any) => {
if (err){
console.log(err);
}
if (choice != choices.saveAndQuit){
this.displayAccountMenu();
}
}
switch(choice){
case choices.initiateWithdraw:
this.initiateWithdraw(done);
break;
case choices.completeWithdraw:
this.completeWithdraw(done);
break;
case choices.showBalace:
console.log("Balance in bits: " + unit.fromSatoshis(this.wallet.balance).bits.toLocaleString());
this.displayAccountMenu();
break;
case choices.showXpub:
console.log(this.wallet.selectedAccount.xpub);
this.displayAccountMenu();
break;
case choices.saveAndQuit:
this.saveAndQuit(done);
break;
case choices.backToMain:
this.displayMainMenu();
break;
case choices.update:
console.log('Updating Wallet...');
this.wallet.update((err,wallet) => done(err));
break;
case choices.nextUnusedIndexes:
console.log('Change: ' + this.wallet.changeAddresses.length);
console.log('External: ' + this.wallet.externalAddresses.length);
this.displayAccountMenu();
break;
default:
this.displayAccountMenu();
}
}
)
}
initiateWithdraw(callback:(err:any) => void){
inquirer.prompt([
{
name:'export',
message:'type the export path',
when: (answers) => {
return (!this.pathToUnsignedTransaction)
},
},
{
name:'address',
message:'enter the address to send to',
},
{
name:'amount',
message:'enter the amount to send in bits',
validate: amount => {if(!Number.isInteger(Number(amount))) return 'Must be an integer'; else return true}
}])
.then((answers:any) => {
let exportPath:string = this.pathToUnsignedTransaction || answers['export'];
let to = answers['address'];
let amount = Number(unit.fromBits(answers['amount']).satoshis);
this.wallet.createTransaction(to, amount, (err, serialized) => {
if(err){
return callback(err);
}
fs.writeFile(exportPath, serialized, (err) => {
if(err){
return callback(err);
}
console.log('transaction written to: ' + exportPath);
console.log('sign the transaction offline then complete it');
return callback(null);
})
})
})
}
completeWithdraw(callback:(err:any) => void){
inquirer.prompt([
{
name:'import',
message:'type the import path (path to signed transaction)',
when: (answers) => {
return (!this.pathToSignedTransaction)
},
}])
.then((answers:any) => {
var importPath:string = this.pathToSignedTransaction || answers['import'];
fs.readFile(importPath, 'utf8', (err, data) => {
if (err){
return callback(err);
}
this.wallet.broadcastTransaction(data, (err, txid) => {
console.log('transaction successfully broadcasted with txid: ' + txid);
return callback(null)
})
})
})
}
}