From 926a093e3d1a1c57d93d39318811914d41c95633 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Thu, 1 Aug 2024 16:38:34 +0200 Subject: [PATCH 01/28] Make amount input focus on mount --- .../shared/Form/FormTokenBalanceInput.tsx | 17 +- .../TokenAmountForm/TokenAmountFormBase.tsx | 11 +- .../shared/TokenBalanceInput/index.tsx | 169 +++++++++--------- 3 files changed, 108 insertions(+), 89 deletions(-) diff --git a/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx b/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx index e813a3033..4cba1fdfb 100644 --- a/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx +++ b/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect } from "react" +import React, { forwardRef, useCallback, useEffect } from "react" import { useField } from "formik" import { logPromiseFailure } from "#/utils" import TokenBalanceInput, { TokenBalanceInputProps } from "../TokenBalanceInput" @@ -7,11 +7,13 @@ export type FormTokenBalanceInputProps = { name: string defaultValue?: bigint } & Omit -export function FormTokenBalanceInput({ - name, - defaultValue, - ...restProps -}: FormTokenBalanceInputProps) { + +export const FormTokenBalanceInput = forwardRef< + HTMLInputElement, + FormTokenBalanceInputProps +>((props, ref) => { + const { name, defaultValue, ...restProps } = props + const [field, meta, helpers] = useField(name) const setAmount = useCallback( @@ -30,6 +32,7 @@ export function FormTokenBalanceInput({ return ( ) -} +}) diff --git a/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx b/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx index 6487627bc..c7964d3c0 100644 --- a/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx +++ b/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useEffect, useRef } from "react" import { FormikProps, useField } from "formik" import { CurrencyType } from "#/types" import { Form, FormTokenBalanceInput } from "../Form" @@ -42,9 +42,18 @@ export default function TokenAmountFormBase({ defaultAmount, ...formikProps }: TokenAmountFormBaseProps & FormikProps) { + const inputRef = useRef(null) + + useEffect(() => { + if (inputRef.current) { + inputRef.current.focus() + } + }, []) + return (
-export default function TokenBalanceInput({ - amount, - currency, - tokenBalance, - placeholder, - size = "lg", - setAmount, - errorMsgText, - helperText, - hasError = false, - fiatCurrency, - withMaxButton = false, - ...inputProps -}: TokenBalanceInputProps) { - const valueRef = useRef(amount) - const styles = useMultiStyleConfig("TokenBalanceInput", { size }) - - const { decimals } = getCurrencyByType(currency) +const TokenBalanceInput = forwardRef( + (props, ref) => { + const { + amount, + currency, + tokenBalance, + placeholder, + size = "lg", + setAmount, + errorMsgText, + helperText, + hasError = false, + fiatCurrency, + withMaxButton = false, + ...inputProps + } = props + + const valueRef = useRef(amount) + const styles = useMultiStyleConfig("TokenBalanceInput", { size }) + + const { decimals } = getCurrencyByType(currency) + + const handleValueChange = (value: string) => { + valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined + } - const handleValueChange = (value: string) => { - valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined - } - - return ( - - - - Amount - - - Balance + return ( + + + + Amount + + + Balance + + - - - - - - handleValueChange(values.value) - } - onChange={() => { - setAmount(valueRef?.current) - }} - decimalScale={decimals} - allowNegative={false} - /> + + + + handleValueChange(values.value) + } + onChange={() => { + setAmount(valueRef?.current) + }} + decimalScale={decimals} + allowNegative={false} + /> - {withMaxButton && ( - - - + {withMaxButton && ( + + + + )} + + + {!hasError && !helperText && !!fiatCurrency && ( + + + )} - - - {!hasError && !helperText && !!fiatCurrency && ( - - - - )} - - ) -} + + ) + }, +) + +export default TokenBalanceInput From b07a3061520222a3fb024c55a71ae1b4f9295922 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Mon, 5 Aug 2024 14:08:06 +0200 Subject: [PATCH 02/28] Implement adjustments for form behavior --- .../TransactionModal/ActionFormModal.tsx | 2 +- .../StakeFormModal/index.tsx | 6 +- .../shared/Form/FormSubmitButton.tsx | 3 +- .../shared/NumberFormatInput/index.tsx | 5 +- .../TokenAmountForm/TokenAmountFormBase.tsx | 1 - .../shared/TokenAmountForm/index.tsx | 6 +- .../shared/TokenBalanceInput/index.tsx | 68 +++++-------------- dapp/src/theme/Button.ts | 7 ++ dapp/src/theme/FormError.ts | 4 +- dapp/src/theme/Input.ts | 2 + dapp/src/utils/forms.ts | 7 +- 11 files changed, 46 insertions(+), 65 deletions(-) diff --git a/dapp/src/components/TransactionModal/ActionFormModal.tsx b/dapp/src/components/TransactionModal/ActionFormModal.tsx index 8c50be64e..53ab0e42b 100644 --- a/dapp/src/components/TransactionModal/ActionFormModal.tsx +++ b/dapp/src/components/TransactionModal/ActionFormModal.tsx @@ -107,7 +107,7 @@ function ActionFormModal({ type }: { type: ActionFlowType }) { <> {!isLoading && } {heading} - + diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx index 1c5037a76..507335389 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx @@ -4,6 +4,7 @@ import { TokenAmountFormValues } from "#/components/shared/TokenAmountForm/Token import { useMinDepositAmount, useWallet } from "#/hooks" import { FormSubmitButton } from "#/components/shared/Form" import { BaseFormProps } from "#/types" +import { formatTokenAmount, getCurrencyByType } from "#/utils" import StakeDetails from "./StakeDetails" function StakeFormModal({ @@ -12,9 +13,12 @@ function StakeFormModal({ const minDepositAmount = useMinDepositAmount() const { balance: tokenBalance } = useWallet() + const { decimals, desiredDecimals } = getCurrencyByType("bitcoin") + const inputPlaceholder = `Minimum ${formatTokenAmount(minDepositAmount, decimals, desiredDecimals)} BTC` + return ( {children} diff --git a/dapp/src/components/shared/NumberFormatInput/index.tsx b/dapp/src/components/shared/NumberFormatInput/index.tsx index ec4f5b274..56df3bb16 100644 --- a/dapp/src/components/shared/NumberFormatInput/index.tsx +++ b/dapp/src/components/shared/NumberFormatInput/index.tsx @@ -2,7 +2,7 @@ import React from "react" import { NumericFormat, NumericFormatProps } from "react-number-format" import { InputProps, chakra, useMultiStyleConfig } from "@chakra-ui/react" -const ChakraWrapper = chakra(NumericFormat) +const ChakraNumericFormat = chakra(NumericFormat) export type NumberFormatInputValues = { formattedValue: string @@ -32,8 +32,7 @@ const NumberFormatInput = React.forwardRef< const { decimalScale, isDisabled, isInvalid, ...restProps } = props return ( - diff --git a/dapp/src/components/shared/TokenAmountForm/index.tsx b/dapp/src/components/shared/TokenAmountForm/index.tsx index 1aa49ad9d..6fb495557 100644 --- a/dapp/src/components/shared/TokenAmountForm/index.tsx +++ b/dapp/src/components/shared/TokenAmountForm/index.tsx @@ -1,5 +1,5 @@ import { FormikErrors, withFormik } from "formik" -import { getErrorsObj, validateTokenAmount } from "#/utils" +import { getErrorsObj, logPromiseFailure, validateTokenAmount } from "#/utils" import { BaseFormProps } from "#/types" import TokenAmountFormBase, { TokenAmountFormBaseProps, @@ -28,9 +28,11 @@ const TokenAmountForm = withFormik( return getErrorsObj(errors) }, - handleSubmit: (values, { props }) => { + handleSubmit: (values, { props, validateForm }) => { + logPromiseFailure(validateForm(values)) props.onSubmitForm(values) }, + validateOnBlur: false, }, )(TokenAmountFormBase) diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index 05ac00f9f..8a97b0a13 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -13,18 +13,18 @@ import { useMultiStyleConfig, } from "@chakra-ui/react" import { - fixedPointNumberToString, + bigIntToUserAmount, getCurrencyByType, userAmountToBigInt, } from "#/utils" import { CurrencyType } from "#/types" import { IconInfoCircle } from "@tabler/icons-react" -import { useCurrencyConversion } from "#/hooks" import NumberFormatInput, { NumberFormatInputValues, NumberFormatInputProps, } from "../NumberFormatInput" import { CurrencyBalance } from "../CurrencyBalance" +import { Alert, AlertIcon, AlertDescription } from "../Alert" const VARIANT = "balance" @@ -42,7 +42,12 @@ function HelperErrorText({ if (hasError) { return ( - {errorMsgText || "Please enter a valid value"} + + + + {errorMsgText || "Please enter a valid value"} + + ) } @@ -59,40 +64,6 @@ function HelperErrorText({ return null } -type FiatCurrencyBalanceProps = { - amount: bigint - currency: CurrencyType - fiatCurrency: CurrencyType -} - -function FiatCurrencyBalance({ - amount, - currency, - fiatCurrency, -}: FiatCurrencyBalanceProps) { - const styles = useMultiStyleConfig("Form") - const { fontWeight } = styles.helperText - - const fiatAmount = useCurrencyConversion({ - from: { amount, currency }, - to: { currency: fiatCurrency }, - }) - - if (fiatAmount !== undefined) { - return ( - - ) - } - - return null -} - export type TokenBalanceInputProps = { amount?: bigint currency: CurrencyType @@ -118,7 +89,6 @@ const TokenBalanceInput = forwardRef( errorMsgText, helperText, hasError = false, - fiatCurrency, withMaxButton = false, ...inputProps } = props @@ -132,16 +102,21 @@ const TokenBalanceInput = forwardRef( valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined } + // This is workaround, we should pass error codes along with error messages + const isBalanceExceeded = + hasError && errorMsgText?.toString().includes("exceeds") + return ( - + Amount - Balance + Wallet balance ( placeholder={placeholder} {...inputProps} ref={ref} - value={ - amount ? fixedPointNumberToString(amount, decimals) : undefined - } + value={amount ? bigIntToUserAmount(amount, decimals) : undefined} onValueChange={(values: NumberFormatInputValues) => handleValueChange(values.value) } @@ -183,15 +156,6 @@ const TokenBalanceInput = forwardRef( errorMsgText={errorMsgText} hasError={hasError} /> - {!hasError && !helperText && !!fiatCurrency && ( - - - - )} ) }, diff --git a/dapp/src/theme/Button.ts b/dapp/src/theme/Button.ts index a9e1e05f3..c6ac23059 100644 --- a/dapp/src/theme/Button.ts +++ b/dapp/src/theme/Button.ts @@ -28,6 +28,9 @@ export const buttonTheme: ComponentSingleStyleConfig = { color: "white", _hover: { bg: "brand.500", + _disabled: { + bg: "grey.400", + }, }, _active: { bg: "brand.400", @@ -38,6 +41,10 @@ export const buttonTheme: ComponentSingleStyleConfig = { opacity: 1, }, }, + _disabled: { + bg: "grey.500", + color: "gold.200", + }, }, outline: ({ colorScheme }: StyleFunctionProps) => { const defaultStyles = { diff --git a/dapp/src/theme/FormError.ts b/dapp/src/theme/FormError.ts index d9a52b265..5cc44f3ad 100644 --- a/dapp/src/theme/FormError.ts +++ b/dapp/src/theme/FormError.ts @@ -4,7 +4,9 @@ import { formErrorAnatomy as parts } from "@chakra-ui/anatomy" const baseStyleText = defineStyle({ fontWeight: "medium", - color: "red.400", + color: "grey.700", + mt: 4, + fontSize: "md", }) const multiStyleConfig = createMultiStyleConfigHelpers(parts.keys) diff --git a/dapp/src/theme/Input.ts b/dapp/src/theme/Input.ts index e6e9d8695..c6d5c5a7c 100644 --- a/dapp/src/theme/Input.ts +++ b/dapp/src/theme/Input.ts @@ -8,6 +8,7 @@ const variantBalanceField = defineStyle({ fontWeight: "bold", bg: "opacity.white.5", paddingRight: 20, + minH: 14, // TODO: Set the color correctly without using the chakra variable. caretColor: "var(--chakra-colors-brand-400)", @@ -18,6 +19,7 @@ const variantBalanceField = defineStyle({ _invalid: { color: "red.400", + borderColor: "red.400", }, }) diff --git a/dapp/src/utils/forms.ts b/dapp/src/utils/forms.ts index d1c8caf4f..69e3d87c0 100644 --- a/dapp/src/utils/forms.ts +++ b/dapp/src/utils/forms.ts @@ -3,10 +3,11 @@ import { getCurrencyByType } from "./currency" import { fixedPointNumberToString } from "./numbers" const ERRORS = { - REQUIRED: "Required.", - EXCEEDED_VALUE: "The amount exceeds your current balance.", + REQUIRED: "Please enter an amount.", + EXCEEDED_VALUE: + "The amount exceeds your current wallet balance. Add more funds to your wallet or lower the deposit amount.", INSUFFICIENT_VALUE: (minValue: string) => - `The minimum amount must be at least ${minValue} BTC.`, + `The amount is below the minimum required deposit of ${minValue} BTC.`, } export function getErrorsObj(errors: { [key in keyof T]: string }) { From 668f4e6a0f662e1aa6a67c84a64fbaa5b7735335 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Mon, 5 Aug 2024 17:42:07 +0200 Subject: [PATCH 03/28] Add withdrawal form input placeholder Removed unused props --- .../ActiveStakingStep/StakeFormModal/index.tsx | 1 - .../ActiveUnstakingStep/UnstakeFormModal/index.tsx | 7 +++++-- .../shared/TokenAmountForm/TokenAmountFormBase.tsx | 2 -- dapp/src/components/shared/TokenBalanceInput/index.tsx | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx index 507335389..5589852d9 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx @@ -20,7 +20,6 @@ function StakeFormModal({ void withMaxButton?: boolean } & InputProps & From 536a1aee54fde7db41b528adbdef7034e5b45f26 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Mon, 5 Aug 2024 17:46:21 +0200 Subject: [PATCH 04/28] Pass desired decimals --- dapp/src/components/shared/TokenBalanceInput/index.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index 42c85b2fe..601488624 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -95,7 +95,7 @@ const TokenBalanceInput = forwardRef( const valueRef = useRef(amount) const styles = useMultiStyleConfig("TokenBalanceInput", { size }) - const { decimals } = getCurrencyByType(currency) + const { decimals, desiredDecimals } = getCurrencyByType(currency) const handleValueChange = (value: string) => { valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined @@ -131,7 +131,11 @@ const TokenBalanceInput = forwardRef( placeholder={placeholder} {...inputProps} ref={ref} - value={amount ? bigIntToUserAmount(amount, decimals) : undefined} + value={ + amount + ? bigIntToUserAmount(amount, decimals, desiredDecimals) + : undefined + } onValueChange={(values: NumberFormatInputValues) => handleValueChange(values.value) } From b0d964799c880e5c406e1307cd072e18e811c1c4 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 7 Aug 2024 12:47:32 +0200 Subject: [PATCH 05/28] Remove explicit form validation on submit --- dapp/src/components/shared/TokenAmountForm/index.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dapp/src/components/shared/TokenAmountForm/index.tsx b/dapp/src/components/shared/TokenAmountForm/index.tsx index 6fb495557..f06130398 100644 --- a/dapp/src/components/shared/TokenAmountForm/index.tsx +++ b/dapp/src/components/shared/TokenAmountForm/index.tsx @@ -1,5 +1,5 @@ import { FormikErrors, withFormik } from "formik" -import { getErrorsObj, logPromiseFailure, validateTokenAmount } from "#/utils" +import { getErrorsObj, validateTokenAmount } from "#/utils" import { BaseFormProps } from "#/types" import TokenAmountFormBase, { TokenAmountFormBaseProps, @@ -28,8 +28,7 @@ const TokenAmountForm = withFormik( return getErrorsObj(errors) }, - handleSubmit: (values, { props, validateForm }) => { - logPromiseFailure(validateForm(values)) + handleSubmit: (values, { props }) => { props.onSubmitForm(values) }, validateOnBlur: false, From 28d18dcdd22253f1df306471638f589c4b1d4495 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 7 Aug 2024 12:53:10 +0200 Subject: [PATCH 06/28] Revert `fiatCurrency` property --- .../shared/TokenBalanceInput/index.tsx | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index 601488624..288d7cd5a 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -19,6 +19,7 @@ import { } from "#/utils" import { CurrencyType } from "#/types" import { IconInfoCircle } from "@tabler/icons-react" +import { useCurrencyConversion } from "#/hooks" import NumberFormatInput, { NumberFormatInputValues, NumberFormatInputProps, @@ -64,12 +65,47 @@ function HelperErrorText({ return null } +type FiatCurrencyBalanceProps = { + amount: bigint + currency: CurrencyType + fiatCurrency: CurrencyType +} + +function FiatCurrencyBalance({ + amount, + currency, + fiatCurrency, +}: FiatCurrencyBalanceProps) { + const styles = useMultiStyleConfig("Form") + const { fontWeight } = styles.helperText + + const fiatAmount = useCurrencyConversion({ + from: { amount, currency }, + to: { currency: fiatCurrency }, + }) + + if (fiatAmount !== undefined) { + return ( + + ) + } + + return null +} + export type TokenBalanceInputProps = { amount?: bigint currency: CurrencyType tokenBalance: bigint placeholder?: string size?: "lg" | "md" + fiatCurrency?: CurrencyType setAmount: (value?: bigint) => void withMaxButton?: boolean } & InputProps & @@ -88,6 +124,7 @@ const TokenBalanceInput = forwardRef( errorMsgText, helperText, hasError = false, + fiatCurrency, withMaxButton = false, ...inputProps } = props @@ -159,6 +196,15 @@ const TokenBalanceInput = forwardRef( errorMsgText={errorMsgText} hasError={hasError} /> + {!hasError && !helperText && !!fiatCurrency && ( + + + + )} ) }, From 33929c591788507d174344ee6e2ba3b06c067d04 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 7 Aug 2024 13:01:36 +0200 Subject: [PATCH 07/28] Revert focus changes replacing with autoFocus attr --- .../shared/Form/FormTokenBalanceInput.tsx | 16 +- .../TokenAmountForm/TokenAmountFormBase.tsx | 12 +- .../shared/TokenBalanceInput/index.tsx | 183 +++++++++--------- 3 files changed, 97 insertions(+), 114 deletions(-) diff --git a/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx b/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx index 4cba1fdfb..7b193f488 100644 --- a/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx +++ b/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, useCallback, useEffect } from "react" +import React, { useCallback, useEffect } from "react" import { useField } from "formik" import { logPromiseFailure } from "#/utils" import TokenBalanceInput, { TokenBalanceInputProps } from "../TokenBalanceInput" @@ -8,12 +8,11 @@ export type FormTokenBalanceInputProps = { defaultValue?: bigint } & Omit -export const FormTokenBalanceInput = forwardRef< - HTMLInputElement, - FormTokenBalanceInputProps ->((props, ref) => { - const { name, defaultValue, ...restProps } = props - +export function FormTokenBalanceInput({ + name, + defaultValue, + ...restProps +}: FormTokenBalanceInputProps) { const [field, meta, helpers] = useField(name) const setAmount = useCallback( @@ -32,7 +31,6 @@ export const FormTokenBalanceInput = forwardRef< return ( ) -}) +} diff --git a/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx b/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx index 6c27b7a01..3675be36b 100644 --- a/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx +++ b/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from "react" +import React from "react" import { FormikProps, useField } from "formik" import { CurrencyType } from "#/types" import { Form, FormTokenBalanceInput } from "../Form" @@ -40,24 +40,16 @@ export default function TokenAmountFormBase({ defaultAmount, ...formikProps }: TokenAmountFormBaseProps & FormikProps) { - const inputRef = useRef(null) - - useEffect(() => { - if (inputRef.current) { - inputRef.current.focus() - } - }, []) - return ( {children} diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index 288d7cd5a..75cbf9397 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, useRef } from "react" +import React, { useRef } from "react" import { Box, Button, @@ -112,102 +112,95 @@ export type TokenBalanceInputProps = { HelperErrorTextProps & Pick -const TokenBalanceInput = forwardRef( - (props, ref) => { - const { - amount, - currency, - tokenBalance, - placeholder, - size = "lg", - setAmount, - errorMsgText, - helperText, - hasError = false, - fiatCurrency, - withMaxButton = false, - ...inputProps - } = props - - const valueRef = useRef(amount) - const styles = useMultiStyleConfig("TokenBalanceInput", { size }) - - const { decimals, desiredDecimals } = getCurrencyByType(currency) - - const handleValueChange = (value: string) => { - valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined - } - - // This is workaround, we should pass error codes along with error messages - const isBalanceExceeded = - hasError && errorMsgText?.toString().includes("exceeds") +export default function TokenBalanceInput({ + amount, + currency, + tokenBalance, + placeholder, + size = "lg", + setAmount, + errorMsgText, + helperText, + hasError = false, + fiatCurrency, + withMaxButton = false, + ...inputProps +}: TokenBalanceInputProps) { + const valueRef = useRef(amount) + const styles = useMultiStyleConfig("TokenBalanceInput", { size }) - return ( - - - - Amount - - - Wallet balance - - - - - - - - handleValueChange(values.value) - } - onChange={() => { - setAmount(valueRef?.current) - }} - decimalScale={decimals} - allowNegative={false} - /> + const { decimals, desiredDecimals } = getCurrencyByType(currency) - {withMaxButton && ( - - - - )} - - - {!hasError && !helperText && !!fiatCurrency && ( - - { + valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined + } + + // This is workaround, we should pass error codes along with error messages + const isBalanceExceeded = + hasError && errorMsgText?.toString().includes("exceeds") + + return ( + + + + Amount + + + Wallet balance + + - - )} - - ) - }, -) + + + + + + handleValueChange(values.value) + } + onChange={() => { + setAmount(valueRef?.current) + }} + decimalScale={decimals} + allowNegative={false} + /> -export default TokenBalanceInput + {withMaxButton && ( + + + + )} + + + {!hasError && !helperText && !!fiatCurrency && ( + + + + )} + + ) +} From fd684a4418f33ffffa3c86453ebf81ec44893cc1 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 7 Aug 2024 13:06:37 +0200 Subject: [PATCH 08/28] Improve exceeded balance check solution --- .../components/shared/TokenBalanceInput/index.tsx | 4 ++-- dapp/src/constants/errors.ts | 8 ++++++++ dapp/src/utils/forms.ts | 15 ++++----------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index 75cbf9397..7a3953e0d 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -20,6 +20,7 @@ import { import { CurrencyType } from "#/types" import { IconInfoCircle } from "@tabler/icons-react" import { useCurrencyConversion } from "#/hooks" +import { TOKEN_FORM_ERRORS } from "#/constants" import NumberFormatInput, { NumberFormatInputValues, NumberFormatInputProps, @@ -135,9 +136,8 @@ export default function TokenBalanceInput({ valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined } - // This is workaround, we should pass error codes along with error messages const isBalanceExceeded = - hasError && errorMsgText?.toString().includes("exceeds") + hasError && errorMsgText === TOKEN_FORM_ERRORS.EXCEEDED_VALUE return ( diff --git a/dapp/src/constants/errors.ts b/dapp/src/constants/errors.ts index 2465ff22e..7125f462e 100644 --- a/dapp/src/constants/errors.ts +++ b/dapp/src/constants/errors.ts @@ -24,3 +24,11 @@ export const CONNECTION_ERRORS: Record = { description: "We encountered an error. Please try again.", }, } + +export const TOKEN_FORM_ERRORS = { + REQUIRED: "Please enter an amount.", + EXCEEDED_VALUE: + "The amount exceeds your current wallet balance. Add more funds to your wallet or lower the deposit amount.", + INSUFFICIENT_VALUE: (minValue: string) => + `The amount is below the minimum required deposit of ${minValue} BTC.`, +} diff --git a/dapp/src/utils/forms.ts b/dapp/src/utils/forms.ts index 69e3d87c0..3542c4365 100644 --- a/dapp/src/utils/forms.ts +++ b/dapp/src/utils/forms.ts @@ -1,15 +1,8 @@ +import { TOKEN_FORM_ERRORS } from "#/constants" import { CurrencyType } from "#/types" import { getCurrencyByType } from "./currency" import { fixedPointNumberToString } from "./numbers" -const ERRORS = { - REQUIRED: "Please enter an amount.", - EXCEEDED_VALUE: - "The amount exceeds your current wallet balance. Add more funds to your wallet or lower the deposit amount.", - INSUFFICIENT_VALUE: (minValue: string) => - `The amount is below the minimum required deposit of ${minValue} BTC.`, -} - export function getErrorsObj(errors: { [key in keyof T]: string }) { return (Object.keys(errors) as Array).every((name) => !errors[name]) ? {} @@ -22,16 +15,16 @@ export function validateTokenAmount( minValue: bigint, currency: CurrencyType, ): string | undefined { - if (value === undefined) return ERRORS.REQUIRED + if (value === undefined) return TOKEN_FORM_ERRORS.REQUIRED const { decimals } = getCurrencyByType(currency) const isMaximumValueExceeded = value > maxValue const isMinimumValueFulfilled = value >= minValue - if (isMaximumValueExceeded) return ERRORS.EXCEEDED_VALUE + if (isMaximumValueExceeded) return TOKEN_FORM_ERRORS.EXCEEDED_VALUE if (!isMinimumValueFulfilled) - return ERRORS.INSUFFICIENT_VALUE( + return TOKEN_FORM_ERRORS.INSUFFICIENT_VALUE( fixedPointNumberToString(minValue, decimals), ) From d3ff3e3bb2e0f0e02d3954d40894cc9311153a08 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 7 Aug 2024 13:48:16 +0200 Subject: [PATCH 09/28] Allow for string values passed to Formik setter --- .../shared/Form/FormTokenBalanceInput.tsx | 2 +- .../TokenAmountForm/TokenAmountFormBase.tsx | 2 +- .../shared/TokenAmountForm/index.tsx | 15 +++++++- .../shared/TokenBalanceInput/index.tsx | 37 +++++++++++++------ dapp/src/utils/numbers.ts | 7 ++++ 5 files changed, 48 insertions(+), 15 deletions(-) diff --git a/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx b/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx index 7b193f488..b83212081 100644 --- a/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx +++ b/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx @@ -16,7 +16,7 @@ export function FormTokenBalanceInput({ const [field, meta, helpers] = useField(name) const setAmount = useCallback( - (value?: bigint) => { + (value?: bigint | string) => { if (!meta.touched) logPromiseFailure(helpers.setTouched(true)) logPromiseFailure(helpers.setValue(value)) }, diff --git a/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx b/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx index 3675be36b..e064892f6 100644 --- a/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx +++ b/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx @@ -6,7 +6,7 @@ import { Form, FormTokenBalanceInput } from "../Form" const TOKEN_AMOUNT_FIELD_NAME = "amount" export type TokenAmountFormValues = { - [TOKEN_AMOUNT_FIELD_NAME]?: bigint + [TOKEN_AMOUNT_FIELD_NAME]?: bigint | string } export const useTokenAmountField = () => { diff --git a/dapp/src/components/shared/TokenAmountForm/index.tsx b/dapp/src/components/shared/TokenAmountForm/index.tsx index f06130398..f361023ec 100644 --- a/dapp/src/components/shared/TokenAmountForm/index.tsx +++ b/dapp/src/components/shared/TokenAmountForm/index.tsx @@ -1,5 +1,10 @@ import { FormikErrors, withFormik } from "formik" -import { getErrorsObj, validateTokenAmount } from "#/utils" +import { + getCurrencyByType, + getErrorsObj, + userAmountToBigInt, + validateTokenAmount, +} from "#/utils" import { BaseFormProps } from "#/types" import TokenAmountFormBase, { TokenAmountFormBaseProps, @@ -19,8 +24,14 @@ const TokenAmountForm = withFormik( validate: ({ amount }, { tokenBalance, currency, minTokenAmount }) => { const errors: FormikErrors = {} + const { decimals } = getCurrencyByType(currency) + const bigIntAmount = + typeof amount === "string" + ? userAmountToBigInt(amount, decimals) + : amount + errors.amount = validateTokenAmount( - amount, + bigIntAmount, tokenBalance, minTokenAmount, currency, diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index 7a3953e0d..bf3b5bae0 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from "react" +import React, { useMemo, useRef } from "react" import { Box, Button, @@ -14,6 +14,7 @@ import { } from "@chakra-ui/react" import { bigIntToUserAmount, + fixedPointNumberToString, getCurrencyByType, userAmountToBigInt, } from "#/utils" @@ -101,13 +102,13 @@ function FiatCurrencyBalance({ } export type TokenBalanceInputProps = { - amount?: bigint + amount?: bigint | string currency: CurrencyType tokenBalance: bigint placeholder?: string size?: "lg" | "md" fiatCurrency?: CurrencyType - setAmount: (value?: bigint) => void + setAmount: (value?: bigint | string) => void withMaxButton?: boolean } & InputProps & HelperErrorTextProps & @@ -127,11 +128,24 @@ export default function TokenBalanceInput({ withMaxButton = false, ...inputProps }: TokenBalanceInputProps) { - const valueRef = useRef(amount) + const valueRef = useRef(amount) const styles = useMultiStyleConfig("TokenBalanceInput", { size }) const { decimals, desiredDecimals } = getCurrencyByType(currency) + const amountValue = useMemo(() => { + if (typeof amount === "string") { + return amount + } + + return amount + ? bigIntToUserAmount(amount, decimals, desiredDecimals) + : undefined + }, [amount, decimals, desiredDecimals]) + + const bigIntAmount = + typeof amount === "string" ? userAmountToBigInt(amount, decimals) : amount + const handleValueChange = (value: string) => { valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined } @@ -164,11 +178,7 @@ export default function TokenBalanceInput({ isInvalid={hasError} placeholder={placeholder} {...inputProps} - value={ - amount - ? bigIntToUserAmount(amount, decimals, desiredDecimals) - : undefined - } + value={amountValue} onValueChange={(values: NumberFormatInputValues) => handleValueChange(values.value) } @@ -181,7 +191,12 @@ export default function TokenBalanceInput({ {withMaxButton && ( - @@ -195,7 +210,7 @@ export default function TokenBalanceInput({ {!hasError && !helperText && !!fiatCurrency && ( diff --git a/dapp/src/utils/numbers.ts b/dapp/src/utils/numbers.ts index b747a68fd..4ac67e7a7 100644 --- a/dapp/src/utils/numbers.ts +++ b/dapp/src/utils/numbers.ts @@ -207,3 +207,10 @@ export const addLeadingZero = (num: number): string => export const getPercentValue = (value: number, maxValue: number) => (value * 100) / maxValue + +export const getBigIntDecimalsLength = (value: bigint, decimals: number) => { + const valueStr = fixedPointNumberToString(value, decimals) + const decimalIndex = valueStr.indexOf(".") + + return decimalIndex === -1 ? 0 : valueStr.length - decimalIndex - 1 +} From b1f2d63ba3127a0b0dca60a92da74d5d9954c12c Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Mon, 12 Aug 2024 16:06:19 +0200 Subject: [PATCH 10/28] Implement alternative error messages per action --- .../StakeFormModal/index.tsx | 3 +- .../UnstakeFormModal/index.tsx | 3 +- .../shared/TokenAmountForm/index.tsx | 9 ++++-- .../shared/TokenBalanceInput/index.tsx | 5 +-- dapp/src/constants/errors.ts | 8 ++++- dapp/src/utils/forms.ts | 32 +++++++++++++++++-- 6 files changed, 51 insertions(+), 9 deletions(-) diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx index 5589852d9..f4c960bd4 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx @@ -3,7 +3,7 @@ import TokenAmountForm from "#/components/shared/TokenAmountForm" import { TokenAmountFormValues } from "#/components/shared/TokenAmountForm/TokenAmountFormBase" import { useMinDepositAmount, useWallet } from "#/hooks" import { FormSubmitButton } from "#/components/shared/Form" -import { BaseFormProps } from "#/types" +import { ACTION_FLOW_TYPES, BaseFormProps } from "#/types" import { formatTokenAmount, getCurrencyByType } from "#/utils" import StakeDetails from "./StakeDetails" @@ -18,6 +18,7 @@ function StakeFormModal({ return ( @@ -21,7 +22,10 @@ const TokenAmountForm = withFormik( mapPropsToValues: () => ({ amount: undefined, }), - validate: ({ amount }, { tokenBalance, currency, minTokenAmount }) => { + validate: ( + { amount }, + { tokenBalance, currency, minTokenAmount, actionType }, + ) => { const errors: FormikErrors = {} const { decimals } = getCurrencyByType(currency) @@ -31,6 +35,7 @@ const TokenAmountForm = withFormik( : amount errors.amount = validateTokenAmount( + actionType, bigIntAmount, tokenBalance, minTokenAmount, diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index bf3b5bae0..27d7dca8f 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -16,12 +16,12 @@ import { bigIntToUserAmount, fixedPointNumberToString, getCurrencyByType, + getTokenAmountErrorKey, userAmountToBigInt, } from "#/utils" import { CurrencyType } from "#/types" import { IconInfoCircle } from "@tabler/icons-react" import { useCurrencyConversion } from "#/hooks" -import { TOKEN_FORM_ERRORS } from "#/constants" import NumberFormatInput, { NumberFormatInputValues, NumberFormatInputProps, @@ -151,7 +151,8 @@ export default function TokenBalanceInput({ } const isBalanceExceeded = - hasError && errorMsgText === TOKEN_FORM_ERRORS.EXCEEDED_VALUE + typeof errorMsgText === "string" && + getTokenAmountErrorKey(errorMsgText) === "EXCEEDED_VALUE" return ( diff --git a/dapp/src/constants/errors.ts b/dapp/src/constants/errors.ts index 7125f462e..faf34d39d 100644 --- a/dapp/src/constants/errors.ts +++ b/dapp/src/constants/errors.ts @@ -25,10 +25,16 @@ export const CONNECTION_ERRORS: Record = { }, } -export const TOKEN_FORM_ERRORS = { +const ACTION_FORM_ERRORS = { REQUIRED: "Please enter an amount.", EXCEEDED_VALUE: "The amount exceeds your current wallet balance. Add more funds to your wallet or lower the deposit amount.", INSUFFICIENT_VALUE: (minValue: string) => `The amount is below the minimum required deposit of ${minValue} BTC.`, } + +export const STAKE_FORM_ERRORS = ACTION_FORM_ERRORS +export const UNSTAKE_FORM_ERRORS = { + ...ACTION_FORM_ERRORS, + EXCEEDED_VALUE: "Your Acre balance is insufficient.", +} diff --git a/dapp/src/utils/forms.ts b/dapp/src/utils/forms.ts index 3542c4365..2bae57bc6 100644 --- a/dapp/src/utils/forms.ts +++ b/dapp/src/utils/forms.ts @@ -1,5 +1,5 @@ -import { TOKEN_FORM_ERRORS } from "#/constants" -import { CurrencyType } from "#/types" +import { STAKE_FORM_ERRORS, UNSTAKE_FORM_ERRORS } from "#/constants" +import { ACTION_FLOW_TYPES, ActionFlowType, CurrencyType } from "#/types" import { getCurrencyByType } from "./currency" import { fixedPointNumberToString } from "./numbers" @@ -10,11 +10,17 @@ export function getErrorsObj(errors: { [key in keyof T]: string }) { } export function validateTokenAmount( + actionType: ActionFlowType, value: bigint | undefined, maxValue: bigint, minValue: bigint, currency: CurrencyType, ): string | undefined { + const TOKEN_FORM_ERRORS = + actionType === ACTION_FLOW_TYPES.STAKE + ? STAKE_FORM_ERRORS + : UNSTAKE_FORM_ERRORS + if (value === undefined) return TOKEN_FORM_ERRORS.REQUIRED const { decimals } = getCurrencyByType(currency) @@ -30,3 +36,25 @@ export function validateTokenAmount( return undefined } + +type GetTokenAmountErrorKeyReturnType = keyof typeof STAKE_FORM_ERRORS | null +export const getTokenAmountErrorKey = ( + errorMessage: string, +): GetTokenAmountErrorKeyReturnType => { + const errorKeys = Object.keys(STAKE_FORM_ERRORS) + const errorKeyValuePairs = [ + ...new Set([ + ...Object.entries(STAKE_FORM_ERRORS), + ...Object.entries(UNSTAKE_FORM_ERRORS), + ]), + ] + const errorKey = + errorKeys.find((key) => { + const errorValue = errorKeyValuePairs.find( + ([_, value]) => value === errorMessage, + ) + return errorValue && errorValue[0] === key + }) ?? null + + return errorKey as GetTokenAmountErrorKeyReturnType +} From 5fdceace4280aa7b6c14dbcf38d57ce509edf8f3 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Mon, 12 Aug 2024 16:39:29 +0200 Subject: [PATCH 11/28] Implement fees ceiling --- .../ActiveStakingStep/StakeFormModal/StakeDetails.tsx | 3 ++- dapp/src/components/shared/CurrencyBalance/index.tsx | 6 ++++-- dapp/src/constants/currency.ts | 1 + dapp/src/utils/numbers.ts | 8 +++++++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/StakeDetails.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/StakeDetails.tsx index c6cea79cc..0b65197ca 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/StakeDetails.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/StakeDetails.tsx @@ -6,7 +6,7 @@ import { useTokenAmountField } from "#/components/shared/TokenAmountForm/TokenAm import { FeesTooltip } from "#/components/TransactionModal/FeesTooltip" import { useMinDepositAmount, useTransactionDetails } from "#/hooks" import { CurrencyType } from "#/types" -import { DESIRED_DECIMALS_FOR_FEE } from "#/constants" +import { DESIRED_DECIMALS_FOR_FEE, FEE_CEIL_PRECISION } from "#/constants" function StakeDetails({ currency }: { currency: CurrencyType }) { const { value = 0n } = useTokenAmountField() @@ -36,6 +36,7 @@ function StakeDetails({ currency }: { currency: CurrencyType }) { currency, amount: total, desiredDecimals: DESIRED_DECIMALS_FOR_FEE, + ceilPrecision: FEE_CEIL_PRECISION, }} to={{ currency: "usd", diff --git a/dapp/src/components/shared/CurrencyBalance/index.tsx b/dapp/src/components/shared/CurrencyBalance/index.tsx index 0be487b6a..502e9c186 100644 --- a/dapp/src/components/shared/CurrencyBalance/index.tsx +++ b/dapp/src/components/shared/CurrencyBalance/index.tsx @@ -17,6 +17,7 @@ export type CurrencyBalanceProps = { amount?: AmountType shouldBeFormatted?: boolean desiredDecimals?: number + ceilPrecision?: number size?: ResponsiveValue variant?: ResponsiveValue< | "greater-balance-md" @@ -34,6 +35,7 @@ export function CurrencyBalance({ amount, shouldBeFormatted = true, desiredDecimals: customDesiredDecimals, + ceilPrecision, size, variant, balanceFontWeight = "bold", @@ -58,10 +60,10 @@ export function CurrencyBalance({ const balance = useMemo(() => { const value = amount ?? 0 if (shouldBeFormatted || typeof value === "bigint") - return formatTokenAmount(value, decimals, desiredDecimals) + return formatTokenAmount(value, decimals, desiredDecimals, ceilPrecision) return numberToLocaleString(value, desiredDecimals) - }, [amount, decimals, desiredDecimals, shouldBeFormatted]) + }, [amount, decimals, desiredDecimals, shouldBeFormatted, ceilPrecision]) return ( diff --git a/dapp/src/constants/currency.ts b/dapp/src/constants/currency.ts index 6c82e4f42..db51cdcf7 100644 --- a/dapp/src/constants/currency.ts +++ b/dapp/src/constants/currency.ts @@ -2,6 +2,7 @@ import { Currency, CurrencyType } from "#/types" import env from "./env" export const DESIRED_DECIMALS_FOR_FEE = 5 +export const FEE_CEIL_PRECISION = 4 export const BITCOIN: Currency = { name: "Bitcoin", diff --git a/dapp/src/utils/numbers.ts b/dapp/src/utils/numbers.ts index 4ac67e7a7..035620bbb 100644 --- a/dapp/src/utils/numbers.ts +++ b/dapp/src/utils/numbers.ts @@ -52,6 +52,7 @@ export const formatTokenAmount = ( amount: number | string | bigint, decimals = 18, desiredDecimals = 2, + ceilPrecision = desiredDecimals, ) => { const fixedPoint = BigInt(amount) @@ -59,11 +60,16 @@ export const formatTokenAmount = ( return `0.${"0".repeat(desiredDecimals)}` } - const formattedAmount = bigIntToUserAmount( + let formattedAmount = bigIntToUserAmount( fixedPoint, decimals, desiredDecimals, ) + if (ceilPrecision !== desiredDecimals) { + formattedAmount = + Math.ceil(formattedAmount * 10 ** ceilPrecision) / 10 ** ceilPrecision + } + const minAmountToDisplay = 1 / 10 ** Math.min(desiredDecimals, decimals) if (minAmountToDisplay > formattedAmount) { From 358e90445c101372c4cb38fde62e3f76fceb5f2b Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Mon, 12 Aug 2024 17:07:38 +0200 Subject: [PATCH 12/28] Fix zero amount value bug --- sdk/src/modules/account.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/modules/account.ts b/sdk/src/modules/account.ts index 99fed0807..5c8011d93 100644 --- a/sdk/src/modules/account.ts +++ b/sdk/src/modules/account.ts @@ -165,7 +165,7 @@ export default class Account { ) const amount = toSatoshi( - depositFromSubgraph?.amountToDeposit ?? deposit.initialAmount, + depositFromSubgraph?.amountToDeposit || deposit.initialAmount, ) return { From 56950f338ee334c2d27a29ae715a57231a6da81e Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Mon, 12 Aug 2024 17:11:51 +0200 Subject: [PATCH 13/28] Resolve too much props passed to component issue --- .../shared/ActivitiesList/ActivitiesList.tsx | 11 +++++++---- .../shared/ActivitiesList/ActivitiesListItem.tsx | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/dapp/src/components/shared/ActivitiesList/ActivitiesList.tsx b/dapp/src/components/shared/ActivitiesList/ActivitiesList.tsx index f390ca39c..dfb744afe 100644 --- a/dapp/src/components/shared/ActivitiesList/ActivitiesList.tsx +++ b/dapp/src/components/shared/ActivitiesList/ActivitiesList.tsx @@ -31,9 +31,9 @@ function ActivitiesList(props: ListProps) { return ( - {latestActivities.map((item) => ( + {latestActivities.map(({ id, amount, status, type, txHash }) => ( handleItemDismiss(item.id)} + amount={amount} + status={status} + type={type} + txHash={txHash} + handleDismiss={() => handleItemDismiss(id)} /> ))} diff --git a/dapp/src/components/shared/ActivitiesList/ActivitiesListItem.tsx b/dapp/src/components/shared/ActivitiesList/ActivitiesListItem.tsx index f6c36bfb5..51bd23344 100644 --- a/dapp/src/components/shared/ActivitiesList/ActivitiesListItem.tsx +++ b/dapp/src/components/shared/ActivitiesList/ActivitiesListItem.tsx @@ -14,7 +14,7 @@ import { import { TextMd } from "../Typography" type ActivitiesListItemProps = Omit & - ActivityType & { + Omit & { handleDismiss?: () => void } From bd10ec7ae7b40241edd980398af23002f1242261 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 13 Aug 2024 13:43:15 +0200 Subject: [PATCH 14/28] Refactor error typecheck function --- dapp/src/constants/errors.ts | 14 ++++++++------ dapp/src/utils/forms.ts | 21 +++++++++------------ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/dapp/src/constants/errors.ts b/dapp/src/constants/errors.ts index faf34d39d..320b53195 100644 --- a/dapp/src/constants/errors.ts +++ b/dapp/src/constants/errors.ts @@ -1,4 +1,4 @@ -import { ConnectionErrorData } from "#/types" +import { ACTION_FLOW_TYPES, ConnectionErrorData } from "#/types" export const CONNECTION_ERRORS: Record = { REJECTED: { @@ -25,7 +25,7 @@ export const CONNECTION_ERRORS: Record = { }, } -const ACTION_FORM_ERRORS = { +export const TOKEN_FORM_ERRORS = { REQUIRED: "Please enter an amount.", EXCEEDED_VALUE: "The amount exceeds your current wallet balance. Add more funds to your wallet or lower the deposit amount.", @@ -33,8 +33,10 @@ const ACTION_FORM_ERRORS = { `The amount is below the minimum required deposit of ${minValue} BTC.`, } -export const STAKE_FORM_ERRORS = ACTION_FORM_ERRORS -export const UNSTAKE_FORM_ERRORS = { - ...ACTION_FORM_ERRORS, - EXCEEDED_VALUE: "Your Acre balance is insufficient.", +export const ACTION_FORM_ERRORS = { + [ACTION_FLOW_TYPES.STAKE]: TOKEN_FORM_ERRORS, + [ACTION_FLOW_TYPES.UNSTAKE]: { + ...TOKEN_FORM_ERRORS, + EXCEEDED_VALUE: "Your Acre balance is insufficient.", + }, } diff --git a/dapp/src/utils/forms.ts b/dapp/src/utils/forms.ts index 2bae57bc6..8c67d11c2 100644 --- a/dapp/src/utils/forms.ts +++ b/dapp/src/utils/forms.ts @@ -1,4 +1,4 @@ -import { STAKE_FORM_ERRORS, UNSTAKE_FORM_ERRORS } from "#/constants" +import { ACTION_FORM_ERRORS, TOKEN_FORM_ERRORS } from "#/constants" import { ACTION_FLOW_TYPES, ActionFlowType, CurrencyType } from "#/types" import { getCurrencyByType } from "./currency" import { fixedPointNumberToString } from "./numbers" @@ -16,36 +16,33 @@ export function validateTokenAmount( minValue: bigint, currency: CurrencyType, ): string | undefined { - const TOKEN_FORM_ERRORS = - actionType === ACTION_FLOW_TYPES.STAKE - ? STAKE_FORM_ERRORS - : UNSTAKE_FORM_ERRORS + const ERRORS_BY_ACTION_TYPE = ACTION_FORM_ERRORS[actionType] - if (value === undefined) return TOKEN_FORM_ERRORS.REQUIRED + if (value === undefined) return ERRORS_BY_ACTION_TYPE.REQUIRED const { decimals } = getCurrencyByType(currency) const isMaximumValueExceeded = value > maxValue const isMinimumValueFulfilled = value >= minValue - if (isMaximumValueExceeded) return TOKEN_FORM_ERRORS.EXCEEDED_VALUE + if (isMaximumValueExceeded) return ERRORS_BY_ACTION_TYPE.EXCEEDED_VALUE if (!isMinimumValueFulfilled) - return TOKEN_FORM_ERRORS.INSUFFICIENT_VALUE( + return ERRORS_BY_ACTION_TYPE.INSUFFICIENT_VALUE( fixedPointNumberToString(minValue, decimals), ) return undefined } -type GetTokenAmountErrorKeyReturnType = keyof typeof STAKE_FORM_ERRORS | null +type GetTokenAmountErrorKeyReturnType = keyof typeof TOKEN_FORM_ERRORS | null export const getTokenAmountErrorKey = ( errorMessage: string, ): GetTokenAmountErrorKeyReturnType => { - const errorKeys = Object.keys(STAKE_FORM_ERRORS) + const errorKeys = Object.keys(ACTION_FORM_ERRORS) const errorKeyValuePairs = [ ...new Set([ - ...Object.entries(STAKE_FORM_ERRORS), - ...Object.entries(UNSTAKE_FORM_ERRORS), + ...Object.entries(ACTION_FORM_ERRORS[ACTION_FLOW_TYPES.STAKE]), + ...Object.entries(ACTION_FORM_ERRORS[ACTION_FLOW_TYPES.UNSTAKE]), ]), ] const errorKey = From 29245a10221c3c6f74ffea8c7bf0d4a6dcee65cf Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 13 Aug 2024 13:46:22 +0200 Subject: [PATCH 15/28] Revert "Allow for string values passed to Formik setter" This reverts commit d3ff3e3bb2e0f0e02d3954d40894cc9311153a08. --- .../shared/Form/FormTokenBalanceInput.tsx | 2 +- .../TokenAmountForm/TokenAmountFormBase.tsx | 2 +- .../shared/TokenAmountForm/index.tsx | 15 +------- .../shared/TokenBalanceInput/index.tsx | 37 ++++++------------- dapp/src/utils/numbers.ts | 7 ---- 5 files changed, 15 insertions(+), 48 deletions(-) diff --git a/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx b/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx index b83212081..7b193f488 100644 --- a/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx +++ b/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx @@ -16,7 +16,7 @@ export function FormTokenBalanceInput({ const [field, meta, helpers] = useField(name) const setAmount = useCallback( - (value?: bigint | string) => { + (value?: bigint) => { if (!meta.touched) logPromiseFailure(helpers.setTouched(true)) logPromiseFailure(helpers.setValue(value)) }, diff --git a/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx b/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx index e064892f6..3675be36b 100644 --- a/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx +++ b/dapp/src/components/shared/TokenAmountForm/TokenAmountFormBase.tsx @@ -6,7 +6,7 @@ import { Form, FormTokenBalanceInput } from "../Form" const TOKEN_AMOUNT_FIELD_NAME = "amount" export type TokenAmountFormValues = { - [TOKEN_AMOUNT_FIELD_NAME]?: bigint | string + [TOKEN_AMOUNT_FIELD_NAME]?: bigint } export const useTokenAmountField = () => { diff --git a/dapp/src/components/shared/TokenAmountForm/index.tsx b/dapp/src/components/shared/TokenAmountForm/index.tsx index 48d726440..3c4dc10e2 100644 --- a/dapp/src/components/shared/TokenAmountForm/index.tsx +++ b/dapp/src/components/shared/TokenAmountForm/index.tsx @@ -1,10 +1,5 @@ import { FormikErrors, withFormik } from "formik" -import { - getCurrencyByType, - getErrorsObj, - userAmountToBigInt, - validateTokenAmount, -} from "#/utils" +import { getErrorsObj, validateTokenAmount } from "#/utils" import { ActionFlowType, BaseFormProps } from "#/types" import TokenAmountFormBase, { TokenAmountFormBaseProps, @@ -28,15 +23,9 @@ const TokenAmountForm = withFormik( ) => { const errors: FormikErrors = {} - const { decimals } = getCurrencyByType(currency) - const bigIntAmount = - typeof amount === "string" - ? userAmountToBigInt(amount, decimals) - : amount - errors.amount = validateTokenAmount( actionType, - bigIntAmount, + amount, tokenBalance, minTokenAmount, currency, diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index 27d7dca8f..0635d4feb 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useRef } from "react" +import React, { useRef } from "react" import { Box, Button, @@ -14,7 +14,6 @@ import { } from "@chakra-ui/react" import { bigIntToUserAmount, - fixedPointNumberToString, getCurrencyByType, getTokenAmountErrorKey, userAmountToBigInt, @@ -102,13 +101,13 @@ function FiatCurrencyBalance({ } export type TokenBalanceInputProps = { - amount?: bigint | string + amount?: bigint currency: CurrencyType tokenBalance: bigint placeholder?: string size?: "lg" | "md" fiatCurrency?: CurrencyType - setAmount: (value?: bigint | string) => void + setAmount: (value?: bigint) => void withMaxButton?: boolean } & InputProps & HelperErrorTextProps & @@ -128,24 +127,11 @@ export default function TokenBalanceInput({ withMaxButton = false, ...inputProps }: TokenBalanceInputProps) { - const valueRef = useRef(amount) + const valueRef = useRef(amount) const styles = useMultiStyleConfig("TokenBalanceInput", { size }) const { decimals, desiredDecimals } = getCurrencyByType(currency) - const amountValue = useMemo(() => { - if (typeof amount === "string") { - return amount - } - - return amount - ? bigIntToUserAmount(amount, decimals, desiredDecimals) - : undefined - }, [amount, decimals, desiredDecimals]) - - const bigIntAmount = - typeof amount === "string" ? userAmountToBigInt(amount, decimals) : amount - const handleValueChange = (value: string) => { valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined } @@ -179,7 +165,11 @@ export default function TokenBalanceInput({ isInvalid={hasError} placeholder={placeholder} {...inputProps} - value={amountValue} + value={ + amount + ? bigIntToUserAmount(amount, decimals, desiredDecimals) + : undefined + } onValueChange={(values: NumberFormatInputValues) => handleValueChange(values.value) } @@ -192,12 +182,7 @@ export default function TokenBalanceInput({ {withMaxButton && ( - @@ -211,7 +196,7 @@ export default function TokenBalanceInput({ {!hasError && !helperText && !!fiatCurrency && ( diff --git a/dapp/src/utils/numbers.ts b/dapp/src/utils/numbers.ts index 035620bbb..d61ab1c59 100644 --- a/dapp/src/utils/numbers.ts +++ b/dapp/src/utils/numbers.ts @@ -213,10 +213,3 @@ export const addLeadingZero = (num: number): string => export const getPercentValue = (value: number, maxValue: number) => (value * 100) / maxValue - -export const getBigIntDecimalsLength = (value: bigint, decimals: number) => { - const valueStr = fixedPointNumberToString(value, decimals) - const decimalIndex = valueStr.indexOf(".") - - return decimalIndex === -1 ? 0 : valueStr.length - decimalIndex - 1 -} From f992924d81cbf347c9fde22e5cbd6cd2038723e8 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 13 Aug 2024 13:56:20 +0200 Subject: [PATCH 16/28] Revert "Fix zero amount value bug" This reverts commit 358e90445c101372c4cb38fde62e3f76fceb5f2b. --- sdk/src/modules/account.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/modules/account.ts b/sdk/src/modules/account.ts index 5c8011d93..99fed0807 100644 --- a/sdk/src/modules/account.ts +++ b/sdk/src/modules/account.ts @@ -165,7 +165,7 @@ export default class Account { ) const amount = toSatoshi( - depositFromSubgraph?.amountToDeposit || deposit.initialAmount, + depositFromSubgraph?.amountToDeposit ?? deposit.initialAmount, ) return { From 4be544e9b26bb408e887bdf63a5e866eb046645d Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 13 Aug 2024 13:56:31 +0200 Subject: [PATCH 17/28] Revert "Resolve too much props passed to component issue" This reverts commit 56950f338ee334c2d27a29ae715a57231a6da81e. --- .../shared/ActivitiesList/ActivitiesList.tsx | 11 ++++------- .../shared/ActivitiesList/ActivitiesListItem.tsx | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/dapp/src/components/shared/ActivitiesList/ActivitiesList.tsx b/dapp/src/components/shared/ActivitiesList/ActivitiesList.tsx index dfb744afe..f390ca39c 100644 --- a/dapp/src/components/shared/ActivitiesList/ActivitiesList.tsx +++ b/dapp/src/components/shared/ActivitiesList/ActivitiesList.tsx @@ -31,9 +31,9 @@ function ActivitiesList(props: ListProps) { return ( - {latestActivities.map(({ id, amount, status, type, txHash }) => ( + {latestActivities.map((item) => ( handleItemDismiss(id)} + {...item} + handleDismiss={() => handleItemDismiss(item.id)} /> ))} diff --git a/dapp/src/components/shared/ActivitiesList/ActivitiesListItem.tsx b/dapp/src/components/shared/ActivitiesList/ActivitiesListItem.tsx index 51bd23344..f6c36bfb5 100644 --- a/dapp/src/components/shared/ActivitiesList/ActivitiesListItem.tsx +++ b/dapp/src/components/shared/ActivitiesList/ActivitiesListItem.tsx @@ -14,7 +14,7 @@ import { import { TextMd } from "../Typography" type ActivitiesListItemProps = Omit & - Omit & { + ActivityType & { handleDismiss?: () => void } From 9e2e806a5d7d49c727ffb638c839229a51c64336 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 13 Aug 2024 14:00:54 +0200 Subject: [PATCH 18/28] Revert "Implement fees ceiling" This reverts commit 5fdceace4280aa7b6c14dbcf38d57ce509edf8f3. --- .../ActiveStakingStep/StakeFormModal/StakeDetails.tsx | 3 +-- dapp/src/components/shared/CurrencyBalance/index.tsx | 6 ++---- dapp/src/constants/currency.ts | 1 - dapp/src/utils/numbers.ts | 8 +------- 4 files changed, 4 insertions(+), 14 deletions(-) diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/StakeDetails.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/StakeDetails.tsx index 0b65197ca..c6cea79cc 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/StakeDetails.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/StakeDetails.tsx @@ -6,7 +6,7 @@ import { useTokenAmountField } from "#/components/shared/TokenAmountForm/TokenAm import { FeesTooltip } from "#/components/TransactionModal/FeesTooltip" import { useMinDepositAmount, useTransactionDetails } from "#/hooks" import { CurrencyType } from "#/types" -import { DESIRED_DECIMALS_FOR_FEE, FEE_CEIL_PRECISION } from "#/constants" +import { DESIRED_DECIMALS_FOR_FEE } from "#/constants" function StakeDetails({ currency }: { currency: CurrencyType }) { const { value = 0n } = useTokenAmountField() @@ -36,7 +36,6 @@ function StakeDetails({ currency }: { currency: CurrencyType }) { currency, amount: total, desiredDecimals: DESIRED_DECIMALS_FOR_FEE, - ceilPrecision: FEE_CEIL_PRECISION, }} to={{ currency: "usd", diff --git a/dapp/src/components/shared/CurrencyBalance/index.tsx b/dapp/src/components/shared/CurrencyBalance/index.tsx index 502e9c186..0be487b6a 100644 --- a/dapp/src/components/shared/CurrencyBalance/index.tsx +++ b/dapp/src/components/shared/CurrencyBalance/index.tsx @@ -17,7 +17,6 @@ export type CurrencyBalanceProps = { amount?: AmountType shouldBeFormatted?: boolean desiredDecimals?: number - ceilPrecision?: number size?: ResponsiveValue variant?: ResponsiveValue< | "greater-balance-md" @@ -35,7 +34,6 @@ export function CurrencyBalance({ amount, shouldBeFormatted = true, desiredDecimals: customDesiredDecimals, - ceilPrecision, size, variant, balanceFontWeight = "bold", @@ -60,10 +58,10 @@ export function CurrencyBalance({ const balance = useMemo(() => { const value = amount ?? 0 if (shouldBeFormatted || typeof value === "bigint") - return formatTokenAmount(value, decimals, desiredDecimals, ceilPrecision) + return formatTokenAmount(value, decimals, desiredDecimals) return numberToLocaleString(value, desiredDecimals) - }, [amount, decimals, desiredDecimals, shouldBeFormatted, ceilPrecision]) + }, [amount, decimals, desiredDecimals, shouldBeFormatted]) return ( diff --git a/dapp/src/constants/currency.ts b/dapp/src/constants/currency.ts index db51cdcf7..6c82e4f42 100644 --- a/dapp/src/constants/currency.ts +++ b/dapp/src/constants/currency.ts @@ -2,7 +2,6 @@ import { Currency, CurrencyType } from "#/types" import env from "./env" export const DESIRED_DECIMALS_FOR_FEE = 5 -export const FEE_CEIL_PRECISION = 4 export const BITCOIN: Currency = { name: "Bitcoin", diff --git a/dapp/src/utils/numbers.ts b/dapp/src/utils/numbers.ts index d61ab1c59..b747a68fd 100644 --- a/dapp/src/utils/numbers.ts +++ b/dapp/src/utils/numbers.ts @@ -52,7 +52,6 @@ export const formatTokenAmount = ( amount: number | string | bigint, decimals = 18, desiredDecimals = 2, - ceilPrecision = desiredDecimals, ) => { const fixedPoint = BigInt(amount) @@ -60,16 +59,11 @@ export const formatTokenAmount = ( return `0.${"0".repeat(desiredDecimals)}` } - let formattedAmount = bigIntToUserAmount( + const formattedAmount = bigIntToUserAmount( fixedPoint, decimals, desiredDecimals, ) - if (ceilPrecision !== desiredDecimals) { - formattedAmount = - Math.ceil(formattedAmount * 10 ** ceilPrecision) / 10 ** ceilPrecision - } - const minAmountToDisplay = 1 / 10 ** Math.min(desiredDecimals, decimals) if (minAmountToDisplay > formattedAmount) { From 19a61edcb163f7d23d1a20f4d24a8681af1a6862 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 14 Aug 2024 13:18:58 +0200 Subject: [PATCH 19/28] Fix error type validation function --- .../shared/TokenBalanceInput/index.tsx | 4 +- dapp/src/utils/forms.ts | 46 +++++++++++-------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index 0635d4feb..98f56af0b 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -15,7 +15,7 @@ import { import { bigIntToUserAmount, getCurrencyByType, - getTokenAmountErrorKey, + isFormError, userAmountToBigInt, } from "#/utils" import { CurrencyType } from "#/types" @@ -138,7 +138,7 @@ export default function TokenBalanceInput({ const isBalanceExceeded = typeof errorMsgText === "string" && - getTokenAmountErrorKey(errorMsgText) === "EXCEEDED_VALUE" + isFormError("EXCEEDED_VALUE", errorMsgText) return ( diff --git a/dapp/src/utils/forms.ts b/dapp/src/utils/forms.ts index 8c67d11c2..6196f3b26 100644 --- a/dapp/src/utils/forms.ts +++ b/dapp/src/utils/forms.ts @@ -1,5 +1,5 @@ import { ACTION_FORM_ERRORS, TOKEN_FORM_ERRORS } from "#/constants" -import { ACTION_FLOW_TYPES, ActionFlowType, CurrencyType } from "#/types" +import { ActionFlowType, CurrencyType } from "#/types" import { getCurrencyByType } from "./currency" import { fixedPointNumberToString } from "./numbers" @@ -34,24 +34,30 @@ export function validateTokenAmount( return undefined } -type GetTokenAmountErrorKeyReturnType = keyof typeof TOKEN_FORM_ERRORS | null -export const getTokenAmountErrorKey = ( - errorMessage: string, -): GetTokenAmountErrorKeyReturnType => { - const errorKeys = Object.keys(ACTION_FORM_ERRORS) - const errorKeyValuePairs = [ - ...new Set([ - ...Object.entries(ACTION_FORM_ERRORS[ACTION_FLOW_TYPES.STAKE]), - ...Object.entries(ACTION_FORM_ERRORS[ACTION_FLOW_TYPES.UNSTAKE]), - ]), +type ParametrizedError = (value: number) => string +export const isFormError = ( + type: keyof typeof TOKEN_FORM_ERRORS, + message: string, +) => { + let errorPredicates = [ + ACTION_FORM_ERRORS.STAKE[type], + ACTION_FORM_ERRORS.UNSTAKE[type], ] - const errorKey = - errorKeys.find((key) => { - const errorValue = errorKeyValuePairs.find( - ([_, value]) => value === errorMessage, - ) - return errorValue && errorValue[0] === key - }) ?? null - - return errorKey as GetTokenAmountErrorKeyReturnType + + const isParametrizedError = errorPredicates.every( + (predicate) => typeof predicate === "function", + ) + + if (isParametrizedError) { + const errorParameter = (message.match(/\d*\.\d+|\d+/g) ?? []).map( + parseFloat, + )[0] + + // Already checked that all predicates are functions + errorPredicates = (errorPredicates as unknown as ParametrizedError[]).map( + (predicate) => predicate(errorParameter), + ) + } + + return errorPredicates.includes(message) } From 401c081e4952b087142c81befb8a168e3318f630 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 14 Aug 2024 13:31:05 +0200 Subject: [PATCH 20/28] Remove unused imports --- dapp/src/components/shared/TokenBalanceInput/index.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index 98f56af0b..d39a55808 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -13,7 +13,7 @@ import { useMultiStyleConfig, } from "@chakra-ui/react" import { - bigIntToUserAmount, + fixedPointNumberToString, getCurrencyByType, isFormError, userAmountToBigInt, @@ -130,7 +130,7 @@ export default function TokenBalanceInput({ const valueRef = useRef(amount) const styles = useMultiStyleConfig("TokenBalanceInput", { size }) - const { decimals, desiredDecimals } = getCurrencyByType(currency) + const { decimals } = getCurrencyByType(currency) const handleValueChange = (value: string) => { valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined @@ -166,9 +166,7 @@ export default function TokenBalanceInput({ placeholder={placeholder} {...inputProps} value={ - amount - ? bigIntToUserAmount(amount, decimals, desiredDecimals) - : undefined + amount ? fixedPointNumberToString(amount, decimals) : undefined } onValueChange={(values: NumberFormatInputValues) => handleValueChange(values.value) From 07b172e48858454f2eeb3453271c9903644b116e Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Mon, 19 Aug 2024 10:45:50 +0200 Subject: [PATCH 21/28] Update invalid state styles --- dapp/src/theme/Input.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/dapp/src/theme/Input.ts b/dapp/src/theme/Input.ts index c6d5c5a7c..32ecb1269 100644 --- a/dapp/src/theme/Input.ts +++ b/dapp/src/theme/Input.ts @@ -18,7 +18,6 @@ const variantBalanceField = defineStyle({ }, _invalid: { - color: "red.400", borderColor: "red.400", }, }) From a5997495a46e1f4b41ca46bc493905c4057cbcb7 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Mon, 19 Aug 2024 10:51:31 +0200 Subject: [PATCH 22/28] Update placeholders --- .../ActiveStakingStep/StakeFormModal/index.tsx | 6 +++--- .../ActiveUnstakingStep/UnstakeFormModal/index.tsx | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx index f4c960bd4..27ebda4c9 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx @@ -4,7 +4,7 @@ import { TokenAmountFormValues } from "#/components/shared/TokenAmountForm/Token import { useMinDepositAmount, useWallet } from "#/hooks" import { FormSubmitButton } from "#/components/shared/Form" import { ACTION_FLOW_TYPES, BaseFormProps } from "#/types" -import { formatTokenAmount, getCurrencyByType } from "#/utils" +import { fixedPointNumberToString, getCurrencyByType } from "#/utils" import StakeDetails from "./StakeDetails" function StakeFormModal({ @@ -13,8 +13,8 @@ function StakeFormModal({ const minDepositAmount = useMinDepositAmount() const { balance: tokenBalance } = useWallet() - const { decimals, desiredDecimals } = getCurrencyByType("bitcoin") - const inputPlaceholder = `Minimum ${formatTokenAmount(minDepositAmount, decimals, desiredDecimals)} BTC` + const { decimals } = getCurrencyByType("bitcoin") + const inputPlaceholder = `Minimum ${fixedPointNumberToString(minDepositAmount, decimals)} BTC` return ( Date: Mon, 19 Aug 2024 21:23:55 +0200 Subject: [PATCH 23/28] Update validation strategy --- .../shared/Form/FormTokenBalanceInput.tsx | 18 ++++++++++++++++-- .../shared/TokenAmountForm/index.tsx | 1 + .../shared/TokenBalanceInput/index.tsx | 11 +++++++---- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx b/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx index 7b193f488..1fc0946d1 100644 --- a/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx +++ b/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect } from "react" +import React, { ChangeEventHandler, useCallback, useEffect } from "react" import { useField } from "formik" import { logPromiseFailure } from "#/utils" import TokenBalanceInput, { TokenBalanceInputProps } from "../TokenBalanceInput" @@ -29,14 +29,28 @@ export function FormTokenBalanceInput({ } }, [defaultValue, setAmount]) + const handleChange = useCallback( + (event) => { + field?.onChange(event) + + if (!meta.error) { + return + } + + helpers.setError(undefined) + }, + [field, meta, helpers], + ) + return ( ) } diff --git a/dapp/src/components/shared/TokenAmountForm/index.tsx b/dapp/src/components/shared/TokenAmountForm/index.tsx index 3c4dc10e2..a1360ed8e 100644 --- a/dapp/src/components/shared/TokenAmountForm/index.tsx +++ b/dapp/src/components/shared/TokenAmountForm/index.tsx @@ -37,6 +37,7 @@ const TokenAmountForm = withFormik( props.onSubmitForm(values) }, validateOnBlur: false, + validateOnChange: false, }, )(TokenAmountFormBase) diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index d39a55808..904e45ade 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from "react" +import React, { ChangeEventHandler, useRef } from "react" import { Box, Button, @@ -136,6 +136,11 @@ export default function TokenBalanceInput({ valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined } + const handleChange: ChangeEventHandler = (event) => { + inputProps.onChange?.(event) + setAmount(valueRef?.current) + } + const isBalanceExceeded = typeof errorMsgText === "string" && isFormError("EXCEEDED_VALUE", errorMsgText) @@ -171,9 +176,7 @@ export default function TokenBalanceInput({ onValueChange={(values: NumberFormatInputValues) => handleValueChange(values.value) } - onChange={() => { - setAmount(valueRef?.current) - }} + onChange={handleChange} decimalScale={decimals} allowNegative={false} /> From 8e1f875df1b8dc6909333a90b4502963628eadb1 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Mon, 19 Aug 2024 21:46:25 +0200 Subject: [PATCH 24/28] Add input suffix --- dapp/src/components/shared/NumberFormatInput/index.tsx | 2 +- dapp/src/components/shared/TokenBalanceInput/index.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dapp/src/components/shared/NumberFormatInput/index.tsx b/dapp/src/components/shared/NumberFormatInput/index.tsx index 56df3bb16..37b147efa 100644 --- a/dapp/src/components/shared/NumberFormatInput/index.tsx +++ b/dapp/src/components/shared/NumberFormatInput/index.tsx @@ -13,7 +13,7 @@ export type NumberFormatInputValues = { export type NumberFormatInputProps = { onValueChange: (values: NumberFormatInputValues) => void } & InputProps & - Pick + Pick /** * Component is from the Threshold Network React Components repository. diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index 904e45ade..3c71ba2ee 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -130,7 +130,7 @@ export default function TokenBalanceInput({ const valueRef = useRef(amount) const styles = useMultiStyleConfig("TokenBalanceInput", { size }) - const { decimals } = getCurrencyByType(currency) + const { decimals, symbol } = getCurrencyByType(currency) const handleValueChange = (value: string) => { valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined @@ -169,6 +169,7 @@ export default function TokenBalanceInput({ variant={VARIANT} isInvalid={hasError} placeholder={placeholder} + suffix={` ${symbol}`} {...inputProps} value={ amount ? fixedPointNumberToString(amount, decimals) : undefined From a0638053f0cabee619fd9d58dd307b0a3b4c3195 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Mon, 19 Aug 2024 22:13:28 +0200 Subject: [PATCH 25/28] Update token amount copies --- .../ActiveStakingStep/StakeFormModal/index.tsx | 2 ++ .../ActiveUnstakingStep/UnstakeFormModal/index.tsx | 2 ++ .../components/shared/TokenAmountForm/TokenAmountFormBase.tsx | 3 +++ dapp/src/components/shared/TokenBalanceInput/index.tsx | 4 +++- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx index 27ebda4c9..4149e4fc7 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx @@ -15,11 +15,13 @@ function StakeFormModal({ const { decimals } = getCurrencyByType("bitcoin") const inputPlaceholder = `Minimum ${fixedPointNumberToString(minDepositAmount, decimals)} BTC` + const tokenAmountLabel = "Wallet balance" return ( void withMaxButton?: boolean + tokenAmountLabel?: string } & InputProps & HelperErrorTextProps & Pick @@ -125,6 +126,7 @@ export default function TokenBalanceInput({ hasError = false, fiatCurrency, withMaxButton = false, + tokenAmountLabel = "Amount", ...inputProps }: TokenBalanceInputProps) { const valueRef = useRef(amount) @@ -152,7 +154,7 @@ export default function TokenBalanceInput({ Amount - Wallet balance + {tokenAmountLabel} Date: Tue, 20 Aug 2024 14:00:53 +0200 Subject: [PATCH 26/28] Move error reset to `setAmount` function --- .../shared/Form/FormTokenBalanceInput.tsx | 20 ++++--------------- .../shared/TokenBalanceInput/index.tsx | 3 +-- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx b/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx index 1fc0946d1..fc922ea8c 100644 --- a/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx +++ b/dapp/src/components/shared/Form/FormTokenBalanceInput.tsx @@ -1,4 +1,4 @@ -import React, { ChangeEventHandler, useCallback, useEffect } from "react" +import React, { useCallback, useEffect } from "react" import { useField } from "formik" import { logPromiseFailure } from "#/utils" import TokenBalanceInput, { TokenBalanceInputProps } from "../TokenBalanceInput" @@ -18,9 +18,11 @@ export function FormTokenBalanceInput({ const setAmount = useCallback( (value?: bigint) => { if (!meta.touched) logPromiseFailure(helpers.setTouched(true)) + if (meta.error) helpers.setError(undefined) + logPromiseFailure(helpers.setValue(value)) }, - [helpers, meta.touched], + [helpers, meta.touched, meta.error], ) useEffect(() => { @@ -29,19 +31,6 @@ export function FormTokenBalanceInput({ } }, [defaultValue, setAmount]) - const handleChange = useCallback( - (event) => { - field?.onChange(event) - - if (!meta.error) { - return - } - - helpers.setError(undefined) - }, - [field, meta, helpers], - ) - return ( ) } diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index d8123eb5b..1d1a2fdf7 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -138,8 +138,7 @@ export default function TokenBalanceInput({ valueRef.current = value ? userAmountToBigInt(value, decimals) : undefined } - const handleChange: ChangeEventHandler = (event) => { - inputProps.onChange?.(event) + const handleChange: ChangeEventHandler = () => { setAmount(valueRef?.current) } From 25ef87cd00b1cf75228550714e8a2bc94ff7c21a Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 20 Aug 2024 16:20:44 +0200 Subject: [PATCH 27/28] Implement input max length --- .../shared/NumberFormatInput/index.tsx | 29 +++++++++++++++++-- .../shared/TokenBalanceInput/index.tsx | 2 ++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/dapp/src/components/shared/NumberFormatInput/index.tsx b/dapp/src/components/shared/NumberFormatInput/index.tsx index 37b147efa..71edadd72 100644 --- a/dapp/src/components/shared/NumberFormatInput/index.tsx +++ b/dapp/src/components/shared/NumberFormatInput/index.tsx @@ -1,5 +1,9 @@ import React from "react" -import { NumericFormat, NumericFormatProps } from "react-number-format" +import { + NumberFormatValues, + NumericFormat, + NumericFormatProps, +} from "react-number-format" import { InputProps, chakra, useMultiStyleConfig } from "@chakra-ui/react" const ChakraNumericFormat = chakra(NumericFormat) @@ -12,6 +16,8 @@ export type NumberFormatInputValues = { export type NumberFormatInputProps = { onValueChange: (values: NumberFormatInputValues) => void + maxIntegerLength?: number + maxDecimalLength?: number } & InputProps & Pick @@ -29,7 +35,25 @@ const NumberFormatInput = React.forwardRef< >((props, ref) => { const { field: css } = useMultiStyleConfig("Input", props) - const { decimalScale, isDisabled, isInvalid, ...restProps } = props + const { + decimalScale, + isDisabled, + isInvalid, + maxIntegerLength, + maxDecimalLength, + ...restProps + } = props + + const handleLengthValidation = (values: NumberFormatValues) => { + const { value } = values + if (!value || !maxIntegerLength || !maxDecimalLength) return true + + const [integer, decimal] = value.split(".") + const isValidIntegerLength = integer && integer.length <= maxIntegerLength + const isValidDecimalLength = decimal && decimal.length <= maxDecimalLength + + return decimal ? isValidDecimalLength : isValidIntegerLength + } return ( ) diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index 1d1a2fdf7..15bf43ddd 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -181,6 +181,8 @@ export default function TokenBalanceInput({ onChange={handleChange} decimalScale={decimals} allowNegative={false} + maxIntegerLength={10} + maxDecimalLength={decimals} /> {withMaxButton && ( From 880bbd9fa4e49205ba3a4d9bf8505eafbed03d2e Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 21 Aug 2024 10:51:43 +0200 Subject: [PATCH 28/28] Refactor length validation --- .../shared/NumberFormatInput/index.tsx | 29 ++++++++----------- .../shared/TokenBalanceInput/index.tsx | 3 +- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/dapp/src/components/shared/NumberFormatInput/index.tsx b/dapp/src/components/shared/NumberFormatInput/index.tsx index 71edadd72..7d0daac37 100644 --- a/dapp/src/components/shared/NumberFormatInput/index.tsx +++ b/dapp/src/components/shared/NumberFormatInput/index.tsx @@ -16,8 +16,7 @@ export type NumberFormatInputValues = { export type NumberFormatInputProps = { onValueChange: (values: NumberFormatInputValues) => void - maxIntegerLength?: number - maxDecimalLength?: number + integerScale?: number } & InputProps & Pick @@ -35,24 +34,20 @@ const NumberFormatInput = React.forwardRef< >((props, ref) => { const { field: css } = useMultiStyleConfig("Input", props) - const { - decimalScale, - isDisabled, - isInvalid, - maxIntegerLength, - maxDecimalLength, - ...restProps - } = props + const { decimalScale, isDisabled, isInvalid, integerScale, ...restProps } = + props const handleLengthValidation = (values: NumberFormatValues) => { - const { value } = values - if (!value || !maxIntegerLength || !maxDecimalLength) return true + const { value, floatValue } = values + if ( + floatValue === undefined || + value === undefined || + integerScale === undefined + ) + return true - const [integer, decimal] = value.split(".") - const isValidIntegerLength = integer && integer.length <= maxIntegerLength - const isValidDecimalLength = decimal && decimal.length <= maxDecimalLength - - return decimal ? isValidDecimalLength : isValidIntegerLength + const [integerPart] = value.split(".") + return integerPart.length <= integerScale } return ( diff --git a/dapp/src/components/shared/TokenBalanceInput/index.tsx b/dapp/src/components/shared/TokenBalanceInput/index.tsx index 15bf43ddd..456882fb2 100644 --- a/dapp/src/components/shared/TokenBalanceInput/index.tsx +++ b/dapp/src/components/shared/TokenBalanceInput/index.tsx @@ -181,8 +181,7 @@ export default function TokenBalanceInput({ onChange={handleChange} decimalScale={decimals} allowNegative={false} - maxIntegerLength={10} - maxDecimalLength={decimals} + integerScale={10} /> {withMaxButton && (