From c57367ea5520bfc3f64689cf5329a990bba94f8d Mon Sep 17 00:00:00 2001 From: Keith Date: Fri, 10 May 2019 18:36:16 +0800 Subject: [PATCH 1/2] feat(neuron-wallet): use real api instead of mock --- .../src/components/Transfer/hooks.ts | 21 +------ .../MainContent/actionCreators/transfer.ts | 7 +-- packages/neuron-ui/src/services/UILayer.ts | 8 +++ packages/neuron-ui/src/utils/const.ts | 2 +- .../neuron-wallet/src/channel/listeners.ts | 58 +------------------ .../neuron-wallet/src/controllers/wallets.ts | 29 ++++++++++ .../neuron-wallet/src/services/wallets.ts | 38 ++++++++++++ 7 files changed, 84 insertions(+), 79 deletions(-) diff --git a/packages/neuron-ui/src/components/Transfer/hooks.ts b/packages/neuron-ui/src/components/Transfer/hooks.ts index b7ab34d230..b8b935d56b 100644 --- a/packages/neuron-ui/src/components/Transfer/hooks.ts +++ b/packages/neuron-ui/src/components/Transfer/hooks.ts @@ -1,8 +1,8 @@ import React, { useCallback, useEffect } from 'react' import { History } from 'history' -import UILayer, { TransferItem } from 'services/UILayer' +import { TransferItem } from 'services/UILayer' import { MainActions, actionCreators } from 'containers/MainContent/reducer' -import { Channel, Routes, CapacityUnit } from 'utils/const' +import { CapacityUnit } from 'utils/const' import initState from 'containers/MainContent/state' export const useUpdateTransferItem = (dispatch: React.Dispatch) => @@ -94,24 +94,7 @@ export const useInitialize = ( if (address) { updateTransferItem('address')(0)(address) } - UILayer.on(Channel.SendCapacity, (_e: Event, args: ChannelResponse) => { - if (args.status) { - history.push(`${Routes.Transaction}/${args.result}`) - } else { - dispatch({ - type: MainActions.UpdateTransfer, - payload: { - submitting: false, - }, - }) - dispatch({ - type: MainActions.ErrorMessage, - payload: { transfer: args.msg }, - }) - } - }) return () => { - UILayer.removeAllListeners(Channel.SendCapacity) dispatch({ type: MainActions.UpdateTransfer, payload: initState.transfer, diff --git a/packages/neuron-ui/src/containers/MainContent/actionCreators/transfer.ts b/packages/neuron-ui/src/containers/MainContent/actionCreators/transfer.ts index dc65cb8ad0..40f50ba0f0 100644 --- a/packages/neuron-ui/src/containers/MainContent/actionCreators/transfer.ts +++ b/packages/neuron-ui/src/containers/MainContent/actionCreators/transfer.ts @@ -1,4 +1,4 @@ -import { sendCapacity, TransferItem } from 'services/UILayer' +import { walletsCall, TransferItem } from 'services/UILayer' import { Message } from 'utils/const' import { verifyAddress } from 'utils/validators' @@ -7,7 +7,6 @@ import { MainActions } from '../reducer' export default { submitTransfer: (items: TransferItem[]) => { - // TODO: verification const errorAction = { type: MainActions.ErrorMessage, payload: { @@ -23,7 +22,7 @@ export default { errorAction.payload.transfer = Message.InvalidAddress return true } - if (+item.capacity < 0) { + if (Number.isNaN(+item.capacity) || +item.capacity < 0) { errorAction.payload.transfer = Message.InvalidCapacity return true } @@ -43,7 +42,7 @@ export default { }, confirmTransfer: ({ items, password }: { items: TransferItem[]; password: string }) => { - sendCapacity(items, password) + walletsCall.sendCapacity({ items, password }) return { type: MainActions.UpdateTransfer, payload: { diff --git a/packages/neuron-ui/src/services/UILayer.ts b/packages/neuron-ui/src/services/UILayer.ts index 82e0a9fc58..eb77608e4b 100644 --- a/packages/neuron-ui/src/services/UILayer.ts +++ b/packages/neuron-ui/src/services/UILayer.ts @@ -25,6 +25,7 @@ export enum WalletsMethod { GetActive = 'getActive', Activate = 'activate', Backup = 'backup', + SendCapacity = 'sendCapacity', } export enum NetworksMethod { @@ -151,6 +152,13 @@ export const walletsCall = instantiateMethodCall(wallets) as { getActive: () => void activate: (id: string) => void backup: (id: string) => void + sendCapacity: (params: { + items: { + address: string + capacity: string + }[] + password: string + }) => void } export const helpers = (method: HelpersMethod, ...params: any) => { diff --git a/packages/neuron-ui/src/utils/const.ts b/packages/neuron-ui/src/utils/const.ts index 9d932f16ff..1210981f5e 100644 --- a/packages/neuron-ui/src/utils/const.ts +++ b/packages/neuron-ui/src/utils/const.ts @@ -1,5 +1,5 @@ export const MAX_NETWORK_NAME_LENGTH = 28 -export const ADDRESS_LENGTH = 40 +export const ADDRESS_LENGTH = 50 export const PAGE_SIZE = 15 export const EXPLORER = 'http://localhost:3000' export const UNREMOVABLE_NETWORK = 'Testnet' diff --git a/packages/neuron-wallet/src/channel/listeners.ts b/packages/neuron-wallet/src/channel/listeners.ts index 4d265f0e4d..77a01c652c 100644 --- a/packages/neuron-wallet/src/channel/listeners.ts +++ b/packages/neuron-wallet/src/channel/listeners.ts @@ -1,6 +1,6 @@ -import { ipcMain, Notification } from 'electron' +import { ipcMain } from 'electron' import { Channel } from '../utils/const' -import { wallets, verifyPassword, transactionHashGen } from '../mock' +import { wallets, verifyPassword } from '../mock' import { ResponseCode } from './wallet' import NetworksController from '../controllers/networks' import TransactionsController from '../controllers/transactions' @@ -31,15 +31,7 @@ const checkPassword = (walletID: string, password: string) => { export default class Listeners { static start = ( - methods: string[] = [ - 'getBalance', - 'checkWalletPassword', - 'sendCapacity', - 'networks', - 'wallets', - 'transactions', - 'helpers', - ], + methods: string[] = ['getBalance', 'checkWalletPassword', 'networks', 'wallets', 'transactions', 'helpers'], ) => { methods.forEach(method => { const descriptor = Object.getOwnPropertyDescriptor(Listeners, method) @@ -79,50 +71,6 @@ export default class Listeners { }) } - /** - * @static sendCapacity - * @memberof ChannelListeners - * @description channel to send capacity - */ - static sendCapacity = () => { - return ipcMain.on( - Channel.SendCapacity, - ( - e: Electron.Event, - { items, password }: { items: { address: string; capacity: string; unit: string }[]; password: string }, - ) => { - setTimeout(() => { - const hash = transactionHashGen() - if (!items.length || !items[0].address) { - e.sender.send(Channel.SendCapacity, { - status: ResponseCode.Fail, - msg: 'Address not specified', - }) - return - } - // TODO: verify password - // TODO: verify capacity - const notification = new Notification({ - title: `Send Capacity`, - body: `Send Capacity to CKB with ${JSON.stringify( - { - items, - password, - }, - null, - 2, - )}`, - }) - notification.show() - e.sender.send(Channel.SendCapacity, { - status: ResponseCode.Success, - result: hash, - }) - }, 3000) - }, - ) - } - /** * @method networks * @memberof ChannelListeners diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts index b5730fdc3f..e21cb6b7e3 100644 --- a/packages/neuron-wallet/src/controllers/wallets.ts +++ b/packages/neuron-wallet/src/controllers/wallets.ts @@ -16,6 +16,7 @@ export enum WalletsMethod { Delete = 'delete', GetActive = 'getActive', Activate = 'activate', + SendCapacity = 'sendCapacity', } class WalletsController { @@ -269,6 +270,34 @@ class WalletsController { } // TODO: verification } + + public static sendCapacity = async (params: { + items: { + address: CKBComponents.Hash256 + capacity: CKBComponents.Capacity + unit: 'byte' | 'shannon' + }[] + password: string + }) => { + if (!params) { + return { + status: ResponseCode.Fail, + msg: 'Parameters not received', + } + } + try { + const result = await WalletsController.service.sendCapacity(params.items, params.password) + return { + status: ResponseCode.Success, + result, + } + } catch (err) { + return { + status: ResponseCode.Fail, + msg: err.message, + } + } + } } export default WalletsController diff --git a/packages/neuron-wallet/src/services/wallets.ts b/packages/neuron-wallet/src/services/wallets.ts index 1b6952d07e..ddf9404e5e 100644 --- a/packages/neuron-wallet/src/services/wallets.ts +++ b/packages/neuron-wallet/src/services/wallets.ts @@ -1,4 +1,7 @@ import { v4 } from 'uuid' + +import ckbCore from '../core' +import TransactionsService from './transactions' import WalletStore, { WalletData } from '../store/walletStore' import Key, { Addresses } from '../keys/key' import { Keystore } from '../keys/keystore' @@ -55,4 +58,39 @@ export default class WalletService { public getActive = (): WalletData => { return walletStore.getActiveWallet() } + + /** + * transactions related + */ + public sendCapacity = ( + items: { + address: CKBComponents.Hash256 + capacity: CKBComponents.Capacity + unit: 'byte' | 'shannon' + }[], + password: string, + ) => { + // TODO: verify password + if (!password) { + throw new Error('Incorrect password') + } + + const changeAddress = walletStore.getActiveWallet().addresses.change[0].address + + // TODO: this is always success code hash, should be replaced in the future + const codeHash = '0x0000000000000000000000000000000000000000000000000000000000000001' + const lockhashes = items.map(({ address }) => + ckbCore.utils.lockScriptToHash({ + // TODO: has be updated with sdk@0.11.0 + binaryHash: codeHash, + args: [ckbCore.utils.blake160(address)], + }), + ) + const targetOutputs = items.map(item => ({ + ...item, + capacity: (BigInt(item.capacity) * (item.unit === 'byte' ? BigInt(1) : BigInt(10 ** 8))).toString(), + })) + + return TransactionsService.generateTx(lockhashes, targetOutputs, changeAddress) + } } From b424a137ce5841f9f75014f30c0011921a4c75f0 Mon Sep 17 00:00:00 2001 From: Keith Date: Sat, 11 May 2019 11:10:08 +0800 Subject: [PATCH 2/2] feat(neuron-wallet): connect wallet controller to transaction service --- .../src/components/Transfer/hooks.ts | 17 +++++++-- .../src/components/Transfer/index.tsx | 36 ++++++++++--------- .../MainContent/actionCreators/transfer.ts | 4 +-- .../src/containers/Providers/hooks.ts | 13 +++++-- packages/neuron-ui/src/services/UILayer.ts | 1 + .../src/widgets/BannerMessages/index.tsx | 1 + .../neuron-ui/src/widgets/QRScanner/index.tsx | 2 +- .../neuron-wallet/src/controllers/wallets.ts | 10 ++++-- .../neuron-wallet/src/services/wallets.ts | 12 +++++-- 9 files changed, 65 insertions(+), 31 deletions(-) diff --git a/packages/neuron-ui/src/components/Transfer/hooks.ts b/packages/neuron-ui/src/components/Transfer/hooks.ts index b8b935d56b..6c817a70ac 100644 --- a/packages/neuron-ui/src/components/Transfer/hooks.ts +++ b/packages/neuron-ui/src/components/Transfer/hooks.ts @@ -40,9 +40,9 @@ export const useOnPasswordChange = (dispatch: React.Dispatch) => [dispatch], ) -export const useOnConfirm = (dispatch: React.Dispatch) => +export const useOnConfirm = (dispatch: React.Dispatch, setLoading: Function) => useCallback( - (items: TransferItem[], pwd: string) => () => { + (id: string, items: TransferItem[], pwd: string) => () => { dispatch({ type: MainActions.SetDialog, payload: { @@ -53,16 +53,18 @@ export const useOnConfirm = (dispatch: React.Dispatch) => type: MainActions.UpdatePassword, payload: '', }) + setLoading(true) setTimeout(() => { dispatch( actionCreators.confirmTransfer({ + id, items, password: pwd, }), ) }, 10) }, - [dispatch], + [dispatch, setLoading], ) export const useOnItemChange = (updateTransferItem: Function) => (field: string, idx: number) => ( @@ -102,6 +104,14 @@ export const useInitialize = ( } }, [address, dispatch, history, updateTransferItem]) +export const useMessageListener = (id: string, messageId: string | null, title: string, setLoading: Function) => { + useEffect(() => { + if (title === 'Transaction' && messageId === id) { + setLoading(false) + } + }, [title, messageId, id, setLoading]) +} + export default { useUpdateTransferItem, useOnSubmit, @@ -109,5 +119,6 @@ export default { useOnConfirm, useOnItemChange, useDropdownItems, + useMessageListener, useInitialize, } diff --git a/packages/neuron-ui/src/components/Transfer/index.tsx b/packages/neuron-ui/src/components/Transfer/index.tsx index 64bb058440..b0ae399d48 100644 --- a/packages/neuron-ui/src/components/Transfer/index.tsx +++ b/packages/neuron-ui/src/components/Transfer/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useState, useMemo } from 'react' import { RouteComponentProps } from 'react-router-dom' import { Container, Row, Col, Card, Form, Button, Alert, InputGroup } from 'react-bootstrap' import { useTranslation } from 'react-i18next' @@ -15,6 +15,8 @@ import { ContentProps } from 'containers/MainContent' import { useOnDialogCancel } from 'containers/MainContent/hooks' import { PlaceHolders } from 'utils/const' +import { useNeuronWallet } from 'utils/hooks' + import { useUpdateTransferItem, useOnSubmit, @@ -23,6 +25,7 @@ import { useOnItemChange, useDropdownItems, useInitialize, + useMessageListener, } from './hooks' const Transfer = ({ @@ -38,13 +41,23 @@ const Transfer = ({ }: React.PropsWithoutRef>) => { const { t } = useTranslation() + const id = useMemo(() => Math.round(Math.random() * 1000).toString(), []) + + const { messages } = useNeuronWallet() + + const [loading, setLoading] = useState(false) + + const lastMessage = messages[messages.length - 1] || { title: '', id: null } + + useMessageListener(id, lastMessage.id, lastMessage.title, setLoading) + const updateTransferItem = useUpdateTransferItem(dispatch) const onSubmit = useOnSubmit(dispatch) const onPasswordChange = useOnPasswordChange(dispatch) - const onConfirm = useOnConfirm(dispatch) + const onConfirm = useOnConfirm(dispatch, setLoading) const onCancel = useOnDialogCancel(dispatch) @@ -54,8 +67,6 @@ const Transfer = ({ useInitialize(address, dispatch, history, updateTransferItem) - const disabled = transfer.submitting && !errorMsgs.transfer - return ( @@ -70,7 +81,7 @@ const Transfer = ({ ))} - @@ -123,7 +127,7 @@ const Transfer = ({ message={} password={password} onChange={onPasswordChange} - onSubmit={onConfirm(transfer.items, password)} + onSubmit={onConfirm(id, transfer.items, password)} onCancel={onCancel} /> diff --git a/packages/neuron-ui/src/containers/MainContent/actionCreators/transfer.ts b/packages/neuron-ui/src/containers/MainContent/actionCreators/transfer.ts index 40f50ba0f0..4baf503297 100644 --- a/packages/neuron-ui/src/containers/MainContent/actionCreators/transfer.ts +++ b/packages/neuron-ui/src/containers/MainContent/actionCreators/transfer.ts @@ -41,8 +41,8 @@ export default { } }, - confirmTransfer: ({ items, password }: { items: TransferItem[]; password: string }) => { - walletsCall.sendCapacity({ items, password }) + confirmTransfer: ({ id, items, password }: { id: string; items: TransferItem[]; password: string }) => { + walletsCall.sendCapacity({ id, items, password }) return { type: MainActions.UpdateTransfer, payload: { diff --git a/packages/neuron-ui/src/containers/Providers/hooks.ts b/packages/neuron-ui/src/containers/Providers/hooks.ts index 898578e11e..501fd82abc 100644 --- a/packages/neuron-ui/src/containers/Providers/hooks.ts +++ b/packages/neuron-ui/src/containers/Providers/hooks.ts @@ -138,6 +138,10 @@ export const useChannelListeners = (i18n: any, chain: any, dispatch: React.Dispa }) break } + case WalletsMethod.SendCapacity: { + history.push(`${Routes.Transaction}/${args.result}`) + break + } default: { break } @@ -145,16 +149,19 @@ export const useChannelListeners = (i18n: any, chain: any, dispatch: React.Dispa } else { const time = new Date().getTime() if (method === WalletsMethod.GetActive) { - // don't show this error in wizard view return } + const title = method === WalletsMethod.SendCapacity ? 'Transaction' : 'Wallet' + const { content, id } = + typeof args.msg === 'string' ? { content: args.msg, id: null } : args.msg || { content: '', id: null } dispatch({ type: ProviderActions.AddMessage, payload: { category: 'danger', - title: 'Wallet', - content: args.msg, + title, + id, + content, time, actions: [], dismiss: () => { diff --git a/packages/neuron-ui/src/services/UILayer.ts b/packages/neuron-ui/src/services/UILayer.ts index eb77608e4b..b2f331bcdc 100644 --- a/packages/neuron-ui/src/services/UILayer.ts +++ b/packages/neuron-ui/src/services/UILayer.ts @@ -153,6 +153,7 @@ export const walletsCall = instantiateMethodCall(wallets) as { activate: (id: string) => void backup: (id: string) => void sendCapacity: (params: { + id: string items: { address: string capacity: string diff --git a/packages/neuron-ui/src/widgets/BannerMessages/index.tsx b/packages/neuron-ui/src/widgets/BannerMessages/index.tsx index 0b87b67ea9..2fcbfa44e2 100644 --- a/packages/neuron-ui/src/widgets/BannerMessages/index.tsx +++ b/packages/neuron-ui/src/widgets/BannerMessages/index.tsx @@ -32,6 +32,7 @@ const ActionZone = styled.div` export interface Message { title: string content: string + id: string | null time: number category: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'dark' | 'light' actions: { label: string; action: React.MouseEventHandler | string }[] diff --git a/packages/neuron-ui/src/widgets/QRScanner/index.tsx b/packages/neuron-ui/src/widgets/QRScanner/index.tsx index 1544657290..500cf097f4 100644 --- a/packages/neuron-ui/src/widgets/QRScanner/index.tsx +++ b/packages/neuron-ui/src/widgets/QRScanner/index.tsx @@ -94,7 +94,7 @@ const QRScanner = ({ title, label, onConfirm, styles }: QRScannerProps) => { onClick={() => { setOpen(true) }} - onKeyPress={() => setOpen(true)} + onKeyPress={() => {}} type="button" > diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts index e21cb6b7e3..007f7b4eea 100644 --- a/packages/neuron-wallet/src/controllers/wallets.ts +++ b/packages/neuron-wallet/src/controllers/wallets.ts @@ -272,6 +272,7 @@ class WalletsController { } public static sendCapacity = async (params: { + id: string items: { address: CKBComponents.Hash256 capacity: CKBComponents.Capacity @@ -286,15 +287,18 @@ class WalletsController { } } try { - const result = await WalletsController.service.sendCapacity(params.items, params.password) + const hash = await WalletsController.service.sendCapacity(params.items, params.password) return { status: ResponseCode.Success, - result, + result: hash, } } catch (err) { return { status: ResponseCode.Fail, - msg: err.message, + msg: { + content: `Error: "${err.message}"`, + id: params.id, + }, } } } diff --git a/packages/neuron-wallet/src/services/wallets.ts b/packages/neuron-wallet/src/services/wallets.ts index ddf9404e5e..422f3c114f 100644 --- a/packages/neuron-wallet/src/services/wallets.ts +++ b/packages/neuron-wallet/src/services/wallets.ts @@ -62,7 +62,7 @@ export default class WalletService { /** * transactions related */ - public sendCapacity = ( + public sendCapacity = async ( items: { address: CKBComponents.Hash256 capacity: CKBComponents.Capacity @@ -79,9 +79,10 @@ export default class WalletService { // TODO: this is always success code hash, should be replaced in the future const codeHash = '0x0000000000000000000000000000000000000000000000000000000000000001' + const lockhashes = items.map(({ address }) => ckbCore.utils.lockScriptToHash({ - // TODO: has be updated with sdk@0.11.0 + // TODO: binaryHash has be updated to codeHash with sdk@0.11.0 binaryHash: codeHash, args: [ckbCore.utils.blake160(address)], }), @@ -91,6 +92,11 @@ export default class WalletService { capacity: (BigInt(item.capacity) * (item.unit === 'byte' ? BigInt(1) : BigInt(10 ** 8))).toString(), })) - return TransactionsService.generateTx(lockhashes, targetOutputs, changeAddress) + const transaction = (await TransactionsService.generateTx( + lockhashes, + targetOutputs, + changeAddress, + )) as CKBComponents.RawTransaction + return ckbCore.rpc.sendTransaction(transaction) } }