committed by
GitHub
11 changed files with 181 additions and 231 deletions
@ -0,0 +1,44 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import type { AccountRaw } from '@ledgerhq/live-common/lib/types' |
||||
|
import { createCommand, Command } from 'helpers/ipc' |
||||
|
import { Observable } from 'rxjs' |
||||
|
import { scanAccountsOnDevice } from 'helpers/libcore' |
||||
|
|
||||
|
type Input = { |
||||
|
devicePath: string, |
||||
|
currencyId: string, |
||||
|
} |
||||
|
|
||||
|
type Result = AccountRaw |
||||
|
|
||||
|
const cmd: Command<Input, Result> = createCommand( |
||||
|
'devices', |
||||
|
'libcoreScanAccounts', |
||||
|
({ devicePath, currencyId }) => |
||||
|
Observable.create(o => { |
||||
|
// TODO scanAccountsOnDevice should directly return a Observable so we just have to pass-in
|
||||
|
scanAccountsOnDevice({ |
||||
|
devicePath, |
||||
|
currencyId, |
||||
|
onAccountScanned: account => { |
||||
|
o.next(account) |
||||
|
}, |
||||
|
}).then( |
||||
|
() => { |
||||
|
o.complete() |
||||
|
}, |
||||
|
e => { |
||||
|
o.error(e) |
||||
|
}, |
||||
|
) |
||||
|
|
||||
|
function unsubscribe() { |
||||
|
// FIXME not implemented
|
||||
|
} |
||||
|
|
||||
|
return unsubscribe |
||||
|
}), |
||||
|
) |
||||
|
|
||||
|
export default cmd |
@ -0,0 +1,82 @@ |
|||||
|
// @flow
|
||||
|
|
||||
|
import type { AccountRaw, OperationRaw } from '@ledgerhq/live-common/lib/types' |
||||
|
import Btc from '@ledgerhq/hw-app-btc' |
||||
|
import { createCommand, Command } from 'helpers/ipc' |
||||
|
import { withDevice } from 'helpers/deviceAccess' |
||||
|
import { getWalletIdentifier } from 'helpers/libcore' |
||||
|
import { fromPromise } from 'rxjs/observable/fromPromise' |
||||
|
|
||||
|
type BitcoinLikeTransaction = { |
||||
|
amount: number, |
||||
|
feePerByte: number, |
||||
|
recipient: string, |
||||
|
} |
||||
|
|
||||
|
type Input = { |
||||
|
account: AccountRaw, |
||||
|
transaction: BitcoinLikeTransaction, |
||||
|
deviceId: string, |
||||
|
} |
||||
|
|
||||
|
type Result = $Exact<OperationRaw> |
||||
|
|
||||
|
const cmd: Command<Input, Result> = createCommand( |
||||
|
'devices', |
||||
|
'libcoreSignAndBroadcast', |
||||
|
({ account, transaction, deviceId }) => { |
||||
|
// TODO: investigate why importing it on file scope causes trouble
|
||||
|
const core = require('init-ledger-core')() |
||||
|
|
||||
|
return fromPromise( |
||||
|
withDevice(deviceId)(async transport => { |
||||
|
const hwApp = new Btc(transport) |
||||
|
|
||||
|
const WALLET_IDENTIFIER = await getWalletIdentifier({ |
||||
|
hwApp, |
||||
|
isSegwit: !!account.isSegwit, |
||||
|
currencyId: account.currencyId, |
||||
|
devicePath: deviceId, |
||||
|
}) |
||||
|
|
||||
|
const njsWallet = await core.getWallet(WALLET_IDENTIFIER) |
||||
|
const njsAccount = await njsWallet.getAccount(account.index) |
||||
|
const bitcoinLikeAccount = njsAccount.asBitcoinLikeAccount() |
||||
|
const njsWalletCurrency = njsWallet.getCurrency() |
||||
|
const amount = core.createAmount(njsWalletCurrency, transaction.amount) |
||||
|
const fees = core.createAmount(njsWalletCurrency, transaction.feePerByte) |
||||
|
const transactionBuilder = bitcoinLikeAccount.buildTransaction() |
||||
|
|
||||
|
// TODO: check if is valid address. if not, it will fail silently on invalid
|
||||
|
|
||||
|
transactionBuilder.sendToAddress(amount, transaction.recipient) |
||||
|
// TODO: don't use hardcoded value for sequence (and first also maybe)
|
||||
|
transactionBuilder.pickInputs(0, 0xffffff) |
||||
|
transactionBuilder.setFeesPerByte(fees) |
||||
|
|
||||
|
const builded = await transactionBuilder.build() |
||||
|
const signedTransaction = await core.signTransaction(hwApp, builded) |
||||
|
|
||||
|
const txHash = await njsAccount |
||||
|
.asBitcoinLikeAccount() |
||||
|
.broadcastRawTransaction(signedTransaction) |
||||
|
|
||||
|
// optimistic operation
|
||||
|
return { |
||||
|
id: txHash, |
||||
|
hash: txHash, |
||||
|
type: 'OUT', |
||||
|
value: amount, |
||||
|
blockHash: null, |
||||
|
blockHeight: null, |
||||
|
senders: [account.freshAddress], |
||||
|
recipients: [transaction.recipient], |
||||
|
accountId: account.id, |
||||
|
date: new Date().toISOString(), |
||||
|
} |
||||
|
}), |
||||
|
) |
||||
|
}, |
||||
|
) |
||||
|
|
||||
|
export default cmd |
@ -1,48 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import type { IPCSend } from 'types/electron' |
|
||||
|
|
||||
import scanAccountsOnDevice from './scanAccountsOnDevice' |
|
||||
import signAndBroadcastTransactionBTCLike from './signAndBroadcastTransaction/btc' |
|
||||
|
|
||||
import sync from './sync' |
|
||||
|
|
||||
export default { |
|
||||
sync, |
|
||||
signAndBroadcastTransactionBTCLike, |
|
||||
scan: async ( |
|
||||
send: IPCSend, |
|
||||
{ |
|
||||
devicePath, |
|
||||
currencyId, |
|
||||
}: { |
|
||||
devicePath: string, |
|
||||
currencyId: string, |
|
||||
}, |
|
||||
) => { |
|
||||
try { |
|
||||
send('accounts.scanAccountsOnDevice.start', { pid: process.pid }, { kill: false }) |
|
||||
const accounts = await scanAccountsOnDevice({ |
|
||||
devicePath, |
|
||||
currencyId, |
|
||||
onAccountScanned: account => { |
|
||||
send('accounts.scanAccountsOnDevice.accountScanned', account, { kill: false }) |
|
||||
}, |
|
||||
}) |
|
||||
send('accounts.scanAccountsOnDevice.success', accounts) |
|
||||
} catch (err) { |
|
||||
send('accounts.scanAccountsOnDevice.fail', formatErr(err)) |
|
||||
} |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
// TODO: move this to a helper
|
|
||||
function formatErr(err) { |
|
||||
if (err instanceof Error) { |
|
||||
return err.message || err.code |
|
||||
} |
|
||||
if (typeof err === 'string') { |
|
||||
return err |
|
||||
} |
|
||||
return 'unknown error' |
|
||||
} |
|
@ -1,71 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import Btc from '@ledgerhq/hw-app-btc' |
|
||||
import { withDevice } from 'helpers/deviceAccess' |
|
||||
import type { AccountRaw } from '@ledgerhq/live-common/lib/types' |
|
||||
|
|
||||
import type { IPCSend } from 'types/electron' |
|
||||
import { getWalletIdentifier } from '../scanAccountsOnDevice' |
|
||||
|
|
||||
type BitcoinLikeTransaction = { |
|
||||
amount: number, |
|
||||
feePerByte: number, |
|
||||
recipient: string, |
|
||||
} |
|
||||
|
|
||||
export default async function signAndBroadcastTransactionBTCLike( |
|
||||
send: IPCSend, |
|
||||
{ |
|
||||
account, |
|
||||
transaction, |
|
||||
deviceId, // which is in fact `devicePath`
|
|
||||
}: { |
|
||||
account: AccountRaw, |
|
||||
transaction: BitcoinLikeTransaction, |
|
||||
deviceId: string, |
|
||||
}, |
|
||||
) { |
|
||||
try { |
|
||||
// TODO: investigate why importing it on file scope causes trouble
|
|
||||
const core = require('init-ledger-core')() |
|
||||
|
|
||||
const txHash = await withDevice(deviceId)(async transport => { |
|
||||
const hwApp = new Btc(transport) |
|
||||
|
|
||||
const WALLET_IDENTIFIER = await getWalletIdentifier({ |
|
||||
hwApp, |
|
||||
isSegwit: !!account.isSegwit, |
|
||||
currencyId: account.currencyId, |
|
||||
devicePath: deviceId, |
|
||||
}) |
|
||||
|
|
||||
const njsWallet = await core.getWallet(WALLET_IDENTIFIER) |
|
||||
const njsAccount = await njsWallet.getAccount(account.index) |
|
||||
const bitcoinLikeAccount = njsAccount.asBitcoinLikeAccount() |
|
||||
const njsWalletCurrency = njsWallet.getCurrency() |
|
||||
const amount = core.createAmount(njsWalletCurrency, transaction.amount) |
|
||||
const fees = core.createAmount(njsWalletCurrency, transaction.feePerByte) |
|
||||
const transactionBuilder = bitcoinLikeAccount.buildTransaction() |
|
||||
|
|
||||
// TODO: check if is valid address. if not, it will fail silently on invalid
|
|
||||
|
|
||||
transactionBuilder.sendToAddress(amount, transaction.recipient) |
|
||||
// TODO: don't use hardcoded value for sequence (and first also maybe)
|
|
||||
transactionBuilder.pickInputs(0, 0xffffff) |
|
||||
transactionBuilder.setFeesPerByte(fees) |
|
||||
|
|
||||
const builded = await transactionBuilder.build() |
|
||||
const signedTransaction = await core.signTransaction(hwApp, builded) |
|
||||
|
|
||||
const txHash = await njsAccount |
|
||||
.asBitcoinLikeAccount() |
|
||||
.broadcastRawTransaction(signedTransaction) |
|
||||
|
|
||||
return txHash |
|
||||
}) |
|
||||
|
|
||||
send('accounts.signAndBroadcastTransactionBTCLike.success', txHash) |
|
||||
} catch (err) { |
|
||||
send('accounts.signAndBroadcastTransactionBTCLike.fail', err) |
|
||||
} |
|
||||
} |
|
@ -1,7 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import type { IPCSend } from 'types/electron' |
|
||||
|
|
||||
export default (send: IPCSend) => { |
|
||||
setTimeout(() => send('accounts.sync.success'), 5e3) |
|
||||
} |
|
Loading…
Reference in new issue