From 874e781bb31697eee732dd881c1e61ac00e2c8fd Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 20 Aug 2019 10:43:42 +0800 Subject: [PATCH] feat(neuron-ui): use real cycles --- .../neuron-ui/src/components/Send/hooks.ts | 17 +++-- .../components/TransactionFeePanel/index.tsx | 2 +- .../neuron-ui/src/services/remote/wallets.ts | 5 +- .../stateProvider/actionCreators/app.ts | 2 +- .../stateProvider/actionCreators/wallets.ts | 2 +- .../neuron-ui/src/tests/formatters.test.ts | 73 ++++++++++++++++++- .../neuron-ui/src/types/Controller/index.d.ts | 7 +- .../neuron-ui/src/utils/addressesToBalance.ts | 5 -- packages/neuron-ui/src/utils/formatters.ts | 61 +++++++++++++++- .../src/controllers/wallets/index.ts | 12 +-- 10 files changed, 149 insertions(+), 37 deletions(-) delete mode 100644 packages/neuron-ui/src/utils/addressesToBalance.ts diff --git a/packages/neuron-ui/src/components/Send/hooks.ts b/packages/neuron-ui/src/components/Send/hooks.ts index f53ac59705..2a8240b596 100644 --- a/packages/neuron-ui/src/components/Send/hooks.ts +++ b/packages/neuron-ui/src/components/Send/hooks.ts @@ -6,7 +6,7 @@ import { calculateCycles } from 'services/remote/wallets' import { Message } from 'utils/const' import { verifyAddress, verifyAmountRange } from 'utils/validators' -import { CKBToShannonFormatter } from 'utils/formatters' +import { outputsToTotalCapacity } from 'utils/formatters' import { TransactionOutput } from '.' let cyclesTimer: ReturnType @@ -97,10 +97,7 @@ const useOnTransactionChange = (walletID: string, items: TransactionOutput[], di if (validateTransactionParams({ items })) { calculateCycles({ walletID, - items: items.map(item => ({ - address: item.address, - capacity: CKBToShannonFormatter(item.amount, item.unit), - })), + capacities: outputsToTotalCapacity(items), }) .then(response => { if (response.status) { @@ -153,7 +150,12 @@ const useOnItemChange = (updateTransactionOutput: Function) => value?: string ) => { if (undefined !== value) { - updateTransactionOutput(field)(idx)(value) + if (field === 'amount') { + const amount = value.replace(/[^\d.]/g, '') + updateTransactionOutput(field)(idx)(amount) + } else { + updateTransactionOutput(field)(idx)(value) + } } }, [updateTransactionOutput] @@ -173,9 +175,10 @@ const useUpdateTransactionPrice = (dispatch: StateDispatch) => useCallback( (_e: React.FormEvent, value?: string) => { if (undefined !== value) { + const price = value.replace(/[^\d]/g, '') dispatch({ type: AppActions.UpdateSendPrice, - payload: value.trim(), + payload: price, }) } }, diff --git a/packages/neuron-ui/src/components/TransactionFeePanel/index.tsx b/packages/neuron-ui/src/components/TransactionFeePanel/index.tsx index 340bec3dad..9c0ee89b1a 100644 --- a/packages/neuron-ui/src/components/TransactionFeePanel/index.tsx +++ b/packages/neuron-ui/src/components/TransactionFeePanel/index.tsx @@ -76,7 +76,7 @@ const TransactionFee: React.FunctionComponent = ({ - + {actionSpacer} diff --git a/packages/neuron-ui/src/services/remote/wallets.ts b/packages/neuron-ui/src/services/remote/wallets.ts index 81934fdc3b..282fcfca7c 100644 --- a/packages/neuron-ui/src/services/remote/wallets.ts +++ b/packages/neuron-ui/src/services/remote/wallets.ts @@ -5,12 +5,15 @@ const CONTROLLER_NAME = 'wallets' export const updateWallet = controllerMethodWrapper(CONTROLLER_NAME)( controller => (params: Controller.UpdateWalletParams) => controller.update(params) ) + export const getCurrentWallet = controllerMethodWrapper(CONTROLLER_NAME)(controller => () => controller.getCurrent()) + export const getWalletList = controllerMethodWrapper(CONTROLLER_NAME)(controller => () => controller.getAll()) export const createWallet = controllerMethodWrapper(CONTROLLER_NAME)( controller => (params: Controller.CreateWalletParams) => controller.create(params) ) + export const importMnemonic = controllerMethodWrapper(CONTROLLER_NAME)( controller => (params: Controller.ImportMnemonicParams) => controller.importMnemonic(params) ) @@ -43,7 +46,7 @@ export const updateAddressDescription = controllerMethodWrapper(CONTROLLER_NAME) ) export const calculateCycles = controllerMethodWrapper(CONTROLLER_NAME)( - controller => (params: Controller.CalculateCycles) => controller.calculateCycles(params) + controller => (params: Controller.ComputeCycles) => controller.computeCycles(params) ) export default { diff --git a/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts b/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts index d0be8121d9..ff5aa97ce5 100644 --- a/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts +++ b/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts @@ -3,7 +3,7 @@ import { getNeuronWalletState } from 'services/remote' import initStates from 'states/initStates' import { Routes } from 'utils/const' import { WalletWizardPath } from 'components/WalletWizard' -import addressesToBalance from 'utils/addressesToBalance' +import { addressesToBalance } from 'utils/formatters' import { wallets as walletsCache, addresses as addressesCache, diff --git a/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts b/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts index 0cf17cadeb..3b5f3c6753 100644 --- a/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts +++ b/packages/neuron-ui/src/states/stateProvider/actionCreators/wallets.ts @@ -19,7 +19,7 @@ import { WalletWizardPath } from 'components/WalletWizard' import i18n from 'utils/i18n' import { wallets as walletsCache, currentWallet as currentWalletCache } from 'utils/localCache' import { Routes } from 'utils/const' -import addressesToBalance from 'utils/addressesToBalance' +import { addressesToBalance } from 'utils/formatters' import { NeuronWalletActions } from '../reducer' import { addNotification, addPopup } from './app' diff --git a/packages/neuron-ui/src/tests/formatters.test.ts b/packages/neuron-ui/src/tests/formatters.test.ts index ecb15da3c1..1ab0735fc9 100644 --- a/packages/neuron-ui/src/tests/formatters.test.ts +++ b/packages/neuron-ui/src/tests/formatters.test.ts @@ -1,5 +1,12 @@ import { CapacityUnit } from 'utils/const' -import { currencyFormatter, currencyCode, CKBToShannonFormatter, shannonToCKBFormatter } from 'utils/formatters' +import { + currencyFormatter, + currencyCode, + CKBToShannonFormatter, + shannonToCKBFormatter, + addressesToBalance, + outputsToTotalCapacity, +} from 'utils/formatters' describe(`formatters`, () => { it(`currencyFormatter`, () => { @@ -201,5 +208,69 @@ describe(`formatters`, () => { expect(shannonToCKBFormatter(fixture.source)).toBe(fixture.target) }) }) + + it('addresses to balance', () => { + const fixture = [ + { + address: 'ckt1q9gry5zg8stq8ruq5wfz3lm5wn2k7qw3ulsfmdhe98f2j1', + identifier: '4040ba0ed8a361c59c30bb92f46128f95eaa9bcb', + description: 'description', + type: 0 as 0 | 1, + txCount: 0, + balance: '100', + index: 0, + }, + { + address: 'ckt1q9gry5zg8stq8ruq5wfz3lm5wn2k7qw3ulsfmdhe98f2j3', + identifier: '4040ba0ed8a361c59c30bb92f46128f95eaa9bcb', + description: 'description', + type: 0 as 0 | 1, + txCount: 123, + balance: '10000', + index: 1, + }, + { + address: 'ckt1q9gry5zg8stq8ruq5wfz3lm5wn2k7qw3ulsfmdhe98f2j2', + identifier: '4040ba0ed8a361c59c30bb92f46128f95eaa9bcd', + description: 'description', + type: 1 as 0 | 1, + txCount: 0, + balance: '200', + index: 2, + }, + { + address: 'ckt1q9gry5zg8stq8ruq5wfz3lm5wn2k7qw3ulsfmdhe98f2jd', + identifier: '4040ba0ed8a361c59c30bb92f46128f95eaa9bcd', + description: 'description', + type: 1 as 0 | 1, + txCount: 123, + balance: '10000', + index: 3, + }, + ] + expect(addressesToBalance(fixture)).toBe('20300') + }) + + it('outputsToTotalCapacity', () => { + const fixture: any = [ + { + amount: '100', + unit: 'CKB', + }, + { + amount: '10000', + unit: 'CKB', + }, + { + amount: '200', + unit: 'CKB', + }, + { + amount: '10000', + unit: 'CKB', + }, + ] + expect(outputsToTotalCapacity(fixture)).toBe('20300') + }) }) }) diff --git a/packages/neuron-ui/src/types/Controller/index.d.ts b/packages/neuron-ui/src/types/Controller/index.d.ts index fd0a527f51..a9756b785d 100644 --- a/packages/neuron-ui/src/types/Controller/index.d.ts +++ b/packages/neuron-ui/src/types/Controller/index.d.ts @@ -45,12 +45,9 @@ declare namespace Controller { description: string } - interface CalculateCycles { + interface ComputeCycles { walletID: string - items: { - address: string - capacity: string - } + capacities: string } type GetAddressesByWalletIDParams = string diff --git a/packages/neuron-ui/src/utils/addressesToBalance.ts b/packages/neuron-ui/src/utils/addressesToBalance.ts deleted file mode 100644 index 5feebc1358..0000000000 --- a/packages/neuron-ui/src/utils/addressesToBalance.ts +++ /dev/null @@ -1,5 +0,0 @@ -/* globals BigInt */ -const addressesToBalance = (addresses: State.Address[] = []) => { - return addresses.reduce((total, addr) => total + BigInt(addr.balance || 0), BigInt(0)).toString() -} -export default addressesToBalance diff --git a/packages/neuron-ui/src/utils/formatters.ts b/packages/neuron-ui/src/utils/formatters.ts index 76d1a7735b..efb1a8f1f6 100644 --- a/packages/neuron-ui/src/utils/formatters.ts +++ b/packages/neuron-ui/src/utils/formatters.ts @@ -4,6 +4,12 @@ import { CapacityUnit } from './const' const base = 10e9 const numberParser = (value: string, exchange: string) => { + if (Number.isNaN(+value)) { + throw new TypeError('Value is not a valid number') + } + if (Number.isNaN(+exchange)) { + throw new TypeError('Exchange is not a valid number') + } const res = (BigInt(value) * BigInt(+exchange * base)).toString() const integer = res.slice(0, res.length - 10) const decimal = res.slice(res.length - 10).replace(/0+$/, '') @@ -41,10 +47,18 @@ export type currencyCode = 'CKB' | 'CNY' | 'USD' * @returns */ export const currencyFormatter = ( - shannons: string, + shannons: string = '0', unit: currencyCode = 'CKB', exchange: string = '0.000000001' ): string => { + if (Number.isNaN(+shannons)) { + throw new TypeError(`Shannons is not a valid number`) + } + + if (Number.isNaN(+exchange)) { + throw new TypeError(`Exchange is not a valid number`) + } + const [integer, decimal] = numberParser(shannons, exchange) const dot = '.' const delimiter = ',' @@ -60,12 +74,16 @@ export const currencyFormatter = ( return `${integer.replace(/\B(?=(\d{3})+(?!\d))/g, delimiter)}${dot}${decimal} ${unit}` } -export const CKBToShannonFormatter = (amount: string, uint: CapacityUnit) => { +export const CKBToShannonFormatter = (amount: string = '0', unit: CapacityUnit) => { + if (Number.isNaN(+amount)) { + console.warn(`Amount is not a valid number`) + return `${amount} ${unit}` + } const [integer = '0', decimal = ''] = amount.split('.') const decimalLength = 10 ** decimal.length const num = integer + decimal - switch (uint) { + switch (unit) { case CapacityUnit.CKB: { return (BigInt(num) * BigInt(1e8 / decimalLength)).toString() } @@ -81,7 +99,11 @@ export const CKBToShannonFormatter = (amount: string, uint: CapacityUnit) => { } } -export const shannonToCKBFormatter = (shannon: string) => { +export const shannonToCKBFormatter = (shannon: string = '0') => { + if (Number.isNaN(+shannon)) { + console.warn(`Shannon is not a valid number`) + return shannon + } const sign = shannon.startsWith('-') ? '-' : '' const unsignedShannon = shannon.replace(/^-?0*/, '') let unsignedCKB = '' @@ -106,6 +128,10 @@ export const shannonToCKBFormatter = (shannon: string) => { } export const localNumberFormatter = (num: string | number = 0) => { + if (Number.isNaN(+num)) { + console.warn(`Nuumber is not a valid number`) + return num + } return numberFormatter.format(+num) } @@ -114,9 +140,34 @@ export const uniformTimeFormatter = (time: string | number | Date) => { } export const priceToFee = (price: string, cycles: string) => { + if (Number.isNaN(+price)) { + console.warn(`Price is not a valid number`) + return `0` + } return (BigInt(price) * BigInt(cycles)).toString() } +export const addressesToBalance = (addresses: State.Address[] = []) => { + return addresses + .reduce((total, addr) => { + if (Number.isNaN(+addr.balance)) { + return total + } + return total + BigInt(addr.balance || 0) + }, BigInt(0)) + .toString() +} + +export const outputsToTotalCapacity = (outputs: { amount: string; unit: CapacityUnit }[]) => { + const totalCapacity = outputs.reduce((total, cur) => { + if (Number.isNaN(+cur.amount)) { + return total + } + return total + BigInt(CKBToShannonFormatter(cur.amount, cur.unit)) + }, BigInt(0)) + return totalCapacity.toString() +} + export default { queryFormatter, currencyFormatter, @@ -125,4 +176,6 @@ export default { localNumberFormatter, uniformTimeFormatter, priceToFee, + addressesToBalance, + outputsToTotalCapacity, } diff --git a/packages/neuron-wallet/src/controllers/wallets/index.ts b/packages/neuron-wallet/src/controllers/wallets/index.ts index 726b3665ec..8c7cf1071c 100644 --- a/packages/neuron-wallet/src/controllers/wallets/index.ts +++ b/packages/neuron-wallet/src/controllers/wallets/index.ts @@ -373,7 +373,7 @@ export default class WalletsController { } @CatchControllerError - public static async computeCycles(params: { id: string; walletID: string; capacities: string }) { + public static async computeCycles(params: { walletID: string; capacities: string }) { if (!params) { throw new IsRequired('Parameters') } @@ -392,16 +392,6 @@ export default class WalletsController { } } - @CatchControllerError - public static async calculateCycles(params: { walletID: string; items: { address: string; capacity: string }[] }) { - // TODO: This is a mock cycles - const cycles = params.items.filter(item => +item.capacity > 0).length.toString() - return { - status: ResponseCode.Success, - result: cycles, - } - } - @CatchControllerError public static async updateAddressDescription({ walletID,