ghost-portal/packages/page-extrinsics/src/Decoder.tsx
Jaco 661ed704db
Pass last decoded extrinsic to submission (#6964)
* Pass last decoded extrinsic to submission (partial)

* Adjust

* Vector

* Adjust

* Adjust

* Adjust

* Fix enum state

* Tweak enum

* Non-crashing enum

* Tuple

* Cleanups

* Cleanups

* CHANGELOG
2022-02-25 14:12:29 +02:00

145 lines
4.2 KiB
TypeScript

// Copyright 2017-2022 @polkadot/app-extrinsics authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { SubmittableExtrinsic, SubmittableExtrinsicFunction } from '@polkadot/api/types';
import type { Call } from '@polkadot/types/interfaces';
import type { DecodedExtrinsic } from './types';
import React, { useCallback, useState } from 'react';
import styled from 'styled-components';
import { Button, Call as CallDisplay, Input, InputAddress, InputExtrinsic, MarkError, TxButton } from '@polkadot/react-components';
import { useApi } from '@polkadot/react-hooks';
import { BalanceFree } from '@polkadot/react-query';
import { assert, isHex } from '@polkadot/util';
import Decoded from './Decoded';
import { useTranslation } from './translate';
interface Props {
className?: string;
setLast: (value: DecodedExtrinsic | null) => void;
}
interface ExtrinsicInfo {
decoded: SubmittableExtrinsic<'promise'> | null;
extrinsic: SubmittableExtrinsic<'promise'> | null;
extrinsicCall: Call | null;
extrinsicError: string | null;
extrinsicFn: SubmittableExtrinsicFunction<'promise'> | null;
extrinsicHex: string | null;
}
const DEFAULT_INFO: ExtrinsicInfo = {
decoded: null,
extrinsic: null,
extrinsicCall: null,
extrinsicError: null,
extrinsicFn: null,
extrinsicHex: null
};
function Decoder ({ className, setLast }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const [{ decoded, extrinsic, extrinsicCall, extrinsicError, extrinsicFn }, setExtrinsicInfo] = useState<ExtrinsicInfo>(DEFAULT_INFO);
const [accountId, setAccountId] = useState<string | null>(null);
const _setExtrinsicHex = useCallback(
(extrinsicHex: string): void => {
try {
assert(isHex(extrinsicHex), 'Expected a hex-encoded call');
let extrinsicCall: Call;
let decoded: SubmittableExtrinsic<'promise'> | null = null;
try {
// cater for an extrinsic input...
decoded = api.tx(extrinsicHex);
extrinsicCall = api.createType('Call', decoded.method);
} catch (e) {
extrinsicCall = api.createType('Call', extrinsicHex);
}
const { method, section } = api.registry.findMetaCall(extrinsicCall.callIndex);
const extrinsicFn = api.tx[section][method];
const extrinsic = extrinsicFn(...extrinsicCall.args);
if (!decoded) {
decoded = extrinsic;
}
setExtrinsicInfo({ ...DEFAULT_INFO, decoded, extrinsic, extrinsicCall, extrinsicFn, extrinsicHex });
setLast({ call: extrinsicCall, fn: extrinsicFn });
} catch (e) {
setExtrinsicInfo({ ...DEFAULT_INFO, extrinsicError: (e as Error).message });
setLast(null);
}
},
[api, setLast]
);
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}
/>
</>
)}
<Decoded
extrinsic={decoded}
withData={false}
/>
<InputAddress
label={t<string>('using the selected account')}
labelExtra={
<BalanceFree
label={<label>{t<string>('free balance')}</label>}
params={accountId}
/>
}
onChange={setAccountId}
type='account'
/>
<Button.Group>
<TxButton
extrinsic={extrinsic}
icon='sign-in-alt'
isUnsigned
label={t<string>('Submit Unsigned')}
withSpinner
/>
<TxButton
accountId={accountId}
extrinsic={extrinsic}
icon='sign-in-alt'
label={t<string>('Submit Transaction')}
/>
</Button.Group>
</div>
);
}
export default React.memo(styled(Decoder)`
.ui--Extrinsic--toplevel {
margin-top: 0;
}
`);