Skip to content

Commit

Permalink
feat: Support XUDT (#3206)
Browse files Browse the repository at this point in the history
* feat: Support XUDT

* fix: Support xudt args length

* fix: Dynamic calculate min capacity

* fix: Send xudt with live acp udt cell
  • Loading branch information
yanguoyu authored Jul 19, 2024
1 parent 316a906 commit 2ec3ff8
Show file tree
Hide file tree
Showing 65 changed files with 1,117 additions and 568 deletions.
17 changes: 9 additions & 8 deletions packages/neuron-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,15 @@
"last 2 chrome versions"
],
"dependencies": {
"@ckb-lumos/bi": "0.21.1",
"@ckb-lumos/rpc": "0.21.1",
"@ckb-lumos/base": "0.21.1",
"@ckb-lumos/codec": "0.21.1",
"@ckb-lumos/hd": "0.21.1",
"@ckb-lumos/helpers": "0.21.1",
"@ckb-lumos/config-manager": "0.21.1",
"@ckb-lumos/common-scripts": "0.21.1",
"@ckb-lumos/lumos": "0.23.0",
"@ckb-lumos/bi": "0.23.0",
"@ckb-lumos/rpc": "0.23.0",
"@ckb-lumos/base": "0.23.0",
"@ckb-lumos/codec": "0.23.0",
"@ckb-lumos/hd": "0.23.0",
"@ckb-lumos/helpers": "0.23.0",
"@ckb-lumos/config-manager": "0.23.0",
"@ckb-lumos/common-scripts": "0.23.0",
"canvg": "2.0.0",
"i18next": "23.7.11",
"immer": "9.0.21",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
.actions {
display: flex;
gap: 16px;
& > svg {
& svg {
cursor: pointer;
&[data-disabled='true'] {
cursor: not-allowed;
Expand Down
7 changes: 6 additions & 1 deletion packages/neuron-ui/src/components/CellManagement/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import { ErrorCode, LockScriptCategory, RoutePath, TypeScriptCategory, isSuccess
import { SortType } from 'widgets/Table'

const cellTypeOrder: Record<string, number> = {
[TypeScriptCategory.SUDT]: 1,
[TypeScriptCategory.SUDT]: 0,
[TypeScriptCategory.XUDT]: 1,
[TypeScriptCategory.NFT]: 2,
[TypeScriptCategory.Spore]: 3,
[TypeScriptCategory.Unknown]: 4,
Expand Down Expand Up @@ -47,6 +48,9 @@ const getLockStatusAndReason = (item: State.LiveCellWithLocalInfo) => {
case TypeScriptCategory.DAO:
lockedReason = { key: 'cell-manage.locked-reason.NFT-SUDT-DAO', params: { type: 'Nervos DAO' } }
break
case TypeScriptCategory.XUDT:
lockedReason = { key: 'cell-manage.locked-reason.NFT-SUDT-DAO', params: { type: 'XUDT' } }
break
case TypeScriptCategory.Unknown:
lockedReason = { key: 'cell-manage.locked-reason.Unknown' }
break
Expand Down Expand Up @@ -82,6 +86,7 @@ const getCellType = (item: State.LiveCellWithLocalInfo) => {
switch (item.typeScriptType) {
case TypeScriptCategory.NFT:
case TypeScriptCategory.SUDT:
case TypeScriptCategory.XUDT:
case TypeScriptCategory.Spore:
case TypeScriptCategory.Unknown:
return item.typeScriptType
Expand Down
4 changes: 2 additions & 2 deletions packages/neuron-ui/src/components/CellManagement/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ const getColumns = ({
<Tooltip tip={t('cell-manage.unlock')} showTriangle placement="top">
<UnLock
data-disabled={!!lockedReason}
onClick={onAction}
onClick={lockedReason ? undefined : onAction}
data-action={Actions.Unlock}
data-index={index}
/>
Expand All @@ -202,7 +202,7 @@ const getColumns = ({
<Tooltip tip={t('cell-manage.lock')} showTriangle placement="top">
<LockCell
data-disabled={!!lockedReason}
onClick={onAction}
onClick={lockedReason ? undefined : onAction}
data-action={Actions.Lock}
data-index={index}
/>
Expand Down
2 changes: 1 addition & 1 deletion packages/neuron-ui/src/components/NervosDAO/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ export const useUpdateDepositEpochList = ({
useEffect(() => {
if (connectionStatus === 'online') {
getBlockHashes(records.map(v => v.depositOutPoint?.txHash).filter(v => !!v) as string[]).then(
depositBlockHashes => {
(depositBlockHashes: { txHash: string; blockHash: string | undefined }[]) => {
const recordKeyIdx: string[] = []
const batchParams: ['getHeader', string][] = []
records.forEach(record => {
Expand Down
10 changes: 8 additions & 2 deletions packages/neuron-ui/src/components/SUDTAccountList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
isSuccessResponse,
useIsInsufficientToCreateSUDTAccount,
useOnGenerateNewAccountTransaction,
UDTType,
} from 'utils'

import { getSUDTAccountList, updateSUDTAccount } from 'services/remote'
Expand All @@ -48,7 +49,11 @@ const SUDTAccountList = () => {
const [keyword, setKeyword] = useState('')
const [dialog, setDialog] = useState<{ id: string; action: 'create' | 'update' } | null>(null)
const [isLoaded, setIsLoaded] = useState(false)
const [insufficient, setInsufficient] = useState({ [AccountType.CKB]: false, [AccountType.SUDT]: false })
const [insufficient, setInsufficient] = useState({
[AccountType.CKB]: false,
[AccountType.SUDT]: false,
[AccountType.XUDT]: false,
})

const isMainnet = isMainnetUtil(networks, networkID)
const [receiveData, setReceiveData] = useState<DataProps | null>(null)
Expand Down Expand Up @@ -178,6 +183,7 @@ const SUDTAccountList = () => {
tokenName: accountToUpdate.tokenName || DEFAULT_SUDT_FIELDS.tokenName,
symbol: accountToUpdate.symbol || DEFAULT_SUDT_FIELDS.symbol,
isCKB: accountToUpdate.tokenId === DEFAULT_SUDT_FIELDS.CKBTokenId,
udtType: accountToUpdate.udtType,
onSubmit: (info: Omit<TokenInfo, 'isCKB'>) => {
const params: any = { id: accountToUpdate.accountId }
Object.keys(info).forEach(key => {
Expand Down Expand Up @@ -208,7 +214,7 @@ const SUDTAccountList = () => {
: undefined

const handleCreateAccount = useCallback(
(info: TokenInfo) => {
(info: TokenInfo & { udtType?: UDTType }) => {
createAccount(info, () => {
setNotice(t('s-udt.create-account-success'))
})
Expand Down
36 changes: 25 additions & 11 deletions packages/neuron-ui/src/components/SUDTCreateDialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import {
isSuccessResponse,
useSUDTAccountInfoErrors,
useFetchTokenInfoList,
useOpenSUDTTokenUrl,
useOpenUDTTokenUrl,
UDTType,
} from 'utils'
import { DEFAULT_SUDT_FIELDS } from 'utils/const'
import styles from './sUDTCreateDialog.module.scss'

export enum AccountType {
SUDT = 'sudt',
XUDT = 'xudt',
CKB = 'ckb',
}

Expand All @@ -33,10 +35,10 @@ export interface TokenInfo extends BasicInfo {

export interface SUDTCreateDialogProps extends TokenInfo {
isMainnet: boolean
onSubmit: (info: TokenInfo) => void
onSubmit: (info: TokenInfo & { udtType?: UDTType }) => void
onCancel: () => void
existingAccountNames?: string[]
insufficient?: { [AccountType.CKB]: boolean; [AccountType.SUDT]: boolean }
insufficient?: { [P in AccountType]: boolean }
}

enum DialogSection {
Expand All @@ -49,6 +51,10 @@ const accountTypes: { key: AccountType; label: string }[] = [
key: AccountType.SUDT,
label: 's-udt.create-dialog.sudt-account',
},
{
key: AccountType.XUDT,
label: 's-udt.create-dialog.xudt-account',
},
{
key: AccountType.CKB,
label: 's-udt.create-dialog.ckb-account',
Expand Down Expand Up @@ -121,23 +127,29 @@ const SUDTCreateDialog = ({
onSubmit,
onCancel,
existingAccountNames = [],
insufficient = { [AccountType.CKB]: false, [AccountType.SUDT]: false },
insufficient = { [AccountType.CKB]: false, [AccountType.SUDT]: false, [AccountType.XUDT]: false },
isMainnet,
}: Partial<Omit<SUDTCreateDialogProps, 'onSubmit' | 'onCancel'>> &
Pick<SUDTCreateDialogProps, 'onSubmit' | 'onCancel' | 'insufficient'>) => {
const [t] = useTranslation()
const [info, dispatch] = useReducer(reducer, { accountName, tokenId, tokenName, symbol, decimal })
const [accountType, setAccountType] = useState([AccountType.SUDT, AccountType.CKB].find(at => !insufficient[at]))
const [accountType, setAccountType] = useState(
[AccountType.SUDT, AccountType.CKB, AccountType.XUDT].find(at => !insufficient[at])
)
const isUDT = accountType === AccountType.SUDT || accountType === AccountType.XUDT
// eslint-disable-next-line no-nested-ternary
const udtType = isUDT ? (accountType === AccountType.SUDT ? UDTType.SUDT : UDTType.XUDT) : undefined
const [step, setStep] = useState(DialogSection.Account)
const tokenInfoList = useFetchTokenInfoList()

const tokenInfoFields: (keyof TokenInfo)[] = ['tokenId', 'tokenName', 'symbol', 'decimal']

const tokenErrors = useSUDTAccountInfoErrors({
info,
isCKB: AccountType.CKB === accountType,
isCKB: !isUDT,
existingAccountNames,
t,
udtType,
})
const isAccountNameReady = info.accountName.trim() && !tokenErrors.accountName && accountType

Expand Down Expand Up @@ -190,7 +202,7 @@ const SUDTCreateDialog = ({
}
case DialogSection.Token: {
if (isTokenReady) {
onSubmit({ ...info, accountName: info.accountName.trim(), tokenName: info.tokenName.trim() })
onSubmit({ ...info, udtType, accountName: info.accountName.trim(), tokenName: info.tokenName.trim() })
}
break
}
Expand All @@ -216,7 +228,7 @@ const SUDTCreateDialog = ({
}
}
}
const openSUDTTokenUrl = useOpenSUDTTokenUrl(info.tokenId, isMainnet)
const openSUDTTokenUrl = useOpenUDTTokenUrl(info.tokenId, udtType, isMainnet)
return (
<Dialog
show
Expand Down Expand Up @@ -285,14 +297,16 @@ const SUDTCreateDialog = ({
className={styles.settingField}
placeholder={t(`s-udt.create-dialog.input.${field.label}`)}
hint={
!tokenErrors[field.key] && field.key === 'tokenId' && accountType === AccountType.SUDT
? t(`s-udt.create-dialog.placeholder.${field.label}`)
!tokenErrors[field.key] && field.key === 'tokenId' && isUDT
? t(`s-udt.create-dialog.placeholder.${field.label}`, {
udtType: AccountType.SUDT === accountType ? 'sUDT' : 'xUDT',
})
: undefined
}
/>
))}
</div>
{accountType === AccountType.SUDT && !tokenErrors.tokenId && info.tokenId && (
{isUDT && !tokenErrors.tokenId && info.tokenId && (
<button
type="button"
className={styles.explorerNavButton}
Expand Down
8 changes: 5 additions & 3 deletions packages/neuron-ui/src/components/SUDTMigrateDialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'
import { SpecialAssetCell } from 'components/SpecialAssetList/hooks'
import { MIN_CKB_REQUIRED_BY_NORMAL_SUDT, SHANNON_CKB_RATIO } from 'utils/const'
import Dialog from 'widgets/Dialog'
import { PresetScript, UDTType } from 'utils'
import styles from './sUDTMigrateDialog.module.scss'

const items = [
Expand Down Expand Up @@ -38,12 +39,13 @@ const SUDTMigrateDialog = ({
setType('')
onCancel()
}
const udtType = cell.customizedAssetInfo.type === PresetScript.SUDT ? UDTType.SUDT : UDTType.XUDT

return (
<Dialog
className={styles.container}
show
title={t('migrate-sudt.title')}
title={t('migrate-sudt.title', { udtType })}
onCancel={handleCancel}
cancelText={t('migrate-sudt.cancel')}
confirmText={t('migrate-sudt.next')}
Expand All @@ -64,8 +66,8 @@ const SUDTMigrateDialog = ({
role="button"
tabIndex={idx}
>
<div className={styles.title}>{t(v.title)}</div>
<div className={styles.subTitle}>{t(v.subTitle)}</div>
<div className={styles.title}>{t(v.title, { udtType })}</div>
<div className={styles.subTitle}>{t(v.subTitle, { udtType })}</div>
</div>
))}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { useTranslation } from 'react-i18next'
import { SpecialAssetCell } from 'components/SpecialAssetList/hooks'
import TextField from 'widgets/TextField'
import Dialog from 'widgets/Dialog'
import { AnyoneCanPayLockInfoOnAggron, getSUDTAmount, isSuccessResponse, validateSpecificAddress } from 'utils'
import {
AnyoneCanPayLockInfoOnAggron,
getSUDTAmount,
getUdtType,
isSuccessResponse,
validateSpecificAddress,
} from 'utils'
import InputSelect from 'widgets/InputSelect'
import { generateSudtMigrateAcpTx } from 'services/remote'
import { AppActions, showGlobalAlertDialog, useDispatch } from 'states'
Expand Down Expand Up @@ -53,6 +59,7 @@ const SUDTMigrateToExistAccountDialog = ({
[sUDTAccounts, tokenInfo]
)
const dispatch = useDispatch()
const udtType = getUdtType(cell.type)
const onSubmit = useCallback(() => {
generateSudtMigrateAcpTx({
outPoint: cell.outPoint,
Expand All @@ -73,7 +80,7 @@ const SUDTMigrateToExistAccountDialog = ({
walletID,
actionType: 'transfer-to-sudt',
onSuccess: () => {
onSuccess(t('special-assets.send-sudt-success'))
onSuccess(t('special-assets.send-sudt-success', { udtType }))
},
},
})
Expand All @@ -92,7 +99,7 @@ const SUDTMigrateToExistAccountDialog = ({
<Dialog
className={styles.container}
show
title={t('migrate-sudt.transfer-to-exist-account.title')}
title={t('migrate-sudt.transfer-to-exist-account.title', { udtType })}
onCancel={onBack}
cancelText={t('migrate-sudt.back')}
confirmText={t('migrate-sudt.next')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'
import { SpecialAssetCell } from 'components/SpecialAssetList/hooks'
import TextField from 'widgets/TextField'
import Dialog from 'widgets/Dialog'
import { getSUDTAmount, isSuccessResponse } from 'utils'
import { getSUDTAmount, getUdtType, isSuccessResponse } from 'utils'
import { generateSudtMigrateAcpTx } from 'services/remote'
import { AppActions, showGlobalAlertDialog, useDispatch } from 'states'
import { useTokenInfo, TokenInfoType } from './hooks'
Expand Down Expand Up @@ -43,6 +43,7 @@ const SUDTMigrateToNewAccountDialog = ({
tokenId: cell.type?.args,
sUDTAccounts,
})
const udtType = getUdtType(cell.type)

const confirmDisabled = useMemo(
() => fields.some(v => (tokenInfoErrors[v.key] || !tokenInfo[v.key]) && v.key !== 'balance'),
Expand All @@ -68,6 +69,7 @@ const SUDTMigrateToNewAccountDialog = ({
decimal: tokenInfo.decimal,
balance: sudtAmount.amountToCopy,
blake160: res.result.outputs[0].lock.args,
udtType,
},
},
})
Expand All @@ -77,7 +79,7 @@ const SUDTMigrateToNewAccountDialog = ({
walletID,
actionType: 'create-sudt-account',
onSuccess: () => {
onSuccess(t('special-assets.migrate-sudt-success'))
onSuccess(t('special-assets.migrate-sudt-success', { udtType }))
},
},
})
Expand Down Expand Up @@ -121,7 +123,7 @@ const SUDTMigrateToNewAccountDialog = ({
<Dialog
className={styles.container}
show
title={t('migrate-sudt.turn-into-new-account.title')}
title={t('migrate-sudt.turn-into-new-account.title', { udtType })}
onCancel={onBack}
cancelText={t('migrate-sudt.cancel')}
confirmText={t('migrate-sudt.confirm')}
Expand Down
5 changes: 4 additions & 1 deletion packages/neuron-ui/src/components/SUDTSend/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { DEFAULT_SUDT_FIELDS } from 'utils/const'
import { generateChequeTransaction, generateSUDTTransaction, getHoldSUDTCellCapacity } from 'services/remote'
import { AppActions, useDispatch } from 'states'
import { ControllerResponse } from 'services/remote/remoteApiWrapper'
import { SendType } from 'utils/enums'
import { SendType, UDTType } from 'utils/enums'

export enum AddressLockType {
secp256 = 'secp256',
Expand Down Expand Up @@ -37,11 +37,13 @@ export function useOptions({
addressLockType,
accountInfo,
isAddressCorrect,
udtType,
}: {
address: string
addressLockType: AddressLockType
accountInfo: SUDTAccount | null
isAddressCorrect: boolean
udtType?: UDTType
}) {
const [holdSUDTCellCapacity, setHoldSUDTCellCapacity] = useState<string | undefined | null>()
useEffect(() => {
Expand All @@ -54,6 +56,7 @@ export function useOptions({
getHoldSUDTCellCapacity({
address,
tokenID: accountInfo.tokenId,
udtType,
})
.then(res => {
if (isSuccessResponse(res)) {
Expand Down
Loading

2 comments on commit 2ec3ff8

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Packaging for test is done in 10005039315

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Packaging for test is done in 10005039001

Please sign in to comment.