diff --git a/CHANGELOG.md b/CHANGELOG.md
index 02eac30843..69d0dca1c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,18 @@
+# 0.100.2 (2021-10-15)
+
+[CKB v0.100.0](https://github.com/nervosnetwork/ckb/releases/tag/v0.100.0) was released on Sep. 22nd, 2021. This version of CKB node is now bundled and preconfigured in Neuron.
+
+**This version is compatible with v0.100.0 and above.**
+
+### New feature
+* Add feature of destroy CKB Asset Account.
+
+### Bug fixes
+* Kill ckb/indexer before Neuron quit.
+* Fix a cosmetic bug of address truncation in addresses book under higher resolution.
+* Remove useless token id prompt when asset account type is `CKB Account`.
+
+
# 0.100.1 (2021-09-29)
[CKB v0.100.0](https://github.com/nervosnetwork/ckb/releases/tag/v0.100.0) was released on Sep. 22nd, 2021. This version of CKB node is now bundled and preconfigured in Neuron.
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 9fabf8c3da..f805c8415d 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -169,7 +169,7 @@ stages:
name: winSiginingCertificate
displayName: "Download Windows Signing Certificate"
inputs:
- secureFile: Neuron_win.p12
+ secureFile: neuron_win_2021.pfx
- pwsh: |
bash ./scripts/download-ckb.sh win
yarn build
diff --git a/lerna.json b/lerna.json
index 6ce79cdb26..b6b25452a7 100644
--- a/lerna.json
+++ b/lerna.json
@@ -2,7 +2,7 @@
"packages": [
"packages/*"
],
- "version": "0.100.1",
+ "version": "0.100.2",
"npmClient": "yarn",
"useWorkspaces": true
}
diff --git a/package.json b/package.json
index 9f1330f759..149af70f76 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "neuron",
"productName": "Neuron",
"description": "CKB Neuron Wallet",
- "version": "0.100.1",
+ "version": "0.100.2",
"private": true,
"author": {
"name": "Nervos Core Dev",
diff --git a/packages/neuron-ui/package.json b/packages/neuron-ui/package.json
index 21a0a00f2e..22cd5fd51b 100644
--- a/packages/neuron-ui/package.json
+++ b/packages/neuron-ui/package.json
@@ -1,6 +1,6 @@
{
"name": "neuron-ui",
- "version": "0.100.1",
+ "version": "0.100.2",
"private": true,
"author": {
"name": "Nervos Core Dev",
diff --git a/packages/neuron-ui/src/components/Addresses/addresses.module.scss b/packages/neuron-ui/src/components/Addresses/addresses.module.scss
index a714f24f93..8f76cb8c06 100644
--- a/packages/neuron-ui/src/components/Addresses/addresses.module.scss
+++ b/packages/neuron-ui/src/components/Addresses/addresses.module.scss
@@ -116,14 +116,12 @@ $change-color: #6666cc;
display: none;
}
- @media screen and (max-width: 1365px) {
+ @media screen and (max-width: 1680px) {
+ width: 20vw;
+
.ellipsis {
display: inline;
}
- }
-
- @media screen and (max-width: 1680px) {
- width: 20vw;
&::after {
position: absolute;
diff --git a/packages/neuron-ui/src/components/HardwareSign/index.tsx b/packages/neuron-ui/src/components/HardwareSign/index.tsx
index 8847ce9552..acf75cebf3 100644
--- a/packages/neuron-ui/src/components/HardwareSign/index.tsx
+++ b/packages/neuron-ui/src/components/HardwareSign/index.tsx
@@ -231,6 +231,7 @@ const HardwareSign = ({
switch (type) {
case 'send':
case 'send-nft':
+ case 'destroy-ckb-account':
case 'claim-cheque': {
if (isSending) {
break
diff --git a/packages/neuron-ui/src/components/PasswordRequest/index.tsx b/packages/neuron-ui/src/components/PasswordRequest/index.tsx
index f4e91d0009..f66b867b5e 100644
--- a/packages/neuron-ui/src/components/PasswordRequest/index.tsx
+++ b/packages/neuron-ui/src/components/PasswordRequest/index.tsx
@@ -185,6 +185,7 @@ const PasswordRequest = () => {
await sendSUDTTransaction(params)(dispatch).then(handleSendTxRes)
break
}
+ case 'destroy-ckb-account':
case 'send-nft':
case 'send-cheque': {
if (isSending) {
diff --git a/packages/neuron-ui/src/components/SUDTCreateDialog/index.tsx b/packages/neuron-ui/src/components/SUDTCreateDialog/index.tsx
index 168039c74e..e1d4158982 100644
--- a/packages/neuron-ui/src/components/SUDTCreateDialog/index.tsx
+++ b/packages/neuron-ui/src/components/SUDTCreateDialog/index.tsx
@@ -289,7 +289,7 @@ const SUDTCreateDialog = ({
error={tokenErrors[field.key]}
className={accountType === AccountType.CKB ? styles.ckbField : undefined}
hint={
- !tokenErrors[field.key] && field.key === 'tokenId'
+ !tokenErrors[field.key] && field.key === 'tokenId' && accountType === AccountType.SUDT
? t(`s-udt.create-dialog.placeholder.${field.label}`)
: undefined
}
diff --git a/packages/neuron-ui/src/components/SUDTSend/index.tsx b/packages/neuron-ui/src/components/SUDTSend/index.tsx
index f03c92f2e9..fb3d216c1a 100644
--- a/packages/neuron-ui/src/components/SUDTSend/index.tsx
+++ b/packages/neuron-ui/src/components/SUDTSend/index.tsx
@@ -16,6 +16,7 @@ import {
generateSendAllSUDTTransaction,
getAnyoneCanPayScript,
generateChequeTransaction,
+ destoryCKBAssetAccount,
} from 'services/remote'
import { ckbCore } from 'services/chain'
import { useState as useGlobalState, useDispatch, AppActions } from 'states'
@@ -330,6 +331,37 @@ const SUDTSend = () => {
[isSubmittable, globalDispatch, walletId, accountType, isSecp256k1ShortAddress]
)
+ const [isDestroying, setIsDestroying] = useState(false)
+ const onDestroy = useCallback(() => {
+ setIsDestroying(true)
+ destoryCKBAssetAccount({ walletID: walletId, id: accountId })
+ .then(res => {
+ if (isSuccessResponse(res)) {
+ const tx = res.result
+ globalDispatch({ type: AppActions.UpdateExperimentalParams, payload: { tx } })
+ globalDispatch({
+ type: AppActions.RequestPassword,
+ payload: {
+ walletID: walletId,
+ actionType: 'destroy-ckb-account',
+ },
+ })
+ } else {
+ globalDispatch({
+ type: AppActions.AddNotification,
+ payload: {
+ type: 'alert',
+ timestamp: +new Date(),
+ content: typeof res.message === 'string' ? res.message : res.message.content!,
+ },
+ })
+ }
+ })
+ .finally(() => {
+ setIsDestroying(false)
+ })
+ }, [globalDispatch, walletId, accountId])
+
if (!isLoaded) {
return (
@@ -412,7 +444,15 @@ const SUDTSend = () => {
-
+
+ {accountType === AccountType.CKB ? (
+
+
+ {t('s-udt.send.destroy-desc')}
+
+ ) : null}
diff --git a/packages/neuron-ui/src/components/SUDTSend/sUDTSend.module.scss b/packages/neuron-ui/src/components/SUDTSend/sUDTSend.module.scss
index 1f8e650655..072c77ffac 100644
--- a/packages/neuron-ui/src/components/SUDTSend/sUDTSend.module.scss
+++ b/packages/neuron-ui/src/components/SUDTSend/sUDTSend.module.scss
@@ -136,6 +136,54 @@
justify-content: flex-end;
}
+.ckb-footer {
+ display: flex;
+ justify-content: space-between;
+}
+
+.tooltip {
+ position: relative;
+ display: inline-block;
+ &:hover {
+ .tooltiptext {
+ visibility: visible;
+ opacity: 1;
+ }
+ }
+}
+
+.tooltiptext {
+ visibility: hidden;
+ position: absolute;
+ background-color: #555;
+ color: #fff;
+ text-align: center;
+ border-radius: 6px;
+ z-index: 1;
+ opacity: 0;
+ transition: opacity .6s;
+ word-break: keep-all;
+ padding: 5px 10px;
+ left: 110%;
+ font-size: 12px;
+ margin-top: 2px;
+ white-space: nowrap;
+ &:hover {
+ visibility: visible;
+ opacity: 1;
+ }
+ &:after {
+ content: "";
+ position: absolute;
+ top: 50%;
+ right: 100%;
+ margin-top: -5px;
+ border-width: 5px;
+ border-style: solid;
+ border-color: transparent #555 transparent transparent;
+ }
+}
+
.modal {
@include overlay;
position: absolute;
diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json
index 9e8c4c5c28..a426665463 100644
--- a/packages/neuron-ui/src/locales/en.json
+++ b/packages/neuron-ui/src/locales/en.json
@@ -405,6 +405,9 @@
},
"create-account-to-claim-cheque": {
"title": "Claim Cheque Asset with New Account"
+ },
+ "destroy-ckb-account": {
+ "title": "Destroy CKB Asset Account"
}
},
"qrcode": {
@@ -718,7 +721,9 @@
"description": "Description",
"submit": "Submit",
"click-to-edit": "Click to edit",
- "cheque-address-hint": "162 CKBytes capacity is going to be temporarily locked to initialize the token transfer, and it will be automatically unlocked after the token being claimed by the receiver."
+ "cheque-address-hint": "162 CKBytes capacity is going to be temporarily locked to initialize the token transfer, and it will be automatically unlocked after the token being claimed by the receiver.",
+ "destroy": "Destroy",
+ "destroy-desc": "Will returns all CKB of this CKB Asset account to your change address."
},
"receive": {
"notation": "Accept {{symbol}} only"
diff --git a/packages/neuron-ui/src/locales/zh-tw.json b/packages/neuron-ui/src/locales/zh-tw.json
index 62e1fe0f9d..c1a968ba00 100644
--- a/packages/neuron-ui/src/locales/zh-tw.json
+++ b/packages/neuron-ui/src/locales/zh-tw.json
@@ -398,6 +398,9 @@
},
"create-account-to-claim-cheque": {
"title": "創建賬戶並領取 Cheque 資產"
+ },
+ "destroy-ckb-account": {
+ "title": "銷毀 CKB 資產賬戶"
}
},
"qrcode": {
@@ -711,7 +714,9 @@
"description": "備註",
"submit": "提交",
"click-to-edit": "點擊編輯",
- "cheque-address-hint": "向該地址轉賬需要臨時鎖定 162 CKB,對方領取後自動解鎖。"
+ "cheque-address-hint": "向該地址轉賬需要臨時鎖定 162 CKB,對方領取後自動解鎖。",
+ "destroy": "銷毀",
+ "destroy-desc": "將會返還所有該 CKB 資產賬戶的 CKB 到你的找零地址中"
},
"receive": {
"notation": "只接收 {{symbol}} 轉賬"
diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json
index 6172c1be50..f013fbce22 100644
--- a/packages/neuron-ui/src/locales/zh.json
+++ b/packages/neuron-ui/src/locales/zh.json
@@ -398,6 +398,9 @@
},
"create-account-to-claim-cheque": {
"title": "创建账户并领取 Cheque 资产"
+ },
+ "destroy-ckb-account": {
+ "title": "销毁 CKB 资产账户"
}
},
"qrcode": {
@@ -711,7 +714,9 @@
"description": "备注",
"submit": "提交",
"click-to-edit": "点击编辑",
- "cheque-address-hint": "向该地址转账需要临时锁定 162 CKB,对方领取后自动解锁。"
+ "cheque-address-hint": "向该地址转账需要临时锁定 162 CKB,对方领取后自动解锁。",
+ "destroy": "销毁",
+ "destroy-desc": "将会返还所有该 CKB 资产账户的 CKB 到你的找零地址中"
},
"receive": {
"notation": "只接收 {{symbol}} 转账"
diff --git a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts
index 825a124d01..9d1ffd7dc3 100644
--- a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts
+++ b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts
@@ -103,6 +103,7 @@ type Action =
| 'migrate-acp'
| 'check-migrate-acp'
| 'get-sudt-token-info'
+ | 'generate-destroy-ckb-account-tx'
// Cheque
| 'generate-create-cheque-tx'
| 'generate-withdraw-cheque-tx'
diff --git a/packages/neuron-ui/src/services/remote/specialAssets.ts b/packages/neuron-ui/src/services/remote/specialAssets.ts
index 3eaa3eeeb5..6919869ef4 100644
--- a/packages/neuron-ui/src/services/remote/specialAssets.ts
+++ b/packages/neuron-ui/src/services/remote/specialAssets.ts
@@ -2,3 +2,4 @@ import { remoteApi } from './remoteApiWrapper'
export const getSpecialAssets = remoteApi
('get-customized-asset-cells')
export const unlockSpecialAsset = remoteApi('generate-withdraw-customized-cell-tx')
+export const destoryCKBAssetAccount = remoteApi('generate-destroy-ckb-account-tx')
diff --git a/packages/neuron-ui/src/types/App/index.d.ts b/packages/neuron-ui/src/types/App/index.d.ts
index 52902e00ae..672c39bd57 100644
--- a/packages/neuron-ui/src/types/App/index.d.ts
+++ b/packages/neuron-ui/src/types/App/index.d.ts
@@ -86,6 +86,7 @@ declare namespace State {
| 'withdraw-cheque'
| 'claim-cheque'
| 'create-account-to-claim-cheque'
+ | 'destroy-ckb-account'
| 'migrate-acp'
| 'send-nft'
| null
diff --git a/packages/neuron-wallet/package.json b/packages/neuron-wallet/package.json
index 9cbf65b31c..9a5aef1632 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.100.1",
+ "version": "0.100.2",
"private": true,
"author": {
"name": "Nervos Core Dev",
@@ -86,7 +86,7 @@
"electron-notarize": "0.2.1",
"jest-when": "2.7.2",
"lint-staged": "9.2.5",
- "neuron-ui": "0.100.1",
+ "neuron-ui": "0.100.2",
"rimraf": "3.0.0",
"ttypescript": "1.5.10"
}
diff --git a/packages/neuron-wallet/src/controllers/api.ts b/packages/neuron-wallet/src/controllers/api.ts
index cbd76de8c5..ef5709beaf 100644
--- a/packages/neuron-wallet/src/controllers/api.ts
+++ b/packages/neuron-wallet/src/controllers/api.ts
@@ -475,6 +475,10 @@ export default class ApiController {
return this.sudtController.getSUDTTokenInfo(params)
})
+ handle('generate-destroy-ckb-account-tx', async (_, params: { walletID: string, id: number })=>{
+ return this.assetAccountController.destoryCKBAssetAccount(params)
+ })
+
// Hardware wallet
handle('connect-device', async (_, deviceInfo: DeviceInfo) => {
await this.hardwareController.connectDevice(deviceInfo)
diff --git a/packages/neuron-wallet/src/controllers/app/index.ts b/packages/neuron-wallet/src/controllers/app/index.ts
index c989139ea5..9e33b8f48c 100644
--- a/packages/neuron-wallet/src/controllers/app/index.ts
+++ b/packages/neuron-wallet/src/controllers/app/index.ts
@@ -41,8 +41,10 @@ export default class AppController {
public end = async () => {
if (!env.isTestMode) {
- await new NodeController().stopNode()
- await IndexerService.getInstance().stop()
+ await Promise.all([
+ new NodeController().stopNode(),
+ IndexerService.getInstance().stop(),
+ ])
}
}
diff --git a/packages/neuron-wallet/src/controllers/asset-account.ts b/packages/neuron-wallet/src/controllers/asset-account.ts
index 41a39e7588..b43b293e43 100644
--- a/packages/neuron-wallet/src/controllers/asset-account.ts
+++ b/packages/neuron-wallet/src/controllers/asset-account.ts
@@ -97,6 +97,23 @@ export default class AssetAccountController {
}
}
+ public async destoryCKBAssetAccount(
+ params: { walletID: string, id: number }
+ ): Promise> {
+ const account = await AssetAccountService.getAccount(params)
+
+ if (!account) {
+ throw new ServiceHasNoResponse('AssetAccount')
+ }
+
+ const { tx } = await AssetAccountService.destoryCKBAssetAccount(params.walletID, account)
+
+ return {
+ status: ResponseCode.Success,
+ result: tx,
+ }
+ }
+
public async getAccount(params: { walletID: string, id: number }): Promise> {
const account = await AssetAccountService.getAccount(params)
diff --git a/packages/neuron-wallet/src/services/asset-account-service.ts b/packages/neuron-wallet/src/services/asset-account-service.ts
index 2594f698df..612f251368 100644
--- a/packages/neuron-wallet/src/services/asset-account-service.ts
+++ b/packages/neuron-wallet/src/services/asset-account-service.ts
@@ -14,6 +14,7 @@ import { TransactionGenerator } from "./tx"
import WalletService from "./wallets"
import OutPoint from "models/chain/out-point"
import SystemScriptInfo from "models/system-script-info"
+import Input from "models/chain/input"
export default class AssetAccountService {
@@ -74,6 +75,30 @@ export default class AssetAccountService {
return totalBalance
}
+ public static async destoryCKBAssetAccount(walletID: string, assetAccount: AssetAccount) {
+ const cells = await AssetAccountService.getACPCells(assetAccount?.blake160, 'CKBytes')
+ const inputs = cells.map(cell => {
+ return Input.fromObject({
+ previousOutput: cell.outPoint(),
+ capacity: cell.capacity,
+ lock: cell.lockScript(),
+ lockHash: cell.lockHash,
+ since: '0',
+ })
+ })
+ // 1. find next unused address
+ const wallet = WalletService.getInstance().get(walletID)
+
+ const address = await wallet.getNextChangeAddress()
+
+ const tx = await TransactionGenerator.generateDestoryCKBAssetAccountTx(walletID, inputs, address!.blake160)
+
+ return {
+ assetAccount,
+ tx,
+ }
+ }
+
public static async getAll(walletId: string): Promise {
const determineTokenID = (account: AssetAccountEntity) => account.tokenID.startsWith('0x') ? account.tokenID : 'CKBytes'
@@ -302,15 +327,39 @@ export default class AssetAccountService {
[assetAccount.tokenID, assetAccount.blake160]
)
+ const wallet = WalletService.getInstance().get(walletID)
+ const entity = AssetAccountEntity.fromModel(assetAccount)
if (exists[0].exist === 1) {
- throw new Error(`Asset account already exists!`)
+ // For hardware wallet in ckb asset account:
+ // 1. If a ckb account has been created, another one cannot be created;
+ // 2. If a ckb account has been destroyed, ckb account can be created.
+ if (wallet.isHardware() && assetAccount.tokenID === 'CKBytes') {
+ const address = await wallet.getNextAddress()
+ if (address) {
+ const acpCells = await AssetAccountService.getACPCells(address.blake160, 'CKBytes')
+ if (acpCells.length) {
+ throw new Error(`Asset account already exists!`)
+ } else {
+ await getConnection()
+ .createQueryBuilder()
+ .delete()
+ .from(AssetAccountEntity)
+ .where("tokenID = :tokenID AND blake160 = :blake160", {
+ tokenID: 'CKBytes',
+ blake160: assetAccount.blake160,
+ })
+ .execute()
+ }
+ }
+ } else {
+ throw new Error(`Asset account already exists!`)
+ }
}
// 2. send tx
const txHash = await new TransactionSender().sendTx(walletID, tx, password, 0, skipSign)
// 3. save asset account
- const entity = AssetAccountEntity.fromModel(assetAccount)
await connection.manager.save([entity.sudtTokenInfo, entity])
return txHash
diff --git a/packages/neuron-wallet/src/services/live-cell-service.ts b/packages/neuron-wallet/src/services/live-cell-service.ts
index c00137e768..1b9b02a87c 100644
--- a/packages/neuron-wallet/src/services/live-cell-service.ts
+++ b/packages/neuron-wallet/src/services/live-cell-service.ts
@@ -40,7 +40,10 @@ export default class LiveCellService {
return item
}
} else {
- return item
+ // if type is falsy, should return cell that type is empty
+ if (!item.type()) {
+ return item
+ }
}
}
diff --git a/packages/neuron-wallet/src/services/tx/transaction-generator.ts b/packages/neuron-wallet/src/services/tx/transaction-generator.ts
index 61c09917bc..8c0a12907f 100644
--- a/packages/neuron-wallet/src/services/tx/transaction-generator.ts
+++ b/packages/neuron-wallet/src/services/tx/transaction-generator.ts
@@ -717,6 +717,51 @@ export class TransactionGenerator {
return tx
}
+ public static async generateDestoryCKBAssetAccountTx(walletId: string, asssetAccountInputs: Input[], changeBlake160: string) {
+ const secpCellDep = await SystemScriptInfo.getInstance().getSecpCellDep()
+ const assetAccountInfo = new AssetAccountInfo()
+ const anyoneCanPayDep = assetAccountInfo.anyoneCanPayCellDep
+
+ const cellDeps = [secpCellDep, anyoneCanPayDep]
+
+ const {
+ inputs: changeInputs,
+ } = await CellsService.gatherInputs('0', walletId)
+
+ if (changeInputs.length === 0) {
+ throw new CapacityNotEnough()
+ }
+
+ const [changeInput] = changeInputs
+ const allInputs = [...asssetAccountInputs, changeInput]
+ const allCapacities = allInputs.reduce((a, b) => {
+ return a + BigInt(b.capacity as string)
+ }, BigInt(0))
+
+
+ const output = Output.fromObject({
+ capacity: '0',
+ lock: SystemScriptInfo.generateSecpScript(changeBlake160)
+ })
+
+ const tx = Transaction.fromObject({
+ version: '0',
+ headerDeps: [],
+ cellDeps,
+ inputs: allInputs,
+ outputs: [output],
+ outputsData: [output.data],
+ witnesses: []
+ })
+
+ const txSize = TransactionSize.tx(tx)
+ tx.fee = TransactionFee.fee(txSize, BigInt(1e4)).toString()
+ const outputCapacity = allCapacities - BigInt(tx.fee)
+ tx.outputs[0].capacity = outputCapacity.toString()
+
+ return tx
+ }
+
// anyone-can-pay lock, CKB
public static async generateAnyoneCanPayToCKBTx(
walletId: string,
diff --git a/yarn.lock b/yarn.lock
index ac0f2ff80b..b5e936285e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -19869,9 +19869,9 @@ url-parse-lax@^3.0.0:
prepend-http "^2.0.0"
url-parse@^1.4.3:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.1.tgz#d5fa9890af8a5e1f274a2c98376510f6425f6e3b"
- integrity sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==
+ version "1.5.3"
+ resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862"
+ integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"