updateAddressMeta()
// listUnspentOutputs() // grouped by address
{
type: 'BTC',
summary: {
"total": 2.35
},
addressesByIndex: {
1: "mpS2RuNkAEALvMhksCa6fPpLVb5yCPanLu"
},
addresses: {
"mpS2RuNkAEALvMhksCa6fPpLVb5yCPanLu": {
"address": "mpS2RuNkAEALvMhksCa6fPpLVb5yCPanLu",
"index": 1,
"isChange": false,
"amount": 2.35,
"txouts": [
{
"txid": "2c3c1de1357c6155bfffab5ce6bbd1807d5db30db09ebc96bbb9a55031626e1a",
"vout": 0,
"address": "mpS2RuNkAEALvMhksCa6fPpLVb5yCPanLu",
"account": "",
"scriptPubKey": "76a91461ca8116d03694952a3ad252d53c695da7d95f6188ac",
"amount": 1,
"confirmations": 105,
"spendable": false,
"solvable": false
},
{
"txid": "8455e6ac64fcf4142bc73d3f0236067319c9483bd15de89ce7c3aa2d0371db60",
"vout": 1,
"address": "mpS2RuNkAEALvMhksCa6fPpLVb5yCPanLu",
"account": "",
"scriptPubKey": "76a91461ca8116d03694952a3ad252d53c695da7d95f6188ac",
"amount": 1.35,
"confirmations": 105,
"spendable": false,
"solvable": false
}
]
}
}
}
// To generate xpub (for testing). The browser will do this, normally.
const mnemonic = bip39.generateMnemonic();
const seed = bip39.mnemonicToSeed(mnemonic, '') // 2nd param is empty string
const hdnode = bitcoin.HDNode.fromSeedBuffer(seed, bitcoin.networks.testnet)
// Bitcoin xPub
const btc = hdnode.derivePath("m/44'/0'/0'").neutered().toBase58() // type string
// Equibit xPub
const eqb = hdnode.derivePath("m/44'/72'/0'").neutered().toBase58() // type string
// portfolioBalance :: portfolio xpub -> portfolio index -> listunspent + updateAddresses
/portfolio-balance/<portfolioIndex>?btc=111&eqb=222
const portfolioBalance.get = function (params) {
const portfolioIndex = hook.id
// Validate that we have a portfolioIndex
// `m / purpose' / coin_type' / account' ` => generate `/ change`
// Generate the change xpub
// https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/hdnode.js
const hdNode = bitcoin.HDNode.fromBase58(xPubBtc, bitcoin.networks.testnet)
const xpubBtcExternal = hdNode.derive(0).toBase58() // string
const xpubBtcChange = hdNode.derive(1).toBase58() // string
const hdNodeEqb = bitcoin.HDNode.fromBase58(xPubEqb, bitcoin.networks.testnet)
const xpubEqbExternal = hdNodeEqb.derive(0).toBase58() // string
const xpubEqbChange = hdNodeEqb.derive(1).toBase58() // string
Promise.all(
app.service('crawl-xpub').create({type: 'btc', xpub: xpubBtcExternal)
app.service('crawl-xpub').create({type: 'btc', xpub: xpubBtcChange)
app.service('crawl-xpub').create({type: 'eqb', xpub: xpubEqbExternal)
app.service('crawl-xpub').create({type: 'eqb', xpub: xpubEqbChange)
).then(responses => {
// Aggregate into from two BTC crawls into one object. Merge addresses into one object.
// and add up the summary amounts.
// Update addressMeta records
// Send response
})
updateAddressMeta()
})
// xpubCrawl :: xpub -> blockchainServerType -> listunspent
// xpub format:
// `m / purpose' / coin_type' / account' / change ` => generate 20 `address_index`
xpubCrawl (xpub, blockchainServerType) {
// Step 1: generate 20 addresses
// Step 2: use `listunspent` with the 20 addresses & see which ones have funds, repeat with remaining addresses until we have 20 empty.
listUnspent()
// Step 3: format the data as noted, above, and return it in the response.
}
...
GET /portfolios?btc[0]=111&eqb[0]=222&btc[1]=333&eqb[1]=444
{
btc: [{
index: 0,
isChange: false
}, {
index: 0,
isChange: true
}],
eqb: [{
index: 0,
isChange: false
}, {
index: 0,
isChange: true
}]
}