Update jsonrpc dependency tree (#54)

* Use tyopeToString from @polkadot/params

* Upgrades... before checks

* Cleanups... WIP.

* more WIP.

* Expand defaultValue & handlers

* Cleanups

* CodeClimate cleanups

* Additional CodeClimate cleanups

* Base for app-rpc

* Initial split into ui-signer

* Signer operational (extrinsics)

* Decode extrinsic method

* RPC progress...

* RPC class works again

* @flow cleanups
This commit is contained in:
Jaco Greeff 2018-05-23 18:01:42 +02:00 committed by GitHub
parent 578555ce1a
commit 397dd0c983
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
103 changed files with 2794 additions and 1594 deletions

View File

@ -15,4 +15,4 @@ all=warn
include_warnings=true
module.name_mapper='^@polkadot/app-\(accounts\|addresses\|explorer\|extrinsics\|home\|rpc\|settings\|storage\|toolbox\|vanitygen\)\(.*\)$' -> '<PROJECT_ROOT>/packages/app-\1/src\2'
module.name_mapper='^@polkadot/apps\(.*\)$' -> '<PROJECT_ROOT>/packages/apps/src\1'
module.name_mapper='^@polkadot/ui-\(app\|keyring\)\(.*\)$' -> '<PROJECT_ROOT>/packages/ui-\1/src\2'
module.name_mapper='^@polkadot/ui-\(app\|keyring\|signer\)\(.*\)$' -> '<PROJECT_ROOT>/packages/ui-\1/src\2'

View File

@ -55,7 +55,7 @@ export default class Address extends React.PureComponent<Props, State> {
render (): React$Node {
const { className, style } = this.props;
const { address, isValid, publicKey, shortValue } = this.state;
const { address, isValid, shortValue } = this.state;
return (
<div
@ -73,26 +73,32 @@ export default class Address extends React.PureComponent<Props, State> {
</div>
<CopyButton value={address} />
</div>
{
!isValid
? null
: [
<Balance
className='accounts--Address-balance'
key='balance'
label='balance '
params={publicKey}
/>,
<Nonce
className='accounts--Address-nonce'
key='nonce'
params={publicKey}
>
{' transactions'}
</Nonce>
]
}
{this.renderBalance()}
</div>
);
}
renderBalance (): React$Node {
const { isValid, publicKey } = this.state;
if (!isValid) {
return null;
}
return [
<Balance
className='accounts--Address-balance'
key='balance'
label='balance '
params={publicKey}
/>,
<Nonce
className='accounts--Address-nonce'
key='nonce'
params={publicKey}
>
{' transactions'}
</Nonce>
];
}
}

View File

@ -62,8 +62,8 @@ class Creator extends React.PureComponent<Props, State> {
}
render (): React$Node {
const { className, style, t } = this.props;
const { address, isNameValid, isPassValid, isSeedValid, isValid, name, password, seed } = this.state;
const { className, style } = this.props;
const { address, isSeedValid } = this.state;
return (
<div
@ -79,58 +79,76 @@ class Creator extends React.PureComponent<Props, State> {
: null
}
/>
<div className='grow'>
<div className='ui--row'>
<Input
className='full'
isError={!isSeedValid}
label={t('creator.seed', {
defaultValue: 'create from the following seed (hex or string)'
})}
onChange={this.onChangeSeed}
value={seed}
/>
</div>
<div className='ui--row'>
<Input
className='full'
isError={!isNameValid}
label={t('creator.name', {
defaultValue: 'name the account'
})}
onChange={this.onChangeName}
value={name}
/>
</div>
<div className='ui--row'>
<Password
className='full'
isError={!isPassValid}
label={t('creator.password', {
defaultValue: 'encrypt it using the password'
})}
onChange={this.onChangePass}
value={password}
/>
</div>
</div>
{this.renderInput()}
</div>
<Button.Group>
<Button
onClick={this.onDiscard}
text={t('creator.discard', {
defaultValue: 'Reset'
{this.renderButtons()}
</div>
);
}
renderButtons (): React$Node {
const { t } = this.props;
const { isValid } = this.state;
return (
<Button.Group>
<Button
onClick={this.onDiscard}
text={t('creator.discard', {
defaultValue: 'Reset'
})}
/>
<Button
isDisabled={!isValid}
isPrimary
onClick={this.onCommit}
text={t('creator.save', {
defaultValue: 'Save'
})}
/>
</Button.Group>
);
}
renderInput (): React$Node {
const { t } = this.props;
const { isNameValid, isPassValid, isSeedValid, name, password, seed } = this.state;
return (
<div className='grow'>
<div className='ui--row'>
<Input
className='full'
isError={!isSeedValid}
label={t('creator.seed', {
defaultValue: 'create from the following seed (hex or string)'
})}
onChange={this.onChangeSeed}
value={seed}
/>
<Button
isDisabled={!isValid}
isPrimary
onClick={this.onCommit}
text={t('creator.save', {
defaultValue: 'Save'
</div>
<div className='ui--row'>
<Input
className='full'
isError={!isNameValid}
label={t('creator.name', {
defaultValue: 'name the account'
})}
onChange={this.onChangeName}
value={name}
/>
</Button.Group>
</div>
<div className='ui--row'>
<Password
className='full'
isError={!isPassValid}
label={t('creator.password', {
defaultValue: 'encrypt it using the password'
})}
onChange={this.onChangePass}
value={password}
/>
</div>
</div>
);
}

View File

@ -44,86 +44,100 @@ class Editor extends React.PureComponent<Props, State> {
}
render (): React$Node {
const { className, style, t } = this.props;
const { defaultPublicKey, currentPair, editedName, isEdited } = this.state;
if (!currentPair) {
return (
<div
className={classes('accounts--Editor', className)}
style={style}
>
{t('editor.none', {
defaultValue: 'There are no saved accounts. Add some first.'
})}
</div>
);
}
const address = currentPair.address();
const { className, style } = this.props;
return (
<div
className={classes('accounts--Editor', className)}
style={style}
>
<div className='ui--grid'>
<Address
className='shrink'
value={address}
/>
<div className='grow'>
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultPublicKey}
hideAddress
isInput={false}
label={t('editor.select', {
defaultValue: 'using my account'
})}
onChange={this.onChangeAccount}
type='account'
value={address}
/>
</div>
<div className='ui--row'>
<Input
className='full'
label={t('editor.name', {
defaultValue: 'identified by the name'
})}
onChange={this.onChangeName}
value={editedName}
/>
</div>
{this.renderData()}
{this.renderButtons()}
</div>
);
}
renderButtons (): React$Node {
const { t } = this.props;
const { currentPair, isEdited } = this.state;
if (!currentPair) {
return null;
}
return (
<Button.Group>
<Button
isNegative
onClick={this.onForget}
text={t('editor.forget', {
defaultValue: 'Forget'
})}
/>
<Button.Group.Divider />
<Button
isDisabled={!isEdited}
onClick={this.onDiscard}
text={t('editor.reset', {
defaultValue: 'Reset'
})}
/>
<Button
isDisabled={!isEdited}
isPrimary
onClick={this.onCommit}
text={t('editor.save', {
defaultValue: 'Save'
})}
/>
</Button.Group>
);
}
renderData (): React$Node {
const { t } = this.props;
const { currentPair, defaultPublicKey, editedName } = this.state;
if (!currentPair) {
return t('editor.none', {
defaultValue: 'There are no saved accounts. Add some first.'
});
}
const address = currentPair.address();
return (
<div className='ui--grid'>
<Address
className='shrink'
value={address}
/>
<div className='grow'>
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultPublicKey}
hideAddress
isInput={false}
label={t('editor.select', {
defaultValue: 'using my account'
})}
onChange={this.onChangeAccount}
type='account'
value={address}
/>
</div>
<div className='ui--row'>
<Input
className='full'
label={t('editor.name', {
defaultValue: 'identified by the name'
})}
onChange={this.onChangeName}
value={editedName}
/>
</div>
</div>
<Button.Group>
<Button
isNegative
onClick={this.onForget}
text={t('editor.forget', {
defaultValue: 'Forget'
})}
/>
<Button.Group.Divider />
<Button
isDisabled={!isEdited}
onClick={this.onDiscard}
text={t('editor.reset', {
defaultValue: 'Reset'
})}
/>
<Button
isDisabled={!isEdited}
isPrimary
onClick={this.onCommit}
text={t('editor.save', {
defaultValue: 'Save'
})}
/>
</Button.Group>
</div>
);
}

View File

@ -31,7 +31,7 @@ const Components: { [Actions]: React$ComponentType<any> } = {
'edit': Editor
};
class App extends React.PureComponent<Props, State> {
class AccountsApp extends React.PureComponent<Props, State> {
state: State = { action: 'edit' };
render (): React$Node {
@ -75,4 +75,4 @@ class App extends React.PureComponent<Props, State> {
}
}
export default translate(App);
export default translate(AccountsApp);

View File

@ -39,8 +39,8 @@ class Creator extends React.PureComponent<Props, State> {
}
render (): React$Node {
const { className, style, t } = this.props;
const { address, isAddressValid, isNameValid, isValid, name } = this.state;
const { className, style } = this.props;
const { address } = this.state;
return (
<div
@ -52,47 +52,65 @@ class Creator extends React.PureComponent<Props, State> {
className='shrink'
value={address}
/>
<div className='grow'>
<div className='ui--row'>
<Input
className='full'
isError={!isAddressValid}
label={t('creator.address', {
defaultValue: 'add the following address'
})}
onChange={this.onChangeAddress}
value={address}
/>
</div>
<div className='ui--row'>
<Input
className='full'
isError={!isNameValid}
label={t('creator.name', {
defaultValue: 'name the entry'
})}
onChange={this.onChangeName}
value={name}
/>
</div>
</div>
{this.renderInput()}
</div>
<Button.Group>
<Button
onClick={this.onDiscard}
text={t('creator.discard', {
defaultValue: 'Reset'
{this.renderButtons()}
</div>
);
}
renderButtons (): React$Node {
const { t } = this.props;
const { isValid } = this.state;
return (
<Button.Group>
<Button
onClick={this.onDiscard}
text={t('creator.discard', {
defaultValue: 'Reset'
})}
/>
<Button
isDisabled={!isValid}
isPrimary
onClick={this.onCommit}
text={t('creator.save', {
defaultValue: 'Save'
})}
/>
</Button.Group>
);
}
renderInput (): React$Node {
const { t } = this.props;
const { address, isAddressValid, isNameValid, name } = this.state;
return (
<div className='grow'>
<div className='ui--row'>
<Input
className='full'
isError={!isAddressValid}
label={t('creator.address', {
defaultValue: 'add the following address'
})}
onChange={this.onChangeAddress}
value={address}
/>
<Button
isDisabled={!isValid}
isPrimary
onClick={this.onCommit}
text={t('creator.save', {
defaultValue: 'Save'
</div>
<div className='ui--row'>
<Input
className='full'
isError={!isNameValid}
label={t('creator.name', {
defaultValue: 'name the entry'
})}
onChange={this.onChangeName}
value={name}
/>
</Button.Group>
</div>
</div>
);
}

View File

@ -45,86 +45,100 @@ class Editor extends React.PureComponent<Props, State> {
}
render (): React$Node {
const { className, style, t } = this.props;
const { currentAddress, defaultPublicKey, editedName, isEdited } = this.state;
if (!currentAddress) {
return (
<div
className={classes('addresses--Editor', className)}
style={style}
>
{t('editor.none', {
defaultValue: 'There are no saved addresses. Add some first.'
})}
</div>
);
}
const address = currentAddress.address();
const { className, style } = this.props;
return (
<div
className={classes('addresses--Editor', className)}
style={style}
>
<div className='ui--grid'>
<Address
className='shrink'
value={address}
/>
<div className='grow'>
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultPublicKey}
hideAddress
isInput={false}
label={t('editor.select', {
defaultValue: 'edit the selected address'
})}
onChange={this.onChangeAddress}
type='address'
value={address}
/>
</div>
<div className='ui--row'>
<Input
className='full'
label={t('editor.name', {
defaultValue: 'identified by the name'
})}
onChange={this.onChangeName}
value={editedName}
/>
</div>
{this.renderData()}
{this.renderButtons()}
</div>
);
}
renderButtons (): React$Node {
const { t } = this.props;
const { currentAddress, isEdited } = this.state;
if (!currentAddress) {
return null;
}
return (
<Button.Group>
<Button
isNegative
onClick={this.onForget}
text={t('editor.forget', {
defaultValue: 'Forget'
})}
/>
<Button.Group.Divider />
<Button
isDisabled={!isEdited}
onClick={this.onDiscard}
text={t('editor.reset', {
defaultValue: 'Reset'
})}
/>
<Button
isDisabled={!isEdited}
isPrimary
onClick={this.onCommit}
text={t('editor.save', {
defaultValue: 'Save'
})}
/>
</Button.Group>
);
}
renderData (): React$Node {
const { t } = this.props;
const { currentAddress, defaultPublicKey, editedName } = this.state;
if (!currentAddress) {
return t('editor.none', {
defaultValue: 'There are no saved addresses. Add some first.'
});
}
const address = currentAddress.address();
return (
<div className='ui--grid'>
<Address
className='shrink'
value={address}
/>
<div className='grow'>
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultPublicKey}
hideAddress
isInput={false}
label={t('editor.select', {
defaultValue: 'edit the selected address'
})}
onChange={this.onChangeAddress}
type='address'
value={address}
/>
</div>
<div className='ui--row'>
<Input
className='full'
label={t('editor.name', {
defaultValue: 'identified by the name'
})}
onChange={this.onChangeName}
value={editedName}
/>
</div>
</div>
<Button.Group>
<Button
isNegative
onClick={this.onForget}
text={t('editor.forget', {
defaultValue: 'Forget'
})}
/>
<Button.Group.Divider />
<Button
isDisabled={!isEdited}
onClick={this.onDiscard}
text={t('editor.reset', {
defaultValue: 'Reset'
})}
/>
<Button
isDisabled={!isEdited}
isPrimary
onClick={this.onCommit}
text={t('editor.save', {
defaultValue: 'Save'
})}
/>
</Button.Group>
</div>
);
}

View File

@ -31,7 +31,7 @@ const Components: { [Actions]: React$ComponentType<any> } = {
'edit': Editor
};
class App extends React.PureComponent<Props, State> {
class AddressesApp extends React.PureComponent<Props, State> {
state: State = { action: 'edit' };
render (): React$Node {
@ -75,4 +75,4 @@ class App extends React.PureComponent<Props, State> {
}
}
export default translate(App);
export default translate(AddressesApp);

View File

@ -12,10 +12,10 @@
},
"dependencies": {
"@babel/runtime": "^7.0.0-beta.47",
"@polkadot/primitives": "^0.14.3",
"@polkadot/primitives-codec": "^0.14.3",
"@polkadot/primitives-json": "^0.14.3",
"@polkadot/primitives": "^0.15.8",
"@polkadot/primitives-codec": "^0.15.8",
"@polkadot/primitives-json": "^0.15.8",
"@polkadot/ui-app": "^0.6.2",
"@polkadot/util-crypto": "^0.20.5"
"@polkadot/util-crypto": "^0.21.3"
}
}

View File

@ -18,7 +18,7 @@ import translate from './translate';
type Props = I18nProps & {};
function App ({ className, style, t }: Props): React$Node {
function ExplorerApp ({ className, style, t }: Props): React$Node {
return (
<div
className={classes('explorer--App', className)}
@ -36,4 +36,4 @@ function App ({ className, style, t }: Props): React$Node {
);
}
export default translate(App);
export default translate(ExplorerApp);

View File

@ -12,9 +12,10 @@
},
"dependencies": {
"@babel/runtime": "^7.0.0-beta.47",
"@polkadot/extrinsics": "^0.14.3",
"@polkadot/extrinsics-codec": "^0.14.3",
"@polkadot/extrinsics": "^0.15.8",
"@polkadot/extrinsics-codec": "^0.15.8",
"@polkadot/ui-app": "^0.6.2",
"@polkadot/ui-signer": "^0.6.2",
"react-dropzone": "^4.2.9"
}
}

View File

@ -6,7 +6,7 @@
import type { Extrinsic } from '@polkadot/extrinsics/types';
import type { BareProps } from '@polkadot/ui-app/types';
import type { RawParam } from '@polkadot/ui-app/Params/types';
import type { EncodedMessage } from './types';
import type { EncodedMessage } from '@polkadot/ui-signer/types';
import React from 'react';
@ -24,7 +24,7 @@ type Props = BareProps & {
isPrivate?: boolean,
labelMethod?: string,
labelSection?: string,
onChange: (values: EncodedMessage) => void
onChange: (encoded: EncodedMessage) => void
};
type State = {
@ -87,7 +87,7 @@ export default class ExtrinsicDisplay extends React.PureComponent<Props, State>
onChange({
extrinsic,
isValid,
value
values: [value]
});
});
}

View File

@ -3,19 +3,17 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Props as BaseProps } from '@polkadot/ui-app/Params/types';
import type { Props } from '@polkadot/ui-app/Params/types';
import React from 'react';
import BaseAccount from '../Account';
type Props = BaseProps & {
defaultValue?: Uint8Array
};
export default class Account extends React.PureComponent<Props> {
render (): React$Node {
const { className, defaultValue, isDisabled, isError, label, style, withLabel } = this.props;
const { className, defaultValue: { value }, isDisabled, isError, label, style, withLabel } = this.props;
// flowlint-next-line unclear-type:off
const defaultValue = ((value: any): Uint8Array);
return (
<BaseAccount
@ -33,9 +31,9 @@ export default class Account extends React.PureComponent<Props> {
}
onChange = (value?: Uint8Array): void => {
const { index, onChange } = this.props;
const { onChange } = this.props;
onChange(index, {
onChange({
isValid: !!value && value.length === 32,
value
});

View File

@ -13,7 +13,7 @@ import Extrinsic from './Extrinsic';
const defaultValue = extrinsics.staking.methods.public.transfer;
export default function Call ({ className, isDisabled, isError, index, label, onChange, style, withLabel }: Props): React$Node {
export default function Call ({ className, isDisabled, isError, label, onChange, style, withLabel }: Props): React$Node {
return (
<Extrinsic
className={className}
@ -21,7 +21,6 @@ export default function Call ({ className, isDisabled, isError, index, label, on
isDisabled={isDisabled}
isError={isError}
isPrivate={false}
index={index}
label={label}
onChange={onChange}
style={style}

View File

@ -4,18 +4,22 @@
// @flow
import type { Extrinsic } from '@polkadot/extrinsics/types';
import type { Props as BaseProps } from '@polkadot/ui-app/Params/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { EncodedMessage } from '../types';
import type { RawParam$OnChange } from '@polkadot/ui-app/Params/types';
import type { EncodedMessage } from '@polkadot/ui-signer/types';
import React from 'react';
import BaseExtrinsic from '../Extrinsic';
import translate from '../translate';
type Props = BaseProps & I18nProps & {
type Props = I18nProps & {
defaultValue: Extrinsic,
isDisabled: boolean,
isError: boolean,
isPrivate: boolean,
label: string,
onChange: RawParam$OnChange,
withLabel?: boolean,
};
@ -49,12 +53,13 @@ class CExtrinsic extends React.PureComponent<Props> {
);
}
onChange = ({ isValid, value }: EncodedMessage): void => {
const { index, onChange } = this.props;
onChange = ({ isValid, values }: EncodedMessage): void => {
const { onChange } = this.props;
onChange(index, {
onChange({
isValid,
value
// flowlint-next-line unclear-type:off
value: ((values[0]: any): Uint8Array)
});
}
}

View File

@ -13,7 +13,7 @@ import Extrinsic from './Extrinsic';
const defaultValue = extrinsics.consensus.methods.private.setCode;
export default function Proposal ({ className, isDisabled, isError, index, label, onChange, style, withLabel }: Props): React$Node {
export default function Proposal ({ className, isDisabled, isError, label, onChange, style, withLabel }: Props): React$Node {
return (
<Extrinsic
className={className}
@ -21,7 +21,6 @@ export default function Proposal ({ className, isDisabled, isError, index, label
isDisabled={isDisabled}
isError={isError}
isPrivate
index={index}
label={label}
onChange={onChange}
style={style}

View File

@ -5,11 +5,12 @@
import type BN from 'bn.js';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { EncodedMessage, QueueTx } from './types';
import type { EncodedMessage, QueueTx$MessageAdd } from '@polkadot/ui-signer/types';
import React from 'react';
import extrinsics from '@polkadot/extrinsics-substrate';
import rpc from '@polkadot/jsonrpc';
import Button from '@polkadot/ui-app/Button';
import classes from '@polkadot/ui-app/util/classes';
@ -19,7 +20,7 @@ import Nonce from './Nonce';
import translate from './translate';
type Props = I18nProps & {
onQueue: (value: QueueTx) => void
queueAdd: QueueTx$MessageAdd
};
type State = {
@ -30,8 +31,7 @@ type State = {
};
const defaultExtrinsic = extrinsics.staking.methods.public.transfer;
let id = 0;
const defaultRpc = rpc.author.methods.submitExtrinsic;
class Selection extends React.PureComponent<Props, State> {
state: State = ({
@ -117,17 +117,15 @@ class Selection extends React.PureComponent<Props, State> {
}
onQueue = (): void => {
const { onQueue } = this.props;
const { encoded: { extrinsic, isValid, value }, nonce, publicKey } = this.state;
const { queueAdd } = this.props;
const { encoded: { isValid, values }, nonce, publicKey } = this.state;
onQueue({
extrinsic,
id: ++id,
queueAdd({
isValid,
nonce,
publicKey,
status: 'queued',
value
rpc: defaultRpc,
values
});
}
}

View File

@ -1,55 +0,0 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-app/types';
import type { QueueTx } from '../types';
import BN from 'bn.js';
import React from 'react';
import { Trans } from 'react-i18next';
import classes from '@polkadot/ui-app/util/classes';
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
import u8aToHexShort from '@polkadot/util/u8a/toHexShort';
import addressEncode from '@polkadot/util-keyring/address/encode';
import translate from '../translate';
type Props = I18nProps & {
value: QueueTx
};
function Decoded ({ className, style, t, value: { extrinsic: { name, section }, nonce = new BN(0), publicKey, value } }: Props): React$Node {
const from = addressEncode(publicKey);
return (
<div
className={classes('extrinsics--Signer-Decoded', className)}
style={style}
>
<div className='expanded'>
<p>
<Trans i18nkey='decoded.short'>
You are about to sign a message from <span className='code'>{from}</span> calling <span className='code'>{section}.{name}</span> with an index of <span className='code'>{nonce.toString()}</span>
</Trans>
</p>
<p>
{t('decoded.data', {
defaultValue: 'The encoded parameters contains the data'
})}
</p>
<p className='code'>
{u8aToHexShort(value, 512)}
</p>
</div>
<IdentityIcon
className='icon'
value={from}
/>
</div>
);
}
export default translate(Decoded);

View File

@ -1,35 +0,0 @@
/* Copyright 2017-2018 Jaco Greeff
/* This software may be modified and distributed under the terms
/* of the ISC license. See the LICENSE file for details. */
.extrinsics--Signer {}
.extrinsics--Signer-Decoded,
.extrinsics--Signer-Unlock {
align-content: center;
display: flex;
line-height: 1.5em;
}
.extrinsics--Signer-Decoded {}
.extrinsics--Signer-Unlock {
padding-top: 1.5rem;
}
.extrinsics--Signer-Content {}
.extrinsics--Signer-Content .expanded {
padding-right: 2em;
}
.extrinsics--Signer-Content .expanded .code {
background: #f5f5f5;
font-family: monospace;
margin: 0 0.25em;
overflow-wrap: break-word;
padding: 0.25em 0.5em;
word-break: break-all;
word-break: break-word;
word-wrap: break-word;
}

View File

@ -1,179 +0,0 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { ApiProps } from '@polkadot/ui-react-rx/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { QueueTx } from '../types';
import './Signer.css';
import React from 'react';
import Button from '@polkadot/ui-app/Button';
import Modal from '@polkadot/ui-app/Modal';
import classes from '@polkadot/ui-app/util/classes';
import keyring from '@polkadot/ui-keyring';
import withApi from '@polkadot/ui-react-rx/with/api';
import translate from '../translate';
import Decoded from './Decoded';
import Unlock from './Unlock';
import submitExtrinsic from './submit';
type Props = I18nProps & ApiProps & {
onSetStatus: (id: number, status: string) => void,
queue: Array<QueueTx>
};
type UnlockI18n = {
key: string,
value: I18Next$Translate$Config
}
type State = {
currentItem?: QueueTx,
password: string,
unlockError: UnlockI18n | null
};
class Signer extends React.PureComponent<Props, State> {
state: State = {
password: '',
unlockError: null
};
static getDerivedStateFromProps ({ queue }: Props, { currentItem, password, unlockError }: State): $Shape<State> {
const nextItem = queue.find(({ status }) =>
status === 'queued'
);
const isSame =
!!nextItem &&
!!currentItem &&
nextItem.publicKey.toString() === currentItem.publicKey.toString();
return {
currentItem: nextItem,
password: isSame ? password : '',
unlockError: isSame ? unlockError : null
};
}
render (): React$Node {
const { className, style, t } = this.props;
const { currentItem, password, unlockError } = this.state;
if (!currentItem) {
return null;
}
return (
<Modal
className={classes('extrinsics--Signer', className)}
dimmer='inverted'
open
style={style}
>
<Modal.Header>
{t('signer.header', {
defaultValue: 'Extrinsic submission'
})}
</Modal.Header>
<Modal.Content className='extrinsics--Signer-Content'>
<Decoded value={currentItem} />
<Unlock
error={unlockError && t(unlockError.key, unlockError.value)}
onChange={this.onChangePassword}
password={password}
value={currentItem.publicKey}
/>
</Modal.Content>
<Modal.Actions>
<Button.Group>
<Button
isNegative
onClick={this.onCancel}
text={t('signer.cancel', {
defaultValue: 'Cancel'
})}
/>
<Button.Or />
<Button
isPrimary
onClick={this.onSign}
text={t('signer.send', {
defaultValue: 'Sign and Submit'
})}
/>
</Button.Group>
</Modal.Actions>
</Modal>
);
}
unlockAccount (publicKey: Uint8Array, password?: string): ?UnlockI18n {
const pair = keyring.getPair(publicKey);
if (pair.hasSecretKey()) {
return null;
}
try {
// $FlowFixMe typo in underlying type, fixed at base (upgrades)
pair.decodePkcs8(void 0, password);
} catch (error) {
return {
key: 'signer.unlock.generic',
value: {
defaultValue: error.message
}
};
}
return null;
}
onChangePassword = (password: string): void => {
this.setState({
password,
unlockError: null
});
}
onCancel = (): void => {
const { onSetStatus } = this.props;
const { currentItem } = this.state;
// This should never be executed
if (!currentItem) {
return;
}
onSetStatus(currentItem.id, 'cancelled');
}
onSign = async (): Promise<void> => {
const { api, onSetStatus } = this.props;
const { currentItem, password } = this.state;
// This should never be executed
if (!currentItem) {
return;
}
const unlockError = this.unlockAccount(currentItem.publicKey, password);
if (unlockError) {
this.setState({ unlockError });
return;
}
onSetStatus(currentItem.id, 'sending');
onSetStatus(currentItem.id, await submitExtrinsic(api, currentItem));
};
}
export default translate(
withApi(Signer)
);

View File

@ -1,34 +0,0 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { RxApiInterface } from '@polkadot/api-rx/types';
import type { QueueTx } from '../types';
import encodeCall from '@polkadot/extrinsics-codec/encode/call';
import keyring from '@polkadot/ui-keyring';
import u8aConcat from '@polkadot/util/u8a/concat';
import u8aToHex from '@polkadot/util/u8a/toHex';
export default function submit (api: RxApiInterface, { nonce, publicKey, value }: QueueTx): Promise<string> {
const message = encodeCall(publicKey, nonce, value);
const signature = keyring.getPair(publicKey).sign(message);
console.log('submitExtrinsic: message =', u8aToHex(message));
console.log('submitExtrinsic: signature =', u8aToHex(signature));
return api.author
.submitExtrinsic(u8aConcat(message, signature))
.toPromise()
.then((result) => {
console.log('submitExtrinsic: result=', result);
return 'sent';
})
.catch((error) => {
console.error('submitExtrinsic: error=', error);
return 'error';
});
}

View File

@ -5,27 +5,3 @@
.extrinsics--App {
padding: 1em 2em;
}
.extrinsics--Status {
display: inline-block;
position: fixed;
right: 0.25rem;
top: 0.125rem;
z-index: 1001;
}
.extrinsics--Status-Item {
background: #bcdaf0;
margin: 0.125rem;
padding: 0.5rem 1rem;
text-align: center;
}
.extrinsics--Status-Item .status {
font-weight: 700;
}
.extrinsics--Status-Item.error {
background: red;
color: white;
}

View File

@ -4,69 +4,30 @@
// @flow
import type { BareProps } from '@polkadot/ui-app/types';
import type { QueueTx, QueueTx$Status } from './types';
import type { QueueProps } from '@polkadot/ui-signer/types';
import './index.css';
import React from 'react';
import classes from '@polkadot/ui-app/util/classes';
import Signer from '@polkadot/ui-signer';
import Selection from './Selection';
import Signer from './Signer';
import Status from './Status';
type Props = BareProps & {};
type Props = BareProps;
type State = {
queue: Array<QueueTx>
};
export default class App extends React.PureComponent<Props, State> {
state: State = {
queue: []
};
render (): React$Node {
const { className, style } = this.props;
const { queue } = this.state;
return (
<div
className={classes('extrinsics--App', className)}
style={style}
>
<Selection onQueue={this.onQueue} />
<Signer
onSetStatus={this.setStatus}
queue={queue}
/>
<Status queue={queue} />
</div>
);
}
setStatus = (id: number, status: QueueTx$Status): void => {
this.setState(
(prevState: State): State => ({
queue: prevState.queue.map((item) =>
item.id === id
? { ...item, status }
: item
)
})
);
if (['cancelled', 'error', 'sent'].includes(status)) {
setTimeout(() => this.setStatus(id, 'completed'), 5000);
}
}
onQueue = (value: QueueTx): void => {
this.setState(
(prevState: State): State => ({
queue: prevState.queue.concat([value])
})
);
}
export default function ExtrinsicsApp ({ className, style }: Props): React$Node {
return (
<div
className={classes('extrinsics--App', className)}
style={style}
>
<Signer.Queue.Consumer>
{({ queueAdd }: QueueProps = {}) => (
<Selection queueAdd={queueAdd} />
)}
</Signer.Queue.Consumer>
</div>
);
}

View File

@ -1,22 +0,0 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type BN from 'bn.js';
import type { Extrinsic } from '@polkadot/extrinsics/types';
export type EncodedMessage = {
extrinsic: Extrinsic,
isValid: boolean,
value: Uint8Array
};
export type QueueTx$Status = 'cancelled' | 'completed' | 'error' | 'incomplete' | 'queued' | 'sending' | 'sent';
export type QueueTx = EncodedMessage & {
id: number,
nonce: BN,
publicKey: Uint8Array,
status: QueueTx$Status
};

15
packages/app-rpc/LICENSE Normal file
View File

@ -0,0 +1,15 @@
ISC License (ISC)
Copyright 2017-2018 Jaco Greeff <jacogr@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

View File

@ -0,0 +1 @@
# @polkadot/app-rpc

View File

@ -0,0 +1,19 @@
{
"name": "@polkadot/app-rpc",
"version": "0.6.2",
"main": "index.js",
"repository": "https://github.com/polkadot-js/apps.git",
"author": "Jaco Greeff <jacogr@gmail.com>",
"license": "ISC",
"scripts": {
"build": "polkadot-dev-build-babel",
"check": "stylelint 'src/**/*.css' && eslint src && flow check",
"test": "jest --coverage"
},
"dependencies": {
"@babel/runtime": "^7.0.0-beta.47",
"@polkadot/ui-app": "^0.6.2",
"@polkadot/ui-keyring": "^0.6.2",
"@polkadot/ui-signer": "^0.6.2"
}
}

View File

@ -0,0 +1,95 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-app/types';
import BN from 'bn.js';
import React from 'react';
import InputAddress from '@polkadot/ui-app/InputAddress';
import Labelled from '@polkadot/ui-app/Labelled';
import classes from '@polkadot/ui-app/util/classes';
import Nonce from '@polkadot/ui-react-rx/Nonce';
import translate from './translate';
type Props = I18nProps & {
defaultValue?: Uint8Array,
isError?: boolean,
onChange: (publicKey?: Uint8Array, nonce: BN) => void
};
type State = {
nonce: BN,
publicKey?: Uint8Array
};
class Account extends React.PureComponent<Props, State> {
state: State;
constructor (props: Props) {
super(props);
this.state = {
publicKey: props.defaultValue,
nonce: new BN(0)
};
}
render (): React$Node {
const { className, defaultValue, isError, style, t } = this.props;
const { publicKey } = this.state;
return (
<div
className={classes('rpc--Account', 'ui--row', className)}
style={style}
>
<div className='large'>
<InputAddress
defaultValue={defaultValue}
isError={isError}
label={t('account.address', {
defaultValue: 'sign data from account'
})}
onChange={this.onChangeAccount}
placeholder='0x...'
type='account'
/>
</div>
<Labelled
className='small'
label={t('account.nonce', {
defaultValue: 'with an index of'
})}
>
<Nonce
className='ui disabled dropdown selection'
onChange={this.onChangeNonce}
params={publicKey}
/>
</Labelled>
</div>
);
}
onChangeAccount = (publicKey: Uint8Array): void => {
const { onChange } = this.props;
this.setState({ publicKey }, () =>
onChange(publicKey, this.state.nonce)
);
};
onChangeNonce = (nonce: BN): void => {
const { onChange } = this.props;
this.setState({ nonce }, () =>
onChange(this.state.publicKey, nonce)
);
};
}
export default translate(Account);

View File

@ -0,0 +1,49 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from '@polkadot/ui-app/types';
import type { QueueTx } from '@polkadot/ui-signer/types';
import React from 'react';
import Output from '@polkadot/ui-app/Output';
import classes from '@polkadot/ui-app/util/classes';
import isUndefined from '@polkadot/util/is/undefined';
type Props = BareProps & {
queue: Array<QueueTx>
};
export default function Results ({ className, queue = [], style }: Props): React$Node {
const filtered = queue
.filter(({ error, result }) =>
!isUndefined(error) || !isUndefined(result)
)
.reverse();
if (!filtered.length) {
return null;
}
return (
<div
className={classes('rpc--Results', className)}
style={style}
>
{filtered.map(({ error, id, result, rpc: { section, name } }) => (
<Output
isError={!!error}
key={id}
label={`${id}: ${section}.${name}`}
value={
error
? error.message
: result
}
/>
))}
</div>
);
}

View File

@ -0,0 +1,141 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { InterfaceMethodDefinition } from '@polkadot/jsonrpc/types';
import type { RawParam } from '@polkadot/ui-app/Params/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { QueueTx$MessageAdd } from '@polkadot/ui-signer/types';
import './index.css';
import BN from 'bn.js';
import React from 'react';
import rpc from '@polkadot/jsonrpc';
import Button from '@polkadot/ui-app/Button';
import InputRpc from '@polkadot/ui-app/InputRpc';
import Params from '@polkadot/ui-app/Params';
import classes from '@polkadot/ui-app/util/classes';
import rawToValues from '@polkadot/ui-signer/rawToValues';
import Account from './Account';
import translate from './translate';
type Props = I18nProps & {
queueAdd: QueueTx$MessageAdd
};
type State = {
isValid: boolean,
nonce: BN,
publicKey?: Uint8Array | null,
rpc: InterfaceMethodDefinition,
values: Array<RawParam>
}
const defaultMethod = rpc.author.methods.submitExtrinsic;
class Selection extends React.PureComponent<Props, State> {
state: State = {
isValid: false,
nonce: new BN(0),
publicKey: null,
rpc: defaultMethod,
values: []
};
render (): React$Node {
const { className, style, t } = this.props;
const { isValid, rpc } = this.state;
return (
<div
className={classes('rpc--Selection', className)}
style={style}
>
<InputRpc
defaultValue={defaultMethod}
onChange={this.onChangeMethod}
/>
{this.renderAccount()}
<Params
item={rpc}
onChange={this.onChangeValues}
/>
<Button.Group>
<Button
isDisabled={!isValid}
isPrimary
onClick={this.onSubmit}
text={t('submit', {
defaultValue: 'Submit RPC call'
})}
/>
</Button.Group>
</div>
);
}
renderAccount (): React$Node {
const { rpc: { isSigned = false }, publicKey } = this.state;
if (!isSigned) {
return null;
}
return (
<Account
defaultValue={publicKey}
onChange={this.onChangeAccount}
/>
);
}
nextState (newState: $Shape<State>): void {
this.setState(
(prevState: State): State => {
const { rpc = prevState.rpc, nonce = prevState.nonce, publicKey = prevState.publicKey, values = prevState.values } = newState;
const isValid = values.reduce((isValid, value) => {
return isValid && value.isValid === true;
}, rpc.isSigned !== true || (!!publicKey && publicKey.length === 32));
return {
isValid,
rpc,
nonce,
publicKey,
values
};
}
);
}
onChangeAccount = (publicKey?: Uint8Array, nonce: BN): void => {
this.nextState({ nonce, publicKey });
}
onChangeMethod = (rpc: InterfaceMethodDefinition): void => {
this.nextState({ rpc });
}
onChangeValues = (values: Array<RawParam>): void => {
this.nextState({ values });
}
onSubmit = (): void => {
const { queueAdd } = this.props;
const { isValid, nonce, publicKey, rpc, values } = this.state;
queueAdd({
isValid,
nonce,
publicKey,
rpc,
values: rawToValues(values)
});
}
}
export default translate(Selection);

View File

@ -0,0 +1,12 @@
/* Copyright 2017-2018 Jaco Greeff
/* This software may be modified and distributed under the terms
/* of the ISC license. See the LICENSE file for details. */
.rpc--App {
padding: 1em 2em;
}
.rpc--Account {
box-sizing: border-box;
padding-left: 2em;
}

View File

@ -0,0 +1,41 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-app/types';
import type { QueueProps } from '@polkadot/ui-signer/types';
import './index.css';
import React from 'react';
import classes from '@polkadot/ui-app/util/classes';
import Signer from '@polkadot/ui-signer';
import Results from './Results';
import Selection from './Selection';
type Props = I18nProps & {};
export default function RpcApp ({ className, style }: Props): React$Node {
return (
<div
className={classes('rpc--App', className)}
style={style}
>
<Signer.Queue.Consumer>
{({ queue, queueAdd }: QueueProps = {}) => [
<Selection
key='add'
queueAdd={queueAdd}
/>,
<Results
key='results'
queue={queue}
/>
]}
</Signer.Queue.Consumer>
</div>
);
}

View File

@ -0,0 +1,8 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import { translate } from 'react-i18next';
export default translate(['rpc', 'ui']);

View File

@ -8,9 +8,9 @@ import type { StorageQuery } from './types';
import React from 'react';
import typeToString from '@polkadot/params/typeToString';
import Button from '@polkadot/ui-app/Button';
import Labelled from '@polkadot/ui-app/Labelled';
import typeToText from '@polkadot/ui-app/Params/typeToText';
import valueToText from '@polkadot/ui-app/Params/valueToText';
import classes from '@polkadot/ui-app/util/classes';
import withStorageDiv from '@polkadot/ui-react-rx/with/storageDiv';
@ -22,10 +22,11 @@ type Props = I18nProps & {
value: StorageQuery
};
type ComponentProps = {};
type State = {
inputs: Array<React$Node>,
// flowlint-next-line unclear-type:off
Component: React$ComponentType<any>;
Component: React$ComponentType<ComponentProps>;
};
const cache = [];
@ -33,21 +34,23 @@ const cache = [];
class Query extends React.PureComponent<Props, State> {
state: State = ({}: $Shape<State>);
static getDerivedStateFromProps ({ value: { id, key, params } }: Props, prevState: State): State | null {
// flowlint-next-line unclear-type:off
const Component = ((): React$ComponentType<any> => {
if (!cache[id]) {
const values = params.map(({ value }) => value);
static getCachedComponent ({ id, key, params }: StorageQuery): React$ComponentType<ComponentProps> {
if (!cache[id]) {
const values = params.map(({ value }) => value);
cache[id] = withStorageDiv(key, { params: values })(
(value) =>
valueToText(key.type, value),
{ className: 'ui--output' }
);
}
cache[id] = withStorageDiv(key, { params: values })(
(value) =>
valueToText(key.type, value),
{ className: 'ui--output' }
);
}
return cache[id];
})();
return cache[id];
}
static getDerivedStateFromProps ({ value }: Props, prevState: State): State | null {
const Component = Query.getCachedComponent(value);
const { key, params } = value;
const inputs = Object
.keys(key.params)
.map((name, index) => {
@ -76,7 +79,7 @@ class Query extends React.PureComponent<Props, State> {
<Labelled
className='storage--actionrow-value'
label={
<div>{key.section}.{key.name}({inputs}): {typeToText(key.type)}</div>
<div>{key.section}.{key.name}({inputs}): {typeToString(key.type)}</div>
}
>
<Component />

View File

@ -10,7 +10,7 @@ import type { StorageQuery } from './types';
import React from 'react';
import storage from '@polkadot/storage-substrate/keys';
import storage from '@polkadot/storage-substrate';
import Button from '@polkadot/ui-app/Button';
import InputStorage from '@polkadot/ui-app/InputStorage';
import Labelled from '@polkadot/ui-app/Labelled';

View File

@ -22,7 +22,7 @@ type State = {
queue: Array<StorageQuery>
}
class App extends React.PureComponent<Props, State> {
class StorageApp extends React.PureComponent<Props, State> {
state: State = {
queue: []
};
@ -62,4 +62,4 @@ class App extends React.PureComponent<Props, State> {
}
}
export default translate(App);
export default translate(StorageApp);

View File

@ -14,7 +14,7 @@ import classes from '@polkadot/ui-app/util/classes';
import hexToU8a from '@polkadot/util/hex/toU8a';
import isHex from '@polkadot/util/is/hex';
import u8aFromString from '@polkadot/util/u8a/fromString';
import blake2AsHex256 from '@polkadot/util-crypto/blake2/asHex256';
import blake2AsHex from '@polkadot/util-crypto/blake2/asHex';
import translate from './translate';
@ -27,65 +27,83 @@ type State = {
class Hash extends React.PureComponent<Props, State> {
state: State = {
data: '',
hash: blake2AsHex256(u8aFromString('')),
hash: blake2AsHex(u8aFromString(''), 256),
isHexData: false
};
render (): React$Node {
const { className, style, t } = this.props;
const { data, hash, isHexData } = this.state;
const { className, style } = this.props;
return (
<div
className={classes('toolbox--Hash', className)}
style={style}
>
<div className='ui--row'>
<Input
className='large'
label={t('hash.data', {
defaultValue: 'from the following data (hex or string)'
})}
onChange={this.onChangeData}
value={data}
/>
<Static
className='small'
label={t('hash.isHex', {
defaultValue: 'hex input data'
})}
value={
isHexData
? t('hash.isHex.yes', {
defaultValue: 'Yes'
})
: t('hash.isHex.no', {
defaultValue: 'No'
})
}
/>
</div>
<div className='ui--row'>
<Output
className='full toolbox--hex'
isHidden={hash.length === 0}
label={t('hash.output', {
defaultValue: 'the resulting hash is'
})}
value={hash}
withCopy
/>
</div>
{this.renderInput()}
{this.renderOutput()}
</div>
);
}
renderInput (): React$Node {
const { t } = this.props;
const { data, isHexData } = this.state;
return (
<div className='ui--row'>
<Input
className='large'
label={t('hash.data', {
defaultValue: 'from the following data (hex or string)'
})}
onChange={this.onChangeData}
value={data}
/>
<Static
className='small'
label={t('hash.isHex', {
defaultValue: 'hex input data'
})}
value={
isHexData
? t('hash.isHex.yes', {
defaultValue: 'Yes'
})
: t('hash.isHex.no', {
defaultValue: 'No'
})
}
/>
</div>
);
}
renderOutput (): React$Node {
const { t } = this.props;
const { hash } = this.state;
return (
<div className='ui--row'>
<Output
className='full toolbox--hex'
isHidden={hash.length === 0}
label={t('hash.output', {
defaultValue: 'the resulting hash is'
})}
value={hash}
withCopy
/>
</div>
);
}
onChangeData = (data: string): void => {
const isHexData = isHex(data);
const hash = blake2AsHex256(
const hash = blake2AsHex(
isHexData
? hexToU8a(data)
: u8aFromString(data)
: u8aFromString(data),
256
);
this.setState({ data, hash, isHexData });

View File

@ -58,82 +58,116 @@ class Sign extends React.PureComponent<Props, State> {
}
render (): React$Node {
const { className, style, t } = this.props;
const { currentPair, data, defaultPublicKey, isHexData, isLocked, isUnlockVisible, signature } = this.state;
const { className, style } = this.props;
const { currentPair, isUnlockVisible } = this.state;
return (
<div
className={classes('toolbox--Sign', className)}
style={style}
>
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultPublicKey}
isInput={false}
label={t('sign.account', {
defaultValue: 'using my account'
})}
onChange={this.onChangeAccount}
type='account'
/>
</div>
<div className='ui--row'>
<Input
className='large'
label={t('sign.data', {
defaultValue: 'sign the following data (hex or string)'
})}
onChange={this.onChangeData}
value={data}
/>
<Static
className='small'
label={t('sign.isHex', {
defaultValue: 'hex input data'
})}
value={
isHexData
? t('sign.isHex.yes', {
defaultValue: 'Yes'
})
: t('sign.isHex.no', {
defaultValue: 'No'
})
}
/>
</div>
<div className='ui--row'>
<Output
className='full toolbox--hex'
isHidden={signature.length === 0}
label={t('sign.signed', {
defaultValue: 'signature of supplied data'
})}
value={signature}
withCopy
/>
</div>
{this.renderAccount()}
{this.renderInput()}
{this.renderSignature()}
<Unlock
isVisible={isUnlockVisible}
onClose={this.toggleUnlock}
pair={currentPair}
/>
{
isLocked
? (
<Button.Group>
<Button
isPrimary
onClick={this.toggleUnlock}
text={t('sign.unlock', {
defaultValue: 'Unlock account'
})}
/>
</Button.Group>
)
: null
}
{this.renderButtons()}
</div>
);
}
renderAccount (): React$Node {
const { t } = this.props;
const { defaultPublicKey } = this.state;
return (
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultPublicKey}
isInput={false}
label={t('sign.account', {
defaultValue: 'using my account'
})}
onChange={this.onChangeAccount}
type='account'
/>
</div>
);
}
renderButtons (): React$Node {
const { t } = this.props;
const { isLocked } = this.state;
if (!isLocked) {
return null;
}
return (
<Button.Group>
<Button
isPrimary
onClick={this.toggleUnlock}
text={t('sign.unlock', {
defaultValue: 'Unlock account'
})}
/>
</Button.Group>
);
}
renderInput (): React$Node {
const { t } = this.props;
const { data, isHexData } = this.state;
return (
<div className='ui--row'>
<Input
className='large'
label={t('sign.data', {
defaultValue: 'sign the following data (hex or string)'
})}
onChange={this.onChangeData}
value={data}
/>
<Static
className='small'
label={t('sign.isHex', {
defaultValue: 'hex input data'
})}
value={
isHexData
? t('sign.isHex.yes', {
defaultValue: 'Yes'
})
: t('sign.isHex.no', {
defaultValue: 'No'
})
}
/>
</div>
);
}
renderSignature (): React$Node {
const { t } = this.props;
const { signature } = this.state;
return (
<div className='ui--row'>
<Output
className='full toolbox--hex'
isHidden={signature.length === 0}
label={t('sign.signed', {
defaultValue: 'signature of supplied data'
})}
value={signature}
withCopy
/>
</div>
);
}

View File

@ -61,7 +61,6 @@ class Unlock extends React.PureComponent<Props, State> {
render (): React$Node {
const { className, isVisible, style, t } = this.props;
const { address, password, unlockError } = this.state;
if (!isVisible) {
return null;
@ -80,56 +79,73 @@ class Unlock extends React.PureComponent<Props, State> {
})}
</Modal.Header>
<Modal.Content>
<div className='toolbox--Unlock-Content'>
<div className='expanded'>
<p>
<Trans i18nkey='unlock.info'>
You are about to unlock your account <span className='code'>{address}</span> to allow for the signing of messages.
</Trans>
</p>
</div>
<IdentityIcon
className='icon'
value={address}
/>
</div>
<div className='toolbox--Unlock-Entry'>
<div className='ui--row'>
<Password
className='medium'
isError={!!unlockError}
label={t('unlock.password', {
defaultValue: 'unlock account using'
})}
onChange={this.onChangePassword}
value={password}
/>
</div>
</div>
{this.renderContent()}
</Modal.Content>
<Modal.Actions>
<Button.Group>
<Button
isNegative
onClick={this.onCancel}
text={t('unlock.cancel', {
defaultValue: 'Cancel'
})}
/>
<Button.Or />
<Button
isPrimary
onClick={this.onUnlock}
text={t('unlock.doit', {
defaultValue: 'Unlock'
})}
/>
</Button.Group>
{this.renderActions()}
</Modal.Actions>
</Modal>
);
}
renderActions (): React$Node {
const { t } = this.props;
return (
<Button.Group>
<Button
isNegative
onClick={this.onCancel}
text={t('unlock.cancel', {
defaultValue: 'Cancel'
})}
/>
<Button.Or />
<Button
isPrimary
onClick={this.onUnlock}
text={t('unlock.doit', {
defaultValue: 'Unlock'
})}
/>
</Button.Group>
);
}
renderContent (): React$Node {
const { t } = this.props;
const { address, password, unlockError } = this.state;
return [
<div className='toolbox--Unlock-Content' key='content'>
<div className='expanded'>
<p>
<Trans i18nkey='unlock.info'>
You are about to unlock your account <span className='code'>{address}</span> to allow for the signing of messages.
</Trans>
</p>
</div>
<IdentityIcon
className='icon'
value={address}
/>
</div>,
<div className='toolbox--Unlock-Entry' key='entry'>
<div className='ui--row'>
<Password
className='medium'
isError={!!unlockError}
label={t('unlock.password', {
defaultValue: 'unlock account using'
})}
onChange={this.onChangePassword}
value={password}
/>
</div>
</div>
];
}
unlockAccount (password?: string): ?UnlockI18n {
const { pair } = this.props;
@ -138,8 +154,7 @@ class Unlock extends React.PureComponent<Props, State> {
}
try {
// $FlowFixMe typo in underlying type, fixed at base (upgrades)
pair.decodePkcs8(void 0, password);
pair.decodePkcs8(password);
} catch (error) {
return {
key: 'unlock.generic',

View File

@ -56,69 +56,95 @@ class Verify extends React.PureComponent<Props, State> {
}
render (): React$Node {
const { className, style, t } = this.props;
const { defaultPublicKey, data, isHexData, isValid, isValidAddress, isValidSignature, signature } = this.state;
const { className, style } = this.props;
return (
<div
className={classes('toolbox--Verify', className)}
style={style}
>
<div className='ui--row'>
<Input
className='large'
label={t('verify.data', {
defaultValue: 'using the following data (hex or string)'
})}
onChange={this.onChangeData}
value={data}
/>
<Static
className='small'
label={t('verify.isHex', {
defaultValue: 'hex input data'
})}
value={
isHexData
? t('verify.isHex.yes', {
defaultValue: 'Yes'
})
: t('verify.isHex.no', {
defaultValue: 'No'
})
}
/>
</div>
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultPublicKey}
isError={!isValidAddress}
isInput
label={t('verify.account', {
defaultValue: 'verify using address'
})}
onChange={this.onChangeAddress}
/>
</div>
<div className='ui--row'>
<Input
className='full'
icon={
<Icon
color={isValid ? 'green' : (isValidSignature ? 'red' : void 0)}
name={isValid ? 'check circle' : (isValidSignature ? 'exclamation circle' : 'help circle')}
size='big'
/>
}
isError={!isValidSignature}
label={t('verify.signature', {
defaultValue: 'checking the supplied signature'
})}
onChange={this.onChangeSignature}
value={signature}
/>
</div>
{this.renderInput()}
{this.renderAddress()}
{this.renderSignature()}
</div>
);
}
renderAddress (): React$Node {
const { t } = this.props;
const { defaultPublicKey, isValidAddress } = this.state;
return (
<div className='ui--row'>
<InputAddress
className='full'
defaultValue={defaultPublicKey}
isError={!isValidAddress}
isInput
label={t('verify.account', {
defaultValue: 'verify using address'
})}
onChange={this.onChangeAddress}
/>
</div>
);
}
renderInput (): React$Node {
const { t } = this.props;
const { data, isHexData } = this.state;
return (
<div className='ui--row'>
<Input
className='large'
label={t('verify.data', {
defaultValue: 'using the following data (hex or string)'
})}
onChange={this.onChangeData}
value={data}
/>
<Static
className='small'
label={t('verify.isHex', {
defaultValue: 'hex input data'
})}
value={
isHexData
? t('verify.isHex.yes', {
defaultValue: 'Yes'
})
: t('verify.isHex.no', {
defaultValue: 'No'
})
}
/>
</div>
);
}
renderSignature (): React$Node {
const { t } = this.props;
const { isValid, isValidSignature, signature } = this.state;
return (
<div className='ui--row'>
<Input
className='full'
icon={
<Icon
color={isValid ? 'green' : (isValidSignature ? 'red' : void 0)}
name={isValid ? 'check circle' : (isValidSignature ? 'exclamation circle' : 'help circle')}
size='big'
/>
}
isError={!isValidSignature}
label={t('verify.signature', {
defaultValue: 'checking the supplied signature'
})}
onChange={this.onChangeSignature}
value={signature}
/>
</div>
);
}
@ -160,8 +186,6 @@ class Verify extends React.PureComponent<Props, State> {
}
onChangeSignature = (signature: string): void => {
console.log('onChangeSig', isHex(signature), signature.length);
const isValidSignature = isHex(signature) && signature.length === 130;
this.nextState({ signature, isValidSignature });

View File

@ -33,13 +33,13 @@ const Components: { [Actions]: React$ComponentType<any> } = {
'verify': Verify
};
class App extends React.PureComponent<Props, State> {
class ToolboxApp extends React.PureComponent<Props, State> {
state: State = {
action: 'hash'
};
render (): React$Node {
const { className, style, t } = this.props;
const { className, style } = this.props;
const { action } = this.state;
const Component = Components[action];
@ -48,36 +48,45 @@ class App extends React.PureComponent<Props, State> {
className={classes('toolbox--App', className)}
style={style}
>
<Button.Group className='accounts--App-navigation'>
<Button
isPrimary={action === 'hash'}
onClick={this.selectHash}
text={t('app.hash', {
defaultValue: 'Hash data'
})}
/>
<Button.Or />
<Button
isPrimary={action === 'sign'}
onClick={this.selectSign}
text={t('app.sign', {
defaultValue: 'Sign message'
})}
/>
<Button.Or />
<Button
isPrimary={action === 'verify'}
onClick={this.selectVerify}
text={t('app.verify', {
defaultValue: 'Verify signature'
})}
/>
</Button.Group>
{this.renderButtons()}
<Component />
</div>
);
}
renderButtons (): React$Node {
const { t } = this.props;
const { action } = this.state;
return (
<Button.Group className='accounts--App-navigation'>
<Button
isPrimary={action === 'hash'}
onClick={this.selectHash}
text={t('app.hash', {
defaultValue: 'Hash data'
})}
/>
<Button.Or />
<Button
isPrimary={action === 'sign'}
onClick={this.selectSign}
text={t('app.sign', {
defaultValue: 'Sign message'
})}
/>
<Button.Or />
<Button
isPrimary={action === 'verify'}
onClick={this.selectVerify}
text={t('app.verify', {
defaultValue: 'Verify signature'
})}
/>
</Button.Group>
);
}
selectHash = (): void => {
this.setState({ action: 'hash' });
}
@ -91,4 +100,4 @@ class App extends React.PureComponent<Props, State> {
}
}
export default translate(App);
export default translate(ToolboxApp);

View File

@ -5,26 +5,26 @@
import type { Generator$Calculation, Generator$Options } from './types';
function calculateAt (atOffset: number, test: Array<string>, address: string): Generator$Calculation {
return {
count: test.reduce((count, c, index) => {
if (index === count) {
count += (c === '?' || c === address.charAt(index + atOffset)) ? 1 : 0;
}
return count;
}, 0),
offset: atOffset
};
}
module.exports = function calculate (test: Array<string>, _address: string, { atOffset = -1, withCase = false }: Generator$Options): Generator$Calculation {
const address = withCase
? _address
: _address.toLowerCase();
const calculateAt = (atOffset: number): Generator$Calculation => {
return {
count: test.reduce((count, c, index) => {
if (index === count) {
count += (c === '?' || c === address.charAt(index + atOffset)) ? 1 : 0;
}
return count;
}, 0),
offset: atOffset
};
};
if (atOffset > 0) {
return calculateAt(atOffset);
return calculateAt(atOffset, test, address);
}
const maxOffset = address.length - test.length - 1;
@ -32,7 +32,7 @@ module.exports = function calculate (test: Array<string>, _address: string, { at
let bestOffset = 1;
for (let index = 1; index < maxOffset; index++) {
const { count, offset } = calculateAt(index);
const { count, offset } = calculateAt(index, test, address);
if (count > bestCount) {
bestCount = count;

View File

@ -42,7 +42,7 @@ const BOOL_OPTIONS = [
{ text: 'Yes', value: true }
];
class App extends React.PureComponent<Props, State> {
class VanityApp extends React.PureComponent<Props, State> {
results: Array<Generator$Result> = [];
state: State = {
elapsed: 0,
@ -57,82 +57,114 @@ class App extends React.PureComponent<Props, State> {
};
render (): React$Node {
const { className, style, t } = this.props;
const { elapsed, isMatchValid, isRunning, match, matches, keyCount, withCase } = this.state;
const { className, style } = this.props;
return (
<div
className={classes('vanity--App', className)}
style={style}
>
<div className='ui--row'>
<Input
className='medium'
isDisabled={isRunning}
isError={!isMatchValid}
label={t('vanity.matching', {
defaultValue: 'generate address containing (? wildcard)'
})}
onChange={this.onChangeMatch}
value={match}
{this.renderOptions()}
{this.renderButtons()}
{this.renderStats()}
{this.renderMatches()}
</div>
);
}
renderButtons (): React$Node {
const { t } = this.props;
const { isMatchValid, isRunning } = this.state;
return (
<Button.Group>
<Button
isDisabled={!isMatchValid}
isPrimary={!isRunning}
onClick={this.toggleStart}
text={
isRunning
? t('vanity.stop', { defaultValue: 'Stop generation' })
: t('vanity.start', { defaultValue: 'Start generation' })
}
/>
</Button.Group>
);
}
renderMatches (): React$Node {
const { matches } = this.state;
return (
<div className='vanity--App-matches'>
{matches.map((match) => (
<Match
{...match}
key={match.address}
onRemove={this.onRemove}
/>
<Dropdown
className='small'
isDisabled={isRunning}
label={t('vanity.case', {
defaultValue: 'case sensitive match'
})}
options={BOOL_OPTIONS}
onChange={this.onChangeCase}
value={withCase}
/>
<Static
className='small'
label={t('vanity.offset', {
defaultValue: 'exact offset'
})}
value={t('vanity.offset.off', {
defaultValue: 'No'
})}
/>
</div>
<Button.Group>
<Button
isDisabled={!isMatchValid}
isPrimary={!isRunning}
onClick={this.toggleStart}
text={
isRunning
? t('vanity.stop', { defaultValue: 'Stop generation' })
: t('vanity.start', { defaultValue: 'Start generation' })
}
/>
</Button.Group>
{
keyCount
? (
<div className='vanity--App-stats'>
{t('vanity.stats', {
defaultValue: 'Evaluated {{count}} keys in {{elapsed}}s ({{avg}}ms/key)',
replace: {
avg: (elapsed / keyCount).toFixed(3),
count: keyCount,
elapsed: (elapsed / 1000).toFixed(2)
}
})}
</div>
)
: null
}
<div className='vanity--App-matches'>
{matches.map((match) => (
<Match
{...match}
key={match.address}
onRemove={this.onRemove}
/>
))}
</div>
))}
</div>
);
}
renderOptions (): React$Node {
const { t } = this.props;
const { isMatchValid, isRunning, match, withCase } = this.state;
return (
<div className='ui--row'>
<Input
className='medium'
isDisabled={isRunning}
isError={!isMatchValid}
label={t('vanity.matching', {
defaultValue: 'generate address containing (? wildcard)'
})}
onChange={this.onChangeMatch}
value={match}
/>
<Dropdown
className='small'
isDisabled={isRunning}
label={t('vanity.case', {
defaultValue: 'case sensitive match'
})}
options={BOOL_OPTIONS}
onChange={this.onChangeCase}
value={withCase}
/>
<Static
className='small'
label={t('vanity.offset', {
defaultValue: 'exact offset'
})}
value={t('vanity.offset.off', {
defaultValue: 'No'
})}
/>
</div>
);
}
renderStats (): React$Node {
const { t } = this.props;
const { elapsed, keyCount } = this.state;
if (!keyCount) {
return null;
}
return (
<div className='vanity--App-stats'>
{t('vanity.stats', {
defaultValue: 'Evaluated {{count}} keys in {{elapsed}}s ({{avg}}ms/key)',
replace: {
avg: (elapsed / keyCount).toFixed(3),
count: keyCount,
elapsed: (elapsed / 1000).toFixed(2)
}
})}
</div>
);
}
@ -237,4 +269,4 @@ class App extends React.PureComponent<Props, State> {
}
}
export default translate(App);
export default translate(VanityApp);

View File

@ -19,10 +19,12 @@
"@polkadot/app-addresses": "^0.6.2",
"@polkadot/app-explorer": "^0.6.2",
"@polkadot/app-extrinsics": "^0.6.2",
"@polkadot/app-rpc": "^0.6.2",
"@polkadot/app-storage": "^0.6.2",
"@polkadot/app-toolbox": "^0.6.2",
"@polkadot/app-vanitygen": "^0.6.2",
"@polkadot/ui-app": "^0.6.2"
"@polkadot/ui-app": "^0.6.2",
"@polkadot/ui-signer": "^0.6.2"
},
"devDependencies": {
"babel-loader": "^8.0.0-beta.2",

View File

@ -3,32 +3,32 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-app/types';
import type { BareProps } from '@polkadot/ui-app/types';
import './App.css';
import React from 'react';
import classes from '@polkadot/ui-app/util/classes';
import Signer from '@polkadot/ui-signer';
import Connecting from '../Connecting';
import Content from '../Content';
import SideBar from '../SideBar';
import translate from '../translate';
type Props = I18nProps & {};
type Props = BareProps & {};
function App ({ className, style }: Props): React$Node {
export default function Apps ({ className, style }: Props): React$Node {
return (
<div
className={classes('apps--App', className)}
style={style}
>
<SideBar />
<Content />
<Signer>
<Content />
</Signer>
<Connecting />
</div>
);
}
export default translate(App);

View File

@ -10,6 +10,7 @@ import addresses from './addresses';
import explorer from './explorer';
import extrinsics from './extrinsics';
// import home from './home';
import rpc from './rpc';
// import settings from './settings';
import storage from './storage';
import toolbox from './toolbox';
@ -19,7 +20,7 @@ import vanitygen from './vanitygen';
export default ({
default: 'explorer',
routes: [].concat(
explorer, extrinsics, storage, toolbox, null, accounts, addresses, vanitygen
explorer, extrinsics, storage, null, accounts, addresses, vanitygen, null, rpc, toolbox
),
unknown
}: Routing);

View File

@ -0,0 +1,21 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Routes } from '../types';
import Rpc from '@polkadot/app-rpc';
export default ([
{
Component: Rpc,
i18n: {
defaultValue: 'Raw RPC'
},
icon: 'code',
isExact: false,
isHidden: false,
name: 'rpc'
}
]: Routes);

View File

@ -10,7 +10,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const packages = ['app-accounts', 'app-addresses', 'app-explorer', 'app-extrinsics', 'app-storage', 'app-toolbox', 'app-vanitygen', 'ui-app', 'ui-keyring'];
const packages = ['app-accounts', 'app-addresses', 'app-explorer', 'app-extrinsics', 'app-rpc', 'app-storage', 'app-toolbox', 'app-vanitygen', 'ui-app', 'ui-keyring', 'ui-signer'];
function createWebpack ({ alias = {}, context, name = 'index' }) {
const pkgJson = require(path.join(context, 'package.json'));

View File

@ -12,14 +12,14 @@
},
"dependencies": {
"@babel/runtime": "^7.0.0-beta.47",
"@polkadot/extrinsics-substrate": "^0.14.3",
"@polkadot/params": "^0.14.3",
"@polkadot/primitives": "^0.14.3",
"@polkadot/storage-substrate": "^0.14.3",
"@polkadot/extrinsics-substrate": "^0.15.8",
"@polkadot/params": "^0.15.8",
"@polkadot/primitives": "^0.15.8",
"@polkadot/storage-substrate": "^0.15.8",
"@polkadot/ui-keyring": "^0.6.2",
"@polkadot/ui-react": "^0.12.3",
"@polkadot/ui-react-rx": "^0.12.3",
"@polkadot/util": "^0.20.5",
"@polkadot/ui-react": "^0.13.4",
"@polkadot/ui-react-rx": "^0.13.4",
"@polkadot/util": "^0.21.3",
"i18next": "^11.1.1",
"i18next-browser-languagedetector": "^2.2.0",
"i18next-xhr-backend": "^1.5.1",

View File

@ -58,8 +58,7 @@ export default class Dropdown extends React.PureComponent<Props> {
);
}
// flowlint-next-line unclear-type:off
onChange = (event: SyntheticEvent<any>, { value }: SUIEvent): void => {
onChange = (event: SyntheticEvent<Element>, { value }: SUIEvent): void => {
const { onChange, transform } = this.props;
onChange(

View File

@ -94,8 +94,7 @@ export default class Input extends React.PureComponent<Props, State> {
);
}
// flowlint-next-line unclear-type:off
onChange = (event: SyntheticEvent<any>, { value }: SUIEvent): void => {
onChange = (event: SyntheticEvent<Element>, { value }: SUIEvent): void => {
this.props.onChange(value);
}
}

View File

@ -6,7 +6,7 @@
import addressDecode from '@polkadot/util-keyring/address/decode';
import addressEncode from '@polkadot/util-keyring/address/encode';
export default function addressToAddress (value?: string | Uint8Array): ?string {
export default function addressToAddress (value?: string | Uint8Array | null): ?string {
// flowlint-next-line sketchy-null-string:off
if (value) {
try {

View File

@ -19,7 +19,7 @@ import classes from '../util/classes';
import addressToAddress from './addressToAddress';
type Props = BareProps & {
defaultValue?: string | Uint8Array,
defaultValue?: string | Uint8Array | null,
hideAddress?: boolean;
isError?: boolean,
isInput?: boolean,

View File

@ -0,0 +1,53 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { InterfaceMethodDefinition } from '@polkadot/jsonrpc/types';
import type { I18nProps } from '../types';
import React from 'react';
import map from '@polkadot/jsonrpc';
import Dropdown from '../Dropdown';
import classes from '../util/classes';
import translate from '../translate';
type Props = I18nProps & {
isError?: boolean,
label?: string,
onChange: (value: InterfaceMethodDefinition) => void,
// flowlint-next-line unclear-type:off
options: Array<any>,
value: InterfaceMethodDefinition,
withLabel?: boolean
};
function SelectMethod ({ className, isError, label = '', onChange, options, style, t, value: { name, section }, withLabel }: Props): React$Node {
if (!options.length) {
return null;
}
const transform = (name: string): InterfaceMethodDefinition =>
// $FlowFixMe ... string to type :(
map[section].methods[name];
return (
<Dropdown
className={classes('ui--DropdownLinked-Items', className)}
isError={isError}
label={label || t('input.rpc.method', {
defaultValue: 'with method name'
})}
onChange={onChange}
options={options}
style={style}
transform={transform}
value={name}
withLabel={withLabel}
/>
);
}
export default translate(SelectMethod);

View File

@ -0,0 +1,44 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { InterfaceMethodDefinition, InterfaceTypes } from '@polkadot/jsonrpc/types';
import type { I18nProps } from '../types';
import React from 'react';
import Dropdown from '../Dropdown';
import classes from '../util/classes';
import translate from '../translate';
type Props = I18nProps & {
defaultValue?: InterfaceTypes,
isError?: boolean,
label: string,
onChange: (value: InterfaceTypes) => void,
// flowlint-next-line unclear-type:off
options: Array<any>,
value: InterfaceMethodDefinition,
withLabel?: boolean
};
function SelectSection ({ className, defaultValue, isError, label, onChange, options, style, t, value: { section }, withLabel }: Props): React$Node {
return (
<Dropdown
className={classes('ui--DropdownLinked-Sections', className)}
defaultValue={defaultValue}
isError={isError}
label={label || t('input.rpc.section', {
defaultValue: 'rpc area'
})}
onChange={onChange}
options={options}
style={style}
value={section}
withLabel={withLabel}
/>
);
}
export default translate(SelectSection);

View File

@ -0,0 +1,114 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
// TODO: We have a lot shared between this and InputExtrinsic & InputStorage
import type { InterfaceMethodDefinition, InterfaceTypes } from '@polkadot/jsonrpc/types';
import type { I18nProps } from '../types';
import '../InputExtrinsic/InputExtrinsic.css';
import React from 'react';
import map from '@polkadot/jsonrpc';
import classes from '../util/classes';
import translate from '../translate';
import SelectMethod from './SelectMethod';
import SelectSection from './SelectSection';
import methodOptions from './options/method';
import sectionOptions from './options/section';
type Props = I18nProps & {
defaultValue: InterfaceMethodDefinition,
isError?: boolean,
labelMethod?: string,
labelSection?: string,
onChange: (value: InterfaceMethodDefinition) => void,
withLabel?: boolean
};
type State = {
// flowlint-next-line unclear-type:off
optionsMethod: Array<any>,
// flowlint-next-line unclear-type:off
optionsSection: Array<any>,
value: InterfaceMethodDefinition
};
class InputRpc extends React.PureComponent<Props, State> {
state: State;
constructor (props: Props) {
super(props);
const { section } = this.props.defaultValue;
this.state = {
optionsMethod: methodOptions(section),
optionsSection: sectionOptions(),
value: this.props.defaultValue
};
}
render (): React$Node {
const { className, labelMethod, labelSection, style, withLabel } = this.props;
const { optionsMethod, optionsSection, value } = this.state;
return (
<div
className={classes('ui--DropdownLinked', 'ui--row', className)}
style={style}
>
<SelectSection
className='small'
label={labelSection}
onChange={this.onSectionChange}
options={optionsSection}
value={value}
withLabel={withLabel}
/>
<SelectMethod
className='large'
label={labelMethod}
onChange={this.onMethodChange}
options={optionsMethod}
value={value}
withLabel={withLabel}
/>
</div>
);
}
onMethodChange = (value: InterfaceMethodDefinition): void => {
const { onChange } = this.props;
const { value: { name, section } } = this.state;
if (value.section === section && value.name === name) {
return;
}
this.setState({ value }, () =>
onChange(value)
);
}
onSectionChange = (newSection: InterfaceTypes): void => {
const { value: { section } } = this.state;
if (newSection === section) {
return;
}
const optionsMethod = methodOptions(newSection);
const value = map[newSection].methods[optionsMethod[0].value];
this.setState({ optionsMethod }, () =>
this.onMethodChange(value)
);
}
}
export default translate(InputRpc);

View File

@ -0,0 +1,52 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { InterfaceTypes } from '@polkadot/jsonrpc/types';
import React from 'react';
import map from '@polkadot/jsonrpc';
// flowlint-next-line unclear-type:off
export default function createOptions (sectionName: any): Array<any> {
const section = map[(sectionName: InterfaceTypes)];
if (!section) {
return [];
}
return Object
.keys(section.methods)
.sort()
.filter((name) => {
const { isDeprecated = false, isHidden = false, isSubscription = false } = section.methods[name];
return !isDeprecated && !isHidden && !isSubscription;
})
.map((name) => {
const { description = '', params = {} } = section.methods[name];
const inputs = Object.keys(params).join(', ');
return {
className: 'ui--DropdownLinked-Item',
key: `${sectionName}_${name}`,
text: [
<div
className='ui--DropdownLinked-Item-call'
key={`${sectionName}_${name}:call`}
>
{name}({inputs})
</div>,
<div
className='ui--DropdownLinked-Item-text'
key={`${sectionName}_${name}:text`}
>
{description || name}
</div>
],
value: name
};
});
}

View File

@ -0,0 +1,29 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import map from '@polkadot/jsonrpc';
// flowlint-next-line unclear-type:off
export default function createOptions (): Array<any> {
return Object
.keys(map)
.sort()
.filter((name) => {
const { isDeprecated = false, methods } = map[name];
const available = Object
.keys(methods)
.filter((name) => {
const { isDeprecated = false, isHidden = false } = methods[name];
return !isDeprecated && !isHidden;
});
return !isDeprecated && available.length !== 0;
})
.map((name) => ({
text: name,
value: name
}));
}

View File

@ -8,7 +8,7 @@ import type { I18nProps } from '../types';
import React from 'react';
import map from '@polkadot/storage-substrate/keys';
import map from '@polkadot/storage-substrate';
import Dropdown from '../Dropdown';
import classes from '../util/classes';

View File

@ -12,7 +12,7 @@ import '../InputExtrinsic/InputExtrinsic.css';
import React from 'react';
import map from '@polkadot/storage-substrate/keys';
import map from '@polkadot/storage-substrate';
import classes from '../util/classes';
import translate from '../translate';

View File

@ -7,7 +7,7 @@ import type { StateDb$SectionNames } from '@polkadot/storage/types';
import React from 'react';
import map from '@polkadot/storage-substrate/keys';
import map from '@polkadot/storage-substrate';
// flowlint-next-line unclear-type:off
export default function createOptions (sectionName: any): Array<any> {

View File

@ -3,7 +3,7 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import map from '@polkadot/storage-substrate/keys';
import map from '@polkadot/storage-substrate';
// flowlint-next-line unclear-type:off
export default function createOptions (): Array<any> {

View File

@ -9,9 +9,11 @@ import React from 'react';
import CopyButton from './CopyButton';
import Labelled from './Labelled';
import classes from './util/classes';
type Props = BareProps & {
children?: React$Node,
isError?: boolean,
isHidden?: boolean,
label?: React$Node,
value?: mixed,
@ -19,7 +21,7 @@ type Props = BareProps & {
withLabel?: boolean
};
export default function Output ({ className, children, isHidden, label, style, value, withCopy = false, withLabel }: Props): React$Node {
export default function Output ({ className, children, isError = false, isHidden, label, style, value, withCopy = false, withLabel }: Props): React$Node {
return (
<Labelled
className={className}
@ -28,7 +30,7 @@ export default function Output ({ className, children, isHidden, label, style, v
style={style}
withLabel={withLabel}
>
<div className='ui--output'>
<div className={classes('ui--output', isError && 'error')}>
{
// flowlint-next-line unclear-type:off
((value: any): React$Node)

View File

@ -12,10 +12,9 @@ import Bare from './Bare';
export default class Account extends React.PureComponent<Props> {
render (): React$Node {
const { className, isDisabled, isError, label, style, value: { options: { initValue } = {} }, withLabel } = this.props;
const { className, defaultValue: { value }, isDisabled, isError, label, style, withLabel } = this.props;
// flowlint-next-line unclear-type:off
const defaultValue = ((initValue: any): Uint8Array);
const defaultValue = ((value: any): Uint8Array);
return (
<Bare
@ -38,9 +37,9 @@ export default class Account extends React.PureComponent<Props> {
}
onChange = (value?: Uint8Array): void => {
const { index, onChange } = this.props;
const { onChange } = this.props;
onChange(index, {
onChange({
isValid: !!value && value.length === 32,
value
});

View File

@ -2,7 +2,6 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
// flowlint sketchy-null-mixed:off,sketchy-null-number:off
import type { Props } from '../types';
@ -14,7 +13,9 @@ import Bare from './Bare';
export default class Amount extends React.PureComponent<Props> {
render (): React$Node {
const { className, isDisabled, isError, label, style, value: { options: { initValue = 0, maxValue, minValue = 0 } = {} }, withLabel } = this.props;
const { className, defaultValue: { value }, isDisabled, isError, label, style, withLabel } = this.props;
// flowlint-next-line unclear-type:off
const defaultValue = new BN(((value: any): BN) || 0).toNumber();
return (
<Bare
@ -23,12 +24,11 @@ export default class Amount extends React.PureComponent<Props> {
>
<Input
className='small'
defaultValue={initValue || minValue}
defaultValue={defaultValue || 0}
isDisabled={isDisabled}
isError={isError}
label={label}
max={maxValue}
min={minValue}
min={0}
onChange={this.onChange}
type='number'
withLabel={withLabel}
@ -38,11 +38,11 @@ export default class Amount extends React.PureComponent<Props> {
}
onChange = (value: number): void => {
const { index, onChange, value: { options: { minValue = 0 } = {} } } = this.props;
const { onChange } = this.props;
onChange(index, {
onChange({
isValid: true,
value: new BN(value || minValue || 0)
value: new BN(value || 0)
});
}
}

View File

@ -0,0 +1,67 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Props as BaseProps, Size } from '../types';
import React from 'react';
import hexToU8a from '@polkadot/util/hex/toU8a';
import Input from '../../Input';
import Bare from './Bare';
type Props = BaseProps & {
length?: number,
size?: Size,
validate?: (u8a: Uint8Array) => boolean
};
const defaultValidate = (u8a: Uint8Array): boolean =>
true;
export default class BaseBytes extends React.PureComponent<Props> {
render (): React$Node {
const { className, isDisabled, isError, label, size = 'full', style, withLabel } = this.props;
return (
<Bare
className={className}
style={style}
>
<Input
className={size}
isDisabled={isDisabled}
isError={isError}
label={label}
onChange={this.onChange}
placeholder='0x...'
type='text'
withLabel={withLabel}
/>
</Bare>
);
}
onChange = (hex: string): void => {
const { length = -1, onChange, validate = defaultValidate } = this.props;
let u8a;
try {
u8a = hexToU8a(hex);
} catch (error) {
u8a = new Uint8Array([]);
}
const isValidLength = length !== -1
? u8a.length === length
: u8a.length !== 0;
onChange({
isValid: isValidLength && validate(u8a),
value: u8a
});
};
}

View File

@ -17,7 +17,9 @@ const options = [
export default class Bool extends React.PureComponent<Props> {
render (): React$Node {
const { className, isDisabled, isError, label, style, value: { options: { initValue = false } = {} }, withLabel } = this.props;
const { className, defaultValue: { value }, isDisabled, isError, label, style, withLabel } = this.props;
// flowlint-next-line unclear-type:off
const defaultValue = ((value: any): boolean);
return (
<Bare
@ -26,9 +28,9 @@ export default class Bool extends React.PureComponent<Props> {
>
<Dropdown
className='small'
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
defaultValue={initValue}
label={label}
options={options}
onChange={this.onChange}
@ -39,9 +41,9 @@ export default class Bool extends React.PureComponent<Props> {
}
onChange = (value: boolean): void => {
const { index, onChange } = this.props;
const { onChange } = this.props;
onChange(index, {
onChange({
isValid: true,
value
});

View File

@ -3,65 +3,26 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Props as BaseProps, Size } from '../types';
import type { Props } from '../types';
import React from 'react';
import hexToU8a from '@polkadot/util/hex/toU8a';
import BaseBytes from './BaseBytes';
import Input from '../../Input';
import Bare from './Bare';
type Props = BaseProps & {
length?: number,
size?: Size,
validate?: (u8a: Uint8Array) => boolean
}
const defaultValidate = (u8a: Uint8Array): boolean =>
true;
export default class Bytes extends React.PureComponent<Props> {
render (): React$Node {
const { className, isDisabled, isError, label, size = 'full', style, withLabel } = this.props;
return (
<Bare
className={className}
style={style}
>
<Input
className={size}
isDisabled={isDisabled}
isError={isError}
label={label}
onChange={this.onChange}
placeholder='0x...'
type='text'
withLabel={withLabel}
/>
</Bare>
);
}
onChange = (hex: string): void => {
const { index, length = -1, onChange, validate = defaultValidate } = this.props;
let u8a;
try {
u8a = hexToU8a(hex);
} catch (error) {
u8a = new Uint8Array([]);
}
const isValidLength = length !== -1
? u8a.length === length
: u8a.length !== 0;
onChange(index, {
isValid: isValidLength && validate(u8a),
value: u8a
});
};
export default function Bytes ({ className, defaultValue, isDisabled, isError, label, name, onChange, style, withLabel }: Props): React$Node {
return (
<BaseBytes
className={className}
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
label={label}
length={-1}
name={name}
onChange={onChange}
size='full'
style={style}
withLabel={withLabel}
/>
);
}

View File

@ -31,9 +31,9 @@ export default class Code extends React.PureComponent<Props> {
// TODO: Validate that we have actual proper WASM code
onChange = (value: Uint8Array): void => {
const { index, onChange } = this.props;
const { onChange } = this.props;
onChange(index, {
onChange({
isValid: value.length !== 0,
value: u8aConcat(bnToU8a(value.length, 32, true), value)
});

View File

@ -7,19 +7,19 @@ import type { Props } from '../types';
import React from 'react';
import Bytes from './Bytes';
import BaseBytes from './BaseBytes';
export default function Hash ({ index, isDisabled, isError, label, onChange, value, withLabel }: Props): React$Node {
export default function Hash ({ defaultValue, isDisabled, isError, label, name, onChange, withLabel }: Props): React$Node {
return (
<Bytes
index={index}
<BaseBytes
defaultValue={defaultValue}
isDisabled={isDisabled}
isError={isError}
label={label}
length={32}
name={name}
onChange={onChange}
size='medium'
value={value}
withLabel={withLabel}
/>
);

View File

@ -89,10 +89,10 @@ export default class KeyValue extends React.PureComponent<Props, State> {
nextState (newState: $Shape<State>): void {
this.setState(
(prevState: State, { index, onChange }: Props) => {
(prevState: State, { onChange }: Props) => {
const { key = prevState.key, value = prevState.value } = newState;
onChange(index, {
onChange({
isValid: key.isValid && value.isValid,
value: u8aConcat(
u8aConcat(bnToU8a(key.u8a.length, 32, true), key.u8a),

View File

@ -3,24 +3,22 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Param$Types } from '@polkadot/params/types';
import type { Props as BareProps } from '../types';
import type { Props as BareProps, RawParam } from '../types';
import React from 'react';
import typeToString from '@polkadot/params/typeToString';
import translate from '../../translate';
import Base from './Base';
import typeToText from '../typeToText';
type Props = BareProps & {
defaultValue: RawParam,
t: I18Next$Translate,
value: {
type: Param$Types
},
withLabel?: boolean
};
function Unknown ({ label, t, value: { type }, withLabel }: Props): React$Node {
function Unknown ({ defaultValue: { type }, label, t, withLabel }: Props): React$Node {
return (
<Base
size='full'
@ -31,7 +29,7 @@ function Unknown ({ label, t, value: { type }, withLabel }: Props): React$Node {
{t('param.unknown', {
defaultValue: `ERROR: Unimplemented type '{{type}}' requested. No renderer exists`,
replace: {
type: typeToText(type)
type: typeToString(type)
}
})}
</div>

View File

@ -2,7 +2,6 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
// flowlint sketchy-null-mixed:off
import type { Props } from '../types';
@ -19,8 +18,9 @@ const options = [
export default class VoteThreshold extends React.PureComponent<Props> {
render (): React$Node {
const { className, isDisabled, isError, label, style, value: { options: { initValue = 0 } = {} }, withLabel } = this.props;
const defaultValue = initValue || 0;
const { className, defaultValue: { value }, isDisabled, isError, label, style, withLabel } = this.props;
// flowlint-next-line unclear-type:off
const defaultValue = ((value: any): number);
return (
<Bare
@ -42,9 +42,9 @@ export default class VoteThreshold extends React.PureComponent<Props> {
}
onChange = (value: number): void => {
const { index, onChange } = this.props;
const { onChange } = this.props;
onChange(index, {
onChange({
isValid: true,
value
});

View File

@ -4,7 +4,7 @@
// @flow
import type { Param$Types } from '@polkadot/params/types';
import type { ComponentMap } from '../types';
import type { Props, ComponentMap } from '../types';
import Account from './Account';
import Amount from './Amount';
@ -34,6 +34,7 @@ const components: ComponentMap = {
'ReferendumIndex': Amount,
'SessionKey': Amount,
'Signature': Hash,
'String': Unknown,
'Timestamp': Amount,
'u32': Amount,
'u64': Amount,
@ -41,14 +42,9 @@ const components: ComponentMap = {
'VoteThreshold': VoteThreshold
};
// flowlint-next-line unclear-type:off
export default function findComponent (type: Param$Types, overrides?: ComponentMap = {}): React$ComponentType<any> | Array<React$ComponentType<any>> {
export default function findComponent (type: Param$Types, overrides?: ComponentMap = {}): React$ComponentType<Props> {
if (Array.isArray(type)) {
return type
.map((type) =>
// $FlowFixMe running out of options to check the embedded arrays
findComponent(type, overrides)
);
return Unknown;
}
return overrides[type] || components[type] || Unknown;

View File

@ -3,137 +3,61 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Param } from '@polkadot/params/types';
import type { I18nProps } from '../../types';
import type { BaseProps, ComponentMap, RawParam } from '../types';
import type { Props as BaseProps, ComponentMap } from '../types';
import React from 'react';
import typeToString from '@polkadot/params/typeToString';
import classes from '../../util/classes';
import translate from '../../translate';
import typeToText from '../typeToText';
import findComponent from './findComponent';
import Unknown from './Unknown';
type Props = I18nProps & {
type Props = I18nProps & BaseProps & {
index: number,
overrides?: ComponentMap,
onChange: (index: number, value: RawParam) => void,
value: Param & {
name: string
};
};
type ComponentProps = BaseProps & {
index: number
overrides?: ComponentMap
};
type State = {
// flowlint-next-line unclear-type:off
Components: React$ComponentType<any> | Array<React$ComponentType<any>>
| null
Component: React$ComponentType<BaseProps> | null
}
class ParamComponent extends React.PureComponent<Props, State> {
state: State = {
Components: []
Component: null
};
static getDerivedStateFromProps ({ overrides, value: { type } = {} }: Props): State {
static getDerivedStateFromProps ({ defaultValue: { type }, overrides }: Props): State {
return {
Components: !type
Component: !type
? null
: findComponent(type, overrides)
};
}
render (): React$Node {
const { Components } = this.state;
const { Component } = this.state;
if (Components === null) {
if (Component === null) {
return null;
}
const { value: { type } } = this.props;
// FIXME We don't handle array or tuple inputs atm
return Array.isArray(type)
? this.renderUnknown()
: this.renderComponents();
}
// flowlint-next-line unclear-type:off
renderComponents = (_Components: React$ComponentType<any> | Array<React$ComponentType<any>> | null = this.state.Components, startIndex: string = '0', { name, type, options = {} } = this.props.value) => {
if (!_Components) {
return null;
}
const { className, index, onChange, style } = this.props;
if (!Array.isArray(type)) {
// flowlint-next-line unclear-type:off
const Component = ((_Components: any): React$ComponentType<any>);
return this.renderComponent(Component, startIndex, {
className,
index,
onChange,
style,
value: {
name,
type,
options
}
});
}
// flowlint-next-line unclear-type:off
const Components = ((_Components: any): Array<React$ComponentType<any>>);
return Components.map((Component, index) => {
return this.renderComponents(Component, `${startIndex}-${index}`, {
name,
type: type[index],
options: {}
});
});
}
// flowlint-next-line unclear-type:off
renderComponent = (Component: React$ComponentType<any>, sub: string, props: ComponentProps): React$Node => {
const { className, index, onChange, style, value: { name, type, options = {} } } = props;
const text = typeToText(type);
const { className, defaultValue, index, name, onChange, style } = this.props;
const type = typeToString(defaultValue.type);
return (
<Component
className={classes('ui--Param', className)}
index={index}
key={`${name}:${text}:${index}}`}
label={`${name}: ${text} (${index})`}
defaultValue={defaultValue}
key={`${name}:${type}:${index}}`}
label={`${name}: ${type} (${index})`}
name={name}
onChange={onChange}
style={style}
value={{
options,
type
}}
/>
);
}
renderUnknown = (): React$Node => {
const { className, index, onChange, style, value: { name, type } = {} } = this.props;
return this.renderComponent(Unknown, '0', {
className,
index,
onChange,
style,
value: {
name,
type
}
});
}
}
export default translate(ParamComponent);

View File

@ -5,7 +5,7 @@
import type { Section$Item } from '@polkadot/params/types';
import type { I18nProps } from '../types';
import type { ComponentMap, RawParam, RawParams } from './types';
import type { ComponentMap, RawParam, RawParams, RawParam$OnChange } from './types';
import './Params.css';
@ -24,40 +24,50 @@ type Props = I18nProps & {
type State = {
item: Section$Item,
handlers: Array<RawParam$OnChange>,
onChangeParam: (at: number, next: RawParam) => void,
values: RawParams
};
class Params extends React.PureComponent<Props, State> {
state: State = ({}: $Shape<State>);
state: State;
static getDerivedStateFromProps ({ item, onChange }: Props, { item: { name, section } = {} }: State): $Shape<State> | null {
constructor (props: Props) {
super(props);
this.state = ({
onChangeParam: this.onChangeParam
}: $Shape<State>);
}
static getDerivedStateFromProps ({ item, onChange }: Props, { item: { name, section } = {}, onChangeParam }: State): $Shape<State> | null {
if (name === item.name && section === item.section) {
return null;
}
const { params = {} } = item;
const values = createValues(params);
const handlers = values.map(
(value, index): RawParam$OnChange =>
(value: RawParam): void =>
onChangeParam(index, value)
);
onChange(values);
return {
item,
values: createValues(params)
handlers,
values
};
}
render (): React$Node {
const { className, item, overrides, style } = this.props;
const { values } = this.state;
if (!values.length) {
return null;
}
const { name, params = {} } = item;
const { className, item: { name, params = {} }, overrides, style } = this.props;
const { handlers, values } = this.state;
const paramNames = Object.keys(params);
if (paramNames.length === 0) {
if (values.length === 0 || paramNames.length === 0) {
return null;
}
@ -67,45 +77,45 @@ class Params extends React.PureComponent<Props, State> {
style={style}
>
<div className='ui--Params-Content'>
{paramNames.map((paramName, index) => {
const { options, type } = params[paramName];
return (
<Param
index={index}
key={`${name}:${paramName}:${index}`}
onChange={this.onChangeParam}
overrides={overrides}
value={{
name: paramName,
options,
type
}}
/>
);
})}
{paramNames.map((paramName, index) => (
<Param
defaultValue={values[index]}
index={index}
key={`${name}:${paramName}:${index}`}
name={paramName}
onChange={handlers[index]}
overrides={overrides}
type={params[paramName].type}
/>
))}
</div>
</div>
);
}
onChangeParam = (at: number, next: RawParam): void => {
onChangeParam = (at: number, { isValid = false, value }: RawParam): void => {
this.setState(
(prevState: State): $Shape<State> => ({
values: prevState.values.map((value, index) =>
index === at
? next
: value
values: prevState.values.map((prev, index) =>
index !== at
? prev
: {
isValid,
type: prev.type,
value
}
)
}),
(): void => {
const { onChange } = this.props;
const { values } = this.state;
onChange(values);
}
this.notifyChange
);
}
notifyChange = (): void => {
const { onChange } = this.props;
const { values } = this.state;
onChange(values);
}
}
export default translate(Params);

View File

@ -2,20 +2,19 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
// flowlint sketchy-null-mixed:off
import type { Param$Type } from '@polkadot/params/types';
import type { Param$Types } from '@polkadot/params/types';
import type { RawParam$Values } from './types';
import BN from 'bn.js';
type Options = {
initValue?: mixed,
minValue?: mixed
};
import typeToString from '@polkadot/params/typeToString';
export default function getInitValue (type: Param$Type, { initValue, minValue }: Options = {}): ?mixed {
export default function getInitValue (type: Param$Types): RawParam$Values {
switch (type) {
case 'Balance':
return new BN(1);
case 'BlockNumber':
case 'Index':
case 'SessionKey':
@ -24,15 +23,43 @@ export default function getInitValue (type: Param$Type, { initValue, minValue }:
case 'ReferendumIndex':
case 'u32':
case 'VoteIndex':
return new BN(initValue || minValue || 0);
return new BN(0);
case 'bool':
return initValue || false;
return false;
case 'String':
return '';
case 'Timestamp':
return new Date(0);
case 'VoteThreshold':
return initValue || 0;
return 0;
case 'AccountId':
case 'Bytes':
case 'Call':
case 'Code':
case 'Digest':
case 'Hash':
case 'Header':
case 'KeyValue':
case 'KeyValueStorage':
case 'MisbehaviorReport':
case 'Proposal':
case 'Signature':
return void 0;
default:
return initValue;
if (Array.isArray(type)) {
return type.map((subtype) =>
getInitValue(subtype)
);
}
// NOTE for @flow, if it fails here, we know what is missing
(type: empty); // eslint-disable-line no-unused-expressions
throw new Error(`Unable to determine default type for ${typeToString(type)}`);
}
}

View File

@ -1,19 +0,0 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Param$Types } from '@polkadot/params/types';
export default function typeToText (type: Param$Types): string {
if (!Array.isArray(type)) {
return type;
}
// $FlowFixMe hate doing this, but it _looks_ ok
const text = type.map(typeToText).join(', ');
return type.length !== 1
? `(${text})`
: `Array<${text}>`;
}

View File

@ -3,33 +3,34 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Param$Type, Param$Types } from '@polkadot/params/types';
import type { Param$Type, Param$Types, Param$Value } from '@polkadot/params/types';
import type { BareProps } from '../types';
export type RawParam$Value = ?Param$Value;
export type RawParam$ValueArray = Array<?Param$Value | RawParam$ValueArray>;
export type RawParam$Values = RawParam$Value | RawParam$ValueArray;
export type RawParam = {
isValid: boolean,
value: mixed,
type: Param$Types,
value: RawParam$Values,
}
export type RawParam$OnChange = (value: $Shape<RawParam>) => void;
export type RawParams = Array<RawParam>;
export type BaseProps = BareProps & {
onChange: (index: number, value: RawParam) => void,
value: {
name: string,
options?: {
initValue?: mixed,
minValue?: mixed,
maxValue?: mixed
},
type: Param$Types
}
defaultValue: RawParam,
name: string,
onChange: RawParam$OnChange
};
export type Props = BaseProps & {
isDisabled?: boolean,
isError?: boolean,
index: number,
label: string,
withLabel?: boolean
};
@ -37,6 +38,5 @@ export type Props = BaseProps & {
export type Size = 'full' | 'large' | 'medium' | 'small';
export type ComponentMap = {
// flowlint-next-line unclear-type:off
[Param$Type]: React$ComponentType<any>
[Param$Type]: React$ComponentType<Props>
};

View File

@ -4,7 +4,7 @@
// @flow
import type BN from 'bn.js';
import type { Param$Types } from '@polkadot/params/types';
import type { Param$Types, Param$Type$Array } from '@polkadot/params/types';
// import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
import numberFormat from '@polkadot/ui-react-rx/util/numberFormat';
@ -14,7 +14,22 @@ import isBn from '@polkadot/util/is/bn';
import isU8a from '@polkadot/util/is/u8a';
// flowlint-next-line unclear-type:off
export default function valueToText (type: Param$Types, value: any): string {
function arrayToText (type: Param$Type$Array, value: Array<any>): string {
if (value.length === 0) {
return 'empty';
}
const values = type.map((_type, index) =>
valueToText(_type, value[index])
).join(', ');
return type.length === 1
? `(${values})`
: values;
}
// flowlint-next-line unclear-type:off
function valueToText (type: Param$Types, value: any): string {
if (type === 'bool') {
return value ? 'Yes' : 'No';
}
@ -24,18 +39,7 @@ export default function valueToText (type: Param$Types, value: any): string {
}
if (Array.isArray(type)) {
if (value.length === 0) {
return 'empty';
}
const values = type.map((_type, index) =>
// $FlowFixMe hate doing this, but it _looks_ ok
valueToText(_type, value[index])
).join(', ');
return type.length === 1
? `(${values})`
: values;
return arrayToText(type, value);
}
if (type === 'AccountId') {
@ -52,3 +56,5 @@ export default function valueToText (type: Param$Types, value: any): string {
return value.toString();
}
export default valueToText;

View File

@ -3,55 +3,56 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Param, Params } from '@polkadot/params/types';
import type { RawParam } from './types';
import type { Params } from '@polkadot/params/types';
import type { RawParam, RawParam$ValueArray } from './types';
import isUndefined from '@polkadot/util/is/undefined';
import getInitValue from './initValue';
export default function values (params: Params): Array<RawParam> {
return Object
.values(params)
// $FlowFixMe yes, we are sure, the type is correct
.map(({ type, options }: Param): RawParam => {
if (Array.isArray(type)) {
// console.error('Unable to determine default values for array/tuple type', type);
//
// return {
// isValid: false,
// value: void 0
// };
const types = Object.keys(params).map((key) => params[key].type);
if (type.length !== 1) {
console.error('Unable to determine default values for tuple type', type);
return types.map((type): RawParam => {
if (Array.isArray(type)) {
// console.error('Unable to determine default values for array/tuple type', type);
//
// return {
// isValid: false,
// value: void 0
// };
return {
isValid: false,
value: void 0
};
}
if (type.length !== 1) {
console.error('Unable to determine default values for tuple type', type);
return type.reduce(({ isValid, value }, type) => {
// $FlowFixMe still not sure how to iterate through these
const avalue = getInitValue(type, {});
value.push(avalue);
return {
isValid: isValid && !isUndefined(avalue),
value
};
}, { isValid: true, value: [] });
return {
isValid: false,
type,
value: void 0
};
}
const value = getInitValue(type, options);
const value: RawParam$ValueArray = [];
return {
isValid: !isUndefined(value),
value
};
});
return type.reduce(({ isValid, type }, subtype) => {
const avalue = getInitValue(subtype);
value.push(avalue);
return {
isValid: isValid && !isUndefined(avalue),
type,
value
};
}, { isValid: true, type, value });
}
const value = getInitValue(type);
return {
isValid: !isUndefined(value),
type,
value
};
});
}
// 0x050000000600000055555555550ae803000000000000

View File

@ -1,3 +1,26 @@
/* Copyright 2017-2018 Jaco Greeff
/* This software may be modified and distributed under the terms
/* of the ISC license. See the LICENSE file for details. */
.ui--output {
background: #f9f9f9;
border-radius: 4px;
border: 1px solid #f5f5f5;
box-sizing: border-box;
line-height: 1em;
margin-bottom: 0.5em;
padding: 0.75em 1em;
position: relative;
word-wrap: break-word;
}
.ui--output .ui--output-button {
position: absolute;
top: 0.25rem;
right: 0.25rem;
}
.ui--output.error {
background: #fff6f6;
border-color: #e0b4b4;
}

View File

@ -76,21 +76,3 @@
.ui--row > div.thirty3 {
padding: 0 0.25rem;
}
.ui--output {
background: #f9f9f9;
border-radius: 4px;
border: 1px solid #f9f9f9;
box-sizing: border-box;
line-height: 1em;
margin-bottom: 0.5em;
padding: 0.75em 1em;
position: relative;
word-wrap: break-word;
}
.ui--output .ui--output-button {
position: absolute;
top: 0.25rem;
right: 0.25rem;
}

View File

@ -2,6 +2,10 @@
/* This software may be modified and distributed under the terms
/* of the ISC license. See the LICENSE file for details. */
.ui.hidden.divider {
margin: 0.75rem 0;
}
.ui.dropdown {
/* from .ui.fluid.dropdown */
display: block;

View File

@ -12,9 +12,9 @@
},
"dependencies": {
"@babel/runtime": "^7.0.0-beta.47",
"@polkadot/ui-react": "^0.12.3",
"@polkadot/util": "^0.20.5",
"@polkadot/util-keyring": "^0.20.5",
"@polkadot/ui-react": "^0.13.4",
"@polkadot/util": "^0.21.3",
"@polkadot/util-keyring": "^0.21.3",
"store": "^2.0.12"
},
"peerDependencies": {

View File

@ -11,7 +11,6 @@ import { accountKey } from '../defaults';
import createOptions from '../options';
export default function forgetAccount (state: State, address: string): void {
// $FlowFixMe oops, types not updated upstream
state.keyring.removePair(address);
store.remove(accountKey(address));

View File

@ -8,17 +8,7 @@ import type { State, KeyringJson } from '../types';
import createItem from './item';
import createHeader from './header';
export default function createOptions (state: State): void {
state.options = {
account: [],
address: [],
all: [],
recent: [],
testing: []
};
const { available, keyring, options } = state;
function addPairs ({ available, keyring }: State): void {
keyring
.getPairs()
.forEach((pair) => {
@ -29,10 +19,12 @@ export default function createOptions (state: State): void {
meta: pair.getMeta()
};
});
}
function addAccounts ({ available, options }: State): void {
Object
.values(available.account)
// $FlowFixMe values() -> mixed, this is an object
.keys(available.account)
.map((address) => available.account[address])
.forEach(({ address, meta: { name, isTesting = false } }: KeyringJson) => {
const option = createItem(address, name);
@ -42,10 +34,12 @@ export default function createOptions (state: State): void {
options.account.push(option);
}
});
}
function addAddresses ({ available, options }: State): void {
Object
.values(available.address)
// $FlowFixMe values() -> mixed, this is an object
.keys(available.address)
.map((address) => available.address[address])
.forEach(({ address, meta: { name, isRecent = false } }: KeyringJson) => {
if (available.account[address]) {
return;
@ -59,6 +53,22 @@ export default function createOptions (state: State): void {
options.address.push(option);
}
});
}
export default function createOptions (state: State): void {
state.options = {
account: [],
address: [],
all: [],
recent: [],
testing: []
};
addPairs(state);
addAccounts(state);
addAddresses(state);
const { options } = state;
options.address = [].concat(
options.address.length ? [ createHeader('Addresses') ] : [],

View File

@ -0,0 +1,15 @@
ISC License (ISC)
Copyright 2017-2018 Jaco Greeff <jacogr@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

View File

@ -0,0 +1 @@
# @polkadot/ui-signer

View File

@ -0,0 +1,24 @@
{
"name": "@polkadot/ui-signer",
"version": "0.6.2",
"main": "index.js",
"repository": "https://github.com/polkadot-js/apps.git",
"author": "Jaco Greeff <jacogr@gmail.com>",
"license": "ISC",
"scripts": {
"build": "polkadot-dev-build-babel",
"check": "stylelint 'src/**/*.css' && eslint src && flow check",
"test": "jest --coverage"
},
"dependencies": {
"@babel/runtime": "^7.0.0-beta.47",
"@polkadot/extrinsics": "^0.15.8",
"@polkadot/ui-app": "^0.6.2",
"@polkadot/ui-keyring": "^0.6.2",
"@polkadot/util": "^0.21.3"
},
"peerDependencies": {
"react": "^16.3.1",
"semantic-ui-react": "^0.80.0"
}
}

View File

@ -0,0 +1,86 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { I18nProps } from '@polkadot/ui-app/types';
import type { QueueTx } from './types';
import BN from 'bn.js';
import React from 'react';
import { Trans } from 'react-i18next';
import extrinsics from '@polkadot/extrinsics-substrate';
import Modal from '@polkadot/ui-app/Modal';
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
import u8aToHexShort from '@polkadot/util/u8a/toHexShort';
import addressEncode from '@polkadot/util-keyring/address/encode';
import translate from './translate';
type Props = I18nProps & {
children?: React$Node,
value: QueueTx
};
function findExtrinsic (sectionId: number, methodId: number): { method: ?string, section: ?string } {
const section = Object.keys(extrinsics).find((section) =>
extrinsics[section].index[0] === sectionId
);
const methods = section
? extrinsics[section].methods.public
: {};
const method = Object.keys(methods).find((method) =>
methods[method].index[1] === methodId
);
return {
method,
section
};
}
function Extrinsic ({ children, className, style, t, value: { nonce = new BN(0), publicKey, values: [_value] } }: Props): React$Node {
const unknown = t('decoded.unknown', {
defaultValue: 'unknown'
});
// flowlint-next-line unclear-type:off
const value = ((_value: any): Uint8Array);
const { method = unknown, section = unknown } = findExtrinsic(value[0], value[1]);
// flowlint-next-line unclear-type:off
const from = addressEncode(((publicKey: any): Uint8Array));
return [
<Modal.Header key='header'>
{t('extrinsic.header', {
defaultValue: 'Extrinsic submission'
})}
</Modal.Header>,
<Modal.Content className='ui--signer-Signer-Content' key='content'>
<div className='ui--signer-Signer-Decoded'>
<div className='expanded'>
<p>
<Trans i18nkey='decoded.short'>
You are about to sign a message from <span className='code'>{from}</span> calling <span className='code'>{section}.{method}</span> with an index of <span className='code'>{nonce.toString()}</span>
</Trans>
</p>
<p>
{t('decoded.data', {
defaultValue: 'The encoded parameters contains the data'
})}
</p>
<p className='code'>
{u8aToHexShort(value, 512)}
</p>
</div>
<IdentityIcon
className='icon'
value={from}
/>
</div>
{children}
</Modal.Content>
];
}
export default translate(Extrinsic);

View File

@ -0,0 +1,245 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { ApiProps } from '@polkadot/ui-react-rx/types';
import type { I18nProps } from '@polkadot/ui-app/types';
import type { QueueTx, QueueTx$MessageSetStatus } from './types';
import React from 'react';
import Button from '@polkadot/ui-app/Button';
import Modal from '@polkadot/ui-app/Modal';
import classes from '@polkadot/ui-app/util/classes';
import keyring from '@polkadot/ui-keyring';
import withApi from '@polkadot/ui-react-rx/with/api';
import Extrinsic from './Extrinsic';
import Unlock from './Unlock';
import signMessage from './sign';
import submitMessage from './submit';
import translate from './translate';
type Props = I18nProps & ApiProps & {
queue: Array<QueueTx>,
queueSetStatus: QueueTx$MessageSetStatus
};
type UnlockI18n = {
key: string,
value: I18Next$Translate$Config
}
type State = {
currentItem?: QueueTx,
password: string,
sendItem: (item: QueueTx) => Promise<void>,
unlockError: UnlockI18n | null
};
class Signer extends React.PureComponent<Props, State> {
state: State;
constructor (props: Props) {
super(props);
this.state = {
password: '',
sendItem: this.sendItem,
unlockError: null
};
}
static getDerivedStateFromProps ({ queue }: Props, { currentItem, password, sendItem, unlockError }: State): $Shape<State> {
const nextItem = queue.find(({ status }) =>
status === 'queued'
);
const isSame =
!!nextItem &&
!!currentItem &&
(
(!nextItem.publicKey && !currentItem.publicKey) ||
(
(nextItem.publicKey && nextItem.publicKey.toString()) === (currentItem.publicKey && currentItem.publicKey.toString())
)
);
if (nextItem && nextItem.rpc.isSigned !== true) {
sendItem(nextItem);
}
return {
currentItem: nextItem,
password: isSame ? password : '',
unlockError: isSame ? unlockError : null
};
}
render (): React$Node {
const { className, style } = this.props;
const { currentItem } = this.state;
if (!currentItem || currentItem.rpc.isSigned !== true) {
return null;
}
return (
<Modal
className={classes('ui--signer-Signer', className)}
dimmer='inverted'
open
style={style}
>
{this.renderContent()}
{this.renderButtons()}
</Modal>
);
}
renderButtons (): React$Node {
const { t } = this.props;
const { currentItem: { rpc: { isSigned = false } = {} } = {} } = this.state;
return (
<Modal.Actions>
<Button.Group>
<Button
isNegative
onClick={this.onCancel}
text={t('extrinsic.cancel', {
defaultValue: 'Cancel'
})}
/>
<Button.Or />
<Button
isPrimary
onClick={this.onSend}
text={
isSigned
? t('extrinsic.signedSend', {
defaultValue: 'Sign and Submit'
})
: t('extrinsic.send', {
defaultValue: 'Submit'
})
}
/>
</Button.Group>
</Modal.Actions>
);
}
renderContent (): React$Node {
const { currentItem } = this.state;
if (!currentItem) {
return null;
}
return (
<Extrinsic value={currentItem}>
{this.renderUnlock()}
</Extrinsic>
);
}
renderUnlock (): React$Node {
const { t } = this.props;
const { currentItem, password, unlockError } = this.state;
if (!currentItem) {
return null;
}
return (
<Unlock
error={unlockError && t(unlockError.key, unlockError.value)}
onChange={this.onChangePassword}
password={password}
value={currentItem.publicKey}
/>
);
}
unlockAccount (publicKey: Uint8Array, password?: string): ?UnlockI18n {
const pair = keyring.getPair(publicKey);
if (pair.hasSecretKey()) {
return null;
}
try {
pair.decodePkcs8(password);
} catch (error) {
return {
key: 'signer.unlock.generic',
value: {
defaultValue: error.message
}
};
}
return null;
}
onChangePassword = (password: string): void => {
this.setState({
password,
unlockError: null
});
}
onCancel = (): void => {
const { queueSetStatus } = this.props;
const { currentItem } = this.state;
// This should never be executed
if (!currentItem) {
return;
}
queueSetStatus(currentItem.id, 'cancelled');
}
onSend = async (): Promise<void> => {
const { currentItem, password } = this.state;
// This should never be executed
if (!currentItem) {
return;
}
this.sendItem(currentItem, password);
};
sendItem = async ({ id, nonce, publicKey, rpc, values }: QueueTx, password?: string): Promise<void> => {
if (rpc.isSigned === true && publicKey) {
const unlockError = this.unlockAccount(publicKey, password);
if (unlockError) {
this.setState({ unlockError });
return;
}
}
const { api, queueSetStatus } = this.props;
queueSetStatus(id, 'sending');
let data = values;
if (rpc.isSigned === true && publicKey) {
// flowlint-next-line unclear-type:off
data = [signMessage(publicKey, nonce, ((data[0]: any): Uint8Array)).data];
}
const { error, result, status } = await submitMessage(api, data, rpc);
queueSetStatus(id, status, result, error);
}
}
export default translate(
withApi(Signer)
);

View File

@ -0,0 +1,83 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from '@polkadot/ui-app/types';
import type { QueueProps, QueueTx$Base, QueueTx$Id, QueueTx$Status } from './types';
import React from 'react';
type Props = BareProps & {
children: React$Node
};
type State = QueueProps;
const { Consumer, Provider } = React.createContext();
let nextId: QueueTx$Id = 0;
export default class Queue extends React.Component<Props, State> {
static Provider = Provider;
static Consumer = Consumer
state: State;
constructor (props: Props) {
super(props);
this.state = {
queue: [],
queueAdd: this.queueAdd,
queueSetStatus: this.queueSetStatus
};
}
render () {
return (
<Provider value={this.state}>
{this.props.children}
</Provider>
);
}
queueSetStatus = (id: QueueTx$Id, status: QueueTx$Status, result?: mixed, error?: Error): void => {
this.setState(
(prevState: State): $Shape<State> => ({
queue: prevState.queue.map((item) =>
item.id === id
? {
...item,
error: error || item.error,
// flowlint-next-line sketchy-null-mixed:off
result: result || item.result,
status
}
: item
)
})
);
if (['cancelled', 'error', 'sent'].includes(status)) {
setTimeout(() => {
this.queueSetStatus(id, 'completed');
}, 5000);
}
}
queueAdd = (value: QueueTx$Base): QueueTx$Id => {
const id: QueueTx$Id = ++nextId;
this.setState(
(prevState: State): $Shape<State> => ({
queue: prevState.queue.concat([{
...value,
id,
status: 'queued'
}])
})
);
return id;
}
}

View File

@ -25,16 +25,16 @@ export default function Status ({ className, queue, style }: Props): React$Node
return (
<div
className={classes('extrinsics--Status', className)}
className={classes('ui--signer-Status', className)}
style={style}
>
{available.map(({ extrinsic: { name, section }, id, status }) =>
{available.map(({ rpc: { name, section }, id, status }) =>
<div
className={classes('extrinsics--Status-Item', status)}
className={classes('ui--signer-Status-Item', status)}
key={id}
>
<div className='header'>
{section}_{name}
{section}.{name}
</div>
<div className='status'>
{status}

View File

@ -12,7 +12,7 @@ import Password from '@polkadot/ui-app/Password';
import classes from '@polkadot/ui-app/util/classes';
import keyring from '@polkadot/ui-keyring';
import translate from '../translate';
import translate from './translate';
type Props = I18nProps & {
error?: string,
@ -52,7 +52,7 @@ class Unlock extends React.PureComponent<Props, State> {
return (
<div
className={classes('extrinsics--Signer-Unlock', className)}
className={classes('ui--signer-Signer-Unlock', className)}
style={style}
>
<div className='ui--row'>

View File

@ -0,0 +1,59 @@
/* Copyright 2017-2018 Jaco Greeff
/* This software may be modified and distributed under the terms
/* of the ISC license. See the LICENSE file for details. */
.ui--signer-Signer {}
.ui--signer-Signer-Decoded,
.ui--signer-Signer-Unlock {
align-content: center;
display: flex;
line-height: 1.5em;
}
.ui--signer-Signer-Decoded {}
.ui--signer-Signer-Unlock {
padding-top: 1.5rem;
}
.ui--signer-Signer-Content {}
.ui--signer-Signer-Content .expanded {
padding-right: 2em;
}
.ui--signer-Signer-Content .expanded .code {
background: #f5f5f5;
font-family: monospace;
margin: 0 0.25em;
overflow-wrap: break-word;
padding: 0.25em 0.5em;
word-break: break-all;
word-break: break-word;
word-wrap: break-word;
}
.ui--signer-Status {
display: inline-block;
position: fixed;
right: 0.25rem;
top: 0.125rem;
z-index: 1001;
}
.ui--signer-Status-Item {
background: #bcdaf0;
margin: 0.125rem;
padding: 0.5rem 1rem;
text-align: center;
}
.ui--signer-Status-Item .status {
font-weight: 700;
}
.ui--signer-Status-Item.error {
background: red;
color: white;
}

View File

@ -0,0 +1,46 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BareProps } from '@polkadot/ui-app/types';
import type { QueueProps } from './types';
import './index.css';
import React from 'react';
import Modal from './Modal';
import Status from './Status';
import Queue from './Queue';
type Props = BareProps & {
children: React$Node
};
function Signer ({ children, className, style }: Props): React$Node {
return (
<Queue>
{children}
<Queue.Consumer>
{({ queue, queueSetStatus }: QueueProps = {}) => [
<Modal
className={className}
key='signer-modal'
queue={queue}
queueSetStatus={queueSetStatus}
style={style}
/>,
<Status
key='signer-status'
queue={queue}
/>
]}
</Queue.Consumer>
</Queue>
);
}
Signer.Queue = Queue;
export default Signer;

View File

@ -0,0 +1,16 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Param$Value, Param$Values } from '@polkadot/params/types';
import type { RawParam } from '@polkadot/ui-app/Params/types';
function rawToValues (raw: Array<RawParam>): Array<Param$Values> {
return raw.map(({ value }) =>
// flowlint-next-line unclear-type:off
((value: any): Param$Value)
);
}
export default rawToValues;

View File

@ -0,0 +1,26 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type BN from 'bn.js';
import type { Signed } from './types';
import encodeCall from '@polkadot/extrinsics-codec/encode/call';
import keyring from '@polkadot/ui-keyring';
import u8aConcat from '@polkadot/util/u8a/concat';
import u8aToHex from '@polkadot/util/u8a/toHex';
export default function signMessage (publicKey: Uint8Array, nonce: BN | number, value: Uint8Array): Signed {
const message = encodeCall(publicKey, nonce, value);
const signature = keyring.getPair(publicKey).sign(message);
console.log(` message :: ${u8aToHex(message)}`);
console.log(`signature :: ${u8aToHex(signature)}`);
return {
data: u8aConcat(message, signature),
message,
signature
};
}

View File

@ -0,0 +1,34 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
// @flow
import type { RxApiInterface } from '@polkadot/api-rx/types';
import type { InterfaceMethodDefinition } from '@polkadot/jsonrpc/types';
import type { Param$Values } from '@polkadot/params/types';
import type { QueueTx$Result } from './types';
export default function submitMessage (api: RxApiInterface, params: Array<Param$Values>, rpc: InterfaceMethodDefinition): Promise<QueueTx$Result> {
const { name, section } = rpc;
// $FlowFixMe This should not be an issue...
return api[section][name]
.apply(null, params)
.toPromise()
.then((result) => {
console.log(`${section}.${name}: result ::`, result);
return {
result,
status: 'sent'
};
})
.catch((error) => {
console.error(error);
return {
error,
status: 'error'
};
});
}

Some files were not shown because too many files have changed in this diff Show More