diff --git a/.ckb-version b/.ckb-version index 2a74e8d160..788549ad69 100644 --- a/.ckb-version +++ b/.ckb-version @@ -1 +1 @@ -v0.101.4 +v0.103.0 diff --git a/.github/workflows/check_checksums.yml b/.github/workflows/check_checksums.yml new file mode 100644 index 0000000000..78ab3ebb38 --- /dev/null +++ b/.github/workflows/check_checksums.yml @@ -0,0 +1,39 @@ +name: Check Checksums + +on: + release: + types: [published, prereleased] + +jobs: + compare: + name: Compare checksums + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Generate checksums from artifacts + run: + ruby ./scripts/release-checksums.rb ${{ github.event.release.tag_name }} | tee generated_checksums.txt + + - name: Fetch checksums from release note + run: | + curl "https://api.github.com/repos/nervosnetwork/neuron/releases/tags/${{ github.event.release.tag_name }}" |\ + python3 -c "import sys, json; print(\"\n\".join(json.load(sys.stdin)['body'].splitlines()[-9:]))" |\ + tee release_note_checksums.txt + + - name: Compare + run: diff generated_checksums.txt release_note_checksums.txt + + - id: comment_body + run: | + body=$(cat generated_checksums.txt) + body="${body//'%'/'%25'}" + body="${body//$'\n'/'%0A'}" + body="${body//$'\r'/'%0D'}" + echo ::set-output name=body::$body + + - uses: peter-evans/commit-comment@v1 + with: + body: ${{ steps.comment_body.outputs.body }} + diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index f9cb0251ad..701f3773e7 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: node: - - 16 + - 16.4.0 os: - macos-latest - ubuntu-20.04 @@ -38,7 +38,7 @@ jobs: path: | node_modules */*/node_modules - key: 2021-8-11-${{ runner.os }}-${{ hashFiles('**/yarn.lock')}} + key: 2022-05-07-${{ runner.os }}-${{ hashFiles('**/yarn.lock')}} - name: Add msbuild to PATH if: matrix.os == 'windows-2019' diff --git a/.github/workflows/package_for_test.yml b/.github/workflows/package_for_test.yml index cafe7d5af1..f01ab472db 100644 --- a/.github/workflows/package_for_test.yml +++ b/.github/workflows/package_for_test.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: node: - - 16 + - 16.4.0 os: - macos-latest - ubuntu-20.04 @@ -33,7 +33,7 @@ jobs: path: | node_modules */*/node_modules - key: 2021-8-11-${{ runner.os }}-${{ hashFiles('**/yarn.lock')}} + key: 2022-05-07-${{ runner.os }}-${{ hashFiles('**/yarn.lock')}} - name: Add msbuild to PATH if: matrix.os == 'windows-2019' diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 44eb9a2e62..68f174ba3f 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: node: - - 16 + - 16.4.0 os: - macos-latest - ubuntu-20.04 @@ -35,7 +35,7 @@ jobs: path: | node_modules */*/node_modules - key: 2021-8-11-${{ runner.os }}-${{ hashFiles('**/yarn.lock')}} + key: 2022-05-07-${{ runner.os }}-${{ hashFiles('**/yarn.lock')}} - name: Install libudev if: matrix.os == 'ubuntu-20.04' diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bd0bb53f2..beb844a13b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,49 @@ +# 0.103.0 (2022-05-10) + +### Hardfork + +Neuron adopts new RFCs introduced by [HARDFORK 2021](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0037-ckb2021/0037-ckb2021.md), prominents are as follows, + +1. adopt the new address format and deprecate the short version of addresses #2346 + + +### CKB +[CKB v0.103.0](https://github.com/nervosnetwork/ckb/releases/tag/v0.103.0) was released on Apr. 11th, 2022. This version of CKB node is now bundled and preconfigured in Neuron. + + +### New features +* #2280: add a balance-not-reserved checkbox in Nervos DAO deposit dialog(@Keith-CY) +* #2291: add a sudt detail link in creating asset account dialog (@yanguoyu) +* #2296, #2320: add address lock script detail in transaction detail window (@Lester-xie, @Keith-CY) +* #2300, #2352: add options of ckb node to boost synchronization (@Keith-CY) +* #2302, #2333: allow ACP account to transfer to secp256k1 addresses (@Lester-xie) +* #2303, #2310, #2313, #2323, #2324, #2332, #2340, #2357, #2363: add multisig address management (@yanguoyu, @Keith-CY) +* #2307: disable some functions when current wallet is an xpub wallet (@yanguoyu) +* #2309: check checksums after publishment (@Keith-CY) +* #2315: add new sign tips and add old sign notice in sign/verify window (@yanguoyu) +* #2330: allow sudt/secp256k1 assets to be migrated to an asset account (@yanguoyu) +* #2331: upgrade bundled ckb version to v0.103.0 (@Keith-CY) +* #2337: allow asset account to be destroied when balance is 0 (@yanguoyu) +* #2341: support syncing cells those have script of hash type 'data1' (@Keith-CY) +* #2342, #2361: mark create and destroy asset account in transaction history (@qiweiii) +* #2346: use new foramt of addresses by default (@Keith-CY) +* #2359: allow secp256k1 address to be timelocked (@Keith-CY) + + +### Bug fixes +* #2304: fix error message of creating sudt/acp account (@Keith-CY) +* #2335: fix parsers (@Keith-CY) +* #2351: avoid saving duplicate blake160 for one wallet (@yanguoyu) +* #2362: ignore rejected transaction by (@Keith-CY) + + +### Refactor +* #2277: remove grommet icons (@yanguoyu) +* #2289: format code (@Lester-xie) +* #2290: replace calculate_dao_maximum_withdraw with calculateDaoMaximumWithdraw method in sdk (@yanguoyu) + + + # 0.101.3 (2022-03-01) [CKB v0.101.4](https://github.com/nervosnetwork/ckb/releases/tag/v0.101.4) was released on Jan. 20th, 2022. This version of CKB node is now bundled and preconfigured in Neuron. diff --git a/README.md b/README.md index 71e68264b2..8a23aaa830 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,8 @@ Nervos CKB Full-Node Desktop Wallet -[![Azure Pipelines Build Status](https://dev.azure.com/nervosnetwork/neuron/_apis/build/status/nervosnetwork.neuron?branchName=develop)](https://dev.azure.com/nervosnetwork/neuron/_build/latest?definitionId=8&branchName=develop) [![Unit Tests](https://github.com/nervosnetwork/neuron/actions/workflows/unit_tests.yml/badge.svg)](https://github.com/nervosnetwork/neuron/actions/workflows/unit_tests.yml) -[![Telegram Group](https://cdn.rawgit.com/Patrolavia/telegram-badge/8fe3382b/chat.svg)](https://t.me/nervos_ckb_dev) +[![Discord](https://img.shields.io/discord/956765352514183188?label=Discord&logo=discord&style=default&color=grey&labelColor=5865F2&logoColor=white)](https://discord.gg/N9nZ3JE2Gg) --- diff --git a/lerna.json b/lerna.json index cf841aab34..40ae796c61 100644 --- a/lerna.json +++ b/lerna.json @@ -2,7 +2,7 @@ "packages": [ "packages/*" ], - "version": "0.101.3", + "version": "0.103.0", "npmClient": "yarn", "useWorkspaces": true } diff --git a/packages/ckb-indexer/package.json b/packages/ckb-indexer/package.json index da4c71159e..3a012e9881 100644 --- a/packages/ckb-indexer/package.json +++ b/packages/ckb-indexer/package.json @@ -18,7 +18,7 @@ "@ckb-lumos/base": "0.18.0-rc2", "@ckb-lumos/rpc": "0.18.0-rc2", "ckb-js-toolkit": "0.100.0-rc1", - "cross-fetch": "3.1.4" + "cross-fetch": "3.1.5" }, "devDependencies": { "events": "3.3.0" diff --git a/packages/neuron-ui/package.json b/packages/neuron-ui/package.json index c9a6c5e108..4e56fc0306 100644 --- a/packages/neuron-ui/package.json +++ b/packages/neuron-ui/package.json @@ -1,6 +1,6 @@ { "name": "neuron-ui", - "version": "0.101.3", + "version": "0.103.0", "private": true, "author": { "name": "Nervos Core Dev", @@ -44,11 +44,11 @@ ], "dependencies": { "primereact": "7.1.0", - "@nervosnetwork/ckb-sdk-core": "0.101.0", + "@nervosnetwork/ckb-sdk-core": "0.102.2", + "@nervosnetwork/ckb-sdk-utils": "0.102.2", "@uifabric/experiments": "7.42.4", "@uifabric/styling": "7.20.0", "canvg": "2.0.0", - "grommet-icons": "4.7.0", "i18next": "21.6.6", "immer": "9.0.12", "jsqr": "1.4.0", @@ -58,8 +58,7 @@ "react-dom": "16.12.0", "react-i18next": "11.15.3", "react-router-dom": "5.1.2", - "sass": "1.47.0", - "styled-components": "5.3.3" + "sass": "1.47.0" }, "devDependencies": { "@storybook/addon-actions": "5.3.18", diff --git a/packages/neuron-ui/src/components/Addresses/addresses.module.scss b/packages/neuron-ui/src/components/Addresses/addresses.module.scss index 8f76cb8c06..4137f3ca80 100644 --- a/packages/neuron-ui/src/components/Addresses/addresses.module.scss +++ b/packages/neuron-ui/src/components/Addresses/addresses.module.scss @@ -100,7 +100,7 @@ $change-color: #6666cc; div { min-width: 100px; - max-width: 500px; + max-width: 818px; display: flex; position: relative; @@ -116,8 +116,8 @@ $change-color: #6666cc; display: none; } - @media screen and (max-width: 1680px) { - width: 20vw; + @media screen and (max-width: 1800px) { + width: 46vw; .ellipsis { display: inline; @@ -139,6 +139,10 @@ $change-color: #6666cc; } } + @media screen and (max-width: 1600px) { + width: 30vw; + } + @media screen and (max-width: 1000px) { width: 15vw; } @@ -154,7 +158,7 @@ $change-color: #6666cc; appearance: none; border: none; background-color: transparent; - width: 1rem; + width: 2rem; height: 1rem; display: flex; justify-content: center; @@ -162,6 +166,10 @@ $change-color: #6666cc; svg { pointer-events: none; + + g { + fill: #000; + } } } } @@ -177,11 +185,18 @@ $change-color: #6666cc; border: none; } - @media screen and (max-width: 1500px) { + @media screen and (max-width: 2400px) { input { width: 100px; } } + + @media screen and (max-width: 1300px) { + input { + width: 90px; + } + } + } .balance { diff --git a/packages/neuron-ui/src/components/Addresses/index.tsx b/packages/neuron-ui/src/components/Addresses/index.tsx index ff39bbc2b9..cada4eeace 100644 --- a/packages/neuron-ui/src/components/Addresses/index.tsx +++ b/packages/neuron-ui/src/components/Addresses/index.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useCallback } from 'react' import { clipboard } from 'electron' import { useHistory } from 'react-router-dom' import { useTranslation } from 'react-i18next' -import { Edit } from 'grommet-icons' +import { ReactComponent as Edit } from 'widgets/Icons/Edit.svg' import TextField from 'widgets/TextField' import Breadcrum from 'widgets/Breadcrum' import CopyZone from 'widgets/CopyZone' @@ -105,9 +105,9 @@ const Addresses = () => {
- {addr.address.slice(0, -6)} + {addr.address.slice(0, -20)} ... - {addr.address.slice(-6)} + {addr.address.slice(-20)}
@@ -135,7 +135,7 @@ const Addresses = () => { onClick={onDescriptionSelected} className={styles.editBtn} > - + ) } diff --git a/packages/neuron-ui/src/components/ApproveMultisigTx/approveMultisigTx.module.scss b/packages/neuron-ui/src/components/ApproveMultisigTx/approveMultisigTx.module.scss new file mode 100644 index 0000000000..a62acbda93 --- /dev/null +++ b/packages/neuron-ui/src/components/ApproveMultisigTx/approveMultisigTx.module.scss @@ -0,0 +1,109 @@ +@import '../../styles/mixin.scss'; + +.title { + display: flex; + line-height: 1.625rem; + flex-wrap: wrap; +} + +.textarea { + margin-top: 8px; + width: 100%; + height: 140px; + resize: none; +} + +.action { + display: flex; + justify-content: space-between; + margin-top: 24px; +} + +.recordTab { + position: relative; + display: flex; + box-sizing: border-box; + margin: 0 0 10px 0; + padding: 0; + line-height: 1.6875rem; + font-size: 1.375rem; + color: #000; + border-bottom: 1px solid #ccc; + + button { + @include bold-text; + appearance: none; + flex: 1; + display: flex; + justify-content: center; + align-items: center; + height: 1.875rem; + font-size: 1rem; + background-color: transparent; + padding: 0 0 6px 0; + margin: 0; + border: none; + + &:hover { + color: var(--nervos-green); + opacity: 0.8; + } + } + + .underline { + display: block; + position: absolute; + bottom: -1px; + left: 0; + height: 4px; + width: 50%; + background: var(--nervos-green); + box-sizing: border-box; + padding: 0; + transition: transform 0.1s ease-in-out; + } + + &[data-idx='1'] { + .underline { + transform: translateX(100%); + } + } +} + +.cellItem { + display: flex; + align-items: center; + justify-content: space-between; + + & > div { + display: flex; + align-items: center; + } + + .tag { + background-color: #ddd; + color: #fff; + padding: 2px; + margin: 4px; + padding: 0 6px; + font-size: 12px; + border-radius: 4px; + border: 1px solid #ddd; + text-decoration: line-through; + + &+.activity { + background: var(--nervos-green); + border-color: var(--nervos-green); + text-decoration: none; + } + } + + .capacity { + flex-shrink: 0; + margin-left: 16px; + } +} + +.statusTitle { + margin-top: 8px; +} diff --git a/packages/neuron-ui/src/components/ApproveMultisigTx/hooks.ts b/packages/neuron-ui/src/components/ApproveMultisigTx/hooks.ts new file mode 100644 index 0000000000..6f1a429689 --- /dev/null +++ b/packages/neuron-ui/src/components/ApproveMultisigTx/hooks.ts @@ -0,0 +1,133 @@ +import { TFunction } from 'i18next' +import { useCallback, useState } from 'react' +import { + broadcastTransaction, + exportTransactionAsJSON, + invokeShowErrorMessage, + MultisigConfig, + OfflineSignJSON, +} from 'services/remote' +import { useDispatch } from 'states' +import { AppActions } from 'states/stateProvider/reducer' +import { isSuccessResponse } from 'utils' + +export const useBroadcast = ({ + offlineSignJson, + walletID, + closeDialog, + t, +}: { + offlineSignJson: OfflineSignJSON + walletID: string + closeDialog: () => void + t: TFunction +}) => { + const broadcast = useCallback(async () => { + const res = await broadcastTransaction({ + ...offlineSignJson, + walletID, + }) + if (isSuccessResponse(res)) { + closeDialog() + } else { + invokeShowErrorMessage({ + title: t('messages.error'), + content: typeof res.message === 'string' ? res.message : res.message.content!, + }) + } + }, [offlineSignJson, walletID, t, closeDialog]) + return broadcast +} + +export const useSignAndExport = ({ + offlineSignJson, + walletID, + multisigConfig, + closeDialog, +}: { + offlineSignJson: OfflineSignJSON + walletID: string + multisigConfig: MultisigConfig + closeDialog: () => void +}) => { + const dispatch = useDispatch() + const signAndExport = useCallback(() => { + dispatch({ + type: AppActions.UpdateGeneratedTx, + payload: offlineSignJson.transaction, + }) + dispatch({ + type: AppActions.RequestPassword, + payload: { + walletID, + actionType: 'send-from-multisig', + multisigConfig, + }, + }) + closeDialog() + }, [dispatch, walletID, multisigConfig, offlineSignJson.transaction, closeDialog]) + return signAndExport +} + +export const useSignAndBroadcast = ({ + offlineSignJson, + multisigConfig, + walletID, + canBroadcastAfterSign, + closeDialog, +}: { + offlineSignJson: OfflineSignJSON + multisigConfig: MultisigConfig + walletID: string + canBroadcastAfterSign: boolean + closeDialog: () => void +}) => { + const dispatch = useDispatch() + const signAndExport = useCallback(() => { + dispatch({ + type: AppActions.UpdateGeneratedTx, + payload: offlineSignJson.transaction, + }) + dispatch({ + type: AppActions.RequestPassword, + payload: { + walletID, + actionType: canBroadcastAfterSign ? 'send-from-multisig-need-one' : 'send-from-multisig', + multisigConfig, + }, + }) + closeDialog() + }, [dispatch, walletID, multisigConfig, offlineSignJson.transaction, canBroadcastAfterSign, closeDialog]) + return signAndExport +} + +export const useExport = ({ + offlineSignJson, + closeDialog, +}: { + offlineSignJson: OfflineSignJSON + closeDialog: () => void +}) => { + return useCallback(async () => { + const res = await exportTransactionAsJSON(offlineSignJson) + if (isSuccessResponse(res)) { + closeDialog() + } + }, [closeDialog, offlineSignJson]) +} + +export const useTabView = () => { + const [tabIdx, setTabIdx] = useState('0') + const onTabClick = (e: React.SyntheticEvent) => { + const { + dataset: { idx }, + } = e.target as HTMLDivElement + if (idx) { + setTabIdx(idx) + } + } + return { + tabIdx, + onTabClick, + } +} diff --git a/packages/neuron-ui/src/components/ApproveMultisigTx/index.tsx b/packages/neuron-ui/src/components/ApproveMultisigTx/index.tsx new file mode 100644 index 0000000000..9d11f4f44c --- /dev/null +++ b/packages/neuron-ui/src/components/ApproveMultisigTx/index.tsx @@ -0,0 +1,157 @@ +import React, { useMemo } from 'react' +import { useTranslation, Trans } from 'react-i18next' +import { MultisigConfig, OfflineSignJSON } from 'services/remote' +import Button from 'widgets/Button' +import CopyZoneAddress from 'widgets/CopyZoneAddress' +import { useState as useGlobalState } from 'states' +import { ckbCore } from 'services/chain' +import { shannonToCKBFormatter } from 'utils' +import ScriptTag from 'components/ScriptTag' +import getMultisigSignStatus from 'utils/getMultisigSignStatus' +import styles from './approveMultisigTx.module.scss' +import { useBroadcast, useExport, useSignAndBroadcast, useSignAndExport, useTabView } from './hooks' + +const Cell = React.memo( + ({ + cell, + isMainnet, + }: { + cell: { + lock: CKBComponents.Script + type?: CKBComponents.Script + data?: string + capacity: string + } + isMainnet: boolean + }) => { + return ( +
+
+ + Type + Data + +
+ {`${shannonToCKBFormatter(cell.capacity)} CKB`} +
+ ) + } +) +const ApproveMultisigTx = ({ + multisigConfig, + closeDialog, + offlineSignJson, + isMainnet, +}: { + multisigConfig: MultisigConfig + closeDialog: () => void + offlineSignJson: OfflineSignJSON + isMainnet: boolean +}) => { + const [t] = useTranslation() + const { wallet } = useGlobalState() + const jsonContent = useMemo(() => { + return JSON.stringify(offlineSignJson, null, 2) + }, [offlineSignJson]) + const { lackOfRCount, lackOfMCount, canBroadcastAfterSign, canSign } = useMemo( + () => + getMultisigSignStatus({ + multisigConfig, + signatures: offlineSignJson.transaction.signatures, + addresses: wallet.addresses, + }), + [multisigConfig, offlineSignJson.transaction.signatures, wallet.addresses] + ) + const onSignAndExport = useSignAndExport({ + multisigConfig, + walletID: wallet.id, + offlineSignJson, + closeDialog, + }) + const onBroadcast = useBroadcast({ offlineSignJson, walletID: wallet.id, closeDialog, t }) + const onSignAndBroadcast = useSignAndBroadcast({ + multisigConfig, + walletID: wallet.id, + offlineSignJson, + canBroadcastAfterSign, + closeDialog, + }) + const onExport = useExport({ offlineSignJson, closeDialog }) + const { tabIdx, onTabClick } = useTabView() + const [label, action] = useMemo(() => { + if (!lackOfMCount && !lackOfRCount) { + return ['broadcast', onBroadcast] + } + if (!canSign) { + return ['export', onExport] + } + if (canBroadcastAfterSign) { + return ['signAndBroadcast', onSignAndBroadcast] + } + return ['signAndExport', onSignAndExport] + }, [ + lackOfMCount, + lackOfRCount, + canSign, + canBroadcastAfterSign, + onBroadcast, + onExport, + onSignAndBroadcast, + onSignAndExport, + ]) + return ( + <> +
+ ]} + /> +
+

{t('multisig-address.approve-dialog.transaction')}

+
+
{t('multisig-address.approve-dialog.content')}
+
+ + +
+
+ {tabIdx === '0' ? ( +