diff --git a/.github/workflows/check-code-style.yml b/.github/workflows/check-code-style.yml index 87de0e3831..3584d79ce5 100644 --- a/.github/workflows/check-code-style.yml +++ b/.github/workflows/check-code-style.yml @@ -18,7 +18,7 @@ jobs: cache: "yarn" - name: Restore - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules diff --git a/.github/workflows/check_storybook.yml b/.github/workflows/check_storybook.yml index 1f351e7fb1..82aded4a8d 100644 --- a/.github/workflows/check_storybook.yml +++ b/.github/workflows/check_storybook.yml @@ -35,7 +35,7 @@ jobs: cache: "yarn" - name: Restore - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index e4a0171d42..0b74b02f2f 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -42,7 +42,7 @@ jobs: cache: "yarn" - name: Restore - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules @@ -51,7 +51,7 @@ jobs: - name: Add msbuild to PATH if: matrix.os == 'windows-2019' - uses: microsoft/setup-msbuild@v1.3.1 + uses: microsoft/setup-msbuild@v2 env: ACTIONS_ALLOW_UNSECURE_COMMANDS: "true" diff --git a/.github/workflows/package_for_test.yml b/.github/workflows/package_for_test.yml index 757811fc6d..a475701b0c 100644 --- a/.github/workflows/package_for_test.yml +++ b/.github/workflows/package_for_test.yml @@ -53,7 +53,7 @@ jobs: cache: "yarn" - name: Restore - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules @@ -62,7 +62,7 @@ jobs: - name: Add msbuild to PATH if: matrix.os == 'windows-2019' - uses: microsoft/setup-msbuild@v1.3.1 + uses: microsoft/setup-msbuild@v2 env: ACTIONS_ALLOW_UNSECURE_COMMANDS: "true" @@ -225,7 +225,7 @@ jobs: - name: Comment by pull request comment event if: ${{ github.event_name == 'issue_comment' }} - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v4 with: comment-id: ${{ github.event.comment.id }} body: | @@ -242,7 +242,7 @@ jobs: steps: - name: Comment by pull request comment event when package failed if: ${{ github.event_name == 'issue_comment' }} - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v4 with: comment-id: ${{ github.event.comment.id }} body: Packageing failed in [${{ github.run_id }}](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}). @${{ github.event.comment.user.login }} diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index e77a974953..334bc4c2f9 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -35,7 +35,7 @@ jobs: cache: "yarn" - name: Restore - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules diff --git a/CHANGELOG.md b/CHANGELOG.md index a35aea95a9..3c71658c54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,39 @@ +# 0.116.0 (2024-05-24) + +### CKB Node & Light Client + +- [CKB@v0.116.1](https://github.com/nervosnetwork/ckb/releases/tag/v0.116.1) was released on May. 11st, 2024. This version of CKB node is now bundled and preconfigured in Neuron. +- [CKB Light Client@v0.3.7](https://github.com/nervosnetwork/ckb-light-client/releases/tag/v0.3.7) was released on Apr. 13th, 2024. This version of CKB Light Client is now bundled and preconfigured in Neuron + +### Assumed valid target + +Block before `0x6dd077b407d019a0bce0cbad8c34e69a524ae4b2599b9feda2c7491f3559d32c`(at height `13,007,704`) will be skipped in validation.(https://github.com/nervosnetwork/neuron/pull/3157) + +--- + +[![Neuron@v0.116.0](https://github.com/Magickbase/neuron-public-issues/assets/7271329/ec10aa01-47fe-47a3-9636-3d4e86fc6c9b)](https://youtu.be/QXv8by2C8zU) + +YouTube: https://youtu.be/QXv8by2C8zU + +--- + +## New features + +- 3134: Support 'replace-by-fee' nervos dao transactions and sudt transactions.(@devchenyan) +- 3144: Reduce size of light client log in debug information and reveal start-block-number in log.(@yanguoyu) +- 3064: Support locking window by pin code.(@yanguoyu) +- 3131: Add detailed result for nervos dao transaction.(@devchenyan) + +## Bug fixes + +- 3121: Locate the first transaction on Explorer directly when users want to set the start-block-number for light client.(@yanguoyu) +- 3101: Show migration instruction properly.(@devchenyan) +- 3062: Migrate legacy ACP to active ACP account(@yanguoyu) +- 3141: Fix some issues about light client synchronizaiton.(@yanguoyu) +- 3120: Remove all sync data when start-block-number is set less than before.(@yanguoyu) + +**Full Changelog**: https://github.com/nervosnetwork/neuron/compare/v0.114.3...v0.116.0 + # 0.114.3 (2024-04-16) ### CKB Node & Light Client diff --git a/compatible.json b/compatible.json index 028ff3cb04..b2865b7a18 100644 --- a/compatible.json +++ b/compatible.json @@ -99,6 +99,22 @@ "0.3", "0.2" ] + }, + "0.116": { + "full": [ + "0.116", + "0.115", + "0.114", + "0.113", + "0.112", + "0.111", + "0.110", + "0.109" + ], + "light": [ + "0.3", + "0.2" + ] } } } diff --git a/lerna.json b/lerna.json index 0e77734cb4..922158bfd4 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.114.3", + "version": "0.116.0", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } diff --git a/package.json b/package.json index 62c7a5ad1b..e8c55808df 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "neuron", "productName": "Neuron", "description": "CKB Neuron Wallet", - "version": "0.114.3", + "version": "0.116.0", "private": true, "author": { "name": "Nervos Core Dev", diff --git a/packages/neuron-ui/package.json b/packages/neuron-ui/package.json index 5c6ce190e1..b98ed97822 100644 --- a/packages/neuron-ui/package.json +++ b/packages/neuron-ui/package.json @@ -1,6 +1,6 @@ { "name": "neuron-ui", - "version": "0.114.3", + "version": "0.116.0", "private": true, "author": { "name": "Nervos Core Dev", diff --git a/packages/neuron-ui/src/components/AmendSend/index.tsx b/packages/neuron-ui/src/components/AmendSend/index.tsx index 92eb75675d..7ba3873d1e 100644 --- a/packages/neuron-ui/src/components/AmendSend/index.tsx +++ b/packages/neuron-ui/src/components/AmendSend/index.tsx @@ -96,6 +96,15 @@ const AmendSend = () => { return '' } + const inputsCapacity = useMemo(() => { + if (transaction) { + return transaction.inputs.reduce((total, cur) => { + return total + BigInt(cur.capacity || '0') + }, BigInt(0)) + } + return undefined + }, [transaction]) + const items: { address: string amount: string @@ -103,21 +112,25 @@ const AmendSend = () => { isLastOutput: boolean output: State.DetailedOutput }[] = useMemo(() => { - if (transaction && transaction.outputs.length) { + if (transaction && transaction.outputs.length && inputsCapacity) { const lastOutputAddress = getLastOutputAddress(transaction.outputs) return transaction.outputs.map(output => { const address = scriptToAddress(output.lock, { isMainnet }) + const capacity = + transaction.outputs.length === 1 && address === lastOutputAddress + ? (inputsCapacity - fee).toString() + : output.capacity return { - capacity: output.capacity, + capacity, address, output, - amount: shannonToCKBFormatter(output.capacity || '0'), + amount: shannonToCKBFormatter(capacity || '0'), isLastOutput: address === lastOutputAddress, } }) } return [] - }, [transaction?.outputs]) + }, [transaction?.outputs, inputsCapacity, fee]) const outputsCapacity = useMemo(() => { const outputList = items.length === 1 ? items : items.filter(item => !item.isLastOutput) @@ -129,15 +142,15 @@ const AmendSend = () => { const totalAmount = shannonToCKBFormatter(outputsCapacity.toString()) const lastOutputsCapacity = useMemo(() => { - if (transaction) { - const inputsCapacity = transaction.inputs.reduce((total, cur) => { - return total + BigInt(cur.capacity || '0') - }, BigInt(0)) + if (inputsCapacity) { + if (items.length === 1) { + return BigInt(items[0].capacity || '0') + } return inputsCapacity - outputsCapacity - fee } return undefined - }, [transaction, fee, outputsCapacity]) + }, [inputsCapacity, fee, outputsCapacity, items]) useEffect(() => { if (transaction && lastOutputsCapacity !== undefined) { diff --git a/packages/neuron-ui/src/components/FormattedTokenAmount/index.tsx b/packages/neuron-ui/src/components/FormattedTokenAmount/index.tsx index 4a180ba15f..b31da32d3c 100644 --- a/packages/neuron-ui/src/components/FormattedTokenAmount/index.tsx +++ b/packages/neuron-ui/src/components/FormattedTokenAmount/index.tsx @@ -12,7 +12,7 @@ type FormattedTokenAmountProps = { item: State.Transaction; show: boolean; symbo type AmountProps = Omit & { sudtAmount?: string isReceive: boolean - amount: string + amount?: string symbolClassName?: string } @@ -30,18 +30,18 @@ const Amount = ({ sudtAmount, show, item, isReceive, amount, symbolClassName, sy ) : (
- - {amount} + + {amount ?? '--'} -  {symbol} +  {amount ? symbol : ''}
) } export const FormattedTokenAmount = ({ item, show, symbolClassName }: FormattedTokenAmountProps) => { - let amount = '--' + let amount: string | undefined let sudtAmount = '' - let copyText = amount + let copyText: string | undefined = amount let isReceive = false let symbol = '' @@ -61,16 +61,22 @@ export const FormattedTokenAmount = ({ item, show, symbolClassName }: FormattedT isReceive = !sudtAmount.includes('-') } } else { - amount = show ? `${shannonToCKBFormatter(item.value, true)}` : `${HIDE_BALANCE}` - isReceive = !amount.includes('-') + amount = show + ? `${shannonToCKBFormatter(item.nervosDao ? item.daoCapacity ?? '--' : item.value, true)}` + : `${HIDE_BALANCE}` + isReceive = !amount?.includes('-') copyText = `${amount} CKB` symbol = 'CKB' + if (item.nervosDao && item.daoCapacity === undefined) { + amount = undefined + copyText = undefined + } } } const props = { sudtAmount, show, item, isReceive, amount, symbolClassName, symbol } - return show ? ( + return show && copyText ? ( diff --git a/packages/neuron-ui/src/components/GeneralSetting/generalSetting.module.scss b/packages/neuron-ui/src/components/GeneralSetting/generalSetting.module.scss index 92e5f8d34f..39881944b7 100644 --- a/packages/neuron-ui/src/components/GeneralSetting/generalSetting.module.scss +++ b/packages/neuron-ui/src/components/GeneralSetting/generalSetting.module.scss @@ -9,7 +9,7 @@ $action-button-width: 11.25rem; gap: 16px; .content { - width: 176px; + min-width: 176px; padding: 16px; background: var(--input-disabled-color); border-radius: 8px; diff --git a/packages/neuron-ui/src/components/History/index.tsx b/packages/neuron-ui/src/components/History/index.tsx index 6e1f19c2d3..f012415c87 100644 --- a/packages/neuron-ui/src/components/History/index.tsx +++ b/packages/neuron-ui/src/components/History/index.tsx @@ -126,7 +126,7 @@ const History = () => { sortable: true, }, { - title: t('history.table.amount'), + title: t('history.table.asset'), dataIndex: 'amount', align: 'left', isBalance: true, diff --git a/packages/neuron-ui/src/components/Overview/index.tsx b/packages/neuron-ui/src/components/Overview/index.tsx index 9ce1a0a94b..2aaf350574 100644 --- a/packages/neuron-ui/src/components/Overview/index.tsx +++ b/packages/neuron-ui/src/components/Overview/index.tsx @@ -268,7 +268,7 @@ const Overview = () => { }, }, { - title: t('overview.amount'), + title: t('overview.asset'), dataIndex: 'amount', align: 'left', isBalance: true, diff --git a/packages/neuron-ui/src/components/SyncStatus/index.tsx b/packages/neuron-ui/src/components/SyncStatus/index.tsx index 5735881e07..d425869b0e 100644 --- a/packages/neuron-ui/src/components/SyncStatus/index.tsx +++ b/packages/neuron-ui/src/components/SyncStatus/index.tsx @@ -1,10 +1,11 @@ import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { SyncStatus as SyncStatusEnum, ConnectionStatus, clsx, localNumberFormatter, getSyncLeftTime } from 'utils' -import { Confirming, NewTab } from 'widgets/Icons/icon' +import { Confirming, NewTab, Question } from 'widgets/Icons/icon' import { ReactComponent as UnexpandStatus } from 'widgets/Icons/UnexpandStatus.svg' import { ReactComponent as StartBlock } from 'widgets/Icons/StartBlock.svg' import Tooltip from 'widgets/Tooltip' +import { openExternal } from 'services/remote' import styles from './syncStatus.module.scss' const SyncDetail = ({ @@ -53,7 +54,17 @@ const SyncDetail = ({ ) : null}
-
{t('network-status.left-time')}
+
+ {t('network-status.left-time')}: + {t('network-status.left-time-tip')}
} + className={styles.question} + tipClassName={styles.questionTip} + placement="right-bottom" + > + + +
{getSyncLeftTime(estimate)}
{isLightClient && ( @@ -114,7 +125,34 @@ const SyncStatus = ({ } if (ConnectionStatus.Offline === connectionStatus) { - return {t('sync.sync-failed')} + return ( + + {t('sync.sync-failed')} + + {t('sync.sync-failed-detail')} +    + + + } + trigger="click" + placement="left-bottom" + showTriangle + tipClassName={styles.helpTip} + > + + + + ) } if (SyncStatusEnum.SyncNotStart === syncStatus) { diff --git a/packages/neuron-ui/src/components/SyncStatus/syncStatus.module.scss b/packages/neuron-ui/src/components/SyncStatus/syncStatus.module.scss index 0392091b75..130bb5597b 100755 --- a/packages/neuron-ui/src/components/SyncStatus/syncStatus.module.scss +++ b/packages/neuron-ui/src/components/SyncStatus/syncStatus.module.scss @@ -99,6 +99,37 @@ .title { font-size: 12px; font-weight: 400; + display: flex; + align-items: center; + + .question { + height: 14px; + flex: 1 1 auto; + svg { + margin-left: 4px; + cursor: pointer; + path { + stroke: var(--secondary-text-color); + } + &:hover { + path { + stroke: var(--primary-color); + } + } + } + } + + .questionTip { + top: calc(100% + 28px); + right: -36px; + width: 200px; + left: inherit; + } + + .leftTimeTip { + white-space: normal; + width: 200px; + } } .number { color: var(--third-text-color); @@ -110,8 +141,20 @@ .redDot { position: relative; - color: '#FF1E1E'; + color: var(--error-color); padding-left: 14px; + display: flex; + align-items: center; + + & > div { + height: 14px; + } + + svg { + margin-left: 4px; + cursor: pointer; + } + &::before { content: ''; height: 6px; @@ -122,4 +165,27 @@ top: calc(50% - 3px); background: var(--error-color); } + + .helpTip { + margin-top: 20px; + right: -10px !important; + + & > div:nth-last-child(1) { + right: 12px; + } + } + + .failedDetail { + white-space: normal; + min-width: 250px; + } + .openHelper { + font-weight: 500; + background: inherit; + color: var(--primary-color); + border: none; + padding: 0; + margin: 0; + cursor: pointer; + } } diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index cb311cc300..1842c982ef 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -36,7 +36,8 @@ "set-start-block-number": "Set start block number" }, "migrating": "migrating...", - "left-time": "Left Time" + "left-time": "Time Remaining", + "left-time-tip": "The time remaining is an estimate and is related to the current network and other factors, and is for reference only." }, "import-hardware": { "title": { @@ -141,7 +142,7 @@ "activity": "Activity", "datetime": "Date & Time", "status": "Status", - "amount": "Amount", + "asset": "Asset", "address": "Address", "sent": "Sent", "sending": "Sending", @@ -312,7 +313,7 @@ "name": "Wallet Name", "type": "Type", "balance": "Balance", - "amount": "Amount", + "asset": "Asset", "timestamp": "Time", "status": "Status", "operation": "Operation" @@ -688,7 +689,9 @@ "block-number": "block number", "syncing-balance": "Balance is updating", "slow": "Sync is slow", - "sync-failed": "Sync failed, please check network", + "sync-failed": "Synchronization failed", + "sync-failed-detail": "Synchronization failed due to network node or other problems.", + "learn-more": "Learn More", "sync-not-start": "Sync not started yet, please try to restart Neuron Wallet", "connecting": "Connecting" }, diff --git a/packages/neuron-ui/src/locales/es.json b/packages/neuron-ui/src/locales/es.json index bb7093c4e9..3ac8e1f57f 100644 --- a/packages/neuron-ui/src/locales/es.json +++ b/packages/neuron-ui/src/locales/es.json @@ -35,7 +35,8 @@ "set-start-block-number": "Establecer el número de bloque inicial" }, "migrating": "migrando...", - "left-time": "Tiempo restante" + "left-time": "Tiempo restante", + "left-time-tip": "El tiempo restante es una estimación y está relacionado con la red actual y otros factores, y es solo para referencia." }, "import-hardware": { "title": { @@ -134,7 +135,7 @@ "activity": "Actividad", "datetime": "Fecha y Hora", "status": "Estado", - "amount": "Cantidad", + "asset": "Activos", "address": "Dirección", "sent": "Enviado", "sending": "Enviando", @@ -304,7 +305,7 @@ "name": "Nombre de la Billetera", "type": "Tipo", "balance": "Saldo", - "amount": "Cantidad", + "asset": "activos", "timestamp": "Tiempo", "status": "Estado", "operation": "Operación" @@ -671,7 +672,9 @@ "block-number": "número de bloque", "syncing-balance": "El saldo se está actualizando", "slow": "La sincronización es lenta", - "sync-failed": "Error de sincronización, por favor compruebe la red", + "sync-failed": "sincronización fallida", + "sync-failed-detail": "Debido a nodos de red u otros problemas, la sincronización ha fallado.", + "learn-more": "Saber más", "sync-not-start": "La sincronización no se ha iniciado todavía, por favor intente reiniciar Neuron Wallet", "connecting": "Conectando" }, diff --git a/packages/neuron-ui/src/locales/fr.json b/packages/neuron-ui/src/locales/fr.json index 534e1fd1d2..a609830ca1 100644 --- a/packages/neuron-ui/src/locales/fr.json +++ b/packages/neuron-ui/src/locales/fr.json @@ -36,7 +36,8 @@ "set-start-block-number": "Définir le numéro de bloc de départ" }, "migrating": "migration en cours...", - "left-time": "Temps restant" + "left-time": "Temps restant", + "left-time-tip": "Le temps restant est une estimation et est lié au réseau actuel et à d'autres facteurs, et est fourni à titre indicatif seulement." }, "import-hardware": { "title": { @@ -141,7 +142,7 @@ "activity": "Activité", "datetime": "Date et heure", "status": "Statut", - "amount": "Montant", + "asset": "actifs", "address": "Adresse", "sent": "Envoyé", "sending": "Envoi", @@ -311,7 +312,7 @@ "name": "Nom du Wallet", "type": "Type", "balance": "Solde", - "amount": "Montant", + "asset": "actifs", "timestamp": "Heure", "status": "Statut", "operation": "Opération" @@ -678,7 +679,9 @@ "block-number": "numéro de bloc", "syncing-balance": "La balance est en cours de mise à jour", "slow": "La synchronisation est lente", - "sync-failed": "Échec de la synchronisation, veuillez vérifier le réseau", + "sync-failed": "échec de la synchronisation", + "sync-failed-detail": "En raison de nœuds réseau ou d'autres problèmes, la synchronisation a échoué.", + "learn-more": "En savoir plus", "sync-not-start": "La synchronisation n'a pas encore commencé, veuillez essayer de redémarrer le Wallet Neuron", "connecting": "Connexion en cours" }, diff --git a/packages/neuron-ui/src/locales/zh-tw.json b/packages/neuron-ui/src/locales/zh-tw.json index 4350e2981a..8e61fb1c5d 100644 --- a/packages/neuron-ui/src/locales/zh-tw.json +++ b/packages/neuron-ui/src/locales/zh-tw.json @@ -36,7 +36,8 @@ "set-start-block-number": "設置同步起始區塊" }, "migrating": "正在數據遷移...", - "left-time": "剩余時間" + "left-time": "剩余時間", + "left-time-tip": "剩余時間僅為估計值,與當前網絡和其他因素有關,僅供參考。" }, "import-hardware": { "title": { @@ -135,7 +136,7 @@ "activity": "收支活動", "datetime": "時間", "status": "狀態", - "amount": "金額", + "asset": "資產", "address": "地址", "sent": "已發送", "sending": "正在發送", @@ -307,7 +308,7 @@ "name": "錢包名稱", "type": "類型", "balance": "余額", - "amount": "金額", + "asset": "資產", "timestamp": "時間", "status": "狀態", "operation": "操作" @@ -682,7 +683,9 @@ "block-number": "區塊高度", "syncing-balance": "區塊同步中, 正在獲取最新余額", "slow": "同步緩慢,請檢查網絡", - "sync-failed": "連接失敗, 請檢查網絡", + "sync-failed": "同步失敗", + "sync-failed-detail": "由於網絡節點或其他問題,同步失敗。", + "learn-more": "了解更多", "sync-not-start": "同步尚未開始, 請嘗試重啟", "connecting": "正在連接節點" }, diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index 86b3a03864..d0ad40e458 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -36,7 +36,8 @@ "set-start-block-number": "设置同步起始区块" }, "migrating": "正在数据迁移...", - "left-time": "剩余时间" + "left-time": "剩余时间", + "left-time-tip": "剩余时间仅为估计值,与当前网络和其他因素有关,仅供参考。" }, "import-hardware": { "title": { @@ -134,7 +135,7 @@ "activity": "收支活动", "datetime": "时间", "status": "状态", - "amount": "金额", + "asset": "资产", "address": "地址", "sent": "已发送", "sending": "正在发送", @@ -305,7 +306,7 @@ "name": "钱包名称", "type": "类型", "balance": "余额", - "amount": "金额", + "asset": "资产", "timestamp": "时间", "status": "状态", "operation": "操作" @@ -681,7 +682,9 @@ "block-number": "区块高度", "syncing-balance": "区块同步中, 正在获取最新余额", "slow": "同步缓慢,请检查网络", - "sync-failed": "连接失败, 请检查网络", + "sync-failed": "同步失败", + "sync-failed-detail": "由于网络节点或其他问题,同步失败。", + "learn-more": "了解更多", "sync-not-start": "同步尚未开始, 请尝试重启", "connecting": "正在连接节点" }, diff --git a/packages/neuron-ui/src/types/App/index.d.ts b/packages/neuron-ui/src/types/App/index.d.ts index 76ce82827b..e9902f710c 100644 --- a/packages/neuron-ui/src/types/App/index.d.ts +++ b/packages/neuron-ui/src/types/App/index.d.ts @@ -19,6 +19,7 @@ declare namespace State { data: string } assetAccountType?: 'CKB' | 'sUDT' | string + daoCapacity?: string } interface DetailedInput { diff --git a/packages/neuron-ui/src/widgets/Icons/Question.svg b/packages/neuron-ui/src/widgets/Icons/Question.svg new file mode 100644 index 0000000000..c2e6b78f5c --- /dev/null +++ b/packages/neuron-ui/src/widgets/Icons/Question.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/neuron-ui/src/widgets/Icons/icon.tsx b/packages/neuron-ui/src/widgets/Icons/icon.tsx index 806510ad82..c8a1f9bd2e 100644 --- a/packages/neuron-ui/src/widgets/Icons/icon.tsx +++ b/packages/neuron-ui/src/widgets/Icons/icon.tsx @@ -57,6 +57,7 @@ import { ReactComponent as DeleteSvg } from './Delete.svg' import { ReactComponent as ImportKeystoreSvg } from './SoftWalletImportKeystore.svg' import { ReactComponent as ImportHardwareSvg } from './HardWalletImport.svg' import { ReactComponent as DepositTimeSortSvg } from './DepositTimeSort.svg' +import { ReactComponent as QuestionSvg } from './Question.svg' import styles from './icon.module.scss' @@ -130,3 +131,4 @@ export const Delete = WrapSvg(DeleteSvg) export const ImportKeystore = WrapSvg(ImportKeystoreSvg) export const ImportHardware = WrapSvg(ImportHardwareSvg) export const DepositTimeSort = WrapSvg(DepositTimeSortSvg) +export const Question = WrapSvg(QuestionSvg) diff --git a/packages/neuron-ui/src/widgets/Tooltip/tooltip.module.scss b/packages/neuron-ui/src/widgets/Tooltip/tooltip.module.scss index 6c4d88ffd1..111bd6d1d3 100644 --- a/packages/neuron-ui/src/widgets/Tooltip/tooltip.module.scss +++ b/packages/neuron-ui/src/widgets/Tooltip/tooltip.module.scss @@ -251,25 +251,25 @@ $placements: left, right, top, bottom, left-top, right-top, left-bottom, right-b .tipWithNode { position: relative; - .tip { + > .tip { @include tip; } &[data-type='always-dark'] { - .tip { + > .tip { background: #000000; color: #ffffff; } } &[data-trigger='click'] { - &[data-tip-show='true'] .tip { + &[data-tip-show='true'] > .tip { display: block; } } @each $placement in $placements { - @include tip-placement($placement, ' .tip'); + @include tip-placement($placement, '> .tip'); } &[data-trigger='hover'] { @@ -286,14 +286,14 @@ $placements: left, right, top, bottom, left-top, right-top, left-bottom, right-b &[data-has-trigger='true'] { &[data-placement='bottom'] { &:not([data-trigger-next-to-child='true']) { - & .tip { + & > .tip { top: calc(100% + 20px); } } } &[data-placement='top'] { &[data-trigger-next-to-child='true'] { - & .tip { + & > .tip { margin-bottom: 2px; } } diff --git a/packages/neuron-wallet/.env b/packages/neuron-wallet/.env index f32862920f..0f76d563dc 100644 --- a/packages/neuron-wallet/.env +++ b/packages/neuron-wallet/.env @@ -117,5 +117,5 @@ DAO_CODE_HASH=0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e MULTISIG_CODE_HASH=0x5c5069eb0857efc65e1bca0c07df34c31663b3622fd3876c876320fc9634e2a8 # CKB NODE OPTIONS -CKB_NODE_ASSUME_VALID_TARGET='0x9443ad8da9172d484367bc5467988cba7a0c46028398309edfdda7d2d79be897' -CKB_NODE_DATA_SIZE=53 +CKB_NODE_ASSUME_VALID_TARGET='0x88b9835cfcb167eddf63f85066debfb9b877c0f9bd51bb1abb1b0eaaa00f70f5' +CKB_NODE_DATA_SIZE=55 diff --git a/packages/neuron-wallet/package.json b/packages/neuron-wallet/package.json index 77479adeae..cad2f98bff 100644 --- a/packages/neuron-wallet/package.json +++ b/packages/neuron-wallet/package.json @@ -3,7 +3,7 @@ "productName": "Neuron", "description": "CKB Neuron Wallet", "homepage": "https://www.nervos.org/", - "version": "0.114.3", + "version": "0.116.0", "private": true, "author": { "name": "Nervos Core Dev", @@ -97,7 +97,7 @@ "electron-builder": "24.9.1", "electron-devtools-installer": "3.2.0", "jest-when": "3.6.0", - "neuron-ui": "0.114.3", + "neuron-ui": "0.116.0", "typescript": "5.3.3" } } diff --git a/packages/neuron-wallet/src/block-sync-renderer/index.ts b/packages/neuron-wallet/src/block-sync-renderer/index.ts index cf690f014b..7bada7dac1 100644 --- a/packages/neuron-wallet/src/block-sync-renderer/index.ts +++ b/packages/neuron-wallet/src/block-sync-renderer/index.ts @@ -9,7 +9,7 @@ import DataUpdateSubject from '../models/subjects/data-update' import AddressCreatedSubject from '../models/subjects/address-created-subject' import WalletDeletedSubject from '../models/subjects/wallet-deleted-subject' import TxDbChangedSubject from '../models/subjects/tx-db-changed-subject' -import { LumosCellQuery, LumosCell } from './sync/synchronizer' +import { type Cell, type QueryOptions } from '@ckb-lumos/base' import { WorkerMessage, StartParams, QueryIndexerParams } from './task' import logger from '../utils/logger' import CommonUtils from '../utils/common' @@ -88,7 +88,7 @@ export const switchToNetwork = async (newNetwork: Network, reconnected = false, await resetSyncTaskQueue.asyncPush(shouldSync) } -export const queryIndexer = async (query: LumosCellQuery): Promise => { +export const queryIndexer = async (query: QueryOptions): Promise => { const _child = child if (!_child) { return [] @@ -102,7 +102,7 @@ export const queryIndexer = async (query: LumosCellQuery): Promise return registerRequest(_child, msg).catch(err => { logger.error(`Sync:\tfailed to register query indexer task`, err) return [] - }) as Promise + }) as Promise } export const createBlockSyncTask = async () => { @@ -177,7 +177,6 @@ export const createBlockSyncTask = async () => { genesisHash: network.genesisHash, url: network.remote, addressMetas, - indexerUrl: network.remote, nodeType: network.type, } const msg: Required> = { type: 'call', channel: 'start', id: requestId++, message } diff --git a/packages/neuron-wallet/src/block-sync-renderer/sync/full-synchronizer.ts b/packages/neuron-wallet/src/block-sync-renderer/sync/full-synchronizer.ts index 972d37e5f0..eecace1ccf 100644 --- a/packages/neuron-wallet/src/block-sync-renderer/sync/full-synchronizer.ts +++ b/packages/neuron-wallet/src/block-sync-renderer/sync/full-synchronizer.ts @@ -10,8 +10,8 @@ import IndexerCacheService from './indexer-cache-service' export default class FullSynchronizer extends Synchronizer { private rpcService: RpcService - constructor(addresses: Address[], nodeUrl: string, indexerUrl: string, nodeType: NetworkType) { - super({ addresses, nodeUrl, indexerUrl }) + constructor(addresses: Address[], nodeUrl: string, nodeType: NetworkType) { + super({ addresses, nodeUrl }) this.rpcService = new RpcService(nodeUrl, nodeType) } diff --git a/packages/neuron-wallet/src/block-sync-renderer/sync/light-synchronizer.ts b/packages/neuron-wallet/src/block-sync-renderer/sync/light-synchronizer.ts index f87dddf99b..eb1f52bcf0 100644 --- a/packages/neuron-wallet/src/block-sync-renderer/sync/light-synchronizer.ts +++ b/packages/neuron-wallet/src/block-sync-renderer/sync/light-synchronizer.ts @@ -23,17 +23,14 @@ import NetworksService from '../../services/networks' import { getConnection } from '../../database/chain/connection' const unpackGroup = molecule.vector(blockchain.OutPoint) +const THRESHOLD_BLOCK_NUMBER_IN_DIFF_WALLET = 100_000 export default class LightSynchronizer extends Synchronizer { private lightRpc: LightRPC private addressMetas: AddressMeta[] constructor(addresses: Address[], nodeUrl: string) { - super({ - addresses, - nodeUrl, - indexerUrl: nodeUrl, - }) + super({ addresses, nodeUrl }) this.lightRpc = new LightRPC(nodeUrl) this.addressMetas = addresses.map(address => AddressMeta.fromObject(address)) // fetch some dep cell @@ -171,6 +168,18 @@ export default class LightSynchronizer extends Synchronizer { await getConnection('light').manager.save(updatedSyncProgress, { chunk: 100 }) } + private static async getWalletsSyncedMinBlockNumber() { + const walletMinBlockNumber = await SyncProgressService.getWalletMinLocalSavedBlockNumber() + const wallets = await WalletService.getInstance().getAll() + return wallets.reduce>( + (pre, cur) => ({ + ...pre, + [cur.id]: Math.max(parseInt(cur.startBlockNumber ?? '0x0'), walletMinBlockNumber?.[cur.id] ?? 0), + }), + {} + ) + } + private async initSyncProgress(appendScripts: AppendScript[] = []) { if (!this.addressMetas.length && !appendScripts.length) { return @@ -178,13 +187,13 @@ export default class LightSynchronizer extends Synchronizer { const existSyncArgses = await SyncProgressService.getExistingSyncArgses() const syncScripts = await this.lightRpc.getScripts() const retainedSyncScripts = syncScripts.filter(v => existSyncArgses.has(v.script.args)) - const existSyncscripts: Record = {} + const existSyncScripts: Record = {} retainedSyncScripts.forEach(v => { - existSyncscripts[scriptToHash(v.script)] = v + existSyncScripts[scriptToHash(v.script)] = v }) + const walletMinBlockNumber = await LightSynchronizer.getWalletsSyncedMinBlockNumber() const currentWalletId = WalletService.getInstance().getCurrent()?.id const allScripts = this.addressMetas - .filter(v => (currentWalletId ? v.walletId === currentWalletId : true)) .map(addressMeta => { const lockScripts = [ addressMeta.generateDefaultLockScript(), @@ -198,32 +207,32 @@ export default class LightSynchronizer extends Synchronizer { })) }) .flat() - const walletMinBlockNumber = await SyncProgressService.getWalletMinLocalSavedBlockNumber() - const wallets = await WalletService.getInstance().getAll() - const walletStartBlockMap = wallets.reduce>( - (pre, cur) => ({ ...pre, [cur.id]: cur.startBlockNumber }), - {} - ) + .filter(v => { + return ( + !currentWalletId || + v.walletId === currentWalletId || + walletMinBlockNumber[v.walletId] > + walletMinBlockNumber[currentWalletId] - THRESHOLD_BLOCK_NUMBER_IN_DIFF_WALLET + ) + }) const otherTypeSyncBlockNumber = await SyncProgressService.getOtherTypeSyncBlockNumber() const addScripts = [ ...allScripts - .filter( - v => - !existSyncscripts[scriptToHash(v.script)] || - +existSyncscripts[scriptToHash(v.script)].blockNumber < +(walletStartBlockMap[v.walletId] ?? 0) - ) - .map(v => { - const blockNumber = Math.max( - parseInt(walletStartBlockMap[v.walletId] ?? '0x0'), - walletMinBlockNumber?.[v.walletId] ?? 0 + .filter(v => { + const scriptHash = scriptToHash(v.script) + return ( + !existSyncScripts[scriptHash] || + +existSyncScripts[scriptHash].blockNumber < walletMinBlockNumber[v.walletId] ) + }) + .map(v => { return { ...v, - blockNumber: `0x${blockNumber.toString(16)}`, + blockNumber: `0x${walletMinBlockNumber[v.walletId].toString(16)}`, } }), ...appendScripts - .filter(v => !existSyncscripts[scriptToHash(v.script)]) + .filter(v => !existSyncScripts[scriptToHash(v.script)]) .map(v => ({ ...v, blockNumber: `0x${(otherTypeSyncBlockNumber[scriptToHash(v.script)] ?? 0).toString(16)}`, diff --git a/packages/neuron-wallet/src/block-sync-renderer/sync/queue.ts b/packages/neuron-wallet/src/block-sync-renderer/sync/queue.ts index 027bc14967..2c5d5008a5 100644 --- a/packages/neuron-wallet/src/block-sync-renderer/sync/queue.ts +++ b/packages/neuron-wallet/src/block-sync-renderer/sync/queue.ts @@ -28,7 +28,6 @@ export default class Queue { #lockHashes: string[] #url: string // ckb node #nodeType: NetworkType - #indexerUrl: string #addresses: AddressInterface[] #rpcService: RpcService #indexerConnector: Synchronizer | undefined @@ -39,9 +38,8 @@ export default class Queue { #anyoneCanPayLockHashes: string[] #assetAccountInfo: AssetAccountInfo - constructor(url: string, addresses: AddressInterface[], indexerUrl: string, nodeType: NetworkType) { + constructor(url: string, addresses: AddressInterface[], nodeType: NetworkType) { this.#url = url - this.#indexerUrl = indexerUrl this.#addresses = addresses this.#rpcService = new RpcService(url, nodeType) this.#nodeType = nodeType @@ -70,7 +68,7 @@ export default class Queue { if (this.#url === BUNDLED_LIGHT_CKB_URL) { this.#indexerConnector = new LightSynchronizer(this.#addresses, this.#url) } else { - this.#indexerConnector = new FullSynchronizer(this.#addresses, this.#url, this.#indexerUrl, this.#nodeType) + this.#indexerConnector = new FullSynchronizer(this.#addresses, this.#url, this.#nodeType) } await this.#indexerConnector!.connect() } catch (error) { diff --git a/packages/neuron-wallet/src/block-sync-renderer/sync/synchronizer.ts b/packages/neuron-wallet/src/block-sync-renderer/sync/synchronizer.ts index b8d1a789a2..cd6b840581 100644 --- a/packages/neuron-wallet/src/block-sync-renderer/sync/synchronizer.ts +++ b/packages/neuron-wallet/src/block-sync-renderer/sync/synchronizer.ts @@ -1,7 +1,7 @@ import { Subject } from 'rxjs' import { queue, QueueObject } from 'async' +import { type QueryOptions } from '@ckb-lumos/base' import { Indexer as CkbIndexer, CellCollector } from '@ckb-lumos/ckb-indexer' -import { QueryOptions } from '@ckb-lumos/base' import AddressMeta from '../../database/address/meta' import { Address } from '../../models/address' import { SyncAddressType } from '../../database/chain/entities/sync-progress' @@ -14,34 +14,6 @@ export interface BlockTips { indexerTipNumber: number | undefined } -export interface LumosCellQuery { - lock: CKBComponents.Script | null - type: CKBComponents.Script | null - data: string | null -} - -export interface LumosCell { - blockHash: string - outPoint: { - txHash: string - index: string - } - cellOutput: { - capacity: string - lock: { - codeHash: string - args: string - hashType: string - } - type?: { - codeHash: string - args: string - hashType: string - } - } - data?: string -} - export interface AppendScript { walletId: string script: CKBComponents.Script @@ -57,7 +29,7 @@ export abstract class Synchronizer { protected processingBlockNumber?: string protected addressesByWalletId: Map = new Map() protected pollingIndexer: boolean = false - private indexerQueryQueue: QueueObject | undefined + private indexerQueryQueue: QueueObject | undefined protected _needGenerateAddress: boolean = false abstract connect(): Promise @@ -68,8 +40,8 @@ export abstract class Synchronizer { // do nothing } - constructor({ addresses, nodeUrl, indexerUrl }: { addresses: Address[]; nodeUrl: string; indexerUrl: string }) { - this.indexer = new CkbIndexer(nodeUrl, indexerUrl) + constructor({ addresses, nodeUrl }: { addresses: Address[]; nodeUrl: string }) { + this.indexer = new CkbIndexer(nodeUrl) this.addressesByWalletId = addresses .map(address => AddressMeta.fromObject(address)) .reduce((addressesByWalletId, addressMeta) => { @@ -155,7 +127,7 @@ export abstract class Synchronizer { return false } - public async getLiveCellsByScript(query: LumosCellQuery) { + public async getLiveCellsByScript(query: QueryOptions) { return new Promise((resolve, reject) => { this.indexerQueryQueue!.push(query, (err: any, result: unknown) => { if (err) { @@ -166,7 +138,7 @@ export abstract class Synchronizer { }) } - private async collectLiveCellsByScript(query: LumosCellQuery) { + private async collectLiveCellsByScript(query: QueryOptions) { const { lock, type, data } = query if (!lock && !type) { throw new Error('at least one parameter is required') diff --git a/packages/neuron-wallet/src/block-sync-renderer/task.ts b/packages/neuron-wallet/src/block-sync-renderer/task.ts index e10291360e..dc937616dd 100644 --- a/packages/neuron-wallet/src/block-sync-renderer/task.ts +++ b/packages/neuron-wallet/src/block-sync-renderer/task.ts @@ -1,4 +1,4 @@ -import type { LumosCellQuery } from './sync/synchronizer' +import { type QueryOptions } from '@ckb-lumos/base' import initConnection from '../database/chain/ormconfig' import { register as registerTxStatusListener } from './tx-status-listener' import SyncQueue from './sync/queue' @@ -31,11 +31,10 @@ export interface StartParams { genesisHash: string url: SyncQueueParams[0] addressMetas: SyncQueueParams[1] - indexerUrl: SyncQueueParams[2] - nodeType: SyncQueueParams[3] + nodeType: SyncQueueParams[2] } -export type QueryIndexerParams = LumosCellQuery +export type QueryIndexerParams = QueryOptions export const listener = async ({ type, id, channel, message }: WorkerMessage) => { if (type === 'kill') { @@ -63,7 +62,7 @@ export const listener = async ({ type, id, channel, message }: WorkerMessage) => try { await initConnection(message.genesisHash) - syncQueue = new SyncQueue(message.url, message.addressMetas, message.indexerUrl, message.nodeType) + syncQueue = new SyncQueue(message.url, message.addressMetas, message.nodeType) syncQueue.start() } catch (err) { logger.error(`Block Sync Task:\t`, err) diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts index 29bff5030e..db66030bfa 100644 --- a/packages/neuron-wallet/src/controllers/wallets.ts +++ b/packages/neuron-wallet/src/controllers/wallets.ts @@ -168,8 +168,8 @@ export default class WalletsController { const keystoreObject = Keystore.fromJson(keystore) const masterPrivateKey = keystoreObject.extendedPrivateKey(password) const masterKeychain = new Keychain( - Buffer.from(masterPrivateKey.privateKey, 'hex'), - Buffer.from(masterPrivateKey.chainCode, 'hex') + Buffer.from(bytes.bytify(masterPrivateKey.privateKey)), + Buffer.from(bytes.bytify(masterPrivateKey.chainCode)) ) const accountKeychain = masterKeychain.derivePath(AccountExtendedPublicKey.ckbAccountPath) const accountExtendedPublicKey = new AccountExtendedPublicKey( diff --git a/packages/neuron-wallet/src/models/blake2b.ts b/packages/neuron-wallet/src/models/blake2b.ts index 7dc99b6c7e..33c01368df 100644 --- a/packages/neuron-wallet/src/models/blake2b.ts +++ b/packages/neuron-wallet/src/models/blake2b.ts @@ -1,6 +1,8 @@ import { CKBHasher } from '@ckb-lumos/base/lib/utils' import { bytes } from '@ckb-lumos/codec' +export const BLAKE160_HEX_LENGTH = 42 + export default class Blake2b { private blake2b: CKBHasher diff --git a/packages/neuron-wallet/src/models/chain/live-cell.ts b/packages/neuron-wallet/src/models/chain/live-cell.ts index c57fb25fed..9a7cda74a2 100644 --- a/packages/neuron-wallet/src/models/chain/live-cell.ts +++ b/packages/neuron-wallet/src/models/chain/live-cell.ts @@ -1,6 +1,6 @@ import Script, { ScriptHashType } from './script' import OutPoint from './out-point' -import { LumosCell } from '../../block-sync-renderer/sync/synchronizer' +import { type Cell, type OutPoint as IOutPoint } from '@ckb-lumos/base' const LUMOS_HASH_TYPE_MAP: Record = { type: ScriptHashType.Type, @@ -8,6 +8,8 @@ const LUMOS_HASH_TYPE_MAP: Record = { data: ScriptHashType.Data, } +export type CellWithOutPoint = Cell & { outPoint: IOutPoint } + export default class LiveCell { public txHash: string public outputIndex: string @@ -54,7 +56,7 @@ export default class LiveCell { return undefined } - public static fromLumos(cell: LumosCell): LiveCell { + public static fromLumos(cell: CellWithOutPoint): LiveCell { const type = cell.cellOutput.type ? new Script( cell.cellOutput.type.codeHash, diff --git a/packages/neuron-wallet/src/models/chain/transaction.ts b/packages/neuron-wallet/src/models/chain/transaction.ts index 1f4a0d1223..a8942bb43c 100644 --- a/packages/neuron-wallet/src/models/chain/transaction.ts +++ b/packages/neuron-wallet/src/models/chain/transaction.ts @@ -82,6 +82,8 @@ export default class Transaction { public signatures: Signatures = {} public assetAccountType?: AssetAccountType + public daoCapacity?: string + constructor( version: string, cellDeps: CellDep[] = [], @@ -106,7 +108,8 @@ export default class Transaction { sudtInfo?: SudtInfo, nftType?: NFTInfo, signatures: Signatures = {}, - assetAccountType?: AssetAccountType + assetAccountType?: AssetAccountType, + daoCapacity?: string ) { this.cellDeps = cellDeps this.headerDeps = headerDeps @@ -133,6 +136,7 @@ export default class Transaction { this.sudtInfo = sudtInfo this.nftInfo = nftType this.signatures = signatures + this.daoCapacity = daoCapacity TypeCheckerUtils.hashChecker(...this.headerDeps, this.blockHash) TypeCheckerUtils.numberChecker( this.version, @@ -171,6 +175,7 @@ export default class Transaction { nftInfo, signatures = {}, assetAccountType, + daoCapacity, }: { version: string cellDeps?: CellDep[] @@ -196,6 +201,7 @@ export default class Transaction { nftInfo?: NFTInfo signatures?: Signatures assetAccountType?: AssetAccountType + daoCapacity?: string }): Transaction { return new Transaction( version, @@ -226,7 +232,8 @@ export default class Transaction { sudtInfo, nftInfo, signatures, - assetAccountType + assetAccountType, + daoCapacity ) } diff --git a/packages/neuron-wallet/src/models/multisig.ts b/packages/neuron-wallet/src/models/multisig.ts index 39f2153d41..6d62aaf6a6 100644 --- a/packages/neuron-wallet/src/models/multisig.ts +++ b/packages/neuron-wallet/src/models/multisig.ts @@ -1,6 +1,8 @@ import { MultisigPrefixError } from '../exceptions' -import Blake2b from './blake2b' import SystemScriptInfo from './system-script-info' +import { since } from '@ckb-lumos/base' +import { bytes, number } from '@ckb-lumos/codec' +import Blake2b, { BLAKE160_HEX_LENGTH } from './blake2b' export interface MultisigPrefix { S: string @@ -11,7 +13,7 @@ export interface MultisigPrefix { export default class Multisig { // 1 epoch = 4h = 240min - EPOCH_MINUTES = 240 + static EPOCH_MINUTES = 240 static defaultS: string = '00' @@ -24,32 +26,22 @@ export default class Multisig { static hash(blake160s: string[], r: number = 0, m: number = 1, n: number = 1): string { const serializeResult = Multisig.serialize(blake160s, r, m, n) - return Blake2b.digest(serializeResult).slice(0, 42) + return Blake2b.digest(serializeResult).slice(0, BLAKE160_HEX_LENGTH) } - since(minutes: number, headerEpoch: string): string { + static since(minutes: number, headerEpoch: string): string { if (minutes < 0) { throw new Error("minutes to calculate since can't be less than 0") } - const currentEpochInfo = this.parseEpoch(BigInt(headerEpoch)) - const totalMinutes = - minutes + - parseInt( - ( - (parseInt(currentEpochInfo.index.toString()) / parseInt(currentEpochInfo.length.toString())) * - this.EPOCH_MINUTES - ).toString() - ) + const currentEpoch = since.parseEpoch(headerEpoch) + const totalMinutes = minutes + Math.floor((currentEpoch.index / currentEpoch.length) * this.EPOCH_MINUTES) const leftMinutes = totalMinutes % this.EPOCH_MINUTES - const epochs: bigint = - BigInt(parseInt((totalMinutes / this.EPOCH_MINUTES).toString(), 10)) + currentEpochInfo.number - const result = this.epochSince(BigInt(this.EPOCH_MINUTES), BigInt(leftMinutes), epochs) - const buf = Buffer.alloc(8) - buf.writeBigUInt64LE(result) - return `0x${buf.toString('hex')}` + const epochs = Math.floor(totalMinutes / this.EPOCH_MINUTES) + currentEpoch.number + const result = this.epochSince(BigInt(this.EPOCH_MINUTES), BigInt(leftMinutes), BigInt(epochs)) + return bytes.hexify(number.Uint64LE.pack(result)) } - args(blake160: string, minutes: number, headerEpoch: string): string { + static args(blake160: string, minutes: number, headerEpoch: string): string { return Multisig.hash([blake160]) + this.since(minutes, headerEpoch).slice(2) } @@ -57,25 +49,14 @@ export default class Multisig { return SystemScriptInfo.generateMultiSignScript(Multisig.hash(blake160s, r, m, n)) } - parseSince(args: string): bigint { - const str = args.slice(42) - const buf = Buffer.from(str, 'hex') - const sin: bigint = buf.readBigUInt64LE() - return sin + static parseSince(args: string): bigint { + return number.Uint64LE.unpack(`0x${args.slice(BLAKE160_HEX_LENGTH)}`).toBigInt() } - private epochSince(length: bigint, index: bigint, number: bigint): bigint { + private static epochSince(length: bigint, index: bigint, number: bigint): bigint { return (BigInt(0x20) << BigInt(56)) + (length << BigInt(40)) + (index << BigInt(24)) + number } - private parseEpoch(epoch: bigint) { - return { - length: (epoch >> BigInt(40)) & BigInt(0xffff), - index: (epoch >> BigInt(24)) & BigInt(0xffff), - number: epoch & BigInt(0xffffff), - } - } - private static getMultisigParamsHex(v: number) { if (v < 0 || v > 255) { throw new MultisigPrefixError() diff --git a/packages/neuron-wallet/src/services/live-cell-service.ts b/packages/neuron-wallet/src/services/live-cell-service.ts index 4b7766d3db..3fe38814a6 100644 --- a/packages/neuron-wallet/src/services/live-cell-service.ts +++ b/packages/neuron-wallet/src/services/live-cell-service.ts @@ -1,7 +1,7 @@ import Script from '../models/chain/script' -import LiveCell from '../models/chain/live-cell' +import LiveCell, { CellWithOutPoint } from '../models/chain/live-cell' import { queryIndexer } from '../block-sync-renderer/index' -import { LumosCell, LumosCellQuery } from '../block-sync-renderer/sync/synchronizer' +import { type QueryOptions } from '@ckb-lumos/base' export default class LiveCellService { private static instance: LiveCellService @@ -20,14 +20,14 @@ export default class LiveCellService { lock: Script | null, type: Script | null, data: string | null - ): Promise { + ): Promise { if (!lock && !type) { throw new Error('at least one parameter is required') } - const query: LumosCellQuery = { lock, type, data } - const liveCells: LumosCell[] = await queryIndexer(query) - return liveCells + const query = { lock, type, data } as QueryOptions + const liveCells = await queryIndexer(query) + return liveCells as CellWithOutPoint[] } public async getOneByLockScriptAndTypeScript(lock: Script | null, type: Script | null) { diff --git a/packages/neuron-wallet/src/services/settings.ts b/packages/neuron-wallet/src/services/settings.ts index d482390249..f836ba5e0e 100644 --- a/packages/neuron-wallet/src/services/settings.ts +++ b/packages/neuron-wallet/src/services/settings.ts @@ -88,7 +88,7 @@ export default class SettingsService extends Store { private generateEncryptString(str: string) { if (safeStorage.isEncryptionAvailable()) { - return safeStorage.encryptString(str).toString('utf-8') + return safeStorage.encryptString(str).toString('hex') } const hash = crypto.createHash('sha256') hash.update(str) @@ -108,6 +108,9 @@ export default class SettingsService extends Store { } verifyLockWindowPassword(password: string) { + if (safeStorage.isEncryptionAvailable()) { + return safeStorage.decryptString(Buffer.from(this.lockWindowInfo.encryptedPassword!, 'hex')) === password + } const encryptedPassword = this.generateEncryptString(password) return SettingsService.getInstance().lockWindowInfo.encryptedPassword === encryptedPassword } diff --git a/packages/neuron-wallet/src/services/sign-message.ts b/packages/neuron-wallet/src/services/sign-message.ts index 8d3bfd562d..aeaf32dca7 100644 --- a/packages/neuron-wallet/src/services/sign-message.ts +++ b/packages/neuron-wallet/src/services/sign-message.ts @@ -6,6 +6,7 @@ import { ec as EC } from 'elliptic' import { AddressNotFound } from '../exceptions' import HardwareWalletService from './hardware' import AddressParser from '../models/address-parser' +import { bytes } from '@ckb-lumos/codec' export default class SignMessage { static GENERATE_COUNT = 100 @@ -91,8 +92,8 @@ export default class SignMessage { private static getPrivateKey(wallet: Wallet, path: string, password: string): string { const masterPrivateKey = wallet.loadKeystore().extendedPrivateKey(password) const masterKeychain = new Keychain( - Buffer.from(masterPrivateKey.privateKey, 'hex'), - Buffer.from(masterPrivateKey.chainCode, 'hex') + Buffer.from(bytes.bytify(masterPrivateKey.privateKey)), + Buffer.from(bytes.bytify(masterPrivateKey.chainCode)) ) return `0x${masterKeychain.derivePath(path).privateKey.toString('hex')}` diff --git a/packages/neuron-wallet/src/services/tx/transaction-generator.ts b/packages/neuron-wallet/src/services/tx/transaction-generator.ts index 3164d353cc..78746dc8b5 100644 --- a/packages/neuron-wallet/src/services/tx/transaction-generator.ts +++ b/packages/neuron-wallet/src/services/tx/transaction-generator.ts @@ -212,7 +212,7 @@ export class TransactionGenerator { if (date) { const blake160 = lockScript.args const minutes: number = +((BigInt(date) - BigInt(tipHeaderTimestamp)) / BigInt(1000 * 60)).toString() - const script = SystemScriptInfo.generateMultiSignScript(new Multisig().args(blake160, +minutes, tipHeaderEpoch)) + const script = SystemScriptInfo.generateMultiSignScript(Multisig.args(blake160, +minutes, tipHeaderEpoch)) output.setLock(script) output.setMultiSignBlake160(script.args.slice(0, 42)) } @@ -333,7 +333,7 @@ export class TransactionGenerator { const blake160 = lockScript.args const minutes: number = +((BigInt(date) - BigInt(tipHeaderTimestamp)) / BigInt(1000 * 60)).toString() const script: Script = SystemScriptInfo.generateMultiSignScript( - new Multisig().args(blake160, minutes, tipHeaderEpoch) + Multisig.args(blake160, minutes, tipHeaderEpoch) ) output.setLock(script) output.setMultiSignBlake160(script.args.slice(0, 42)) @@ -625,7 +625,7 @@ export class TransactionGenerator { lock: lockScript, }) - const since = new Multisig().parseSince(prevOutput.lock.args) + const since = Multisig.parseSince(prevOutput.lock.args) const input = new Input(outPoint, since.toString(), prevOutput.capacity, prevOutput.lock) const tx = Transaction.fromObject({ diff --git a/packages/neuron-wallet/src/services/tx/transaction-service.ts b/packages/neuron-wallet/src/services/tx/transaction-service.ts index eb5cf93dd3..8b43057ed9 100644 --- a/packages/neuron-wallet/src/services/tx/transaction-service.ts +++ b/packages/neuron-wallet/src/services/tx/transaction-service.ts @@ -256,6 +256,7 @@ export class TransactionsService { .addSelect('input.transactionHash', 'transactionHash') .addSelect('input.outPointTxHash', 'outPointTxHash') .addSelect('input.outPointIndex', 'outPointIndex') + .addSelect('input.data', 'data') .where( ` input.transactionHash IN (:...txHashes) AND @@ -266,7 +267,13 @@ export class TransactionsService { walletId: params.walletID, } ) - .getRawMany() + .getRawMany<{ + capacity: string + transactionHash: string + outPointTxHash: string + outPointIndex: string + data: string + }>() const outputs = await connection .getRepository(OutputEntity) @@ -284,7 +291,7 @@ export class TransactionsService { walletId: params.walletID, } ) - .getRawMany() + .getRawMany<{ capacity: string; transactionHash: string; daoData: string }>() const assetAccountInputs = await connection .getRepository(InputEntity) @@ -373,13 +380,17 @@ export class TransactionsService { ).filter(o => inputPreviousTxHashes.includes(o.txHash)) const sums = new Map() - const daoFlag = new Map() + const daoInfo = new Map() outputs.map(o => { const s = sums.get(o.transactionHash) || BigInt(0) sums.set(o.transactionHash, s + BigInt(o.capacity)) if (o.daoData) { - daoFlag.set(o.transactionHash, true) + if (daoInfo.has(o.transactionHash)) { + daoInfo.get(o.transactionHash)!.outputs.push(o) + } else { + daoInfo.set(o.transactionHash, { inputs: [], outputs: [o] }) + } } }) @@ -391,7 +402,11 @@ export class TransactionsService { return dc.txHash === i.outPointTxHash && dc.index === i.outPointIndex }) if (result) { - daoFlag.set(i.transactionHash, true) + if (daoInfo.has(i.transactionHash)) { + daoInfo.get(i.transactionHash)!.inputs.push(i) + } else { + daoInfo.set(i.transactionHash, { inputs: [i], outputs: [] }) + } } }) @@ -477,6 +492,16 @@ export class TransactionsService { nftInfo = { type: NFTType.Receive, data: receiveNFTCell.typeArgs! } } + const txDaoInfo = daoInfo.get(tx.hash) + let daoCapacity: string | undefined + if (txDaoInfo) { + if (txDaoInfo.inputs.length && !txDaoInfo.outputs.length) { + daoCapacity = txDaoInfo.inputs.reduce((pre, cur) => BigInt(cur.capacity) + pre, BigInt(value)).toString() + } else if (!txDaoInfo.inputs.length && txDaoInfo.outputs.length) { + daoCapacity = `-${txDaoInfo.outputs.reduce((pre, cur) => BigInt(cur.capacity) + pre, BigInt(0)).toString()}` + } + } + return Transaction.fromObject({ timestamp: tx.timestamp, value: value.toString(), @@ -484,7 +509,7 @@ export class TransactionsService { version: tx.version, type: txType, assetAccountType: assetAccountType, - nervosDao: daoFlag.get(tx.hash!), + nervosDao: !!txDaoInfo, status: tx.status, description: tx.description, createdAt: tx.createdAt, @@ -492,6 +517,7 @@ export class TransactionsService { blockNumber: tx.blockNumber, sudtInfo: sudtInfo, nftInfo: nftInfo, + daoCapacity, }) }) ) diff --git a/packages/neuron-wallet/tests/block-sync-renderer/full-synchronizer.test.ts b/packages/neuron-wallet/tests/block-sync-renderer/full-synchronizer.test.ts index b4f2cd19fd..b4af6a23a4 100644 --- a/packages/neuron-wallet/tests/block-sync-renderer/full-synchronizer.test.ts +++ b/packages/neuron-wallet/tests/block-sync-renderer/full-synchronizer.test.ts @@ -87,14 +87,12 @@ describe('unit tests for IndexerConnector', () => { }) describe('#constructor', () => { - const STUB_URI = 'stub_uri' - describe('when init with indexer folder path', () => { beforeEach(() => { - new stubbedFullSynchronizer([], nodeUrl, STUB_URI) + new stubbedFullSynchronizer([], nodeUrl) }) it('inits lumos indexer with a node url and indexer folder path', () => { - expect(stubbedIndexerConstructor).toHaveBeenCalledWith(nodeUrl, STUB_URI) + expect(stubbedIndexerConstructor).toHaveBeenCalledWith(nodeUrl) }) }) describe('when init without indexer folder path', () => { @@ -102,7 +100,7 @@ describe('unit tests for IndexerConnector', () => { new stubbedFullSynchronizer([], nodeUrl) }) it('inits mercury indexer with a node url and a default port', () => { - expect(stubbedIndexerConstructor).toHaveBeenCalledWith(nodeUrl, STUB_URI) + expect(stubbedIndexerConstructor).toHaveBeenCalledWith(nodeUrl) }) }) }) diff --git a/packages/neuron-wallet/tests/block-sync-renderer/index/createBlockSyncTask.test.ts b/packages/neuron-wallet/tests/block-sync-renderer/index/createBlockSyncTask.test.ts index 61da50c476..d53022eb38 100644 --- a/packages/neuron-wallet/tests/block-sync-renderer/index/createBlockSyncTask.test.ts +++ b/packages/neuron-wallet/tests/block-sync-renderer/index/createBlockSyncTask.test.ts @@ -75,7 +75,6 @@ describe(`Create block sync task`, () => { message: { addressMetas: STUB_ADDRESS_METAS, genesisHash: STUB_NETWORK.genesisHash, - indexerUrl: STUB_NETWORK.remote, nodeType: NetworkType.Normal, url: STUB_NETWORK.remote, }, diff --git a/packages/neuron-wallet/tests/block-sync-renderer/light-synchronizer.test.ts b/packages/neuron-wallet/tests/block-sync-renderer/light-synchronizer.test.ts index ccd7ee549e..8279828f14 100644 --- a/packages/neuron-wallet/tests/block-sync-renderer/light-synchronizer.test.ts +++ b/packages/neuron-wallet/tests/block-sync-renderer/light-synchronizer.test.ts @@ -24,6 +24,7 @@ const schedulerWaitMock = jest.fn() const getMultisigConfigForLightMock = jest.fn() const walletGetCurrentMock = jest.fn() const walletGetAllMock = jest.fn() +const updateBlockStartNumberMock = jest.fn() function mockReset() { getSyncStatusMock.mockReset() @@ -46,6 +47,8 @@ function mockReset() { removeByHashesAndAddressType.mockReset() walletGetCurrentMock.mockReset() walletGetAllMock.mockReset() + + updateBlockStartNumberMock.mockReset() } jest.mock('../../src/services/sync-progress', () => { @@ -138,6 +141,7 @@ describe('test light synchronizer', () => { }) it('when syncing script in the local DB', async () => { getScriptsMock.mockResolvedValue([{ script, blockNumber: '0xaa' }]) + walletGetAllMock.mockReturnValue([{ id: 'walletId' }]) getExistingSyncArgsesMock.mockResolvedValue(new Set([script.args])) const addressMeta = AddressMeta.fromObject({ walletId: 'walletId', @@ -187,6 +191,7 @@ describe('test light synchronizer', () => { }) it('when syncing script not in the local DB', async () => { getScriptsMock.mockResolvedValue([{ script, blockNumber: '0xaa' }]) + walletGetAllMock.mockReturnValue([{ id: 'walletId' }]) getExistingSyncArgsesMock.mockResolvedValue(new Set()) const addressMeta = AddressMeta.fromObject({ walletId: 'walletId', @@ -256,6 +261,7 @@ describe('test light synchronizer', () => { addressType: 0, blake160: script.args, }) + walletGetAllMock.mockReturnValue([{ id: 'walletId' }]) getWalletMinLocalSavedBlockNumberMock.mockResolvedValue({ walletId: 170 }) const connect = new LightSynchronizer([addressMeta], '') //@ts-ignore @@ -367,6 +373,152 @@ describe('test light synchronizer', () => { ) expect(setScriptsMock).toHaveBeenLastCalledWith([], 'delete') }) + it('when other wallet min synced block number near the current wallet', async () => { + walletGetCurrentMock.mockReturnValue({ id: 'walletId1' }) + walletGetAllMock.mockReturnValue([ + { id: 'walletId1', startBlockNumber: '0xaa' }, + { id: 'walletId2', startBlockNumber: '0xab' }, + ]) + const addressMeta1 = AddressMeta.fromObject({ + walletId: 'walletId1', + address, + path: '', + addressIndex: 10, + addressType: 0, + blake160: script.args, + }) + const script2: Script = { + args: '0x403f0d4e833b2a8d372772a63facaa310dfeef93', + codeHash: '0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8', + hashType: 'type', + } + const getScriptsResult = [{ script: script2, blockNumber: '0xaa', scriptType: 'lock' }] + getScriptsMock.mockResolvedValue(getScriptsResult) + getExistingSyncArgsesMock.mockResolvedValue(new Set([script2.args])) + const addressMeta2 = AddressMeta.fromObject({ + walletId: 'walletId2', + address, + path: '', + addressIndex: 10, + addressType: 0, + blake160: script2.args, + }) + const connect = new LightSynchronizer([addressMeta1, addressMeta2], '') + //@ts-ignore + await connect.initSyncProgress() + expect(setScriptsMock).toHaveBeenNthCalledWith( + 1, + [ + ...[ + addressMeta1.generateDefaultLockScript().toSDK(), + addressMeta1.generateACPLockScript().toSDK(), + addressMeta1.generateLegacyACPLockScript().toSDK(), + ].map(script => ({ + script, + scriptType: 'lock', + walletId: addressMeta1.walletId, + blockNumber: '0xaa', + })), + ...[ + addressMeta2.generateDefaultLockScript().toSDK(), + addressMeta2.generateACPLockScript().toSDK(), + addressMeta2.generateLegacyACPLockScript().toSDK(), + ].map(script => ({ + script, + scriptType: 'lock', + walletId: addressMeta2.walletId, + blockNumber: '0xab', + })), + ], + 'partial' + ) + expect(setScriptsMock).toHaveBeenLastCalledWith([], 'delete') + expect(initSyncProgressMock).toBeCalledWith([ + ...[ + addressMeta1.generateDefaultLockScript().toSDK(), + addressMeta1.generateACPLockScript().toSDK(), + addressMeta1.generateLegacyACPLockScript().toSDK(), + ].map(script => ({ + script, + scriptType: 'lock', + walletId: addressMeta1.walletId, + blockNumber: '0xaa', + })), + ...[ + addressMeta2.generateDefaultLockScript().toSDK(), + addressMeta2.generateACPLockScript().toSDK(), + addressMeta2.generateLegacyACPLockScript().toSDK(), + ].map(script => ({ + script, + scriptType: 'lock', + walletId: addressMeta2.walletId, + blockNumber: '0xab', + })), + ]) + expect(updateSyncProgressFlagMock).toBeCalledWith(['walletId1', 'walletId2']) + }) + it('when other wallet min synced block number bigger than the current wallet', async () => { + walletGetCurrentMock.mockReturnValue({ id: 'walletId1' }) + walletGetAllMock.mockReturnValue([ + { id: 'walletId1', startBlockNumber: '0xaaaa0' }, + { id: 'walletId2', startBlockNumber: '0xab' }, + ]) + const addressMeta1 = AddressMeta.fromObject({ + walletId: 'walletId1', + address, + path: '', + addressIndex: 10, + addressType: 0, + blake160: script.args, + }) + const script2: Script = { + args: '0x403f0d4e833b2a8d372772a63facaa310dfeef93', + codeHash: '0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8', + hashType: 'type', + } + const getScriptsResult = [{ script: script2, blockNumber: '0xab', scriptType: 'lock' }] + getScriptsMock.mockResolvedValue(getScriptsResult) + getExistingSyncArgsesMock.mockResolvedValue(new Set([script2.args])) + const addressMeta2 = AddressMeta.fromObject({ + walletId: 'walletId2', + address, + path: '', + addressIndex: 10, + addressType: 0, + blake160: script2.args, + }) + const connect = new LightSynchronizer([addressMeta1, addressMeta2], '') + //@ts-ignore + await connect.initSyncProgress() + expect(setScriptsMock).toHaveBeenNthCalledWith( + 1, + [ + addressMeta1.generateDefaultLockScript().toSDK(), + addressMeta1.generateACPLockScript().toSDK(), + addressMeta1.generateLegacyACPLockScript().toSDK(), + ].map(script => ({ + script, + scriptType: 'lock', + walletId: addressMeta1.walletId, + blockNumber: '0xaaaa0', + })), + 'partial' + ) + expect(setScriptsMock).toHaveBeenLastCalledWith(getScriptsResult, 'delete') + expect(initSyncProgressMock).toBeCalledWith( + [ + addressMeta1.generateDefaultLockScript().toSDK(), + addressMeta1.generateACPLockScript().toSDK(), + addressMeta1.generateLegacyACPLockScript().toSDK(), + ].map(script => ({ + script, + scriptType: 'lock', + walletId: addressMeta1.walletId, + blockNumber: '0xaaaa0', + })) + ) + expect(updateSyncProgressFlagMock).toBeCalledWith(['walletId1', 'walletId2']) + }) }) describe('test initSync', () => { @@ -428,16 +580,10 @@ describe('test light synchronizer', () => { }) describe('#notifyCurrentBlockNumberProcessed', () => { - const synchronizer = new LightSynchronizer([], '') - const updateBlockStartNumberMock = jest.fn() - beforeAll(() => { + it('last process block number finish', async () => { + const synchronizer = new LightSynchronizer([], '') // @ts-ignore private property synchronizer.updateBlockStartNumber = updateBlockStartNumberMock - }) - beforeEach(() => { - updateBlockStartNumberMock.mockReset() - }) - it('last process block number finish', async () => { // @ts-ignore private property synchronizer.processingBlockNumber = '0xaa' getCurrentWalletMinSyncedBlockNumberMock.mockResolvedValueOnce(100) @@ -447,6 +593,9 @@ describe('test light synchronizer', () => { expect(updateBlockStartNumberMock).toBeCalledWith(100) }) it('not last process block number finish', async () => { + const synchronizer = new LightSynchronizer([], '') + // @ts-ignore private property + synchronizer.updateBlockStartNumber = updateBlockStartNumberMock // @ts-ignore private property synchronizer.processingBlockNumber = undefined await synchronizer.notifyCurrentBlockNumberProcessed('0xaa') diff --git a/packages/neuron-wallet/tests/block-sync-renderer/synchronizer.test.ts b/packages/neuron-wallet/tests/block-sync-renderer/synchronizer.test.ts index c3c1e7e325..9836446cf2 100644 --- a/packages/neuron-wallet/tests/block-sync-renderer/synchronizer.test.ts +++ b/packages/neuron-wallet/tests/block-sync-renderer/synchronizer.test.ts @@ -1,8 +1,9 @@ import { scriptToAddress } from '../../src/utils/scriptAndAddress' import { AddressType } from '@ckb-lumos/hd' +import { QueryOptions, type Cell } from '@ckb-lumos/base' import { Address, AddressVersion } from '../../src/models/address' import SystemScriptInfo from '../../src/models/system-script-info' -import { Synchronizer, type LumosCell, type LumosCellQuery } from '../../src/block-sync-renderer/sync/synchronizer' +import { Synchronizer } from '../../src/block-sync-renderer/sync/synchronizer' import AddressMeta from '../../src/database/address/meta' import IndexerTxHashCache from '../../src/database/chain/entities/indexer-tx-hash-cache' import { ScriptHashType } from '../../src/models/chain/script' @@ -110,22 +111,18 @@ describe('unit tests for IndexerConnector', () => { }) describe('#constructor', () => { - const STUB_URI = 'stub_uri' - it('inits lumos indexer with a node url and indexer folder path', () => { new TestSynchronizer({ addresses: [], nodeUrl, - indexerUrl: STUB_URI, }) - expect(stubbedIndexerConstructor).toHaveBeenCalledWith(nodeUrl, STUB_URI) + expect(stubbedIndexerConstructor).toHaveBeenCalledWith(nodeUrl) }) it('init with addresses', () => { const synchronizer = new TestSynchronizer({ addresses: [addressObj1, addressObj2], nodeUrl, - indexerUrl: STUB_URI, }) expect(synchronizer.getAddressesByWalletId().get(walletId1)?.[0]).toStrictEqual( AddressMeta.fromObject(addressObj1) @@ -140,7 +137,6 @@ describe('unit tests for IndexerConnector', () => { const synchronizer = new TestSynchronizer({ addresses: [addressObj1, addressObj2], nodeUrl, - indexerUrl: '', }) it('no cached tx', async () => { stubbedNextUnprocessedTxsGroupedByBlockNumberFn.mockResolvedValue([]) @@ -178,7 +174,6 @@ describe('unit tests for IndexerConnector', () => { const synchronizer = new TestSynchronizer({ addresses: [addressObj1, addressObj2], nodeUrl, - indexerUrl: '', }) synchronizer.blockTipsSubject.subscribe(stubbedBlockTipsSubscribe) @@ -220,10 +215,11 @@ describe('unit tests for IndexerConnector', () => { }) describe('#getLiveCellsByScript', () => { - let fakeCell1: LumosCell, fakeCell2: LumosCell - let cells: LumosCell[] + let fakeCell1: Cell, fakeCell2: Cell + let cells: Cell[] fakeCell1 = { + data: '0x', blockHash: '0x', outPoint: { txHash: '0x', @@ -244,6 +240,7 @@ describe('unit tests for IndexerConnector', () => { }, } fakeCell2 = { + data: '0x', blockHash: '0x', outPoint: { txHash: '0x', @@ -257,7 +254,7 @@ describe('unit tests for IndexerConnector', () => { args: '0x2', }, type: { - hashType: 'lock', + hashType: 'data', codeHash: '0xcode', args: '0x2', }, @@ -268,11 +265,10 @@ describe('unit tests for IndexerConnector', () => { const synchronizer = new TestSynchronizer({ addresses: [addressObj1, addressObj2], nodeUrl, - indexerUrl: '', }) describe('when success', () => { - const query: LumosCellQuery = { + const query = { lock: { hashType: ScriptHashType.Data, codeHash: '0xcode', @@ -283,7 +279,6 @@ describe('unit tests for IndexerConnector', () => { codeHash: '0xcode', args: '0x', }, - data: null, } beforeEach(async () => { @@ -298,14 +293,14 @@ describe('unit tests for IndexerConnector', () => { it('transform the query parameter', () => { expect(stubbedCellCollectorConstructor.mock.calls[0][1]).toEqual({ lock: { - hashType: query.lock!.hashType, - codeHash: query.lock!.codeHash, - args: query.lock!.args, + hashType: query.lock.hashType, + codeHash: query.lock.codeHash, + args: query.lock.args, }, type: { - hashType: query.type!.hashType, - codeHash: query.type!.codeHash, - args: query.type!.args, + hashType: query.type.hashType, + codeHash: query.type.codeHash, + args: query.type.args, }, data: 'any', }) @@ -316,7 +311,7 @@ describe('unit tests for IndexerConnector', () => { }) }) describe('when handling concurrent requests', () => { - const query1: LumosCellQuery = { + const query1: QueryOptions = { lock: { hashType: ScriptHashType.Data, codeHash: '0xcode', @@ -327,9 +322,8 @@ describe('unit tests for IndexerConnector', () => { codeHash: '0xcode', args: '0x1', }, - data: null, } - const query2: LumosCellQuery = { + const query2: QueryOptions = { lock: { hashType: ScriptHashType.Type, codeHash: '0xcode', @@ -340,7 +334,6 @@ describe('unit tests for IndexerConnector', () => { codeHash: '0xcode', args: '0x2', }, - data: null, } const results: unknown[] = [] @@ -401,7 +394,7 @@ describe('unit tests for IndexerConnector', () => { it('throws error', async () => { let err try { - await synchronizer.getLiveCellsByScript({ lock: null, type: null, data: null }) + await synchronizer.getLiveCellsByScript({}) } catch (error) { err = error } diff --git a/packages/neuron-wallet/tests/block-sync-renderer/task.test.ts b/packages/neuron-wallet/tests/block-sync-renderer/task.test.ts index 413e782d13..9e0369e00a 100644 --- a/packages/neuron-wallet/tests/block-sync-renderer/task.test.ts +++ b/packages/neuron-wallet/tests/block-sync-renderer/task.test.ts @@ -6,7 +6,6 @@ const STUB_START_MESSAGE = { genesisHash: 'stub_genesis_hash', url: 'stub_url', addressMetas: 'stub_address_metas', - indexerUrl: 'stub_indexer_url', nodeType: 2, }, } @@ -71,7 +70,6 @@ describe(`Block Sync Task`, () => { expect(stubbedSyncQueue).toHaveBeenCalledWith( STUB_START_MESSAGE.message.url, STUB_START_MESSAGE.message.addressMetas, - STUB_START_MESSAGE.message.indexerUrl, STUB_START_MESSAGE.message.nodeType ) expect(stubbedSyncQueueStart).toHaveBeenCalled() diff --git a/packages/neuron-wallet/tests/models/chain/live-cell.test.ts b/packages/neuron-wallet/tests/models/chain/live-cell.test.ts index 84a9b97830..1bc5cba2fd 100644 --- a/packages/neuron-wallet/tests/models/chain/live-cell.test.ts +++ b/packages/neuron-wallet/tests/models/chain/live-cell.test.ts @@ -1,6 +1,5 @@ import Script, { ScriptHashType } from '../../../src/models/chain/script' -import { LumosCell } from '../../../src/block-sync-renderer/sync/synchronizer' -import LiveCell from '../../../src/models/chain/live-cell' +import LiveCell, { CellWithOutPoint } from '../../../src/models/chain/live-cell' describe('LiveCell Test', () => { const INITIAL_DATA = { @@ -88,7 +87,7 @@ describe('LiveCell Test', () => { }) describe('get cells from lumos cell', () => { - const LUMOS_CELL: LumosCell = { + const LUMOS_CELL: CellWithOutPoint = { blockHash: '0x0000000000000000000000000000000000000000000000000000000000000000', outPoint: { txHash: INITIAL_DATA.txHash, diff --git a/packages/neuron-wallet/tests/models/multi-sign.test.ts b/packages/neuron-wallet/tests/models/multi-sign.test.ts index bf9416a94b..97fe34b0e6 100644 --- a/packages/neuron-wallet/tests/models/multi-sign.test.ts +++ b/packages/neuron-wallet/tests/models/multi-sign.test.ts @@ -15,13 +15,13 @@ describe('MultiSign Test', () => { const serialized = '0x0000010136c329ed630d6ce750712a477543672adab57f4c' it('since', () => { - const since = new Multisig().since(minutes, headerEpoch) + const since = Multisig.since(minutes, headerEpoch) expect(since).toEqual(expectedSince) }) it('since, minutes < 0', () => { expect(() => { - new Multisig().since(-1, headerEpoch) + Multisig.since(-1, headerEpoch) }).toThrowError() }) @@ -47,7 +47,7 @@ describe('MultiSign Test', () => { }) it('args', () => { - const args = new Multisig().args(bob.blake160, minutes, headerEpoch) + const args = Multisig.args(bob.blake160, minutes, headerEpoch) expect(args).toEqual(expectedArgs) }) }) diff --git a/packages/neuron-wallet/tests/services/cells.test.ts b/packages/neuron-wallet/tests/services/cells.test.ts index 8d3dcd17d1..813a373f69 100644 --- a/packages/neuron-wallet/tests/services/cells.test.ts +++ b/packages/neuron-wallet/tests/services/cells.test.ts @@ -1676,7 +1676,7 @@ describe('CellsService', () => { '61', OutputStatus.Sent, false, - SystemScriptInfo.generateMultiSignScript(new Multisig().args(bob.blake160, +10, '0x7080291000049')) + SystemScriptInfo.generateMultiSignScript(Multisig.args(bob.blake160, +10, '0x7080291000049')) ) await expect(CellsService.getLiveOrSentCellByWalletId(bob.walletId)).resolves.toHaveLength(1) }) @@ -1718,7 +1718,7 @@ describe('CellsService', () => { it('MULTI_LOCK_TIME', () => { const output = Output.fromObject({ capacity: '1000', - lock: SystemScriptInfo.generateMultiSignScript(new Multisig().args(bob.blake160, +10, '0x7080291000049')), + lock: SystemScriptInfo.generateMultiSignScript(Multisig.args(bob.blake160, +10, '0x7080291000049')), }) expect(CellsService.getCellLockType(output)).toBe(LockScriptCategory.MULTI_LOCK_TIME) }) diff --git a/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts b/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts index 3644000e9d..c8ba7d414a 100644 --- a/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts +++ b/packages/neuron-wallet/tests/services/tx/transaction-generator.test.ts @@ -1,5 +1,6 @@ import { when } from 'jest-when' import { bytes } from '@ckb-lumos/codec' +import { since } from '@ckb-lumos/base' import OutputEntity from '../../../src/database/chain/entities/output' import InputEntity from '../../../src/database/chain/entities/input' import TransactionEntity from '../../../src/database/chain/entities/transaction' @@ -27,7 +28,7 @@ import { SudtAcpHaveDataError, TargetOutputNotFoundError, } from '../../../src/exceptions' -import LiveCell from '../../../src/models/chain/live-cell' +import LiveCell, { CellWithOutPoint } from '../../../src/models/chain/live-cell' import { keyInfos } from '../../setupAndTeardown/public-key-info.fixture' const randomHex = (length: number = 64): string => { @@ -85,7 +86,6 @@ import HdPublicKeyInfo from '../../../src/database/chain/entities/hd-public-key- import AssetAccount from '../../../src/models/asset-account' import MultisigConfigModel from '../../../src/models/multisig-config' import MultisigOutput from '../../../src/database/chain/entities/multisig-output' -import { LumosCell } from '../../../src/block-sync-renderer/sync/synchronizer' import { closeConnection, getConnection, initConnection } from '../../setupAndTeardown' describe('TransactionGenerator', () => { @@ -470,13 +470,11 @@ describe('TransactionGenerator', () => { const multiSignOutput = tx.outputs.find(o => o.lock.codeHash === SystemScriptInfo.MULTI_SIGN_CODE_HASH) expect(multiSignOutput).toBeDefined() - const multiSign = new Multisig() - const epoch = multiSign.parseSince(multiSignOutput!.lock.args) - // @ts-ignore: Private method - const parsedEpoch = multiSign.parseEpoch(epoch) - expect(parsedEpoch.number).toEqual(BigInt(5)) - expect(parsedEpoch.length).toEqual(BigInt(240)) - expect(parsedEpoch.index).toEqual(BigInt(43)) + const epoch = Multisig.parseSince(multiSignOutput!.lock.args) + const parsedEpoch = since.parseEpoch(epoch) + expect(parsedEpoch.number).toEqual(5) + expect(parsedEpoch.length).toEqual(240) + expect(parsedEpoch.index).toEqual(43) }) }) }) @@ -733,13 +731,11 @@ describe('TransactionGenerator', () => { expect(tx.outputs[0].lock.codeHash).toEqual(SystemScriptInfo.MULTI_SIGN_CODE_HASH) - const multiSign = new Multisig() - const epoch = multiSign.parseSince(tx.outputs[0].lock.args) - // @ts-ignore: Private method - const parsedEpoch = multiSign.parseEpoch(epoch) - expect(parsedEpoch.number).toEqual(BigInt(5)) - expect(parsedEpoch.length).toEqual(BigInt(240)) - expect(parsedEpoch.index).toEqual(BigInt(43)) + const epoch = Multisig.parseSince(tx.outputs[0].lock.args) + const parsedEpoch = since.parseEpoch(epoch) + expect(parsedEpoch.number).toEqual(5) + expect(parsedEpoch.length).toEqual(240) + expect(parsedEpoch.index).toEqual(43) }) }) @@ -1045,7 +1041,7 @@ describe('TransactionGenerator', () => { describe('generateWithdrawMultiSignTx', () => { const prevOutput = Output.fromObject({ capacity: toShannon('1000'), - lock: SystemScriptInfo.generateMultiSignScript(new Multisig().args(bob.lockScript.args, 100, '0x7080018000001')), + lock: SystemScriptInfo.generateMultiSignScript(Multisig.args(bob.lockScript.args, 100, '0x7080018000001')), }) const outPoint = OutPoint.fromObject({ txHash: '0x' + '0'.repeat(64), @@ -1100,8 +1096,8 @@ describe('TransactionGenerator', () => { tokenID: string | undefined = undefined, lockScript: Script = bobAnyoneCanPayLockScript, customData: string = '0x' - ): LumosCell => { - const liveCell: LumosCell = { + ): CellWithOutPoint => { + const liveCell: CellWithOutPoint = { blockHash: randomHex(), outPoint: { txHash: randomHex(), @@ -1112,18 +1108,17 @@ describe('TransactionGenerator', () => { lock: { codeHash: lockScript.codeHash, args: lockScript.args, - hashType: lockScript.hashType.toString(), + hashType: lockScript.hashType, }, }, data: '0x', } if (tokenID) { const typeScript = assetAccountInfo.generateSudtScript(tokenID) - // @ts-ignore liveCell.cellOutput.type = { codeHash: typeScript.codeHash, args: typeScript.args, - hashType: typeScript.hashType.toString(), + hashType: typeScript.hashType, } } liveCell.data = amount ? BufferUtils.writeBigUInt128LE(BigInt(amount)) : '0x'