Skip to content

Commit

Permalink
ui(fb): display warning if all funds are used for fb (#649)
Browse files Browse the repository at this point in the history
  • Loading branch information
theborakompanioni authored Aug 28, 2023
1 parent 77c3ec9 commit 4d0479e
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState, useEffect, useMemo } from 'react'
import * as rb from 'react-bootstrap'
import * as Api from '../../libs/JmWalletApi'
import { Trans, useTranslation } from 'react-i18next'
import { useReloadCurrentWalletInfo } from '../../context/WalletContext'
import { CurrentWallet, Utxo, Utxos, WalletInfo, useReloadCurrentWalletInfo } from '../../context/WalletContext'
import Alert from '../Alert'
import Sprite from '../Sprite'
import { ConfirmModal } from '../Modal'
Expand Down Expand Up @@ -33,23 +33,37 @@ const steps = {
failed: 8,
}

const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDone }) => {
interface CreateFidelityBondProps {
otherFidelityBondExists: boolean
wallet: CurrentWallet
walletInfo: WalletInfo
onDone: () => void
}

const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDone }: CreateFidelityBondProps) => {
const reloadCurrentWalletInfo = useReloadCurrentWalletInfo()
const { t, i18n } = useTranslation()

const [isExpanded, setIsExpanded] = useState(false)
const [isLoading, setIsLoading] = useState(false)
const [alert, setAlert] = useState(null)
const [alert, setAlert] = useState<SimpleAlert>()
const [step, setStep] = useState(steps.selectDate)
const [showConfirmInputsModal, setShowConfirmInputsModal] = useState(false)

const [lockDate, setLockDate] = useState(null)
const [selectedJar, setSelectedJar] = useState(null)
const [selectedUtxos, setSelectedUtxos] = useState([])
const [lockDate, setLockDate] = useState<Api.Lockdate | null>(null)
const [selectedJar, setSelectedJar] = useState<JarIndex>()
const [selectedUtxos, setSelectedUtxos] = useState<Utxos>([])
const [timelockedAddress, setTimelockedAddress] = useState(null)
const [utxoIdsToBeSpent, setUtxoIdsToBeSpent] = useState([])
const [createdFidelityBondUtxo, setCreatedFidelityBondUtxo] = useState(null)
const [frozenUtxos, setFrozenUtxos] = useState([])
const [createdFidelityBondUtxo, setCreatedFidelityBondUtxo] = useState<Utxo>()
const [frozenUtxos, setFrozenUtxos] = useState<Utxos>([])

const allUtxosSelected = useMemo(() => {
return (
walletInfo.balanceSummary.calculatedTotalBalanceInSats ===
selectedUtxos.map((it) => it.value).reduce((prev, curr) => prev + curr, 0)
)
}, [walletInfo, selectedUtxos])

const yearsRange = useMemo(() => {
if (isDebugFeatureEnabled('allowCreatingExpiredFidelityBond')) {
Expand All @@ -62,12 +76,12 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
setIsLoading(false)
setIsExpanded(false)
setStep(steps.selectDate)
setSelectedJar(null)
setSelectedJar(undefined)
setSelectedUtxos([])
setLockDate(null)
setTimelockedAddress(null)
setAlert(null)
setCreatedFidelityBondUtxo(null)
setAlert(undefined)
setCreatedFidelityBondUtxo(undefined)
setFrozenUtxos([])
setUtxoIdsToBeSpent([])
}
Expand All @@ -92,20 +106,20 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
}
}, [isExpanded, reloadCurrentWalletInfo, t])

const freezeUtxos = (utxos) => {
const freezeUtxos = (utxos: Utxos) => {
changeUtxoFreeze(utxos, true)
}

const unfreezeUtxos = (utxos) => {
const unfreezeUtxos = (utxos: Utxos) => {
changeUtxoFreeze(utxos, false)
}

const changeUtxoFreeze = (utxos, freeze) => {
const changeUtxoFreeze = (utxos: Utxos, freeze: boolean) => {
setIsLoading(true)

const abortCtrl = new AbortController()

let utxosThatWereFrozen = []
let utxosThatWereFrozen: Utxos = []

const { name: walletName, token } = wallet
const freezeCalls = utxos.map((utxo) =>
Expand All @@ -125,7 +139,7 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon

Promise.all(freezeCalls)
.then((_) => reloadCurrentWalletInfo.reloadUtxos({ signal: abortCtrl.signal }))
.then(() => setAlert(null))
.then(() => setAlert(undefined))
.then(() => freeze && setFrozenUtxos([...frozenUtxos, ...utxosThatWereFrozen]))
.catch((err) => {
setAlert({ variant: 'danger', message: err.message, dismissible: true })
Expand All @@ -135,7 +149,7 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
})
}

const loadTimeLockedAddress = (lockDate) => {
const loadTimeLockedAddress = (lockDate: Api.Lockdate) => {
setIsLoading(true)

const abortCtrl = new AbortController()
Expand All @@ -150,29 +164,29 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
return res.ok ? res.json() : Api.Helper.throwError(res, t('earn.fidelity_bond.error_loading_address'))
})
.then((data) => setTimelockedAddress(data.address))
.then((_) => setAlert(null))
.then((_) => setAlert(undefined))
.catch((err) => {
setAlert({ variant: 'danger', message: err.message })
})
.finally(() => setIsLoading(false))
}

const directSweepToFidelityBond = (jar, address) => {
const directSweepToFidelityBond = (jarIndex: JarIndex, address: Api.BitcoinAddress) => {
setIsLoading(true)

Api.postDirectSend(
{ walletName: wallet.name, token: wallet.token },
{
mixdepth: jar,
mixdepth: jarIndex,
destination: address,
amount_sats: 0, // sweep
}
)
.then((res) =>
res.ok ? res.json() : Api.Helper.throwError(res, t('earn.fidelity_bond.error_creating_fidelity_bond'))
)
.then((body) => setUtxoIdsToBeSpent(body.txinfo.inputs.map((input) => input.outpoint)))
.then((_) => setAlert(null))
.then((body) => setUtxoIdsToBeSpent(body.txinfo.inputs.map((input: any) => input.outpoint)))
.then((_) => setAlert(undefined))
.catch((err) => {
setIsLoading(false)
setAlert({ variant: 'danger', message: err.message })
Expand Down Expand Up @@ -230,7 +244,7 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
}
}, [utxoIdsToBeSpent, reloadCurrentWalletInfo, timelockedAddress, t])

const stepComponent = (currentStep) => {
const stepComponent = (currentStep: number) => {
switch (currentStep) {
case steps.selectDate:
if (isLoading) {
Expand Down Expand Up @@ -264,21 +278,26 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
)
case steps.selectUtxos:
return (
<SelectUtxos
walletInfo={walletInfo}
jar={selectedJar}
utxos={walletInfo.utxosByJar[selectedJar]}
selectedUtxos={selectedUtxos}
onUtxoSelected={(utxo) => setSelectedUtxos([...selectedUtxos, utxo])}
onUtxoDeselected={(utxo) => setSelectedUtxos(selectedUtxos.filter((it) => it !== utxo))}
/>
<>
<SelectUtxos
walletInfo={walletInfo}
jar={selectedJar!}
utxos={walletInfo.utxosByJar[selectedJar!]}
selectedUtxos={selectedUtxos}
onUtxoSelected={(utxo) => setSelectedUtxos([...selectedUtxos, utxo])}
onUtxoDeselected={(utxo) => setSelectedUtxos(selectedUtxos.filter((it) => it !== utxo))}
/>
{allUtxosSelected && (
<Alert variant="warning" message={<Trans i18nKey="earn.fidelity_bond.alert_all_funds_in_use" />} />
)}
</>
)
case steps.freezeUtxos:
return (
<FreezeUtxos
walletInfo={walletInfo}
jar={selectedJar}
utxos={walletInfo.utxosByJar[selectedJar]}
jar={selectedJar!}
utxos={walletInfo.utxosByJar[selectedJar!]}
selectedUtxos={selectedUtxos}
isLoading={isLoading}
/>
Expand All @@ -298,13 +317,18 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
}

return (
<ReviewInputs
lockDate={lockDate}
jar={selectedJar}
utxos={walletInfo.utxosByJar[selectedJar]}
selectedUtxos={selectedUtxos}
timelockedAddress={timelockedAddress}
/>
<>
<ReviewInputs
lockDate={lockDate!}
jar={selectedJar!}
utxos={walletInfo.utxosByJar[selectedJar!]}
selectedUtxos={selectedUtxos}
timelockedAddress={timelockedAddress}
/>
{allUtxosSelected && (
<Alert variant="warning" message={<Trans i18nKey="earn.fidelity_bond.alert_all_funds_in_use" />} />
)}
</>
)

case steps.createFidelityBond:
Expand All @@ -315,8 +339,8 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
</div>
) : (
<div className="d-flex justify-content-center align-items-center gap-2 mt-5">
{alert === null ? (
<CreatedFidelityBond fbUtxo={createdFidelityBondUtxo} frozenUtxos={frozenUtxos} />
{!alert ? (
<CreatedFidelityBond fbUtxo={createdFidelityBondUtxo!} frozenUtxos={frozenUtxos} />
) : (
<>{t('earn.fidelity_bond.error_creating_fidelity_bond')}</>
)}
Expand All @@ -330,7 +354,7 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
</div>
) : (
<div className="d-flex justify-content-center align-items-center gap-2 mt-5">
{alert === null ? (
{!alert ? (
<Done text={t('earn.fidelity_bond.unfreeze_utxos.done')} />
) : (
<>{t('earn.fidelity_bond.error_unfreezing_utxos')}</>
Expand All @@ -342,7 +366,7 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
}
}

const primaryButtonText = (currentStep) => {
const primaryButtonText = (currentStep: number) => {
switch (currentStep) {
case steps.selectDate:
return t('earn.fidelity_bond.select_date.text_primary_button')
Expand All @@ -356,12 +380,12 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
return t('earn.fidelity_bond.select_utxos.text_primary_button')
case steps.freezeUtxos:
const utxosAreFrozen = fb.utxo.allAreFrozen(
fb.utxo.utxosToFreeze(walletInfo.utxosByJar[selectedJar], selectedUtxos)
fb.utxo.utxosToFreeze(walletInfo.utxosByJar[selectedJar!], selectedUtxos)
)

if (utxosAreFrozen) {
return t('earn.fidelity_bond.freeze_utxos.text_primary_button_all_frozen')
} else if (alert !== null) {
} else if (alert) {
return t('earn.fidelity_bond.freeze_utxos.text_primary_button_error')
}

Expand All @@ -375,7 +399,7 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon

return t('earn.fidelity_bond.review_inputs.text_primary_button')
case steps.createFidelityBond:
if (alert !== null) {
if (alert) {
return t('earn.fidelity_bond.create_fidelity_bond.text_primary_button_error')
}

Expand All @@ -397,7 +421,7 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
)
}

const secondaryButtonText = (currentStep) => {
const secondaryButtonText = (currentStep: number) => {
if (nextStep(step) === steps.done || nextStep(step) === steps.failed) {
return null
}
Expand All @@ -420,7 +444,7 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
}
}

const nextStep = (currentStep) => {
const nextStep = (currentStep: number) => {
if (currentStep === steps.selectDate) {
if (lockDate !== null) {
return steps.selectJar
Expand All @@ -445,7 +469,7 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
}

const utxosAreFrozen = fb.utxo.allAreFrozen(
fb.utxo.utxosToFreeze(walletInfo.utxosByJar[selectedJar], selectedUtxos)
fb.utxo.utxosToFreeze(walletInfo.utxosByJar[selectedJar!], selectedUtxos)
)
if (utxosAreFrozen) {
return steps.reviewInputs
Expand All @@ -463,11 +487,11 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
}

if (currentStep === steps.createFidelityBond) {
if (!isLoading && alert === null && frozenUtxos.length > 0) {
if (!isLoading && !alert && frozenUtxos.length > 0) {
return steps.unfreezeUtxos
}

if (alert !== null) {
if (alert) {
return steps.failed
}

Expand All @@ -491,7 +515,7 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
}

if (step === steps.freezeUtxos) {
const utxosToFreeze = fb.utxo.utxosToFreeze(walletInfo.utxosByJar[selectedJar], selectedUtxos)
const utxosToFreeze = fb.utxo.utxosToFreeze(walletInfo.utxosByJar[selectedJar!], selectedUtxos)
const utxosAreFrozen = fb.utxo.allAreFrozen(utxosToFreeze)

if (!utxosAreFrozen) {
Expand All @@ -500,11 +524,11 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
}

if (nextStep(step) === steps.reviewInputs) {
loadTimeLockedAddress(lockDate)
loadTimeLockedAddress(lockDate!)
}

if (step === steps.reviewInputs && timelockedAddress === null) {
loadTimeLockedAddress(lockDate)
loadTimeLockedAddress(lockDate!)
return
}

Expand All @@ -528,7 +552,13 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
return
}

setStep(nextStep(step))
const next = nextStep(step)
if (next !== null) {
setStep(next)
} else {
reset()
setStep(0)
}
}

const onSecondaryButtonClicked = () => {
Expand All @@ -542,7 +572,7 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon

return (
<div className={styles.container}>
{alert && <Alert {...alert} className="mt-0" onClose={() => setAlert(null)} />}
{alert && <Alert {...alert} className="mt-0" onClose={() => setAlert(undefined)} />}
{lockDate && (
<ConfirmModal
isShown={showConfirmInputsModal}
Expand All @@ -551,7 +581,7 @@ const CreateFidelityBond = ({ otherFidelityBondExists, wallet, walletInfo, onDon
onConfirm={() => {
setStep(steps.createFidelityBond)
setShowConfirmInputsModal(false)
directSweepToFidelityBond(selectedJar, timelockedAddress)
directSweepToFidelityBond(selectedJar!, timelockedAddress!)
}}
>
{t('earn.fidelity_bond.confirm_modal.body', {
Expand Down
2 changes: 1 addition & 1 deletion src/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ declare type JarIndex = number

declare type Unit = 'BTC' | 'sats'

declare type SimpleAlert = import('react-bootstrap').AlertProps & { message: string }
declare type SimpleAlert = import('react-bootstrap').AlertProps & { message: string | import('react').ReactNode }
1 change: 1 addition & 0 deletions src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@
"error_unfreezing_utxos": "Could not unfreeze UTXOs.",
"error_loading_address": "Could not load time-locked address.",
"error_creating_fidelity_bond": "The transaction to create the fidelity bond failed.",
"alert_all_funds_in_use": "<strong>Keep in mind</strong>: As you are using all available funds for the creation of this fidelity bond, you will not have any UTXOs left. <br /><br />A fidelity bond <strong>will not participate in collaborative transactions</strong> and you <strong>have to fund your wallet again</strong> to start sending or earning.",
"text_loading": "Loading...",
"text_creating": "Creating...",
"text_unfreezing": "Unfreezing...",
Expand Down

0 comments on commit 4d0479e

Please sign in to comment.