refactor: extract address-list component
This commit is contained in:
parent
8376f08e8b
commit
ff29aacace
6 changed files with 431 additions and 368 deletions
|
|
@ -48,7 +48,7 @@ You can now use this wallet on the LNBits [SatsPayServer](https://github.com/lnb
|
||||||
- shows the UTXOs for all wallets
|
- shows the UTXOs for all wallets
|
||||||
- there can be multiple UTXOs for the same address
|
- there can be multiple UTXOs for the same address
|
||||||
|
|
||||||
### Make Payment
|
### New Payment
|
||||||
- create a new `Partially Signed Bitcoin Transaction`
|
- create a new `Partially Signed Bitcoin Transaction`
|
||||||
- multiple `Send Addresses` can be added
|
- multiple `Send Addresses` can be added
|
||||||
- the `Max` button next to an address is for sending the remaining funds to this address (no change)
|
- the `Max` button next to an address is for sending the remaining funds to this address (no change)
|
||||||
|
|
@ -57,7 +57,7 @@ You can now use this wallet on the LNBits [SatsPayServer](https://github.com/lnb
|
||||||
- `Show Advanced` allows to (see `screenshot 2`):
|
- `Show Advanced` allows to (see `screenshot 2`):
|
||||||
- select from which account the change address will be selected (defaults to the first one)
|
- select from which account the change address will be selected (defaults to the first one)
|
||||||
- select the `Fee Rate`
|
- select the `Fee Rate`
|
||||||
- it defaults to the `Medium` value at the moment the `Make Payment` button was clicked
|
- it defaults to the `Medium` value at the moment the `New Payment` button was clicked
|
||||||
- it can be refreshed
|
- it can be refreshed
|
||||||
- warnings are shown if the fee is too Low or to High
|
- warnings are shown if the fee is too Low or to High
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,204 @@
|
||||||
|
<div>
|
||||||
|
<div class="row items-center no-wrap q-mb-md">
|
||||||
|
<div class="col q-pr-lg">
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
clearable
|
||||||
|
dense
|
||||||
|
emit-value
|
||||||
|
v-model="selectedWallet"
|
||||||
|
:options="accounts"
|
||||||
|
label="Wallet Account"
|
||||||
|
></q-select>
|
||||||
|
</div>
|
||||||
|
<div class="col q-pr-lg">
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
clearable
|
||||||
|
dense
|
||||||
|
emit-value
|
||||||
|
multiple
|
||||||
|
:options="filterOptions"
|
||||||
|
v-model="filterValues"
|
||||||
|
label="Filter"
|
||||||
|
></q-select>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<q-input
|
||||||
|
borderless
|
||||||
|
dense
|
||||||
|
debounce="300"
|
||||||
|
v-model="addressesTable.filter"
|
||||||
|
placeholder="Search"
|
||||||
|
>
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-icon name="search"></q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-table
|
||||||
|
style="height: 400px"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
:data="getFilteredAddresses()"
|
||||||
|
row-key="id"
|
||||||
|
virtual-scroll
|
||||||
|
:columns="addressesTable.columns"
|
||||||
|
:pagination.sync="addressesTable.pagination"
|
||||||
|
:filter="addressesTable.filter"
|
||||||
|
>
|
||||||
|
<template v-slot:body="props">
|
||||||
|
<q-tr :props="props">
|
||||||
|
<q-td auto-width>
|
||||||
|
<q-btn
|
||||||
|
size="sm"
|
||||||
|
color="accent"
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
@click="props.row.expanded= !props.row.expanded"
|
||||||
|
:icon="props.row.expanded? 'remove' : 'add'"
|
||||||
|
/>
|
||||||
|
</q-td>
|
||||||
|
|
||||||
|
<q-td key="address" :props="props">
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
style="color: unset"
|
||||||
|
:href="mempool_endpoint + '/address/' + props.row.address"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{props.row.address}}</a
|
||||||
|
>
|
||||||
|
<q-badge
|
||||||
|
v-if="props.row.branch_index === 1"
|
||||||
|
color="orange"
|
||||||
|
class="q-mr-md"
|
||||||
|
outline
|
||||||
|
>
|
||||||
|
change
|
||||||
|
</q-badge>
|
||||||
|
<q-btn
|
||||||
|
v-if="props.row.gapLimitExceeded"
|
||||||
|
color="yellow"
|
||||||
|
icon="warning"
|
||||||
|
title="Gap Limit Exceeded"
|
||||||
|
@click="props.row.expanded= !props.row.expanded"
|
||||||
|
outline
|
||||||
|
class="q-ml-md"
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</q-td>
|
||||||
|
|
||||||
|
<q-td
|
||||||
|
key="amount"
|
||||||
|
:props="props"
|
||||||
|
:class="props.row.amount > 0 ? 'text-green-13 text-weight-bold' : ''"
|
||||||
|
>
|
||||||
|
<div>{{satBtc(props.row.amount)}}</div>
|
||||||
|
</q-td>
|
||||||
|
|
||||||
|
<q-td key="note" :props="props" :class="">
|
||||||
|
<div>{{props.row.note}}</div>
|
||||||
|
</q-td>
|
||||||
|
<q-td key="wallet" :props="props" :class="">
|
||||||
|
<div>{{getWalletName(props.row.wallet)}}</div>
|
||||||
|
</q-td>
|
||||||
|
</q-tr>
|
||||||
|
<q-tr v-show="props.row.expanded" :props="props">
|
||||||
|
<q-td colspan="100%">
|
||||||
|
<div class="row items-center q-mt-md q-mb-lg">
|
||||||
|
<div class="col-2 q-pr-lg"></div>
|
||||||
|
<div class="col-4 q-pr-lg">
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
dense
|
||||||
|
size="md"
|
||||||
|
icon="qr_code"
|
||||||
|
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
|
||||||
|
@click="showAddressDetails(props.row)"
|
||||||
|
>
|
||||||
|
QR Code</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 q-pr-lg">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
dense
|
||||||
|
size="md"
|
||||||
|
icon="refresh"
|
||||||
|
color="grey"
|
||||||
|
@click="scanAddress(props.row)"
|
||||||
|
>
|
||||||
|
Rescan</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 q-pr-lg">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
dense
|
||||||
|
size="md"
|
||||||
|
icon="history"
|
||||||
|
color="grey"
|
||||||
|
@click="searchInTab('history', props.row.address)"
|
||||||
|
>History</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 q-pr-lg">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
dense
|
||||||
|
size="md"
|
||||||
|
color="grey"
|
||||||
|
@click="searchInTab('utxos', props.row.address)"
|
||||||
|
>View Coins</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row items-center no-wrap q-mb-md">
|
||||||
|
<div class="col-2 q-pr-lg">Note:</div>
|
||||||
|
<div class="col-8 q-pr-lg">
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.trim="props.row.note"
|
||||||
|
type="text"
|
||||||
|
label="Note"
|
||||||
|
></q-input>
|
||||||
|
</div>
|
||||||
|
<div class="col-2 q-pr-lg">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
color="grey"
|
||||||
|
@click="updateNoteForAddress(props.row, props.row.note)"
|
||||||
|
>Update
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="props.row.error" class="row items-center no-wrap q-mb-md">
|
||||||
|
<div class="col-2 q-pr-lg"></div>
|
||||||
|
<div class="col-10 q-pr-lg">
|
||||||
|
<q-badge color="red">{{props.row.error}}</q-badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="props.row.gapLimitExceeded"
|
||||||
|
class="row items-center no-wrap q-mb-md"
|
||||||
|
>
|
||||||
|
<div class="col-2 q-pr-lg"></div>
|
||||||
|
<div class="col-10 q-pr-lg">
|
||||||
|
<q-badge color="yellow" text-color="black"
|
||||||
|
>Gap limit of 20 addresses exceeded. Other wallets might not
|
||||||
|
detect funds at this address.</q-badge
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-td>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,169 @@
|
||||||
|
async function addressList(path) {
|
||||||
|
const template = await loadTemplateAsync(path)
|
||||||
|
Vue.component('address-list', {
|
||||||
|
name: 'address-list',
|
||||||
|
template,
|
||||||
|
|
||||||
|
props: ['accounts', 'mempool_endpoint', 'inkey'],
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
addresses: [],
|
||||||
|
show: false,
|
||||||
|
data: [],
|
||||||
|
history: [],
|
||||||
|
selectedWallet: null,
|
||||||
|
note: '',
|
||||||
|
filterOptions: [
|
||||||
|
'Show Change Addresses',
|
||||||
|
'Show Gap Addresses',
|
||||||
|
'Only With Amount'
|
||||||
|
],
|
||||||
|
filterValues: [],
|
||||||
|
|
||||||
|
addressesTable: {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'expand',
|
||||||
|
align: 'left',
|
||||||
|
label: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'address',
|
||||||
|
align: 'left',
|
||||||
|
label: 'Address',
|
||||||
|
field: 'address',
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'amount',
|
||||||
|
align: 'left',
|
||||||
|
label: 'Amount',
|
||||||
|
field: 'amount',
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'note',
|
||||||
|
align: 'left',
|
||||||
|
label: 'Note',
|
||||||
|
field: 'note',
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'wallet',
|
||||||
|
align: 'left',
|
||||||
|
label: 'Account',
|
||||||
|
field: 'wallet',
|
||||||
|
sortable: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
pagination: {
|
||||||
|
rowsPerPage: 0,
|
||||||
|
sortBy: 'amount',
|
||||||
|
descending: true
|
||||||
|
},
|
||||||
|
filter: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
immediate: true,
|
||||||
|
accounts(newVal, oldVal) {
|
||||||
|
if ((newVal || []).length !== (oldVal || []).length) {
|
||||||
|
console.log('### refreshAddresses')
|
||||||
|
this.refreshAddresses() // todo await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
satBtc(val, showUnit = true) {
|
||||||
|
return satOrBtc(val, showUnit, this['sats_denominated'])
|
||||||
|
},
|
||||||
|
getWalletName: function (walletId) {
|
||||||
|
const wallet = (this.accounts || []).find(wl => wl.id === walletId)
|
||||||
|
return wallet ? wallet.title : 'unknown'
|
||||||
|
},
|
||||||
|
getFilteredAddresses: function () {
|
||||||
|
const selectedWalletId = this.selectedWallet?.id
|
||||||
|
const filter = this.filterValues || []
|
||||||
|
const includeChangeAddrs = filter.includes('Show Change Addresses')
|
||||||
|
const includeGapAddrs = filter.includes('Show Gap Addresses')
|
||||||
|
const excludeNoAmount = filter.includes('Only With Amount')
|
||||||
|
|
||||||
|
const walletsLimit = (this.accounts || []).reduce((r, w) => {
|
||||||
|
r[`_${w.id}`] = w.address_no
|
||||||
|
return r
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
console.log('### walletsLimit', walletsLimit)
|
||||||
|
console.log('### this.addresses', this.addresses)
|
||||||
|
const fAddresses = this.addresses.filter(
|
||||||
|
a =>
|
||||||
|
(includeChangeAddrs || !a.isChange) &&
|
||||||
|
(includeGapAddrs ||
|
||||||
|
a.isChange ||
|
||||||
|
a.addressIndex <= walletsLimit[`_${a.wallet}`]) &&
|
||||||
|
!(excludeNoAmount && a.amount === 0) &&
|
||||||
|
(!selectedWalletId || a.wallet === selectedWalletId)
|
||||||
|
)
|
||||||
|
console.log('### fAddresses', fAddresses)
|
||||||
|
return fAddresses
|
||||||
|
},
|
||||||
|
getAddressesForWallet: async function (walletId) {
|
||||||
|
try {
|
||||||
|
const {data} = await LNbits.api.request(
|
||||||
|
'GET',
|
||||||
|
'/watchonly/api/v1/addresses/' + walletId,
|
||||||
|
this.inkey
|
||||||
|
)
|
||||||
|
return data.map(mapAddressesData)
|
||||||
|
} catch (err) {
|
||||||
|
this.$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: `Failed to fetch addresses for wallet with id ${walletId}.`,
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
LNbits.utils.notifyApiError(err)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
refreshAddresses: async function () {
|
||||||
|
console.log('### refreshAddresses, this.accounts', this.accounts)
|
||||||
|
if (!this.accounts) return
|
||||||
|
this.addresses = []
|
||||||
|
for (const {id, type} of this.accounts) {
|
||||||
|
const newAddresses = await this.getAddressesForWallet(id)
|
||||||
|
const uniqueAddresses = newAddresses.filter(
|
||||||
|
newAddr => !this.addresses.find(a => a.address === newAddr.address)
|
||||||
|
)
|
||||||
|
|
||||||
|
const lastAcctiveAddress =
|
||||||
|
uniqueAddresses.filter(a => !a.isChange && a.hasActivity).pop() ||
|
||||||
|
{}
|
||||||
|
|
||||||
|
uniqueAddresses.forEach(a => {
|
||||||
|
a.expanded = false
|
||||||
|
a.accountType = type
|
||||||
|
a.gapLimitExceeded =
|
||||||
|
!a.isChange &&
|
||||||
|
a.addressIndex >
|
||||||
|
lastAcctiveAddress.addressIndex + DEFAULT_RECEIVE_GAP_LIMIT
|
||||||
|
})
|
||||||
|
this.addresses.push(...uniqueAddresses)
|
||||||
|
}
|
||||||
|
console.log('### refreshAddresses, this.addresse', this.addresses)
|
||||||
|
this.$emit('update:addresses', this.addresses)
|
||||||
|
},
|
||||||
|
scanAddress: async function (addressData) {
|
||||||
|
this.$emit('scan:address', addressData)
|
||||||
|
},
|
||||||
|
showAddressDetails: function (addressData) {
|
||||||
|
this.$emit('show-address-details', addressData)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created: async function () {
|
||||||
|
await this.refreshAddresses()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ const watchOnly = async () => {
|
||||||
|
|
||||||
await walletConfig('static/components/wallet-config/wallet-config.html')
|
await walletConfig('static/components/wallet-config/wallet-config.html')
|
||||||
await walletList('static/components/wallet-list/wallet-list.html')
|
await walletList('static/components/wallet-list/wallet-list.html')
|
||||||
|
await addressList('static/components/address-list/address-list.html')
|
||||||
|
|
||||||
Vue.filter('reverse', function (value) {
|
Vue.filter('reverse', function (value) {
|
||||||
// slice to make a copy of array, then reverse the copy
|
// slice to make a copy of array, then reverse the copy
|
||||||
|
|
@ -71,7 +72,12 @@ const watchOnly = async () => {
|
||||||
...tables,
|
...tables,
|
||||||
...tableData,
|
...tableData,
|
||||||
|
|
||||||
walletAccounts: []
|
walletAccounts: [],
|
||||||
|
addresses: [],
|
||||||
|
history: [],
|
||||||
|
|
||||||
|
showAddress: false,
|
||||||
|
addressNote: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -79,56 +85,13 @@ const watchOnly = async () => {
|
||||||
//################### CONFIG ###################
|
//################### CONFIG ###################
|
||||||
|
|
||||||
//################### WALLETS ###################
|
//################### WALLETS ###################
|
||||||
getAddressesForWallet: async function (walletId) {
|
|
||||||
try {
|
|
||||||
const {data} = await LNbits.api.request(
|
|
||||||
'GET',
|
|
||||||
'/watchonly/api/v1/addresses/' + walletId,
|
|
||||||
this.g.user.wallets[0].inkey
|
|
||||||
)
|
|
||||||
return data.map(mapAddressesData)
|
|
||||||
} catch (err) {
|
|
||||||
this.$q.notify({
|
|
||||||
type: 'warning',
|
|
||||||
message: `Failed to fetch addresses for wallet with id ${walletId}.`,
|
|
||||||
timeout: 10000
|
|
||||||
})
|
|
||||||
LNbits.utils.notifyApiError(err)
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
},
|
|
||||||
getWalletName: function (walletId) {
|
getWalletName: function (walletId) {
|
||||||
const wallet = this.walletAccounts.find(wl => wl.id === walletId)
|
const wallet = this.walletAccounts.find(wl => wl.id === walletId)
|
||||||
return wallet ? wallet.title : 'unknown'
|
return wallet ? wallet.title : 'unknown'
|
||||||
},
|
},
|
||||||
//################### ADDRESSES ###################
|
//################### ADDRESSES ###################
|
||||||
|
|
||||||
refreshAddresses: async function () {
|
|
||||||
// const wallets = await this.getWatchOnlyWallets() todo: revisit
|
|
||||||
// const wallets =
|
|
||||||
this.addresses.data = []
|
|
||||||
for (const {id, type} of this.walletAccounts) {
|
|
||||||
const newAddresses = await this.getAddressesForWallet(id)
|
|
||||||
const uniqueAddresses = newAddresses.filter(
|
|
||||||
newAddr =>
|
|
||||||
!this.addresses.data.find(a => a.address === newAddr.address)
|
|
||||||
)
|
|
||||||
|
|
||||||
const lastAcctiveAddress =
|
|
||||||
uniqueAddresses.filter(a => !a.isChange && a.hasActivity).pop() ||
|
|
||||||
{}
|
|
||||||
|
|
||||||
uniqueAddresses.forEach(a => {
|
|
||||||
a.expanded = false
|
|
||||||
a.accountType = type
|
|
||||||
a.gapLimitExceeded =
|
|
||||||
!a.isChange &&
|
|
||||||
a.addressIndex >
|
|
||||||
lastAcctiveAddress.addressIndex + DEFAULT_RECEIVE_GAP_LIMIT
|
|
||||||
})
|
|
||||||
this.addresses.data.push(...uniqueAddresses)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateAmountForAddress: async function (addressData, amount = 0) {
|
updateAmountForAddress: async function (addressData, amount = 0) {
|
||||||
try {
|
try {
|
||||||
const wallet = this.g.user.wallets[0]
|
const wallet = this.g.user.wallets[0]
|
||||||
|
|
@ -171,35 +134,12 @@ const watchOnly = async () => {
|
||||||
{note: addressData.note}
|
{note: addressData.note}
|
||||||
)
|
)
|
||||||
const updatedAddress =
|
const updatedAddress =
|
||||||
this.addresses.data.find(a => a.id === addressData.id) || {}
|
this.addresses.find(a => a.id === addressData.id) || {}
|
||||||
updatedAddress.note = note
|
updatedAddress.note = note
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
LNbits.utils.notifyApiError(err)
|
LNbits.utils.notifyApiError(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getFilteredAddresses: function () {
|
|
||||||
const selectedWalletId = this.addresses.selectedWallet?.id
|
|
||||||
const filter = this.addresses.filterValues || []
|
|
||||||
const includeChangeAddrs = filter.includes('Show Change Addresses')
|
|
||||||
const includeGapAddrs = filter.includes('Show Gap Addresses')
|
|
||||||
const excludeNoAmount = filter.includes('Only With Amount')
|
|
||||||
|
|
||||||
const walletsLimit = this.walletAccounts.reduce((r, w) => {
|
|
||||||
r[`_${w.id}`] = w.address_no
|
|
||||||
return r
|
|
||||||
}, {})
|
|
||||||
|
|
||||||
const addresses = this.addresses.data.filter(
|
|
||||||
a =>
|
|
||||||
(includeChangeAddrs || !a.isChange) &&
|
|
||||||
(includeGapAddrs ||
|
|
||||||
a.isChange ||
|
|
||||||
a.addressIndex <= walletsLimit[`_${a.wallet}`]) &&
|
|
||||||
!(excludeNoAmount && a.amount === 0) &&
|
|
||||||
(!selectedWalletId || a.wallet === selectedWalletId)
|
|
||||||
)
|
|
||||||
return addresses
|
|
||||||
},
|
|
||||||
|
|
||||||
//################### ADDRESS HISTORY ###################
|
//################### ADDRESS HISTORY ###################
|
||||||
addressHistoryFromTxs: function (addressData, txs) {
|
addressHistoryFromTxs: function (addressData, txs) {
|
||||||
|
|
@ -219,9 +159,7 @@ const watchOnly = async () => {
|
||||||
return addressHistory
|
return addressHistory
|
||||||
},
|
},
|
||||||
getFilteredAddressesHistory: function () {
|
getFilteredAddressesHistory: function () {
|
||||||
return this.addresses.history.filter(
|
return this.history.filter(a => (!a.isChange || a.sent) && !a.isSubItem)
|
||||||
a => (!a.isChange || a.sent) && !a.isSubItem
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
exportHistoryToCSV: function () {
|
exportHistoryToCSV: function () {
|
||||||
const history = this.getFilteredAddressesHistory().map(a => ({
|
const history = this.getFilteredAddressesHistory().map(a => ({
|
||||||
|
|
@ -235,7 +173,7 @@ const watchOnly = async () => {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
markSameTxAddressHistory: function () {
|
markSameTxAddressHistory: function () {
|
||||||
this.addresses.history
|
this.history
|
||||||
.filter(s => s.sent)
|
.filter(s => s.sent)
|
||||||
.forEach((el, i, arr) => {
|
.forEach((el, i, arr) => {
|
||||||
if (el.isSubItem) return
|
if (el.isSubItem) return
|
||||||
|
|
@ -321,7 +259,7 @@ const watchOnly = async () => {
|
||||||
},
|
},
|
||||||
initPaymentData: async function () {
|
initPaymentData: async function () {
|
||||||
if (!this.payment.show) return
|
if (!this.payment.show) return
|
||||||
await this.refreshAddresses()
|
await this.$refs.addressList.refreshAddresses()
|
||||||
|
|
||||||
this.payment.showAdvanced = false
|
this.payment.showAdvanced = false
|
||||||
this.payment.changeWallet = this.walletAccounts[0]
|
this.payment.changeWallet = this.walletAccounts[0]
|
||||||
|
|
@ -347,7 +285,7 @@ const watchOnly = async () => {
|
||||||
},
|
},
|
||||||
selectChangeAddress: function (wallet = {}) {
|
selectChangeAddress: function (wallet = {}) {
|
||||||
this.payment.changeAddress =
|
this.payment.changeAddress =
|
||||||
this.addresses.data.find(
|
this.addresses.find(
|
||||||
a => a.wallet === wallet.id && a.isChange && !a.hasActivity
|
a => a.wallet === wallet.id && a.isChange && !a.hasActivity
|
||||||
) || {}
|
) || {}
|
||||||
},
|
},
|
||||||
|
|
@ -840,18 +778,18 @@ const watchOnly = async () => {
|
||||||
},
|
},
|
||||||
//################### UTXOs ###################
|
//################### UTXOs ###################
|
||||||
scanAllAddresses: async function () {
|
scanAllAddresses: async function () {
|
||||||
await this.refreshAddresses()
|
await this.$refs.addressList.refreshAddresses()
|
||||||
this.addresses.history = []
|
this.history = []
|
||||||
let addresses = this.addresses.data
|
let addresses = this.addresses
|
||||||
this.utxos.data = []
|
this.utxos.data = []
|
||||||
this.utxos.total = 0
|
this.utxos.total = 0
|
||||||
// Loop while new funds are found on the gap adresses.
|
// Loop while new funds are found on the gap adresses.
|
||||||
// Use 1000 limit as a safety check (scan 20 000 addresses max)
|
// Use 1000 limit as a safety check (scan 20 000 addresses max)
|
||||||
for (let i = 0; i < 1000 && addresses.length; i++) {
|
for (let i = 0; i < 1000 && addresses.length; i++) {
|
||||||
await this.updateUtxosForAddresses(addresses)
|
await this.updateUtxosForAddresses(addresses)
|
||||||
const oldAddresses = this.addresses.data.slice()
|
const oldAddresses = this.addresses.slice()
|
||||||
await this.refreshAddresses()
|
await this.$refs.addressList.refreshAddresses()
|
||||||
const newAddresses = this.addresses.data.slice()
|
const newAddresses = this.addresses.slice()
|
||||||
// check if gap addresses have been extended
|
// check if gap addresses have been extended
|
||||||
addresses = newAddresses.filter(
|
addresses = newAddresses.filter(
|
||||||
newAddr => !oldAddresses.find(oldAddr => oldAddr.id === newAddr.id)
|
newAddr => !oldAddresses.find(oldAddr => oldAddr.id === newAddr.id)
|
||||||
|
|
@ -868,11 +806,12 @@ const watchOnly = async () => {
|
||||||
scanAddressWithAmount: async function () {
|
scanAddressWithAmount: async function () {
|
||||||
this.utxos.data = []
|
this.utxos.data = []
|
||||||
this.utxos.total = 0
|
this.utxos.total = 0
|
||||||
this.addresses.history = []
|
this.history = []
|
||||||
const addresses = this.addresses.data.filter(a => a.hasActivity)
|
const addresses = this.addresses.filter(a => a.hasActivity)
|
||||||
await this.updateUtxosForAddresses(addresses)
|
await this.updateUtxosForAddresses(addresses)
|
||||||
},
|
},
|
||||||
scanAddress: async function (addressData) {
|
scanAddress: async function (addressData) {
|
||||||
|
console.log('### scanAddress', addressData)
|
||||||
this.updateUtxosForAddresses([addressData])
|
this.updateUtxosForAddresses([addressData])
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
|
|
@ -887,15 +826,13 @@ const watchOnly = async () => {
|
||||||
for (addrData of addresses) {
|
for (addrData of addresses) {
|
||||||
const addressHistory = await this.getAddressTxsDelayed(addrData)
|
const addressHistory = await this.getAddressTxsDelayed(addrData)
|
||||||
// remove old entries
|
// remove old entries
|
||||||
this.addresses.history = this.addresses.history.filter(
|
this.history = this.history.filter(
|
||||||
h => h.address !== addrData.address
|
h => h.address !== addrData.address
|
||||||
)
|
)
|
||||||
|
|
||||||
// add new entrie
|
// add new entrie
|
||||||
this.addresses.history.push(...addressHistory)
|
this.history.push(...addressHistory)
|
||||||
this.addresses.history.sort((a, b) =>
|
this.history.sort((a, b) => (!a.height ? -1 : b.height - a.height))
|
||||||
!a.height ? -1 : b.height - a.height
|
|
||||||
)
|
|
||||||
this.markSameTxAddressHistory()
|
this.markSameTxAddressHistory()
|
||||||
|
|
||||||
if (addressHistory.length) {
|
if (addressHistory.length) {
|
||||||
|
|
@ -1038,9 +975,10 @@ const watchOnly = async () => {
|
||||||
//################### OTHER ###################
|
//################### OTHER ###################
|
||||||
|
|
||||||
openQrCodeDialog: function (addressData) {
|
openQrCodeDialog: function (addressData) {
|
||||||
|
console.log('### addressData', addressData)
|
||||||
this.currentAddress = addressData
|
this.currentAddress = addressData
|
||||||
this.addresses.note = addressData.note || ''
|
this.addressNote = addressData.note || ''
|
||||||
this.addresses.show = true
|
this.showAddress = true
|
||||||
},
|
},
|
||||||
searchInTab: function (tab, value) {
|
searchInTab: function (tab, value) {
|
||||||
this.tab = tab
|
this.tab = tab
|
||||||
|
|
@ -1052,7 +990,7 @@ const watchOnly = async () => {
|
||||||
},
|
},
|
||||||
updateAccounts: async function (accounts) {
|
updateAccounts: async function (accounts) {
|
||||||
this.walletAccounts = accounts
|
this.walletAccounts = accounts
|
||||||
await this.refreshAddresses()
|
// await this.refreshAddressesxx() // todo: automatic now?
|
||||||
await this.scanAddressWithAmount()
|
await this.scanAddressWithAmount()
|
||||||
|
|
||||||
if (this.payment.changeWallet) {
|
if (this.payment.changeWallet) {
|
||||||
|
|
@ -1066,13 +1004,17 @@ const watchOnly = async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleNewReceiveAddress: function (addressData) {
|
showAddressDetails: function (addressData) {
|
||||||
|
console.log('### showAddressDetails addressData', addressData)
|
||||||
this.openQrCodeDialog(addressData)
|
this.openQrCodeDialog(addressData)
|
||||||
|
},
|
||||||
|
handleAddressesUpdated: function (addresses) {
|
||||||
|
this.addresses = addresses
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created: async function () {
|
created: async function () {
|
||||||
if (this.g.user.wallets.length) {
|
if (this.g.user.wallets.length) {
|
||||||
await this.refreshAddresses()
|
// await this.refreshAddressesxxx() todo: done when <address-list is created
|
||||||
await this.scanAddressWithAmount()
|
await this.scanAddressWithAmount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,49 +87,7 @@ const tables = {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
addressesTable: {
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
name: 'expand',
|
|
||||||
align: 'left',
|
|
||||||
label: ''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'address',
|
|
||||||
align: 'left',
|
|
||||||
label: 'Address',
|
|
||||||
field: 'address',
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'amount',
|
|
||||||
align: 'left',
|
|
||||||
label: 'Amount',
|
|
||||||
field: 'amount',
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'note',
|
|
||||||
align: 'left',
|
|
||||||
label: 'Note',
|
|
||||||
field: 'note',
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'wallet',
|
|
||||||
align: 'left',
|
|
||||||
label: 'Account',
|
|
||||||
field: 'wallet',
|
|
||||||
sortable: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
pagination: {
|
|
||||||
rowsPerPage: 0,
|
|
||||||
sortBy: 'amount',
|
|
||||||
descending: true
|
|
||||||
},
|
|
||||||
filter: ''
|
|
||||||
},
|
|
||||||
historyTable: {
|
historyTable: {
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
|
|
@ -194,20 +152,6 @@ const tables = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableData = {
|
const tableData = {
|
||||||
// walletAccounts: [], // todo: remove?
|
|
||||||
addresses: {
|
|
||||||
show: false,
|
|
||||||
data: [],
|
|
||||||
history: [],
|
|
||||||
selectedWallet: null,
|
|
||||||
note: '',
|
|
||||||
filterOptions: [
|
|
||||||
'Show Change Addresses',
|
|
||||||
'Show Gap Addresses',
|
|
||||||
'Only With Amount'
|
|
||||||
],
|
|
||||||
filterValues: []
|
|
||||||
},
|
|
||||||
utxos: {
|
utxos: {
|
||||||
data: [],
|
data: [],
|
||||||
total: 0
|
total: 0
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,13 @@
|
||||||
:adminkey="g.user.wallets[0].adminkey"
|
:adminkey="g.user.wallets[0].adminkey"
|
||||||
:inkey="g.user.wallets[0].inkey"
|
:inkey="g.user.wallets[0].inkey"
|
||||||
:sats-denominated="config.data.sats_denominated"
|
:sats-denominated="config.data.sats_denominated"
|
||||||
:addresses="addresses.data"
|
:addresses="addresses"
|
||||||
@accounts-update="updateAccounts"
|
@accounts-update="updateAccounts"
|
||||||
@new-receive-address="handleNewReceiveAddress"
|
@new-receive-address="showAddressDetails"
|
||||||
>
|
>
|
||||||
</wallet-list>
|
</wallet-list>
|
||||||
|
|
||||||
{% raw %}
|
{% raw %}
|
||||||
<!-- :walletAccounts.sync="walletAccounts" -->
|
|
||||||
<!-- :walletAccounts="walletAccounts" -->
|
|
||||||
<q-card>
|
<q-card>
|
||||||
<div class="row q-pt-sm q-pb-sm items-center no-wrap q-mb-md">
|
<div class="row q-pt-sm q-pb-sm items-center no-wrap q-mb-md">
|
||||||
<div class="col-3 q-pl-md">
|
<div class="col-3 q-pl-md">
|
||||||
|
|
@ -46,7 +45,7 @@
|
||||||
class="btn-full"
|
class="btn-full"
|
||||||
@click="goToPaymentView"
|
@click="goToPaymentView"
|
||||||
:disabled="scan.scanning == true"
|
:disabled="scan.scanning == true"
|
||||||
>Make Payment</q-btn
|
>New Payment</q-btn
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -70,210 +69,16 @@
|
||||||
</q-tabs>
|
</q-tabs>
|
||||||
<q-tab-panels v-model="tab">
|
<q-tab-panels v-model="tab">
|
||||||
<q-tab-panel name="addresses">
|
<q-tab-panel name="addresses">
|
||||||
<div class="row items-center no-wrap q-mb-md">
|
<address-list
|
||||||
<div class="col q-pr-lg">
|
ref="addressList"
|
||||||
<q-select
|
:accounts="walletAccounts"
|
||||||
filled
|
:mempool_endpoint="config.data.mempool_endpoint"
|
||||||
clearable
|
@update:addresses="handleAddressesUpdated"
|
||||||
dense
|
@scan:address="scanAddress"
|
||||||
emit-value
|
@show-address-details="showAddressDetails"
|
||||||
v-model="addresses.selectedWallet"
|
:inkey="g.user.wallets[0].inkey"
|
||||||
:options="walletAccounts"
|
|
||||||
label="Wallet Account"
|
|
||||||
></q-select>
|
|
||||||
</div>
|
|
||||||
<div class="col q-pr-lg">
|
|
||||||
<q-select
|
|
||||||
filled
|
|
||||||
clearable
|
|
||||||
dense
|
|
||||||
emit-value
|
|
||||||
multiple
|
|
||||||
:options="addresses.filterOptions"
|
|
||||||
v-model="addresses.filterValues"
|
|
||||||
:options="walletAccounts"
|
|
||||||
label="Filter"
|
|
||||||
></q-select>
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<q-input
|
|
||||||
borderless
|
|
||||||
dense
|
|
||||||
debounce="300"
|
|
||||||
v-model="addressesTable.filter"
|
|
||||||
placeholder="Search"
|
|
||||||
>
|
>
|
||||||
<template v-slot:append>
|
</address-list>
|
||||||
<q-icon name="search"></q-icon>
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<q-table
|
|
||||||
style="height: 400px"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
:data="getFilteredAddresses()"
|
|
||||||
row-key="id"
|
|
||||||
virtual-scroll
|
|
||||||
:columns="addressesTable.columns"
|
|
||||||
:pagination.sync="addressesTable.pagination"
|
|
||||||
:filter="addressesTable.filter"
|
|
||||||
>
|
|
||||||
<template v-slot:body="props">
|
|
||||||
<q-tr :props="props">
|
|
||||||
<q-td auto-width>
|
|
||||||
<q-btn
|
|
||||||
size="sm"
|
|
||||||
color="accent"
|
|
||||||
round
|
|
||||||
dense
|
|
||||||
@click="props.row.expanded= !props.row.expanded"
|
|
||||||
:icon="props.row.expanded? 'remove' : 'add'"
|
|
||||||
/>
|
|
||||||
</q-td>
|
|
||||||
|
|
||||||
<q-td key="address" :props="props">
|
|
||||||
<div>
|
|
||||||
<a
|
|
||||||
style="color: unset"
|
|
||||||
:href="config.data.mempool_endpoint + '/address/' + props.row.address"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{{props.row.address}}</a
|
|
||||||
>
|
|
||||||
<q-badge
|
|
||||||
v-if="props.row.branch_index === 1"
|
|
||||||
color="orange"
|
|
||||||
class="q-mr-md"
|
|
||||||
outline
|
|
||||||
>
|
|
||||||
change
|
|
||||||
</q-badge>
|
|
||||||
<q-btn
|
|
||||||
v-if="props.row.gapLimitExceeded"
|
|
||||||
color="yellow"
|
|
||||||
icon="warning"
|
|
||||||
title="Gap Limit Exceeded"
|
|
||||||
@click="props.row.expanded= !props.row.expanded"
|
|
||||||
outline
|
|
||||||
class="q-ml-md"
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
</q-btn>
|
|
||||||
</div>
|
|
||||||
</q-td>
|
|
||||||
|
|
||||||
<q-td
|
|
||||||
key="amount"
|
|
||||||
:props="props"
|
|
||||||
:class="props.row.amount > 0 ? 'text-green-13 text-weight-bold' : ''"
|
|
||||||
>
|
|
||||||
<div>{{satBtc(props.row.amount)}}</div>
|
|
||||||
</q-td>
|
|
||||||
|
|
||||||
<q-td key="note" :props="props" :class="">
|
|
||||||
<div>{{props.row.note}}</div>
|
|
||||||
</q-td>
|
|
||||||
<q-td key="wallet" :props="props" :class="">
|
|
||||||
<div>{{getWalletName(props.row.wallet)}}</div>
|
|
||||||
</q-td>
|
|
||||||
</q-tr>
|
|
||||||
<q-tr v-show="props.row.expanded" :props="props">
|
|
||||||
<q-td colspan="100%">
|
|
||||||
<div class="row items-center q-mt-md q-mb-lg">
|
|
||||||
<div class="col-2 q-pr-lg"></div>
|
|
||||||
<div class="col-4 q-pr-lg">
|
|
||||||
<q-btn
|
|
||||||
unelevated
|
|
||||||
dense
|
|
||||||
size="md"
|
|
||||||
icon="qr_code"
|
|
||||||
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
|
|
||||||
@click="openQrCodeDialog(props.row)"
|
|
||||||
>QR Code</q-btn
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="col-2 q-pr-lg">
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
dense
|
|
||||||
size="md"
|
|
||||||
icon="refresh"
|
|
||||||
color="grey"
|
|
||||||
@click="scanAddress(props.row)"
|
|
||||||
>Rescan</q-btn
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="col-2 q-pr-lg">
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
dense
|
|
||||||
size="md"
|
|
||||||
icon="history"
|
|
||||||
color="grey"
|
|
||||||
@click="searchInTab('history', props.row.address)"
|
|
||||||
>History</q-btn
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="col-2 q-pr-lg">
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
dense
|
|
||||||
size="md"
|
|
||||||
color="grey"
|
|
||||||
@click="searchInTab('utxos', props.row.address)"
|
|
||||||
>View Coins</q-btn
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row items-center no-wrap q-mb-md">
|
|
||||||
<div class="col-2 q-pr-lg">Note:</div>
|
|
||||||
<div class="col-8 q-pr-lg">
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
v-model.trim="props.row.note"
|
|
||||||
type="text"
|
|
||||||
label="Note"
|
|
||||||
></q-input>
|
|
||||||
</div>
|
|
||||||
<div class="col-2 q-pr-lg">
|
|
||||||
<q-btn
|
|
||||||
outline
|
|
||||||
color="grey"
|
|
||||||
@click="updateNoteForAddress(props.row, props.row.note)"
|
|
||||||
>Update</q-btn
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="props.row.error"
|
|
||||||
class="row items-center no-wrap q-mb-md"
|
|
||||||
>
|
|
||||||
<div class="col-2 q-pr-lg"></div>
|
|
||||||
<div class="col-10 q-pr-lg">
|
|
||||||
<q-badge color="red">{{props.row.error}}</q-badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="props.row.gapLimitExceeded"
|
|
||||||
class="row items-center no-wrap q-mb-md"
|
|
||||||
>
|
|
||||||
<div class="col-2 q-pr-lg"></div>
|
|
||||||
<div class="col-10 q-pr-lg">
|
|
||||||
<q-badge color="yellow" text-color="black"
|
|
||||||
>Gap limit of 20 addresses exceeded. Other wallets
|
|
||||||
might not detect funds at this address.</q-badge
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-td>
|
|
||||||
</q-tr>
|
|
||||||
</template>
|
|
||||||
</q-table>
|
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
<q-tab-panel name="history">
|
<q-tab-panel name="history">
|
||||||
<div class="row items-center no-wrap q-mb-md">
|
<div class="row items-center no-wrap q-mb-md">
|
||||||
|
|
@ -427,7 +232,7 @@
|
||||||
<div class="row items-center no-wrap q-mb-md">
|
<div class="row items-center no-wrap q-mb-md">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<q-toggle
|
<q-toggle
|
||||||
label="Make Payment"
|
label="New Payment"
|
||||||
color="secodary"
|
color="secodary"
|
||||||
v-model="payment.show"
|
v-model="payment.show"
|
||||||
@input="initPaymentData()"
|
@input="initPaymentData()"
|
||||||
|
|
@ -1264,11 +1069,9 @@
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<q-dialog v-model="addresses.show" position="top">
|
|
||||||
<q-card v-if="addresses.data" class="q-pa-lg lnbits__dialog-card">
|
|
||||||
{% raw %}
|
{% raw %}
|
||||||
|
<q-dialog v-model="showAddress" position="top">
|
||||||
|
<q-card class="q-pa-lg lnbits__dialog-card">
|
||||||
<h5 class="text-subtitle1 q-my-none">Address Details</h5>
|
<h5 class="text-subtitle1 q-my-none">Address Details</h5>
|
||||||
<q-separator></q-separator><br />
|
<q-separator></q-separator><br />
|
||||||
|
|
||||||
|
|
@ -1296,7 +1099,7 @@
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
v-model.trim="addresses.note"
|
v-model.trim="addressNote"
|
||||||
type="text"
|
type="text"
|
||||||
label="Note"
|
label="Note"
|
||||||
></q-input>
|
></q-input>
|
||||||
|
|
@ -1313,7 +1116,7 @@
|
||||||
outline
|
outline
|
||||||
v-close-popup
|
v-close-popup
|
||||||
color="grey"
|
color="grey"
|
||||||
@click="updateNoteForAddress(currentAddress, addresses.note)"
|
@click="updateNoteForAddress(currentAddress, addressNote)"
|
||||||
class="q-ml-sm"
|
class="q-ml-sm"
|
||||||
>Save Note</q-btn
|
>Save Note</q-btn
|
||||||
>
|
>
|
||||||
|
|
@ -1473,5 +1276,6 @@
|
||||||
<script src="{{ url_for('watchonly_static', path='components/my-checkbox/my-checkbox.js') }}"></script>
|
<script src="{{ url_for('watchonly_static', path='components/my-checkbox/my-checkbox.js') }}"></script>
|
||||||
<script src="{{ url_for('watchonly_static', path='components/wallet-config/wallet-config.js') }}"></script>
|
<script src="{{ url_for('watchonly_static', path='components/wallet-config/wallet-config.js') }}"></script>
|
||||||
<script src="{{ url_for('watchonly_static', path='components/wallet-list/wallet-list.js') }}"></script>
|
<script src="{{ url_for('watchonly_static', path='components/wallet-list/wallet-list.js') }}"></script>
|
||||||
|
<script src="{{ url_for('watchonly_static', path='components/address-list/address-list.js') }}"></script>
|
||||||
<script src="{{ url_for('watchonly_static', path='js/index.js') }}"></script>
|
<script src="{{ url_for('watchonly_static', path='js/index.js') }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue