diff --git a/packages/neuron-ui/src/components/ApproveMultisigTxDialog/index.tsx b/packages/neuron-ui/src/components/ApproveMultisigTxDialog/index.tsx
index 71adb6570c..0aa3544869 100644
--- a/packages/neuron-ui/src/components/ApproveMultisigTxDialog/index.tsx
+++ b/packages/neuron-ui/src/components/ApproveMultisigTxDialog/index.tsx
@@ -13,28 +13,25 @@ import getMultisigSignStatus from 'utils/getMultisigSignStatus'
import { useBroadcast, useExport, useSignAndBroadcast, useSignAndExport, useTabView } from './hooks'
import styles from './approveMultisigTx.module.scss'
-interface CellProps {
- lock: CKBComponents.Script
- type?: CKBComponents.Script
- data?: string
- capacity: string
- lockHash: string
-}
-
-const Cell = React.memo(({ cell, isMainnet }: { cell: CellProps; isMainnet: boolean }) => {
- const address = useMemo(() => ckbCore.utils.scriptToAddress(cell.lock, isMainnet), [cell, isMainnet])
- return (
-
-
- {address.slice(0, 6)}...{address.slice(-6)}
-
Type
-
Data
-
+const Cell = React.memo(
+ ({ cell, isMainnet }: { cell: State.DetailedInput | State.DetailedOutput; isMainnet: boolean }) => {
+ const address = useMemo(
+ () => (cell.lock ? ckbCore.utils.scriptToAddress(cell.lock, isMainnet) : ''),
+ [cell, isMainnet]
+ )
+ return (
+
+
+ {address.slice(0, 6)}...{address.slice(-6)}
+ Type
+ Data
+
+
+
{`${shannonToCKBFormatter(cell.capacity ?? '0')} CKB`}
-
{`${shannonToCKBFormatter(cell.capacity)} CKB`}
-
- )
-})
+ )
+ }
+)
const ApproveMultisigTxDialog = ({
multisigConfig,
closeDialog,
@@ -152,12 +149,12 @@ const ApproveMultisigTxDialog = ({
Inputs
- {offlineSignJson.transaction?.inputs?.map((input: CellProps) => (
+ {offlineSignJson.transaction?.inputs?.map(input => (
|
))}
Outputs
- {offlineSignJson.transaction?.outputs?.map((output: CellProps) => (
+ {offlineSignJson.transaction?.outputs?.map(output => (
|
))}
diff --git a/packages/neuron-ui/src/components/DepositDialog/depositDialog.module.scss b/packages/neuron-ui/src/components/DepositDialog/depositDialog.module.scss
index 46c18e26ac..9ffadbba2d 100644
--- a/packages/neuron-ui/src/components/DepositDialog/depositDialog.module.scss
+++ b/packages/neuron-ui/src/components/DepositDialog/depositDialog.module.scss
@@ -44,13 +44,19 @@
font-size: 14px;
line-height: 20px;
color: var(--main-text-color);
+
+ & > div {
+ &:nth-last-child(1) {
+ width: 230px;
+ text-align: right;
+ }
+ }
}
.notice {
$lineHeight: 24px;
display: flex;
box-sizing: border-box;
- max-width: 648px;
margin-top: 22px;
border: 1px solid rgba(252, 136, 0, 0.2);
padding: 7px 54px;
@@ -65,7 +71,7 @@
& > svg {
$size: 14px;
flex-shrink: 0;
- margin-top: ($lineHeight - $size) / 2;
+ margin: calc(($lineHeight - $size) / 2) 4px 0 0;
width: $size;
height: $size;
diff --git a/packages/neuron-ui/src/components/DepositDialog/hooks.ts b/packages/neuron-ui/src/components/DepositDialog/hooks.ts
new file mode 100644
index 0000000000..27d6b35794
--- /dev/null
+++ b/packages/neuron-ui/src/components/DepositDialog/hooks.ts
@@ -0,0 +1,270 @@
+import { isErrorWithI18n } from 'exceptions'
+import { TFunction } from 'i18next'
+import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import {
+ generateDaoDepositAllTx as generateDaoDepositAllTxAPI,
+ generateDaoDepositTx as generateDaoDepositTxAPI,
+} from 'services/remote'
+import { AppActions, useDispatch } from 'states'
+import {
+ CKBToShannonFormatter,
+ ErrorCode,
+ isSuccessResponse,
+ padFractionDigitsIfDecimal,
+ shannonToCKBFormatter,
+ useClearGeneratedTx,
+ validateAmount,
+} from 'utils'
+import { MAX_DECIMAL_DIGITS, MIN_DEPOSIT_AMOUNT, SHANNON_CKB_RATIO } from 'utils/const'
+
+const PERCENT_100 = 100
+
+function checkDepositValue(depositValue: string, t: TFunction): string | undefined {
+ try {
+ validateAmount(depositValue)
+ } catch (err) {
+ if (isErrorWithI18n(err)) {
+ return t(`messages.codes.${err.code}`, {
+ fieldName: 'deposit',
+ fieldValue: depositValue,
+ length: MAX_DECIMAL_DIGITS,
+ })
+ }
+ return undefined
+ }
+ if (BigInt(CKBToShannonFormatter(depositValue)) < BigInt(MIN_DEPOSIT_AMOUNT * SHANNON_CKB_RATIO)) {
+ return t('nervos-dao.minimal-fee-required', { minimal: MIN_DEPOSIT_AMOUNT })
+ }
+ return undefined
+}
+
+function generateDaoDepositTx({
+ walletID,
+ capacity,
+ suggestFeeRate,
+ t,
+}: {
+ walletID: string
+ capacity: string
+ suggestFeeRate: number
+ t: TFunction
+}): Promise
{
+ return generateDaoDepositTxAPI({
+ feeRate: `${suggestFeeRate}`,
+ capacity,
+ walletID,
+ }).then(res => {
+ if (isSuccessResponse(res)) {
+ return res.result
+ }
+ if (res.status === 0) {
+ throw new Error(`${typeof res.message === 'string' ? res.message : res.message.content}`)
+ } else if (res.status === ErrorCode.CapacityNotEnoughForChange) {
+ throw new Error(t(`messages.codes.106`))
+ } else {
+ throw new Error(t(`messages.codes.${res.status}`))
+ }
+ })
+}
+
+function generateDaoDepositAllTx({
+ suggestFeeRate,
+ isBalanceReserved,
+ walletID,
+}: {
+ suggestFeeRate: number
+ isBalanceReserved: boolean
+ walletID: string
+}): Promise {
+ return generateDaoDepositAllTxAPI({
+ walletID,
+ feeRate: `${suggestFeeRate}`,
+ isBalanceReserved,
+ }).then(res => {
+ if (isSuccessResponse(res)) {
+ return res.result
+ }
+ throw new Error(`${typeof res.message === 'string' ? res.message : res.message.content}`)
+ })
+}
+
+export const useGenerateDaoDepositTx = ({
+ walletID,
+ isBalanceReserved,
+ depositValue,
+ suggestFeeRate,
+ showDepositDialog,
+ slidePercent,
+}: {
+ walletID: string
+ isBalanceReserved: boolean
+ depositValue: string
+ suggestFeeRate: number
+ showDepositDialog: boolean
+ slidePercent: number
+}) => {
+ const timer = useRef>()
+ const [errorMessage, setErrorMessage] = useState('')
+ const [maxDepositValue, setMaxDepositValue] = useState()
+ const [t] = useTranslation()
+ const dispatch = useDispatch()
+ const clearGeneratedTx = useClearGeneratedTx()
+ const isDepositAll = useMemo(() => slidePercent === PERCENT_100, [slidePercent])
+ useEffect(() => {
+ clearTimeout(timer.current)
+ if (!showDepositDialog) {
+ return
+ }
+ timer.current = setTimeout(() => {
+ setErrorMessage('')
+ const errorDepositValue = checkDepositValue(depositValue, t)
+ if (errorDepositValue) {
+ clearGeneratedTx()
+ setErrorMessage(errorDepositValue)
+ return
+ }
+
+ const generateDaoDepositResult: Promise = isDepositAll
+ ? generateDaoDepositAllTx({ walletID, isBalanceReserved, suggestFeeRate })
+ : generateDaoDepositTx({ walletID, capacity: CKBToShannonFormatter(depositValue), suggestFeeRate, t })
+ generateDaoDepositResult
+ .then(res => {
+ dispatch({
+ type: AppActions.UpdateGeneratedTx,
+ payload: res,
+ })
+ if (isDepositAll) {
+ setMaxDepositValue(shannonToCKBFormatter(res?.outputs[0]?.capacity ?? '0', false, ''))
+ if (!isBalanceReserved) {
+ setErrorMessage(t('messages.remain-ckb-for-withdraw'))
+ }
+ }
+ })
+ .catch((err: unknown) => {
+ clearGeneratedTx()
+ setErrorMessage(err instanceof Error ? err.message : '')
+ })
+ })
+ }, [
+ clearGeneratedTx,
+ dispatch,
+ walletID,
+ t,
+ setErrorMessage,
+ isBalanceReserved,
+ depositValue,
+ suggestFeeRate,
+ showDepositDialog,
+ isDepositAll,
+ ])
+ return {
+ errorMessage,
+ maxDepositValue: isDepositAll ? maxDepositValue ?? depositValue : null,
+ }
+}
+
+function calculatePercent(amount: string, total: string) {
+ if (!total || total === '0') return 0
+ return +((BigInt(PERCENT_100) * BigInt(amount)) / BigInt(total)).toString()
+}
+
+export const useDepositValue = (balance: string) => {
+ const [depositValue, setDepositValue] = useState(`${MIN_DEPOSIT_AMOUNT}`)
+ const [slidePercent, setSlidePercent] = useState(
+ calculatePercent(CKBToShannonFormatter(`${MIN_DEPOSIT_AMOUNT}`), balance)
+ )
+ const onSliderChange = useCallback(
+ (percent: number) => {
+ setSlidePercent(percent)
+ const amount = shannonToCKBFormatter(
+ ((BigInt(percent) * BigInt(balance)) / BigInt(PERCENT_100)).toString(),
+ false,
+ ''
+ )
+ setDepositValue(padFractionDigitsIfDecimal(amount, 8))
+ },
+ [balance]
+ )
+ const onChangeDepositValue = useCallback(
+ (e: React.SyntheticEvent) => {
+ const { value } = e.currentTarget
+ const amount = value.replace(/,/g, '')
+ if (Number.isNaN(+amount) || /[^\d.]/.test(amount) || +amount < 0) {
+ return
+ }
+ setDepositValue(amount)
+ try {
+ validateAmount(amount)
+ const percent = calculatePercent(CKBToShannonFormatter(amount), balance)
+ setSlidePercent(percent >= PERCENT_100 ? 100 : percent)
+ } catch (error) {
+ // here we can ignore the error, it used to verify amount and set slide percent
+ }
+ },
+ [setDepositValue, balance]
+ )
+ const resetDepositValue = useCallback(() => {
+ setDepositValue(`${MIN_DEPOSIT_AMOUNT}`)
+ setSlidePercent(calculatePercent(CKBToShannonFormatter(`${MIN_DEPOSIT_AMOUNT}`), balance))
+ }, [balance])
+ return {
+ onChangeDepositValue,
+ setDepositValue,
+ depositValue,
+ slidePercent,
+ onSliderChange,
+ resetDepositValue,
+ }
+}
+
+export const useBalanceReserved = () => {
+ const [isBalanceReserved, setIsBalanceReserved] = useState(true)
+ const onIsBalanceReservedChange = (e: React.SyntheticEvent) => {
+ setIsBalanceReserved(!e.currentTarget.checked)
+ }
+ return {
+ isBalanceReserved,
+ onIsBalanceReservedChange,
+ setIsBalanceReserved,
+ }
+}
+
+export const useOnDepositDialogSubmit = ({
+ onCloseDepositDialog,
+ walletID,
+}: {
+ onCloseDepositDialog: () => void
+ walletID: string
+}) => {
+ const dispatch = useDispatch()
+ return useCallback(() => {
+ dispatch({
+ type: AppActions.RequestPassword,
+ payload: {
+ walletID,
+ actionType: 'send',
+ },
+ })
+ onCloseDepositDialog()
+ }, [dispatch, walletID, onCloseDepositDialog])
+}
+
+export const useOnDepositDialogCancel = ({
+ onCloseDepositDialog,
+ resetDepositValue,
+ setIsBalanceReserved,
+}: {
+ onCloseDepositDialog: () => void
+ resetDepositValue: () => void
+ setIsBalanceReserved: Dispatch>
+}) => {
+ const dispatch = useDispatch()
+ const clearGeneratedTx = useClearGeneratedTx()
+ return useCallback(() => {
+ onCloseDepositDialog()
+ resetDepositValue()
+ setIsBalanceReserved(true)
+ clearGeneratedTx()
+ }, [dispatch, onCloseDepositDialog, resetDepositValue, clearGeneratedTx])
+}
diff --git a/packages/neuron-ui/src/components/DepositDialog/index.tsx b/packages/neuron-ui/src/components/DepositDialog/index.tsx
index 849555ff4d..6737a2f42c 100644
--- a/packages/neuron-ui/src/components/DepositDialog/index.tsx
+++ b/packages/neuron-ui/src/components/DepositDialog/index.tsx
@@ -1,84 +1,80 @@
-import React, { useEffect, useMemo } from 'react'
+import React from 'react'
import { Slider } from 'office-ui-fabric-react'
-import { useTranslation, Trans } from 'react-i18next'
+import { Trans, useTranslation } from 'react-i18next'
import TextField from 'widgets/TextField'
import Spinner, { SpinnerSize } from 'widgets/Spinner'
import { openExternal } from 'services/remote'
-import { CONSTANTS, localNumberFormatter, shannonToCKBFormatter } from 'utils'
+import { localNumberFormatter, shannonToCKBFormatter } from 'utils'
import { Attention, Success } from 'widgets/Icons/icon'
import Dialog from 'widgets/Dialog'
import styles from './depositDialog.module.scss'
-
-const { SHANNON_CKB_RATIO } = CONSTANTS
+import {
+ useBalanceReserved,
+ useDepositValue,
+ useGenerateDaoDepositTx,
+ useOnDepositDialogCancel,
+ useOnDepositDialogSubmit,
+} from './hooks'
const NERVOS_DAO_RFC_URL =
'https://www.github.com/nervosnetwork/rfcs/blob/master/rfcs/0023-dao-deposit-withdraw/0023-dao-deposit-withdraw.md'
interface DepositDialogProps {
+ balance: string
show: boolean
- value: any
fee: string
- onOpen: () => void
- onDismiss: () => void
- onChange: (e: React.SyntheticEvent) => void
- onSubmit: () => void
- onSlide: (value: number) => void
- maxDepositAmount: bigint
+ onCloseDepositDialog: () => void
isDepositing: boolean
- errorMessage: string
isTxGenerated: boolean
- isBalanceReserved: boolean
- onIsBalanceReservedChange: (e: React.SyntheticEvent) => void
+ suggestFeeRate: number
+ walletID: string
}
+const RfcLink = React.memo(() => (
+
diff --git a/packages/neuron-ui/src/components/HardwareSign/index.tsx b/packages/neuron-ui/src/components/HardwareSign/index.tsx
index d4351dc020..92c2ec715d 100644
--- a/packages/neuron-ui/src/components/HardwareSign/index.tsx
+++ b/packages/neuron-ui/src/components/HardwareSign/index.tsx
@@ -460,7 +460,7 @@ const HardwareSign = ({
- {wallet.isHD ?