node-ethereum-wallet
Version:
Command-line light wallet for Ethereum
362 lines (306 loc) • 8.49 kB
JavaScript
const Web3 = require('web3')
const cmdline = require('node-cmdline-parser')
const lightwallet = require('eth-lightwallet')
const txDecoder = require('ethereum-tx-decoder')
const SignerProvider = require('ethjs-provider-signer')
const nodePersist = require('node-persist')
const asyncjs = require('async')
const _ = require('underscore')
const Util = require('ethereumjs-util')
const Erc20 = require('./erc20')
const pkg = require('../package')
class EthereumWallet {
constructor(provider = false) {
this.provider = provider
return this
}
get libVersion() {
return pkg.version
}
async init(storage_method = false) {
if (!storage_method) {
this.storage = nodePersist.create({ dir: require('os').homedir() + '/.'+pkg.name })
await this.storage.init()
} else {
if (typeof storage_method === 'string') {
if (storage_method === 'testnet')
this.storage = nodePersist.create({ dir: require('os').homedir() + '/.'+pkg.name+'/testnet' })
else
this.storage = nodePersist.create({dir: storage_method})
await this.storage.init()
} else {
this.storage = storage_method
}
}
if(await this.hasKeystore) {
this.setProvider(this.provider)
}
}
unlock(password) {
let _this = this
return new Promise((resolve, reject) => {
_this.keystore.keyFromPassword(password, (err, pwDerivedKey) => {
if(err) return reject(err)
if (_this.keystore.isDerivedKeyCorrect(pwDerivedKey)) {
_this._rawpwd = password
_this._password = pwDerivedKey
resolve(true)
} else {
resolve(false)
}
})
})
}
get isUnlocked() {
return this._password !== undefined
}
get password() {
if (this._password) {
return this._password
} else {
throw new Error("Password not provided or invalid")
}
}
async setProvider(provider = false) {
let the_provider
this.keystore.passwordProvider = cb => cb(null, this._rawpwd)
if(typeof provider === 'string') {
if (provider === 'ropsten' || provider === 'testnet')
provider = 'https://api.myetherapi.com/rop'
the_provider = new SignerProvider(provider, {
signTransaction: this.keystore.signTransaction.bind(this.keystore),
accounts: (cb) => cb(null, this.keystore.getAddresses().map((a) => '0x' + a))
})
}
else if(provider === false) {
the_provider = new SignerProvider('https://api.myetherapi.com/eth', {
signTransaction: this.keystore.signTransaction.bind(this.keystore),
accounts: (cb) => cb(null, this.keystore.getAddresses().map((a) => '0x' + a))
})
}
else if(provider instanceof Web3.providers.HttpProvider || provider instanceof Web3.providers.WebsocketProvider || provider instanceof SignerProvider || typeof provider === 'object') {
the_provider = provider
}
this._web3 = new Web3(the_provider)
return this
}
get hasKeystore() {
return new Promise((resolve, reject) => {
this.storage.getItem('keystore').then((read_ks) => {
if (read_ks !== undefined) {
this.keystore = lightwallet.keystore.deserialize(read_ks)
resolve(true)
} else {
resolve(false)
}
})
})
}
get rawKeystore() {
return this.keystore.serialize()
}
get seed() {
return this.keystore.getSeed(this.password)
}
get isSyncing() {
return new Promise((res, rej) => {
this._web3.eth.isSyncing((err, sync) => {
if (err)
rej(err)
else
res(sync)
})
})
}
generateSeed() {
return lightwallet.keystore.generateRandomSeed()
}
isSeedValid(seed) {
return lightwallet.keystore.isSeedValid(seed)
}
createKeystore(password, seedPhrase) {
let _this = this
return new Promise((resolve, reject) => {
lightwallet.keystore.createVault({password, seedPhrase, hdPathString: "m/44'/60'/0'/0"}, function(error, ks) {
if (error) reject(error)
_this.keystore = ks
_this.setProvider(_this.provider)
_this.unlock(password)
// save keystore
_this.storage.setItem('keystore', _this.keystore.serialize()).then(() => resolve())
})
})
}
async saveKeystore() {
await this.storage.setItem('keystore', this.keystore.serialize())
}
async getNewAddress(amount = 1) {
if(isNaN(amount)) amount = 1
this.keystore.generateNewAddress(this.password, amount)
await this.saveKeystore()
let addresses = this.keystore.getAddresses()
let list = addresses.slice(addresses.length-amount)
if(amount===1)
return list[0]
else
return list
}
get addresses() {
return this.keystore.getAddresses()
}
privKeyToAddress(privKey) {
return '0x'+lightwallet.keystore._computeAddressFromPrivKey(privKey)
}
dumpPrivKey(address) {
return this.keystore.exportPrivateKey(address, this.password)
}
// web3
getBalance(address, minconf = 0) {
let _this = this
return new Promise((resolve, reject) => {
_this.blockNumber.then((blocknumber) => {
this._web3.eth.getBalance(address, blocknumber-minconf, (err, bal) => {
err ? reject(err) : resolve(bal)
})
})
})
}
getWalletBalance(minconf = 0) {
let _this = this
return new Promise(async (resolve, reject) => {
let blocknumber = await _this.blockNumber
asyncjs.map(this.addresses, (addr, cb) => this._web3.eth.getBalance(addr, blocknumber-minconf, cb), function(err, balances) {
resolve(String(balances.reduce((acc, val) => Number(acc)+Number(val))))
})
})
}
get balance() {
return this.getWalletBalance()
}
getTransaction(txid) {
let _this = this
return new Promise((resolve, reject) => {
_this._web3.eth.getTransaction(txid, (err, data) => {
if(err)
reject(err)
else
resolve(data)
})
})
}
decodeRawTx(rawtx) {
let decoded = txDecoder.decodeTx(rawtx)
decoded.gasPrice = decoded.gasPrice.toString()
decoded.gasLimit = decoded.gasLimit.toString()
decoded.value = decoded.value.toString()
return decoded
}
get gasPrice() {
let _this = this
return new Promise((resolve, reject) => {
_this._web3.eth.getGasPrice((err, data) => {
if(err)
reject(err)
else
resolve(parseInt(data))
})
})
}
get web3() {
return this._web3
}
get blockNumber() {
let _this = this
return new Promise((resolve, reject) => {
_this._web3.eth.getBlockNumber((err, data) => {
if(err)
reject(err)
else
resolve(data)
})
})
}
getBlock(block) {
let _this = this
return new Promise((resolve, reject) => {
_this._web3.eth.getBlock(block, (err, data) => {
if(err)
reject(err)
else
resolve(data)
})
})
}
getNonce(addr, includePending = false) {
let _this = this
return new Promise((resolve, reject) => {
if (includePending) {
_this._web3.eth.getTransactionCount(addr, 'pending', (err, data) => {
if(err)
reject(err)
else
resolve(data)
})
} else {
_this._web3.eth.getTransactionCount(addr, (err, data) => {
if(err)
reject(err)
else
resolve(data)
})
}
})
}
estimateGas(txOptions) {
let _this = this
return new Promise((resolve, reject) => {
_this._web3.eth.estimateGas(txOptions, (err, data) => {
if(err)
reject(err)
else
resolve(data)
})
})
}
async createRawTx(from, to, value, gasPrice = undefined, gasLimit = undefined, gas = undefined) {
let nonce = await this.getNonce(from)
to = to.substr(2).toLowerCase()
if (!gasPrice) gasPrice = await this.gasPrice
if (!gas) gas = await this.estimateGas({value, to, gasPrice, nonce})
return lightwallet.txutils.valueTx({ value, to, gasPrice, nonce, gasLimit, gas })
}
signTx(rawtx, from) {
return '0x'+lightwallet.signing.signTx(this.keystore, this.password, rawtx, from)
}
signTransaction(rawtx, from) {
const privKey = Buffer.from(this.keystore.exportPrivateKey(from, this.password), 'hex')
return rawtx.sign(privKey)
}
pushTx(rawtx) {
return sendRawTx(rawtx)
}
sendRawTx(rawtx) {
let _this = this
return new Promise((resolve, reject) => {
_this._web3.eth.sendSignedTransaction(rawtx, (err, hash) => {
if(err)
reject(err)
else
resolve(hash)
})
})
}
async sendToAddress(from, to, value, gasPrice = undefined, gasLimit = undefined, gas = undefined) {
let rawtx = await this.createRawTx(from, to, value, gasPrice, gasLimit, gas)
let signed_rawtx = this.signTx(rawtx, from)
return this.sendRawTx(signed_rawtx)
}
get Erc20() {
let ret = Erc20
ret.prototype.parent = this
return ret
}
}
if (typeof module)
module.exports = EthereumWallet