Skip to content

Commit

Permalink
fix(Earn): validate offer minsize (#745)
Browse files Browse the repository at this point in the history
  • Loading branch information
theborakompanioni authored May 5, 2024
1 parent bd1f250 commit 7aef192
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 2 deletions.
44 changes: 42 additions & 2 deletions src/components/Earn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as rb from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { TFunction } from 'i18next'
import { useSettings } from '../context/SettingsContext'
import { CurrentWallet, useCurrentWalletInfo, useReloadCurrentWalletInfo } from '../context/WalletContext'
import { CurrentWallet, useCurrentWalletInfo, useReloadCurrentWalletInfo, WalletInfo } from '../context/WalletContext'
import { useServiceInfo, useReloadServiceInfo, Offer } from '../context/ServiceInfoContext'
import { factorToPercentage, isAbsoluteOffer, isRelativeOffer, isValidNumber, percentageToFactor } from '../utils'
import * as Api from '../libs/JmWalletApi'
Expand All @@ -22,6 +22,7 @@ import Accordion from './Accordion'
import BitcoinAmountInput, { AmountValue, toAmountValue } from './BitcoinAmountInput'
import { isValidAmount } from './Send/helpers'
import styles from './Earn.module.css'
import { JM_DUST_THRESHOLD } from '../constants/config'

// In order to prevent state mismatch, the 'maker stop' response is delayed shortly.
// Even though the API response suggests that the maker has started or stopped immediately, it seems that this is not always the case.
Expand Down Expand Up @@ -203,6 +204,7 @@ interface EarnFormProps {
onSubmit: (values: EarnFormValues) => Promise<void>
isLoading: boolean
disabled?: boolean
walletInfo?: WalletInfo
}

const EarnForm = ({
Expand All @@ -211,9 +213,27 @@ const EarnForm = ({
onSubmit,
isLoading,
disabled = false,
walletInfo,
}: EarnFormProps) => {
const { t } = useTranslation()

const maxAvailableBalanceInJar = useMemo(() => {
return Math.max(
0,
Math.max(
...Object.values(walletInfo?.balanceSummary.accountBalances || []).map(
(it) => it.calculatedAvailableBalanceInSats,
),
),
)
}, [walletInfo])

const offerMinsizeMin = JM_DUST_THRESHOLD

const offerMinsizeMax = useMemo(() => {
return Math.max(0, maxAvailableBalanceInJar - JM_DUST_THRESHOLD)
}, [maxAvailableBalanceInJar])

const validate = (values: EarnFormValues) => {
const errors = {} as FormikErrors<EarnFormValues>
const isRelOffer = isRelativeOffer(values.offertype)
Expand Down Expand Up @@ -241,6 +261,16 @@ const EarnForm = ({

if (!isValidAmount(values.minsize?.value ?? null, false)) {
errors.minsize = t('earn.feedback_invalid_min_amount')
} else {
const minsize = values.minsize?.value || 0
if (offerMinsizeMin > offerMinsizeMax) {
errors.minsize = t('earn.feedback_invalid_min_amount_insufficient_funds')
} else if (minsize < offerMinsizeMin || minsize > offerMinsizeMax) {
errors.minsize = t('earn.feedback_invalid_min_amount_range', {
minAmountMin: offerMinsizeMin.toLocaleString(),
minAmountMax: offerMinsizeMax.toLocaleString(),
})
}
}
return errors
}
Expand Down Expand Up @@ -426,6 +456,15 @@ export default function Earn({ wallet }: EarnProps) {
const [moveToJarFidelityBondId, setMoveToJarFidelityBondId] = useState<Api.UtxoId>()
const [renewFidelityBondId, setRenewFidelityBondId] = useState<Api.UtxoId>()

const isSufficientFundsAvailable = useMemo(
() => (currentWalletInfo?.balanceSummary.calculatedAvailableBalanceInSats ?? 0) > 0,
[currentWalletInfo],
)

const isOperationDisabled = useMemo(() => {
return !isSufficientFundsAvailable || serviceInfo?.rescanning === true || isWaitingMakerStart || isWaitingMakerStop
}, [isSufficientFundsAvailable, serviceInfo, isWaitingMakerStart, isWaitingMakerStop])

const startMakerService = useCallback(
(values: EarnFormValues) => {
setIsSending(true)
Expand Down Expand Up @@ -701,8 +740,9 @@ export default function Earn({ wallet }: EarnProps) {
<EarnForm
initialValues={initialValues}
onSubmit={onSubmitStart}
walletInfo={currentWalletInfo}
isLoading={isLoading}
disabled={serviceInfo?.rescanning === true || isWaitingMakerStart || isWaitingMakerStop}
disabled={isOperationDisabled}
submitButtonText={(_) => {
return (
<>
Expand Down
4 changes: 4 additions & 0 deletions src/constants/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ export const CJ_STATE_MAKER_RUNNING = 1
export const CJ_STATE_NONE_RUNNING = 2

export const JM_API_AUTH_TOKEN_EXPIRY: Milliseconds = Math.round(0.5 * 60 * 60 * 1_000)

// cap of dusty offer minsizes ("has dusty minsize, capping at 27300")
// See: https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/v0.9.11/src/jmclient/configure.py#L70 (last check on 2024-04-22 of v0.9.11)
export const JM_DUST_THRESHOLD = 27_300
2 changes: 2 additions & 0 deletions src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,8 @@
"label_min_amount_input": "Minimum amount",
"placeholder_min_amount_input": "Enter a minimum amount in sats or BTC...",
"feedback_invalid_min_amount": "Please provide a minimum amount.",
"feedback_invalid_min_amount_range": "Please provide a minimum amount in sats between {{ minAmountMin }} and {{ minAmountMax }}.",
"feedback_invalid_min_amount_insufficient_funds": "Invalid minimum amount: Insufficient funds available.",
"button_start": "Start Earning!",
"button_stop": "Stop",
"text_starting": "Starting",
Expand Down

0 comments on commit 7aef192

Please sign in to comment.