Add Signer interface for API (#683)
* Add Signer interface for API * Attach signer to API instance * Integrate signer with updates (untested) * Update with working signer integration (logging an issue still) * Restore console hooks, feedback on signed * Remove unneeded console.log * Marge install
This commit is contained in:
parent
ce63296007
commit
61674a7989
24
packages/app-js/src/snippets/transfer.ts
Normal file
24
packages/app-js/src/snippets/transfer.ts
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2017-2019 @polkadot/app-123code authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
export default `// transfer
|
||||
const sender = '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ';
|
||||
const recipient = '5GTZEKgRKX2a5hepNjwTBzR5nR6UqtEsMZ4TnL1Yuah1N2Tu';
|
||||
|
||||
const unsub = await api.tx.balances
|
||||
.transfer(recipient,12345)
|
||||
.signAndSend(sender, ({ events = [], status, type }) => {
|
||||
console.log('Transaction status:', type);
|
||||
|
||||
if (type === 'Finalised') {
|
||||
console.log('Completed at block hash', status.asFinalised.toHex());
|
||||
console.log('Events:');
|
||||
|
||||
events.forEach(({ phase, event: { data, method, section } }) => {
|
||||
console.log('\t', phase.toString(), \`: \${section}.\${method}\`, data.toString());
|
||||
});
|
||||
|
||||
unsub();
|
||||
}
|
||||
});`;
|
||||
@ -49,20 +49,20 @@ class Content extends React.Component<Props> {
|
||||
return (
|
||||
<div className='apps--Content'>
|
||||
<QueueConsumer>
|
||||
{({ queueAction, stqueue, txqueue }: QueueProps) => [
|
||||
<Component
|
||||
key='content-content'
|
||||
basePath={`/${name}`}
|
||||
location={location}
|
||||
onStatusChange={queueAction}
|
||||
/>,
|
||||
<Status
|
||||
key='content-status'
|
||||
queueAction={queueAction}
|
||||
stqueue={stqueue}
|
||||
txqueue={txqueue}
|
||||
/>
|
||||
]}
|
||||
{({ queueAction, stqueue, txqueue }: QueueProps) => (
|
||||
<>
|
||||
<Component
|
||||
basePath={`/${name}`}
|
||||
location={location}
|
||||
onStatusChange={queueAction}
|
||||
/>
|
||||
<Status
|
||||
queueAction={queueAction}
|
||||
stqueue={stqueue}
|
||||
txqueue={txqueue}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</QueueConsumer>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -3,16 +3,18 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ProviderInterface } from '@polkadot/rpc-provider/types';
|
||||
import { QueueTx$ExtrinsicAdd, QueueTx$MessageSetStatus } from '@polkadot/ui-app/Status/types';
|
||||
import { ApiProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import ApiPromise from '@polkadot/api/promise';
|
||||
import defaults from '@polkadot/rpc-provider/defaults';
|
||||
import WsProvider from '@polkadot/rpc-provider/ws';
|
||||
import { WsProvider } from '@polkadot/rpc-provider';
|
||||
import { InputNumber } from '@polkadot/ui-app/InputNumber';
|
||||
import { formatBalance } from '@polkadot/ui-app/util';
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import settings from '@polkadot/ui-settings';
|
||||
import ApiSigner from '@polkadot/ui-signer/ApiSigner';
|
||||
import { ChainProperties } from '@polkadot/types';
|
||||
|
||||
import ApiContext from './ApiContext';
|
||||
@ -20,6 +22,8 @@ import { isTestChain } from './util';
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode,
|
||||
queueExtrinsic: QueueTx$ExtrinsicAdd,
|
||||
queueSetTxStatus: QueueTx$MessageSetStatus,
|
||||
url?: string
|
||||
};
|
||||
|
||||
@ -33,10 +37,12 @@ export default class ApiWrapper extends React.PureComponent<Props, State> {
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
const { url } = props;
|
||||
const { queueExtrinsic, queueSetTxStatus, url } = props;
|
||||
const provider = new WsProvider(url);
|
||||
const signer = new ApiSigner(queueExtrinsic, queueSetTxStatus);
|
||||
|
||||
const setApi = (provider: ProviderInterface): void => {
|
||||
const api = new ApiPromise(provider);
|
||||
const api = new ApiPromise({ provider, signer });
|
||||
|
||||
this.setState({ api }, () => {
|
||||
this.updateSubscriptions();
|
||||
@ -48,7 +54,7 @@ export default class ApiWrapper extends React.PureComponent<Props, State> {
|
||||
this.state = {
|
||||
isApiConnected: false,
|
||||
isApiReady: false,
|
||||
api: new ApiPromise(provider),
|
||||
api: new ApiPromise({ provider, signer }),
|
||||
setApiUrl
|
||||
} as State;
|
||||
}
|
||||
|
||||
@ -156,11 +156,13 @@ export default class Queue extends React.Component<Props, State> {
|
||||
return id;
|
||||
}
|
||||
|
||||
queueExtrinsic = ({ accountId, extrinsic, isUnsigned }: PartialQueueTx$Extrinsic): number => {
|
||||
queueExtrinsic = ({ accountId, extrinsic, signerCallback, signerOptions, isUnsigned }: PartialQueueTx$Extrinsic): number => {
|
||||
return this.queueAdd({
|
||||
accountId,
|
||||
extrinsic,
|
||||
isUnsigned
|
||||
isUnsigned,
|
||||
signerCallback,
|
||||
signerOptions
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
|
||||
import { RpcMethod } from '@polkadot/jsonrpc/types';
|
||||
import { AccountId, Address } from '@polkadot/types';
|
||||
import { SignatureOptions } from '@polkadot/types/ExtrinsicSignature';
|
||||
|
||||
export type Actions = 'create' | 'edit' | 'restore' | 'forget' | 'backup' | 'changePassword' | 'transfer';
|
||||
|
||||
@ -21,6 +22,8 @@ export type AccountInfo = {
|
||||
|
||||
export type QueueTx$Status = 'future' | 'ready' | 'finalised' | 'usurped' | 'dropped' | 'invalid' | 'broadcast' | 'cancelled' | 'completed' | 'error' | 'incomplete' | 'queued' | 'sending' | 'sent' | 'blocked';
|
||||
|
||||
export type SignerCallback = (id: number, isSigned: boolean) => void;
|
||||
|
||||
export type QueueTx = AccountInfo & {
|
||||
error?: Error,
|
||||
extrinsic?: SubmittableExtrinsic,
|
||||
@ -28,6 +31,8 @@ export type QueueTx = AccountInfo & {
|
||||
isUnsigned?: boolean,
|
||||
result?: any,
|
||||
rpc: RpcMethod,
|
||||
signerCallback?: SignerCallback,
|
||||
signerOptions?: SignatureOptions,
|
||||
values?: Array<any>,
|
||||
status: QueueTx$Status
|
||||
};
|
||||
@ -58,6 +63,8 @@ export type PartialAccountInfo = {
|
||||
|
||||
export type PartialQueueTx$Extrinsic = PartialAccountInfo & {
|
||||
extrinsic: SubmittableExtrinsic,
|
||||
signerCallback?: SignerCallback,
|
||||
signerOptions?: SignatureOptions,
|
||||
isUnsigned?: boolean
|
||||
};
|
||||
|
||||
|
||||
@ -13,6 +13,9 @@ import ReactDOM from 'react-dom';
|
||||
import { HashRouter } from 'react-router-dom';
|
||||
import { Api } from '@polkadot/ui-api/index';
|
||||
|
||||
import { QueueConsumer } from './Status/Context';
|
||||
import Queue from './Status/Queue';
|
||||
|
||||
export { default as AddressMini } from './AddressMini';
|
||||
export { default as AddressRow } from './AddressRow';
|
||||
export { default as AddressSummary } from './AddressSummary';
|
||||
@ -58,14 +61,24 @@ export default function createApp (App: React.ComponentType<BareProps>, { classN
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<Api url={url}>
|
||||
<HashRouter>
|
||||
<App
|
||||
className={className}
|
||||
style={style}
|
||||
/>
|
||||
</HashRouter>
|
||||
</Api>,
|
||||
<Queue>
|
||||
<QueueConsumer>
|
||||
{({ queueExtrinsic, queueSetTxStatus }) => (
|
||||
<Api
|
||||
queueExtrinsic={queueExtrinsic}
|
||||
queueSetTxStatus={queueSetTxStatus}
|
||||
url={url}
|
||||
>
|
||||
<HashRouter>
|
||||
<App
|
||||
className={className}
|
||||
style={style}
|
||||
/>
|
||||
</HashRouter>
|
||||
</Api>
|
||||
)}
|
||||
</QueueConsumer>
|
||||
</Queue>,
|
||||
rootElement
|
||||
);
|
||||
}
|
||||
|
||||
45
packages/ui-signer/src/ApiSigner.ts
Normal file
45
packages/ui-signer/src/ApiSigner.ts
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright 2017-2019 @polkadot/ui-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 { Signer } from '@polkadot/api/types';
|
||||
import { SubmittableResult } from '@polkadot/api';
|
||||
import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
|
||||
import { QueueTx$ExtrinsicAdd, QueueTx$MessageSetStatus } from '@polkadot/ui-app/Status/types';
|
||||
import { Hash } from '@polkadot/types';
|
||||
import { SignatureOptions } from '@polkadot/types/ExtrinsicSignature';
|
||||
|
||||
export default class ApiSigner implements Signer {
|
||||
private _queueExtrinsic: QueueTx$ExtrinsicAdd;
|
||||
private _queueSetTxStatus: QueueTx$MessageSetStatus;
|
||||
|
||||
constructor (queueExtrinsic: QueueTx$ExtrinsicAdd, queueSetTxStatus: QueueTx$MessageSetStatus) {
|
||||
this._queueExtrinsic = queueExtrinsic;
|
||||
this._queueSetTxStatus = queueSetTxStatus;
|
||||
}
|
||||
|
||||
async sign (extrinsic: SubmittableExtrinsic, accountId: string, signerOptions: SignatureOptions): Promise<number> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._queueExtrinsic({
|
||||
accountId,
|
||||
extrinsic,
|
||||
signerOptions,
|
||||
signerCallback: (id: number, isSigned: boolean): void => {
|
||||
if (isSigned) {
|
||||
resolve(id);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
update (id: number, status: Hash | SubmittableResult): void {
|
||||
if (status instanceof Hash) {
|
||||
this._queueSetTxStatus(id, 'sent', status.toHex());
|
||||
} else {
|
||||
this._queueSetTxStatus(id, status.type.toLowerCase() as any, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,9 @@ import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
|
||||
import { ApiProps } from '@polkadot/ui-api/types';
|
||||
import { I18nProps, BareProps } from '@polkadot/ui-app/types';
|
||||
import { RpcMethod } from '@polkadot/jsonrpc/types';
|
||||
import { QueueTx, QueueTx$MessageSetStatus, QueueTx$Result, QueueTx$Status } from '@polkadot/ui-app/Status/types';
|
||||
import { KeyringPair } from '@polkadot/keyring/types';
|
||||
import { SignatureOptions } from '@polkadot/types/ExtrinsicSignature';
|
||||
import { QueueTx, QueueTx$MessageSetStatus, QueueTx$Result, QueueTx$Status, SignerCallback } from '@polkadot/ui-app/Status/types';
|
||||
|
||||
import React from 'react';
|
||||
import { Button, Modal } from '@polkadot/ui-app/index';
|
||||
@ -214,7 +216,10 @@ class Signer extends React.PureComponent<Props, State> {
|
||||
return;
|
||||
}
|
||||
|
||||
queueSetTxStatus(currentItem.id, 'cancelled');
|
||||
const { id, signerCallback } = currentItem;
|
||||
|
||||
queueSetTxStatus(id, 'cancelled');
|
||||
signerCallback && signerCallback(id, false);
|
||||
}
|
||||
|
||||
private onSend = async (): Promise<void> => {
|
||||
@ -242,7 +247,7 @@ class Signer extends React.PureComponent<Props, State> {
|
||||
queueSetTxStatus(id, status, result, error);
|
||||
}
|
||||
|
||||
private async sendExtrinsic ({ accountId, extrinsic, id, isUnsigned }: QueueTx, password?: string): Promise<void> {
|
||||
private async sendExtrinsic ({ accountId, extrinsic, id, signerCallback, signerOptions, isUnsigned }: QueueTx, password?: string): Promise<void> {
|
||||
assert(extrinsic, 'Expected an extrinsic to be supplied to sendExtrinsic');
|
||||
|
||||
if (!isUnsigned) {
|
||||
@ -261,11 +266,13 @@ class Signer extends React.PureComponent<Props, State> {
|
||||
|
||||
queueSetTxStatus(id, 'sending');
|
||||
|
||||
if (!isUnsigned) {
|
||||
return this.makeExtrinsicCall(submittable, id, submittable.signAndSend, keyring.getPair(accountId as string));
|
||||
} else {
|
||||
if (isUnsigned) {
|
||||
return this.makeExtrinsicCall(submittable, id, submittable.send);
|
||||
} else if (signerOptions) {
|
||||
return this.makeExtrinsicSignature(submittable, id, keyring.getPair(accountId as string), signerOptions, signerCallback);
|
||||
}
|
||||
|
||||
return this.makeExtrinsicCall(submittable, id, submittable.signAndSend, keyring.getPair(accountId as string));
|
||||
}
|
||||
|
||||
private async submitRpc ({ method, section }: RpcMethod, values: Array<any>): Promise<QueueTx$Result> {
|
||||
@ -316,6 +323,14 @@ class Signer extends React.PureComponent<Props, State> {
|
||||
queueSetTxStatus(id, 'error', {}, error);
|
||||
}
|
||||
}
|
||||
|
||||
private async makeExtrinsicSignature (extrinsic: SubmittableExtrinsic, id: number, pair: KeyringPair, options: SignatureOptions, signerCallback?: SignerCallback): Promise<void> {
|
||||
console.log('makeExtrinsicSignature: extrinsic ::', extrinsic.toHex());
|
||||
|
||||
extrinsic.sign(pair, options);
|
||||
|
||||
signerCallback && signerCallback(id, true);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@ -9,7 +9,6 @@ import './index.css';
|
||||
|
||||
import React from 'react';
|
||||
import { QueueConsumer } from '@polkadot/ui-app/Status/Context';
|
||||
import Queue, { Props as QueueComponentProps } from '@polkadot/ui-app/Status/Queue';
|
||||
|
||||
import Modal from './Modal';
|
||||
|
||||
@ -17,39 +16,25 @@ type Props = BareProps & {
|
||||
children: React.ReactNode
|
||||
};
|
||||
|
||||
export type SignerType = React.ComponentType<Props> & {
|
||||
Queue: React.ComponentType<QueueComponentProps>
|
||||
};
|
||||
|
||||
class Signer extends React.PureComponent<Props> {
|
||||
static Queue = Queue;
|
||||
|
||||
export default class Signer extends React.PureComponent<Props> {
|
||||
render () {
|
||||
const { children } = this.props;
|
||||
const { children, className, style } = this.props;
|
||||
|
||||
return (
|
||||
<Queue>
|
||||
<>
|
||||
{children}
|
||||
<QueueConsumer>
|
||||
{this.handleQueueProps}
|
||||
{({ txqueue, queueSetTxStatus }: QueueProps) => (
|
||||
<Modal
|
||||
className={className}
|
||||
key='signer-modal'
|
||||
queue={txqueue}
|
||||
queueSetTxStatus={queueSetTxStatus}
|
||||
style={style}
|
||||
/>
|
||||
)}
|
||||
</QueueConsumer>
|
||||
</Queue>
|
||||
);
|
||||
}
|
||||
|
||||
private handleQueueProps = ({ txqueue, queueSetTxStatus }: QueueProps) => {
|
||||
const { className, style } = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className={className}
|
||||
key='signer-modal'
|
||||
queue={txqueue}
|
||||
queueSetTxStatus={queueSetTxStatus}
|
||||
style={style}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default (Signer as SignerType);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user