Commit 5da920d3 authored by Bitcoin Please's avatar Bitcoin Please
Browse files

Added support for multiple address types, plus balances.

parent d724ccfa
......@@ -5,6 +5,7 @@
import getBytesFromIPFS from './utils/getters/getBytesFromIPFS'
import getCompletedPct from './utils/getters/getCompletedPct'
import getFormatFunded from './utils/getters/getFormatFunded'
import getFormattedValue from './utils/getters/getFormattedValue'
import getIPFSFromBytes from './utils/getters/getIPFSFromBytes'
import getMarkdown from './utils/getters/getMarkdown'
import getShuffledArray from './utils/getters/getShuffledArray'
......@@ -25,6 +26,7 @@ const getters = {
getBytesFromIPFS,
getCompletedPct,
getFormatFunded,
getFormattedValue,
getIPFSFromBytes,
getMarkdown,
getShuffledArray,
......
/* Import modules. */
// const numeral = require('numeral')
import numeral from 'numeral'
/**
* Get Formatted Value
*
* Format BCH value to value units and calculate market value.
*/
const getFormattedValue = () => (_satoshis, _marketPrice, _currency) => {
// console.log('CURRENCY', _currency)
// let value = getIotas(iotas, units, marketPrice)
let value = _satoshis
let unit = 'sats'
let fiat = ((_satoshis / 100000000) * (_marketPrice))
fiat = numeral(fiat).format('$0,0.00[00]') + ' ' + _currency
// const fiat = !_marketPrice
// ? '-'
// : (_marketPrice.value * (value / 1000000)).toLocaleString('en-US', {
// style: '_currency',
// currency: _marketPrice.currency,
// minimumFractionDigits: 2,
// maximumFractionDigits: 5
// })
/* Adjust value and unit (based on size of balance). */
switch (true) {
case value < 100:
break
case value < 100000:
value /= 100
unit = 'bits'
break
case value < 100000000:
value /= 100000
unit = 'mBCH'
break
default:
value /= 100000000
unit = 'BCH'
break
}
/* Rounded value (based on decimal value). */
// TODO: Run more display tests to decide if we're keeping the rounding.
// 2-decimal places is ideal for "primary" displays.
// 4-decimal places is acceptable for "secondary" displays.
const rounded = Math.round(value * 10) / 10 +
(Math.round(value * 10) / 10 === value ? '' : '+')
/* Return (formatted) value. */
return {
value,
rounded,
unit,
fiat
}
}
/* Export module. */
export default getFormattedValue
......@@ -6,7 +6,7 @@ const Nito = require('nitojs')
*
* Returns the next avaialble "receiving" address.
*/
const getAddress = (state, getters) => {
const getAddress = (state, getters) => (_account) => {
/* Validate wallet. */
if (!getters.getWallet) {
return null
......@@ -25,11 +25,29 @@ const getAddress = (state, getters) => {
}
/* Initialize current (coin) index. */
const currentIndex = accounts.deposit
const currentIndex = accounts[_account]
// console.log('GET ADDRESS (currentIndex):', currentIndex)
/* Set chain. */
const chain = 0 // receiving account
/* Initialize chain. */
let chain = null
/* Select chain. */
switch(_account) {
case 'deposit':
chain = 0
break
case 'change':
chain = 1
break
case 'causes':
chain = 6767
break
}
/* Validate chain. */
if (chain === null) {
return null
}
/* Set derivation path. */
const path = getters.getDerivationPath(chain, currentIndex)
......
......@@ -2,91 +2,83 @@
const Nito = require('nitojs')
/**
* Get (Total Wallet) Balance
* Get Balance by Session Id
*
* Retrieves the current (total) wallet balance.
* Retrieves the current (total) session balance.
*
* NOTE: Optional (market price) parameter is used to calculate fiat value,
* and return a "formatted" value package.
*/
const getBalance = (state, getters, rootState, rootGetters) => async (_wallet, _currency) => {
/* Validate (wallet) accounts. */
if (!getters.getAccountsByWallet(_wallet)) {
return null
}
/* Initialize account addresses. */
const getBalanceBySessionId = (
state, getters, rootState, rootGetters
) => async (_currency) => {
/* Initialize (search) addresses. */
const addresses = []
// const wallet = getters.getWallet
// console.log('GET BALANCE (wallet)', wallet)
// FIXME: We currently ONLY retrieve balances for index 0.
/* Set deposit address. */
addresses.push(getters.getAddress('deposit'))
/* Set change address. */
addresses.push(getters.getAddress('change'))
/* Set Causes address. */
addresses.push(getters.getAddress('causes'))
/* Add all active receiving account (addresses) to pool. */
Object.keys(getters.getAccountsByWallet(_wallet)).forEach(index => {
/* Initialize HD node. */
const hdNode = getters.getHDNode
// FIXME
const change = 0
/* Set derivation path. */
const path = `${getters.getDerivationPath(_wallet)}/${change}/${index}`
// console.log('GET BALANCE (path)', path)
/* Initialize child node. */
const childNode = hdNode.derivePath(path)
const address = Nito.Address.toCashAddress(childNode)
// console.log('GET WALLET BALANCE (receiving address)', address)
/* Add to all receiving (pool). */
addresses.push(address)
})
console.log('GET WALLET BALANCE (all accounts)', addresses)
try {
/* Initialize balance. */
let balance = 0
/* Retrieve (ALL) account(s) details. */
const details = await Nito.Address.details(addresses)
// console.log('ALL ACCOUNTS DETAILS', JSON.stringify(details, null, 4))
/* Validate (use of) unconfirmed transactions. */
if (rootGetters['profile/getFlags'] && rootGetters['profile/getFlags'].ut) {
details.forEach((address) => {
/* Both confirmed and unconfirmed. */
balance += (address.balanceSat + address.unconfirmedBalanceSat)
})
} else {
details.forEach((address) => {
/* Confirmed ONLY. */
balance += address.balanceSat
})
}
// Object.keys(coins).forEach(txid => {
// /* Add to all receiving (pool). */
// addresses.push(coins[txid].cashAddress)
// })
// console.log('GET BALANCE (all accounts)', addresses)
/* Validate search accounts. */
if (!addresses && addresses.length) {
return 0
}
/* Retrieve market price. */
const marketPrice =
await rootGetters['blockchain/getTicker'](_wallet, _currency)
console.info(`Market price (${_currency})`, marketPrice) // eslint-disable-line no-console
/* Initialize balance. */
let balance = 0
/* Validate market price. */
if (marketPrice) {
/* Format balance (for display). */
// TODO: Support additional currencies.
const formattedBalance =
rootGetters['utils/getFormattedValue'](balance, marketPrice, 'USD')
/* Loop through all address. */
// FIXME: We do not use for-each with callback here because of async.
for (let i = 0; i < addresses.length; i++) {
/* Set address. */
const address = addresses[i]
/* Return (formatted) balance. */
return formattedBalance
/* Retrieve (address) balances. */
const balances = await Nito.Address.balance(address)
/* Check unconfirmed flag. */
if (rootGetters['getFlags'].unconfirmed) {
balance += (balances.confirmed + balances.unconfirmed)
} else {
/* Return (empty) balance. */
return null
balance += balances.confirmed
}
} catch (err) {
console.error(err) // eslint-disable-line no-console
}
/* Bugsnag alert. */
throw new Error(err)
/* Retrieve market price. */
const marketPrice = await Nito.Markets.getTicker('BCH', _currency)
console.info(`Market price (${_currency})`, marketPrice) // eslint-disable-line no-console
/* Validate market price. */
if (marketPrice) {
/* Format balance (for display). */
// TODO: Support additional currencies.
const formattedBalance =
rootGetters['utils/getFormattedValue'](balance, marketPrice, 'USD')
/* Return (formatted) balance. */
return formattedBalance
} else {
/* Return (empty) balance. */
return null
}
}
/* Export module. */
export default getBalance
export default getBalanceBySessionId
......@@ -13,6 +13,8 @@
<div class="account-content dashboard">
<h3 class="account-title">Bitcoin Cash Wallet</h3>
<div class="qr-code float-right d-none d-md-block m-3" v-html="qr" />
<div class="row my-3">
<div class="col-1">
<!-- offset fix -->
......@@ -23,7 +25,7 @@
</div>
<div class="col-8">
<a :href="'https://explorer.bitcoin.com/bch/address/' + getAddress" target="_blank">{{getAddress}}</a>
<a :href="'https://explorer.bitcoin.com/bch/address/' + getAddress('deposit')" target="_blank">{{getAddress('deposit')}}</a>
</div>
<div class="col-1">
......@@ -63,7 +65,21 @@
</div>
<div class="col-8">
$0.00
{{balanceDisplay}}
</div>
<div class="col-1">
<!-- offset fix -->
</div>
</div>
<div class="row my-3">
<div class="col-1">
<!-- offset fix -->
</div>
<div class="col-10">
<div class="qr-code text-center d-md-none" v-html="qr" />
</div>
<div class="col-1">
......@@ -86,6 +102,10 @@
/* Initialize vuex. */
import { mapActions, mapGetters } from 'vuex'
/* Import modules. */
import Nito from 'nitojs'
import QRCode from 'qrcode'
/* Import components. */
import Footer from '@/components/Footer.vue'
import Header from '@/components/Header.vue'
......@@ -105,6 +125,9 @@ export default {
data: () => {
return {
ownerSlug: null,
blockchain: null,
balance: null,
showMnemonic: null,
}
},
......@@ -115,8 +138,52 @@ export default {
...mapGetters('wallet', [
'getAddress',
'getBalance',
'getMnemonic',
]),
balanceDisplay() {
if (!this.balance) {
return 'loading...'
}
const balance = `${this.balance.rounded} ${this.balance.unit} | ${this.balance.fiat}`
return balance
},
qr() {
if (!this.getAddress('deposit')) {
return null
}
/* Initialize (string) value. */
let strValue = ''
/* Initialize scanner parameters. */
const params = {
type: 'svg',
width: 225,
height: 225,
color: {
dark: '#000',
light: '#fff'
}
}
QRCode.toString(this.getAddress('deposit'), params, (err, value) => {
if (err) {
return console.error('QR Code ERROR:', err)
}
/* Set (string) value. */
strValue = value
})
/* Return (string) value. */
return strValue
},
},
methods: {
...mapActions([
......@@ -124,13 +191,97 @@ export default {
'displayNotification',
]),
// ...mapActions('campaigns', [
// 'addCampaign',
// ]),
...mapActions('wallet', [
// 'updateCoins',
]),
toggleMnemonic() {
this.showMnemonic = !this.showMnemonic
},
/**
* Initialize Blockchain
*/
initBlockchain() {
/* Initialize Nito blockchain. */
this.blockchain = new Nito.Blockchain()
console.log('NITO BLOCKCHAIN', this.blockchain)
if (this.depositAddress) {
/* Subscribe to address updates. */
const watching = this.blockchain
.subscribe('address', this.depositAddress)
console.log('DEPOSIT (watching):', watching)
}
/* Handle blockchain updates. */
this.blockchain.on('update', (_msg) => {
console.log('DEPOSIT RECEIVED BLOCKCHAIN UPDATE (msg):', _msg)
/* Update coins. */
// FIXME: Why is this blocking the entire initial UI setup??
this.updateCoins()
})
},
/**
* Update Balance
*/
async updateBalance() {
/* Retreive session balance. */
this.balance = await this
.getBalance('USD')
.catch(err => console.error(err))
console.log('DEPOSIT (balance):', this.balance)
},
/**
* Set Clipboard
*/
setClipboard() {
try {
const textArea = document.createElement('textarea')
textArea.value = this.depositAddress
document.body.appendChild(textArea)
if (navigator.userAgent.match(/ipad|iphone/i)) {
const range = document.createRange()
range.selectNodeContents(textArea)
const selection = window.getSelection()
selection.removeAllRanges()
selection.addRange(range)
textArea.setSelectionRange(0, 999999)
} else {
textArea.select()
}
document.execCommand('copy')
document.body.removeChild(textArea)
/* Set message. */
const message = `Your deposit address has been copied to your clipboard.`
/* Display notification. */
this.$notify({
message,
icon: 'ti-info-alt', // ti-info-alt | ti-alert
verticalAlign: 'top',
horizontalAlign: 'right',
type: 'info', // info | danger
// timeout: 0, // 0: persistent | 5000: default
})
return true
} catch (err) {
console.error(err) // eslint-disable-line no-console
/* Bugsnag alert. */
throw new Error(err)
}
},
},
created: function () {
/* Set owner slug. */
......@@ -138,6 +289,19 @@ export default {
/* Initialize mnemonic flag. */
this.showMnemonic = false
/* Initialize blockchain. */
this.initBlockchain()
/* Update balance. */
this.updateBalance()
},
beforeDestroy() {
/* Validate blockchain. */
if (this.blockchain) {
/* Stop blockchain. */
this.blockchain.stop()
}
},
}
</script>
......@@ -146,4 +310,13 @@ export default {
.mnemonic {
cursor: pointer;
}
.qr-code {
/* margin: 0 0 10px 10px; */
/* margin: 20px auto; */
padding: 0;
/* border: 1pt solid rgba(200, 200, 200, 0.2); */
/* float: right; */
}
</style>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment