Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Manage Nervos DAO with multisig address #3298

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 69 additions & 23 deletions packages/neuron-ui/src/components/DepositDialog/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import { TFunction } from 'i18next'
import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
MultisigConfig,
generateDaoDepositAllTx as generateDaoDepositAllTxAPI,
generateDaoDepositTx as generateDaoDepositTxAPI,
generateMultisigDaoDepositTx as generateMultisigDaoDepositTxAPI,
generateMultisigDaoDepositAllTx as generateMultisigDaoDepositAllTxAPI,
} from 'services/remote'
import { AppActions, useDispatch } from 'states'
import {
Expand All @@ -17,6 +20,7 @@ import {
useClearGeneratedTx,
validateAmount,
} from 'utils'
import getMultisigSignStatus from 'utils/getMultisigSignStatus'
import { MAX_DECIMAL_DIGITS, MIN_DEPOSIT_AMOUNT, SHANNON_CKB_RATIO } from 'utils/const'

const PERCENT_100 = 100
Expand Down Expand Up @@ -45,17 +49,26 @@ function generateDaoDepositTx({
capacity,
suggestFeeRate,
t,
multisigConfig,
}: {
walletID: string
capacity: string
suggestFeeRate: number
t: TFunction
multisigConfig?: MultisigConfig
}): Promise<State.GeneratedTx | null> {
return generateDaoDepositTxAPI({
feeRate: `${suggestFeeRate}`,
capacity,
walletID,
}).then(res => {
const generateCall = multisigConfig
? generateMultisigDaoDepositTxAPI({
feeRate: `${suggestFeeRate}`,
capacity,
multisigConfig,
})
: generateDaoDepositTxAPI({
walletID,
feeRate: `${suggestFeeRate}`,
capacity,
})
return generateCall.then(res => {
if (isSuccessResponse(res)) {
return res.result
}
Expand All @@ -73,16 +86,25 @@ function generateDaoDepositAllTx({
suggestFeeRate,
isBalanceReserved,
walletID,
multisigConfig,
}: {
suggestFeeRate: number
isBalanceReserved: boolean
walletID: string
multisigConfig?: MultisigConfig
}): Promise<State.GeneratedTx | null> {
return generateDaoDepositAllTxAPI({
walletID,
feeRate: `${suggestFeeRate}`,
isBalanceReserved,
}).then(res => {
const generateAllCall = multisigConfig
? generateMultisigDaoDepositAllTxAPI({
feeRate: `${suggestFeeRate}`,
isBalanceReserved,
multisigConfig,
})
: generateDaoDepositAllTxAPI({
walletID,
feeRate: `${suggestFeeRate}`,
isBalanceReserved,
})
return generateAllCall.then(res => {
if (isSuccessResponse(res)) {
return res.result
}
Expand All @@ -97,13 +119,15 @@ export const useGenerateDaoDepositTx = ({
suggestFeeRate,
showDepositDialog,
slidePercent,
multisigConfig,
}: {
walletID: string
isBalanceReserved: boolean
depositValue: string
suggestFeeRate: number
showDepositDialog: boolean
slidePercent: number
multisigConfig?: MultisigConfig
}) => {
const timer = useRef<ReturnType<typeof setTimeout>>()
const [errorMessage, setErrorMessage] = useState('')
Expand All @@ -127,8 +151,14 @@ export const useGenerateDaoDepositTx = ({
}

const generateDaoDepositResult: Promise<State.GeneratedTx | null> = isDepositAll
? generateDaoDepositAllTx({ walletID, isBalanceReserved, suggestFeeRate })
: generateDaoDepositTx({ walletID, capacity: CKBToShannonFormatter(depositValue), suggestFeeRate, t })
? generateDaoDepositAllTx({ walletID, isBalanceReserved, suggestFeeRate, multisigConfig })
: generateDaoDepositTx({
walletID,
capacity: CKBToShannonFormatter(depositValue),
suggestFeeRate,
t,
multisigConfig,
})
generateDaoDepositResult
.then(res => {
dispatch({
Expand Down Expand Up @@ -239,22 +269,38 @@ export const useBalanceReserved = () => {

export const useOnDepositDialogSubmit = ({
onDepositSuccess,
walletID,
wallet,
multisigConfig,
}: {
onDepositSuccess: () => void
walletID: string
wallet: State.Wallet
multisigConfig?: MultisigConfig
}) => {
const dispatch = useDispatch()
return useCallback(() => {
dispatch({
type: AppActions.RequestPassword,
payload: {
walletID,
actionType: 'send',
onSuccess: onDepositSuccess,
},
})
}, [dispatch, walletID, onDepositSuccess])
if (multisigConfig) {
const { canBroadcastAfterSign } = getMultisigSignStatus({ multisigConfig, addresses: wallet.addresses })
dispatch({
type: AppActions.RequestPassword,
payload: {
walletID: wallet.id,
actionType: canBroadcastAfterSign ? 'send-from-multisig-need-one' : 'send-from-multisig',
multisigConfig,
onSuccess: onDepositSuccess,
title: 'password-request.verify-password',
},
})
} else {
dispatch({
type: AppActions.RequestPassword,
payload: {
walletID: wallet.id,
actionType: 'send',
onSuccess: onDepositSuccess,
},
})
}
}, [dispatch, wallet.id, onDepositSuccess, multisigConfig])
}

export const useOnDepositDialogCancel = ({
Expand Down
25 changes: 18 additions & 7 deletions packages/neuron-ui/src/components/DepositDialog/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { useCallback } from 'react'
import React, { useCallback, useMemo } from 'react'
import { Slider } from 'office-ui-fabric-react'
import { Trans, useTranslation } from 'react-i18next'
import TextField from 'widgets/TextField'
import Spinner, { SpinnerSize } from 'widgets/Spinner'
import { openExternal } from 'services/remote'
import { openExternal, MultisigConfig } from 'services/remote'
import { localNumberFormatter, shannonToCKBFormatter } from 'utils'
import getMultisigSignStatus from 'utils/getMultisigSignStatus'
import { Attention, Success } from 'widgets/Icons/icon'
import Dialog from 'widgets/Dialog'
import Tooltip from 'widgets/Tooltip'
Expand All @@ -30,9 +31,10 @@ interface DepositDialogProps {
isDepositing: boolean
isTxGenerated: boolean
suggestFeeRate: number
walletID: string
wallet: State.Wallet
globalAPC: number
onDepositSuccess: () => void
multisigConfig?: MultisigConfig
}

const RfcLink = React.memo(() => (
Expand All @@ -50,7 +52,7 @@ const RfcLink = React.memo(() => (
))

const DepositDialog = ({
walletID,
wallet,
balance,
show,
fee,
Expand All @@ -60,6 +62,7 @@ const DepositDialog = ({
suggestFeeRate,
globalAPC,
onDepositSuccess,
multisigConfig,
}: DepositDialogProps) => {
const [t, { language }] = useTranslation()
const disabled = !isTxGenerated
Expand All @@ -69,14 +72,22 @@ const DepositDialog = ({
show
)
const { errorMessage, maxDepositValue } = useGenerateDaoDepositTx({
walletID,
walletID: wallet.id,
isBalanceReserved,
depositValue,
suggestFeeRate,
showDepositDialog: show,
slidePercent,
multisigConfig,
})
const onConfirm = useOnDepositDialogSubmit({ onDepositSuccess, walletID })

const canSign = useMemo(() => {
if (!multisigConfig) return true
const multisigSignStatus = getMultisigSignStatus({ multisigConfig, addresses: wallet.addresses })
return multisigSignStatus.canSign
}, [multisigConfig, wallet.addresses])

const onConfirm = useOnDepositDialogSubmit({ onDepositSuccess, wallet, multisigConfig })
const onCancel = useOnDepositDialogCancel({ onCloseDepositDialog, resetDepositValue, setIsBalanceReserved })
const onSubmit = useCallback(
(e: React.FormEvent) => {
Expand Down Expand Up @@ -105,7 +116,7 @@ const DepositDialog = ({
onCancel={onCancel}
onConfirm={onConfirm}
cancelText={t('nervos-dao.cancel')}
confirmText={t('nervos-dao.proceed')}
confirmText={canSign ? t('nervos-dao.proceed') : t('nervos-dao-detail.export')}
className={styles.container}
>
{isDepositing ? (
Expand Down
46 changes: 46 additions & 0 deletions packages/neuron-ui/src/components/MultisigAddress/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,12 +298,58 @@ const useApproveAction = () => {
}
}

const useDaoDepositAction = () => {
const [isDialogOpen, setIsDialogOpen] = useState(false)
const [depositFromMultisig, setDepositFromMultisig] = useState<MultisigConfig | undefined>()
const onOpenDialog = useCallback(
(option: MultisigConfig) => {
setIsDialogOpen(true)
setDepositFromMultisig(option)
},
[setIsDialogOpen, setDepositFromMultisig]
)
const closeDialog = useCallback(() => {
setIsDialogOpen(false)
}, [setIsDialogOpen])

return {
action: onOpenDialog,
closeDialog,
depositFromMultisig,
isDialogOpen,
}
}

const useDaoWithdrawAction = () => {
const [isDialogOpen, setIsDialogOpen] = useState(false)
const [withdrawFromMultisig, setWithdrawFromMultisig] = useState<MultisigConfig | undefined>()
const onOpenDialog = useCallback(
(option: MultisigConfig) => {
setIsDialogOpen(true)
setWithdrawFromMultisig(option)
},
[setIsDialogOpen, setWithdrawFromMultisig]
)
const closeDialog = useCallback(() => {
setIsDialogOpen(false)
}, [setIsDialogOpen])

return {
action: onOpenDialog,
closeDialog,
withdrawFromMultisig,
isDialogOpen,
}
}

export const useActions = ({ deleteConfigById }: { deleteConfigById: (id: number) => void }) => {
return {
deleteAction: useDeleteAction(deleteConfigById),
infoAction: useInfoAction(),
sendAction: useSendAction(),
approveAction: useApproveAction(),
daoDepositAction: useDaoDepositAction(),
daoWithdrawAction: useDaoWithdrawAction(),
}
}

Expand Down
Loading
Loading