ghost-keychain/packages/extension-ui/src/Popup/Signing/Extrinsic.tsx
Jaco Greeff 206c0b4d85
Swap to actual react-i18next (#431)
* Swap to actual react-i18next

* Update i18n creation
2020-09-04 16:32:42 +02:00

150 lines
4.4 KiB
TypeScript

// Copyright 2019-2020 @polkadot/extension-ui authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Chain } from '@polkadot/extension-chains/types';
import { Call, ExtrinsicEra, ExtrinsicPayload } from '@polkadot/types/interfaces';
import { AnyJson, SignerPayloadJSON } from '@polkadot/types/types';
import BN from 'bn.js';
import React, { useEffect, useRef, useState } from 'react';
import { TFunction } from 'i18next';
import { formatNumber, bnToBn } from '@polkadot/util';
import { Table } from '../../components';
import useMetadata from '../../hooks/useMetadata';
import useTranslation from '../../hooks/useTranslation';
interface Decoded {
args: AnyJson | null;
method: Call | null;
}
interface Props {
className?: string;
isDecoded: boolean;
payload: ExtrinsicPayload;
request: SignerPayloadJSON;
url: string;
}
function decodeMethod (data: string, isDecoded: boolean, chain: Chain | null, specVersion: BN): Decoded {
let args: AnyJson | null = null;
let method: Call | null = null;
try {
if (isDecoded && chain && chain.hasMetadata && specVersion.eqn(chain.specVersion)) {
method = chain.registry.createType('Call', data);
args = (method.toHuman() as { args: AnyJson }).args;
}
} catch (error) {
args = null;
method = null;
}
return { args, method };
}
function renderMethod (data: string, { args, method }: Decoded, t: TFunction): React.ReactNode {
if (!args || !method) {
return (
<tr>
<td className='label'>{t<string>('method data')}</td>
<td className='data'>{data}</td>
</tr>
);
}
return (
<>
<tr>
<td className='label'>{t<string>('method')}</td>
<td className='data'>
<details>
<summary>{method.sectionName}.{method.methodName}{
method.meta
? `(${method.meta.args.map(({ name }) => name).join(', ')})`
: ''
}</summary>
<pre>{JSON.stringify(args, null, 2)}</pre>
</details>
</td>
</tr>
{method.meta && (
<tr>
<td className='label'>{t<string>('info')}</td>
<td className='data'>
<details>
<summary>{method.meta.documentation.map((d) => d.toString().trim()).join(' ')}</summary>
</details>
</td>
</tr>
)}
</>
);
}
function mortalityAsString (era: ExtrinsicEra, hexBlockNumber: string, t: TFunction): string {
if (era.isImmortalEra) {
return t<string>('immortal');
}
const blockNumber = bnToBn(hexBlockNumber);
const mortal = era.asMortalEra;
return t<string>('mortal, valid from {{birth}} to {{death}}', {
replace: {
birth: formatNumber(mortal.birth(blockNumber)),
death: formatNumber(mortal.death(blockNumber))
}
});
}
function Extrinsic ({ className, isDecoded, payload: { era, nonce, tip }, request: { blockNumber, genesisHash, method, specVersion: hexSpec }, url }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const chain = useMetadata(genesisHash);
const specVersion = useRef(bnToBn(hexSpec)).current;
const [decoded, setDecoded] = useState<Decoded>({ args: null, method: null });
useEffect((): void => {
setDecoded(decodeMethod(method, isDecoded, chain, specVersion));
}, [method, isDecoded, chain, specVersion]);
return (
<Table
className={className}
isFull
>
<tr>
<td className='label'>{t<string>('from')}</td>
<td className='data'>{url}</td>
</tr>
<tr>
<td className='label'>{chain ? t<string>('chain') : t<string>('genesis')}</td>
<td className='data'>{chain ? chain.name : genesisHash}</td>
</tr>
<tr>
<td className='label'>{t<string>('version')}</td>
<td className='data'>{specVersion.toNumber()}</td>
</tr>
<tr>
<td className='label'>{t<string>('nonce')}</td>
<td className='data'>{formatNumber(nonce)}</td>
</tr>
{!tip.isEmpty && (
<tr>
<td className='label'>{t<string>('tip')}</td>
<td className='data'>{formatNumber(tip)}</td>
</tr>
)}
{renderMethod(method, decoded, t)}
<tr>
<td className='label'>lifetime</td>
<td className='data'>{mortalityAsString(era, blockNumber, t)}</td>
</tr>
</Table>
);
}
export default React.memo(Extrinsic);