From adbc3427d46300052736376192a9fa093c138448 Mon Sep 17 00:00:00 2001 From: Jun Ma Date: Sat, 4 Mar 2023 18:25:31 +0800 Subject: [PATCH] Support dynamic fee_rate based on fee_rate_statistics --- packages/neuron-ui/package.json | 4 +- .../src/components/NFTSend/index.tsx | 8 +- .../src/components/NervosDAO/hooks.ts | 25 ++- .../src/components/NervosDAO/index.tsx | 33 +++- .../src/components/PricePanel/index.tsx | 175 ++++++++++++++++++ .../PricePanel/pricePanel.module.scss | 93 ++++++++++ .../src/components/SUDTSend/index.tsx | 3 +- .../neuron-ui/src/components/Send/hooks.ts | 50 ++++- .../src/components/SendMetaInfo/index.tsx | 4 +- .../src/components/SpecialAssetList/index.tsx | 6 +- .../components/TransactionFeePanel/index.tsx | 69 +------ packages/neuron-ui/src/locales/en.json | 16 ++ packages/neuron-ui/src/locales/zh-tw.json | 16 ++ packages/neuron-ui/src/locales/zh.json | 16 ++ packages/neuron-ui/src/services/chain.ts | 3 +- .../src/states/stateProvider/reducer.ts | 6 + .../src/stories/SendMetaInfo.stories.tsx | 2 +- packages/neuron-ui/src/styles/color.scss | 4 + packages/neuron-ui/src/types/App/index.d.ts | 3 + packages/neuron-ui/src/utils/calculateFee.ts | 2 +- .../src/utils/hooks/createSUDTAccount.ts | 14 +- .../hooks/useGetCountDownAndFeeRateStats.ts | 62 +++++++ .../index.module.scss | 94 ++++++++++ .../DropdownWithCustomRender/index.tsx | 171 +++++++++++++++++ .../src/widgets/Icons/LineDownArrow.svg | 4 + .../neuron-ui/src/widgets/Icons/Transfer.svg | 5 + packages/neuron-ui/src/widgets/Icons/icon.tsx | 4 + .../widgets/TextField/textField.module.scss | 4 +- packages/neuron-wallet/package.json | 4 +- yarn.lock | 60 +++--- 30 files changed, 830 insertions(+), 130 deletions(-) create mode 100644 packages/neuron-ui/src/components/PricePanel/index.tsx create mode 100644 packages/neuron-ui/src/components/PricePanel/pricePanel.module.scss create mode 100644 packages/neuron-ui/src/utils/hooks/useGetCountDownAndFeeRateStats.ts create mode 100644 packages/neuron-ui/src/widgets/DropdownWithCustomRender/index.module.scss create mode 100644 packages/neuron-ui/src/widgets/DropdownWithCustomRender/index.tsx create mode 100644 packages/neuron-ui/src/widgets/Icons/LineDownArrow.svg create mode 100644 packages/neuron-ui/src/widgets/Icons/Transfer.svg diff --git a/packages/neuron-ui/package.json b/packages/neuron-ui/package.json index e38ead95af..43ed8b4eef 100644 --- a/packages/neuron-ui/package.json +++ b/packages/neuron-ui/package.json @@ -40,8 +40,8 @@ "last 2 chrome versions" ], "dependencies": { - "@nervosnetwork/ckb-sdk-core": "0.103.1", - "@nervosnetwork/ckb-sdk-utils": "0.103.1", + "@nervosnetwork/ckb-sdk-core": "0.107.0", + "@nervosnetwork/ckb-sdk-utils": "0.107.0", "@uifabric/experiments": "7.45.14", "@uifabric/styling": "7.25.1", "canvg": "2.0.0", diff --git a/packages/neuron-ui/src/components/NFTSend/index.tsx b/packages/neuron-ui/src/components/NFTSend/index.tsx index 8baef82697..8202c7b11a 100644 --- a/packages/neuron-ui/src/components/NFTSend/index.tsx +++ b/packages/neuron-ui/src/components/NFTSend/index.tsx @@ -3,10 +3,10 @@ import { useTranslation } from 'react-i18next' import { useLocation, useParams } from 'react-router-dom' import { useState as useGlobalState, useDispatch, AppActions } from 'states' import { isMainnet as isMainnetUtil, isSuccessResponse, validateAddress } from 'utils' +import useGetCountDownAndFeeRateStats from 'utils/hooks/useGetCountDownAndFeeRateStats' import TextField from 'widgets/TextField' import { generateNFTSendTransaction } from 'services/remote' import Button from 'widgets/Button' -import { MEDIUM_FEE_RATE } from 'utils/const' import { ReactComponent as Attention } from 'widgets/Icons/Attention.svg' import { isErrorWithI18n } from 'exceptions' import styles from './NFTSend.module.scss' @@ -48,8 +48,8 @@ const NFTSend = () => { } = useGlobalState() const [t] = useTranslation() const globalDispatch = useDispatch() + const { suggestFeeRate } = useGetCountDownAndFeeRateStats() const timerRef = useRef(null) - const [sendState, dispatch] = useReducer(reducer, initState) const [remoteError, setRemoteError] = useState('') @@ -122,7 +122,7 @@ const NFTSend = () => { receiveAddress: sendState.address, outPoint, description: sendState.description, - feeRate: `${MEDIUM_FEE_RATE}`, + feeRate: `${suggestFeeRate}`, } generateNFTSendTransaction(params) @@ -141,7 +141,7 @@ const NFTSend = () => { }) }, TIMER_DELAY) return clearTimer - }, [isSubmittable, globalDispatch, sendState, walletId, outPoint]) + }, [isSubmittable, globalDispatch, sendState, walletId, outPoint, suggestFeeRate]) return (
diff --git a/packages/neuron-ui/src/components/NervosDAO/hooks.ts b/packages/neuron-ui/src/components/NervosDAO/hooks.ts index 58a66ea222..3a70110bad 100644 --- a/packages/neuron-ui/src/components/NervosDAO/hooks.ts +++ b/packages/neuron-ui/src/components/NervosDAO/hooks.ts @@ -44,17 +44,19 @@ export const useUpdateMaxDeposit = ({ setMaxDepositTx, setMaxDepositErrorMessage, isBalanceReserved, + suggestFeeRate, }: { wallet: State.Wallet setMaxDepositAmount: React.Dispatch> setMaxDepositTx: React.Dispatch> setMaxDepositErrorMessage: React.Dispatch> isBalanceReserved: boolean + suggestFeeRate: number | string }) => { useEffect(() => { generateDaoDepositAllTx({ walletID: wallet.id, - feeRate: `${MEDIUM_FEE_RATE}`, + feeRate: `${suggestFeeRate}`, isBalanceReserved, }) .then((res: any) => { @@ -72,7 +74,15 @@ export const useUpdateMaxDeposit = ({ setMaxDepositTx(undefined) setMaxDepositErrorMessage(err.message) }) - }, [wallet.id, wallet.balance, setMaxDepositAmount, setMaxDepositErrorMessage, setMaxDepositTx, isBalanceReserved]) + }, [ + wallet.id, + wallet.balance, + setMaxDepositAmount, + setMaxDepositErrorMessage, + setMaxDepositTx, + isBalanceReserved, + suggestFeeRate, + ]) } export const useInitData = ({ @@ -129,6 +139,7 @@ export const useUpdateDepositValue = ({ maxDepositErrorMessage, isBalanceReserved, t, + suggestFeeRate, }: { setDepositValue: React.Dispatch> setErrorMessage: React.Dispatch> @@ -140,6 +151,7 @@ export const useUpdateDepositValue = ({ maxDepositErrorMessage: string isBalanceReserved: boolean t: TFunction + suggestFeeRate: number | string }) => useCallback( (value: string) => { @@ -171,7 +183,7 @@ export const useUpdateDepositValue = ({ const capacity = CKBToShannonFormatter(amount, CapacityUnit.CKB) if (BigInt(capacity) < maxDepositAmount) { generateDaoDepositTx({ - feeRate: `${MEDIUM_FEE_RATE}`, + feeRate: `${suggestFeeRate}`, capacity, walletID, }).then(res => { @@ -213,6 +225,7 @@ export const useUpdateDepositValue = ({ setDepositValue, setErrorMessage, isBalanceReserved, + suggestFeeRate, ] ) @@ -298,19 +311,21 @@ export const useOnWithdrawDialogSubmit = ({ clearGeneratedTx, walletID, dispatch, + suggestFeeRate, }: { activeRecord: State.NervosDAORecord | null setActiveRecord: React.Dispatch clearGeneratedTx: () => void walletID: string dispatch: React.Dispatch + suggestFeeRate: number | string }) => useCallback(() => { if (activeRecord) { generateDaoWithdrawTx({ walletID, outPoint: activeRecord.outPoint, - feeRate: `${MEDIUM_FEE_RATE}`, + feeRate: `${suggestFeeRate}`, }) .then(res => { if (isSuccessResponse(res)) { @@ -342,7 +357,7 @@ export const useOnWithdrawDialogSubmit = ({ }) } setActiveRecord(null) - }, [activeRecord, setActiveRecord, clearGeneratedTx, walletID, dispatch]) + }, [activeRecord, setActiveRecord, clearGeneratedTx, walletID, dispatch, suggestFeeRate]) export const useOnActionClick = ({ records, diff --git a/packages/neuron-ui/src/components/NervosDAO/index.tsx b/packages/neuron-ui/src/components/NervosDAO/index.tsx index 635300d2d3..2fef820a42 100644 --- a/packages/neuron-ui/src/components/NervosDAO/index.tsx +++ b/packages/neuron-ui/src/components/NervosDAO/index.tsx @@ -1,9 +1,9 @@ import React, { useEffect, useState, useMemo, useCallback } from 'react' +import { useState as useGlobalState, useDispatch, AppActions } from 'states' import { useTranslation } from 'react-i18next' import { ReactComponent as TooltipIcon } from 'widgets/Icons/Tooltip.svg' import appState from 'states/init/app' -import { useState as useGlobalState, useDispatch } from 'states' import { CONSTANTS, @@ -15,9 +15,12 @@ import { getCurrentUrl, getSyncStatus, CKBToShannonFormatter, + ErrorCode, + CapacityUnit, + isSuccessResponse, } from 'utils' -import { openExternal } from 'services/remote' +import { generateDaoDepositTx, openExternal } from 'services/remote' import DepositDialog from 'components/DepositDialog' import WithdrawDialog from 'components/WithdrawDialog' @@ -26,6 +29,7 @@ import BalanceSyncIcon from 'components/BalanceSyncingIcon' import Button from 'widgets/Button' import CopyZone from 'widgets/CopyZone' +import useGetCountDownAndFeeRateStats from 'utils/hooks/useGetCountDownAndFeeRateStats' import hooks from './hooks' import styles from './nervosDAO.module.scss' @@ -55,6 +59,7 @@ const NervosDAO = () => { } = useGlobalState() const dispatch = useDispatch() const [t, { language }] = useTranslation() + const { suggestFeeRate } = useGetCountDownAndFeeRateStats() const [depositValue, setDepositValue] = useState(`${MIN_DEPOSIT_AMOUNT}`) const [showDepositDialog, setShowDepositDialog] = useState(false) const [activeRecord, setActiveRecord] = useState(null) @@ -79,6 +84,7 @@ const NervosDAO = () => { maxDepositErrorMessage, isBalanceReserved, t, + suggestFeeRate, }) const onDepositValueChange = hooks.useOnDepositValueChange({ updateDepositValue }) @@ -102,6 +108,7 @@ const NervosDAO = () => { setMaxDepositTx, setMaxDepositErrorMessage, isBalanceReserved, + suggestFeeRate, }) hooks.useInitData({ clearGeneratedTx, dispatch, updateDepositValue, wallet, setGenesisBlockTimestamp }) hooks.useUpdateGlobalAPC({ bestKnownBlockTimestamp, genesisBlockTimestamp, setGlobalAPC }) @@ -111,6 +118,7 @@ const NervosDAO = () => { clearGeneratedTx, walletID: wallet.id, dispatch, + suggestFeeRate, }) const onActionClick = hooks.useOnActionClick({ @@ -244,6 +252,27 @@ const NervosDAO = () => { } }, [maxDepositAmount, depositValue, setDepositValue]) + useEffect(() => { + generateDaoDepositTx({ + feeRate: `${suggestFeeRate}`, + capacity: CKBToShannonFormatter(depositValue, CapacityUnit.CKB), + walletID: wallet.id, + }).then(res => { + if (isSuccessResponse(res)) { + dispatch({ + type: AppActions.UpdateGeneratedTx, + payload: res.result, + }) + } else if (res.status === 0) { + setErrorMessage(`${typeof res.message === 'string' ? res.message : res.message.content}`) + } else if (res.status === ErrorCode.CapacityNotEnoughForChange) { + setErrorMessage(t(`messages.codes.106`)) + } else { + setErrorMessage(t(`messages.codes.${res.status}`)) + } + }) + }, [suggestFeeRate, depositValue]) + const MemoizedDepositDialog = useMemo(() => { return ( void +} +enum PriceTypeEnum { + Custom = 'custom', + Standard = 'standard', +} +const DEFAULT_PRICE_ARRAY = ['1000', '2000', '3000'] +const DEFAULT_COUNT_DOWN = 30 + +const PricePanel: React.FunctionComponent = ({ price, field, onPriceChange }: PricePanelProps) => { + const [t] = useTranslation() + const [type, setType] = useState(PriceTypeEnum.Standard) + const [feeRateValueArray, setFeeRateValueArray] = useState([]) + const [priceArray, setPriceArray] = useState(DEFAULT_PRICE_ARRAY) + const [currentModeIdx, setCurrentModeIdx] = useState(1) + + const { + app: { send = appState.send }, + wallet: { id: walletID = '' }, + } = useGlobalState() + const { countDown, suggestFeeRate } = useGetCountDownAndFeeRateStats({ seconds: DEFAULT_COUNT_DOWN }) + + const isStandard = type === PriceTypeEnum.Standard + const label = isStandard ? t('price-switch.price') : t('price-switch.customPrice') + const percent = (countDown / DEFAULT_COUNT_DOWN) * 100 + + const handleDropdownChange = useCallback( + (e: { value: string; index?: number }) => { + const { value: dropdownChangedValue, index = 1 } = e + setCurrentModeIdx(index) + + onPriceChange?.(dropdownChangedValue) + }, + [setCurrentModeIdx, onPriceChange] + ) + + const dropdownValue = useMemo(() => priceArray[currentModeIdx], [priceArray, currentModeIdx]) + + const handleInputChange = useCallback( + (e: React.SyntheticEvent) => { + const { value: inputChangedValue } = e.currentTarget + + onPriceChange?.(inputChangedValue) + }, + [onPriceChange] + ) + + const inputError = useMemo(() => (Number(price) < 1000 ? t('price-switch.errorTip', { minPrice: 1000 }) : null), [ + price, + ]) + + const feeValuesArray = useMemo( + () => + feeRateValueArray?.map((item: FeeRateValueArrayItemType) => ({ + feeRateValue: item.feeRateValue, + value: shannonToCKBFormatter(item.feeValue), + })), + [feeRateValueArray] + ) + + useEffect(() => { + if (isStandard) { + handleDropdownChange({ value: priceArray[currentModeIdx], index: currentModeIdx }) + } + }, [isStandard, handleDropdownChange, priceArray]) + + const options = useMemo( + () => + ['slow', 'standard', 'fast'].map((mode, idx) => ({ + idx, + value: priceArray[idx], + label: ( +
+ {t(`price-switch.${mode}`)} + + {t('price-switch.priceColumn', { + priceValue: feeValuesArray?.[idx]?.feeRateValue || priceArray[idx], + feeValue: feeValuesArray?.[idx]?.value || 0, + })} + +
+ ), + })), + [priceArray, feeValuesArray] + ) + + useEffect(() => { + useGetBatchGeneratedTx({ walletID, items: send.outputs, priceArray }).then(res => { + setFeeRateValueArray(res) + }) + }, [send.outputs, priceArray]) + + useEffect(() => { + if (suggestFeeRate === 0) { + setPriceArray(['1000', '2000', '3000']) + } else { + setPriceArray(['1000', `${suggestFeeRate}`, `${Number(suggestFeeRate) * 2 - 1000}`]) + } + }, [suggestFeeRate]) + + const inputHint = t('price-switch.hintTip', { suggestFeeRate }) + + return ( +
+
+
+ + +
+ {isStandard ? ( +
+ + {t('price-switch.countDownTip', { countDown })} +
+ ) : null} +
+ + {isStandard ? ( +
+ +
+ ) : ( + + )} +
+ ) +} + +PricePanel.displayName = 'PricePanel' + +export default React.memo(PricePanel) diff --git a/packages/neuron-ui/src/components/PricePanel/pricePanel.module.scss b/packages/neuron-ui/src/components/PricePanel/pricePanel.module.scss new file mode 100644 index 0000000000..23939161ee --- /dev/null +++ b/packages/neuron-ui/src/components/PricePanel/pricePanel.module.scss @@ -0,0 +1,93 @@ +@import '../../styles/mixin.scss'; + +.pricePanel { + display: flex; + justify-content: space-between; + font-size: 0.875rem; + line-height: 1.125rem; + + .transferSwitch { + display: flex; + } +} + +.transferWrap { + display: flex; + align-items: center; + position: relative; + border: none; + background: none; + + &:hover { + font-size: 14px; + + svg > path { + fill: var(--active-color); + } + + &::after { + position: absolute; + display: inline-block; + left: 50%; + transform: translate(-50%, -99%); + padding: 12px; + border-radius: 8px; + overflow-wrap: break-word; + background-color: var(--tooltip-background-color); + color: var(--tooltip-font-color); + user-select: text; + white-space: nowrap; + text-align: center; + content: attr(data-content); + } + &::before { + content: ''; + user-select: text; + border-width: 9px; + border-style: solid; + border-color: transparent; + display: inline-block; + border-top-color: var(--tooltip-background-color); + transform: translate(-50%, -70%); + left: 50%; + position: absolute; + } + } +} + +.dropdownBox { + margin: 4px 0px 19px 0px; +} +.suffix { + font-family: inherit; + display: flex; + justify-content: center; + align-items: center; + color: #434343; + padding: 0 10px; + font-size: 0.75rem; + max-width: 200px; +} +.timeoutWrap { + display: flex; + align-items: center; + font-size: 14px; + gap: 4px; +} +.labelWrap { + color: #333; + font-weight: 500; + font-size: 12px; + + .labelComment { + display: inline-block; + font-weight: 400; + background-color: #effaf7; + color: #00c891; + padding: 2px 8px; + border-radius: 24px; + margin-left: 8px; + font-size: 10px; + line-height: 14px; + } +} diff --git a/packages/neuron-ui/src/components/SUDTSend/index.tsx b/packages/neuron-ui/src/components/SUDTSend/index.tsx index fbb80a63e7..34afa7c3ec 100644 --- a/packages/neuron-ui/src/components/SUDTSend/index.tsx +++ b/packages/neuron-ui/src/components/SUDTSend/index.tsx @@ -280,8 +280,7 @@ const SUDTSend = () => { }, [dispatch, sendState.sendAll]) const onPriceChange = useCallback( - (e: React.SyntheticEvent) => { - const { value } = e.target as HTMLInputElement + (value: string) => { const price = value.split('.')[0].replace(/[^\d]/, '') dispatch({ type: Fields.Price, payload: price }) }, diff --git a/packages/neuron-ui/src/components/Send/hooks.ts b/packages/neuron-ui/src/components/Send/hooks.ts index aa8daf4219..55b1141b45 100644 --- a/packages/neuron-ui/src/components/Send/hooks.ts +++ b/packages/neuron-ui/src/components/Send/hooks.ts @@ -2,6 +2,7 @@ import React, { useState, useCallback, useEffect, useMemo } from 'react' import { TFunction } from 'i18next' import { AppActions, StateDispatch } from 'states/stateProvider/reducer' import { generateTx, generateSendingAllTx } from 'services/remote/wallets' +import { ControllerResponse, SuccessFromController } from 'services/remote/remoteApiWrapper' import { outputsToTotalAmount, @@ -168,6 +169,7 @@ const useOnTransactionChange = ( type: AppActions.UpdateGeneratedTx, payload: null, }) + updateTransactionWith(generateTx)({ walletID, items, @@ -233,9 +235,9 @@ const useOnItemChange = (updateTransactionOutput: Function) => const useUpdateTransactionPrice = (dispatch: StateDispatch) => useCallback( - (e: React.SyntheticEvent) => { - const { value } = e.target as HTMLInputElement + (value: string) => { const price = value.split('.')[0].replace(/[^\d]/g, '') + dispatch({ type: AppActions.UpdateSendPrice, payload: price.replace(/,/g, ''), @@ -264,6 +266,50 @@ const clear = (dispatch: StateDispatch) => { const useClear = (dispatch: StateDispatch) => useCallback(() => clear(dispatch), [dispatch]) +export type FeeRateValueArrayItemType = { + feeRateValue: string + feeValue: string +} + +export const useGetBatchGeneratedTx = async ({ + walletID, + priceArray = [], + items, +}: { + walletID: string + priceArray?: string[] + items: Readonly +}) => { + const getUpdateGeneratedTx = (params: Controller.GenerateTransactionParams) => + generateTx(params).then((res: ControllerResponse) => { + if (res.status === 1) { + return (res as SuccessFromController).result + } + return res + }) + + const realParams = { + walletID, + items: items.map(item => ({ + address: item.address!, + capacity: CKBToShannonFormatter(item.amount, item.unit), + date: item.date, + })), + } + + const requestArray = priceArray.map(itemPrice => getUpdateGeneratedTx({ ...realParams, feeRate: itemPrice })) + const allPromiseResult = await Promise.allSettled(requestArray) + + const feeRateValueArray: FeeRateValueArrayItemType[] = allPromiseResult?.map( + (batchItem: PromiseSettledResult, index: number) => ({ + feeRateValue: priceArray[index], + feeValue: batchItem.status === 'fulfilled' ? calculateFee(batchItem.value) : '', + }) + ) + + return feeRateValueArray +} + export const useInitialize = ( walletID: string, items: Readonly, diff --git a/packages/neuron-ui/src/components/SendMetaInfo/index.tsx b/packages/neuron-ui/src/components/SendMetaInfo/index.tsx index 0d41481f26..5e4bd5aa33 100644 --- a/packages/neuron-ui/src/components/SendMetaInfo/index.tsx +++ b/packages/neuron-ui/src/components/SendMetaInfo/index.tsx @@ -15,7 +15,7 @@ interface SendMetaInfoProps { fee: string price: string handleDescriptionChange: React.EventHandler - handlePriceChange: React.EventHandler + handlePriceChange: (value: string) => void } const SendMetaInfo = ({ @@ -41,6 +41,7 @@ const SendMetaInfo = ({ error={errorMessage} /> ) : null} + - ) } diff --git a/packages/neuron-ui/src/components/SpecialAssetList/index.tsx b/packages/neuron-ui/src/components/SpecialAssetList/index.tsx index fa97067e1e..43e170ac89 100644 --- a/packages/neuron-ui/src/components/SpecialAssetList/index.tsx +++ b/packages/neuron-ui/src/components/SpecialAssetList/index.tsx @@ -27,6 +27,7 @@ import { TokenInfo } from 'components/SUDTCreateDialog' import SUDTMigrateDialog from 'components/SUDTMigrateDialog' import SUDTMigrateToNewAccountDialog from 'components/SUDTMigrateToNewAccountDialog' import SUDTMigrateToExistAccountDialog from 'components/SUDTMigrateToExistAccountDialog' +import useGetCountDownAndFeeRateStats from 'utils/hooks/useGetCountDownAndFeeRateStats' import { useMigrate, useClickMigrate, @@ -36,7 +37,7 @@ import { } from './hooks' import styles from './specialAssetList.module.scss' -const { PAGE_SIZE, MEDIUM_FEE_RATE } = CONSTANTS +const { PAGE_SIZE } = CONSTANTS export interface SpecialAssetCell { blockHash: string @@ -110,6 +111,7 @@ const SpecialAssetList = () => { }, sUDTAccounts, } = useGlobalState() + const { suggestFeeRate } = useGetCountDownAndFeeRateStats() const isMainnet = isMainnetUtil(networks, networkID) const foundTokenInfo = tokenInfoList.find(token => token.tokenID === accountToClaim?.account.tokenID) const accountNames = useMemo(() => sUDTAccounts.filter(v => !!v.accountName).map(v => v.accountName!), [sUDTAccounts]) @@ -248,7 +250,7 @@ const SpecialAssetList = () => { unlockSpecialAsset({ walletID: id, outPoint: cell.outPoint, - feeRate: `${MEDIUM_FEE_RATE}`, + feeRate: `${suggestFeeRate}`, customizedAssetInfo: cell.customizedAssetInfo, }).then(handleRes('unlock')) return diff --git a/packages/neuron-ui/src/components/TransactionFeePanel/index.tsx b/packages/neuron-ui/src/components/TransactionFeePanel/index.tsx index e1b8f2ad13..052d8f0127 100644 --- a/packages/neuron-ui/src/components/TransactionFeePanel/index.tsx +++ b/packages/neuron-ui/src/components/TransactionFeePanel/index.tsx @@ -1,85 +1,32 @@ -import React, { useState } from 'react' -import { Stack, Toggle, IDropdownOption } from 'office-ui-fabric-react' -import Dropdown from 'widgets/Dropdown' +import React from 'react' +import { Stack } from 'office-ui-fabric-react' import TextField from 'widgets/TextField' +import PricePanel from 'components/PricePanel' import { useTranslation } from 'react-i18next' -import { Price, localNumberFormatter } from 'utils' interface TransactionFeeProps { fee: string price: string - onPriceChange: any + onPriceChange: (value: string) => void } -const TransactionFee: React.FunctionComponent = ({ +const TransactionFeePanel: React.FunctionComponent = ({ price, fee, onPriceChange, }: TransactionFeeProps) => { const [t] = useTranslation() - const [showDetail, setShowDetail] = useState(false) return ( - - { - setShowDetail(!showDetail) - }} - label={t('send.advanced-fee-settings')} - inlineLabel - onText=" " - offText=" " - styles={{ - label: { - fontSize: 14, - fontWeight: 500, - }, - }} - /> - - - - - - { - if (item) { - e.target.value = item.key - onPriceChange(e) - } - }} - aria-label="expected speed" - /> + ) } -TransactionFee.displayName = 'TransactionFee' +TransactionFeePanel.displayName = 'TransactionFeePanel' -export default TransactionFee +export default TransactionFeePanel diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index 1b5edfdd61..7f0fecd41f 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -991,6 +991,22 @@ "view-concise-data": "View concise data", "view-raw-data": "View raw data" } + }, + "dropdown": { + "placeholder": "Please select..." + }, + "price-switch": { + "price": "Price", + "customPrice": "Custom Price", + "errorTip": "Price cannot be less than {{minPrice}} shannons/KB, please re-enter", + "hintTip": "Suggested price is {{suggestFeeRate}} shannons/KB", + "priceColumn": "Price {{priceValue}} shannons/KB | Fee {{feeValue}} CKB", + "fast": "Fast", + "standard": "Standard", + "slow": "Slow", + "countDownTip": "The price will refresh after {{countDown}} seconds", + "switchToCustomPrice": "Switch to custom price", + "switchToPrice": "Switch to price" } } } diff --git a/packages/neuron-ui/src/locales/zh-tw.json b/packages/neuron-ui/src/locales/zh-tw.json index e980435550..d45a4fe26f 100644 --- a/packages/neuron-ui/src/locales/zh-tw.json +++ b/packages/neuron-ui/src/locales/zh-tw.json @@ -983,6 +983,22 @@ "view-concise-data": "查看概要", "view-raw-data": "查看原始數據" } + }, + "dropdown": { + "placeholder": "請選擇 ..." + }, + "price-switch": { + "price": "單價", + "customPrice": "自定義價格", + "errorTip": "價格設定最少不得少於 {{minPrice}} shannons/byte,請重新輸入", + "hintTip": "建議價格 {{suggestFeeRate}} shannons/KB", + "priceColumn": "單價 {{priceValue}} shannons/KB | 費用 {{feeValue}} CKB", + "fast": "快速", + "standard": "標準", + "slow": "慢速", + "countDownTip": "單價將在{{countDown}}秒後刷新", + "switchToCustomPrice": "切換到自定義價格", + "switchToPrice": "切換到單價" } } } diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index 316a253421..aa817d5a31 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -983,6 +983,22 @@ "view-concise-data": "查看概要", "view-raw-data": "查看原始数据" } + }, + "dropdown": { + "placeholder": "请选择 ..." + }, + "price-switch": { + "price": "单价", + "customPrice": "自定义价格", + "errorTip": "价格设定最少不得少于 {{minPrice}} shannons/KB,请重新输入", + "hintTip": "建议价格 {{suggestFeeRate}} shannons/KB", + "priceColumn": "单价 {{priceValue}} shannons/KB | 费用 {{feeValue}} CKB", + "fast": "快速", + "standard": "标准", + "slow": "慢速", + "countDownTip": "单价将在{{countDown}}秒后刷新", + "switchToCustomPrice": "切换到自定义价格", + "switchToPrice": "切换到单价" } } } diff --git a/packages/neuron-ui/src/services/chain.ts b/packages/neuron-ui/src/services/chain.ts index ecda9466ea..e83b569cbf 100644 --- a/packages/neuron-ui/src/services/chain.ts +++ b/packages/neuron-ui/src/services/chain.ts @@ -1,7 +1,7 @@ import CKBCore from '@nervosnetwork/ckb-sdk-core' export const ckbCore = new CKBCore('') -export const { getHeader, getBlock, getBlockchainInfo, getTipHeader, getHeaderByNumber } = ckbCore.rpc +export const { getHeader, getBlock, getBlockchainInfo, getTipHeader, getHeaderByNumber, getFeeRateStats } = ckbCore.rpc export const { calculateDaoMaximumWithdraw } = ckbCore @@ -15,4 +15,5 @@ export default { getHeaderByNumber, calculateDaoMaximumWithdraw, toUint64Le, + getFeeRateStats, } diff --git a/packages/neuron-ui/src/states/stateProvider/reducer.ts b/packages/neuron-ui/src/states/stateProvider/reducer.ts index 7b41290369..bd0db0a3e7 100644 --- a/packages/neuron-ui/src/states/stateProvider/reducer.ts +++ b/packages/neuron-ui/src/states/stateProvider/reducer.ts @@ -58,6 +58,9 @@ export enum AppActions { UpdateExperimentalParams = 'updateExperimentalParams', // offline sign UpdateLoadedTransaction = 'updateLoadedTransaction', + + GetFeeRateStats = 'getFeeRateStats', + UpdateCountDown = 'updateCountDown', } export type StateAction = @@ -89,6 +92,8 @@ export type StateAction = | { type: AppActions.Ignore; payload?: any } | { type: AppActions.UpdateExperimentalParams; payload: { tx: any; assetAccount?: any } | null } | { type: AppActions.UpdateLoadedTransaction; payload: { filePath?: string; json: OfflineSignJSON } } + | { type: AppActions.GetFeeRateStats; payload: State.FeeRateStatsType } + | { type: AppActions.UpdateCountDown; payload: number } | { type: NeuronWalletActions.InitAppState; payload: any } | { type: NeuronWalletActions.UpdateCurrentWallet; payload: Partial } | { type: NeuronWalletActions.UpdateWalletList; payload: State.WalletIdentity[] } @@ -365,6 +370,7 @@ export const reducer = produce((state: Draft, action: } break } + default: { break } diff --git a/packages/neuron-ui/src/stories/SendMetaInfo.stories.tsx b/packages/neuron-ui/src/stories/SendMetaInfo.stories.tsx index c4909c7098..d1a0f3889c 100644 --- a/packages/neuron-ui/src/stories/SendMetaInfo.stories.tsx +++ b/packages/neuron-ui/src/stories/SendMetaInfo.stories.tsx @@ -15,7 +15,7 @@ stories.add('Common', () => { fee: text('Fee', ''), price: text('Price', ''), handleDescriptionChange: (e: React.ChangeEvent) => action('Update Description')(e.target.value), - handlePriceChange: (e: React.ChangeEvent) => action('Update Price')(e.target.value), + handlePriceChange: (value: string) => action('Update Price')(value), } return }) diff --git a/packages/neuron-ui/src/styles/color.scss b/packages/neuron-ui/src/styles/color.scss index e6d4962cd6..6cfb27bc6a 100644 --- a/packages/neuron-ui/src/styles/color.scss +++ b/packages/neuron-ui/src/styles/color.scss @@ -1,4 +1,8 @@ :root { --nervos-green: #3cc68a; --nervos-green-light: #31a572; // most recent color to nervos green that pairs with white background + + --active-color: #00c891; + --tooltip-background-color: rgba(0, 0, 0, 0.8); + --tooltip-font-color: #ffffff; } diff --git a/packages/neuron-ui/src/types/App/index.d.ts b/packages/neuron-ui/src/types/App/index.d.ts index 9994a955ea..f3cd426448 100644 --- a/packages/neuron-ui/src/types/App/index.d.ts +++ b/packages/neuron-ui/src/types/App/index.d.ts @@ -61,6 +61,7 @@ declare namespace State { content?: string meta?: Meta } + interface Send { txID: string outputs: Output[] @@ -120,6 +121,8 @@ declare namespace State { type AlertDialog = Record<'title' | 'message', string> | null type GlobalDialogType = 'unlock-success' | 'rebuild-sync' | null + type FeeRateStatsType = { mean: string | number; median: string | number; suggestFeeRate: string | number } + interface App { tipBlockNumber: string tipBlockHash: string diff --git a/packages/neuron-ui/src/utils/calculateFee.ts b/packages/neuron-ui/src/utils/calculateFee.ts index 709c2a0390..2c7103f747 100644 --- a/packages/neuron-ui/src/utils/calculateFee.ts +++ b/packages/neuron-ui/src/utils/calculateFee.ts @@ -1,5 +1,5 @@ export const calculateFee = (tx: any) => { - if (!tx) { + if (!tx?.inputs || !tx?.outputs) { return '0' } const inputCapacities = tx.inputs.reduce( diff --git a/packages/neuron-ui/src/utils/hooks/createSUDTAccount.ts b/packages/neuron-ui/src/utils/hooks/createSUDTAccount.ts index a4136869a1..ddca3b5290 100644 --- a/packages/neuron-ui/src/utils/hooks/createSUDTAccount.ts +++ b/packages/neuron-ui/src/utils/hooks/createSUDTAccount.ts @@ -10,6 +10,7 @@ import { invokeShowErrorMessage, } from 'services/remote' import { getExplorerUrl } from 'utils' +import useGetCountDownAndFeeRateStats from './useGetCountDownAndFeeRateStats' import { ErrorCode } from '../enums' import { isSuccessResponse } from '../is' import { @@ -29,8 +30,10 @@ export const useIsInsufficientToCreateSUDTAccount = ({ walletId, balance, setInsufficient, -}: IsInsufficientToCreateSudtAccountProps) => - useEffect(() => { +}: IsInsufficientToCreateSudtAccountProps) => { + const { suggestFeeRate } = useGetCountDownAndFeeRateStats() + + return useEffect(() => { const createDummySUDTAccount = () => { if (balance <= BigInt(MIN_CKB_REQUIRED_BY_NORMAL_SUDT) * BigInt(SHANNON_CKB_RATIO)) { return true @@ -42,7 +45,7 @@ export const useIsInsufficientToCreateSUDTAccount = ({ accountName: DEFAULT_SUDT_FIELDS.accountName, symbol: DEFAULT_SUDT_FIELDS.symbol, decimal: '0', - feeRate: `${MEDIUM_FEE_RATE}`, + feeRate: `${suggestFeeRate}`, } return generateCreateSUDTAccountTransaction(params).catch(() => false) } @@ -57,7 +60,7 @@ export const useIsInsufficientToCreateSUDTAccount = ({ accountName: DEFAULT_SUDT_FIELDS.accountName, symbol: DEFAULT_SUDT_FIELDS.CKBSymbol, decimal: DEFAULT_SUDT_FIELDS.CKBDecimal, - feeRate: `${MEDIUM_FEE_RATE}`, + feeRate: `${suggestFeeRate}`, } return generateCreateSUDTAccountTransaction(params).catch(() => false) } @@ -76,7 +79,8 @@ export const useIsInsufficientToCreateSUDTAccount = ({ [AccountType.SUDT]: insufficientToCreateSUDTAccount, }) }) - }, [walletId, balance, setInsufficient]) + }, [walletId, balance, setInsufficient, suggestFeeRate]) +} export const useOnGenerateNewAccountTransaction = ({ walletId, diff --git a/packages/neuron-ui/src/utils/hooks/useGetCountDownAndFeeRateStats.ts b/packages/neuron-ui/src/utils/hooks/useGetCountDownAndFeeRateStats.ts new file mode 100644 index 0000000000..43d6b15f0f --- /dev/null +++ b/packages/neuron-ui/src/utils/hooks/useGetCountDownAndFeeRateStats.ts @@ -0,0 +1,62 @@ +import { useState, useEffect, useCallback } from 'react' +import { getFeeRateStats } from 'services/chain' +import { AppActions, StateDispatch, useDispatch } from 'states' +import { MEDIUM_FEE_RATE } from 'utils/const' + +type CountdownOptions = { + seconds?: number + interval?: number +} + +const useGetCountDownAndFeeRateStats = ({ seconds = 30, interval = 1000 }: CountdownOptions = {}) => { + const [countDown, setCountDown] = useState(seconds) + const [feeFatestatsData, setFeeFatestatsData] = useState<{ + mean?: string + median?: string + suggestFeeRate: number + }>({ suggestFeeRate: MEDIUM_FEE_RATE }) + const dispatch = useDispatch() + + const handleGetFeeRateStatis = useCallback( + (stateDispatch: StateDispatch) => { + getFeeRateStats() + .then(res => { + const { mean, median } = res + const suggested = mean && median ? Math.max(1000, Number(mean), Number(median)) : MEDIUM_FEE_RATE + + setFeeFatestatsData(states => ({ ...states, ...res, suggestFeeRate: suggested })) + }) + .catch((err: Error) => { + stateDispatch({ + type: AppActions.AddNotification, + payload: { + type: 'alert', + timestamp: +new Date(), + content: err.message, + }, + }) + }) + }, + [getFeeRateStats, setFeeFatestatsData] + ) + + useEffect(() => { + const countInterval = setInterval(() => { + setCountDown(count => (count <= 0 ? seconds : count - 1)) + }, interval) + + return () => { + clearInterval(countInterval) + } + }, []) + + useEffect(() => { + if (countDown === seconds) { + handleGetFeeRateStatis(dispatch) + } + }, [countDown, seconds, dispatch]) + + return { countDown, ...feeFatestatsData } +} + +export default useGetCountDownAndFeeRateStats diff --git a/packages/neuron-ui/src/widgets/DropdownWithCustomRender/index.module.scss b/packages/neuron-ui/src/widgets/DropdownWithCustomRender/index.module.scss new file mode 100644 index 0000000000..73ed79612d --- /dev/null +++ b/packages/neuron-ui/src/widgets/DropdownWithCustomRender/index.module.scss @@ -0,0 +1,94 @@ +:root { + --dropdown-base-font-color: rgb(50, 49, 48); + --selected-color: rgb(227, 227, 227); +} + +.dropdown-root { + position: relative; +} + +.dropdown-control { + position: relative; + overflow: hidden; + background-color: white; + border: 1px solid rgb(96, 94, 92); + box-sizing: border-box; + cursor: pointer; + outline: none; + transition: all 200ms ease; + font-size: 0.75rem; + font-weight: 400; + color: var(--dropdown-base-font-color); + border-color: rgb(96, 94, 92); + display: flex; + justify-content: space-between; + align-items: center; + padding: 3px 10px; + padding-bottom: 3.5px; +} + +.dropdown-control:hover { + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06); +} + +.dropdown-arrow { + transition: 0.2s; +} + +.is-open .dropdown-arrow { + transform: rotate(180deg); +} + +.dropdown-menu { + background-color: white; + box-shadow: rgb(0 0 0 / 13%) 0px 3.2px 7.2px 0px, + rgb(0 0 0 / 11%) 0px 0.6px 1.8px 0px; + box-sizing: border-box; + margin-top: 0px; + max-height: 200px; + overflow-y: auto; + position: absolute; + top: 100%; + width: 100%; + z-index: 1000; + -webkit-overflow-scrolling: touch; +} + +.dropdown-option { + box-sizing: border-box; + color: var(--dropdown-base-font-color); + cursor: pointer; + display: block; + padding: 6px 10px; + font-size: 0.75rem; +} + +.dropdown-option:last-child { + border-bottom-right-radius: 2px; + border-bottom-left-radius: 2px; +} + +.dropdown-option:hover { + background-color: rgba(0, 0, 0, 0.06); + color: var(--dropdown-base-font-color); +} + +.dropdown-option.is-selected { + color: var(--dropdown-base-font-color); +} + +.dropdown-disabled { + background-color: var(--selected-color); +} + +.dropdown-placeholder { + font-size: 12px; +} + +.dropdown-noresults { + box-sizing: border-box; + color: #ccc; + cursor: default; + display: block; + padding: 8px 10px; +} diff --git a/packages/neuron-ui/src/widgets/DropdownWithCustomRender/index.tsx b/packages/neuron-ui/src/widgets/DropdownWithCustomRender/index.tsx new file mode 100644 index 0000000000..fedf9e2743 --- /dev/null +++ b/packages/neuron-ui/src/widgets/DropdownWithCustomRender/index.tsx @@ -0,0 +1,171 @@ +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { LineDownArrow } from 'widgets/Icons/icon' + +import styles from './index.module.scss' + +export interface OptionProps { + label: string | React.ReactNode + value: string + className?: string + index?: number +} + +interface DropdownWithCustomRenderProps { + onChange: (changeValue: OptionProps) => void + options: OptionProps[] + value?: string + placeholder?: string + onFocus?: (isOpen: boolean) => void + disabled?: boolean + className?: string +} + +const DropdownWithCustomRender = ({ + options: optionsFromProps, + onChange, + value: valueFromProps = '', + placeholder, + onFocus, + disabled, + className, +}: DropdownWithCustomRenderProps) => { + const [t] = useTranslation() + const [isOpen, setIsOpen] = useState(false) + const dropdownRef = useRef(null) + + const DEFAULT_PLACEHOLDER_STRING = t('dropdown.placeholder') + + // render selected value in options + const parseValue = (value: string, options: OptionProps[]) => { + const option = options.find(v => v?.value === value) + + return ( + option || { + label: placeholder ?? DEFAULT_PLACEHOLDER_STRING, + value: '', + } + ) + } + + const selectedOption = useMemo(() => parseValue(valueFromProps, optionsFromProps), [valueFromProps, optionsFromProps]) + + const setValue = useCallback( + ({ value, label, index }: OptionProps) => { + const newState = { + value, + label, + index, + } + onChange?.(newState) + setIsOpen(false) + }, + [onChange, setIsOpen] + ) + + const handleDocumentClick = (event: MouseEvent | TouchEvent) => { + if (!dropdownRef.current?.contains(event.target as HTMLElement)) { + setIsOpen(false) + } + } + + const handleMouseDown = (event: React.MouseEvent | React.TouchEvent) => { + if (typeof onFocus === 'function') { + onFocus(isOpen) + } + if (event.type === 'mousedown' && (event as React.MouseEvent).button !== 0) { + return + } + event.stopPropagation() + event.preventDefault() + + if (!disabled) { + setIsOpen(openState => !openState) + } + } + + const isValueSelected = selectedOption.value !== '' + const dropdownLabelOrPlaceholder = selectedOption.label || DEFAULT_PLACEHOLDER_STRING + + const disabledClass = disabled ? `${styles['dropdown-disabled']}` : '' + const dropdownClass = `${styles['dropdown-root']} ${className} ${isOpen ? styles['is-open'] : ''}` + const controlClass = `${styles['dropdown-control']} ${disabledClass}` + const placeholderClass = `${styles['dropdown-placeholder']} ${isValueSelected ? styles['is-selected'] : ''}` + const arrowClass = `${styles['dropdown-arrow']}` + const menuClass = `${styles['dropdown-menu']}` + + const renderOption = useCallback( + (option: OptionProps) => { + const { value, label, className: optionClassName, index } = option + const isSelected = value === selectedOption.value + const optionClass = `${styles['dropdown-option']} ${optionClassName} ${isSelected ? styles['is-selected'] : ''}` + + const renderedValue = { + value, + label, + index, + } + + return ( +
setValue(renderedValue)} + onClick={() => setValue(renderedValue)} + onKeyDown={() => setValue(renderedValue)} + role="option" + tabIndex={0} + aria-selected={isSelected ? 'true' : 'false'} + > + {label} +
+ ) + }, + [selectedOption, setValue] + ) + + const menu = useMemo(() => { + const buildMenu = () => { + const ops = optionsFromProps.map((option, index) => renderOption({ ...option, index })) + + return ops.length ? ops :
No options found
+ } + + return isOpen ? ( +
+ {buildMenu()} +
+ ) : null + }, [isOpen, optionsFromProps, renderOption]) + + useEffect(() => { + document.addEventListener('click', handleDocumentClick, false) + document.addEventListener('touchend', handleDocumentClick, false) + + return () => { + document.removeEventListener('click', handleDocumentClick, false) + document.removeEventListener('touchend', handleDocumentClick, false) + } + }, []) + + return ( +
+
+
{dropdownLabelOrPlaceholder}
+ +
+ {menu} +
+ ) +} + +DropdownWithCustomRender.displayName = 'DropdownWithCustomRender' + +export default React.memo(DropdownWithCustomRender) diff --git a/packages/neuron-ui/src/widgets/Icons/LineDownArrow.svg b/packages/neuron-ui/src/widgets/Icons/LineDownArrow.svg new file mode 100644 index 0000000000..5f7b069fac --- /dev/null +++ b/packages/neuron-ui/src/widgets/Icons/LineDownArrow.svg @@ -0,0 +1,4 @@ + + + diff --git a/packages/neuron-ui/src/widgets/Icons/Transfer.svg b/packages/neuron-ui/src/widgets/Icons/Transfer.svg new file mode 100644 index 0000000000..8b92c924a8 --- /dev/null +++ b/packages/neuron-ui/src/widgets/Icons/Transfer.svg @@ -0,0 +1,5 @@ + + + diff --git a/packages/neuron-ui/src/widgets/Icons/icon.tsx b/packages/neuron-ui/src/widgets/Icons/icon.tsx index 5bae71d5ce..a2de766d62 100644 --- a/packages/neuron-ui/src/widgets/Icons/icon.tsx +++ b/packages/neuron-ui/src/widgets/Icons/icon.tsx @@ -25,6 +25,8 @@ import { ReactComponent as PendingIconSvg } from './PendingIcon.svg' import { ReactComponent as NewTabSvg } from './new_tab.svg' import { ReactComponent as TooltipSvg } from './Tooltip.svg' import { ReactComponent as OpenFolderSvg } from './OpenFolder.svg' +import { ReactComponent as TransferSvg } from './Transfer.svg' +import { ReactComponent as LineDownArrowSvg } from './LineDownArrow.svg' import styles from './icon.module.scss' @@ -64,3 +66,5 @@ export const PendingIcon = WrapSvg(PendingIconSvg) export const NewTab = WrapSvg(NewTabSvg) export const Tooltip = WrapSvg(TooltipSvg) export const OpenFolder = WrapSvg(OpenFolderSvg) +export const Transfer = WrapSvg(TransferSvg) +export const LineDownArrow = WrapSvg(LineDownArrowSvg) diff --git a/packages/neuron-ui/src/widgets/TextField/textField.module.scss b/packages/neuron-ui/src/widgets/TextField/textField.module.scss index a1c503f923..3f0ef823b4 100644 --- a/packages/neuron-ui/src/widgets/TextField/textField.module.scss +++ b/packages/neuron-ui/src/widgets/TextField/textField.module.scss @@ -94,14 +94,12 @@ $secondary-font-size: 0.63rem; grid-area: message; color: $error-red; font-size: $secondary-font-size; - line-height: 0.6875rem; word-break: break-all; svg { width: 8px; - height: 8px; + height: 1rem; margin-right: 5px; - margin-top: 0.125rem; } } diff --git a/packages/neuron-wallet/package.json b/packages/neuron-wallet/package.json index d23073a6aa..70a0789f3b 100644 --- a/packages/neuron-wallet/package.json +++ b/packages/neuron-wallet/package.json @@ -41,8 +41,8 @@ "@iarna/toml": "2.2.5", "@ledgerhq/hw-transport-node-hid": "6.27.12", "@nervina-labs/ckb-indexer": "0.1.1", - "@nervosnetwork/ckb-sdk-core": "0.103.1", - "@nervosnetwork/ckb-sdk-utils": "0.103.1", + "@nervosnetwork/ckb-sdk-core": "0.107.0", + "@nervosnetwork/ckb-sdk-utils": "0.107.0", "archiver": "5.3.0", "async": "3.2.4", "bn.js": "4.12.0", diff --git a/yarn.lock b/yarn.lock index 36bffa0c0c..3c9e8244e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2976,31 +2976,31 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@nervosnetwork/ckb-sdk-core@0.103.1": - version "0.103.1" - resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-sdk-core/-/ckb-sdk-core-0.103.1.tgz#136d616ec53af96d3b93dcbce593e6db484c802d" - integrity sha512-LeNwId3GaQILVpnY1zfNBY897XQJPODKeP6d7w6tww9anu0jSxrvXVxGMDTTn3Yz5mixzBgpFGo4SXRrH0eiaQ== - dependencies: - "@nervosnetwork/ckb-sdk-rpc" "0.103.1" - "@nervosnetwork/ckb-sdk-utils" "0.103.1" - "@nervosnetwork/ckb-types" "0.103.1" +"@nervosnetwork/ckb-sdk-core@0.107.0": + version "0.107.0" + resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-sdk-core/-/ckb-sdk-core-0.107.0.tgz#3baba0ed96d4cc48a34dc984bcc0bee0f1729aa9" + integrity sha512-2Tmt1bDjgX9FDrH+1MThjs8N33X/QGRsln/ldaN9vfzXEhXWCcru9KZR9gBQNIiyLjXOb8sv1aUH8Ma9y8QMZQ== + dependencies: + "@nervosnetwork/ckb-sdk-rpc" "0.107.0" + "@nervosnetwork/ckb-sdk-utils" "0.107.0" + "@nervosnetwork/ckb-types" "0.107.0" tslib "2.3.1" -"@nervosnetwork/ckb-sdk-rpc@0.103.1": - version "0.103.1" - resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-sdk-rpc/-/ckb-sdk-rpc-0.103.1.tgz#6c0d0bad8a05a9280ddc34adf973bf55e0725e06" - integrity sha512-n3V/5Be7PilyHXUcR6yAiwanMcuZNLfslu9A84OpfB22hlkwIv7OFG/JvFNack/cANPfChRAWB+VWiYGwSON/A== +"@nervosnetwork/ckb-sdk-rpc@0.107.0": + version "0.107.0" + resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-sdk-rpc/-/ckb-sdk-rpc-0.107.0.tgz#c0ed51ae96f237c6140945c1b8408cdf64ffa4e1" + integrity sha512-zLUrvjQuYqkX08POB4Q8Szws+c6XcX4iyhu+evYllrdvd9XF20GF/B62mMIQpnos3phu9ySs4H58VTxWp7aiag== dependencies: - "@nervosnetwork/ckb-sdk-utils" "0.103.1" + "@nervosnetwork/ckb-sdk-utils" "0.107.0" axios "0.21.4" tslib "2.3.1" -"@nervosnetwork/ckb-sdk-utils@0.103.1": - version "0.103.1" - resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-sdk-utils/-/ckb-sdk-utils-0.103.1.tgz#8356f88027a4054835fee0648617cc7020c8db1b" - integrity sha512-OzpFNKkOOqYWDHDjPf11uOfOf8EQOEstOBxVtyFjrGEx3B9FwsGxItlHYeuTfpFkHF2Ut+G9M3X71w8h/IApdQ== +"@nervosnetwork/ckb-sdk-utils@0.107.0": + version "0.107.0" + resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-sdk-utils/-/ckb-sdk-utils-0.107.0.tgz#36bc402c60c8174c24092e21211351e8fbd4f9f6" + integrity sha512-CPos2k3S9YI/MSUWv1D85KDwkvYBQcyl9YoYvsBN+AsMqqSUb8h1D3oectaJ2hN37o4Z2hrSDewEA6dXIWFLAg== dependencies: - "@nervosnetwork/ckb-types" "0.103.1" + "@nervosnetwork/ckb-types" "0.107.0" bech32 "2.0.0" elliptic "6.5.4" jsbi "3.1.3" @@ -3011,6 +3011,11 @@ resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-types/-/ckb-types-0.103.1.tgz#b5626ce905353e70136bfaccf9def0819b76ca27" integrity sha512-gGRR1VvUS/KRq2ChhXHPiHpgyLYazPM2R8lK87shQI82Gp2/m6k1HVDeNR5XOYwQ3YmBbxHGQtQr/kMq7DUlZA== +"@nervosnetwork/ckb-types@0.107.0": + version "0.107.0" + resolved "https://registry.yarnpkg.com/@nervosnetwork/ckb-types/-/ckb-types-0.107.0.tgz#980947e20d17edd145ff3805e7312bbeb38be2f6" + integrity sha512-vaPQXzfbwVJnSqPtGFbdH8/WrL6YQmNQz1Tf1dRBIZbIEIXjVGO5DUHwAiU6pjC6j1QsrlBrvv/P622vM4Q1sA== + "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": version "5.1.1-v1" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" @@ -5192,14 +5197,7 @@ dependencies: "@types/react" "*" -"@types/react-transition-group@^4.4.1": - version "4.4.5" - resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416" - integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA== - dependencies: - "@types/react" "*" - -"@types/react@*", "@types/react@17.0.53": +"@types/react@*", "@types/react@17.0.53", "@types/react@^17": version "17.0.53" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.53.tgz#10d4d5999b8af3d6bc6a9369d7eb953da82442ab" integrity sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw== @@ -18713,14 +18711,6 @@ pretty-hrtime@^1.0.3: resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" integrity sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A== -primereact@8.7.1: - version "8.7.1" - resolved "https://registry.yarnpkg.com/primereact/-/primereact-8.7.1.tgz#2206ba4db54cbea44ebb0d0b9df8bd1ce0fbf5ef" - integrity sha512-2uv3t9LjcT9f4j2DgDIK0xa+lP8ysmQuD9DbBJwnfuyz362P0w2mmcDfp4UksiD1ld6oF0u3XAWVNZclaPJUjg== - dependencies: - "@types/react-transition-group" "^4.4.1" - react-transition-group "^4.4.1" - prismjs@^1.8.4: version "1.29.0" resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" @@ -19507,7 +19497,7 @@ react-textarea-autosize@^7.1.0: "@babel/runtime" "^7.1.2" prop-types "^15.6.0" -react-transition-group@^4.3.0, react-transition-group@^4.4.1: +react-transition-group@^4.3.0: version "4.4.5" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==