// Copyright 2017-2019 @polkadot/react-signer 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 { SignerOptions, SignerResult } from '@polkadot/api/types'; import { SubmittableExtrinsic } from '@polkadot/api/promise/types'; import { ApiProps } from '@polkadot/react-api/types'; import { I18nProps, BareProps } from '@polkadot/react-components/types'; import { RpcMethod } from '@polkadot/jsonrpc/types'; import { KeyringPair } from '@polkadot/keyring/types'; import { SubjectInfo } from '@polkadot/ui-keyring/observable/types'; import { QueueTx, QueueTxMessageSetStatus, QueueTxResult, QueueTxStatus } from '@polkadot/react-components/Status/types'; import { SignerPayloadJSON } from '@polkadot/types/types'; import BN from 'bn.js'; import React from 'react'; import { SubmittableResult } from '@polkadot/api'; import { web3FromSource } from '@polkadot/extension-dapp'; import { createType } from '@polkadot/types'; import { Button, InputBalance, Modal } from '@polkadot/react-components'; import { withApi, withMulti, withObservable } from '@polkadot/react-api'; import keyring from '@polkadot/ui-keyring'; import { assert, isFunction } from '@polkadot/util'; import { format } from '@polkadot/util/logger'; import ledgerSigner from './LedgerSigner'; import PasswordCheck from './PasswordCheck'; import Transaction from './Transaction'; import Qr from './Qr'; import Unlock from './Unlock'; import translate from './translate'; interface BaseProps extends BareProps { queue: QueueTx[]; queueSetTxStatus: QueueTxMessageSetStatus; } interface Props extends I18nProps, ApiProps, BaseProps { allAccounts?: SubjectInfo; } interface State { currentItem?: QueueTx; isQrScanning: boolean; isQrVisible: boolean; isSendable: boolean; isV2?: boolean; password: string; qrAddress: string; qrPayload: Uint8Array; qrResolve?: (result: SignerResult) => void; qrReject?: (error: Error) => void; tip?: BN; unlockError?: string | null; } let qrId = 0; function extractExternal (accountId?: string | null): { isExternal: boolean; isHardware: boolean; hardwareType?: string } { if (!accountId) { return { isExternal: false, isHardware: false }; } let publicKey; try { publicKey = keyring.decodeAddress(accountId); } catch (error) { console.error(error); return { isExternal: false, isHardware: false }; } const pair = keyring.getPair(publicKey); return { isExternal: !!pair.meta.isExternal, isHardware: !!pair.meta.isHardware, hardwareType: pair.meta.hardwareType }; } // eslint-disable-next-line @typescript-eslint/require-await async function makeExtrinsicSignature (payload: SignerPayloadJSON, { id, signerCb }: QueueTx, pair: KeyringPair): Promise { console.log('makeExtrinsicSignature: payload ::', JSON.stringify(payload)); const result = createType('ExtrinsicPayload', payload, { version: payload.version }).sign(pair); if (isFunction(signerCb)) { signerCb(id, { id, ...result }); } } class Signer extends React.PureComponent { public state: State = { isSendable: false, isQrScanning: false, isQrVisible: false, password: '', qrAddress: '', qrPayload: new Uint8Array(), unlockError: null }; public static getDerivedStateFromProps ({ allAccounts, api, queue }: Props, { currentItem, password, unlockError }: State): Partial { let isV2: boolean; try { isV2 = !!api.tx.session.setKeys; } catch (e) { isV2 = false; } const nextItem = queue.find(({ status }): boolean => ['queued', 'qr'].includes(status)); const isSame = !!nextItem && !!currentItem && ( (!nextItem.accountId && !currentItem.accountId) || ( (nextItem.accountId && nextItem.accountId.toString()) === (currentItem.accountId && currentItem.accountId.toString()) ) ); let isSendable = !!nextItem && !!nextItem.isUnsigned; if (!isSendable && nextItem && nextItem.accountId && allAccounts) { try { const pair = keyring.getPair(nextItem.accountId); isSendable = !!pair && !!allAccounts[nextItem.accountId]; } catch (error) { // swallow } } return { currentItem: nextItem, isSendable, isV2, password: isSame ? password : '', unlockError: isSame ? unlockError : null }; } public async componentDidUpdate (): Promise { const { currentItem } = this.state; if (currentItem && currentItem.status === 'queued' && !(currentItem.extrinsic || currentItem.payload)) { return this.sendRpc(currentItem); } } public render (): React.ReactNode { const { currentItem } = this.state; if (!currentItem) { return null; } return ( {this.renderContent()} {this.renderButtons()} ); } private renderButtons (): React.ReactNode { const { t } = this.props; const { currentItem, isQrScanning, isQrVisible, isSendable } = this.state; if (!currentItem) { return null; } const { isExternal, isHardware, hardwareType } = extractExternal(currentItem.accountId); return (