diff --git a/src/bridge/LibcoreBridge.js b/src/bridge/LibcoreBridge.js index eb4864fe..9b9776cd 100644 --- a/src/bridge/LibcoreBridge.js +++ b/src/bridge/LibcoreBridge.js @@ -1,7 +1,8 @@ // @flow import React from 'react' import { ipcRenderer } from 'electron' -import { decodeAccount } from 'reducers/accounts' + +import { decodeAccount, encodeAccount } from 'reducers/accounts' import runJob from 'renderer/runJob' import FeesBitcoinKind from 'components/FeesField/BitcoinKind' import AdvancedOptionsBitcoinKind from 'components/AdvancedOptions/BitcoinKind' @@ -153,7 +154,16 @@ const LibcoreBridge: WalletBridge = { getMaxAmount: (a, t) => Promise.resolve(a.balance - t.feePerByte), - signAndBroadcast: () => Promise.reject(notImplemented), + signAndBroadcast: ({ account, transaction, deviceId }) => { + const rawAccount = encodeAccount(account) + return runJob({ + channel: 'accounts', + job: 'signAndBroadcastTransactionBTCLike', + successResponse: 'accounts.signAndBroadcastTransactionBTCLike.success', + errorResponse: 'accounts.signAndBroadcastTransactionBTCLike.fail', + data: { account: rawAccount, transaction, deviceId }, + }) + }, } export default LibcoreBridge diff --git a/src/bridge/makeMockBridge.js b/src/bridge/makeMockBridge.js index 1bc2eef9..21032f3d 100644 --- a/src/bridge/makeMockBridge.js +++ b/src/bridge/makeMockBridge.js @@ -146,7 +146,7 @@ function makeMockBridge(opts?: Opts): WalletBridge<*> { getMaxAmount, - signAndBroadcast: async (account, t) => { + signAndBroadcast: async ({ account, transaction: t }) => { const rng = new Prando() const op = genOperation(account, account.operations, account.currency, rng) op.amount = -t.amount diff --git a/src/bridge/types.js b/src/bridge/types.js index 716ba35d..d85d3fe0 100644 --- a/src/bridge/types.js +++ b/src/bridge/types.js @@ -98,5 +98,9 @@ export interface WalletBridge { * NOTE: in future, when transaction balance is close to account.balance, we could wipe it all at this level... * to implement that, we might want to have special logic `account.balance-transaction.amount < dust` but not sure where this should leave (i would say on UI side because we need to inform user visually). */ - signAndBroadcast(account: Account, transaction: Transaction, deviceId: DeviceId): Promise; + signAndBroadcast({ + account: Account, + transaction: Transaction, + deviceId: DeviceId, + }): Promise; } diff --git a/src/components/DeviceSignTransaction.js b/src/components/DeviceSignTransaction.js index 471c3ab4..ff9c3865 100644 --- a/src/components/DeviceSignTransaction.js +++ b/src/components/DeviceSignTransaction.js @@ -34,7 +34,7 @@ class DeviceSignTransaction extends PureComponent { sign = async () => { const { device, account, transaction, bridge, onSuccess } = this.props try { - const txid = await bridge.signAndBroadcast(account, transaction, device.path) + const txid = await bridge.signAndBroadcast({ account, transaction, deviceId: device.path }) onSuccess(txid) } catch (error) { this.setState({ error }) diff --git a/src/internals/accounts/index.js b/src/internals/accounts/index.js index 18c02cfe..6a18e8e8 100644 --- a/src/internals/accounts/index.js +++ b/src/internals/accounts/index.js @@ -3,11 +3,13 @@ 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, { @@ -29,7 +31,6 @@ export default { }) send('accounts.scanAccountsOnDevice.success', accounts) } catch (err) { - console.log(err) send('accounts.scanAccountsOnDevice.fail', formatErr(err)) } }, diff --git a/src/internals/accounts/scanAccountsOnDevice.js b/src/internals/accounts/scanAccountsOnDevice.js index f8ba2daf..4cb1688c 100644 --- a/src/internals/accounts/scanAccountsOnDevice.js +++ b/src/internals/accounts/scanAccountsOnDevice.js @@ -46,19 +46,33 @@ export default async function scanAccountsOnDevice(props: Props): Promise = await CommNodeHid.open(deviceId) + 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) + + send('accounts.signAndBroadcastTransactionBTCLike.success', txHash) + } catch (err) { + send('accounts.signAndBroadcastTransactionBTCLike.fail', err) + } +} diff --git a/src/reducers/accounts.js b/src/reducers/accounts.js index 6ecb4009..40847073 100644 --- a/src/reducers/accounts.js +++ b/src/reducers/accounts.js @@ -119,6 +119,10 @@ export function decodeAccount(account: AccountRaw): Account { }) } +export function encodeAccount(account: Account): AccountRaw { + return accountModel.encode(account).data +} + // Yeah. `any` should be `AccountRaw[]` but it can also be a map // of wrapped accounts. And as flow is apparently incapable of doing // such a simple thing, let's put any, right? I don't care. diff --git a/src/renderer/runJob.js b/src/renderer/runJob.js index fa136b48..b2fc8e64 100644 --- a/src/renderer/runJob.js +++ b/src/renderer/runJob.js @@ -14,7 +14,7 @@ export default function runJob({ successResponse: string, errorResponse: string, data?: any, -}): Promise { +}): Promise { return new Promise((resolve, reject) => { ipcRenderer.send(channel, { type: job, data }) ipcRenderer.on('msg', handler)