feat: propagate config update only when explicitly updated
This commit is contained in:
parent
657ed7a37c
commit
4671954896
11 changed files with 102 additions and 80 deletions
|
|
@ -18,6 +18,7 @@ class WalletAccount(BaseModel):
|
||||||
address_no: int
|
address_no: int
|
||||||
balance: int
|
balance: int
|
||||||
type: str = ""
|
type: str = ""
|
||||||
|
network: str = "Mainnet"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_row(cls, row: Row) -> "WalletAccount":
|
def from_row(cls, row: Row) -> "WalletAccount":
|
||||||
|
|
@ -100,3 +101,4 @@ class Config(BaseModel):
|
||||||
receive_gap_limit = 20
|
receive_gap_limit = 20
|
||||||
change_gap_limit = 5
|
change_gap_limit = 5
|
||||||
sats_denominated = True
|
sats_denominated = True
|
||||||
|
network = "Mainnet"
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,14 @@ async function feeRate(path) {
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshRecommendedFees: async function () {
|
refreshRecommendedFees: async function () {
|
||||||
const {
|
const fn = async () => {
|
||||||
bitcoin: {fees: feesAPI}
|
const {
|
||||||
} = mempoolJS({
|
bitcoin: {fees: feesAPI}
|
||||||
hostname: new URL(this.mempoolEndpoint).hostname
|
} = mempoolJS({
|
||||||
})
|
hostname: this.mempoolEndpoint
|
||||||
|
})
|
||||||
const fn = async () => feesAPI.getFeesRecommended()
|
return feesAPI.getFeesRecommended()
|
||||||
|
}
|
||||||
this.recommededFees = await retryWithDelay(fn)
|
this.recommededFees = await retryWithDelay(fn)
|
||||||
},
|
},
|
||||||
getFeeRateLabel: function (feeRate) {
|
getFeeRateLabel: function (feeRate) {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
<q-form @submit="checkAndSend" ref="paymentFormRef" class="q-gutter-md">
|
<q-form @submit="checkAndSend" ref="paymentFormRef" class="q-gutter-md">
|
||||||
<q-card class="q-mt-lg">
|
<q-card class="q-mt-lg">
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
|
|
||||||
<send-to
|
<send-to
|
||||||
:data.sync="sendToList"
|
:data.sync="sendToList"
|
||||||
:fee-rate="feeRate"
|
:fee-rate="feeRate"
|
||||||
|
|
@ -42,7 +41,11 @@
|
||||||
<div v-show="showCustomFee" class="row items-center no-wrap q-mt-md">
|
<div v-show="showCustomFee" class="row items-center no-wrap q-mt-md">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<q-separator class="q-mb-md"></q-separator>
|
<q-separator class="q-mb-md"></q-separator>
|
||||||
<fee-rate :fee-value="feeValue" :rate.sync="feeRate" :mempool-endpoint="mempoolEndpoint"></fee-rate>
|
<fee-rate
|
||||||
|
:fee-value="feeValue"
|
||||||
|
:rate.sync="feeRate"
|
||||||
|
:mempool-endpoint="mempoolEndpoint"
|
||||||
|
></fee-rate>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|
|
||||||
|
|
@ -292,7 +292,7 @@ async function payment(path) {
|
||||||
const {
|
const {
|
||||||
bitcoin: {transactions: transactionsAPI}
|
bitcoin: {transactions: transactionsAPI}
|
||||||
} = mempoolJS({
|
} = mempoolJS({
|
||||||
hostname: new URL(this.mempoolEndpoint).hostname
|
hostname: this.mempoolEndpoint
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,7 @@
|
||||||
<q-card>
|
<q-card>
|
||||||
<div class="row items-center no-wrap q-mb-md">
|
<div class="row items-center no-wrap q-mb-md">
|
||||||
<div class="col-2 q-ml-lg">
|
<div class="col-2 q-ml-lg">
|
||||||
<q-btn
|
<q-btn unelevated @click="show = true" color="primary" icon="settings">
|
||||||
unelevated
|
|
||||||
@click="config.show = true"
|
|
||||||
color="primary"
|
|
||||||
icon="settings"
|
|
||||||
>
|
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
|
|
@ -21,13 +16,13 @@
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
||||||
<q-dialog v-model="config.show" position="top">
|
<q-dialog v-model="show" position="top">
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
<q-form @submit="updateConfig" class="q-gutter-md">
|
<q-form @submit="updateConfig" class="q-gutter-md">
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
v-model.trim="config.data.mempool_endpoint"
|
v-model.trim="config.mempool_endpoint"
|
||||||
type="text"
|
type="text"
|
||||||
label="Mempool Endpoint"
|
label="Mempool Endpoint"
|
||||||
>
|
>
|
||||||
|
|
@ -36,7 +31,7 @@
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
v-model.number="config.data.receive_gap_limit"
|
v-model.number="config.receive_gap_limit"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
label="Receive Gap Limit"
|
label="Receive Gap Limit"
|
||||||
|
|
@ -45,7 +40,7 @@
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
v-model.number="config.data.change_gap_limit"
|
v-model.number="config.change_gap_limit"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
label="Change Gap Limit"
|
label="Change Gap Limit"
|
||||||
|
|
@ -55,15 +50,15 @@
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
emit-value
|
emit-value
|
||||||
v-model="config.data.network"
|
v-model="config.network"
|
||||||
:options="networOptions"
|
:options="networOptions"
|
||||||
label="Network"
|
label="Network"
|
||||||
></q-select>
|
></q-select>
|
||||||
|
|
||||||
<q-toggle
|
<q-toggle
|
||||||
:label="config.data.sats_denominated ? 'sats denominated' : 'BTC denominated'"
|
:label="config.sats_denominated ? 'sats denominated' : 'BTC denominated'"
|
||||||
color="secodary"
|
color="secodary"
|
||||||
v-model="config.data.sats_denominated"
|
v-model="config.sats_denominated"
|
||||||
></q-toggle>
|
></q-toggle>
|
||||||
|
|
||||||
<div class="row q-mt-lg">
|
<div class="row q-mt-lg">
|
||||||
|
|
@ -71,7 +66,7 @@
|
||||||
unelevated
|
unelevated
|
||||||
color="primary"
|
color="primary"
|
||||||
:disable="
|
:disable="
|
||||||
!config.data.mempool_endpoint "
|
!config.mempool_endpoint "
|
||||||
type="submit"
|
type="submit"
|
||||||
>Update</q-btn
|
>Update</q-btn
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,51 @@ async function walletConfig(path) {
|
||||||
name: 'wallet-config',
|
name: 'wallet-config',
|
||||||
template: t,
|
template: t,
|
||||||
|
|
||||||
props: ['total', 'config', 'adminkey'],
|
props: ['total', 'config-data', 'adminkey'],
|
||||||
data: function () {
|
data: function () {
|
||||||
return {}
|
return {
|
||||||
|
networOptions: ['Mainnet', 'Testnet'],
|
||||||
|
internalConfig: {
|
||||||
|
mempool_endpoint: 'https://mempool.space',
|
||||||
|
receive_gap_limit: 20,
|
||||||
|
change_gap_limit: 5
|
||||||
|
},
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
config: {
|
||||||
|
get() {
|
||||||
|
console.log('### get config', this.internalConfig)
|
||||||
|
return this.internalConfig
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
value.isLoaded = true
|
||||||
|
console.log('### set config', this.internalConfig)
|
||||||
|
this.internalConfig = JSON.parse(JSON.stringify(value))
|
||||||
|
this.$emit(
|
||||||
|
'update:config-data',
|
||||||
|
JSON.parse(JSON.stringify(this.internalConfig))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
satBtc(val, showUnit = true) {
|
satBtc(val, showUnit = true) {
|
||||||
return satOrBtc(val, showUnit, this.config.data.sats_denominated)
|
return satOrBtc(val, showUnit, this.config.sats_denominated)
|
||||||
},
|
},
|
||||||
updateConfig: async function () {
|
updateConfig: async function () {
|
||||||
try {
|
try {
|
||||||
await LNbits.api.request(
|
const {data} = await LNbits.api.request(
|
||||||
'PUT',
|
'PUT',
|
||||||
'/watchonly/api/v1/config',
|
'/watchonly/api/v1/config',
|
||||||
this.adminkey,
|
this.adminkey,
|
||||||
this.config.data
|
this.config
|
||||||
)
|
)
|
||||||
this.config.show = false
|
this.show = false
|
||||||
|
this.config = data
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
LNbits.utils.notifyApiError(error)
|
LNbits.utils.notifyApiError(error)
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +60,7 @@ async function walletConfig(path) {
|
||||||
'/watchonly/api/v1/config',
|
'/watchonly/api/v1/config',
|
||||||
this.adminkey
|
this.adminkey
|
||||||
)
|
)
|
||||||
this.config.data = data
|
this.config = data
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
LNbits.utils.notifyApiError(error)
|
LNbits.utils.notifyApiError(error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -168,15 +168,6 @@
|
||||||
label="Account Extended Public Key; xpub, ypub, zpub; Bitcoin Descriptor"
|
label="Account Extended Public Key; xpub, ypub, zpub; Bitcoin Descriptor"
|
||||||
></q-input>
|
></q-input>
|
||||||
|
|
||||||
<q-select
|
|
||||||
filled
|
|
||||||
dense
|
|
||||||
emit-value
|
|
||||||
v-model="formDialog.data.network"
|
|
||||||
:options="networOptions"
|
|
||||||
label="Network"
|
|
||||||
></q-select>
|
|
||||||
|
|
||||||
<div class="row q-mt-lg">
|
<div class="row q-mt-lg">
|
||||||
<q-btn
|
<q-btn
|
||||||
unelevated
|
unelevated
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ async function walletList(path) {
|
||||||
data: {}
|
data: {}
|
||||||
},
|
},
|
||||||
filter: '',
|
filter: '',
|
||||||
networOptions: ['Mainnet', 'Testnet'],
|
|
||||||
walletsTable: {
|
walletsTable: {
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -31,15 +31,7 @@ const watchOnly = async () => {
|
||||||
|
|
||||||
tab: 'addresses',
|
tab: 'addresses',
|
||||||
|
|
||||||
config: {
|
config: {sats_denominated: true},
|
||||||
data: {
|
|
||||||
mempool_endpoint: 'https://mempool.space',
|
|
||||||
receive_gap_limit: 20,
|
|
||||||
change_gap_limit: 5
|
|
||||||
},
|
|
||||||
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
|
|
||||||
qrCodeDialog: {
|
qrCodeDialog: {
|
||||||
show: false,
|
show: false,
|
||||||
|
|
@ -58,6 +50,16 @@ const watchOnly = async () => {
|
||||||
fetchedUtxos: false
|
fetchedUtxos: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
mempoolHostname() {
|
||||||
|
if (!this.config.isLoaded) return
|
||||||
|
const hostname = new URL(this.config.mempool_endpoint).hostname
|
||||||
|
if (this.config.network === 'testnet') {
|
||||||
|
hostname += '/testnet'
|
||||||
|
}
|
||||||
|
return hostname
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
updateAmountForAddress: async function (addressData, amount = 0) {
|
updateAmountForAddress: async function (addressData, amount = 0) {
|
||||||
|
|
@ -321,31 +323,32 @@ const watchOnly = async () => {
|
||||||
|
|
||||||
//################### MEMPOOL API ###################
|
//################### MEMPOOL API ###################
|
||||||
getAddressTxsDelayed: async function (addrData) {
|
getAddressTxsDelayed: async function (addrData) {
|
||||||
const {
|
const fn = async () => {
|
||||||
bitcoin: {addresses: addressesAPI}
|
const {
|
||||||
} = mempoolJS({
|
bitcoin: {addresses: addressesAPI}
|
||||||
hostname: new URL(this.config.data.mempool_endpoint).hostname
|
} = mempoolJS({
|
||||||
})
|
hostname: this.mempoolHostname
|
||||||
|
})
|
||||||
const fn = async () =>
|
return addressesAPI.getAddressTxs({
|
||||||
addressesAPI.getAddressTxs({
|
|
||||||
address: addrData.address
|
address: addrData.address
|
||||||
})
|
})
|
||||||
|
}
|
||||||
const addressTxs = await retryWithDelay(fn)
|
const addressTxs = await retryWithDelay(fn)
|
||||||
return this.addressHistoryFromTxs(addrData, addressTxs)
|
return this.addressHistoryFromTxs(addrData, addressTxs)
|
||||||
},
|
},
|
||||||
|
|
||||||
getAddressTxsUtxoDelayed: async function (address) {
|
getAddressTxsUtxoDelayed: async function (address) {
|
||||||
const {
|
const fn = async () => {
|
||||||
bitcoin: {addresses: addressesAPI}
|
const {
|
||||||
} = mempoolJS({
|
bitcoin: {addresses: addressesAPI}
|
||||||
hostname: new URL(this.config.data.mempool_endpoint).hostname
|
} = mempoolJS({
|
||||||
})
|
hostname: this.mempoolHostname
|
||||||
|
})
|
||||||
|
|
||||||
const fn = async () =>
|
return addressesAPI.getAddressTxsUtxo({
|
||||||
addressesAPI.getAddressTxsUtxo({
|
|
||||||
address
|
address
|
||||||
})
|
})
|
||||||
|
}
|
||||||
return retryWithDelay(fn)
|
return retryWithDelay(fn)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@
|
||||||
<div class="col-12 col-md-7 q-gutter-y-md">
|
<div class="col-12 col-md-7 q-gutter-y-md">
|
||||||
<wallet-config
|
<wallet-config
|
||||||
:total="utxos.total"
|
:total="utxos.total"
|
||||||
:config="config"
|
:config-data.sync="config"
|
||||||
:adminkey="g.user.wallets[0].adminkey"
|
:adminkey="g.user.wallets[0].adminkey"
|
||||||
>
|
>
|
||||||
<template v-slot:serial>
|
<template v-slot:serial>
|
||||||
<serial-signer
|
<serial-signer
|
||||||
ref="serialSigner"
|
ref="serialSigner"
|
||||||
:sats-denominated="config.data.sats_denominated"
|
:sats-denominated="config.sats_denominated"
|
||||||
@signed:psbt="updateSignedPsbt"
|
@signed:psbt="updateSignedPsbt"
|
||||||
></serial-signer>
|
></serial-signer>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
<wallet-list
|
<wallet-list
|
||||||
: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.sats_denominated"
|
||||||
:addresses="addresses"
|
:addresses="addresses"
|
||||||
@accounts-update="updateAccounts"
|
@accounts-update="updateAccounts"
|
||||||
@new-receive-address="showAddressDetails"
|
@new-receive-address="showAddressDetails"
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
||||||
<q-card>
|
<q-card v-if="config.isLoaded">
|
||||||
<q-card-section v-show="!showPayment">
|
<q-card-section v-show="!showPayment">
|
||||||
<q-tabs v-model="tab" no-caps class="bg-dark text-white shadow-2">
|
<q-tabs v-model="tab" no-caps class="bg-dark text-white shadow-2">
|
||||||
<q-tab name="addresses" label="Addresses"></q-tab>
|
<q-tab name="addresses" label="Addresses"></q-tab>
|
||||||
|
|
@ -89,8 +89,8 @@
|
||||||
ref="addressList"
|
ref="addressList"
|
||||||
:addresses="addresses"
|
:addresses="addresses"
|
||||||
:accounts="walletAccounts"
|
:accounts="walletAccounts"
|
||||||
:mempool-endpoint="config.data.mempool_endpoint"
|
:mempool-endpoint="mempoolHostname"
|
||||||
:sats-denominated="config.data.sats_denominated"
|
:sats-denominated="config.sats_denominated"
|
||||||
@scan:address="scanAddress"
|
@scan:address="scanAddress"
|
||||||
@show-address-details="showAddressDetails"
|
@show-address-details="showAddressDetails"
|
||||||
@update:addresses="initUtxos"
|
@update:addresses="initUtxos"
|
||||||
|
|
@ -101,29 +101,28 @@
|
||||||
<q-tab-panel name="history">
|
<q-tab-panel name="history">
|
||||||
<history
|
<history
|
||||||
:history="history"
|
:history="history"
|
||||||
:mempool-endpoint="config.data.mempool_endpoint"
|
:mempool-endpoint="mempoolHostname"
|
||||||
:sats-denominated="config.data.sats_denominated"
|
:sats-denominated="config.sats_denominated"
|
||||||
></history>
|
></history>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
<q-tab-panel name="utxos">
|
<q-tab-panel name="utxos">
|
||||||
<utxo-list
|
<utxo-list
|
||||||
:utxos="utxos.data"
|
:utxos="utxos.data"
|
||||||
:mempool-endpoint="config.data.mempool_endpoint"
|
:mempool-endpoint="mempoolHostname"
|
||||||
:sats-denominated="config.data.sats_denominated"
|
:sats-denominated="config.sats_denominated"
|
||||||
></utxo-list>
|
></utxo-list>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
</q-tab-panels>
|
</q-tab-panels>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
<div class="q-pt-sm">
|
<div v-if="config.isLoaded" class="q-pt-sm">
|
||||||
{{config.data.mempool_endpoint}}
|
|
||||||
<payment
|
<payment
|
||||||
ref="paymentRef"
|
ref="paymentRef"
|
||||||
v-show="showPayment"
|
v-show="showPayment"
|
||||||
:accounts="walletAccounts"
|
:accounts="walletAccounts"
|
||||||
:addresses="addresses"
|
:addresses="addresses"
|
||||||
:utxos="utxos.data"
|
:utxos="utxos.data"
|
||||||
:mempool-endpoint="config.data.mempool_endpoint"
|
:mempool-endpoint="mempoolHostname"
|
||||||
:adminkey="g.user.wallets[0].adminkey"
|
:adminkey="g.user.wallets[0].adminkey"
|
||||||
:serial-signer-ref="$refs.serialSigner"
|
:serial-signer-ref="$refs.serialSigner"
|
||||||
></payment>
|
></payment>
|
||||||
|
|
@ -168,7 +167,7 @@
|
||||||
size="ms"
|
size="ms"
|
||||||
icon="launch"
|
icon="launch"
|
||||||
type="a"
|
type="a"
|
||||||
:href="config.mempool_endpoint + '/address/' + currentAddress.address"
|
:href="mempoolHostname + '/address/' + currentAddress.address"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
></q-btn>
|
></q-btn>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -233,8 +233,9 @@ async def api_psbt_create(
|
||||||
descriptors[masterpub.fingerprint] = parse_key(masterpub.public_key)
|
descriptors[masterpub.fingerprint] = parse_key(masterpub.public_key)
|
||||||
|
|
||||||
inputs_extra = []
|
inputs_extra = []
|
||||||
bip32_derivations = {}
|
|
||||||
for i, inp in enumerate(data.inputs):
|
for i, inp in enumerate(data.inputs):
|
||||||
|
bip32_derivations = {}
|
||||||
descriptor = descriptors[inp.masterpub_fingerprint][0]
|
descriptor = descriptors[inp.masterpub_fingerprint][0]
|
||||||
d = descriptor.derive(inp.address_index, inp.branch_index)
|
d = descriptor.derive(inp.address_index, inp.branch_index)
|
||||||
for k in d.keys:
|
for k in d.keys:
|
||||||
|
|
@ -291,6 +292,7 @@ async def api_psbt_extract_tx(
|
||||||
if not final_psbt:
|
if not final_psbt:
|
||||||
raise ValueError("PSBT cannot be finalized!")
|
raise ValueError("PSBT cannot be finalized!")
|
||||||
res.tx_hex = final_psbt.to_string()
|
res.tx_hex = final_psbt.to_string()
|
||||||
|
print('### hex', res.tx_hex)
|
||||||
|
|
||||||
transaction = Transaction.from_string(res.tx_hex)
|
transaction = Transaction.from_string(res.tx_hex)
|
||||||
tx = {
|
tx = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue