Call decoder (#5435)
* Call decoder * Allow for extrinsics to be decoded
This commit is contained in:
parent
6e5d2efbdd
commit
42c5e4ae6c
@ -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<Props> {
|
||||
const { api } = useApi();
|
||||
const allBalances = useCall<DeriveBalancesAll>(api.derive.balances?.all, [params]);
|
||||
|
||||
return (
|
||||
<InputBalance
|
||||
className={className}
|
||||
defaultValue={allBalances?.freeBalance}
|
||||
isDisabled
|
||||
label={label}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(BalanceDisplay);
|
||||
102
packages/page-extrinsics/src/Decoder.tsx
Normal file
102
packages/page-extrinsics/src/Decoder.tsx
Normal file
@ -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<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const [{ extrinsicCall, extrinsicError, extrinsicFn, extrinsicHash }, setExtrinsicInfo] = useState<ExtrinsicInfo>(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 (
|
||||
<div className={className}>
|
||||
<Input
|
||||
isError={!extrinsicFn}
|
||||
label={t<string>('hex-encoded call')}
|
||||
onChange={_setExtrinsicHex}
|
||||
placeholder={t<string>('0x...')}
|
||||
/>
|
||||
{extrinsicError && (
|
||||
<MarkError content={extrinsicError} />
|
||||
)}
|
||||
{extrinsicFn && extrinsicCall && (
|
||||
<>
|
||||
<InputExtrinsic
|
||||
defaultValue={extrinsicFn}
|
||||
isDisabled
|
||||
label={t<string>('decoded call')}
|
||||
/>
|
||||
<CallDisplay
|
||||
className='details'
|
||||
value={extrinsicCall}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{extrinsicHash && (
|
||||
<Output
|
||||
isDisabled
|
||||
label='encoded call hash'
|
||||
value={extrinsicHash}
|
||||
withCopy
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Decoder);
|
||||
@ -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<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { apiDefaultTxSudo } = useApi();
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
@ -44,7 +48,7 @@ function Selection (): React.ReactElement {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className='extrinsics--Selection'>
|
||||
<div className={className}>
|
||||
<InputAddress
|
||||
label={t<string>('using the selected account')}
|
||||
labelExtra={
|
||||
@ -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<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const itemsRef = useRef([{
|
||||
isRoot: true,
|
||||
name: 'create',
|
||||
text: t<string>('Submission')
|
||||
}]);
|
||||
const itemsRef = useRef([
|
||||
{
|
||||
isRoot: true,
|
||||
name: 'create',
|
||||
text: t<string>('Submission')
|
||||
},
|
||||
{
|
||||
name: 'decode',
|
||||
text: t<string>('Decode')
|
||||
}
|
||||
]);
|
||||
|
||||
return (
|
||||
<main className='extrinsics--App'>
|
||||
@ -25,7 +33,12 @@ function ExtrinsicsApp ({ basePath }: Props): React.ReactElement<Props> {
|
||||
basePath={basePath}
|
||||
items={itemsRef.current}
|
||||
/>
|
||||
<Selection />
|
||||
<Switch>
|
||||
<Route path={`${basePath}/decode`}><Decoder /></Route>
|
||||
<Route>
|
||||
<Submission />
|
||||
</Route>
|
||||
</Switch>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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<Props> | null {
|
||||
function SelectMethod ({ api, className = '', defaultValue, isDisabled, isError, onChange, options, value }: Props): React.ReactElement<Props> | 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 (
|
||||
<Dropdown
|
||||
className={`ui--DropdownLinked-Items ${className}`}
|
||||
defaultValue={defaultValue}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
|
||||
@ -11,17 +11,19 @@ import Dropdown from '../Dropdown';
|
||||
interface Props {
|
||||
className?: string;
|
||||
defaultValue?: string;
|
||||
isDisabled?: boolean;
|
||||
isError?: boolean;
|
||||
onChange: (value: string) => void;
|
||||
onChange?: (value: string) => void;
|
||||
options: DropdownOptions;
|
||||
value: SubmittableExtrinsicFunction<'promise'>;
|
||||
}
|
||||
|
||||
function SelectSection ({ className = '', defaultValue, isError, onChange, options, value }: Props): React.ReactElement<Props> {
|
||||
function SelectSection ({ className = '', defaultValue, isDisabled, isError, onChange, options, value }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<Dropdown
|
||||
className={`ui--DropdownLinked-Sections ${className}`}
|
||||
defaultValue={defaultValue}
|
||||
isDisabled={isDisabled}
|
||||
isError={isError}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
|
||||
@ -22,11 +22,11 @@ interface Props {
|
||||
isError?: boolean;
|
||||
isPrivate?: boolean;
|
||||
label: React.ReactNode;
|
||||
onChange: (value: SubmittableExtrinsicFunction<'promise'>) => void;
|
||||
onChange?: (value: SubmittableExtrinsicFunction<'promise'>) => void;
|
||||
withLabel?: boolean;
|
||||
}
|
||||
|
||||
function InputExtrinsic ({ className = '', defaultValue, help, label, onChange, withLabel }: Props): React.ReactElement<Props> {
|
||||
function InputExtrinsic ({ className = '', defaultValue, help, isDisabled, label, onChange, withLabel }: Props): React.ReactElement<Props> {
|
||||
const { api } = useApi();
|
||||
const [optionsMethod, setOptionsMethod] = useState<DropdownOptions>(() => methodOptions(api, defaultValue.section));
|
||||
const [optionsSection] = useState<DropdownOptions>(() => 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,
|
||||
>
|
||||
<SelectSection
|
||||
className='small'
|
||||
onChange={_onSectionChange}
|
||||
defaultValue={isDisabled ? value.section : undefined}
|
||||
isDisabled={isDisabled}
|
||||
onChange={isDisabled ? undefined : _onSectionChange}
|
||||
options={optionsSection}
|
||||
value={value}
|
||||
/>
|
||||
<SelectMethod
|
||||
api={api}
|
||||
className='large'
|
||||
onChange={_onKeyChange}
|
||||
defaultValue={isDisabled ? value.method : undefined}
|
||||
isDisabled={isDisabled}
|
||||
onChange={isDisabled ? undefined : _onKeyChange}
|
||||
options={optionsMethod}
|
||||
value={value}
|
||||
/>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user