Browse Source

Merge pull request #1573 from meriadec/cli

Cli mode
gre-patch-1
Meriadec Pillet 6 years ago
committed by GitHub
parent
commit
5206049643
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 82
      babel.config.js
  2. 29
      package.json
  3. 9
      scripts/cli/cli.sh
  4. 21
      scripts/cli/getDevice.js
  5. 106
      scripts/cli/txBetweenAccounts.js
  6. 1
      src/helpers/deviceAccess.js
  7. 4
      src/helpers/withLibcore.js
  8. 1502
      yarn.lock

82
babel.config.js

@ -1,32 +1,62 @@
const { NODE_ENV } = process.env
const { NODE_ENV, CLI } = process.env
const __TEST__ = NODE_ENV === 'test'
const __CLI__ = !!CLI
module.exports = () => ({
presets: [
[
require('@babel/preset-env'),
{
loose: true,
modules: __TEST__ ? 'commonjs' : false,
targets: {
electron: '1.8',
node: 'current',
module.exports = (api) => {
if (api) {
api.cache(true);
}
return {
presets: [
[
require('@babel/preset-env'),
{
loose: true,
modules: __TEST__ || __CLI__ ? 'commonjs' : false,
targets: {
electron: '1.8',
node: 'current',
},
},
},
],
require('@babel/preset-flow'),
require('@babel/preset-react'),
],
require('@babel/preset-flow'),
require('@babel/preset-react'),
require('@babel/preset-stage-0'),
],
plugins: [
[require('babel-plugin-module-resolver'), { root: ['src'] }],
[
require('babel-plugin-styled-components'),
{
displayName: true,
ssr: __TEST__,
},
plugins: [
[require('babel-plugin-module-resolver'), { root: ['src'] }],
[
require('babel-plugin-styled-components'),
{
displayName: true,
ssr: __TEST__,
},
],
// Stage 0
"@babel/plugin-proposal-function-bind",
// Stage 1
"@babel/plugin-proposal-export-default-from",
"@babel/plugin-proposal-logical-assignment-operators",
["@babel/plugin-proposal-optional-chaining", { "loose": false }],
["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }],
["@babel/plugin-proposal-nullish-coalescing-operator", { "loose": false }],
"@babel/plugin-proposal-do-expressions",
// Stage 2
["@babel/plugin-proposal-decorators", { "legacy": true }],
"@babel/plugin-proposal-function-sent",
"@babel/plugin-proposal-export-namespace-from",
"@babel/plugin-proposal-numeric-separator",
"@babel/plugin-proposal-throw-expressions",
// Stage 3
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-syntax-import-meta",
["@babel/plugin-proposal-class-properties", { "loose": false }],
"@babel/plugin-proposal-json-strings"
],
],
})
}
}

29
package.json

@ -13,6 +13,7 @@
"dist": "bash ./scripts/dist.sh",
"dist:dir": "bash ./scripts/dist-dir.sh",
"compile": "bash ./scripts/compile.sh",
"cli": "bash ./scripts/cli/cli.sh",
"lint": "eslint src webpack .storybook test-e2e",
"flow": "flow",
"test": "jest src",
@ -114,12 +115,28 @@
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@babel/core": "7.0.0-beta.42",
"@babel/polyfill": "7.0.0-beta.42",
"@babel/preset-env": "7.0.0-beta.42",
"@babel/preset-flow": "7.0.0-beta.42",
"@babel/preset-react": "7.0.0-beta.42",
"@babel/preset-stage-0": "7.0.0-beta.42",
"@babel/core": "7.1.2",
"@babel/plugin-proposal-class-properties": "7.1.0",
"@babel/plugin-proposal-decorators": "7.1.2",
"@babel/plugin-proposal-do-expressions": "7.0.0",
"@babel/plugin-proposal-export-default-from": "7.0.0",
"@babel/plugin-proposal-export-namespace-from": "7.0.0",
"@babel/plugin-proposal-function-bind": "7.0.0",
"@babel/plugin-proposal-function-sent": "7.1.0",
"@babel/plugin-proposal-json-strings": "7.0.0",
"@babel/plugin-proposal-logical-assignment-operators": "7.0.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "7.0.0",
"@babel/plugin-proposal-numeric-separator": "7.0.0",
"@babel/plugin-proposal-optional-chaining": "7.0.0",
"@babel/plugin-proposal-pipeline-operator": "7.0.0",
"@babel/plugin-proposal-throw-expressions": "7.0.0",
"@babel/plugin-syntax-dynamic-import": "7.0.0",
"@babel/plugin-syntax-import-meta": "7.0.0",
"@babel/polyfill": "7.0.0",
"@babel/preset-env": "7.1.0",
"@babel/preset-flow": "7.0.0",
"@babel/preset-react": "7.0.0",
"@babel/register": "7.0.0",
"@octokit/rest": "^15.10.0",
"@storybook/addon-actions": "^3.4.7",
"@storybook/addon-knobs": "^3.4.7",

9
scripts/cli/cli.sh

@ -0,0 +1,9 @@
#!/bin/bash
# TODO: os specific
export LEDGER_DATA_DIR="$HOME/.config/Electron"
export LEDGER_LOGS_DIRECTORY="$LEDGER_DATA_DIR/logs"
export LEDGER_LIVE_SQLITE_PATH="$LEDGER_DATA_DIR/sqlite"
export CLI=1
node -r @babel/register scripts/cli/txBetweenAccounts.js

21
scripts/cli/getDevice.js

@ -0,0 +1,21 @@
import CommNodeHid from '@ledgerhq/hw-transport-node-hid'
export default function getDevice() {
return new Promise((resolve, reject) => {
const sub = CommNodeHid.listen({
error: err => {
sub.unsubscribe()
reject(err)
},
next: async e => {
if (!e.device) {
return
}
if (e.type === 'add') {
sub.unsubscribe()
resolve(e.device)
}
},
})
})
}

106
scripts/cli/txBetweenAccounts.js

@ -0,0 +1,106 @@
/* eslint-disable no-console */
import chalk from 'chalk'
import path from 'path'
import fs from 'fs'
import inquirer from 'inquirer'
import { createAccountModel } from '@ledgerhq/live-common/lib/models/account'
import { formatCurrencyUnit } from '@ledgerhq/live-common/lib/helpers/currencies'
import 'globals'
import withLibcore from 'helpers/withLibcore'
import { doSignAndBroadcast } from 'commands/libcoreSignAndBroadcast'
import getDevice from './getDevice'
const accountModel = createAccountModel()
async function main() {
try {
// GET ACCOUNTS
const app = await parseAppFile()
const accounts = app.accounts.map(accountModel.decode)
// GET SENDER ACCOUNT
const senderAccount = await chooseAccount(accounts, 'Choose sender account')
// GET RECIPIENT ACCOUNT
const recipientAccount = await chooseAccount(accounts, 'Choose recipient account')
// GET AMOUNT & FEE
const { amount, feePerByte } = await inquirer.prompt([
{
type: 'input',
name: 'amount',
message: 'Amount',
default: 0,
},
{
type: 'input',
name: 'feePerByte',
message: 'Fee per byte',
default: 0,
},
])
// GET DEVICE
console.log(chalk.blue(`Waiting for device...`))
const device = await getDevice()
console.log(chalk.blue(`Using device with path [${device.path}]`))
await withLibcore(async core =>
doSignAndBroadcast({
accountId: senderAccount.id,
currencyId: senderAccount.currency.id,
xpub: senderAccount.xpub,
freshAddress: senderAccount.freshAddress,
freshAddressPath: senderAccount.freshAddressPath,
index: senderAccount.index,
transaction: {
amount,
feePerByte,
recipient: recipientAccount.freshAddress,
},
deviceId: device.path,
core,
isCancelled: () => false,
onSigned: () => {
console.log(`>> signed`)
},
onOperationBroadcasted: operation => {
console.log(`>> broadcasted`, operation)
},
}),
)
} catch (err) {
console.log(`[ERROR]`, err)
}
}
async function parseAppFile() {
const appFilePath = path.resolve(process.env.LEDGER_DATA_DIR, 'app.json')
const appFileContent = fs.readFileSync(appFilePath, 'utf-8')
const parsedApp = JSON.parse(appFileContent)
return parsedApp.data
}
async function chooseAccount(accounts, msg) {
const { account } = await inquirer.prompt([
{
type: 'list',
choices: accounts.map(account => ({
name: `${account.name} | ${chalk.green(
formatCurrencyUnit(account.unit, account.balance, {
showCode: true,
}),
)}`,
value: account,
})),
name: 'account',
message: msg,
},
])
return account
}
main()

1
src/helpers/deviceAccess.js

@ -29,6 +29,7 @@ let busy = false
TransportNodeHid.setListenDevicesPollingSkip(() => busy)
const refreshBusyUIState = throttle(() => {
if (process.env.CLI) return
process.send({
type: 'setDeviceBusy',
busy,

4
src/helpers/withLibcore.js

@ -8,7 +8,7 @@ export default async function withLibcore<A>(job: Job<A>): Promise<A> {
const core = require('./init-libcore').default
core.getPoolInstance()
try {
if (counter++ === 0) {
if (counter++ === 0 && !process.env.CLI) {
process.send({
type: 'setLibcoreBusy',
busy: true,
@ -17,7 +17,7 @@ export default async function withLibcore<A>(job: Job<A>): Promise<A> {
const res = await job(core)
return res
} finally {
if (--counter === 0) {
if (--counter === 0 && !process.env.CLI) {
process.send({
type: 'setLibcoreBusy',
busy: false,

1502
yarn.lock

File diff suppressed because it is too large
Loading…
Cancel
Save