Skip to content

Commit

Permalink
Merge pull request #5 from MainframeHQ/ledger-update
Browse files Browse the repository at this point in the history
update ledger integration to work with latest firmware
  • Loading branch information
avclarke authored Sep 5, 2018
2 parents b69b1e0 + aed3c5f commit 1b956b7
Show file tree
Hide file tree
Showing 4 changed files with 343 additions and 270 deletions.
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "contracts-cli",
"version": "0.1.1",
"version": "0.1.2",
"main": "lib/index.js",
"author": "Mainframe",
"description": "Command line tool for interacting with Ethereum smart contracts",
Expand All @@ -20,6 +20,8 @@
"prepublishOnly": "yarn build"
},
"dependencies": {
"@ledgerhq/hw-transport-node-hid": "^4.22.0",
"@ledgerhq/web3-subprovider": "^4.21.0",
"chalk": "^2.4.0",
"commander": "^2.17.1",
"config": "^2.0.1",
Expand All @@ -28,11 +30,10 @@
"fs-extra": "^5.0.0",
"got": "^8.3.2",
"inquirer": "^5.2.0",
"ledger-wallet-provider": "^2.0.1-beta.2",
"truffle-hdwallet-provider": "^0.0.3",
"truffle-hdwallet-provider-privkey": "^0.2.0",
"web3": "1.0.0-beta.34",
"web3-provider-engine": "^14.0.4",
"web3": "1.0.0-beta.36",
"web3-provider-engine": "^14.0.6",
"yargs": "^12.0.1"
},
"yargs": {
Expand Down
6 changes: 4 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ log.header(`\nEthereum ${ethNetwork}\n`)

const startCli = async () => {
try {
const web3 = await openWallet(ethNetwork, config.rpcUrl[ethNetwork])
const account = await web3.eth.getCoinbase()
const { web3, account } = await openWallet(
ethNetwork,
config.rpcUrl[ethNetwork],
)

log.info(`Using account: ${account}`, 'blue')
await callContract(web3, ethNetwork, config)
Expand Down
158 changes: 63 additions & 95 deletions src/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ import HDWalletProvider from 'truffle-hdwallet-provider'
import HDWalletProviderPK from 'truffle-hdwallet-provider-privkey'
import ProviderEngine from 'web3-provider-engine'
import RpcSubprovider from 'web3-provider-engine/subproviders/rpc'
import LedgerWalletSubproviderFactory from 'ledger-wallet-provider'
import Transport from '@ledgerhq/hw-transport-node-hid'
import createLedgerSubprovider from '@ledgerhq/web3-subprovider'
import { prompt } from 'inquirer'

import type { EthNetwork } from './types'
import { log } from './cli-utils'

const LEDGER_ROOT_PATH = `44'/60'/0'/0`

const decodeKeystore = async (path?: string) => {
const questions = [
{
Expand Down Expand Up @@ -74,7 +73,7 @@ const requestAccountSelection = async (accounts: Array<string>) => {
export const openWallet = async (
ethNetwork: EthNetwork,
rpcUrl: string,
): Promise<Web3> => {
): Promise<{ web3: Web3, account: string }> => {
const answers = await prompt([
{
type: 'list',
Expand All @@ -83,100 +82,69 @@ export const openWallet = async (
choices: ['Private Key', 'Mnemonic', 'Ledger', 'Keystore File'],
},
])
let provider
let web3
switch (answers.wallet) {
case 'Private Key': {
const key = await requestPrivateKey()
provider = new HDWalletProviderPK(key, rpcUrl)
web3 = new Web3(provider)
break
}
case 'Keystore File': {
let decodedKey = await decodeKeystore()
if (decodedKey.slice(0, 2) === '0x') {
decodedKey = decodedKey.slice(2, decodedKey.length)
}
provider = new HDWalletProviderPK(decodedKey, rpcUrl)
web3 = new Web3(provider)
break
if (answers.wallet === 'Ledger') {
const engine = new ProviderEngine()
const web3 = new Web3(engine)
try {
const ledgerIndexAnswers = await prompt([
{
type: 'input',
name: 'accountIndex',
default: 0,
message: 'Ledger accounts selection offset: ',
},
])
const networkId = ethNetwork === 'testnet' ? 3 : 1
const getTransport = () => Transport.create()
const ledgerWalletSelectedProvider = createLedgerSubprovider(
//$FlowFixMe Transport type
getTransport,
{
networkId,
accountsLength: 5,
accountsOffset: Number(ledgerIndexAnswers.accountIndex),
},
)
engine.addProvider(ledgerWalletSelectedProvider)
engine.addProvider(new RpcSubprovider({ rpcUrl }))
engine.start()
const accounts = await web3.eth.getAccounts()
const accountIndex = await requestAccountSelection(accounts)
const account = accounts[accountIndex]
return { web3, account }
} catch (err) {
log.warn(err)
log.info(`Error connecting to Ledger, please make sure:\n
- You have entered your pin and unlocked your ledger\n
- Selected Ethereum wallet\n
- Have browser support turned off`)
throw err
}
case 'Mnemonic': {
const mnemonic = await requestMnemonic()
provider = new HDWalletProvider(mnemonic, rpcUrl)
web3 = new Web3(provider)
break
}
case 'Ledger':
default: {
const engine = new ProviderEngine()
web3 = new Web3(engine)
try {
const ledgerWalletSubProvider = await LedgerWalletSubproviderFactory()

// If needing to select high index account on ledger ask for input

const choices = ['select from first 10', 'enter account index']
const answers = await prompt([
{
type: 'list',
name: 'accountSelect',
message: 'Ledger account select: ',
choices,
},
])

let selectedPath

if (choices.indexOf(answers.accountSelect) === 0) {
// Select ledger account from list
const listAccounts: {
[string]: string,
} = await ledgerWalletSubProvider.ledger.getMultipleAccounts(
LEDGER_ROOT_PATH,
0,
10,
)
const accountAddresses = Object.values(listAccounts)
const accountPaths = Object.keys(listAccounts)
// $FlowFixMe: Object.values return Array<any>
const accountIndex = await requestAccountSelection(accountAddresses)
selectedPath = accountPaths[accountIndex]
} else {
// Select ledger account from input index
const ledgerIndexAnswers = await prompt([
{
type: 'input',
name: 'accountIndex',
message: 'Ledger account select: ',
},
])
const accounts = await ledgerWalletSubProvider.ledger.getMultipleAccounts(
LEDGER_ROOT_PATH,
Number(ledgerIndexAnswers.accountIndex) - 1,
1,
)
selectedPath = Object.keys(accounts)[0]
} else {
let provider
switch (answers.wallet) {
case 'Keystore File': {
let decodedKey = await decodeKeystore()
if (decodedKey.slice(0, 2) === '0x') {
decodedKey = decodedKey.slice(2, decodedKey.length)
}

// Set selected account as provider
const networkId = ethNetwork === 'testnet' ? 3 : 1
const ledgerWalletSelectedProvider = await LedgerWalletSubproviderFactory(
() => networkId,
selectedPath,
)
engine.addProvider(ledgerWalletSelectedProvider)
engine.addProvider(new RpcSubprovider({ rpcUrl }))
engine.start()
} catch (err) {
log.warn(err)
log.info(`Error connecting to Ledger, please make sure:\n
- You have entered your pin and unlocked your ledger\n
- Selected Ethereum wallet\n
- Have browser support turned off`)
process.exit()
provider = new HDWalletProviderPK(decodedKey, rpcUrl)
break
}
case 'Mnemonic': {
const mnemonic = await requestMnemonic()
provider = new HDWalletProvider(mnemonic, rpcUrl)
break
}
case 'Private Key':
default: {
const key = await requestPrivateKey()
provider = new HDWalletProviderPK(key, rpcUrl)
break
}
}
const web3 = new Web3(provider)
const account = await web3.eth.getCoinbase()
return { web3, account }
}
return web3
}
Loading

0 comments on commit 1b956b7

Please sign in to comment.