Expand available extrinsic methods (#7)

* Public/Private extrinsics

* Better dropdown displays

* Expand definition with name

* Pass actual full selected extrinsic around

* Create param layout from parameters

* u32 encoding

* Re-introduce Params component

* Proposal & params display operational

* Parameter-drive method construction

* s/Method/Extrinsic/

* Cleanup locations
This commit is contained in:
Jaco Greeff 2018-04-27 16:15:20 +02:00 committed by GitHub
parent 6ec84ca16c
commit 79dcf5ab54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 1059 additions and 578 deletions

View File

@ -15,7 +15,7 @@ import InputAddress from './InputAddress';
type Props = BaseProps & {
label: string,
subject: rxjs$BehaviorSubject<*>
subject: rxjs$BehaviorSubject<Uint8Array>
};
function Account ({ className, label, subject, style, t }: Props): React$Node {

View File

@ -3,30 +3,33 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BaseProps } from './types';
import type { BaseProps, QueueTx } from './types';
import React from 'react';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import withObservable from '@polkadot/rx-react/with/observable';
import CallDisplay from './CallDisplay';
import CallSelect from './CallSelect';
import Nonce from './Nonce';
import Sender from './Sender';
import Submission from './Submission';
import Signer from './Signer';
import translate from './translate';
type Props = BaseProps & {};
const queue: rxjs$BehaviorSubject<QueueTx> = new BehaviorSubject(({
isValid: false,
status: 'incomplete'
}: $Shape<QueueTx>));
const QueuedSigner = withObservable(queue)(Signer);
function App ({ className, style }: Props): React$Node {
return (
<div
className={['extrinsics--App', className].join(' ')}
style={style}
>
<Sender />
<CallSelect />
<Nonce />
<CallDisplay />
<Signer />
<Submission subject={queue} />
<QueuedSigner subject={queue} />
</div>
);
}

View File

@ -1,18 +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--CallDisplay {
margin-top: 1.5rem;
}
.extrinsics--CallDisplay-error {
color: red;
opacity: 0.75;
}
.extrinsics--CallDisplay-Component {
margin: 1.5rem 0;
}
.extrinsics--CallDisplay-Button {}

View File

@ -1,27 +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 { BaseProps } from '../types';
import './CallDisplay.css';
import React from 'react';
import translate from '../translate';
function ErrorDisplay ({ className, style, t }: BaseProps): React$Node {
return (
<div
className={['extrinsics--CallDisplay-error', className].join(' ')}
style={style}
>
{t('calldisplay.error', {
defaultValue: 'ERROR: Invalid or unimplemented extrinsic function'
})}
</div>
);
}
export default translate(ErrorDisplay);

View File

@ -1,69 +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 { BaseProps } from '../types';
import './CallDisplay.css';
import React from 'react';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import withObservable from '@polkadot/rx-react/with/observable';
import translate from '../translate';
import StakingStake from '../Staking/Stake';
import StakingTransfer from '../Staking/Transfer';
import StakingUnstake from '../Staking/Unstake';
import { extrinsicName } from '../subjects';
import ErrorComponent from './Error';
import queueExtrinsic from './queue';
type ValueGetter = {
getValues: () => Array<mixed>
}
type Props = BaseProps & {
value?: string;
};
const COMPONENTS = {
'staking_stake': StakingStake,
'staking_transfer': StakingTransfer,
'staking_unstake': StakingUnstake
};
function CallDisplay ({ className, style, t, value }: Props): React$Node {
// flowlint-next-line sketchy-null-string:off
if (!value) {
return null;
}
const Component = COMPONENTS[value] || ErrorComponent;
const onSubmit = () => {
// flowlint-next-line unclear-type:off
queueExtrinsic(value, ((Component: any): ValueGetter).getValues());
};
return (
<div
className={['extrinsics--CallDisplay', className].join(' ')}
style={style}
>
<Component className='extrinsics--CallDisplay-Component' />
<Button
className='extrinsics--CallDisplay-Execute'
onClick={onSubmit}
primary
>
{t('calldisplay.submit', {
defaultValue: 'Submit Extrinsic'
})}
</Button>
</div>
);
}
export default translate(
withObservable(extrinsicName)(CallDisplay)
);

View File

@ -1,25 +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 encode from '../encode';
import extrinsics from '../extrinsics';
import { senderAddr, senderIndex, queueTx } from '../subjects';
export default function queue (method: string, values: Array<mixed>): void {
// flowlint-next-line unclear-type:off
const publicKey = ((senderAddr.getValue(): any): Uint8Array);
const message = encode(
extrinsics.get(method),
publicKey,
senderIndex.getValue() || 0,
values
);
queueTx.next({
message,
method,
publicKey
});
}

View File

@ -1,65 +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 { BaseProps } from '../types';
import './CallSelect.css';
import React from 'react';
import Dropdown from 'semantic-ui-react/dist/es/modules/Dropdown';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import translate from '../translate';
import { extrinsicName } from '../subjects';
import extrinsics from '../extrinsics';
type Props = BaseProps & {};
// eslint-disable-next-line no-unused-vars
const onChange = (event: SyntheticEvent<*>, { value }): void => {
extrinsicName.next(value);
};
const options = [];
extrinsics.sections.forEach(({ description, methods, name }) => {
options.push({
disabled: true,
text: description,
value: name
});
methods.forEach(({ description, name }) => {
options.push({
className: 'extrinsics--CallSelect-indent',
text: description,
value: name
});
});
});
function CallSelect ({ className, style, t }: Props): React$Node {
return (
<div
className={['extrinsics--CallSelect', 'extrinsics--split', className].join(' ')}
style={style}
>
<div className='large'>
<Label>
{t('callselect.label', {
defaultValue: 'submit the following extrinsic'
})}
</Label>
<Dropdown
selection
onChange={onChange}
options={options}
/>
</div>
</div>
);
}
export default translate(CallSelect);

View File

@ -0,0 +1,43 @@
// 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, EncodedParams } from '../types';
import React from 'react';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import withObservable from '@polkadot/rx-react/with/observable';
import InputExtrinsic from '../InputExtrinsic';
import Params from '../Params';
import createSubjects from './subjects';
type Props = BareProps & {
isPrivate?: boolean,
label: string,
subject: rxjs$BehaviorSubject<EncodedParams>
};
export default function ExtrinsicMethod ({ className, isPrivate, label, style, subject }: Props): React$Node {
const subjects = createSubjects(subject);
const MethodParams = withObservable(subjects.method)(Params);
return (
<div
className={['extrinsics--Extrinsic', 'extrinsics--split', className].join(' ')}
style={style}
>
<div className='full'>
<Label>{label}</Label>
<InputExtrinsic
isPrivate={isPrivate}
subject={subjects.method}
/>
</div>
<MethodParams
subject={subjects.params}
/>
</div>
);
}

View File

@ -0,0 +1,45 @@
// 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 { Extrinsic } from '../extrinsics/types';
import type { EncodedParams, RawParam } from '../types';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import isUndefined from '@polkadot/util/is/undefined';
import encodeExtrinsic from '../encode/extrinsic';
type Subjects = {
method: rxjs$BehaviorSubject<Extrinsic>,
params: rxjs$BehaviorSubject<Array<RawParam>>
};
export default function subjects (subject: rxjs$BehaviorSubject<EncodedParams>): Subjects {
const subjects = {
method: new BehaviorSubject(({}: $Shape<Extrinsic>)),
params: new BehaviorSubject([])
};
const onChange = (): void => {
const extrinsic = subjects.method.getValue();
const values = subjects.params.getValue();
const isValid = !!extrinsic.params && extrinsic.params.reduce((isValid, param, index) => {
return isValid && !isUndefined(values[index]) && !isUndefined(values[index].value) && values[index].isValid;
}, !!values);
const raw = values.map((param) => param && param.value);
const data = extrinsic.params && encodeExtrinsic(extrinsic, raw);
subject.next({
data,
extrinsic,
isValid
});
};
subjects.method.subscribe(onChange);
subjects.params.subscribe(onChange);
return subjects;
}

View File

@ -3,6 +3,9 @@
/* of the ISC license. See the LICENSE file for details. */
.ui--InputyAddress-PairDisplay {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
position: relative;
white-space: nowrap;
}
@ -19,14 +22,19 @@
}
.ui--InputyAddress-PairDisplay-address {
flex: 1;
font-family: monospace;
margin-left: 1rem;
opacity: 0.5;
position: absolute;
right: 0;
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
}
.ui--InputyAddress-PairDisplay-name {
flex: 1 0;
margin-left: 3rem;
overflow: hidden;
text-transform: uppercase;
text-overflow: ellipsis;
}

View File

@ -3,6 +3,8 @@
// of the ISC license. See the LICENSE file for details.
// @flow
// TODO: This is a useful, shared component, move out
import './InputAddress.css';
import React from 'react';
@ -44,9 +46,9 @@ export default function InputAddress (props: Props): React$Node {
return (
<Dropdown
selection
{...props}
className={['ui--InputAddress', props.className].join(' ')}
selection
options={options}
onChange={onChange}
/>

View File

@ -0,0 +1,40 @@
/* 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--InputExtrinsic {}
.ui--InputExtrinsic-Header {}
.ui--InputExtrinsic .text {
box-sizing: border-box;
display: flex !important;
flex-wrap: nowrap;
justify-content: space-between;
position: relative;
width: 100%;
white-space: nowrap;
}
.ui--InputExtrinsic-Item > .text {
padding-left: 1em;
}
.ui--InputExtrinsic-Item-text,
.ui--InputExtrinsic-Item-call {
display: inline-block;
}
.ui--InputExtrinsic-Item-call {
flex: 1;
font-family: monospace;
margin-left: 1rem;
opacity: 0.5;
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
}
.ui--InputExtrinsic-Item-text {
flex: 1 0;
}

View File

@ -0,0 +1,60 @@
// 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: This is a useful, shared component, move out
import type { Extrinsic } from '../extrinsics/types';
import './InputExtrinsic.css';
import React from 'react';
import Dropdown from 'semantic-ui-react/dist/es/modules/Dropdown';
import extrinsics from '../extrinsics';
import options from './options';
type Props = {
className?: string,
isPrivate?: boolean,
onChange?: (event: SyntheticEvent<*>, value: Extrinsic) => void,
style?: {
[string]: string
},
subject?: rxjs$Subject<Extrinsic>
};
export default function InputExtrinsic (props: Props): React$Node {
// eslint-disable-next-line no-unused-vars
const onChange = (event: SyntheticEvent<*>, { value }): void => {
const extrinsic = extrinsics.get(value);
if (props.subject) {
props.subject.next(extrinsic);
}
if (props.onChange) {
props.onChange(event, extrinsic);
}
};
const _props = Object.assign({}, props);
delete _props.isPrivate;
return (
<Dropdown
selection
{..._props}
className={['ui--InputExtrinsic', props.className].join(' ')}
onChange={onChange}
options={
// flowlint-next-line sketchy-null-bool:off
props.isPrivate
? options.private
: options.public
}
/>
);
}

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 React from 'react';
import extrinsics from '../extrinsics';
const options = {
private: [],
public: []
};
extrinsics.sections.forEach(({ description, hasPrivate, hasPublic, methods, name }) => {
const header = {
className: 'ui--InputExtrinsic-Header',
disabled: true,
text: description,
value: name
};
hasPublic && options.public.push(header);
hasPrivate && options.private.push(header);
methods.forEach(({ description, name, params, isPrivate }) => {
const inputs = params.map(({ name }) => name).join(', ');
options[isPrivate ? 'private' : 'public'].push({
className: 'ui--InputExtrinsic-Item',
text: [
<div className='ui--InputExtrinsic-Item-text' key='name'>
{description}
</div>,
<div className='ui--InputExtrinsic-Item-call' key='call'>
{name}({inputs})
</div>
],
value: name
});
});
});
export default options;

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 type { Props } from './types';
import React from 'react';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import BaseAccount from '../Account';
export default function Account ({ label, subject }: Props): React$Node {
const account: rxjs$BehaviorSubject<Uint8Array> = new BehaviorSubject(new Uint8Array([]));
account.subscribe((value: Uint8Array) => {
subject.next({
isValid: !!(value && value.length),
value
});
});
return (
<BaseAccount
label={label}
subject={account}
/>
);
}

View File

@ -0,0 +1,40 @@
// 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 } from './types';
import BN from 'bn.js';
import React from 'react';
import Input from 'semantic-ui-react/dist/es/elements/Input';
import Base from './Base';
export default function Amount ({ label, subject, t, value: { options: { initValue = 0, maxValue, minValue = 0 } = {} }, type }: Props): React$Node {
// flowlint-next-line sketchy-null-mixed:off, sketchy-null-number:off
const defaultValue = initValue || minValue;
// eslint-disable-next-line no-unused-vars
const onChange = (event: SyntheticEvent<*>, { value }): void => {
subject.next({
isValid: true,
// flowlint-next-line sketchy-null-mixed:off, sketchy-null-number:off
value: new BN(value || minValue || 0)
});
};
return (
<Base
label={label}
size='small'
>
<Input
defaultValue={defaultValue}
max={maxValue}
min={minValue}
onChange={onChange}
type='number'
/>
</Base>
);
}

View File

@ -0,0 +1,31 @@
// 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 React from 'react';
import Label from 'semantic-ui-react/dist/es/elements/Label';
type Props = {
className?: string,
children: React$Node,
label?: string,
size?: 'full' | 'large' | 'medium' | 'small',
style?: {
[string]: string
}
};
export default function Base ({ children, className, label, size = 'medium', style }: Props): React$Node {
return (
<div
className={['extrinsics--split', className].join(' ')}
style={style}
>
<div className={size}>
<Label>{label}</Label>
{children}
</div>
</div>
);
}

View File

@ -0,0 +1,43 @@
// 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 { BaseProps } from './types';
import React from 'react';
import Account from './Account';
import Amount from './Amount';
import Proposal from './Proposal';
import Unknown from './Unknown';
type Props = BaseProps & {
label?: string;
}
const Components = {
'AccountId': Account,
'Balance': Amount,
'BlockNumber': Amount,
'Bytes': Unknown,
'Proposal': Proposal,
'u32': Amount,
'u64': Amount
};
export default function Param ({ className, label, style, subject, value }: Props): React$Node {
// flowlint-next-line sketchy-null-string:off
const defaultLabel = label || `${value.name}: ${value.type}`;
const Component = Components[value.type];
return (
<Component
className={className}
label={defaultLabel}
style={style}
subject={subject}
value={value}
/>
);
}

View File

@ -2,8 +2,11 @@
/* This software may be modified and distributed under the terms
/* of the ISC license. See the LICENSE file for details. */
.extrinsics--CallSelect {}
.extrinsics--Params {}
.extrinsics--CallSelect-indent > span.text {
padding-left: 1.25em;
.extrinsics--Params-Content {
box-sizing: border-box;
padding-left: 2em;
}
.extrinsics--Params-Param {}

View File

@ -0,0 +1,31 @@
// 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 { EncodedParams } from '../types';
import type { Props } from './types';
import React from 'react';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import Extrinsic from '../Extrinsic';
export default function Proposal ({ label, subject }: Props): React$Node {
const extrinsic: rxjs$BehaviorSubject<EncodedParams> = new BehaviorSubject(({ isValid: false }: $Shape<EncodedParams>));
extrinsic.subscribe(({ data, isValid }: EncodedParams) => {
subject.next({
isValid,
value: data
});
});
return (
<Extrinsic
isPrivate
label={label}
subject={extrinsic}
/>
);
}

View File

@ -0,0 +1,35 @@
// 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 } from './types';
import React from 'react';
import translate from '../translate';
import Base from './Base';
type Props = BaseProps & {
t: I18Next$Translate
};
function Unknown ({ label, t, value: { type } }: Props): React$Node {
return (
<Base
size='full'
label={label}
>
<div className='extrinsics--Param-Unknown ui disabled dropdown selection'>
{t('param.unknown', {
defaultValue: `ERROR: Unknown input of type '{{type}}' found. No renderer exists`,
replace: {
type
}
})}
</div>
</Base>
);
}
export default translate(Unknown);

View File

@ -0,0 +1,56 @@
// 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 { Extrinsic } from '../extrinsics/types';
import type { BaseProps, RawParam } from '../types';
import './Params.css';
import React from 'react';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import translate from '../translate';
import Param from './Param';
import createSubscriber from './subscriber';
type Props = BaseProps & {
subject: rxjs$BehaviorSubject<Array<RawParam>>,
value: Extrinsic;
};
function Params ({ className, style, subject, t, value: { name, params } }: Props): React$Node {
if (!params || !params.length) {
return null;
}
const subjects = createSubscriber(params, subject);
return (
<div
className={['extrinsics--Params', 'extrinsics--split', className].join(' ')}
style={style}
>
<div className='full'>
<Label>
{t('params.intro', {
defaultValue: 'with the supplied parameters'
})}
</Label>
<div className='extrinsics--Params-Content'>
{params.map((param, index) => (
<Param
className='extrinsics--Params-Param'
key={`${name}:${param.name}:${param.type}`}
subject={subjects[index]}
value={param}
/>
))}
</div>
</div>
</div>
);
}
export default translate(Params);

View File

@ -0,0 +1,25 @@
// 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 { Extrinsic$Param } from '../extrinsics/types';
import BN from 'bn.js';
export default function getInitValue ({ options: { initValue, minValue } = {}, type }: Extrinsic$Param): mixed {
// flowlint-next-line sketchy-null-mixed:off
const defaultValue = initValue || minValue;
switch (type) {
case 'Balance':
case 'BlockNumber':
case 'u32':
case 'u64':
// flowlint-next-line sketchy-null-mixed:off
return new BN(defaultValue || 0);
default:
return defaultValue;
}
}

View File

@ -0,0 +1,23 @@
// 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 { Extrinsic$Params } from '../extrinsics/types';
import type { RawParam } from '../types';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import isUndefined from '@polkadot/util/is/undefined';
import getInitValue from './initValue';
export default function createSubjects (params: Extrinsic$Params): Array<rxjs$BehaviorSubject<RawParam>> {
return params.map((param) => {
const value = getInitValue(param);
return new BehaviorSubject({
isValid: !isUndefined(value),
value
});
});
}

View File

@ -0,0 +1,23 @@
// 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 { Extrinsic$Params } from '../extrinsics/types';
import type { RawParam } from '../types';
import createSubjects from './subjects';
export default function subscriber (params: Extrinsic$Params, subject: rxjs$BehaviorSubject<Array<RawParam>>): Array<rxjs$BehaviorSubject<RawParam>> {
const subjects = createSubjects(params);
if (subject) {
const onChange = (): void => {
subject.next(subjects.map((s) => s.getValue()));
};
subjects.forEach((s) => s.subscribe(onChange));
}
return subjects;
}

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 { Extrinsic$Param } from '../extrinsics/types';
import type { BareProps, RawParam } from '../types';
export type BaseProps = BareProps & {
subject: rxjs$BehaviorSubject<RawParam>,
value: Extrinsic$Param
};
export type Props = BaseProps & {
label: string
};

View File

@ -1,29 +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 { BaseProps } from './types';
import React from 'react';
import translate from './translate';
import Account from './Account';
import { recipientAddr } from './subjects';
type Props = BaseProps & {};
function Recipient (props: Props): React$Node {
return (
<Account
{...props}
className={['extrinsics--Recipient', props.className].join(' ')}
label={props.t('recipient.label', {
defaultValue: 'to the recipient'
})}
subject={recipientAddr}
/>
);
}
export default translate(Recipient);

View File

@ -1,29 +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 { BaseProps } from './types';
import React from 'react';
import Account from './Account';
import { senderAddr } from './subjects';
import translate from './translate';
type Props = BaseProps & {};
function Sender (props: Props): React$Node {
return (
<Account
{...props}
className={['extrinsics--Sender', props.className].join(' ')}
label={props.t('sender.label', {
defaultValue: 'using the selected account'
})}
subject={senderAddr}
/>
);
}
export default translate(Sender);

View File

@ -18,7 +18,7 @@ type Props = BaseProps & {
value: QueueTx
};
function Extrinsic ({ className, style, t, value: { message, method, publicKey } }: Props): React$Node {
function Decoded ({ className, style, t, value: { data, extrinsic, index, publicKey } }: Props): React$Node {
const from = u8aToHexShort(publicKey);
return (
@ -33,20 +33,20 @@ function Extrinsic ({ className, style, t, value: { message, method, publicKey }
/>
<div className='expanded'>
<p>
<Trans i18nkey='extrinsic.short'>
You are about to sign a message from <span className='code'>{from}</span> calling <span className='code'>{method}</span>
<Trans i18nkey='decoded.short'>
You are about to sign a message from <span className='code'>{from}</span> calling <span className='code'>{extrinsic.name}</span> with an index of <span className='code'>{index.toString()}</span>
</Trans>
</p>
<p>
{t('extrinsic.data', {
defaultValue: 'The encoded message to be signed contains the data'
{t('decoded.data', {
defaultValue: 'The encoded parameters contains the data'
})}
</p>
<p className='code'>{u8aToHex(message)}</p>
<p className='code'>{u8aToHex(data)}</p>
</div>
</div>
</Modal.Content>
);
}
export default translate(Extrinsic);
export default translate(Decoded);

View File

@ -12,27 +12,29 @@ import React from 'react';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import Modal from 'semantic-ui-react/dist/es/modules/Modal';
import withApi from '@polkadot/rx-react/with/api';
import withObservable from '@polkadot/rx-react/with/observable';
import { queueTx } from '../subjects';
import translate from '../translate';
import Extrinsic from './Extrinsic';
import Decoded from './Decoded';
import submitExtrinsic from './submit';
type Props = BaseProps & ApiProps & {
value?: QueueTx
subject: rxjs$BehaviorSubject<QueueTx>,
value: QueueTx
};
function Signer ({ api, className, style, t, value }: Props): React$Node {
if (!value) {
function Signer ({ api, className, subject, style, t, value }: Props): React$Node {
if (value.status !== 'queued') {
return null;
}
const onClose = () => {
queueTx.next();
const onClose = (): void => {
subject.next({
...value,
status: 'cancelled'
});
};
const onSign = () => {
submitExtrinsic(api, value.publicKey, value.message).then(onClose);
const onSign = (): void => {
submitExtrinsic(api, value, subject);
};
return (
@ -47,7 +49,7 @@ function Signer ({ api, className, style, t, value }: Props): React$Node {
defaultValue: 'Sign and submit'
})}
</Modal.Header>
<Extrinsic value={value} />
<Decoded value={value} />
<Modal.Actions>
<Button onClick={onClose}>
{t('signer.cancel', {
@ -68,7 +70,5 @@ function Signer ({ api, className, style, t, value }: Props): React$Node {
}
export default translate(
withApi(
withObservable(queueTx)(Signer)
)
withApi(Signer)
);

View File

@ -4,24 +4,36 @@
// @flow
import type { RxApiInterface } from '@polkadot/rx-api/types';
import type { QueueTx } from '../types';
import encodeCall from '../encode/call';
import keyring from '../keyring';
import sign from './sign';
export default function submit (api: RxApiInterface, publicKey: Uint8Array, message: Uint8Array): Promise<void> {
export default function submit (api: RxApiInterface, tx: QueueTx, subject: rxjs$BehaviorSubject<QueueTx>): Promise<void> {
return api.author
.submitExtrinsic(
sign(
keyring.getPair(publicKey),
message
keyring.getPair(tx.publicKey),
encodeCall(tx.publicKey, tx.index, tx.data)
)
)
.toPromise()
.then((result) => {
console.log('submitExtrinsic: result=', result);
subject.next({
...tx,
status: 'sent'
});
})
.catch((error) => {
console.error('submitExtrinsic: error=', error);
subject.next({
...tx,
status: 'error'
});
});
}

View File

@ -1,8 +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
export default function getValues (): Array<mixed> {
return [];
}

View File

@ -1,26 +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 { BaseProps } from '../../types';
import React from 'react';
import translate from '../../translate';
import getValues from './getValues';
type Props = BaseProps & {};
function StakingStake ({ className, style }: Props): React$Node {
return (
<div
className={['extrinsics--StakingStake', className].join(' ')}
style={style}
/>
);
}
StakingStake.getValues = getValues;
export default translate(StakingStake);

View File

@ -1,48 +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 { BaseProps } from '../../types';
import BN from 'bn.js';
import React from 'react';
import Input from 'semantic-ui-react/dist/es/elements/Input';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import translate from '../../translate';
import { amount } from './subjects';
type Props = BaseProps & {};
// eslint-disable-next-line no-unused-vars
const onChange = (event: SyntheticEvent<*>, { value }): void => {
amount.next(
new BN(value || 0)
);
};
function Amount ({ className, style, t }: Props): React$Node {
return (
<div
className={['extrinsics--split', className].join(' ')}
style={style}
>
<div className='small'>
<Label>
{t('transfer.amount', {
defaultValue: 'sending an amount of'
})}
</Label>
<Input
defaultValue={1}
min={1}
onChange={onChange}
type='number'
/>
</div>
</div>
);
}
export default translate(Amount);

View File

@ -1,14 +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 { recipientAddr } from '../../subjects';
import { amount } from './subjects';
export default function getValues (): Array<mixed> {
return [
recipientAddr.getValue(),
amount.getValue()
];
}

View File

@ -1,31 +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 { BaseProps } from '../../types';
import React from 'react';
import translate from '../../translate';
import Recipient from '../../Recipient';
import Amount from './Amount';
import getValues from './getValues';
type Props = BaseProps & {};
function StakingTransfer ({ className, style }: Props): React$Node {
return (
<div
className={['extrinsics--StakingTransfer', className].join(' ')}
style={style}
>
<Amount />
<Recipient />
</div>
);
}
StakingTransfer.getValues = getValues;
export default translate(StakingTransfer);

View File

@ -1,9 +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 BN from 'bn.js';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
export const amount = new BehaviorSubject(new BN(1));

View File

@ -1,8 +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
export default function getValues (): Array<mixed> {
return [];
}

View File

@ -1,26 +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 { BaseProps } from '../../types';
import React from 'react';
import translate from '../../translate';
import getValues from './getValues';
type Props = BaseProps & {};
function StakingUnstake ({ className, style }: Props): React$Node {
return (
<div
className={['extrinsics--StakingUnstake', className].join(' ')}
style={style}
/>
);
}
StakingUnstake.getValues = getValues;
export default translate(StakingUnstake);

View File

@ -3,40 +3,36 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { BaseProps } from './types';
import type BN from 'bn.js';
import type { BareProps } from '../types';
import React from 'react';
import Label from 'semantic-ui-react/dist/es/elements/Label';
import RxNonce from '@polkadot/rx-react/Nonce';
import withObservableParams from '@polkadot/rx-react/with/observableParams';
import translate from './translate';
import { senderAddr, senderIndex } from './subjects';
type Props = BareProps & {
label: string,
subject: rxjs$BehaviorSubject<BN>,
value: rxjs$BehaviorSubject<Uint8Array>
};
type Props = BaseProps & {};
export default function Nonce ({ className, label, style, subject, t, value }: Props): React$Node {
const SenderNonce = withObservableParams(value)(RxNonce);
const SenderNonce = withObservableParams(senderAddr)(RxNonce);
function Nonce ({ className, style, t }: Props): React$Node {
return (
<div
className={['extrinsics--split', className].join(' ')}
style={style}
>
<div className='small'>
<Label>
{t('nonce.label', {
defaultValue: 'with an index'
})}
</Label>
<Label>{label}</Label>
<SenderNonce
className='ui disabled dropdown selection'
classNameUpdated='hasUpdated'
subject={senderIndex}
subject={subject}
/>
</div>
</div>
);
}
export default translate(Nonce);

View File

@ -0,0 +1,15 @@
/* 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--Submission {}
.extrinsics--Submission-ButtonRow {
margin: 1em 0;
text-align: right;
}
.extrinsics--Submission-ButtonRow .button {
text-align: left;
margin-left: 0.5rem;
}

View File

@ -0,0 +1,39 @@
// 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 { BaseProps, QueueTx } from '../types';
import React from 'react';
import Button from 'semantic-ui-react/dist/es/elements/Button';
import translate from '../translate';
type Props = BaseProps & {
subject: rxjs$BehaviorSubject<QueueTx>,
value: QueueTx;
};
function Submit ({ className, subject, style, t, value }: Props): React$Node {
const onSubmit = (): void => {
subject.next({
...value,
status: 'queued'
});
};
return (
<Button
disabled={!value.isValid}
onClick={onSubmit}
primary
>
{t('submit.label', {
defaultValue: 'Submit Extrinsic'
})}
</Button>
);
}
export default translate(Submit);

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.
// @flow
import type { BaseProps, QueueTx } from '../types';
import './Submission.css';
import React from 'react';
import withObservable from '@polkadot/rx-react/with/observable';
import Account from '../Account';
import Extrinsic from '../Extrinsic';
import translate from '../translate';
import Nonce from './Nonce';
import Submit from './Submit';
import createSubjects from './subjects';
type Props = BaseProps & {
subject: rxjs$BehaviorSubject<QueueTx>
};
function Submission ({ className, subject, style, t }: Props): React$Node {
const subjects = createSubjects();
const Button = withObservable(subjects.call)(Submit);
return (
<div
className={['extrinsics--Submission', className].join(' ')}
style={style}
>
<Account
label={t('display.sender', {
defaultValue: 'using the selected account'
})}
subject={subjects.sender}
/>
<Nonce
label={t('display.nonce', {
defaultValue: 'with an index'
})}
subject={subjects.index}
value={subjects.sender}
/>
<Extrinsic
label={t('display.method', {
defaultValue: 'submit the following extrinsic'
})}
subject={subjects.method}
/>
<div className='extrinsics--Submission-ButtonRow'>
<Button subject={subject} />
</div>
</div>
);
}
export default translate(Submission);

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 { EncodedParams, QueueTx } from '../types';
import BN from 'bn.js';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
type Subjects = {
call: rxjs$BehaviorSubject<QueueTx>,
index: rxjs$BehaviorSubject<BN>,
method: rxjs$BehaviorSubject<EncodedParams>,
sender: rxjs$BehaviorSubject<Uint8Array>
};
export default function createSubjects (): Subjects {
const subjects = {
index: new BehaviorSubject(new BN(0)),
call: new BehaviorSubject(({ isValid: false }: $Shape<QueueTx>)),
method: new BehaviorSubject(({ isValid: false }: $Shape<EncodedParams>)),
sender: new BehaviorSubject(new Uint8Array([]))
};
const onChange = (): void => {
const publicKey = subjects.sender.getValue();
const message = subjects.method.getValue();
const index = subjects.index.getValue();
const isValid = !!(publicKey && publicKey.length && message && message.isValid);
subjects.call.next({
data: message.data,
extrinsic: message.extrinsic,
isValid,
index,
publicKey,
status: 'incomplete'
});
};
subjects.method.subscribe(onChange);
subjects.sender.subscribe(onChange);
return subjects;
}

View File

@ -0,0 +1,19 @@
// 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: Move to API
import type BN from 'bn.js';
const bnToU8a = require('@polkadot/util/bn/toU8a');
const u8aConcat = require('@polkadot/util/u8a/concat');
module.exports = function encodeCall (publicKey: Uint8Array, nonce: number | BN, data: Uint8Array): Uint8Array {
return u8aConcat(
publicKey,
bnToU8a(nonce, 64, true),
data
);
};

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 { Extrinsic } from '../extrinsics/types';
const u8aConcat = require('@polkadot/util/u8a/concat');
const encodeParams = require('./params');
module.exports = function encodeExtrinsic (extrinsic: Extrinsic, values?: Array<mixed>): Uint8Array {
if (!extrinsic) {
return new Uint8Array([]);
}
return u8aConcat(
extrinsic.index,
encodeParams(extrinsic.params, values)
);
};

View File

@ -8,16 +8,13 @@
import type BN from 'bn.js';
import type { Extrinsic } from '../extrinsics/types';
const bnToU8a = require('@polkadot/util/bn/toU8a');
const u8aConcat = require('@polkadot/util/u8a/concat');
const encodeCall = require('./call');
const encodeExtrinsic = require('./extrinsic');
const encodeParams = require('./params');
module.exports = function encode ({ index, params }: Extrinsic, publicKey: Uint8Array, nonce: number | BN, values: Array<mixed>): Uint8Array {
return u8aConcat(
module.exports = function encode (extrinsic: Extrinsic, publicKey: Uint8Array, nonce: number | BN, values?: Array<mixed>): Uint8Array {
return encodeCall(
publicKey,
bnToU8a(nonce, 64, true),
index,
encodeParams(values, params)
nonce,
encodeExtrinsic(extrinsic, values)
);
};

View File

@ -3,20 +3,31 @@
// of the ISC license. See the LICENSE file for details.
// @flow
import type { Extrinsic$Type } from '../extrinsics/types';
import type { Extrinsic$Param } from '../extrinsics/types';
const bnToU8a = require('@polkadot/util/bn/toU8a');
const u8aToU8a = require('@polkadot/util/u8a/toU8a');
// flowlint-next-line unclear-type:off
module.exports = function encodeValue (value: any, type: Extrinsic$Type): Uint8Array {
module.exports = function encodeValue ({ type }: Extrinsic$Param, value: any): Uint8Array {
switch (type) {
case 'AccountId':
return value;
case 'Balance':
case 'BlockNumber':
case 'u64':
return bnToU8a(value, 64, true);
case 'Bytes':
return u8aToU8a(value);
case 'Proposal':
return u8aToU8a(value);
case 'u32':
return bnToU8a(value, 32, true);
default:
throw new Error(`Unable to encode value with type '${type}'`);
}

View File

@ -9,8 +9,8 @@ const u8aConcat = require('@polkadot/util/u8a/concat');
const encodeParam = require('./param');
module.exports = function encodeParams (values: Array<mixed>, params: Extrinsic$Params): Uint8Array {
return u8aConcat.apply(null, values.map((value, index) => {
return encodeParam(value, params[index]);
module.exports = function encodeParams (params: Extrinsic$Params, values?: Array<mixed> = []): Uint8Array {
return u8aConcat.apply(null, params.map((param, index) => {
return encodeParam(param, values[index]);
}));
};

View File

@ -7,5 +7,8 @@ import type { ExtrinsicsBasic } from './types';
module.exports = ({
description: 'Consensus',
methods: {}
methods: {
public: [],
private: []
}
}: ExtrinsicsBasic);

View File

@ -7,5 +7,8 @@ import type { ExtrinsicsBasic } from './types';
module.exports = ({
description: 'Council',
methods: {}
methods: {
public: [],
private: []
}
}: ExtrinsicsBasic);

View File

@ -7,5 +7,8 @@ import type { ExtrinsicsBasic } from './types';
module.exports = ({
description: 'Council Voting',
methods: {}
methods: {
public: [],
private: []
}
}: ExtrinsicsBasic);

View File

@ -7,5 +7,18 @@ import type { ExtrinsicsBasic } from './types';
module.exports = ({
description: 'Democracy',
methods: {}
methods: {
public: [
{
description: 'Propose',
index: 0,
name: 'propose',
params: [
{ name: 'proposal', type: 'Proposal' },
{ name: 'value', type: 'Balance' }
]
}
],
private: []
}
}: ExtrinsicsBasic);

View File

@ -5,8 +5,9 @@
// TODO: Move to API
import type { Extrinsic, Extrinsics, ExtrinsicsBasic, ExtrinsicsMap, ExtrinsicSection } from './types';
import type { Extrinsic, Extrinsics, ExtrinsicBasic, ExtrinsicsBasic, ExtrinsicsMap, ExtrinsicSection } from './types';
const assert = require('@polkadot/util/assert');
const bnToU8a = require('@polkadot/util/bn/toU8a');
const u8aConcat = require('@polkadot/util/u8a/concat');
@ -25,51 +26,57 @@ const map: { [string]: ExtrinsicsBasic } = {
council, // 4
councilVoting // 5
};
const sectionNames = Object.keys(map);
const extrinsics: Extrinsics = ({
sections: []
}: $Shape<Extrinsics>);
const extrinsicsMap: ExtrinsicsMap = {};
const extrinsics: Extrinsics = {
sections: [],
get: (method: string): Extrinsic => {
const extrinsic = extrinsicsMap[method];
sectionNames.reduce((sections: Array<ExtrinsicSection>, sectionName: string, _index) => {
const sectionSource = map[sectionName];
const sectionIndex = bnToU8a(_index, 8);
const methodNames = Object.keys(sectionSource.methods);
assert(extrinsic, `Unable to retrieve extrinsic with name '${method}'`);
if (!methodNames.length) {
return sections;
return extrinsic;
}
};
const sectionNames = Object.keys(map);
function mapMethods (sectionSource: ExtrinsicsBasic, section: ExtrinsicSection, isPrivate: boolean): void {
const methods = sectionSource.methods[isPrivate ? 'private' : 'public'];
methods.forEach(({ description, index, name, params }: ExtrinsicBasic) => {
const method: Extrinsic = {
description,
index: u8aConcat(section.index, bnToU8a(index, 8)),
isPrivate,
name: `${section.name}_${name}`,
params
};
extrinsicsMap[method.name] = method;
section[isPrivate ? 'hasPrivate' : 'hasPublic'] = true;
section.methods.push(method);
});
}
sectionNames.reduce((sections: Array<ExtrinsicSection>, sectionName: string, index: number) => {
const sectionSource = map[sectionName];
const section: ExtrinsicSection = {
description: sectionSource.description,
hasPrivate: false,
hasPublic: false,
index: bnToU8a(index, 8),
methods: [],
name: sectionName
};
methodNames.reduce((methods: Array<Extrinsic>, methodName: string) => {
const methodSource = sectionSource.methods[methodName];
const name = `${sectionName}_${methodName}`;
const index = u8aConcat(sectionIndex, bnToU8a(methodSource.index, 8));
const method: Extrinsic = {
description: methodSource.description,
index,
name,
params: methodSource.params
};
methods.push(method);
extrinsicsMap[name] = method;
return methods;
}, section.methods);
mapMethods(sectionSource, section, true);
mapMethods(sectionSource, section, false);
sections.push(section);
return sections;
}, extrinsics.sections);
extrinsics.get = function get (sectionMethod: string): Extrinsic {
return extrinsicsMap[sectionMethod];
};
module.exports = extrinsics;

View File

@ -7,5 +7,8 @@ import type { ExtrinsicsBasic } from './types';
module.exports = ({
description: 'Session',
methods: {}
methods: {
public: [],
private: []
}
}: ExtrinsicsBasic);

View File

@ -8,20 +8,60 @@ import type { ExtrinsicsBasic } from './types';
module.exports = ({
description: 'Staking',
methods: {
transfer: {
description: 'Transfer',
index: 0,
params: ['AccountId', 'Balance']
},
stake: {
description: 'Stake',
index: 1,
params: []
},
unstake: {
description: 'Unstake',
index: 2,
params: []
}
public: [
{
description: 'Transfer',
index: 0,
name: 'transfer',
params: [
{ name: 'recipient', type: 'AccountId' },
{ name: 'value', type: 'Balance', options: { minValue: 1 } }
]
},
{
description: 'Stake',
index: 1,
name: 'stake',
params: []
},
{
description: 'Unstake',
index: 2,
name: 'unstake',
params: []
}
],
private: [
{
description: 'Set sessions per era',
index: 0,
name: 'setSessionsPerEra',
params: [
{ name: 'sessions', type: 'u64' }
]
},
{
description: 'Set bonding duration',
index: 1,
name: 'setBondingDuration',
params: [
{ name: 'duration', type: 'u64' }
]
},
{
description: 'Set validator count',
index: 2,
name: 'setValidatorCount',
params: [
{ name: 'count', type: 'u32' }
]
},
{
description: 'Force new era',
index: 3,
name: 'forceNewEra',
params: []
}
]
}
}: ExtrinsicsBasic);

View File

@ -3,32 +3,49 @@
// of the ISC license. See the LICENSE file for details.
// @flow
export type Extrinsic$Type = 'AccountId' | 'Balance' | 'BlockNumber';
export type Extrinsic$Type = 'AccountId' | 'Balance' | 'BlockNumber' | 'Bytes' | 'Proposal' | 'u32' | 'u64';
export type Extrinsic$Params = Array<Extrinsic$Type>;
export type Extrinsic$Param = {
name: string,
options?: {
initValue?: mixed,
minValue?: mixed,
maxValue?: mixed
},
type: Extrinsic$Type
};
export type Extrinsic$Params = Array<Extrinsic$Param>;
export type ExtrinsicBasic = {
description: string,
index: number,
isPrivate?: boolean,
name: string,
params: Extrinsic$Params
}
export type ExtrinsicsBasic = {
description: string,
methods: {
[string]: ExtrinsicBasic
private: Array<ExtrinsicBasic>,
public: Array<ExtrinsicBasic>
}
}
export type Extrinsic = {
description: string,
index: Uint8Array,
isPrivate: boolean,
name: string,
params: Extrinsic$Params
};
export type ExtrinsicSection = {
description: string,
hasPrivate: boolean,
hasPublic: boolean,
index: Uint8Array,
methods: Array<Extrinsic>,
name: string
};

View File

@ -21,7 +21,6 @@
.extrinsics--split > div {
box-sizing: border-box;
padding-right: 0.5rem;
}
.extrinsics--split > div.full {
@ -29,6 +28,12 @@
min-width: 0;
}
.extrinsics--split > div.large,
.extrinsics--split > div.medium,
.extrinsics--split > div.small {
padding-right: 0.5rem;
}
.extrinsics--split > div.large {
flex: 0 75%;
min-width: 0;

View File

@ -1,15 +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 { QueueTx } from './types';
import BN from 'bn.js';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
export const extrinsicName: rxjs$BehaviorSubject<?string> = new BehaviorSubject();
export const recipientAddr: rxjs$BehaviorSubject<?Uint8Array> = new BehaviorSubject();
export const queueTx: rxjs$BehaviorSubject<?QueueTx> = new BehaviorSubject();
export const senderAddr: rxjs$BehaviorSubject<?Uint8Array> = new BehaviorSubject();
export const senderIndex: rxjs$BehaviorSubject<BN> = new BehaviorSubject(new BN(0));

View File

@ -3,16 +3,35 @@
// of the ISC license. See the LICENSE file for details.
// @flow
export type BaseProps = {
import type BN from 'bn.js';
import type { Extrinsic } from './extrinsics/types';
export type BareProps = {
className?: string,
style?: {
[string]: string
},
}
};
export type BaseProps = BareProps & {
t: I18Next$Translate
};
export type QueueTx = {
message: Uint8Array,
method: string,
publicKey: Uint8Array
export type RawParam = {
isValid: boolean,
value: mixed,
}
export type EncodedParams = {
data: Uint8Array,
extrinsic: Extrinsic,
isValid: boolean
};
export type QueueTx$Status = 'incomplete' | 'cancelled' | 'error' | 'queued' | 'sent';
export type QueueTx = EncodedParams & {
index: BN,
publicKey: Uint8Array,
status: QueueTx$Status
};