From 42c5e4ae6c343181f780a29a00b7b71aed6bd744 Mon Sep 17 00:00:00 2001 From: Jaco Date: Thu, 3 Jun 2021 17:47:16 +0300 Subject: [PATCH] Call decoder (#5435) * Call decoder * Allow for extrinsics to be decoded --- packages/page-extrinsics/src/Balance.tsx | 31 ------ packages/page-extrinsics/src/Decoder.tsx | 102 ++++++++++++++++++ .../src/{Selection.tsx => Submission.tsx} | 8 +- packages/page-extrinsics/src/index.tsx | 27 +++-- .../src/InputExtrinsic/SelectMethod.tsx | 8 +- .../src/InputExtrinsic/SelectSection.tsx | 6 +- .../src/InputExtrinsic/index.tsx | 14 ++- 7 files changed, 147 insertions(+), 49 deletions(-) delete mode 100644 packages/page-extrinsics/src/Balance.tsx create mode 100644 packages/page-extrinsics/src/Decoder.tsx rename packages/page-extrinsics/src/{Selection.tsx => Submission.tsx} (94%) diff --git a/packages/page-extrinsics/src/Balance.tsx b/packages/page-extrinsics/src/Balance.tsx deleted file mode 100644 index 3f3c3fa836..0000000000 --- a/packages/page-extrinsics/src/Balance.tsx +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2017-2021 @polkadot/react-query authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -import type { DeriveBalancesAll } from '@polkadot/api-derive/types'; - -import React from 'react'; - -import { InputBalance } from '@polkadot/react-components'; -import { useApi, useCall } from '@polkadot/react-hooks'; - -interface Props { - className?: string; - label?: React.ReactNode; - params?: any; -} - -function BalanceDisplay ({ className = '', label, params }: Props): React.ReactElement { - const { api } = useApi(); - const allBalances = useCall(api.derive.balances?.all, [params]); - - return ( - - ); -} - -export default React.memo(BalanceDisplay); diff --git a/packages/page-extrinsics/src/Decoder.tsx b/packages/page-extrinsics/src/Decoder.tsx new file mode 100644 index 0000000000..53554e18c5 --- /dev/null +++ b/packages/page-extrinsics/src/Decoder.tsx @@ -0,0 +1,102 @@ +// Copyright 2017-2021 @polkadot/app-extrinsics authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { SubmittableExtrinsicFunction } from '@polkadot/api/types'; +import type { Call } from '@polkadot/types/interfaces'; + +import React, { useCallback, useState } from 'react'; + +import { Call as CallDisplay, Input, InputExtrinsic, MarkError, Output } from '@polkadot/react-components'; +import { useApi } from '@polkadot/react-hooks'; +import { assert, isHex } from '@polkadot/util'; + +import { useTranslation } from './translate'; + +interface Props { + className?: string; +} + +interface ExtrinsicInfo { + extrinsicCall: Call | null; + extrinsicError: string | null; + extrinsicFn: SubmittableExtrinsicFunction<'promise'> | null; + extrinsicHash: string | null; + extrinsicHex: string | null; +} + +const DEFAULT_INFO: ExtrinsicInfo = { + extrinsicCall: null, + extrinsicError: null, + extrinsicFn: null, + extrinsicHash: null, + extrinsicHex: null +}; + +function Decoder ({ className }: Props): React.ReactElement { + const { t } = useTranslation(); + const { api } = useApi(); + const [{ extrinsicCall, extrinsicError, extrinsicFn, extrinsicHash }, setExtrinsicInfo] = useState(DEFAULT_INFO); + + const _setExtrinsicHex = useCallback( + (extrinsicHex: string): void => { + try { + assert(isHex(extrinsicHex), 'Expected a hex-encoded call'); + + let extrinsicCall: Call; + + try { + // cater for an extrinsic input... + extrinsicCall = api.createType('Call', api.tx(extrinsicHex).method); + } catch (e) { + extrinsicCall = api.createType('Call', extrinsicHex); + } + + const extrinsicHash = extrinsicCall.hash.toHex(); + const { method, section } = api.registry.findMetaCall(extrinsicCall.callIndex); + const extrinsicFn = api.tx[section][method]; + + setExtrinsicInfo({ ...DEFAULT_INFO, extrinsicCall, extrinsicFn, extrinsicHash, extrinsicHex }); + } catch (e) { + setExtrinsicInfo({ ...DEFAULT_INFO, extrinsicError: (e as Error).message }); + } + }, + [api] + ); + + return ( +
+ ('hex-encoded call')} + onChange={_setExtrinsicHex} + placeholder={t('0x...')} + /> + {extrinsicError && ( + + )} + {extrinsicFn && extrinsicCall && ( + <> + ('decoded call')} + /> + + + )} + {extrinsicHash && ( + + )} +
+ ); +} + +export default React.memo(Decoder); diff --git a/packages/page-extrinsics/src/Selection.tsx b/packages/page-extrinsics/src/Submission.tsx similarity index 94% rename from packages/page-extrinsics/src/Selection.tsx rename to packages/page-extrinsics/src/Submission.tsx index 9f9c65d43c..86c503d23f 100644 --- a/packages/page-extrinsics/src/Selection.tsx +++ b/packages/page-extrinsics/src/Submission.tsx @@ -12,7 +12,11 @@ import { u8aToHex } from '@polkadot/util'; import { useTranslation } from './translate'; -function Selection (): React.ReactElement { +interface Props { + className?: string; +} + +function Selection ({ className }: Props): React.ReactElement { const { t } = useTranslation(); const { apiDefaultTxSudo } = useApi(); const [accountId, setAccountId] = useState(null); @@ -44,7 +48,7 @@ function Selection (): React.ReactElement { ); return ( -
+
('using the selected account')} labelExtra={ diff --git a/packages/page-extrinsics/src/index.tsx b/packages/page-extrinsics/src/index.tsx index e82a65f1b3..95ab154ab3 100644 --- a/packages/page-extrinsics/src/index.tsx +++ b/packages/page-extrinsics/src/index.tsx @@ -4,20 +4,28 @@ import type { AppProps as Props } from '@polkadot/react-components/types'; import React, { useRef } from 'react'; +import { Route, Switch } from 'react-router'; import { Tabs } from '@polkadot/react-components'; -import Selection from './Selection'; +import Decoder from './Decoder'; +import Submission from './Submission'; import { useTranslation } from './translate'; function ExtrinsicsApp ({ basePath }: Props): React.ReactElement { const { t } = useTranslation(); - const itemsRef = useRef([{ - isRoot: true, - name: 'create', - text: t('Submission') - }]); + const itemsRef = useRef([ + { + isRoot: true, + name: 'create', + text: t('Submission') + }, + { + name: 'decode', + text: t('Decode') + } + ]); return (
@@ -25,7 +33,12 @@ function ExtrinsicsApp ({ basePath }: Props): React.ReactElement { basePath={basePath} items={itemsRef.current} /> - + + + + + +
); } diff --git a/packages/react-components/src/InputExtrinsic/SelectMethod.tsx b/packages/react-components/src/InputExtrinsic/SelectMethod.tsx index 4ec09bafaf..ddf77524ef 100644 --- a/packages/react-components/src/InputExtrinsic/SelectMethod.tsx +++ b/packages/react-components/src/InputExtrinsic/SelectMethod.tsx @@ -13,13 +13,15 @@ import Dropdown from '../Dropdown'; interface Props { api: ApiPromise; className?: string; + defaultValue?: string; + isDisabled?: boolean; isError?: boolean; - onChange: (value: SubmittableExtrinsicFunction<'promise'>) => void; + onChange?: (value: SubmittableExtrinsicFunction<'promise'>) => void; options: DropdownOptions; value: SubmittableExtrinsicFunction<'promise'>; } -function SelectMethod ({ api, className = '', isError, onChange, options, value }: Props): React.ReactElement | null { +function SelectMethod ({ api, className = '', defaultValue, isDisabled, isError, onChange, options, value }: Props): React.ReactElement | null { const transform = useCallback( (method: string): SubmittableExtrinsicFunction<'promise'> => api.tx[value.section][method], @@ -33,6 +35,8 @@ function SelectMethod ({ api, className = '', isError, onChange, options, value return ( void; + onChange?: (value: string) => void; options: DropdownOptions; value: SubmittableExtrinsicFunction<'promise'>; } -function SelectSection ({ className = '', defaultValue, isError, onChange, options, value }: Props): React.ReactElement { +function SelectSection ({ className = '', defaultValue, isDisabled, isError, onChange, options, value }: Props): React.ReactElement { return ( ) => void; + onChange?: (value: SubmittableExtrinsicFunction<'promise'>) => void; withLabel?: boolean; } -function InputExtrinsic ({ className = '', defaultValue, help, label, onChange, withLabel }: Props): React.ReactElement { +function InputExtrinsic ({ className = '', defaultValue, help, isDisabled, label, onChange, withLabel }: Props): React.ReactElement { const { api } = useApi(); const [optionsMethod, setOptionsMethod] = useState(() => methodOptions(api, defaultValue.section)); const [optionsSection] = useState(() => sectionOptions(api)); @@ -40,7 +40,7 @@ function InputExtrinsic ({ className = '', defaultValue, help, label, onChange, // set this via callback, since the we are setting a function (alternatively... we have issues) setValue((): SubmittableExtrinsicFunction<'promise'> => newValue); - onChange(newValue); + onChange && onChange(newValue); }, [onChange, value] ); @@ -68,14 +68,18 @@ function InputExtrinsic ({ className = '', defaultValue, help, label, onChange, >