Support signing messages for external accounts (#718)
* support signing messages * Apply suggestions from code review Co-authored-by: Jaco <jacogr@gmail.com> Co-authored-by: Jaco <jacogr@gmail.com>
This commit is contained in:
@@ -2,33 +2,44 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ExtrinsicPayload } from '@polkadot/types/interfaces';
|
||||
import type { SignerPayloadJSON } from '@polkadot/types/types';
|
||||
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { QrDisplayPayload, QrScanSignature } from '@polkadot/react-qr';
|
||||
import { hexToU8a } from '@polkadot/util';
|
||||
|
||||
import { Button } from '../../components';
|
||||
import useTranslation from '../../hooks/useTranslation';
|
||||
import { CMD_MORTAL, CMD_SIGN_MESSAGE } from './Request';
|
||||
|
||||
interface Props {
|
||||
address: string;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
cmd: number;
|
||||
genesisHash: string;
|
||||
onSignature: ({ signature }: { signature: string }) => void;
|
||||
payload: ExtrinsicPayload;
|
||||
request: SignerPayloadJSON;
|
||||
payload: ExtrinsicPayload | string;
|
||||
|
||||
}
|
||||
|
||||
const CMD_MORTAL = 2;
|
||||
|
||||
function Qr ({ className, onSignature, payload, request }: Props): React.ReactElement<Props> {
|
||||
function Qr ({ address, className, cmd, genesisHash, onSignature, payload }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [isScanning, setIsScanning] = useState(false);
|
||||
|
||||
const payloadU8a = useMemo(
|
||||
() => payload.toU8a(),
|
||||
[payload]
|
||||
() => {
|
||||
switch (cmd) {
|
||||
case CMD_MORTAL:
|
||||
return (payload as ExtrinsicPayload).toU8a();
|
||||
case CMD_SIGN_MESSAGE:
|
||||
return hexToU8a(payload as string);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
[cmd, payload]
|
||||
);
|
||||
|
||||
const _onShowQr = useCallback(
|
||||
@@ -36,6 +47,16 @@ function Qr ({ className, onSignature, payload, request }: Props): React.ReactEl
|
||||
[]
|
||||
);
|
||||
|
||||
if (!payloadU8a) {
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className='qrContainer'>
|
||||
Transaction command:{cmd} not supported.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className='qrContainer'>
|
||||
@@ -43,9 +64,9 @@ function Qr ({ className, onSignature, payload, request }: Props): React.ReactEl
|
||||
? <QrScanSignature onScan={onSignature} />
|
||||
: (
|
||||
<QrDisplayPayload
|
||||
address={request.address}
|
||||
cmd={CMD_MORTAL}
|
||||
genesisHash={request.genesisHash}
|
||||
address={address}
|
||||
cmd={cmd}
|
||||
genesisHash={genesisHash}
|
||||
payload={payloadU8a}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -8,8 +8,9 @@ import type { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'
|
||||
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
import { decodeAddress } from '@polkadot/util-crypto';
|
||||
|
||||
import { ActionContext, Address, VerticalSpace } from '../../../components';
|
||||
import { AccountContext, ActionContext, Address, VerticalSpace } from '../../../components';
|
||||
import { approveSignSignature } from '../../../messaging';
|
||||
import Bytes from '../Bytes';
|
||||
import Extrinsic from '../Extrinsic';
|
||||
@@ -31,6 +32,9 @@ interface Data {
|
||||
payload: ExtrinsicPayload | null;
|
||||
}
|
||||
|
||||
export const CMD_MORTAL = 2;
|
||||
export const CMD_SIGN_MESSAGE = 3;
|
||||
|
||||
// keep it global, we can and will re-use this across requests
|
||||
const registry = new TypeRegistry();
|
||||
|
||||
@@ -42,6 +46,7 @@ export default function Request ({ account: { accountIndex, addressOffset, isExt
|
||||
const onAction = useContext(ActionContext);
|
||||
const [{ hexBytes, payload }, setData] = useState<Data>({ hexBytes: null, payload: null });
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const { accounts } = useContext(AccountContext);
|
||||
|
||||
useEffect((): void => {
|
||||
const payload = request.payload;
|
||||
@@ -88,9 +93,11 @@ export default function Request ({ account: { accountIndex, addressOffset, isExt
|
||||
{isExternal && !isHardware
|
||||
? (
|
||||
<Qr
|
||||
address={json.address}
|
||||
cmd={CMD_MORTAL}
|
||||
genesisHash={json.genesisHash}
|
||||
onSignature={_onSignature}
|
||||
payload={payload}
|
||||
request={json}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
@@ -123,20 +130,32 @@ export default function Request ({ account: { accountIndex, addressOffset, isExt
|
||||
</>
|
||||
);
|
||||
} else if (hexBytes !== null) {
|
||||
const raw = request.payload as SignerPayloadRaw;
|
||||
const { address, data } = request.payload as SignerPayloadRaw;
|
||||
const account = accounts.find((account) => decodeAddress(account.address).toString() === decodeAddress(address).toString());
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<Address
|
||||
address={raw.address}
|
||||
address={address}
|
||||
isExternal={isExternal}
|
||||
/>
|
||||
</div>
|
||||
<Bytes
|
||||
bytes={raw.data}
|
||||
url={url}
|
||||
/>
|
||||
{isExternal && !isHardware && account?.genesisHash
|
||||
? (
|
||||
<Qr
|
||||
address={address}
|
||||
cmd={CMD_SIGN_MESSAGE}
|
||||
genesisHash={account.genesisHash}
|
||||
onSignature={_onSignature}
|
||||
payload={data}
|
||||
/>
|
||||
)
|
||||
: <Bytes
|
||||
bytes={data}
|
||||
url={url}
|
||||
/>
|
||||
}
|
||||
<VerticalSpace />
|
||||
<SignArea
|
||||
buttonText={buttonText}
|
||||
|
||||
Reference in New Issue
Block a user