|
|
@ -1,9 +1,11 @@ |
|
|
|
// @flow
|
|
|
|
|
|
|
|
import axios from 'axios' |
|
|
|
// import axios from 'axios'
|
|
|
|
import bitcoin from 'bitcoinjs-lib' |
|
|
|
import { formatCurrencyUnit } from '@ledgerhq/common/lib/data/currency' |
|
|
|
|
|
|
|
const blockexplorer = require('blockchain.info/blockexplorer').usingNetwork(3) |
|
|
|
|
|
|
|
export function format(v: string | number, options: Object = { alwaysShowSign: true }) { |
|
|
|
return formatCurrencyUnit( |
|
|
|
{ |
|
|
@ -30,13 +32,26 @@ export const networks = [ |
|
|
|
] |
|
|
|
|
|
|
|
export function computeTransaction(addresses: Array<*>) { |
|
|
|
// return (transaction: Object) => {
|
|
|
|
// const outputVal = transaction.outputs
|
|
|
|
// .filter(o => addresses.includes(o.address))
|
|
|
|
// .reduce((acc, cur) => acc + cur.value, 0)
|
|
|
|
// const inputVal = transaction.inputs
|
|
|
|
// .filter(i => addresses.includes(i.address))
|
|
|
|
// .reduce((acc, cur) => acc + cur.value, 0)
|
|
|
|
// const balance = outputVal - inputVal
|
|
|
|
// return {
|
|
|
|
// ...transaction,
|
|
|
|
// balance,
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
return (transaction: Object) => { |
|
|
|
const outputVal = transaction.outputs |
|
|
|
.filter(o => addresses.includes(o.address)) |
|
|
|
const outputVal = transaction.out |
|
|
|
.filter(o => addresses.includes(o.addr)) |
|
|
|
.reduce((acc, cur) => acc + cur.value, 0) |
|
|
|
const inputVal = transaction.inputs |
|
|
|
.filter(i => addresses.includes(i.address)) |
|
|
|
.reduce((acc, cur) => acc + cur.value, 0) |
|
|
|
.filter(i => addresses.includes(i.prev_out.addr)) |
|
|
|
.reduce((acc, cur) => acc + cur.prev_out.value, 0) |
|
|
|
const balance = outputVal - inputVal |
|
|
|
return { |
|
|
|
...transaction, |
|
|
@ -46,11 +61,12 @@ export function computeTransaction(addresses: Array<*>) { |
|
|
|
} |
|
|
|
|
|
|
|
export function getTransactions(addresses: Array<string>) { |
|
|
|
return axios.get( |
|
|
|
`http://api.ledgerwallet.com/blockchain/v2/btc_testnet/addresses/${addresses.join( |
|
|
|
',', |
|
|
|
)}/transactions?noToken=true`,
|
|
|
|
) |
|
|
|
// return axios.get(
|
|
|
|
// `http://api.ledgerwallet.com/blockchain/v2/btc_testnet/addresses/${addresses.join(
|
|
|
|
// ',',
|
|
|
|
// )}/transactions?noToken=true`,
|
|
|
|
// )
|
|
|
|
return blockexplorer.getMultiAddress(addresses) |
|
|
|
} |
|
|
|
|
|
|
|
export async function getAccount({ |
|
|
@ -64,9 +80,11 @@ export async function getAccount({ |
|
|
|
segwit: boolean, |
|
|
|
network: Object, |
|
|
|
}) { |
|
|
|
const gapLimit = 20 |
|
|
|
const script = segwit ? parseInt(network.scriptHash, 10) : parseInt(network.pubKeyHash, 10) |
|
|
|
|
|
|
|
let transactions = [] |
|
|
|
let lastAddress = null |
|
|
|
|
|
|
|
const pubKeyToSegwitAddress = (pubKey, scriptVersion) => { |
|
|
|
const script = [0x00, 0x14].concat(Array.from(bitcoin.crypto.hash160(pubKey))) |
|
|
@ -82,26 +100,47 @@ export async function getAccount({ |
|
|
|
return pubKeyToSegwitAddress(hdnode.getPublicKeyBuffer(), script) |
|
|
|
} |
|
|
|
|
|
|
|
const nextPath = (index = 0) => { |
|
|
|
const count = 20 |
|
|
|
const getAddress = path => getPublicAddress({ hdnode, path, script, segwit }) |
|
|
|
const getAddress = ({ type, index }) => ({ |
|
|
|
type, |
|
|
|
index, |
|
|
|
address: getPublicAddress({ |
|
|
|
hdnode, |
|
|
|
path: `${type === 'external' ? 0 : 1}/${index}`, |
|
|
|
script, |
|
|
|
segwit, |
|
|
|
}), |
|
|
|
}) |
|
|
|
|
|
|
|
const getLastAddress = (addresses, lastTx) => { |
|
|
|
const address = addresses |
|
|
|
.filter(a => a.type === 'external') |
|
|
|
.find(a => a.address === lastTx.addr) || { index: 0 } |
|
|
|
|
|
|
|
return getAddress({ type: 'external', index: address.index + 1 }) |
|
|
|
} |
|
|
|
|
|
|
|
return Promise.all( |
|
|
|
Array.from(Array(count).keys()).map(v => |
|
|
|
const nextPath = (index = 0) => |
|
|
|
Promise.all( |
|
|
|
Array.from(new Array(gapLimit).keys()).map(v => |
|
|
|
Promise.all([ |
|
|
|
getAddress(`0/${v + index}`), // external chain
|
|
|
|
getAddress(`1/${v + index}`), // internal chain
|
|
|
|
getAddress({ type: 'external', index: v + index }), |
|
|
|
getAddress({ type: 'internal', index: v + index }), |
|
|
|
]), |
|
|
|
), |
|
|
|
).then(async results => { |
|
|
|
const currentAddresses = results.reduce((result, v) => [...result, ...v], []) |
|
|
|
const addresses = results.reduce((result, v) => [...result, ...v], []) |
|
|
|
|
|
|
|
const { data: { txs } } = await getTransactions(currentAddresses) |
|
|
|
const listAddresses = addresses.map(a => a.address) |
|
|
|
|
|
|
|
transactions = [...transactions, ...txs.map(computeTransaction(currentAddresses))] |
|
|
|
const { txs } = await getTransactions(listAddresses) |
|
|
|
|
|
|
|
if (txs.length > 0) { |
|
|
|
return nextPath(index + (count - 1)) |
|
|
|
const hasTransactions = txs.length > 0 |
|
|
|
|
|
|
|
transactions = [...transactions, ...txs.map(computeTransaction(listAddresses))] |
|
|
|
lastAddress = hasTransactions ? getLastAddress(addresses, txs[0].out[0]) : lastAddress |
|
|
|
|
|
|
|
if (hasTransactions) { |
|
|
|
return nextPath(index + (gapLimit - 1)) |
|
|
|
} |
|
|
|
|
|
|
|
return { |
|
|
@ -110,9 +149,14 @@ export async function getAccount({ |
|
|
|
return result |
|
|
|
}, 0), |
|
|
|
transactions, |
|
|
|
...(lastAddress !== null |
|
|
|
? { |
|
|
|
currentIndex: lastAddress.index, |
|
|
|
address: lastAddress.address, |
|
|
|
} |
|
|
|
: {}), |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
return nextPath(currentIndex) |
|
|
|
} |
|
|
|