Skip to content

Commit

Permalink
feat: basic fidelity bonds (#307)
Browse files Browse the repository at this point in the history
* dev: add simple fidelity bond screen in advanced mode

* dev: add link to fidelity form page in CurrentWalletAdvanced

* dev: fix build errors and remove 'as' tag of translation elements

* refactor: move FidelityBondAdvanced component to own file

* ui: highlight expiry date of fidelity bond address

* dev: display Fidelity Bond create form regardless if FBs already exist

* dev: avoid flickering when loading new fb address

* fix: rename Locktime to Lockdate

* dev: add translation strings to section 'global'

* wip: FidelityBondSimple

* review: distinct simple and dev mode Fidelity Bond screens

* review: fix dev FB view in dark mode

* refactor: rename FidelityBondAdvanced to FidelityBondDevOnly

* dev: add link to default view to FidelityBondDevOnly

* dev: sort utxo by value

* dev: reset user confirmation on data change

* dev: create fidelity bond with collaborative transaction WIP

* dev: wait for collaborative transaction to finish

* dev: unfreeze utxos after creating sweep tx

* review: render whitespace only when necessary in FB alert

* review: do not provide time related values to Date.UTC for FB

* Update src/components/FidelityBondDevOnly.tsx

Co-authored-by: Daniel <10026790+dnlggr@users.noreply.github.com>

* review: do not duplicate 'initialLockdate' values

* dev: allow lockdates in the past in dev mode

* dev: display percentage of utxo to total value in account

* dev: style percentage bar

* dev: display percentage bar in account selector

* dev: i18n for 'time elapsed' phrase

* dev: externalize components UtxoSelector and UtxoCheckbox

* dev: externalize components AccountSelector and AccountCheckbox

* dev: externalize LockdateForm

* dev: externalize FidelityBondDetailsSetupForm

* dev: display all accounts by default in FB form

* refactor: remove unused vars and imports

* dev: fix comments and add todos

* dev: minimal i18n for FB form

* ui: remove account/utxo checkbox overlay

* dev: add checkbox-card component

* dev: show unit of funds according to settings in FB form

* dev: allow already expired lockdates in dev mode

* dev: use minimum makers to create fb

* refactor: combine FidelityBond and FidelityBondSimple

* dev: undo coin control setup on errors

* dev: perform coin control actions sequentially

* dev: use direct-send when renewing an expired fidelity bond

* review: improve code readability

* review: do not return value from function 'sweepToFidelityBond'

* dev: log timelocked address to console in dev mode

* review: remove FidelityBondDevOnly component

* review: use dropdowns in lockdate form

* Update src/components/FidelityBond.tsx

Co-authored-by: Daniel <10026790+dnlggr@users.noreply.github.com>

* dev: add debug feature 'allowCreatingExpiredFidelityBond'

* review: fix var naming

* review: fix comment

* Update src/components/FidelityBond.tsx

Co-authored-by: Daniel <10026790+dnlggr@users.noreply.github.com>

* Update src/components/FidelityBond.tsx

Co-authored-by: Daniel <10026790+dnlggr@users.noreply.github.com>

* dev: unfreeze sequentially after fidelity bond tx was created

* review: remove ability to select multiple jars

* review: add tests for fb utils

* review: remove coin control

* review: reflect missing coin control in account checkbox

* review: remove time elapsed string from fb summary

* dev: add taker started alert to FB screen

* dev: reset lockdate form if date is invalid

* dev: reload after taker operation

* dev: add calculated total balance to useBalanceSummary hook

* dev: handle expired timelocked funds in useBalanceSummary hook

* dev: use isLocked to detect if freeze/unfreeze is enabled

* dev: fix build

* refactor: proper var names

* dev: show success msg only if fb has been created successfully

* dev: do not wait for collaborative tx to finish

* dev: direct-send if renewing FB

* dev: utilize 'isLocked' in CurrentWalletAdvanced and Jam view

* dev: link to FB view in Cheatsheet

* dev: utilize 'calculatedTotalBalanceInSats' where possible

* Update src/components/FidelityBond.tsx

Co-authored-by: Daniel <10026790+dnlggr@users.noreply.github.com>

* review: adapt error handling style on FB sweep tx

sorry @dnlggr for letting this slip in _again_

* review: always show all accounts in FB form

* review: initial lockdate should be 3 month from now

* review: move YearsRange and initialLockdate to fb_utils

* review: display month names

* review: wrap rows in container

* review: remove unnecessary comment

* review: left-align confirmation toggle in FB form

* review: turn accountBalances from array to object

* Update src/i18n/locales/en/translation.json

Co-authored-by: Gigi <109058+dergigi@users.noreply.github.com>

* Update src/components/FidelityBond.tsx

Co-authored-by: Gigi <109058+dergigi@users.noreply.github.com>

* review: remove initializing flag

* review: show timelocked address in confirmation step

* test: add test for LockdateForm component

* test: add test for _minMonth

* test: add test for _selectableYears

* test: add test for _selectableMonths

* ui: fix table in light theme

Co-authored-by: Daniel <10026790+dnlggr@users.noreply.github.com>
Co-authored-by: Gigi <109058+dergigi@users.noreply.github.com>
  • Loading branch information
3 people committed Jun 15, 2022
1 parent 9d00212 commit c68e4c5
Show file tree
Hide file tree
Showing 32 changed files with 1,699 additions and 223 deletions.
4 changes: 4 additions & 0 deletions public/sprite.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/components/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useState, useEffect, useCallback } from 'react'
import { Route, Routes, Navigate } from 'react-router-dom'
import * as rb from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import { isFeatureEnabled } from '../constants/features'
import Wallets from './Wallets'
import CreateWallet from './CreateWallet'
import Jam from './Jam'
Expand All @@ -10,6 +11,7 @@ import Earn from './Earn'
import Receive from './Receive'
import CurrentWalletMagic from './CurrentWalletMagic'
import CurrentWalletAdvanced from './CurrentWalletAdvanced'
import FidelityBond from './FidelityBond'
import Settings from './Settings'
import Navbar from './Navbar'
import Layout from './Layout'
Expand Down Expand Up @@ -129,6 +131,9 @@ export default function App() {
<Route path={routes.earn} element={<Earn />} />
<Route path={routes.receive} element={<Receive />} />
<Route path={routes.settings} element={<Settings stopWallet={stopWallet} />} />
{isFeatureEnabled('fidelityBonds') && (
<Route path={routes.fidelityBonds} element={<FidelityBond />} />
)}
</>
)}
</Route>
Expand Down
15 changes: 11 additions & 4 deletions src/components/Cheatsheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Link } from 'react-router-dom'
import { Trans, useTranslation } from 'react-i18next'
import { routes } from '../constants/routes'
import Sprite from './Sprite'
import { isFeatureEnabled } from '../constants/features'
import styles from './Cheatsheet.module.css'

interface CheatsheetProps {
Expand Down Expand Up @@ -41,6 +42,7 @@ function ListItem({ number, children, ...props }: PropsWithChildren<NumberedProp

export default function Cheatsheet({ show = false, onHide }: CheatsheetProps) {
const { t } = useTranslation()
const featureFidelityBondsEnabled = isFeatureEnabled('fidelityBonds')

return (
<rb.Offcanvas className={styles.cheatsheet} show={show} onHide={onHide} placement="bottom" onClick={onHide}>
Expand Down Expand Up @@ -95,17 +97,22 @@ export default function Cheatsheet({ show = false, onHide }: CheatsheetProps) {
</h6>
<div className="small text-secondary">{t('cheatsheet.item_2.description')}</div>
</ListItem>
<ListItem number={3} className={styles['upcoming-feature']}>
<ListItem number={3} className={featureFidelityBondsEnabled ? '' : styles['upcoming-feature']}>
<h6>
<Trans i18nKey="cheatsheet.item_3.title">
Optional: <Link to={routes.earn}>Lock</Link> funds in a fidelity bond.
Optional: <Link to={featureFidelityBondsEnabled ? routes.fidelityBonds : routes.home}>Lock</Link> funds
in a fidelity bond.
</Trans>
</h6>
<div className="small text-secondary">
{t('cheatsheet.item_3.description')}
<br />
{/* the following phrase is intentionally not translated because it will be removed soon */}
<strong>Feature not implemented yet. Coming soon!</strong>
{!featureFidelityBondsEnabled && (
<>
<br />
<strong>Feature not implemented yet. Coming soon!</strong>
</>
)}
</div>
</ListItem>
<ListItem number={4}>
Expand Down
2 changes: 2 additions & 0 deletions src/components/CopyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export function CopyButton({
successTextTimeout = 1_500,
className,
showSprites = true,
...props
}: CopyButtonProps) {
const [showValueCopiedConfirmation, setShowValueCopiedConfirmation] = useState(false)
const [valueCopiedFlag, setValueCopiedFlag] = useState(0)
Expand All @@ -101,6 +102,7 @@ export function CopyButton({

return (
<Copyable
{...props}
className={`btn ${className || ''}`}
value={value}
onError={onError}
Expand Down
63 changes: 46 additions & 17 deletions src/components/CurrentWalletAdvanced.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import * as rb from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { Trans, useTranslation } from 'react-i18next'
// @ts-ignore
import DisplayAccounts from './DisplayAccounts'
// @ts-ignore
Expand All @@ -9,12 +10,16 @@ import DisplayAccountUTXOs from './DisplayAccountUTXOs'
import DisplayUTXOs from './DisplayUTXOs'
// @ts-ignore
import { useCurrentWallet, useCurrentWalletInfo, useReloadCurrentWalletInfo } from '../context/WalletContext'
import { isFeatureEnabled } from '../constants/features'
import { routes } from '../constants/routes'
import styles from './CurrentWalletAdvanced.module.css'

type Utxos = any[]
type Alert = { message: string; variant: string }

export default function CurrentWalletAdvanced() {
const featureFidelityBondsEnabled = isFeatureEnabled('fidelityBonds')

const { t } = useTranslation()
const currentWallet = useCurrentWallet()
const walletInfo = useCurrentWalletInfo()
Expand Down Expand Up @@ -43,8 +48,8 @@ export default function CurrentWalletAdvanced() {
const unspentOutputs = info.data.utxos.utxos
setUtxos(unspentOutputs)

const lockedOutputs = unspentOutputs.filter((utxo) => utxo.locktime)
setFidelityBonds(lockedOutputs)
const fbOutputs = unspentOutputs.filter((utxo) => utxo.locktime)
setFidelityBonds(fbOutputs)
}
})
.catch((err) => {
Expand Down Expand Up @@ -76,21 +81,45 @@ export default function CurrentWalletAdvanced() {
{!isLoading && walletInfo && (
<DisplayAccounts accounts={walletInfo.data.display.walletinfo.accounts} className="mb-4" />
)}
{!!fidelityBonds?.length && (
<div className="mt-5 mb-3 pe-3">
<h5>{t('current_wallet_advanced.title_fidelity_bonds')}</h5>
<DisplayUTXOs utxos={fidelityBonds} className="pe-2" />
</div>
)}

<div className="mt-5 mb-3">
<h5>{t('current_wallet_advanced.title_fidelity_bonds')}</h5>
{isLoading && (
<div>
<rb.Placeholder as="div" animation="wave" className={styles['current-wallet-placeholder-container']}>
<rb.Placeholder xs={12} className={styles['current-wallet-placeholder']} />
</rb.Placeholder>
</div>
)}

{!isLoading && fidelityBonds && (
<>
{fidelityBonds.length === 0 ? (
<rb.Alert variant="info">
<>
<Trans i18nKey="fidelity_bond.alert_no_fidelity_bonds" as="span">
No Fidelity Bond present.
</Trans>
{featureFidelityBondsEnabled && (
<>
{' '}
<Link to={routes.fidelityBonds}>
<Trans i18nKey="current_wallet_advanced.link_fidelity_bonds_create_text" as="span">
Create a Fidelity Bond.
</Trans>
</Link>
</>
)}
</>
</rb.Alert>
) : (
<DisplayUTXOs utxos={fidelityBonds} />
)}
</>
)}
</div>
<>
<rb.Button
variant="outline-dark"
disabled={isLoading}
onClick={() => {
setShowUTXO(!showUTXO)
}}
className={isLoading ? 'mt-3 mb-3 pe-auto' : 'mb-3'}
>
<rb.Button variant="outline-dark" disabled={isLoading} onClick={() => setShowUTXO(!showUTXO)} className="mb-3">
{showUTXO ? t('current_wallet_advanced.button_hide_utxos') : t('current_wallet_advanced.button_show_utxos')}
</rb.Button>
<rb.Fade in={showUTXO} mountOnEnter={true} unmountOnExit={true}>
Expand Down
3 changes: 2 additions & 1 deletion src/components/DisplayUTXOs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Alert from './Alert'
import { useSettings } from '../context/SettingsContext'
import { useCurrentWallet } from '../context/WalletContext'
import { useServiceInfo } from '../context/ServiceInfoContext'
import { isLocked } from '../hooks/BalanceSummary'
import * as Api from '../libs/JmWalletApi'

const Utxo = ({ utxo, ...props }) => {
Expand All @@ -21,7 +22,7 @@ const Utxo = ({ utxo, ...props }) => {
const isOperationEnabled = useCallback(() => {
const noServiceIsRunning = serviceInfo && !serviceInfo.makerRunning && !serviceInfo.coinjoinInProgress

const isUnfreezeEnabled = !utxo.locktime || new Date(utxo.locktime).getTime() < Date.now()
const isUnfreezeEnabled = !isLocked(utxo)
const allowedToExecute = !utxo.frozen || isUnfreezeEnabled

return noServiceIsRunning && allowedToExecute
Expand Down
4 changes: 4 additions & 0 deletions src/components/FidelityBond.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* Firefox */
.fidelity-bond input[type='number'] {
-moz-appearance: unset !important;
}

0 comments on commit c68e4c5

Please sign in to comment.