Browse Source

Merge branch 'master' into joystream-update-membership-roles-another-updater

Mokhtar Naamani 5 years ago
parent
commit
2f7ee8c96d
100 changed files with 1916 additions and 1657 deletions
  1. 1 1
      .123trigger
  2. 0 27
      .circleci/config.yml
  3. 1 0
      .eslintignore
  4. 1 1
      .eslintrc.js
  5. 19 2
      .github/workflows/pr-any.yml
  6. 31 0
      .github/workflows/push-master.yml
  7. 89 0
      i18next-scanner.config.js
  8. 1 1
      lerna.json
  9. 19 18
      package.json
  10. 3 3
      packages/app-123code/package.json
  11. 1 1
      packages/app-123code/src/translate.ts
  12. 5 5
      packages/app-accounts/package.json
  13. 1 1
      packages/app-accounts/src/Vanity/translate.ts
  14. 4 7
      packages/app-accounts/src/modals/Create.tsx
  15. 125 191
      packages/app-accounts/src/modals/Transfer.tsx
  16. 1 1
      packages/app-accounts/src/translate.ts
  17. 3 3
      packages/app-address-book/package.json
  18. 1 1
      packages/app-address-book/src/Address.tsx
  19. 2 2
      packages/app-address-book/src/modals/Create.tsx
  20. 1 1
      packages/app-address-book/src/translate.ts
  21. 3 3
      packages/app-claims/package.json
  22. 1 1
      packages/app-claims/src/translate.ts
  23. 4 4
      packages/app-contracts/package.json
  24. 30 7
      packages/app-contracts/src/ABI.tsx
  25. 56 14
      packages/app-contracts/src/Codes/Code.tsx
  26. 2 0
      packages/app-contracts/src/Codes/Upload.tsx
  27. 238 274
      packages/app-contracts/src/Contracts/Call.tsx
  28. 74 105
      packages/app-contracts/src/Contracts/Contract.tsx
  29. 29 50
      packages/app-contracts/src/Contracts/ValidateAddr.tsx
  30. 80 91
      packages/app-contracts/src/Contracts/index.tsx
  31. 54 0
      packages/app-contracts/src/Contracts/util.tsx
  32. 71 36
      packages/app-contracts/src/Deploy.tsx
  33. 0 3
      packages/app-contracts/src/Modal.tsx
  34. 1 7
      packages/app-contracts/src/Params.tsx
  35. 1 1
      packages/app-contracts/src/RemoveABI.tsx
  36. 10 0
      packages/app-contracts/src/constants.ts
  37. 8 2
      packages/app-contracts/src/index.tsx
  38. 3 2
      packages/app-contracts/src/store.ts
  39. 1 1
      packages/app-contracts/src/translate.ts
  40. 2 1
      packages/app-contracts/src/types.ts
  41. 4 4
      packages/app-council/package.json
  42. 7 8
      packages/app-council/src/Overview/index.tsx
  43. 1 1
      packages/app-council/src/translate.ts
  44. 4 4
      packages/app-dashboard/package.json
  45. 1 1
      packages/app-dashboard/src/translate.ts
  46. 4 4
      packages/app-democracy/package.json
  47. 47 52
      packages/app-democracy/src/Overview/Propose.tsx
  48. 2 0
      packages/app-democracy/src/Overview/index.tsx
  49. 0 9
      packages/app-democracy/src/index.tsx
  50. 1 1
      packages/app-democracy/src/translate.ts
  51. 3 3
      packages/app-explorer/package.json
  52. 1 1
      packages/app-explorer/src/NodeInfo/translate.ts
  53. 1 1
      packages/app-explorer/src/translate.ts
  54. 5 5
      packages/app-extrinsics/package.json
  55. 1 1
      packages/app-extrinsics/src/translate.ts
  56. 3 3
      packages/app-generic-asset/package.json
  57. 94 147
      packages/app-generic-asset/src/Transfer.tsx
  58. 1 1
      packages/app-generic-asset/src/translate.ts
  59. 3 3
      packages/app-js/package.json
  60. 4 12
      packages/app-js/src/ActionButtons.tsx
  61. 56 40
      packages/app-js/src/Playground.tsx
  62. 1 1
      packages/app-js/src/translate.ts
  63. 4 4
      packages/app-parachains/package.json
  64. 12 7
      packages/app-parachains/src/Overview/Parachains.tsx
  65. 14 9
      packages/app-parachains/src/Overview/Summary.tsx
  66. 1 1
      packages/app-parachains/src/translate.ts
  67. 4 4
      packages/app-settings/package.json
  68. 14 13
      packages/app-settings/src/md/basics.md
  69. 1 1
      packages/app-settings/src/translate.ts
  70. 5 5
      packages/app-staking/package.json
  71. 1 1
      packages/app-staking/src/Actions/Account/BondExtra.tsx
  72. 2 3
      packages/app-staking/src/Actions/Account/InjectKeys.tsx
  73. 17 4
      packages/app-staking/src/Actions/Account/Nominate.tsx
  74. 13 9
      packages/app-staking/src/Actions/Account/SetSessionKey.tsx
  75. 1 1
      packages/app-staking/src/Actions/Account/Validate.tsx
  76. 14 12
      packages/app-staking/src/Actions/Account/index.tsx
  77. 143 156
      packages/app-staking/src/Overview/Address.tsx
  78. 45 52
      packages/app-staking/src/index.tsx
  79. 1 1
      packages/app-staking/src/translate.ts
  80. 4 4
      packages/app-storage/package.json
  81. 1 1
      packages/app-storage/src/translate.ts
  82. 3 3
      packages/app-sudo/package.json
  83. 1 1
      packages/app-sudo/src/translate.ts
  84. 3 3
      packages/app-toolbox/package.json
  85. 1 1
      packages/app-toolbox/src/Rpc/translate.ts
  86. 126 174
      packages/app-toolbox/src/Verify.tsx
  87. 1 1
      packages/app-toolbox/src/translate.ts
  88. 4 4
      packages/app-transfer/package.json
  89. 4 4
      packages/app-treasury/package.json
  90. 1 1
      packages/app-treasury/src/translate.ts
  91. 2 2
      packages/apps-routing/package.json
  92. 5 5
      packages/apps/package.json
  93. 0 0
      packages/apps/public/locales/en/app-123code.json
  94. 84 0
      packages/apps/public/locales/en/app-accounts.json
  95. 19 0
      packages/apps/public/locales/en/app-address-book.json
  96. 15 0
      packages/apps/public/locales/en/app-claims.json
  97. 61 0
      packages/apps/public/locales/en/app-contracts.json
  98. 34 0
      packages/apps/public/locales/en/app-council.json
  99. 0 0
      packages/apps/public/locales/en/app-dashboard.json
  100. 34 0
      packages/apps/public/locales/en/app-democracy.json

+ 1 - 1
.123trigger

@@ -1 +1 @@
-1
+2

+ 0 - 27
.circleci/config.yml

@@ -1,27 +0,0 @@
-version: 2
-jobs:
-  build:
-    branches:
-      ignore:
-        - gh-pages
-    working_directory: ~/polkadot-js
-    docker:
-      - image: circleci/node:10
-    steps:
-      - checkout
-      - restore_cache:
-          key: dependency-cache-{{ checksum "yarn.lock" }}
-      - run:
-          name: install-apt
-          command: sudo apt-get install libusb-1.0.0
-      - run:
-          name: install-deps
-          command: yarn install --frozen-lockfile
-      - save_cache:
-          key: dependency-cache-{{ checksum "yarn.lock" }}
-          paths:
-            - ./node_modules
-            - ~/.cache/yarn
-      - run:
-          name: build
-          command: yarn polkadot-dev-circleci-build

+ 1 - 0
.eslintignore

@@ -1,3 +1,4 @@
 **/build/*
 **/coverage/*
 **/node_modules/*
+i18next-scanner.config.js

+ 1 - 1
.eslintrc.js

@@ -6,7 +6,7 @@ module.exports = {
   parserOptions: {
     ...base.parserOptions,
     project: [
-      './tsconfig.eslint.json'
+      './tsconfig.json'
     ]
   },
   rules: {

+ 19 - 2
.github/workflows/pr.yml → .github/workflows/pr-any.yml

@@ -1,4 +1,4 @@
-name: CI
+name: PR
 on: [pull_request]
 
 jobs:
@@ -51,4 +51,21 @@ jobs:
     - name: build
       run: |
         yarn install --frozen-lockfile
-        yarn build
+        yarn build:code
+
+  build_i18n:
+    name: Build i18n
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        node-version: [12.x]
+    steps:
+    - uses: actions/checkout@v1
+    - name: Use Node.js ${{ matrix.node-version }}
+      uses: actions/setup-node@v1
+      with:
+        node-version: ${{ matrix.node-version }}
+    - name: build
+      run: |
+        yarn install --frozen-lockfile
+        yarn build:i18n

+ 31 - 0
.github/workflows/push-master.yml

@@ -0,0 +1,31 @@
+name: Master
+on:
+  push:
+    branches:
+      - master
+
+jobs:
+  build_code:
+    name: Build Code
+    if: "! contains(github.event.head_commit.message, '[CI Skip]')"
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        node-version: [12.x]
+    steps:
+    - uses: actions/checkout@v1
+    - name: Use Node.js ${{ matrix.node-version }}
+      uses: actions/setup-node@v1
+      with:
+        node-version: ${{ matrix.node-version }}
+    - name: build
+      env:
+        CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
+        GH_PAGES_SRC: packages/apps/build
+        GH_PAT: ${{ secrets.GH_PAT }}
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+      run: |
+        yarn install --frozen-lockfile
+        yarn polkadot-dev-ghact-build
+        yarn polkadot-dev-ghact-docs

+ 89 - 0
i18next-scanner.config.js

@@ -0,0 +1,89 @@
+const fs = require('fs');
+const path = require('path');
+const typescript = require('typescript');
+
+module.exports = {
+  input: [
+    'packages/*/src/**/*.{ts,tsx}',
+    // Use ! to filter out files or directories
+    '!packages/*/src/**/*.spec.{ts,tsx}',
+    '!packages/*/src/i18n/**',
+    '!**/node_modules/**'
+  ],
+  output: './',
+  options: {
+    debug: true,
+    func: {
+      list: ['t', 'i18next.t', 'i18n.t'],
+      extensions: ['.tsx']
+    },
+    trans: {
+      component: 'Trans'
+    },
+    lngs: ['en'],
+    defaultLng: 'en',
+    ns: [
+      'app-123code',
+      'app-accounts',
+      'app-address-book',
+      'app-claims',
+      'app-contracts',
+      'app-council',
+      'app-dashboard',
+      'app-democracy',
+      'app-explorer',
+      'app-extrinsics',
+      'app-generic-asset',
+      'app-js',
+      'app-parachains',
+      'app-settings',
+      'app-staking',
+      'app-storage',
+      'app-sudo',
+      'app-toolbox',
+      'app-transfer',
+      'app-treasury',
+      'apps',
+      'apps-routing',
+      'react-api',
+      'react-components',
+      'react-params',
+      'react-query',
+      'react-signer',
+      'ui'
+    ],
+    defaultNs: 'ui',
+    resource: {
+      loadPath: 'packages/apps/public/locales/{{lng}}/{{ns}}.json',
+      savePath: 'packages/apps/public/locales/{{lng}}/{{ns}}.json',
+      jsonIndent: 2,
+      lineEnding: '\n'
+    },
+    nsSeparator: false, // namespace separator
+    keySeparator: false // key separator
+  },
+  transform: function transform (file, enc, done) {
+    const { ext } = path.parse(file.path);
+
+    if (ext === '.tsx') {
+      const content = fs.readFileSync(file.path, enc);
+
+      const { outputText } = typescript.transpileModule(content, {
+        compilerOptions: {
+          target: 'es2018'
+        },
+        fileName: path.basename(file.path)
+      });
+
+      const parserHandler = (key, options) => {
+        options.defaultValue = key;
+        options.ns = /packages\/(.*?)\/src/g.exec(file.path)[1];
+        this.parser.set(key, options);
+      };
+
+      this.parser.parseFuncFromString(outputText, parserHandler);
+    }
+
+    done();
+  }
+};

+ 1 - 1
lerna.json

@@ -10,5 +10,5 @@
   "packages": [
     "packages/*"
   ],
-  "version": "0.36.0-beta.65"
+  "version": "0.36.0-beta.101"
 }

+ 19 - 18
package.json

@@ -1,5 +1,5 @@
 {
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "private": true,
   "engines": {
     "node": ">=10.13.0",
@@ -10,22 +10,23 @@
     "packages/*"
   ],
   "resolutions": {
-    "@polkadot/api": "^0.94.0-beta.11",
-    "@polkadot/api-contract": "^0.94.0-beta.11",
-    "@polkadot/keyring": "^1.5.1",
-    "@polkadot/types": "^0.94.0-beta.11",
-    "@polkadot/util": "^1.5.1",
-    "@polkadot/util-crypto": "^1.5.1",
-    "@types/styled-components": "4.1.8",
+    "@polkadot/api": "^0.95.0-beta.36",
+    "@polkadot/api-contract": "^0.95.0-beta.36",
+    "@polkadot/keyring": "^1.6.1",
+    "@polkadot/types": "^0.95.0-beta.36",
+    "@polkadot/util": "^1.6.1",
+    "@polkadot/util-crypto": "^1.6.1",
     "babel-core": "^7.0.0-bridge.0",
-    "rxjs": "^6.4.0",
-    "typescript": "^3.6.3"
+    "typescript": "^3.6.4"
   },
   "scripts": {
     "analyze": "yarn run build && cd packages/apps && yarn run source-map-explorer build/main.*.js",
-    "build": "NODE_ENV=production polkadot-dev-build-ts",
-    "check": "yarn lint",
+    "build": "yarn run build:code && yarn run build:i18n",
+    "build:code": "NODE_ENV=production polkadot-dev-build-ts",
+    "build:i18n": "i18next-scanner --config i18next-scanner.config.js",
+    "docs": "echo \"skipping docs\"",
     "clean": "polkadot-dev-clean-build",
+    "clean:i18n": "rm -rf packages/apps/public/locales/en && mkdir -p packages/apps/public/locales/en",
     "lint": "eslint --ext .js,.jsx,.ts,.tsx . && tsc --noEmit --pretty",
     "postinstall": "polkadot-dev-yarn-only",
     "test": "echo \"skipping tests\"",
@@ -33,14 +34,14 @@
     "start": "cd packages/apps && webpack --config webpack.config.js"
   },
   "devDependencies": {
-    "@babel/core": "^7.6.2",
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/dev-react": "^0.31.1",
-    "@polkadot/ts": "^0.1.73",
-    "autoprefixer": "^9.6.1",
+    "@babel/core": "^7.6.4",
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/dev-react": "^0.32.0-beta.11",
+    "@polkadot/ts": "^0.1.82",
+    "autoprefixer": "^9.6.5",
     "empty": "^0.10.1",
-    "gh-pages": "^2.1.1",
     "html-loader": "^0.5.5",
+    "i18next-scanner": "^2.10.3",
     "markdown-loader": "^5.1.0",
     "postcss": "^7.0.18",
     "postcss-clean": "^1.1.0",

+ 3 - 3
packages/app-123code/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-123code",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "A basic app that shows the ropes on customisation",
   "main": "index.js",
   "scripts": {},
@@ -10,7 +10,7 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101"
   }
 }

+ 1 - 1
packages/app-123code/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['123code', 'ui']);
+export default withTranslation(['app-123code']);

+ 5 - 5
packages/app-accounts/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-accounts",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "main": "index.js",
   "repository": "https://github.com/polkadot-js/apps.git",
   "author": "Jaco Greeff <jacogr@gmail.com>",
@@ -10,14 +10,14 @@
   "contributors": [],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65",
-    "@polkadot/react-qr": "^0.46.0-beta.0",
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101",
+    "@polkadot/react-qr": "^0.46.0-beta.16",
     "@types/file-saver": "^2.0.0",
     "@types/yargs": "^13.0.2",
     "babel-plugin-module-resolver": "^3.1.1",
     "detect-browser": "^4.7.0",
     "file-saver": "^2.0.0",
-    "yargs": "^14.0.0"
+    "yargs": "^14.2.0"
   }
 }

+ 1 - 1
packages/app-accounts/src/Vanity/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['vanitygen', 'ui']);
+export default withTranslation(['app-accounts']);

+ 4 - 7
packages/app-accounts/src/modals/Create.tsx

@@ -142,11 +142,7 @@ function Create ({ className, onClose, onStatusChange, seed: propsSeed, t, type:
       setAddress(generateSeed(null, derivePath, newSeedType, pairType));
     }
   };
-  const _onChangeName = (_name: string): void => {
-    const name = _name.trim();
-
-    setName({ isNameValid: !!name, name });
-  };
+  const _onChangeName = (name: string): void => setName({ isNameValid: !!name.trim(), name });
   const _toggleConfirmation = (): void => setIsConfirmationOpen(!isConfirmationOpen);
 
   const _onCommit = (): void => {
@@ -158,7 +154,7 @@ function Create ({ className, onClose, onStatusChange, seed: propsSeed, t, type:
     const status = { action: 'create' } as ActionStatus;
 
     try {
-      const { json, pair } = keyring.addUri(`${seed}${derivePath}`, password, { name, tags: [] }, pairType);
+      const { json, pair } = keyring.addUri(`${seed}${derivePath}`, password, { name: name.trim(), tags: [] }, pairType);
       const blob = new Blob([JSON.stringify(json)], { type: 'application/json; charset=utf-8' });
       const { address } = pair;
 
@@ -265,11 +261,12 @@ function Create ({ className, onClose, onStatusChange, seed: propsSeed, t, type:
             />
             <Input
               className='full'
-              help={t('You can set a custom derivation path for this account using the following syntax "/<soft-key>//<hard-key>". The "/<soft-key>" and "//<hard-key>" may be repeated and mixed`.')}
+              help={t('You can set a custom derivation path for this account using the following syntax "/<soft-key>//<hard-key>///<password>". The "/<soft-key>" and "//<hard-key>" may be repeated and mixed`. The "///password" is optional and should only occur once.')}
               isError={!!deriveError}
               label={t('secret derivation path')}
               onChange={_onChangeDerive}
               onEnter={_onCommit}
+              placeholder={t('//hard/soft///password')}
               value={derivePath}
             />
             {deriveError && (

+ 125 - 191
packages/app-accounts/src/modals/Transfer.tsx

@@ -4,218 +4,157 @@
 // of the Apache-2.0 license. See the LICENSE file for details.
 
 import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
-import { Index } from '@polkadot/types/interfaces';
-import { ApiProps } from '@polkadot/react-api/types';
 import { DerivedFees } from '@polkadot/api-derive/types';
 import { I18nProps } from '@polkadot/react-components/types';
 
 import BN from 'bn.js';
-import React from 'react';
+import React, { useContext, useEffect, useState } from 'react';
 import styled from 'styled-components';
 import { Button, InputAddress, InputBalance, Modal, TxButton } from '@polkadot/react-components';
 import { Available } from '@polkadot/react-query';
-import Checks, { calcTxLength } from '@polkadot/react-signer/Checks';
-import { withApi, withCalls, withMulti } from '@polkadot/react-api';
-import { ZERO_FEES } from '@polkadot/react-signer/Checks/constants';
-import { bnMax } from '@polkadot/util';
+import Checks from '@polkadot/react-signer/Checks';
+import { ApiContext } from '@polkadot/react-api';
 
 import translate from '../translate';
 
-interface Props extends ApiProps, I18nProps {
+interface Props extends I18nProps {
   balances_fees?: DerivedFees;
   className?: string;
   onClose: () => void;
   recipientId?: string;
   senderId?: string;
-  system_accountNonce?: BN;
-}
-
-interface State {
-  amount: BN;
-  extrinsic: SubmittableExtrinsic | null;
-  hasAvailable: boolean;
-  maxBalance?: BN;
-  recipientId?: string | null;
-  senderId?: string | null;
 }
 
 const ZERO = new BN(0);
 
-class Transfer extends React.PureComponent<Props, State> {
-  public state: State;
-
-  public constructor (props: Props) {
-    super(props);
-
-    this.state = {
-      amount: ZERO,
-      extrinsic: null,
-      hasAvailable: true,
-      maxBalance: ZERO,
-      recipientId: props.recipientId || null,
-      senderId: props.senderId || null
-    };
-  }
-
-  public componentDidUpdate (prevProps: Props, prevState: State): void {
-    const { balances_fees } = this.props;
-    const { extrinsic, recipientId, senderId } = this.state;
-    const hasLengthChanged = ((extrinsic && extrinsic.encodedLength) || 0) !== ((prevState.extrinsic && prevState.extrinsic.encodedLength) || 0);
-
-    if ((recipientId && prevState.recipientId !== recipientId) ||
-      (balances_fees !== prevProps.balances_fees) || (prevState.senderId !== senderId) ||
-      hasLengthChanged
-    ) {
-      this.setMaxBalance().catch((error: Error): void => console.error(error));
-    }
-  }
-
-  public render (): React.ReactNode {
-    const { className, onClose, recipientId: propRecipientId, senderId: propSenderId, t } = this.props;
-    const { extrinsic, hasAvailable, maxBalance, recipientId, senderId } = this.state;
-    const available = <span className='label'>{t('available ')}</span>;
-
-    return (
-      <Modal
-        className='app--accounts-Modal'
-        dimmer='inverted'
-        open
-      >
-        <Modal.Header>{t('Send funds')}</Modal.Header>
-        <Modal.Content>
-          <div className={className}>
-            <InputAddress
-              defaultValue={propSenderId}
-              help={t('The account you will send funds from.')}
-              isDisabled={!!propSenderId}
-              label={t('send from account')}
-              labelExtra={<Available label={available} params={senderId} />}
-              onChange={this.onChangeFrom}
-              type='account'
-            />
-            <InputAddress
-              defaultValue={propRecipientId}
-              help={t('Select a contact or paste the address you want to send funds to.')}
-              isDisabled={!!propRecipientId}
-              label={t('send to address')}
-              labelExtra={<Available label={available} params={recipientId} />}
-              onChange={this.onChangeTo}
-              type='allPlus'
-            />
-            <InputBalance
-              help={t('Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 mili is equivalent to sending 0.001.')}
-              isError={!hasAvailable}
-              label={t('amount')}
-              maxValue={maxBalance}
-              onChange={this.onChangeAmount}
-              withMax
-            />
-            <Checks
-              accountId={senderId}
-              extrinsic={extrinsic}
-              isSendable
-              onChange={this.onChangeFees}
-            />
-          </div>
-        </Modal.Content>
-        <Modal.Actions>
-          <Button.Group>
-            <Button
-              icon='cancel'
-              isNegative
-              label={t('Cancel')}
-              onClick={onClose}
-            />
-            <Button.Or />
-            <TxButton
-              accountId={senderId}
-              extrinsic={extrinsic}
-              icon='send'
-              isDisabled={!hasAvailable}
-              isPrimary
-              label={t('Make Transfer')}
-              onStart={onClose}
-              withSpinner={false}
-            />
-          </Button.Group>
-        </Modal.Actions>
-      </Modal>
-    );
-  }
-
-  private nextState (newState: Partial<State>): void {
-    this.setState((prevState: State): State => {
-      const { api } = this.props;
-      const { amount = prevState.amount, recipientId = prevState.recipientId, hasAvailable = prevState.hasAvailable, maxBalance = prevState.maxBalance, senderId = prevState.senderId } = newState;
-      const extrinsic = recipientId && senderId
-        ? api.tx.balances.transfer(recipientId, amount)
-        : null;
-
-      return {
-        amount,
-        extrinsic,
-        hasAvailable,
-        maxBalance,
-        recipientId,
-        senderId
-      };
-    });
-  }
-
-  private onChangeAmount = (amount: BN = new BN(0)): void => {
-    this.nextState({ amount });
-  }
-
-  private onChangeFrom = (senderId: string | null): void => {
-    this.nextState({ senderId });
-  }
-
-  private onChangeTo = (recipientId: string | null): void => {
-    this.nextState({ recipientId });
-  }
-
-  private onChangeFees = (hasAvailable: boolean): void => {
-    this.setState({ hasAvailable });
-  }
-
-  private setMaxBalance = async (): Promise<void> => {
-    const { api, balances_fees = ZERO_FEES } = this.props;
-    const { senderId, recipientId } = this.state;
-
-    if (!senderId || !recipientId) {
-      return;
-    }
+// TODO Re-enable when we have proper fee calculation (incl. weights)
+// async function calcMax (api: ApiPromise, balances_fees: DerivedFees | undefined, senderId: string, recipientId: string): Promise<BN> {
+//   let maxBalance = new BN(1);
 
-    const { transferFee, transactionBaseFee, transactionByteFee, creationFee } = balances_fees;
-    const accountNonce = await api.query.system.accountNonce<Index>(senderId);
-    const senderBalance = (await api.derive.balances.all(senderId)).availableBalance;
-    const recipientBalance = (await api.derive.balances.all(recipientId)).availableBalance;
+//   if (!balances_fees) {
+//     return maxBalance;
+//   }
 
-    let prevMax = new BN(0);
-    let maxBalance = new BN(1);
-    let extrinsic;
+//   const { transferFee, transactionBaseFee, transactionByteFee, creationFee } = balances_fees;
 
-    while (!prevMax.eq(maxBalance)) {
-      prevMax = maxBalance;
-      extrinsic = api.tx.balances.transfer(recipientId, prevMax);
+//   const [senderNonce, senderBalances, recipientBalances] = await Promise.all([
+//     api.query.system.accountNonce<Index>(senderId),
+//     api.derive.balances.all(senderId),
+//     api.derive.balances.all(recipientId)
+//   ]);
 
-      const txLength = calcTxLength(extrinsic, accountNonce);
-      const fees = transactionBaseFee
-        .add(transactionByteFee.mul(txLength))
-        .add(transferFee)
-        .add(recipientBalance.isZero() ? creationFee : ZERO);
+//   let prevMax = new BN(0);
 
-      maxBalance = bnMax(senderBalance.sub(fees), ZERO);
-    }
+//   // something goes screwy here when we move this out of the component :(
+//   let extrinsic: any;
+
+//   while (!prevMax.eq(maxBalance)) {
+//     prevMax = maxBalance;
+//     extrinsic = api.tx.balances.transfer(senderNonce, prevMax);
 
-    this.nextState({
-      extrinsic,
-      maxBalance
-    });
-  }
+//     const txLength = calcTxLength(extrinsic, senderNonce);
+//     const fees = transactionBaseFee
+//       .add(transactionByteFee.mul(txLength))
+//       .add(transferFee)
+//       .add(recipientBalances.availableBalance.isZero() ? creationFee : ZERO);
+
+//     maxBalance = bnMax(senderBalances.availableBalance.sub(fees), ZERO);
+//   }
+
+//   return maxBalance;
+// }
+
+function Transfer ({ className, onClose, recipientId: propRecipientId, senderId: propSenderId, t }: Props): React.ReactElement<Props> {
+  const { api } = useContext(ApiContext);
+  const [amount, setAmount] = useState<BN | undefined>(new BN(0));
+  const [extrinsic, setExtrinsic] = useState<SubmittableExtrinsic | null>(null);
+  const [hasAvailable, setHasAvailable] = useState(true);
+  const [maxBalance] = useState(new BN(0));
+  const [recipientId, setRecipientId] = useState<string | null>(propRecipientId || null);
+  const [senderId, setSenderId] = useState<string | null>(propSenderId || null);
+
+  useEffect((): void => {
+    if (senderId && recipientId) {
+      setExtrinsic(api.tx.balances.transfer(recipientId, amount || ZERO));
+
+      // We currently have not enabled the max functionality - we don't take care of weights
+      // calcMax(api, balances_fees, senderId, recipientId)
+      //   .then(([maxBalance]): void => setMaxBalance(maxBalance))
+      //   .catch((error: Error): void => console.error(error));
+    }
+  }, [amount, recipientId, senderId]);
+
+  const available = <span className='label'>{t('available ')}</span>;
+
+  return (
+    <Modal
+      className='app--accounts-Modal'
+      dimmer='inverted'
+      open
+    >
+      <Modal.Header>{t('Send funds')}</Modal.Header>
+      <Modal.Content>
+        <div className={className}>
+          <InputAddress
+            defaultValue={propSenderId}
+            help={t('The account you will send funds from.')}
+            isDisabled={!!propSenderId}
+            label={t('send from account')}
+            labelExtra={<Available label={available} params={senderId} />}
+            onChange={setSenderId}
+            type='account'
+          />
+          <InputAddress
+            defaultValue={propRecipientId}
+            help={t('Select a contact or paste the address you want to send funds to.')}
+            isDisabled={!!propRecipientId}
+            label={t('send to address')}
+            labelExtra={<Available label={available} params={recipientId} />}
+            onChange={setRecipientId}
+            type='allPlus'
+          />
+          <InputBalance
+            help={t('Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.')}
+            isError={!hasAvailable}
+            label={t('amount')}
+            maxValue={maxBalance}
+            onChange={setAmount}
+            withMax
+          />
+          <Checks
+            accountId={senderId}
+            extrinsic={extrinsic}
+            isSendable
+            onChange={setHasAvailable}
+          />
+        </div>
+      </Modal.Content>
+      <Modal.Actions>
+        <Button.Group>
+          <Button
+            icon='cancel'
+            isNegative
+            label={t('Cancel')}
+            onClick={onClose}
+          />
+          <Button.Or />
+          <TxButton
+            accountId={senderId}
+            extrinsic={extrinsic}
+            icon='send'
+            isDisabled={!hasAvailable}
+            isPrimary
+            label={t('Make Transfer')}
+            onStart={onClose}
+            withSpinner={false}
+          />
+        </Button.Group>
+      </Modal.Actions>
+    </Modal>
+  );
 }
 
-export default withMulti(
+export default translate(
   styled(Transfer)`
     article.padded {
       box-shadow: none;
@@ -235,10 +174,5 @@ export default withMulti(
     label.with-help {
       flex-basis: 10rem;
     }
-  `,
-  translate,
-  withApi,
-  withCalls<Props>(
-    'derive.balances.fees'
-  )
+  `
 );

+ 1 - 1
packages/app-accounts/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['accounts', 'ui']);
+export default withTranslation(['app-accounts']);

+ 3 - 3
packages/app-address-book/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-address-book",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "main": "index.js",
   "repository": "https://github.com/polkadot-js/apps.git",
   "author": "Jaco Greeff <jacogr@gmail.com>",
@@ -10,7 +10,7 @@
   "contributors": [],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101"
   }
 }

+ 1 - 1
packages/app-address-book/src/Address.tsx

@@ -20,7 +20,7 @@ interface Props extends I18nProps {
   className?: string;
 }
 
-const WITH_BALANCE = { available: true, free: true, total: true };
+const WITH_BALANCE = { available: true, bonded: true, free: true, total: true };
 const WITH_EXTENDED = { nonce: true };
 
 const isEditable = true;

+ 2 - 2
packages/app-address-book/src/modals/Create.tsx

@@ -49,7 +49,7 @@ function Create ({ onClose, onStatusChange, t }: Props): React.ReactElement<Prop
 
     setAddress({ address: address || input, isAddressExisting, isAddressValid });
   };
-  const _onChangeName = (name: string): void => setName({ isNameValid: !!name, name });
+  const _onChangeName = (name: string): void => setName({ isNameValid: !!name.trim(), name });
   const _onCommit = (): void => {
     const status = { action: 'create' } as ActionStatus;
 
@@ -58,7 +58,7 @@ function Create ({ onClose, onStatusChange, t }: Props): React.ReactElement<Prop
     }
 
     try {
-      keyring.saveAddress(address, { name, genesisHash: keyring.genesisHash, tags: [] });
+      keyring.saveAddress(address, { name: name.trim(), genesisHash: keyring.genesisHash, tags: [] });
 
       status.account = address;
       status.status = address ? 'success' : 'error';

+ 1 - 1
packages/app-address-book/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['address-book', 'ui']);
+export default withTranslation(['app-address-book']);

+ 3 - 3
packages/app-claims/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-claims",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "An app for claiming Polkadot tokens",
   "main": "index.js",
   "scripts": {},
@@ -11,7 +11,7 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101"
   }
 }

+ 1 - 1
packages/app-claims/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['sudo', 'ui']);
+export default withTranslation(['app-claims']);

+ 4 - 4
packages/app-contracts/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-contracts",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "Deployment and management of substrate contracts",
   "main": "index.js",
   "scripts": {},
@@ -10,8 +10,8 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/api-contract": "^0.94.0-beta.11",
-    "@polkadot/react-components": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/api-contract": "^0.95.0-beta.36",
+    "@polkadot/react-components": "^0.36.0-beta.101"
   }
 }

+ 30 - 7
packages/app-contracts/src/ABI.tsx

@@ -15,6 +15,7 @@ import translate from './translate';
 interface Props extends I18nProps {
   className?: string;
   contractAbi?: Abi | null;
+  errorText?: string | null;
   help?: React.ReactNode;
   isError?: boolean;
   isDisabled?: boolean;
@@ -24,10 +25,12 @@ interface Props extends I18nProps {
   onRemove?: () => void;
   onRemoved?: () => void;
   onSelect?: () => void;
+  onSelectConstructor?: (constructorIndex?: number) => void;
 }
 
 interface State {
   contractAbi: Abi | null;
+  errorText: string | null;
   isAbiValid: boolean;
   isEmpty: boolean;
   isError: boolean;
@@ -36,6 +39,7 @@ interface State {
 class ABI extends React.PureComponent<Props, State> {
   public state: State = {
     contractAbi: null,
+    errorText: null,
     isAbiValid: false,
     isEmpty: true,
     isError: false
@@ -49,6 +53,7 @@ class ABI extends React.PureComponent<Props, State> {
 
     this.state = {
       contractAbi: contractAbi || null,
+      errorText: null,
       isAbiValid,
       isEmpty: !isAbiValid,
       isError: isError || (isRequired && !isAbiValid) || false
@@ -83,7 +88,7 @@ class ABI extends React.PureComponent<Props, State> {
 
   private renderInputFile (): React.ReactNode {
     const { className, help, isDisabled, isRequired, label, t } = this.props;
-    const { isAbiValid, isEmpty, isError } = this.state;
+    const { isAbiValid, isEmpty, isError, errorText } = this.state;
 
     return (
       <div className={className}>
@@ -95,7 +100,17 @@ class ABI extends React.PureComponent<Props, State> {
           onChange={this.onChange}
           placeholder={
             !isEmpty && !isAbiValid
-              ? t('invalid ABI file selected')
+              ? (
+                <>
+                  {t('invalid ABI file selected')}
+                  {!!errorText && (
+                    <>
+                      {' — '}
+                      {t(errorText)}
+                    </>
+                  )}
+                </>
+              )
               : t('click to select or drag and drop a JSON ABI file')
           }
         />
@@ -104,7 +119,7 @@ class ABI extends React.PureComponent<Props, State> {
   }
 
   private renderMessages (): React.ReactNode {
-    const { help, isDisabled, label, onRemove } = this.props;
+    const { help, isDisabled, label, onRemove, onSelectConstructor } = this.props;
     const { contractAbi } = this.state;
 
     if (!contractAbi) {
@@ -122,17 +137,24 @@ class ABI extends React.PureComponent<Props, State> {
           onRemove={onRemove || this.onRemove}
           isLabelled={!!label}
           isRemovable={!isDisabled}
+          onSelectConstructor={onSelectConstructor}
+          withConstructors
         />
       </Labelled>
     );
   }
 
   private onChange = (u8a: Uint8Array): void => {
-    const { onChange } = this.props;
+    const { onChange, t } = this.props;
     const json = u8aToString(u8a);
-
     try {
-      const contractAbi = new Abi(JSON.parse(json));
+      const abi = JSON.parse(json);
+
+      if (abi.deploy || abi.messages) {
+        throw new Error(t('You are using an ABI with an outdated format. Please generate a new one.'));
+      }
+
+      const contractAbi = new Abi(abi);
 
       this.setState({
         contractAbi,
@@ -146,7 +168,8 @@ class ABI extends React.PureComponent<Props, State> {
       this.setState({
         isAbiValid: false,
         isEmpty: false,
-        isError: true
+        isError: true,
+        errorText: error
       }, (): void => onChange(null, null));
     }
   }

+ 56 - 14
packages/app-contracts/src/Codes/Code.tsx

@@ -19,28 +19,41 @@ import translate from '../translate';
 
 interface Props extends I18nProps, RouteComponentProps<{}> {
   code: CodeStored;
-  showDeploy: (codeHash?: string) => () => void;
+  showDeploy: (codeHash?: string, constructorIndex?: number) => () => void;
 }
 
 interface State {
+  isAbiOpen: boolean;
   isForgetOpen: boolean;
   isRemoveABIOpen: boolean;
 }
 
 const CodeCard = styled(Card)`
   && {
-    min-height: 13rem;
+    max-width: 100%;
+    min-width: 100%;
   }
 `;
 
-class Contract extends React.PureComponent<Props, State> {
+class Code extends React.PureComponent<Props, State> {
   public state: State = {
+    isAbiOpen: false,
     isForgetOpen: false,
     isRemoveABIOpen: false
   };
 
   public render (): React.ReactNode {
-    const { code, code: { contractAbi } } = this.props;
+    const { code, code: { contractAbi }, t } = this.props;
+    const { isAbiOpen } = this.state;
+
+    const abi = (
+      <ABI
+        contractAbi={contractAbi}
+        onChange={this.onChangeABI}
+        onRemove={this.toggleRemoveABI}
+        onSelectConstructor={this.onDeployConstructor}
+      />
+    );
 
     return (
       <CodeCard>
@@ -51,11 +64,18 @@ class Contract extends React.PureComponent<Props, State> {
           isEditable
           withTags
         >
-          <ABI
-            contractAbi={contractAbi}
-            onChange={this.onChangeABI}
-            onRemove={this.toggleRemoveABI}
-          />
+          {contractAbi
+            ? (
+              <details
+                onClick={this.toggleAbi}
+                open={isAbiOpen}
+              >
+                <summary>{t('ABI')}</summary>
+                {abi}
+              </details>
+            )
+            : abi
+          }
         </CodeRow>
       </CodeCard>
     );
@@ -121,6 +141,17 @@ class Contract extends React.PureComponent<Props, State> {
     return modals;
   }
 
+  private toggleAbi = (event: React.MouseEvent): () => void => {
+    return (): void => {
+      event.preventDefault();
+      const { isAbiOpen } = this.state;
+
+      this.setState({
+        isAbiOpen: !isAbiOpen
+      });
+    };
+  }
+
   private toggleForget = (): void => {
     const { isForgetOpen } = this.state;
 
@@ -137,6 +168,12 @@ class Contract extends React.PureComponent<Props, State> {
     });
   }
 
+  private onDeployConstructor = (constructorIndex = 0): void => {
+    const { code: { json: { codeHash } }, showDeploy } = this.props;
+
+    codeHash && showDeploy && showDeploy(codeHash, constructorIndex)();
+  }
+
   private onForget = (): void => {
     const { code: { json: { codeHash } } } = this.props;
 
@@ -153,14 +190,19 @@ class Contract extends React.PureComponent<Props, State> {
     }
   }
 
-  private onChangeABI = async (abi: string | null = null): Promise<void> => {
+  private onChangeABI = (abi: string | null = null): void => {
     const { code: { json: { codeHash } } } = this.props;
 
-    await contracts.saveCode(
-      codeHash,
-      { abi }
+    this.setState(
+      { isAbiOpen: true },
+      (): void => {
+        contracts.saveCode(
+          codeHash,
+          { abi }
+        );
+      }
     );
   }
 }
 
-export default translate(withRouter(Contract));
+export default translate(withRouter(Code));

+ 2 - 0
packages/app-contracts/src/Codes/Upload.tsx

@@ -15,6 +15,7 @@ import { compactAddLength } from '@polkadot/util';
 import ContractModal, { ContractModalProps, ContractModalState } from '../Modal';
 import store from '../store';
 import translate from '../translate';
+import { GAS_LIMIT } from '../constants';
 
 interface Props extends ContractModalProps, ApiProps {}
 
@@ -30,6 +31,7 @@ class Upload extends ContractModal<Props, State> {
 
     this.defaultState = {
       ...this.defaultState,
+      gasLimit: new BN(GAS_LIMIT),
       isWasmValid: false,
       wasm: null
     };

+ 238 - 274
packages/app-contracts/src/Contracts/Call.tsx

@@ -3,309 +3,273 @@
 // of the Apache-2.0 license. See the LICENSE file for details.
 
 import { ApiProps } from '@polkadot/react-api/types';
-import { BareProps, I18nProps } from '@polkadot/react-components/types';
+import { BareProps, CallContract, I18nProps, StringOrNull } from '@polkadot/react-components/types';
+import { QueueProps } from '@polkadot/react-components/Status/types';
+import { ContractExecResult } from '@polkadot/types/interfaces/contracts';
 
 import BN from 'bn.js';
-import React from 'react';
-import { RouteComponentProps } from 'react-router';
-import { withRouter } from 'react-router-dom';
-import { Abi } from '@polkadot/api-contract';
-import { Button, Dropdown, InputAddress, InputBalance, InputNumber, Modal, TxButton, TxComponent } from '@polkadot/react-components';
-import { getContractAbi } from '@polkadot/react-components/util';
+import React, { useState } from 'react';
+import rpc from '@polkadot/jsonrpc';
+import { Button, Dropdown, InputAddress, InputBalance, InputNumber, Modal, Output, TxButton } from '@polkadot/react-components';
+import { QueueConsumer } from '@polkadot/react-components/Status/Context';
 import { withApi, withMulti } from '@polkadot/react-api';
+import { isNull, isUndefined } from '@polkadot/util';
 
-import translate from '../translate';
 import Params from '../Params';
 
-interface Props extends BareProps, I18nProps, ApiProps, RouteComponentProps<{}> {
-  address: string | null;
+import translate from '../translate';
+import { GAS_LIMIT } from '../constants';
+import { findCallMethod, getContractForAddress, getCallMethodOptions, getContractMethodFn } from './util';
+
+interface Props extends BareProps, I18nProps, ApiProps {
+  callContract: CallContract | null;
+  callMethodIndex: number | null;
   isOpen: boolean;
-  method: string | null;
+  onChangeCallContract: (callContract: CallContract) => void;
+  onChangeCallMethodIndex: (callMethodIndex: number) => void;
   onClose: () => void;
 }
 
-interface State {
-  accountId: string | null;
-  address: string | null;
-  contractAbi?: Abi | null;
-  endowment: BN;
-  gasLimit: BN;
-  isAddressValid: boolean;
-  isBusy: boolean;
-  method: string | null;
-  params: any[];
-}
-
-class Call extends TxComponent<Props, State> {
-  public defaultState: State = {
-    address: null,
-    accountId: null,
-    endowment: new BN(0),
-    gasLimit: new BN(0),
-    method: null,
-    isAddressValid: false,
-    isBusy: false,
-    params: []
-  };
-
-  public state: State = this.defaultState;
+function Call (props: Props): React.ReactElement<Props> | null {
+  const { isOpen, callContract, callMethodIndex, onChangeCallContract, onChangeCallMethodIndex, onClose, api, t } = props;
 
-  public static getDerivedStateFromProps ({ address: propsAddress, method: propsMethod, isOpen }: Props, { address, method }: State): Pick<State, never> | null {
-    if (!isOpen) {
-      return {
-        address: null,
-        method: null,
-        contractAbi: null,
-        isAddressValid: false
-      };
-    }
-
-    return {
-      ...(
-        !address
-          ? {
-            address: propsAddress,
-            contractAbi: propsAddress ? getContractAbi(propsAddress) : null,
-            isAddressValid: !!propsAddress
-          }
-          : {}
-      ),
-      ...(
-        !method
-          ? { method: propsMethod }
-          : {}
-      )
-    };
+  if (isNull(callContract) || isNull(callMethodIndex)) {
+    return null;
   }
 
-  public render (): React.ReactNode {
-    const { isOpen, t } = this.props;
-
-    return (
-      <Modal
-        className='app--contracts-Modal'
-        dimmer='inverted'
-        onClose={this.onClose}
-        open={isOpen}
-      >
-        <Modal.Header>
-          {t('Call a contract')}
-        </Modal.Header>
-        <Modal.Content>
-          {this.renderContent()}
-        </Modal.Content>
-        <Modal.Actions>
-          {this.renderButtons()}
-        </Modal.Actions>
-      </Modal>
-    );
-  }
-
-  public renderContent (): React.ReactNode {
-    const { t } = this.props;
-    const { gasLimit } = this.state;
-
-    const [address, contractAbi, method] = this.getCallProps();
-    const isEndowValid = true;
-    const isGasValid = !gasLimit.isZero();
+  const hasRpc = api.rpc.contracts && api.rpc.contracts.call;
+  const callMethod = findCallMethod(callContract, callMethodIndex);
+  const useRpc = hasRpc && callMethod && !callMethod.mutates;
+  // const isRpc = false;
 
-    if (!address || !contractAbi) {
-      return null;
-    }
-
-    const methodOptions = contractAbi
-      ? Object.keys(contractAbi.messages).map((key): { key: string; text: string; value: string } => {
-        const fn = contractAbi.messages[key];
-        const type = fn.type ? `: ${fn.type}` : '';
-        const args = fn.args.map(({ name, type }): string => `${name}: ${type}`);
-        const text = `${key}(${args.join(', ')})${type}`;
-
-        return {
-          key,
-          text,
-          value: key
-        };
-      })
-      : [];
-
-    return (
-      <div className='contracts--Call'>
-        <InputAddress
-          help={t('Specify the user account to use for this contract call. And fees will be deducted from this account.')}
-          label={t('call from account')}
-          onChange={this.onChangeAccount}
-          type='account'
-        />
-        <InputAddress
-          help={t('A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.')}
-          label={t('contract to use')}
-          onChange={this.onChangeAddress}
-          type='contract'
-          value={address}
-        />
-        <Dropdown
-          defaultValue={method}
-          help={t('The message to send to this contract. Parameters are adjusted based on the ABI provided.')}
-          isError={!method}
-          label={t('message to send')}
-          onChange={this.onChangeMethod}
-          options={methodOptions}
-          style={{ fontFamily: 'monospace' }}
-          value={method}
-        />
-        <Params
-          onChange={this.onChangeParams}
-          onEnter={this.sendTx}
-          params={
-            method && contractAbi && contractAbi.messages[method]
-              ? contractAbi.messages[method].args
-              : undefined
-          }
-        />
-        <InputBalance
-          help={t('The allotted value for this contract, i.e. the amount transferred to the contract as part of this call.')}
-          isError={!isEndowValid}
-          label={t('value')}
-          onChange={this.onChangeEndowment}
-        />
-        <InputNumber
-          help={t('The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.')}
-          isError={!isGasValid}
-          label={t('maximum gas allowed')}
-          onChange={this.onChangeGas}
-          onEnter={this.sendTx}
-        />
-      </div>
-    );
-  }
+  const [accountId, setAccountId] = useState<StringOrNull>(null);
+  const [endowment, setEndowment] = useState<BN>(new BN(0));
+  const [gasLimit, setGasLimit] = useState<BN>(new BN(GAS_LIMIT));
+  const [isBusy, setIsBusy] = useState(false);
+  const [params, setParams] = useState<any[]>([]);
 
-  private renderButtons (): React.ReactNode {
-    const { api, t } = this.props;
-    const { accountId, gasLimit, isAddressValid } = this.state;
-    const isEndowValid = true; // !endowment.isZero();
-    const isGasValid = !gasLimit.isZero();
-    const isValid = !!accountId && isEndowValid && isGasValid && isAddressValid;
+  const _onChangeAccountId = (accountId: StringOrNull): void => setAccountId(accountId);
 
-    return (
-      <Button.Group>
-        <Button
-          icon='cancel'
-          isNegative
-          onClick={this.onClose}
-          label={t('Cancel')}
-        />
-        <Button.Or />
-        <TxButton
-          accountId={accountId}
-          icon='sign-in'
-          isDisabled={!isValid}
-          isPrimary
-          label={t('Call')}
-          onClick={this.toggleBusy}
-          onFailed={this.toggleBusy}
-          onSuccess={this.toggleBusy}
-          params={this.constructCall}
-          tx={api.tx.contracts ? 'contracts.call' : 'contract.call'}
-          ref={this.button}
-        />
-      </Button.Group>
-    );
-  }
-
-  private getCallProps = (): [string | null, Abi | null, string | null] => {
-    let address;
-    let contractAbi;
-    let method;
+  const _onChangeCallAddress = (callAddress: StringOrNull): void => {
+    const callContract = getContractForAddress(callAddress);
 
-    if (!this.state.address) {
-      return [null, null, null];
-    } else {
-      address = this.state.address;
-      contractAbi = this.state.contractAbi || getContractAbi(address);
-      method = contractAbi && this.state.method && contractAbi.messages[this.state.method]
-        ? this.state.method
-        : (
-          contractAbi
-            ? Object.keys(contractAbi.messages)[0]
-            : null
-        );
-    }
+    onChangeCallContract && callContract.abi && onChangeCallContract(callContract);
+  };
 
-    return [
-      address || null,
-      contractAbi || null,
-      method || null
-    ];
-  }
+  const _onChangeCallMethodString = (callMethodString: string): void => {
+    setParams([]);
+    onChangeCallMethodIndex && onChangeCallMethodIndex(parseInt(callMethodString, 10) || 0);
+  };
 
-  private constructCall = (): any[] => {
-    const {
-      endowment, gasLimit, params
-    } = this.state;
+  const _onChangeEndowment = (endowment?: BN): void => endowment && setEndowment(endowment);
+  const _onChangeGasLimit = (gasLimit?: BN): void => gasLimit && setGasLimit(gasLimit);
 
-    const [address, contractAbi, method] = this.getCallProps();
+  const _onChangeParams = (params: any[]): void => setParams(params);
+  const _toggleBusy = (): void => setIsBusy(!isBusy);
 
-    if (!contractAbi || !method) {
+  const _constructTx = (): any[] => {
+    const fn = getContractMethodFn(callContract, callMethod);
+    if (!fn || !callContract || !callContract.address) {
       return [];
     }
 
-    return [address, endowment, gasLimit, contractAbi.messages[method](...params)];
-  }
-
-  private onChangeAccount = (accountId: string | null): void => {
-    this.setState({ accountId });
-  }
-
-  private onChangeAddress = (address: string | null): void => {
-    const contractAbi = getContractAbi(address);
-
-    this.setState({ address, contractAbi, isAddressValid: !!contractAbi });
-  }
-
-  private onChangeEndowment = (endowment?: BN | null): void => {
-    this.setState({ endowment: endowment || new BN(0) });
-  }
-
-  private onChangeGas = (gasLimit: BN | undefined): void => {
-    this.setState({ gasLimit: gasLimit || new BN(0) });
-  }
-
-  private onChangeMethod = (method: string | null): void => {
-    this.setState({ method, params: [] });
-  }
-
-  private onChangeParams = (params: any[]): void => {
-    this.setState({ params });
-  }
-
-  private toggleBusy = (): void => {
-    this.setState(({ isBusy }): Pick<State, never> => ({
-      isBusy: !isBusy
-    }));
-  }
+    return [callContract.address, endowment, gasLimit, fn(...params)];
+  };
 
-  private reset = (): void => {
-    this.setState((state: State): Pick<State, never> => {
-      if (!state.isBusy) {
-        return {
-          ...state,
-          ...this.defaultState
-        };
+  const _constructRpc = (): [any] | null => {
+    const fn = getContractMethodFn(callContract, callMethod);
+    if (!fn || !accountId || !callContract || !callContract.address || !callContract.abi || !callMethod) {
+      return null;
+    }
+    return [
+      {
+        origin: accountId,
+        dest: callContract.address,
+        value: endowment,
+        gasLimit,
+        inputData: fn(...params)
       }
+    ];
+  };
 
-      return {};
-    });
-  }
-
-  private onClose = (): void => {
-    const { onClose } = this.props;
-
-    this.reset();
-    onClose && onClose();
-  }
+  const isEndowmentValid = true;
+  const isGasValid = !gasLimit.isZero();
+  const isValid = !!accountId && isEndowmentValid && isGasValid && callContract && callContract.address && callContract.abi;
+
+  return (
+    <Modal
+      className='app--contracts-Modal'
+      dimmer='inverted'
+      onClose={onClose}
+      open={isOpen}
+    >
+      <Modal.Header>
+        {t('Call a contract')}
+      </Modal.Header>
+      <Modal.Content>
+        {callContract && (
+          <div className='contracts--CallControls'>
+            <InputAddress
+              defaultValue={accountId}
+              help={t('Specify the user account to use for this contract call. And fees will be deducted from this account.')}
+              isDisabled={isBusy}
+              label={t('call from account')}
+              onChange={_onChangeAccountId}
+              type='account'
+              value={accountId}
+            />
+            <InputAddress
+              help={t('A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.')}
+              isDisabled={isBusy}
+              label={t('contract to use')}
+              onChange={_onChangeCallAddress}
+              type='contract'
+              value={callContract.address}
+            />
+            {callMethodIndex !== null && (
+              <>
+                <Dropdown
+                  help={t('The message to send to this contract. Parameters are adjusted based on the ABI provided.')}
+                  isDisabled={isBusy}
+                  isError={callMethod === null}
+                  label={t('message to send')}
+                  onChange={_onChangeCallMethodString}
+                  options={getCallMethodOptions(callContract)}
+                  value={`${callMethodIndex}`}
+                />
+                <Params
+                  isDisabled={isBusy}
+                  onChange={_onChangeParams}
+                  params={
+                    callMethod
+                      ? callMethod.args
+                      : undefined
+                  }
+                />
+              </>
+            )}
+            <InputBalance
+              help={t('The allotted value for this contract, i.e. the amount transferred to the contract as part of this call.')}
+              isDisabled={isBusy}
+              isError={!isEndowmentValid}
+              label={t('value')}
+              onChange={_onChangeEndowment}
+              value={endowment}
+            />
+            <InputNumber
+              defaultValue={gasLimit}
+              help={t('The maximum amount of gas that can be used by this call. If the code requires more, the call will fail.')}
+              isDisabled={isBusy}
+              isError={!isGasValid}
+              label={t('maximum gas allowed')}
+              onChange={_onChangeGasLimit}
+              value={gasLimit}
+            />
+          </div>
+        )}
+        <QueueConsumer>
+          {
+            ({ queueRpc, txqueue }: QueueProps): React.ReactNode => {
+              const _onSubmitRpc = (): void => {
+                const values = _constructRpc();
+
+                if (values) {
+                  queueRpc({
+                    accountId,
+                    rpc: rpc.contracts.methods.call,
+                    values
+                  });
+                }
+              };
+
+              const results = txqueue
+                .filter(({ error, result, rpc, values }): boolean =>
+                  ((!isUndefined(error) || !isUndefined(result)) &&
+                  rpc.section === 'contracts' && rpc.method === 'call' && !!values && values[0].dest === callContract.address)
+                )
+                .reverse();
+
+              return (
+                <>
+                  <Button.Group>
+                    <Button
+                      icon='cancel'
+                      isNegative
+                      onClick={onClose}
+                      label={t('Cancel')}
+                    />
+                    <Button.Or />
+                    {useRpc
+                      ? (
+                        <Button
+                          icon='sign-in'
+                          isDisabled={!isValid}
+                          isPrimary
+                          label={t('Call')}
+                          onClick={_onSubmitRpc}
+                        />
+                      )
+                      : (
+                        <TxButton
+                          accountId={accountId}
+                          icon='sign-in'
+                          isDisabled={!isValid}
+                          isPrimary
+                          label={t('Call')}
+                          onClick={_toggleBusy}
+                          onFailed={_toggleBusy}
+                          onSuccess={_toggleBusy}
+                          params={_constructTx}
+                          tx={api.tx.contracts ? 'contracts.call' : 'contract.call'}
+                        />
+                      )
+                    }
+                  </Button.Group>
+                  {results.length > 0 && (
+                    <>
+                      <h3>{t('Call results')}</h3>
+                      <div>
+                        {
+                          results.map(
+                            (tx, index): React.ReactNode => {
+                              let output: string;
+                              const contractExecResult = tx.result as ContractExecResult;
+                              if (contractExecResult.isSuccess) {
+                                const { data } = contractExecResult.asSuccess;
+                                output = data.toHex();
+                              } else {
+                                output = 'Error';
+                              }
+
+                              return (
+                                <Output
+                                  isError={contractExecResult.isError}
+                                  key={`result-${tx.id}`}
+                                  label={t(`#${results.length - 1 - index}`)}
+                                  style={{ fontFamily: 'monospace' }}
+                                  value={output}
+                                  withCopy
+                                  withLabel
+                                />
+                              );
+                            }
+                          )
+                        }
+                      </div>
+                    </>
+                  )}
+                </>
+              );
+            }
+          }
+        </QueueConsumer>
+      </Modal.Content>
+    </Modal>
+  );
 }
 
 export default withMulti(
-  translate(withRouter(Call)),
+  Call,
+  translate,
   withApi
 );

+ 74 - 105
packages/app-contracts/src/Contracts/Contract.tsx

@@ -3,105 +3,41 @@
 // of the Apache-2.0 license. See the LICENSE file for details.
 
 import { ActionStatus } from '@polkadot/react-components/Status/types';
-import { I18nProps } from '@polkadot/react-components/types';
+import { CallContract, I18nProps } from '@polkadot/react-components/types';
 
-import React from 'react';
+import React, { useState } from 'react';
+import styled from 'styled-components';
 import { RouteComponentProps } from 'react-router';
 import { withRouter } from 'react-router-dom';
 import keyring from '@polkadot/ui-keyring';
 import { AddressRow, Button, Card, Forget, Messages } from '@polkadot/react-components';
-import { getContractAbi } from '@polkadot/react-components/util';
 
 import translate from '../translate';
 
 interface Props extends I18nProps, RouteComponentProps {
   basePath: string;
-  address: string;
-  onCall: (callAddress?: string, callMethod?: string) => void;
+  contract: CallContract;
+  onCall: (_: CallContract) => (_?: number) => () => void;
 }
 
-interface State {
-  isBackupOpen: boolean;
-  isForgetOpen: boolean;
-  isPasswordOpen: boolean;
-}
-
-class Contract extends React.PureComponent<Props, State> {
-  public state: State = {
-    isBackupOpen: false,
-    isForgetOpen: false,
-    isPasswordOpen: false
-  };
-
-  public render (): React.ReactNode {
-    const { address, onCall } = this.props;
-
-    const contractAbi = getContractAbi(address);
-
-    if (!contractAbi) {
-      return null;
-    }
-
-    return (
-      <Card>
-        {this.renderModals()}
-        <AddressRow
-          buttons={this.renderButtons()}
-          isContract
-          isEditable
-          type='contract'
-          value={address}
-          withBalance={false}
-          withNonce={false}
-          withTags
-        >
-          <Messages
-            address={address}
-            contractAbi={contractAbi}
-            isRemovable={false}
-            onSelect={onCall}
-          />
-        </AddressRow>
-      </Card>
-    );
-  }
-
-  private renderModals (): React.ReactNode {
-    const { address } = this.props;
-    const { isForgetOpen } = this.state;
-
-    if (!address) {
-      return null;
-    }
-
-    const modals = [];
-
-    if (isForgetOpen) {
-      modals.push(
-        <Forget
-          address={address}
-          mode='contract'
-          onForget={this.onForget}
-          key='modal-forget-contract'
-          onClose={this.toggleForget}
-        />
-      );
-    }
-
-    return modals;
+const ContractCard = styled(Card)`
+  && {
+    min-width: 100%;
+    max-width: 100%;
   }
+`;
 
-  private toggleForget = (): void => {
-    const { isForgetOpen } = this.state;
+function Contract (props: Props): React.ReactElement<Props> | null {
+  const { contract, contract: { abi, address }, onCall, t } = props;
 
-    this.setState({
-      isForgetOpen: !isForgetOpen
-    });
+  if (!address || !abi) {
+    return null;
   }
 
-  private onForget = (): void => {
-    const { address, t } = this.props;
+  const [isForgetOpen, setIsForgetOpen] = useState(false);
 
+  const _toggleForget = (): void => setIsForgetOpen(!isForgetOpen);
+  const _onForget = (): void => {
     if (!address) {
       return;
     }
@@ -119,32 +55,65 @@ class Contract extends React.PureComponent<Props, State> {
       status.status = 'error';
       status.message = error.message;
     }
-    this.toggleForget();
-  }
+    _toggleForget();
+  };
 
-  private renderButtons (): React.ReactNode {
-    const { address, onCall, t } = this.props;
+  const _onCallMessage = onCall(contract);
+  const _onCall = _onCallMessage();
 
-    return (
-      <div className='contracts--Contract-buttons'>
-        <Button
-          icon='trash'
-          isNegative
-          onClick={this.toggleForget}
-          size='small'
-          tooltip={t('Forget this contract')}
-        />
-        <Button
-          icon='play'
-          isPrimary
-          label={t('execute')}
-          onClick={(): void => onCall(address)}
-          size='small'
-          tooltip={t('Call a method on this contract')}
-        />
-      </div>
-    );
-  }
+  return (
+    <ContractCard>
+      {
+        isForgetOpen && (
+          <Forget
+            address={address}
+            mode='contract'
+            onForget={_onForget}
+            key='modal-forget-contract'
+            onClose={_toggleForget}
+          />
+        )
+      }
+      <AddressRow
+        buttons={
+          <div className='contracts--Contract-buttons'>
+            <Button
+              icon='trash'
+              isNegative
+              onClick={_toggleForget}
+              size='small'
+              tooltip={t('Forget this contract')}
+            />
+            <Button
+              icon='play'
+              isPrimary
+              label={t('execute')}
+              onClick={_onCall}
+              size='small'
+              tooltip={t('Call a method on this contract')}
+            />
+          </div>
+        }
+        isContract
+        isEditable
+        type='contract'
+        value={address}
+        withBalance={false}
+        withNonce={false}
+        withTags
+      >
+        <details>
+          <summary>{t('Messages')}</summary>
+          <Messages
+            address={address}
+            contractAbi={abi}
+            isRemovable={false}
+            onSelect={_onCallMessage}
+          />
+        </details>
+      </AddressRow>
+    </ContractCard>
+  );
 }
 
 export default translate(withRouter(Contract));

+ 29 - 50
packages/app-contracts/src/Contracts/ValidateAddr.tsx

@@ -7,7 +7,7 @@ import { ContractInfo } from '@polkadot/types/interfaces';
 import { I18nProps } from '@polkadot/react-components/types';
 import { ApiProps } from '@polkadot/react-api/types';
 
-import React from 'react';
+import React, { useEffect, useState } from 'react';
 import { Option } from '@polkadot/types';
 import { withCalls } from '@polkadot/react-api';
 import { InfoForInput } from '@polkadot/react-components';
@@ -21,68 +21,47 @@ interface Props extends ApiProps, I18nProps {
   onChange: (isValid: boolean) => void;
 }
 
-interface State {
-  isStored: boolean;
-  isValidAddr: boolean;
-  isValid: boolean;
-}
-
-class ValidateAddr extends React.PureComponent<Props, State> {
-  public state: State = {
-    isStored: false,
-    isValidAddr: false,
-    isValid: false
-  };
-
-  public static getDerivedStateFromProps ({ address, contracts_contractInfoOf, onChange }: Props): State {
-    let isValidAddr = false;
+function ValidateAddr ({ address, contracts_contractInfoOf, onChange, t }: Props): React.ReactElement<Props> | null {
+  const [isAddress, setIsAddress] = useState(false);
+  const [isStored, setIsStored] = useState(false);
 
+  useEffect((): void => {
     try {
       keyring.decodeAddress(address || '');
-
-      isValidAddr = true;
+      setIsAddress(true);
     } catch (error) {
-      // ignore
+      setIsAddress(false);
     }
+  }, [address]);
 
-    const isStored = (
-      (!!contracts_contractInfoOf && contracts_contractInfoOf.isSome)
-      // (!!contract_codeHashOf && contract_codeHashOf.isSome)
-    );
-    const isValid = isValidAddr && isStored;
+  useEffect((): void => {
+    setIsStored(!!contracts_contractInfoOf && contracts_contractInfoOf.isSome);
+  }, [contracts_contractInfoOf]);
 
-    // FIXME Really not convinced this is the correct place to do this type of callback?
-    onChange(isValid);
+  useEffect((): void => {
+    onChange(isAddress && isStored);
+  }, [isAddress, isStored]);
 
-    return {
-      isStored,
-      isValidAddr,
-      isValid
-    };
+  if (isStored || !isAddress) {
+    return null;
   }
 
-  public render (): React.ReactNode {
-    const { t } = this.props;
-    const { isValid, isValidAddr } = this.state;
-
-    if (isValid || !isValidAddr) {
-      return null;
-    }
-
-    return (
-      <InfoForInput type='error'>
-        {
-          isValidAddr
-            ? t('Unable to find deployed contract code at the specified address')
-            : t('The value is not in a valid address format')
-        }
-      </InfoForInput>
-    );
-  }
+  return (
+    <InfoForInput type='error'>
+      {
+        isAddress
+          ? t('Unable to find deployed contract code at the specified address')
+          : t('The value is not in a valid address format')
+      }
+    </InfoForInput>
+  );
 }
 
 export default translate(
   withCalls<Props>(
-    ['query.contracts.contractInfoOf', { fallbacks: ['query.contract.contractInfoOf'], paramName: 'address' }]
+    ['query.contracts.contractInfoOf', {
+      fallbacks: ['query.contract.contractInfoOf'],
+      paramName: 'address'
+    }]
   )(ValidateAddr)
 );

+ 80 - 91
packages/app-contracts/src/Contracts/index.tsx

@@ -2,10 +2,10 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { I18nProps } from '@polkadot/react-components/types';
+import { CallContract, NullContract, I18nProps } from '@polkadot/react-components/types';
 import { ComponentProps } from '../types';
 
-import React from 'react';
+import React, { useState } from 'react';
 import { RouteComponentProps } from 'react-router';
 import { withRouter } from 'react-router-dom';
 import { Button, CardGrid } from '@polkadot/react-components';
@@ -14,107 +14,96 @@ import translate from '../translate';
 import Add from './Add';
 import Contract from './Contract';
 import Call from './Call';
+import { getContractForAddress } from './util';
 
 interface Props extends ComponentProps, I18nProps, RouteComponentProps {}
 
-interface State {
-  isAddOpen: boolean;
-  isCallOpen: boolean;
-  callAddress: string | null;
-  callMethod: string | null;
-}
+function Contracts (props: Props): React.ReactElement<Props> {
+  const { accounts, basePath, contracts, hasCode, showDeploy, t } = props;
+  // const { callAddress, callMethod, isAddOpen, isCallOpen } = this.state;
 
-class Contracts extends React.PureComponent<Props, State> {
-  public state: State = {
-    callAddress: null,
-    callMethod: null,
-    isAddOpen: false,
-    isCallOpen: false
-  };
+  const [callContract, setCallContract] = useState<CallContract | null>(null);
+  const [callMethodIndex, setCallMethodIndex] = useState<number | null>(null);
+  const [isAddOpen, setIsAddOpen] = useState(false);
+  const [isCallOpen, setIsCallOpen] = useState(false);
 
-  public render (): React.ReactNode {
-    const { accounts, basePath, contracts, hasCode, showDeploy, t } = this.props;
-    const { callAddress, callMethod, isAddOpen, isCallOpen } = this.state;
+  const _toggleAdd = (): void => setIsAddOpen(!isAddOpen);
+  const _toggleCall = (): void => setIsCallOpen(!isCallOpen);
 
-    return (
-      <>
-        <CardGrid
-          emptyText={t('No contracts available')}
-          buttons={
-            <Button.Group>
-              {hasCode && (
-                <>
-                  <Button
-                    icon='cloud upload'
-                    isPrimary
-                    label={t('Deploy a code hash')}
-                    onClick={showDeploy()}
-                  />
-                  <Button.Or />
-                </>
-              )}
-              <Button
-                icon='add'
-                isPrimary
-                label={t('Add an existing contract')}
-                onClick={this.showAdd}
-              />
-            </Button.Group>
-          }
-        >
-          {accounts && contracts && Object.keys(contracts).map((address): React.ReactNode => {
+  const _onChangeCallContract = (newCallContract: CallContract): void => {
+    if (callContract && newCallContract.address !== callContract.address) {
+      setCallMethodIndex(0);
+    }
+    setCallContract(callContract);
+  };
+  const _onChangeCallMethodIndex = (callMethodIndex: number): void => {
+    !!callContract && setCallMethodIndex(callMethodIndex);
+  };
+  const _onCall = (callContract: CallContract): (_?: number) => () => void => {
+    return function (callMethodIndex?: number): () => void {
+      return function (): void {
+        setCallContract(callContract);
+        setCallMethodIndex(callMethodIndex || 0);
+        setIsCallOpen(true);
+      };
+    };
+  };
+
+  return (
+    <>
+      <CardGrid
+        emptyText={t('No contracts available')}
+        buttons={
+          <Button.Group>
+            {hasCode && (
+              <>
+                <Button
+                  icon='cloud upload'
+                  isPrimary
+                  label={t('Deploy a code hash')}
+                  onClick={showDeploy()}
+                />
+                <Button.Or />
+              </>
+            )}
+            <Button
+              icon='add'
+              isPrimary
+              label={t('Add an existing contract')}
+              onClick={_toggleAdd}
+            />
+          </Button.Group>
+        }
+      >
+        {(accounts && contracts && Object.keys(contracts)
+          .map((address): CallContract | NullContract => getContractForAddress(address))
+          .filter(({ abi, address }: CallContract | NullContract): boolean => !!address && !!abi) as CallContract[])
+          .map((contract: CallContract): React.ReactNode => {
             return (
               <Contract
                 basePath={basePath}
-                address={address}
-                key={address}
-                onCall={this.showCall}
+                contract={contract}
+                key={contract.address}
+                onCall={_onCall}
               />
             );
           })}
-        </CardGrid>
-        <Add
-          basePath={basePath}
-          isOpen={isAddOpen}
-          onClose={this.hideAdd}
-        />
-        <Call
-          address={callAddress}
-          isOpen={isCallOpen}
-          method={callMethod}
-          onClose={this.hideCall}
-        />
-      </>
-    );
-  }
-
-  private showAdd = (): void => {
-    this.setState({
-      isAddOpen: true
-    });
-  }
-
-  private hideAdd = (): void => {
-    this.setState({
-      isAddOpen: false
-    });
-  }
-
-  private showCall = (callAddress?: string, callMethod?: string): void => {
-    this.setState({
-      isCallOpen: true,
-      callAddress: callAddress || null,
-      callMethod: callMethod || null
-    });
-  }
-
-  private hideCall = (): void => {
-    this.setState({
-      isCallOpen: false,
-      callAddress: null,
-      callMethod: null
-    });
-  }
+      </CardGrid>
+      <Add
+        basePath={basePath}
+        isOpen={isAddOpen}
+        onClose={_toggleAdd}
+      />
+      <Call
+        callContract={callContract}
+        callMethodIndex={callMethodIndex}
+        isOpen={isCallOpen}
+        onChangeCallContract={_onChangeCallContract}
+        onChangeCallMethodIndex={_onChangeCallMethodIndex}
+        onClose={_toggleCall}
+      />
+    </>
+  );
 }
 
 export default translate(withRouter(Contracts));

+ 54 - 0
packages/app-contracts/src/Contracts/util.tsx

@@ -0,0 +1,54 @@
+// Copyright 2017-2019 @polkadot/app-contracts authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+import { ContractABIFn, ContractABIMethod } from '@polkadot/api-contract/types';
+import { CallContract, NullContract, StringOrNull } from '@polkadot/react-components/types';
+import { CONTRACT_NULL } from '../constants';
+
+import React from 'react';
+import { MessageSignature } from '@polkadot/react-components';
+import { getContractAbi } from '@polkadot/react-components/util';
+import { stringCamelCase } from '@polkadot/util';
+
+export function findCallMethod (callContract: CallContract | null, callMethodIndex = 0): ContractABIMethod | null {
+  const message = callContract && callContract.abi.abi.contract.messages[callMethodIndex];
+
+  return message || null;
+}
+
+export function getContractMethodFn (callContract: CallContract | null, callMethod: ContractABIMethod | null): ContractABIFn | null {
+  const fn = callContract && callContract.abi && callMethod && callContract.abi.messages[stringCamelCase(callMethod.name)];
+
+  return fn || null;
+}
+
+export function getContractForAddress (address: StringOrNull): CallContract | NullContract {
+  if (!address) {
+    return CONTRACT_NULL;
+  } else {
+    const abi = getContractAbi(address);
+    return abi
+      ? {
+        address,
+        abi
+      }
+      : CONTRACT_NULL;
+  }
+}
+
+export function getCallMethodOptions (callContract: CallContract | null): any[] {
+  return callContract && callContract.abi
+    ? callContract.abi.abi.contract.messages.map((message, messageIndex): { key: string; text: React.ReactNode; value: string } => {
+      const key = message.name;
+
+      return {
+        key,
+        text: (
+          <MessageSignature message={message} />
+        ),
+        value: `${messageIndex}`
+      };
+    })
+    : [];
+}

+ 71 - 36
packages/app-contracts/src/Deploy.tsx

@@ -3,7 +3,6 @@
 // of the Apache-2.0 license. See the LICENSE file for details.
 
 import { AccountId } from '@polkadot/types/interfaces';
-import { TypeDef } from '@polkadot/types/types';
 import { ApiProps } from '@polkadot/react-api/types';
 import { I18nProps } from '@polkadot/react-components/types';
 
@@ -15,23 +14,25 @@ import { SubmittableResult } from '@polkadot/api';
 import { Abi } from '@polkadot/api-contract';
 import { withApi, withMulti } from '@polkadot/react-api';
 import keyring from '@polkadot/ui-keyring';
-import { Button, Dropdown, InputBalance, TxButton } from '@polkadot/react-components';
-import { getTypeDef } from '@polkadot/types';
+import { Button, Dropdown, InputBalance, MessageSignature, TxButton } from '@polkadot/react-components';
 import createValues from '@polkadot/react-params/values';
 
 import ContractModal, { ContractModalProps, ContractModalState } from './Modal';
 import Params from './Params';
 import store from './store';
 import translate from './translate';
+import { GAS_LIMIT } from './constants';
 
-type ConstructOptions = { key: string; text: string; value: string }[];
+type ConstructOptions = { key: string; text: React.ReactNode; value: string }[];
 
 interface Props extends ContractModalProps, ApiProps, I18nProps, RouteComponentProps {
   codeHash?: string;
+  constructorIndex?: number;
 }
 
 interface State extends ContractModalState {
   codeHash?: string;
+  constructorIndex: number;
   constructOptions: ConstructOptions;
   endowment: BN;
   isHashValid: boolean;
@@ -48,8 +49,10 @@ class Deploy extends ContractModal<Props, State> {
 
     this.defaultState = {
       ...this.defaultState,
+      constructorIndex: -1,
       constructOptions: [],
       endowment: new BN(0),
+      gasLimit: new BN(GAS_LIMIT),
       isHashValid: false,
       params: [],
       ...Deploy.getCodeState(props.codeHash)
@@ -59,44 +62,35 @@ class Deploy extends ContractModal<Props, State> {
 
   public static getDerivedStateFromProps (props: Props, state: State): Pick<State, never> {
     if (props.codeHash && (!state.codeHash || state.codeHash !== props.codeHash)) {
-      return Deploy.getCodeState(props.codeHash);
+      return Deploy.getCodeState(props.codeHash, Math.max(props.constructorIndex || 0));
     }
+
     return {};
   }
 
-  private static getContractAbiState = (abi: string | null | undefined, contractAbi: Abi | null = null): Partial<State> => {
+  private static getContractAbiState = (abi: string | null | undefined, contractAbi: Abi | null = null, constructorIndex = 0): Partial<State> => {
     if (contractAbi) {
-      const args = contractAbi.deploy.args.map(({ name, type }): string => `${name}: ${type}`);
-      const text = `deploy(${args.join(', ')})`;
-
       return {
         abi,
-        constructOptions: [{
-          key: 'deploy',
-          text,
-          value: 'deploy'
-        }],
+        constructorIndex,
         contractAbi,
         isAbiValid: !!contractAbi,
-        params: createValues(
-          contractAbi.deploy.args.map(({ name, type }): { type: TypeDef } => ({
-            type: getTypeDef(type, name)
-          }))
-        )
+        ...Deploy.getConstructorState(contractAbi, Math.max(constructorIndex, 0))
       };
     } else {
       return {
+        constructorIndex: -1,
         constructOptions: [] as ConstructOptions,
         abi: null,
         contractAbi: null,
         isAbiSupplied: false,
         isAbiValid: false,
-        params: [] as unknown[]
+        ...Deploy.getConstructorState()
       };
     }
   }
 
-  private static getCodeState = (codeHash: string | null = null): Pick<State, never> => {
+  private static getCodeState = (codeHash: string | null = null, constructorIndex = 0): Pick<State, never> => {
     if (codeHash) {
       const code = store.getCode(codeHash);
 
@@ -109,7 +103,7 @@ class Deploy extends ContractModal<Props, State> {
           name: `${json.name} (instance)`,
           isHashValid: true,
           isNameValid: true,
-          ...Deploy.getContractAbiState(json.abi, contractAbi)
+          ...Deploy.getContractAbiState(json.abi, contractAbi, Math.max(constructorIndex, 0))
         };
       }
     }
@@ -117,11 +111,43 @@ class Deploy extends ContractModal<Props, State> {
     return {};
   }
 
+  private static getConstructorState = (contractAbi: Abi | null = null, ci = 0): Pick<State, never> => {
+    const constructorIndex = Math.max(ci, 0);
+    if (!contractAbi || constructorIndex < 0 || constructorIndex >= contractAbi.constructors.length) {
+      return {
+        constructorIndex: -1,
+        constructOptions: [],
+        params: []
+      };
+    }
+
+    const { abi: { contract: { constructors } } } = contractAbi;
+    const constructor = constructors[constructorIndex];
+    const constructOptions: ConstructOptions = constructors.map(
+      (constr) => {
+        return {
+          key: `${constructorIndex}`,
+          text: (
+            <MessageSignature
+              asConstructor
+              message={constr}
+            />
+          ),
+          value: `${constructorIndex}`
+        };
+      });
+
+    return {
+      constructorIndex,
+      constructOptions,
+      params: createValues(constructor.args)
+    };
+  }
+
   protected renderContent = (): React.ReactNode => {
     const { t } = this.props;
-    const { codeHash, constructOptions, contractAbi, endowment, isAbiSupplied, isBusy, isHashValid } = this.state;
+    const { codeHash, constructorIndex, constructOptions, contractAbi, endowment, isAbiSupplied, isBusy, isHashValid } = this.state;
 
-    const isEndowValid = !endowment.isZero();
     const codeOptions = store.getAllCode().map(({ json: { codeHash, name } }): { text: string; value: string } => ({
       text: `${name} (${codeHash})`,
       value: codeHash
@@ -154,13 +180,14 @@ class Deploy extends ContractModal<Props, State> {
           contractAbi
             ? (
               <Dropdown
-                defaultValue='deploy'
                 help={t('The deployment constructor information for this contract, as provided by the ABI.')}
-                isDisabled
-                label={t('constructor')}
+                isDisabled={contractAbi.abi.contract.constructors.length <= 1}
+                label={t('constructor ')}
+                onChange={this.onChangeConstructorIndex}
                 options={constructOptions}
                 style={{ fontFamily: 'monospace' }}
-                value='deploy'
+                value={`${constructorIndex}`}
+                withLabel
               />
             )
             : null
@@ -170,16 +197,15 @@ class Deploy extends ContractModal<Props, State> {
           onChange={this.onChangeParams}
           onEnter={this.sendTx}
           params={
-            contractAbi
-              ? contractAbi.deploy.args
+            contractAbi && constructorIndex >= 0
+              ? contractAbi.abi.contract.constructors[constructorIndex].args
               : []
           }
         />
         <InputBalance
-          defaultValue={endowment}
-          help={t('The allotted endownment for this contract, i.e. the amount transferred to the contract upon instantiation.')}
+          help={t('The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.')}
           isDisabled={isBusy}
-          isError={!isEndowValid}
+          isError={endowment.isZero()}
           label={t('endowment')}
           onChange={this.onChangeEndowment}
           onEnter={this.sendTx}
@@ -224,13 +250,13 @@ class Deploy extends ContractModal<Props, State> {
   }
 
   private constructCall = (): any[] => {
-    const { codeHash, contractAbi, endowment, gasLimit, params } = this.state;
+    const { codeHash, constructorIndex, contractAbi, endowment, gasLimit, params } = this.state;
 
-    if (!contractAbi) {
+    if (!contractAbi || constructorIndex < 0) {
       return [];
     }
 
-    return [endowment, gasLimit, codeHash, contractAbi.deploy(...params)];
+    return [endowment, gasLimit, codeHash, contractAbi.constructors[constructorIndex](...params)];
   }
 
   protected onAddAbi = (abi: string | null | undefined, contractAbi?: Abi | null): void => {
@@ -245,6 +271,15 @@ class Deploy extends ContractModal<Props, State> {
     );
   }
 
+  private onChangeConstructorIndex = (constructorIndexString: string): void => {
+    const { contractAbi } = this.state;
+    const constructorIndex = Math.max(0, parseInt(constructorIndexString, 10) || 0);
+
+    this.setState(
+      Deploy.getConstructorState(contractAbi, constructorIndex)
+    );
+  };
+
   private onChangeEndowment = (endowment?: BN | null): void => {
     this.setState({ endowment: endowment || new BN(0) });
   }

+ 0 - 3
packages/app-contracts/src/Modal.tsx

@@ -106,7 +106,6 @@ class ContractModal<P extends ContractModalProps, S extends ContractModalState>
 
     return (
       <InputAddress
-        defaultValue={accountId}
         help={t('Specify the user account to use for this deployment. And fees will be deducted from this account.')}
         isDisabled={isBusy}
         isInput={false}
@@ -124,7 +123,6 @@ class ContractModal<P extends ContractModalProps, S extends ContractModalState>
 
     return (
       <Input
-        defaultValue={name}
         help={t(
           this.isContract
             ? 'A name for the deployed contract to help users distinguish. Only used for display purposes.'
@@ -151,7 +149,6 @@ class ContractModal<P extends ContractModalProps, S extends ContractModalState>
 
     return (
       <InputNumber
-        defaultValue={gasLimit}
         help={t('The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.')}
         isDisabled={isBusy}
         isError={!isGasValid}

+ 1 - 7
packages/app-contracts/src/Params.tsx

@@ -8,7 +8,6 @@ import { RawParams } from '@polkadot/react-params/types';
 
 import React, { useEffect, useState } from 'react';
 import UIParams from '@polkadot/react-params';
-import { getTypeDef } from '@polkadot/types';
 
 interface Props {
   isDisabled?: boolean;
@@ -27,12 +26,7 @@ export default function Params ({ isDisabled, onChange, onEnter, params: propPar
 
   useEffect((): void => {
     if (propParams) {
-      setParams(
-        propParams.map(({ name, type }): ParamDef => ({
-          name,
-          type: getTypeDef(type, name)
-        }))
-      );
+      setParams(propParams);
     }
   }, [propParams]);
 

+ 1 - 1
packages/app-contracts/src/RemoveABI.tsx

@@ -37,7 +37,7 @@ function RemoveABI ({ code, onClose, onRemove, t }: Props): React.ReactElement<P
           isInline
         >
           <p>{t('You are about to remove this code\'s ABI. Once completed, should you need to access it again, you will have to manually re-upload it.')}</p>
-          <p>{t('This operaion does not impact the associated on-chain code or any of its contracts.')}</p>
+          <p>{t('This operation does not impact the associated on-chain code or any of its contracts.')}</p>
         </CodeRow>
       </Modal.Content>
       <Modal.Actions>

+ 10 - 0
packages/app-contracts/src/constants.ts

@@ -0,0 +1,10 @@
+// Copyright 2017-2019 @polkadot/app-staking authors & contributors
+// This software may be modified and distributed under the terms
+// of the Apache-2.0 license. See the LICENSE file for details.
+
+export const ENDOWMENT = 1000;
+export const GAS_LIMIT = 500000;
+export const CONTRACT_NULL = {
+  abi: null,
+  address: null
+};

+ 8 - 2
packages/app-contracts/src/index.tsx

@@ -28,6 +28,7 @@ interface Props extends AppProps, I18nProps, RouteComponentProps {
 
 interface State {
   codeHash?: string;
+  constructorIndex: number;
   hasContracts: boolean;
   isDeployOpen: boolean;
   updated: number;
@@ -35,6 +36,7 @@ interface State {
 
 class App extends React.PureComponent<Props, State> {
   public state: State = {
+    constructorIndex: 0,
     hasContracts: false,
     isDeployOpen: false,
     updated: 0
@@ -62,7 +64,7 @@ class App extends React.PureComponent<Props, State> {
 
   public render (): React.ReactNode {
     const { basePath, t } = this.props;
-    const { codeHash, isDeployOpen } = this.state;
+    const { codeHash, constructorIndex, isDeployOpen } = this.state;
     const hidden: string[] = [];
 
     return (
@@ -93,6 +95,7 @@ class App extends React.PureComponent<Props, State> {
         <Deploy
           basePath={basePath}
           codeHash={codeHash}
+          constructorIndex={constructorIndex}
           isOpen={isDeployOpen}
           onClose={this.hideDeploy}
         />
@@ -103,6 +106,7 @@ class App extends React.PureComponent<Props, State> {
   private renderComponent (Component: React.ComponentType<ComponentProps>): () => React.ReactNode {
     return (): React.ReactNode => {
       const { accounts, basePath, contracts, onStatusChange } = this.props;
+      const { updated } = this.state;
 
       if (!contracts) {
         return null;
@@ -116,15 +120,17 @@ class App extends React.PureComponent<Props, State> {
           hasCode={store.hasCode}
           onStatusChange={onStatusChange}
           showDeploy={this.showDeploy}
+          updated={updated}
         />
       );
     };
   }
 
-  private showDeploy = (codeHash?: string): () => void =>
+  private showDeploy = (codeHash?: string, constructorIndex = 0): () => void =>
     (): void => {
       this.setState({
         codeHash: codeHash || undefined,
+        constructorIndex,
         isDeployOpen: true
       });
     }

+ 3 - 2
packages/app-contracts/src/store.ts

@@ -74,10 +74,11 @@ class Store extends EventEmitter {
 
   private addCode (json: CodeJson): void {
     try {
+      const abi = json.abi ? JSON.parse(json.abi) : null;
       this.allCode[json.codeHash] = {
         json,
-        contractAbi: json.abi
-          ? new Abi(JSON.parse(json.abi))
+        contractAbi: abi
+          ? new Abi(abi)
           : undefined
       };
 

+ 1 - 1
packages/app-contracts/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['contracts', 'ui']);
+export default withTranslation(['app-contracts']);

+ 2 - 1
packages/app-contracts/src/types.ts

@@ -12,7 +12,8 @@ export interface ComponentProps extends AppProps {
   accounts: SubjectInfo[];
   contracts: SubjectInfo[];
   hasCode: boolean;
-  showDeploy: (codeHash?: string) => () => void;
+  showDeploy: (codeHash?: string, constructorIndex?: number) => () => void;
+  updated: number;
 }
 
 export interface CodeJson {

+ 4 - 4
packages/app-council/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-council",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "Council",
   "main": "index.js",
   "scripts": {},
@@ -10,8 +10,8 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65",
-    "@polkadot/react-query": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101",
+    "@polkadot/react-query": "^0.36.0-beta.101"
   }
 }

+ 7 - 8
packages/app-council/src/Overview/index.tsx

@@ -3,13 +3,12 @@
 // of the Apache-2.0 license. See the LICENSE file for details.
 
 import { DerivedElectionsInfo } from '@polkadot/api-derive/types';
-import { BlockNumber, SetIndex, VoteIndex } from '@polkadot/types/interfaces';
 import { ComponentProps as Props } from './types';
 
-import BN from 'bn.js';
 import React from 'react';
 import { withCalls } from '@polkadot/react-api';
 import { Button } from '@polkadot/react-components';
+import { createType } from '@polkadot/types';
 
 import Members from './Members';
 import SubmitCandidacy from './SubmitCandidacy';
@@ -19,12 +18,12 @@ import Vote from './Vote';
 const NULL_INFO: DerivedElectionsInfo = {
   members: {},
   candidates: [],
-  candidateCount: new BN(0),
-  desiredSeats: new BN(0),
-  nextVoterSet: new BN(0) as SetIndex,
-  termDuration: new BN(0) as BlockNumber,
-  voteCount: new BN(0) as VoteIndex,
-  voterCount: new BN(0) as SetIndex
+  candidateCount: createType('u32'),
+  desiredSeats: createType('u32'),
+  nextVoterSet: createType('SetIndex'),
+  termDuration: createType('BlockNumber'),
+  voteCount: createType('VoteIndex'),
+  voterCount: createType('SetIndex')
 };
 
 function Overview ({ electionsInfo = NULL_INFO }: Props): React.ReactElement<Props> {

+ 1 - 1
packages/app-council/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['council', 'ui']);
+export default withTranslation(['app-council']);

+ 4 - 4
packages/app-dashboard/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-dashboard",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "Dashboard for all apps, allowing for an overview and quick navigation",
   "main": "index.js",
   "scripts": {},
@@ -10,8 +10,8 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/apps-routing": "^0.36.0-beta.65",
-    "@polkadot/react-components": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/apps-routing": "^0.36.0-beta.101",
+    "@polkadot/react-components": "^0.36.0-beta.101"
   }
 }

+ 1 - 1
packages/app-dashboard/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['dashboard', 'ui']);
+export default withTranslation(['app-dashboard']);

+ 4 - 4
packages/app-democracy/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-democracy",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "A referendum & proposal app",
   "main": "index.js",
   "scripts": {},
@@ -10,8 +10,8 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65",
-    "@polkadot/react-query": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101",
+    "@polkadot/react-query": "^0.36.0-beta.101"
   }
 }

+ 47 - 52
packages/app-democracy/src/Propose.tsx → packages/app-democracy/src/Overview/Propose.tsx

@@ -3,51 +3,72 @@
 // of the Apache-2.0 license. See the LICENSE file for details.
 
 import { Call } from '@polkadot/types/interfaces';
-import { I18nProps } from '@polkadot/react-components/types';
 import { ApiProps } from '@polkadot/react-api/types';
 
 import BN from 'bn.js';
 import React from 'react';
-import { RouteComponentProps } from 'react-router';
-import { withRouter } from 'react-router-dom';
 import { createType } from '@polkadot/types';
-import { Button, Extrinsic, InputAddress, InputBalance, TxButton, TxComponent } from '@polkadot/react-components';
+import { Button, Extrinsic, InputBalance } from '@polkadot/react-components';
+import TxModal, { TxModalState, TxModalProps } from '@polkadot/react-components/TxModal';
 import { withApi, withMulti } from '@polkadot/react-api';
 
-import translate from './translate';
+import translate from '../translate';
 
-interface Props extends I18nProps, ApiProps, RouteComponentProps {
-  basePath: string;
-}
+interface Props extends TxModalProps, ApiProps {}
 
-interface State {
+interface State extends TxModalState {
   accountId?: string | null;
   method: Call | null;
   value: BN;
   isValid: boolean;
 }
 
-class Propose extends TxComponent<Props, State> {
+class Propose extends TxModal<Props, State> {
   public state: State = {
-    method: null,
-    value: new BN(0),
-    isValid: false
+    ...this.defaultState,
+    value: new BN(0)
   };
 
-  public render (): React.ReactNode {
+  protected headerText = (): string => this.props.t('Submit proposal');
+
+  protected txMethod = (): string => 'democracy.propose';
+
+  protected txParams = (): [Call, BN] => {
+    const { value, method } = this.state;
+
+    return [createType('Proposal', method || undefined), value];
+  }
+
+  protected isDisabled = (): boolean => {
+    const { accountId, value, method } = this.state;
+    const hasValue = !!value && value.gtn(0);
+    const hasMethod = !!method;
+
+    return !accountId || !hasValue || !hasMethod;
+  }
+
+  protected renderTrigger = (): React.ReactNode => {
+    const { t } = this.props;
+
+    return (
+      <Button.Group>
+        <Button
+          isPrimary
+          label={t('Submit proposal')}
+          icon='add'
+          onClick={this.showModal}
+        />
+      </Button.Group>
+    );
+  }
+
+  protected renderContent = (): React.ReactNode => {
     const { apiDefaultTxSudo, t } = this.props;
-    const { isValid, accountId, method, value } = this.state;
+    const { value } = this.state;
     const hasValue = !!value && value.gtn(0);
 
     return (
       <section>
-        <InputAddress
-          className='medium'
-          label={t('account')}
-          help={t('The account used to make the new proposal')}
-          type='account'
-          onChange={this.onChangeAccount}
-        />
         <Extrinsic
           defaultValue={apiDefaultTxSudo}
           label={t('propose')}
@@ -62,28 +83,13 @@ class Propose extends TxComponent<Props, State> {
           onChange={this.onChangeValue}
           onEnter={this.sendTx}
         />
-        <Button.Group>
-          <TxButton
-            accountId={accountId}
-            label={t('Submit Proposal')}
-            icon='sign-in'
-            tx='democracy.propose'
-            isDisabled={!isValid}
-            params={[
-              ...(method ? [createType('Proposal', method)] : []),
-              ...(hasValue ? [value] : [])
-            ]}
-            onSuccess={this.onSubmitProposal}
-            ref={this.button}
-          />
-        </Button.Group>
       </section>
     );
   }
 
   private nextState (newState: Partial<State>): void {
     this.setState(
-      (prevState: State): State => {
+      (prevState: State): Pick<State, never> => {
         const { accountId = prevState.accountId, method = prevState.method, value = prevState.value } = newState;
         const isValid = !!method && !!value && value.gt(new BN(0)) && !!accountId && accountId.length > 0;
 
@@ -97,13 +103,9 @@ class Propose extends TxComponent<Props, State> {
     );
   }
 
-  private onChangeAccount = (accountId: string | null): void => {
-    this.nextState({ accountId });
-  }
-
   private onChangeExtrinsic = (method?: Call): void => {
     if (!method) {
-      return;
+      return this.nextState({ method: null });
     }
 
     this.nextState({ method });
@@ -112,16 +114,9 @@ class Propose extends TxComponent<Props, State> {
   private onChangeValue = (value?: BN): void => {
     this.nextState({ value });
   }
-
-  private onSubmitProposal = (): void => {
-    const { history, basePath } = this.props;
-
-    history.push(basePath);
-  }
 }
 
 export default withMulti(
-  withRouter(Propose),
-  translate,
-  withApi
+  withApi(Propose),
+  translate
 );

+ 2 - 0
packages/app-democracy/src/Overview/index.tsx

@@ -8,11 +8,13 @@ import { Columar } from '@polkadot/react-components';
 import Proposals from './Proposals';
 import Referendums from './Referendums';
 import Summary from './Summary';
+import Propose from './Propose';
 
 export default function Overview (): React.ReactElement {
   return (
     <>
       <Summary />
+      <Propose />
       <Columar>
         <Referendums />
         <Proposals />

+ 0 - 9
packages/app-democracy/src/index.tsx

@@ -11,7 +11,6 @@ import uiSettings from '@polkadot/ui-settings';
 
 import basicMd from './md/basic.md';
 import Overview from './Overview';
-import Propose from './Propose';
 import translate from './translate';
 
 interface Props extends AppProps, BareProps, I18nProps {}
@@ -33,19 +32,11 @@ function App ({ basePath, t }: Props): React.ReactElement<Props> {
               isRoot: true,
               name: 'overview',
               text: t('Democracy overview')
-            },
-            {
-              name: 'propose',
-              text: t('Submit proposal')
             }
           ]}
         />
       </header>
       <Switch>
-        <Route
-          path={`${basePath}/propose`}
-          render={(): React.ReactNode => <Propose basePath={basePath} />}
-        />
         <Route component={Overview} />
       </Switch>
     </main>

+ 1 - 1
packages/app-democracy/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['democracy', 'ui']);
+export default withTranslation(['app-democracy']);

+ 3 - 3
packages/app-explorer/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-explorer",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "main": "index.js",
   "repository": "https://github.com/polkadot-js/apps.git",
   "author": "Jaco Greeff <jacogr@gmail.com>",
@@ -10,7 +10,7 @@
   "contributors": [],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101"
   }
 }

+ 1 - 1
packages/app-explorer/src/NodeInfo/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['status', 'ui']);
+export default withTranslation(['app-explorer']);

+ 1 - 1
packages/app-explorer/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['explorer', 'ui']);
+export default withTranslation(['app-explorer']);

+ 5 - 5
packages/app-extrinsics/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-extrinsics",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "main": "index.js",
   "repository": "https://github.com/polkadot-js/apps.git",
   "author": "Jaco Greeff <jacogr@gmail.com>",
@@ -10,9 +10,9 @@
   "contributors": [],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65",
-    "@polkadot/react-params": "^0.36.0-beta.65",
-    "@polkadot/react-signer": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101",
+    "@polkadot/react-params": "^0.36.0-beta.101",
+    "@polkadot/react-signer": "^0.36.0-beta.101"
   }
 }

+ 1 - 1
packages/app-extrinsics/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['extrinsics', 'ui']);
+export default withTranslation(['app-extrinsics']);

+ 3 - 3
packages/app-generic-asset/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-generic-asset",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "A basic GenericAsset transfer app",
   "main": "index.js",
   "scripts": {},
@@ -10,7 +10,7 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101"
   }
 }

+ 94 - 147
packages/app-generic-asset/src/Transfer.tsx

@@ -3,177 +3,125 @@
 // of the Apache-2.0 license. See the LICENSE file for details.
 
 import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
-import { ApiProps } from '@polkadot/react-api/types';
 import { I18nProps } from '@polkadot/react-components/types';
 
 import BN from 'bn.js';
-import React from 'react';
+import React, { useContext, useEffect, useState } from 'react';
 import styled from 'styled-components';
 import { Button, InputAddress, InputBalance, TxButton, Dropdown } from '@polkadot/react-components';
 import { Available } from '@polkadot/react-query';
 import Checks from '@polkadot/react-signer/Checks';
-import { withApi, withMulti, withObservable } from '@polkadot/react-api';
+import { ApiContext, withMulti, withObservable } from '@polkadot/react-api';
 
 import assetRegistry, { AssetsSubjectInfo } from './assetsRegistry';
 import translate from './translate';
 
-type Props = ApiProps & I18nProps & {
+interface Props extends I18nProps {
   className?: string;
   onClose: () => void;
   recipientId?: string;
   senderId?: string;
   assets?: AssetsSubjectInfo;
-};
-
-interface State {
-  assetId: string;
-  amount: BN;
-  extrinsic: SubmittableExtrinsic | null;
-  hasAvailable: boolean;
-  recipientId?: string | null;
-  senderId?: string | null;
 }
 
-const ZERO = new BN(0);
-
-class Transfer extends React.PureComponent<Props> {
-  public state: State;
-
-  public constructor (props: Props) {
-    super(props);
-
-    this.state = {
-      assetId: '0',
-      amount: ZERO,
-      extrinsic: null,
-      hasAvailable: true,
-      recipientId: props.recipientId || null,
-      senderId: props.senderId || null
-    };
-  }
-
-  public render (): React.ReactNode {
-    const { assets, className, onClose, recipientId: propRecipientId, senderId: propSenderId, t } = this.props;
-    const { extrinsic, hasAvailable, recipientId, senderId, assetId } = this.state;
-    const available = <span className='label'>{t('available ')}</span>;
-    const options = assets
-      ? Object.entries(assets)
-        .map(([id, name]): { value: string; text: string } => ({
-          value: id,
-          text: `${name} (${id})`
-        }))
-      : [];
-
-    return (
-      <div>
-        <div className={className}>
-          <InputAddress
-            defaultValue={propSenderId}
-            help={t('The account you will send funds from.')}
-            isDisabled={!!propSenderId}
-            label={t('send from account')}
-            labelExtra={<Available label={available} params={senderId} />}
-            onChange={this.onChangeFrom}
-            type='account'
-          />
-          <InputAddress
-            defaultValue={propRecipientId}
-            help={t('Select a contact or paste the address you want to send funds to.')}
-            isDisabled={!!propRecipientId}
-            label={t('send to address')}
-            labelExtra={<Available label={available} params={recipientId} />}
-            onChange={this.onChangeTo}
-            type='allPlus'
-          />
-          <Dropdown
-            allowAdd
-            help={t('Enter the Asset ID of the token you want to transfer.')}
-            label={t('asset id')}
-            onChange={this.onChangeAssetId}
-            options={options}
-            onAdd={this.onAddAssetId}
-            value={assetId}
-          />
-          <InputBalance
-            help={t('Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 mili is equivalent to sending 0.001.')}
-            isError={!hasAvailable}
-            label={t('amount')}
-            onChange={this.onChangeAmount}
-          />
-          <Checks
-            accountId={senderId}
-            extrinsic={extrinsic}
-            isSendable
-            onChange={this.onChangeFees}
-          />
-        </div>
-        <Button.Group>
-          <TxButton
-            accountId={senderId}
-            extrinsic={extrinsic}
-            isDisabled={!hasAvailable}
-            isPrimary
-            label={t('Make Transfer')}
-            icon='send'
-            onStart={onClose}
-            withSpinner={false}
-          />
-        </Button.Group>
-      </div>
-    );
-  }
+interface Option {
+  text: string;
+  value: string;
+}
 
-  private nextState (newState: Partial<State>): void {
-    this.setState((prevState: State): State => {
-      const { api } = this.props;
-      const {
-        amount = prevState.amount,
-        recipientId = prevState.recipientId,
-        hasAvailable = prevState.hasAvailable,
-        senderId = prevState.senderId,
-        assetId = prevState.assetId
-      } = newState;
-      const extrinsic = recipientId && senderId
+function Transfer ({ assets, className, onClose, recipientId: propRecipientId, senderId: propSenderId, t }: Props): React.ReactElement<Props> {
+  const { api } = useContext(ApiContext);
+  const [assetId, setAssetId] = useState('0');
+  const [amount, setAmount] = useState<BN | undefined>(new BN(0));
+  const [extrinsic, setExtrinsic] = useState<SubmittableExtrinsic | null>(null);
+  const [hasAvailable, setHasAvailable] = useState(true);
+  const [options, setOptions] = useState<Option[]>([]);
+  const [recipientId, setRecipientId] = useState(propRecipientId || null);
+  const [senderId, setSenderId] = useState(propSenderId || null);
+
+  // build up our list of options via assets
+  useEffect((): void => {
+    setOptions(Object.entries(assets || {}).map(([id, name]): Option => ({
+      value: id,
+      text: `${name} (${id})`
+    })));
+  }, [assets]);
+
+  // create an extrinsic if we have correct values
+  useEffect((): void => {
+    setExtrinsic(
+      recipientId && senderId && amount
         ? api.tx.genericAsset.transfer(assetId, recipientId, amount)
-        : null;
-
-      return {
-        assetId,
-        amount,
-        extrinsic,
-        hasAvailable,
-        recipientId,
-        senderId
-      };
-    });
-  }
-
-  private onChangeAmount = (amount: BN = new BN(0)): void => {
-    this.nextState({ amount });
-  }
-
-  private onChangeFrom = (senderId: string | null): void => {
-    this.nextState({ senderId });
-  }
-
-  private onChangeTo = (recipientId: string | null): void => {
-    this.nextState({ recipientId });
-  }
-
-  private onChangeFees = (hasAvailable: boolean): void => {
-    this.setState({ hasAvailable });
-  }
-
-  private onChangeAssetId = (assetId: string): void => {
-    this.nextState({ assetId });
-  }
+        : null
+    );
+  }, [amount, assetId, recipientId, senderId]);
 
-  private onAddAssetId = (id: string): void => {
+  const _onAddAssetId = (id: string): void => {
     // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec
     if (id.trim().match(/^\d+$/)) {
       assetRegistry.add(id, id);
     }
-  }
+  };
+
+  const available = <span className='label'>{t('available ')}</span>;
+
+  return (
+    <div>
+      <div className={className}>
+        <InputAddress
+          defaultValue={propSenderId}
+          help={t('The account you will send funds from.')}
+          isDisabled={!!propSenderId}
+          label={t('send from account')}
+          labelExtra={<Available label={available} params={senderId} />}
+          onChange={setSenderId}
+          type='account'
+        />
+        <InputAddress
+          defaultValue={propRecipientId}
+          help={t('Select a contact or paste the address you want to send funds to.')}
+          isDisabled={!!propRecipientId}
+          label={t('send to address')}
+          labelExtra={<Available label={available} params={recipientId} />}
+          onChange={setRecipientId}
+          type='allPlus'
+        />
+        <Dropdown
+          allowAdd
+          help={t('Enter the Asset ID of the token you want to transfer.')}
+          label={t('asset id')}
+          onChange={setAssetId}
+          options={options}
+          onAdd={_onAddAssetId}
+          value={assetId}
+        />
+        <InputBalance
+          help={t('Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.')}
+          isError={!hasAvailable}
+          label={t('amount')}
+          onChange={setAmount}
+        />
+        <Checks
+          accountId={senderId}
+          extrinsic={extrinsic}
+          isSendable
+          onChange={setHasAvailable}
+        />
+      </div>
+      <Button.Group>
+        <TxButton
+          accountId={senderId}
+          extrinsic={extrinsic}
+          isDisabled={!hasAvailable}
+          isPrimary
+          label={t('Make Transfer')}
+          icon='send'
+          onStart={onClose}
+          withSpinner={false}
+        />
+      </Button.Group>
+    </div>
+  );
 }
 
 export default withMulti(
@@ -198,6 +146,5 @@ export default withMulti(
     }
   `,
   translate,
-  withApi,
   withObservable(assetRegistry.subject, { propName: 'assets' })
 );

+ 1 - 1
packages/app-generic-asset/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['generic-asset', 'ui']);
+export default withTranslation(['app-generic-asset']);

+ 3 - 3
packages/app-js/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-js",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "A simple JavaScript console for playing with the API",
   "main": "index.js",
   "scripts": {},
@@ -10,8 +10,8 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65",
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101",
     "snappyjs": "^0.6.0"
   }
 }

+ 4 - 12
packages/app-js/src/ActionButtons.tsx

@@ -13,6 +13,7 @@ import translate from './translate';
 
 interface Props extends BareProps, I18nProps {
   isCustomExample: boolean;
+  isRunning: boolean;
   generateLink: () => void;
   removeSnippet: () => void;
   runJs: () => void;
@@ -21,8 +22,7 @@ interface Props extends BareProps, I18nProps {
   stopJs: () => void;
 }
 
-function ActionButtons ({ className, generateLink, isCustomExample, removeSnippet, runJs, saveSnippet, stopJs, t }: Props): React.ReactElement<Props> {
-  const [isRunning, setIsRunning] = useState(false);
+function ActionButtons ({ className, generateLink, isCustomExample, isRunning, removeSnippet, runJs, saveSnippet, stopJs, t }: Props): React.ReactElement<Props> {
   const [isOpen, setIsOpen] = useState(false);
   const [shareText, setShareText] = useState(t('Generate link to share code example'));
   const [snippetName, setSnippetName] = useState('');
@@ -48,14 +48,6 @@ function ActionButtons ({ className, generateLink, isCustomExample, removeSnippe
     saveSnippet(snippetName);
     _onPopupClose();
   };
-  const _runJs = (): void => {
-    setIsRunning(true);
-    runJs();
-  };
-  const _stopJs = (): void => {
-    stopJs();
-    setIsRunning(false);
-  };
 
   return (
     <div className={`${className} action-button`}>
@@ -129,7 +121,7 @@ function ActionButtons ({ className, generateLink, isCustomExample, removeSnippe
             icon='close'
             isCircular
             isNegative
-            onClick={_stopJs}
+            onClick={stopJs}
           />
         )
         : (
@@ -138,7 +130,7 @@ function ActionButtons ({ className, generateLink, isCustomExample, removeSnippe
             icon='play'
             isCircular
             isPrimary
-            onClick={_runJs}
+            onClick={runJs}
           />
         )
       }

+ 56 - 40
packages/app-js/src/Playground.tsx

@@ -55,6 +55,7 @@ interface Props extends AppProps, I18nProps, RouteComponentProps<{}> {
 }
 
 const snippets: Snippet[] = JSON.parse(JSON.stringify(allSnippets));
+let hasSnippetWrappers = false;
 
 function decodeBase64 (base64: string): Snippet {
   const sharedExample: Snippet = {
@@ -91,14 +92,53 @@ function decodeBase64 (base64: string): Snippet {
 function Playground ({ className, history, match: { params: { base64 } }, t }: Props): React.ReactElement<Props> {
   const { api, isDevelopment } = useContext(ApiContext);
   const injectedRef = useRef<Injected | null>(null);
+  const [code, setCode] = useState('');
   const [isAnimated, setIsAnimated] = useState(true);
   const [isCustomExample, setIsCustomExample] = useState(false);
+  const [isRunning, setIsRunning] = useState(false);
   const [customExamples, setCustomExamples] = useState<Snippet[]>([]);
   const [logs, setLogs] = useState<Log[]>([]);
   const [options, setOptions] = useState<Snippet[]>([]);
   const [selected, setSelected] = useState(snippets[0]);
   const [sharedExample, setSharedExample] = useState<Snippet | undefined>();
 
+  // add snippet wrappers
+  useEffect((): void => {
+    if (!hasSnippetWrappers) {
+      snippets.forEach((snippet): void => {
+        snippet.code = `${makeWrapper(isDevelopment)}${snippet.code}`;
+      });
+
+      hasSnippetWrappers = true;
+    }
+  }, []);
+
+  // initialize all options
+  useEffect((): void => {
+    const sharedExample = base64 ? decodeBase64(base64) : undefined;
+    const localData = {
+      examples: localStorage.getItem(STORE_EXAMPLES),
+      selectedValue: localStorage.getItem(STORE_SELECTED)
+    };
+    const customExamples = localData.examples ? JSON.parse(localData.examples) : [];
+
+    const options: Snippet[] = sharedExample
+      ? [sharedExample, ...customExamples, ...snippets]
+      : [...customExamples, ...snippets];
+
+    const selected = options.find((option): boolean => option.value === localData.selectedValue);
+
+    setCustomExamples(customExamples);
+    setIsCustomExample((selected && selected.type === 'custom') || false);
+    setOptions(options);
+    setSelected(sharedExample || selected || snippets[0]);
+    setSharedExample(sharedExample);
+  }, []);
+
+  useEffect((): void => {
+    setCode(selected.code);
+  }, [selected]);
+
   const _clearConsole = (): void => setLogs([]);
   const _hookConsole = (type: LogType, args: any[]): void => {
     logs.push({ args, type });
@@ -135,8 +175,11 @@ function Playground ({ className, history, match: { params: { base64 } }, t }: P
       injectedRef.current.api.disconnect();
       injectedRef.current = null;
     }
+
+    setIsRunning(false);
   };
   const _runJs = async (): Promise<void> => {
+    setIsRunning(true);
     _stopJs();
     _clearConsole();
 
@@ -158,27 +201,30 @@ function Playground ({ className, history, match: { params: { base64 } }, t }: P
 
     await injectedRef.current.api.isReady;
 
-    // squash into a single line so exceptions (with linenumbers) maps to the same line/origin
+    // squash into a single line so exceptions (with line numbers) maps to the same line/origin
     // as we have in the editor view (TODO: Make the console.error here actually return the full stack)
-    const exec = `(async ({${Object.keys(injectedRef.current).join(',')}}) => { try { ${selected.code} \n } catch (error) { console.error(error); } })(injected);`;
+    const exec = `(async ({${Object.keys(injectedRef.current).join(',')}}) => { try { ${code} \n } catch (error) { console.error(error); } })(injected);`;
 
     // eslint-disable-next-line no-new-func
     new Function('injected', exec)(injectedRef.current);
   };
   const _selectExample = (value: string): void => {
+    _stopJs();
+
     if (value.length) {
       const option = options.find((option): boolean => option.value === value);
 
       if (option) {
         localStorage.setItem(STORE_SELECTED, value);
-        setLogs([]);
+
+        _clearConsole();
         setIsCustomExample(option.type === 'custom');
         setSelected(option);
       }
     }
   };
   const _generateLink = (): void => {
-    const u8a = util.stringToU8a(selected.code);
+    const u8a = util.stringToU8a(code);
     const compU8a = snappy.compress(u8a);
     const compStr = compU8a.reduce((str: string, ch: number): string => {
       return str + String.fromCharCode(ch);
@@ -197,12 +243,6 @@ function Playground ({ className, history, match: { params: { base64 } }, t }: P
 
     _copyToClipboard(`${basePath}/#${path}`);
   };
-  const _onEdit = (code: string): void => {
-    if (code !== selected.code) {
-      setIsCustomExample(false);
-      setSelected({ ...selected, code });
-    }
-  };
   const _removeSnippet = (): void => {
     const filtered = customExamples.filter((value): boolean => value.value !== selected.value);
     const nextOptions = [...filtered, ...snippets];
@@ -218,7 +258,7 @@ function Playground ({ className, history, match: { params: { base64 } }, t }: P
     // The <Dropdown> component doesn't take boolean custom props and no
     // camelCase keys, that's why 'custom' is passed as a string here
     const snapshot: Snippet = {
-      code: selected.code,
+      code,
       label: CUSTOM_LABEL,
       text: snippetName,
       type: 'custom',
@@ -240,31 +280,6 @@ function Playground ({ className, history, match: { params: { base64 } }, t }: P
     setSharedExample(selected.type === 'shared' ? undefined : sharedExample);
   };
 
-  useEffect((): void => {
-    snippets.forEach((snippet): void => {
-      snippet.code = `${makeWrapper(isDevelopment)}${snippet.code}`;
-    });
-
-    const sharedExample = base64 ? decodeBase64(base64) : undefined;
-    const localData = {
-      examples: localStorage.getItem(STORE_EXAMPLES),
-      selectedValue: localStorage.getItem(STORE_SELECTED)
-    };
-    const customExamples = localData.examples ? JSON.parse(localData.examples) : [];
-
-    const options: Snippet[] = sharedExample
-      ? [sharedExample, ...customExamples, ...snippets]
-      : [...customExamples, ...snippets];
-
-    const selected = options.find((option): boolean => option.value === localData.selectedValue);
-
-    setCustomExamples(customExamples);
-    setIsCustomExample((selected && selected.type === 'custom') || false);
-    setOptions(options);
-    setSelected(sharedExample || selected || snippets[0]);
-    setSharedExample(sharedExample);
-  }, []);
-
   const snippetName = selected.type === 'custom' ? selected.text : undefined;
 
   return (
@@ -272,10 +287,10 @@ function Playground ({ className, history, match: { params: { base64 } }, t }: P
       <header className='container'>
         <Dropdown
           className='js--Dropdown'
-          defaultValue={selected.value}
           onChange={_selectExample}
-          options={options}
           label={t('Select example')}
+          options={options}
+          value={selected.value}
         />
       </header>
       <section className='js--Content'>
@@ -288,6 +303,7 @@ function Playground ({ className, history, match: { params: { base64 } }, t }: P
             <ActionButtons
               generateLink={_generateLink}
               isCustomExample={isCustomExample}
+              isRunning={isRunning}
               removeSnippet={_removeSnippet}
               runJs={_runJs}
               saveSnippet={_saveSnippet}
@@ -295,8 +311,8 @@ function Playground ({ className, history, match: { params: { base64 } }, t }: P
               stopJs={_stopJs}
             />
             <Editor
-              code={selected.code}
-              onEdit={_onEdit}
+              code={code}
+              onEdit={setCode}
             />
           </article>
         </Transition>

+ 1 - 1
packages/app-js/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['js', 'ui']);
+export default withTranslation(['app-js']);

+ 4 - 4
packages/app-parachains/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-parachains",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "Parachains",
   "main": "index.js",
   "scripts": {},
@@ -10,8 +10,8 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65",
-    "@polkadot/react-query": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101",
+    "@polkadot/react-query": "^0.36.0-beta.101"
   }
 }

+ 12 - 7
packages/app-parachains/src/Overview/Parachains.tsx

@@ -1,4 +1,3 @@
-/* eslint-disable @typescript-eslint/camelcase */
 // Copyright 2017-2019 @polkadot/app-parachains authors & contributors
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
@@ -14,24 +13,30 @@ import translate from '../translate';
 import Parachain from './Parachain';
 
 interface Props extends I18nProps {
-  parachains_parachains?: BN[];
+  parachains?: BN[];
 }
 
-function Parachains ({ parachains_parachains, t }: Props): React.ReactElement<Props> {
+function Parachains ({ parachains, t }: Props): React.ReactElement<Props> {
   return (
     <Column
       emptyText={t('no deployed parachains')}
       headerText={t('parachains')}
     >
-      {parachains_parachains && parachains_parachains.map((paraId): React.ReactNode =>
-        <Parachain key={paraId.toString()} paraId={paraId} />
-      )}
+      {parachains && parachains.map((paraId): React.ReactNode => (
+        <Parachain
+          key={paraId.toString()}
+          paraId={paraId}
+        />
+      ))}
     </Column>
   );
 }
 
 export default translate(
   withCalls<Props>(
-    'query.parachains.parachains'
+    ['query.registrar.parachains', {
+      fallbacks: ['query.parachains.parachains'],
+      propName: 'parachains'
+    }]
   )(Parachains)
 );

+ 14 - 9
packages/app-parachains/src/Overview/Summary.tsx

@@ -1,4 +1,3 @@
-/* eslint-disable @typescript-eslint/camelcase */
 // Copyright 2017-2019 @polkadot/app-parachains authors & contributors
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
@@ -14,22 +13,22 @@ import { formatNumber } from '@polkadot/util';
 import translate from '../translate';
 
 interface Props extends I18nProps {
-  parachains_nextFreeId?: BN;
-  parachains_parachains?: BN[];
+  nextFreeId?: BN;
+  parachains?: BN[];
 }
 
-function Summary ({ parachains_nextFreeId, parachains_parachains = [], t }: Props): React.ReactElement<Props> {
+function Summary ({ nextFreeId, parachains = [], t }: Props): React.ReactElement<Props> {
   return (
     <SummaryBox>
       <section>
         <CardSummary label={t('parachains')}>
-          {formatNumber(parachains_parachains.length)}
+          {formatNumber(parachains.length)}
         </CardSummary>
       </section>
-      {parachains_nextFreeId && (
+      {nextFreeId && (
         <section>
           <CardSummary label={t('next id')}>
-            {formatNumber(parachains_nextFreeId)}
+            {formatNumber(nextFreeId)}
           </CardSummary>
         </section>
       )}
@@ -39,7 +38,13 @@ function Summary ({ parachains_nextFreeId, parachains_parachains = [], t }: Prop
 
 export default translate(
   withCalls<Props>(
-    'query.parachains.nextFreeId',
-    'query.parachains.parachains'
+    ['query.registrar.nextFreeId', {
+      fallbacks: ['query.parachains.nextFreeId'],
+      propName: 'nextFreeId'
+    }],
+    ['query.registrar.parachains', {
+      fallbacks: ['query.parachains.parachains'],
+      propName: 'parachains'
+    }]
   )(Summary)
 );

+ 1 - 1
packages/app-parachains/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['parachains', 'ui']);
+export default withTranslation(['app-parachains']);

+ 4 - 4
packages/app-settings/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-settings",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "Settings management",
   "main": "index.js",
   "scripts": {},
@@ -10,9 +10,9 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65",
-    "@polkadot/react-query": "^0.36.0-beta.65",
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101",
+    "@polkadot/react-query": "^0.36.0-beta.101",
     "query-string": "^6.8.3"
   }
 }

+ 14 - 13
packages/app-settings/src/md/basics.md

@@ -6,7 +6,7 @@ Here you are able to tweak a number of the settings for your interface - where i
 
 In the general section you can setup the basic operation for your UI.
 
-- **endpoint** Defines the node to connect to. Select from one of the available hosted RPCs or connect to your own by specifying a custom URL. (Be aware that custome URLs should be behind secure WebSockets if you are using a UI hosted on a https endpoint)
+- **endpoint** Defines the node to connect to. Select from one of the available hosted RPCs or connect to your own by specifying a custom URL. (Be aware that custom URLs should be behind secure WebSockets if you are using a UI hosted on a https endpoint)
 - **theme** Swap between a Polkadot or Substrate themed interface.
 - **mode** By default the UI appears in a fully-featured mode. You can select to only have the core features (which provides all the basics, unless you are a power user).
 
@@ -14,7 +14,7 @@ In the general section you can setup the basic operation for your UI.
 
 In this section, you are able to define custom types to be added to the underlying API. When developing a custom chain, it is typical that additional types are added. Defining them here allows the underlying API (and therefore the UI) have an understanding how to encode and decode these types.
 
-Types are saved accross refreshes, so it is a single operation for all future usages of the UI. For the definitions, a simple JSON format is used, and example could be -
+Types are saved across refreshes, so it is a single operation for all future usages of the UI. For the definitions, a simple JSON format is used, and example could be -
 
 ```
 {
@@ -34,27 +34,28 @@ Types are saved accross refreshes, so it is a single operation for all future us
 }
 ```
 
-Be aware that the types are registered in the order they appear here. Since `Transaction` above requires both `TransactionInput` and `TransactionOutput` it is defined after the definitions for those are available. (Circular deps are not supported here). For a slightly more complex example, using both types and enums, the following would be used -
+Be aware that the types are registered in the order they appear here, however it does support resolution independent of the order. (Circular deps are not supported here). For a slightly more complex example, using both types, enums, tuples and Compact numbers, the following would be used -
 
 ```
 {
+  "ArrayThing": "Vec<Thing>",
+  "MyNumber": "u32",
+  "Thing": {
+    "count_enum": "SimpleEnum",
+    "type_enum": "TypeEnum",
+    "counter": "MyNumber",
+    "tuple": "(u32, u64)",
+    "ids": "Vec<AccountId>"
+  },
   "SimpleEnum": {
     "_enum": ["One", "Two", "Three"]
   },
   "TypeEnum": {
     "_enum": {
       "One": "u32",
-      "Two": "u64",
+      "Two": "Compact<u64>",
       "Three": null
     }
-  },
-  "MyNumber": "u32",
-  "Thing": {
-    "count_enum": "SimpleEnum",
-    "type_enum": "TypeEnum",
-    "counter": "MyNumber",
-    "ids": "Vec<AccountId>"
-  },
-  "ArrayThing": "Vec<Thing>"
+  }
 }
 ```

+ 1 - 1
packages/app-settings/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['settings', 'ui']);
+export default withTranslation(['app-settings']);

+ 5 - 5
packages/app-staking/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-staking",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "A basic staking app",
   "main": "index.js",
   "scripts": {},
@@ -10,9 +10,9 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/app-explorer": "^0.36.0-beta.65",
-    "@polkadot/react-components": "^0.36.0-beta.65",
-    "@polkadot/react-query": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/app-explorer": "^0.36.0-beta.101",
+    "@polkadot/react-components": "^0.36.0-beta.101",
+    "@polkadot/react-query": "^0.36.0-beta.101"
   }
 }

+ 1 - 1
packages/app-staking/src/Actions/Account/BondExtra.tsx

@@ -117,7 +117,7 @@ class BondExtra extends TxComponent<Props, State> {
             autoFocus
             className='medium'
             help={t('Amount to add to the currently bonded funds. This is adjusted using the available funds on the account.')}
-            label={t('additionnal bonded funds')}
+            label={t('additional bonded funds')}
             maxValue={maxBalance}
             onChange={this.onChangeValue}
             onEnter={this.sendTx}

+ 2 - 3
packages/app-staking/src/Actions/Account/InjectKeys.tsx

@@ -6,8 +6,7 @@ import { I18nProps } from '@polkadot/react-components/types';
 import { KeypairType } from '@polkadot/util-crypto/types';
 
 import React, { useContext, useEffect, useState } from 'react';
-import { Button, Dropdown, Icon, Input, Modal } from '@polkadot/react-components';
-import { QueueContext } from '@polkadot/react-components/Status';
+import { Button, Dropdown, Icon, Input, Modal, StatusContext } from '@polkadot/react-components';
 import keyring from '@polkadot/ui-keyring';
 import { assert, u8aToHex } from '@polkadot/util';
 import { keyExtractSuri, mnemonicValidate } from '@polkadot/util-crypto';
@@ -30,7 +29,7 @@ const CRYPTO_MAP: Record<string, KeypairType[]> = {
 const EMPTY_KEY = '0x';
 
 function InjectKeys ({ isOpen = true, onClose, t }: Props): React.ReactElement<Props> | null {
-  const { queueRpc } = useContext(QueueContext);
+  const { queueRpc } = useContext(StatusContext);
   // this needs to align with what is set as the first value in `type`
   const [crypto, setCrypto] = useState<KeypairType>('sr25519');
   const [publicKey, setPublicKey] = useState(EMPTY_KEY);

+ 17 - 4
packages/app-staking/src/Actions/Account/Nominate.tsx

@@ -19,13 +19,24 @@ interface Props extends I18nProps {
   stashOptions: KeyringSectionOption[];
 }
 
+// We only allow a maximum of 16 nominees, negative to slice
+const MAX_NOMINEES = -16;
+
 function Nominate ({ controllerId, isOpen, onClose, stashId, stashOptions, t }: Props): React.ReactElement<Props> | null {
-  const [nominees, setNominees] = useState<string[] | undefined>();
+  const [nominees, setNominees] = useState<string[]>([]);
 
   if (!isOpen) {
     return null;
   }
 
+  const _onChangeNominees = (_nominees: string[]): void => {
+    const newNominees = _nominees.slice(MAX_NOMINEES);
+
+    if (JSON.stringify(newNominees) !== JSON.stringify(nominees)) {
+      setNominees(newNominees);
+    }
+  };
+
   return (
     <Modal
       className='staking--Nominating'
@@ -51,13 +62,15 @@ function Nominate ({ controllerId, isOpen, onClose, stashId, stashOptions, t }:
         />
         <InputAddress
           className='medium'
+          help={t('Stash accounts that are to be nominated. Block rewards are split between validators and nominators. Only 16 nominees will be taken into account.')}
+          isInput={false}
           isMultiple
-          help={t('Stash accounts that are to be nominated. Block rewards are split between validators and nominators')}
           label={t('nominate the following addresses')}
-          onChangeMulti={setNominees}
+          onChangeMulti={_onChangeNominees}
           options={stashOptions}
           placeholder={t('select accounts(s) nominate')}
           type='account'
+          value={nominees}
         />
       </Modal.Content>
       <Modal.Actions>
@@ -71,7 +84,7 @@ function Nominate ({ controllerId, isOpen, onClose, stashId, stashOptions, t }:
           <Button.Or />
           <TxButton
             accountId={controllerId}
-            isDisabled={!nominees || nominees.length === 0}
+            isDisabled={nominees.length === 0}
             isPrimary
             onClick={onClose}
             params={[nominees]}

+ 13 - 9
packages/app-staking/src/Actions/Account/SetSessionAccount.tsx → packages/app-staking/src/Actions/Account/SetSessionKey.tsx

@@ -23,9 +23,12 @@ const EMPTY_PROOF = new Uint8Array();
 
 function SetSessionKey ({ controllerId, isOpen, onClose, sessionIds, stashId, t }: Props): React.ReactElement<Props> | null {
   const { isSubstrateV2 } = useContext(ApiContext);
-  const [ed25519, setEd25519] = useState<string | null>(null);
-  const [ed25519Error, setEd25519Error] = useState<string | null>(sessionIds[0] || controllerId);
-  const [keys, setKeys] = useState<string | null>(null);
+  const [keysError, setKeysError] = useState<string | null>(null);
+  const [keys, setKeys] = useState<string | null>(
+    isSubstrateV2
+      ? null
+      : sessionIds[0] || controllerId
+  );
 
   if (!isOpen) {
     return null;
@@ -33,7 +36,7 @@ function SetSessionKey ({ controllerId, isOpen, onClose, sessionIds, stashId, t
 
   const hasError = isSubstrateV2
     ? !keys
-    : (!ed25519 || !!ed25519Error);
+    : (!keys || !!keysError);
 
   return (
     <Modal
@@ -67,14 +70,15 @@ function SetSessionKey ({ controllerId, isOpen, onClose, sessionIds, stashId, t
               <InputAddress
                 className='medium'
                 help={t('Changing the key only takes effect at the start of the next session. If validating, it must be an ed25519 key.')}
+                isError={!!keysError}
                 label={t('Session key (ed25519)')}
-                onChange={setEd25519}
-                value={ed25519}
+                onChange={setKeys}
+                value={keys}
               />
               <ValidationSessionKey
                 controllerId={controllerId}
-                onError={setEd25519Error}
-                sessionId={ed25519}
+                onError={setKeysError}
+                sessionId={keys}
                 stashId={stashId}
               />
             </>
@@ -100,7 +104,7 @@ function SetSessionKey ({ controllerId, isOpen, onClose, sessionIds, stashId, t
             params={
               isSubstrateV2
                 ? [keys, EMPTY_PROOF]
-                : [ed25519]
+                : [keys]
             }
             tx={
               isSubstrateV2

+ 1 - 1
packages/app-staking/src/Actions/Account/Validate.tsx

@@ -168,7 +168,7 @@ class Validate extends TxComponent<Props, State> {
           <InputBalance
             className='medium'
             defaultValue={validatorPrefs && validatorPrefs.validatorPayment && validatorPrefs.validatorPayment.toBn()}
-            help={t('Amount taken up-front from the reward by the validator before spliting the remainder between themselves and the nominators')}
+            help={t('Amount taken up-front from the reward by the validator before splitting the remainder between themselves and the nominators')}
             label={t('reward commission')}
             onChange={this.onChangePayment}
             onEnter={this.sendTx}

+ 14 - 12
packages/app-staking/src/Actions/Account/index.tsx

@@ -20,7 +20,7 @@ import InjectKeys from './InjectKeys';
 import Nominate from './Nominate';
 import SetControllerAccount from './SetControllerAccount';
 import SetRewardDestination from './SetRewardDestination';
-import SetSessionAccount from './SetSessionAccount';
+import SetSessionKey from './SetSessionKey';
 import translate from '../../translate';
 import Unbond from './Unbond';
 import Validate from './Validate';
@@ -62,6 +62,14 @@ interface State {
   validatorPrefs?: ValidatorPrefs;
 }
 
+const DEFAULT_BALANCES = {
+  available: true,
+  bonded: false,
+  total: false,
+  redeemable: false,
+  unlocking: false
+};
+
 function toIdString (id?: AccountId | null): string | null {
   return id
     ? id.toString()
@@ -95,7 +103,7 @@ class Account extends React.PureComponent<Props, State> {
     }
 
     const { controllerId, nextSessionIds, nominators, online, offline, rewardDestination, sessionIds, stakers, stakingLedger, stashId, validatorPrefs } = staking_info;
-    const isStashNominating = nominators && nominators.length !== 0;
+    const isStashNominating = nominators && !!nominators.length;
     const _stashId = toIdString(stashId);
     const isStashValidating = !!allStashes && !!_stashId && allStashes.includes(_stashId);
 
@@ -143,13 +151,7 @@ class Account extends React.PureComponent<Props, State> {
         type='account'
         value={stashId}
         withAddressOrName
-        withBalance={{
-          available: true,
-          bonded: false,
-          free: false,
-          redeemable: false,
-          unlocking: false
-        }}
+        withBalance={DEFAULT_BALANCES}
       >
         <BondExtra
           controllerId={controllerId}
@@ -175,7 +177,7 @@ class Account extends React.PureComponent<Props, State> {
         <div className={className}>
           <div className='staking--Accounts'>
             {this.renderControllerAccount()}
-            {!isSubstrateV2 && sessionIds.length && (
+            {!isSubstrateV2 && !!sessionIds.length && (
               <div className='staking--Account-detail actions'>
                 <AddressRow
                   label={t('session')}
@@ -208,7 +210,7 @@ class Account extends React.PureComponent<Props, State> {
                 withValidatorPrefs={isStashValidating}
               />
             </div>
-            {nominees && nominees.length !== 0 && (
+            {nominees && !!nominees.length && (
               <div className='staking--Account-Nominee'>
                 <label className='staking--label'>{t('nominating')}</label>
                 {nominees.map((nomineeId, index): React.ReactNode => (
@@ -502,7 +504,7 @@ class Account extends React.PureComponent<Props, State> {
     }
 
     return (
-      <SetSessionAccount
+      <SetSessionKey
         controllerId={controllerId}
         isOpen={isSetSessionAccountOpen}
         onClose={this.toggleSetSessionAccount}

+ 143 - 156
packages/app-staking/src/Overview/Address.tsx

@@ -1,17 +1,16 @@
-/* eslint-disable @typescript-eslint/camelcase */
 // Copyright 2017-2019 @polkadot/app-staking authors & contributors
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { AccountId, Balance, BlockNumber, Exposure } from '@polkadot/types/interfaces';
+import { AccountId, Balance, BlockNumber } from '@polkadot/types/interfaces';
 import { DerivedStaking, DerivedStakingOnlineStatus } from '@polkadot/api-derive/types';
-import { ApiProps } from '@polkadot/react-api/types';
 import { I18nProps } from '@polkadot/react-components/types';
 import { ValidatorFilter } from '../types';
 
-import React from 'react';
+import BN from 'bn.js';
+import React, { useContext, useEffect, useState } from 'react';
 import styled from 'styled-components';
-import { withCalls, withMulti } from '@polkadot/react-api/with';
+import { ApiContext, withCalls, withMulti } from '@polkadot/react-api';
 import { AddressCard, AddressMini, OnlineStatus } from '@polkadot/react-components';
 import { classes } from '@polkadot/react-components/util';
 import keyring from '@polkadot/ui-keyring';
@@ -20,7 +19,7 @@ import { updateOnlineStatus } from '../util';
 
 import translate from '../translate';
 
-interface Props extends ApiProps, I18nProps {
+interface Props extends I18nProps {
   address: string;
   className?: string;
   defaultName: string;
@@ -28,171 +27,156 @@ interface Props extends ApiProps, I18nProps {
   lastAuthor: string;
   lastBlock: string;
   recentlyOnline?: Record<string, BlockNumber>;
-  staking_info?: DerivedStaking;
+  stakingInfo?: DerivedStaking;
 }
 
-interface State {
-  controllerId: string | null;
-  onlineStatus: DerivedStakingOnlineStatus;
+interface StakingState {
+  balanceOpts: { bonded: boolean | BN[] };
+  controllerId?: string;
+  hasNominators: boolean;
+  nominators: [AccountId, Balance][];
   stashActive: string | null;
   stashTotal: string | null;
-  sessionId: string | null;
-  stakers?: Exposure;
-  stashId: string | null;
-  badgeExpanded: boolean;
+  sessionId?: string;
+  stashId?: string;
 }
 
-class Address extends React.PureComponent<Props, State> {
-  public state: State;
-
-  public constructor (props: Props) {
-    super(props);
-
-    this.state = {
-      controllerId: null,
-      onlineStatus: {},
-      sessionId: null,
-      stashActive: null,
-      stashId: null,
-      stashTotal: null,
-      badgeExpanded: false
-    };
-  }
+interface OnlineState {
+  hasOfflineWarnings: boolean;
+  onlineStatus: DerivedStakingOnlineStatus;
+}
 
-  public static getDerivedStateFromProps ({ recentlyOnline = {}, staking_info }: Props, prevState: State): Pick<State, never> | null {
-    if (!staking_info) {
-      return null;
+const WITH_VALIDATOR_PREFS = { validatorPayment: true };
+
+function Address ({ address, className, defaultName, filter, lastAuthor, lastBlock, recentlyOnline, stakingInfo, t }: Props): React.ReactElement<Props> | null {
+  const { isSubstrateV2 } = useContext(ApiContext);
+  const [isNominatorMe, seIsNominatorMe] = useState(false);
+  const [{ hasOfflineWarnings, onlineStatus }, setOnlineStatus] = useState<OnlineState>({
+    hasOfflineWarnings: false,
+    onlineStatus: {}
+  });
+  const [{ balanceOpts, controllerId, hasNominators, nominators, sessionId, stashId }, setStakingState] = useState<StakingState>({
+    balanceOpts: { bonded: true },
+    hasNominators: false,
+    nominators: [],
+    stashActive: null,
+    stashTotal: null
+  });
+
+  useEffect((): void => {
+    if (stakingInfo) {
+      const { controllerId, nextSessionId, stakers, stakingLedger, stashId } = stakingInfo;
+      const nominators = stakers
+        ? stakers.others.map(({ who, value }): [AccountId, Balance] => [who, value.unwrap()])
+        : [];
+      const myAccounts = keyring.getAccounts().map(({ address }): string => address);
+
+      seIsNominatorMe(nominators.some(([who]): boolean =>
+        myAccounts.includes(who.toString())
+      ));
+      setStakingState({
+        balanceOpts: {
+          bonded: stakers && !stakers.own.isEmpty
+            ? [stakers.own.unwrap(), stakers.total.unwrap().sub(stakers.own.unwrap())]
+            : true
+        },
+        controllerId: controllerId && controllerId.toString(),
+        hasNominators: nominators.length !== 0,
+        nominators,
+        sessionId: nextSessionId && nextSessionId.toString(),
+        stashActive: stakingLedger
+          ? formatBalance(stakingLedger.active)
+          : null,
+        stashId: stashId && stashId.toString(),
+        stashTotal: stakingLedger
+          ? formatBalance(stakingLedger.total)
+          : null
+      });
     }
+  }, [stakingInfo]);
 
-    const { controllerId, nextSessionId, online, offline, stakers, stakingLedger, stashId } = staking_info;
-
-    return {
-      controllerId: controllerId && controllerId.toString(),
-      onlineStatus: updateOnlineStatus(recentlyOnline)(staking_info.sessionIds || null, { offline, online }),
-      sessionId: nextSessionId && nextSessionId.toString(),
-      stashActive: stakingLedger
-        ? formatBalance(stakingLedger.active)
-        : prevState.stashActive,
-      stakers,
-      stashId: stashId && stashId.toString(),
-      stashTotal: stakingLedger
-        ? formatBalance(stakingLedger.total)
-        : prevState.stashTotal
-    };
-  }
+  useEffect((): void => {
+    if (stakingInfo) {
+      const { online, offline, sessionIds, stashId } = stakingInfo;
+      const onlineStatus = updateOnlineStatus(recentlyOnline || {})(sessionIds, { offline, online });
 
-  public render (): React.ReactNode {
-    const { address, className, defaultName, filter, isSubstrateV2, lastAuthor, lastBlock, t } = this.props;
-    const { controllerId, onlineStatus, sessionId, stakers, stashId } = this.state;
-    const bonded = stakers && !stakers.own.isEmpty
-      ? [stakers.own.unwrap(), stakers.total.unwrap().sub(stakers.own.unwrap())]
-      : true;
-
-    if ((filter === 'hasNominators' && !this.hasNominators()) ||
-      (filter === 'noNominators' && this.hasNominators()) ||
-      (filter === 'hasWarnings' && !this.hasWarnings()) ||
-      (filter === 'noWarnings' && this.hasWarnings()) ||
-      (filter === 'iNominated' && !this.iNominated())) {
-      return null;
+      setOnlineStatus({
+        hasOfflineWarnings: !!(stashId && onlineStatus.offline && onlineStatus.offline.length),
+        onlineStatus
+      });
     }
-
-    const isAuthor = !!lastBlock && !!lastAuthor && [address, controllerId, stashId].includes(lastAuthor);
-    const nominators = this.getNominators();
-
-    return (
-      <AddressCard
-        buttons={
-          <div className='staking--Address-info'>
-            {isAuthor && (
-              <div className={classes(isSubstrateV2 ? 'blockNumberV2' : 'blockNumberV1')}>#{lastBlock}</div>
-            )}
-            {controllerId && (
-              <div>
-                <label className={classes('staking--label', isSubstrateV2 && !isAuthor && 'controllerSpacer')}>{t('controller')}</label>
-                <AddressMini value={controllerId} />
-              </div>
-            )}
-            {!isSubstrateV2 && sessionId && (
-              <div>
-                <label className='staking--label'>{t('session')}</label>
-                <AddressMini value={sessionId} />
-              </div>
-            )}
-          </div>
-        }
-        className={className}
-        defaultName={defaultName}
-        iconInfo={controllerId && onlineStatus && (
-          <OnlineStatus
-            accountId={controllerId}
-            value={onlineStatus}
-            tooltip
-          />
-        )}
-        key={stashId || controllerId || undefined}
-        value={stashId || address}
-        withBalance={{ bonded }}
-        withValidatorPrefs={{ validatorPayment: true }}
-      >
-        {nominators.length !== 0 && (
-          <details>
-            <summary>
-              {t('Nominators ({{count}})', {
-                replace: {
-                  count: nominators.length
-                }
-              })}
-            </summary>
-            {nominators.map(([who, bonded]): React.ReactNode =>
-              <AddressMini
-                bonded={bonded}
-                key={who.toString()}
-                value={who}
-                withBonded
-              />
-            )}
-          </details>
-        )}
-      </AddressCard>
-    );
-  }
-
-  private getNominators (): [AccountId, Balance][] {
-    const { stakers } = this.state;
-
-    return stakers
-      ? stakers.others.map(({ who, value }): [AccountId, Balance] => [who, value.unwrap()])
-      : [];
-  }
-
-  private iNominated (): boolean {
-    const nominators = this.getNominators();
-    const myAddresses = keyring.getAccounts().map(({ address }): string => address);
-
-    return nominators.some(([who]): boolean =>
-      myAddresses.includes(who.toString())
-    );
-  }
-
-  private hasNominators (): boolean {
-    const nominators = this.getNominators();
-
-    return !!nominators.length;
+  }, [recentlyOnline, stakingInfo]);
+
+  if ((filter === 'hasNominators' && !hasNominators) ||
+    (filter === 'noNominators' && hasNominators) ||
+    (filter === 'hasWarnings' && !hasOfflineWarnings) ||
+    (filter === 'noWarnings' && hasOfflineWarnings) ||
+    (filter === 'iNominated' && !isNominatorMe)) {
+    return null;
   }
 
-  private hasWarnings (): boolean {
-    const { stashId, onlineStatus } = this.state;
-
-    if (!stashId || !onlineStatus.offline || !onlineStatus.offline.length) {
-      return false;
-    }
-
-    return true;
-  }
+  const isAuthor = !!lastBlock && !!lastAuthor && [address, controllerId, stashId].includes(lastAuthor);
+
+  return (
+    <AddressCard
+      buttons={
+        <div className='staking--Address-info'>
+          {isAuthor && (
+            <div className={classes(isSubstrateV2 ? 'blockNumberV2' : 'blockNumberV1')}>#{lastBlock}</div>
+          )}
+          {controllerId && (
+            <div>
+              <label className={classes('staking--label', isSubstrateV2 && !isAuthor && 'controllerSpacer')}>{t('controller')}</label>
+              <AddressMini value={controllerId} />
+            </div>
+          )}
+          {!isSubstrateV2 && sessionId && (
+            <div>
+              <label className='staking--label'>{t('session')}</label>
+              <AddressMini value={sessionId} />
+            </div>
+          )}
+        </div>
+      }
+      className={className}
+      defaultName={defaultName}
+      iconInfo={controllerId && onlineStatus && (
+        <OnlineStatus
+          accountId={controllerId}
+          value={onlineStatus}
+          tooltip
+        />
+      )}
+      key={stashId || controllerId || undefined}
+      value={stashId || address}
+      withBalance={balanceOpts}
+      withValidatorPrefs={WITH_VALIDATOR_PREFS}
+    >
+      {hasNominators && (
+        <details>
+          <summary>
+            {t('Nominators ({{count}})', {
+              replace: {
+                count: nominators.length
+              }
+            })}
+          </summary>
+          {nominators.map(([who, bonded]): React.ReactNode =>
+            <AddressMini
+              bonded={bonded}
+              key={who.toString()}
+              value={who}
+              withBonded
+            />
+          )}
+        </details>
+      )}
+    </AddressCard>
+  );
 }
 
 export default withMulti(
-  styled(Address as React.ComponentClass<Props>)`
+  styled(Address)`
     .blockNumberV1,
     .blockNumberV2 {
       background: #3f3f3f;
@@ -235,6 +219,9 @@ export default withMulti(
   `,
   translate,
   withCalls<Props>(
-    ['derive.staking.info', { paramName: 'address' }]
+    ['derive.staking.info', {
+      paramName: 'address',
+      propName: 'stakingInfo'
+    }]
   )
 );

+ 45 - 52
packages/app-staking/src/index.tsx

@@ -2,17 +2,16 @@
 // This software may be modified and distributed under the terms
 // of the Apache-2.0 license. See the LICENSE file for details.
 
-import { AccountId, AuthorityId, BlockNumber, EventRecord } from '@polkadot/types/interfaces';
+import { AccountId, BlockNumber, EventRecord } from '@polkadot/types/interfaces';
 import { AppProps, I18nProps } from '@polkadot/react-components/types';
 import { ApiProps } from '@polkadot/react-api/types';
 import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
 import { ComponentProps } from './types';
 
-import BN from 'bn.js';
-import React, { useEffect, useState } from 'react';
+import React, { useEffect, useReducer } from 'react';
 import { Route, Switch } from 'react-router';
 import styled from 'styled-components';
-import { createType, Option } from '@polkadot/types';
+import { Option } from '@polkadot/types';
 import { HelpOverlay } from '@polkadot/react-components';
 import Tabs from '@polkadot/react-components/Tabs';
 import { withCalls, withMulti, withObservable } from '@polkadot/react-api';
@@ -25,55 +24,37 @@ import translate from './translate';
 
 interface Props extends AppProps, ApiProps, I18nProps {
   allAccounts?: SubjectInfo;
-  allStashesAndControllers?: [AccountId[], Option<AccountId>[]];
+  allStashesAndControllers?: [string[], string[]];
   bestNumber?: BlockNumber;
-  currentValidatorsControllersV1OrStashesV2?: AccountId[];
-  recentlyOnline?: AuthorityId[];
+  currentValidators?: string[];
+  recentlyOnline?: string[];
 }
 
-interface State {
-  allControllers: string[];
-  allStashes: string[];
-  currentValidators: string[];
-}
-
-function App ({ allAccounts, allStashesAndControllers, bestNumber, className, currentValidatorsControllersV1OrStashesV2, basePath, recentlyOnline: propsRecentlyOnline, t }: Props): React.ReactElement<Props> {
-  const [{ allControllers, allStashes, currentValidators }, setState] = useState<State>({
-    allControllers: [],
-    allStashes: [],
-    currentValidators: []
-  });
-  const [recentlyOnline, setRecentlyOnline] = useState<Record<string, BlockNumber>>({});
+const EMPY_ACCOUNTS: string[] = [];
+const EMPTY_ALL: [string[], string[]] = [EMPY_ACCOUNTS, EMPY_ACCOUNTS];
 
-  useEffect((): void => {
-    const [_stashes, _controllers] = (allStashesAndControllers || [[], []]);
-    const _validators = currentValidatorsControllersV1OrStashesV2 || [];
+function offlineReducer (prev: Record<string, BlockNumber>, { bestNumber, recentlyOnline }: { bestNumber: BlockNumber; recentlyOnline: string[] }): Record<string, BlockNumber> {
+  return {
+    ...prev,
+    ...recentlyOnline.reduce(
+      (result: Record<string, BlockNumber>, authorityId): Record<string, BlockNumber> => ({
+        ...result,
+        [authorityId]: bestNumber
+      }),
+      {}
+    )
+  };
+}
 
-    setState({
-      allControllers: _controllers
-        .filter((optId): boolean => optId.isSome)
-        .map((accountId): string => accountId.unwrap().toString()),
-      allStashes: _stashes
-        .filter((): boolean => true)
-        .map((accountId): string => accountId.toString()),
-      currentValidators: _validators.map((authorityId): string =>
-        authorityId.toString()
-      )
-    });
-  }, [allStashesAndControllers, currentValidatorsControllersV1OrStashesV2]);
+function App ({ allAccounts, allStashesAndControllers: [allStashes, allControllers] = EMPTY_ALL, bestNumber, className, currentValidators = EMPY_ACCOUNTS, basePath, recentlyOnline, t }: Props): React.ReactElement<Props> {
+  const [online, dispatchOffline] = useReducer(offlineReducer, {});
 
+  // dispatch a combinator for the new recentlyOnline events
   useEffect((): void => {
-    setRecentlyOnline({
-      ...(recentlyOnline || {}),
-      ...(propsRecentlyOnline || []).reduce(
-        (result: Record<string, BlockNumber>, authorityId): Record<string, BlockNumber> => ({
-          ...result,
-          [authorityId.toString()]: bestNumber || createType('BlockNumber', new BN(0))
-        }),
-        {}
-      )
-    });
-  }, [bestNumber, propsRecentlyOnline]);
+    if (bestNumber && recentlyOnline && recentlyOnline.length) {
+      dispatchOffline({ bestNumber, recentlyOnline });
+    }
+  }, [bestNumber, recentlyOnline]);
 
   const _renderComponent = (Component: React.ComponentType<ComponentProps>): () => React.ReactNode => {
     // eslint-disable-next-line react/display-name
@@ -88,7 +69,7 @@ function App ({ allAccounts, allStashesAndControllers, bestNumber, className, cu
           allControllers={allControllers}
           allStashes={allStashes}
           currentValidators={currentValidators}
-          recentlyOnline={recentlyOnline}
+          recentlyOnline={online}
         />
       );
     };
@@ -135,17 +116,29 @@ export default withMulti(
   translate,
   withCalls<Props>(
     ['derive.chain.bestNumber', { propName: 'bestNumber' }],
-    ['derive.staking.controllers', { propName: 'allStashesAndControllers' }],
-    ['query.session.validators', { propName: 'currentValidatorsControllersV1OrStashesV2' }],
+    ['derive.staking.controllers', {
+      propName: 'allStashesAndControllers',
+      transform: ([stashes, controllers]: [AccountId[], Option<AccountId>[]]): [string[], string[]] => [
+        stashes.map((accountId): string => accountId.toString()),
+        controllers
+          .filter((optId): boolean => optId.isSome)
+          .map((accountId): string => accountId.unwrap().toString())
+      ]
+    }],
+    ['query.session.validators', {
+      propName: 'currentValidators',
+      transform: (validators: AccountId[]): string[] =>
+        validators.map((accountId): string => accountId.toString())
+    }],
     ['query.system.events', {
       propName: 'recentlyOnline',
-      transform: (value?: EventRecord[]): AuthorityId[] =>
+      transform: (value?: EventRecord[]): string[] =>
         (value || [])
           .filter(({ event: { method, section } }): boolean =>
             section === 'imOnline' && method === 'HeartbeatReceived'
           )
-          .map(({ event: { data: [authorityId] } }): AuthorityId =>
-            authorityId as AuthorityId
+          .map(({ event: { data: [authorityId] } }): string =>
+            authorityId.toString()
           )
     }]
   ),

+ 1 - 1
packages/app-staking/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['staking', 'ui']);
+export default withTranslation(['app-staking']);

+ 4 - 4
packages/app-storage/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-storage",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "main": "index.js",
   "repository": "https://github.com/polkadot-js/apps.git",
   "author": "Jaco Greeff <jacogr@gmail.com>",
@@ -10,8 +10,8 @@
   "contributors": [],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65",
-    "@polkadot/react-params": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101",
+    "@polkadot/react-params": "^0.36.0-beta.101"
   }
 }

+ 1 - 1
packages/app-storage/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['storage', 'ui']);
+export default withTranslation(['app-storage']);

+ 3 - 3
packages/app-sudo/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-sudo",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "A basic app that shows the ropes on customisation",
   "main": "index.js",
   "scripts": {},
@@ -11,7 +11,7 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101"
   }
 }

+ 1 - 1
packages/app-sudo/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['sudo', 'ui']);
+export default withTranslation(['app-sudo']);

+ 3 - 3
packages/app-toolbox/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-toolbox",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "main": "index.js",
   "repository": "https://github.com/polkadot-js/apps.git",
   "author": "Jaco Greeff <jacogr@gmail.com>",
@@ -10,7 +10,7 @@
   "contributors": [],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101"
   }
 }

+ 1 - 1
packages/app-toolbox/src/Rpc/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['rpc', 'ui']);
+export default withTranslation(['app-toolbox']);

+ 126 - 174
packages/app-toolbox/src/Verify.tsx

@@ -5,205 +5,157 @@
 import { I18nProps as Props } from '@polkadot/react-components/types';
 import { KeypairType } from '@polkadot/util-crypto/types';
 
-import React from 'react';
+import React, { useEffect, useState } from 'react';
 import { Dropdown, Icon, Input, InputAddress, Static } from '@polkadot/react-components';
 import keyring from '@polkadot/ui-keyring';
 import uiSettings from '@polkadot/ui-settings';
 import { isHex } from '@polkadot/util';
 import { naclVerify, schnorrkelVerify } from '@polkadot/util-crypto';
+import styled from 'styled-components';
 
 import translate from './translate';
 
 type CryptoTypes = KeypairType | 'unknown';
 
-interface CryptoOption {
-  text: string;
-  value: string;
-}
-
-interface State {
-  currentPublicKey: Uint8Array | null;
-  cryptoOptions: CryptoOption[];
-  cryptoType: CryptoTypes;
-  defaultPublicKey?: Uint8Array;
-  data: string;
-  isHexData: boolean;
-  isValidAddress: boolean;
-  isValidSignature: boolean;
-  isValid: boolean;
-  signature: string;
-}
-
-class Verify extends React.PureComponent<Props, State> {
-  public state: State;
-
-  public constructor (props: Props) {
-    super(props);
-
-    const { t } = this.props;
-    const pairs = keyring.getPairs();
-    const currentPair = pairs[0];
-    const currentPublicKey = currentPair
-      ? currentPair.publicKey
-      : null;
-
-    this.state = {
-      cryptoOptions: [{ value: 'unknown', text: t('Crypto not detected') }].concat(uiSettings.availableCryptos as any[]),
-      cryptoType: 'unknown',
-      currentPublicKey,
-      defaultPublicKey: currentPublicKey || undefined,
-      data: '',
-      isHexData: false,
-      isValidAddress: !!currentPair,
-      isValidSignature: false,
-      isValid: false,
-      signature: ''
-    };
+const AlignedIcon = styled(Icon)`
+  &&&::before {
+    position: relative;
+    left: 0.88rem;
+    top: 1rem;
+    width: 32px;
+    height: 32px;
+    font-size: 32px;
+    background: white !important;
+    border-radius: 50%;
   }
 
-  public render (): React.ReactNode {
-    const { t } = this.props;
-    const { cryptoOptions, cryptoType, data, defaultPublicKey, isHexData, isValid, isValidAddress, isValidSignature, signature } = this.state;
-
-    return (
-      <div className='toolbox--Verify'>
-        <div className='ui--row'>
-          <InputAddress
-            className='full'
-            defaultValue={defaultPublicKey}
-            help={t('The account that signed the input')}
-            isError={!isValidAddress}
-            isInput
-            label={t('verify using address')}
-            onChange={this.onChangeAddress}
-          />
-        </div>
-        <div className='ui--row'>
-          <Input
-            autoFocus
-            className='full'
-            help={t('The data that was signed. This is used in combination with the signature for the verification. It can either be hex or a string.')}
-            label={t('using the following data')}
-            onChange={this.onChangeData}
-            value={data}
-          />
-        </div>
-        <div className='ui--row'>
-          <Input
-            className='full'
-            icon={
-              <Icon
-                color={isValid ? 'green' : (isValidSignature ? 'red' : undefined)}
-                name={isValid ? 'check circle' : (isValidSignature ? 'exclamation circle' : 'help circle')}
-                size='big'
-              />
-            }
-            isError={!isValidSignature}
-            help={t('The signature as by the account being checked, supplied as a hex-formatted string.')}
-            label={t('the supplied signature')}
-            onChange={this.onChangeSignature}
-            value={signature}
-          />
-        </div>
-        <div className='ui--row'>
-          <Dropdown
-            defaultValue={cryptoType}
-            help={t('Cryptography used to create this signature. It is auto-detected on valid signatures.')}
-            isDisabled
-            label={t('signature crypto type')}
-            options={cryptoOptions}
-          />
-          <Static
-            className='medium'
-            help={t('Detection on the input string to determine if it is hex or non-hex.')}
-            label={t('hex input data')}
-            value={
-              isHexData
-                ? t('Yes')
-                : t('No')
-            }
-          />
-        </div>
-      </div>
-    );
+  &&&.big.icon {
+    font-size: 32px;
   }
+`;
+
+function Verify ({ t }: Props): React.ReactElement<Props> {
+  const [{ cryptoType, isValid }, setValidity] = useState<{ cryptoType: CryptoTypes; isValid: boolean }>({ cryptoType: 'unknown', isValid: false });
+  const [{ data, isHexData }, setData] = useState<{ data: string; isHexData: boolean }>({ data: '', isHexData: false });
+  const [{ publicKey, isValidPk }, setPublicKey] = useState<{ publicKey: Uint8Array | null; isValidPk: boolean }>({ publicKey: null, isValidPk: false });
+  const [{ signature, isValidSignature }, setSignature] = useState<{ signature: string; isValidSignature: boolean }>({ signature: '', isValidSignature: false });
+  const [cryptoOptions] = useState([{ value: 'unknown', text: t('Crypto not detected') }].concat(uiSettings.availableCryptos as any[]));
+
+  useEffect((): void => {
+    let cryptoType: CryptoTypes = 'unknown';
+    let isValid = isValidPk && isValidSignature;
+
+    // We cannot just use the keyring verify since it may be an address. So here we first check
+    // for ed25519, if not valid, we try against sr25519 - if neither are valid, well, we have
+    // not been able to validate the signature
+    if (isValid && publicKey) {
+      let isValidSr = false;
+      let isValidEd = false;
+
+      try {
+        isValidEd = naclVerify(data, signature, publicKey);
+      } catch (error) {
+        // do nothing, already set to false
+      }
 
-  private nextState (newState: Partial<State>): void {
-    this.setState(
-      (prevState: State): Pick<State, never> => {
-        const { isHexData = prevState.isHexData, isValidAddress = prevState.isValidAddress, isValidSignature = prevState.isValidSignature, currentPublicKey = prevState.currentPublicKey, data = prevState.data, signature = prevState.signature } = newState;
-        let cryptoType: CryptoTypes = 'unknown';
-        let isValid = isValidAddress && isValidSignature;
-
-        // We cannot just use the keyring verify since it may be an address. So here we first check
-        // for ed25519, if not valid, we try against sr25519 - if neither are valid, well, we have
-        // not been able to validate the signature
-        if (isValid && currentPublicKey) {
-          let isValidSr = false;
-          let isValidEd = false;
-
-          try {
-            isValidEd = naclVerify(data, signature, currentPublicKey);
-          } catch (error) {
-            // do nothing, already set to false
-          }
-
-          if (isValidEd) {
-            cryptoType = 'ed25519';
-          } else {
-            try {
-              isValidSr = schnorrkelVerify(data, signature, currentPublicKey);
-            } catch (error) {
-              // do nothing, already set to false
-            }
-
-            if (isValidSr) {
-              cryptoType = 'sr25519';
-            } else {
-              isValid = false;
-            }
-          }
+      if (isValidEd) {
+        cryptoType = 'ed25519';
+      } else {
+        try {
+          isValidSr = schnorrkelVerify(data, signature, publicKey);
+        } catch (error) {
+          // do nothing, already set to false
         }
 
-        return {
-          cryptoType,
-          isHexData,
-          isValid,
-          isValidAddress,
-          isValidSignature,
-          currentPublicKey,
-          data,
-          signature
-        };
+        if (isValidSr) {
+          cryptoType = 'sr25519';
+        } else {
+          isValid = false;
+        }
       }
-    );
-  }
-
-  private onChangeData = (data: string): void => {
-    const isHexData = isHex(data);
-
-    this.nextState({ data, isHexData });
-  }
-
-  private onChangeSignature = (signature: string): void => {
-    const isValidSignature = isHex(signature) && signature.length === 130;
+    }
 
-    this.nextState({ signature, isValidSignature });
-  }
+    setValidity({ cryptoType, isValid });
+  }, [data, isValidPk, isValidSignature, publicKey, signature]);
 
-  private onChangeAddress = (accountId: string | null): void => {
-    let currentPublicKey;
+  const _onChangeAddress = (accountId: string | null): void => {
+    let publicKey: Uint8Array | null = null;
 
     try {
-      currentPublicKey = keyring.decodeAddress(accountId || '');
+      publicKey = keyring.decodeAddress(accountId || '');
     } catch (err) {
       console.error(err);
     }
 
-    const isValidAddress = currentPublicKey && currentPublicKey.length === 32;
-
-    this.nextState({ currentPublicKey, isValidAddress });
-  }
+    setPublicKey({ publicKey, isValidPk: !!publicKey && publicKey.length === 32 });
+  };
+
+  const _onChangeData = (data: string): void =>
+    setData({ data, isHexData: isHex(data) });
+
+  const _onChangeSignature = (signature: string): void =>
+    setSignature({ signature, isValidSignature: isHex(signature) && signature.length === 130 });
+
+  return (
+    <div className='toolbox--Verify'>
+      <div className='ui--row'>
+        <InputAddress
+          className='full'
+          help={t('The account that signed the input')}
+          isError={!isValidPk}
+          isInput
+          label={t('verify using address')}
+          onChange={_onChangeAddress}
+        />
+      </div>
+      <div className='ui--row'>
+        <Input
+          autoFocus
+          className='full'
+          help={t('The data that was signed. This is used in combination with the signature for the verification. It can either be hex or a string.')}
+          label={t('using the following data')}
+          onChange={_onChangeData}
+          value={data}
+        />
+      </div>
+      <div className='ui--row'>
+        <div className="ui--AlignedIconContainer" style={{ position: 'absolute', zIndex: 1 }}>
+          <AlignedIcon
+            color={isValid ? 'green' : (isValidSignature ? 'red' : undefined)}
+            name={isValid ? 'check circle' : (isValidSignature ? 'exclamation circle' : 'help circle')}
+            size="big"
+          />
+        </div>
+        <Input
+          className='full'
+          isError={!isValidSignature}
+          help={t('The signature as by the account being checked, supplied as a hex-formatted string.')}
+          label={t('the supplied signature')}
+          onChange={_onChangeSignature}
+          value={signature}
+        />
+      </div>
+      <div className='ui--row'>
+        <Dropdown
+          defaultValue={cryptoType}
+          help={t('Cryptography used to create this signature. It is auto-detected on valid signatures.')}
+          isDisabled
+          label={t('signature crypto type')}
+          options={cryptoOptions}
+        />
+        <Static
+          className='medium'
+          help={t('Detection on the input string to determine if it is hex or non-hex.')}
+          label={t('hex input data')}
+          value={
+            isHexData
+              ? t('Yes')
+              : t('No')
+          }
+        />
+      </div>
+    </div>
+  );
 }
 
 export default translate(Verify);

+ 1 - 1
packages/app-toolbox/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['toolbox', 'ui']);
+export default withTranslation(['app-toolbox']);

+ 4 - 4
packages/app-transfer/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-transfer",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "A basic transfer app",
   "main": "index.js",
   "scripts": {},
@@ -10,8 +10,8 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65",
-    "@polkadot/react-query": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101",
+    "@polkadot/react-query": "^0.36.0-beta.101"
   }
 }

+ 4 - 4
packages/app-treasury/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/app-treasury",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "A referendum & proposal app",
   "main": "index.js",
   "scripts": {},
@@ -11,8 +11,8 @@
   ],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65",
-    "@polkadot/react-query": "^0.36.0-beta.65"
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101",
+    "@polkadot/react-query": "^0.36.0-beta.101"
   }
 }

+ 1 - 1
packages/app-treasury/src/translate.ts

@@ -4,4 +4,4 @@
 
 import { withTranslation } from 'react-i18next';
 
-export default withTranslation(['democracy', 'ui']);
+export default withTranslation(['app-treasury']);

+ 2 - 2
packages/apps-routing/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/apps-routing",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "main": "index.js",
   "repository": "https://github.com/polkadot-js/apps.git",
   "author": "Jaco Greeff <jacogr@gmail.com>",
@@ -10,6 +10,6 @@
   "contributors": [],
   "license": "Apache-2.0",
   "dependencies": {
-    "@babel/runtime": "^7.6.2"
+    "@babel/runtime": "^7.6.3"
   }
 }

+ 5 - 5
packages/apps/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@polkadot/apps",
-  "version": "0.36.0-beta.65",
+  "version": "0.36.0-beta.101",
   "description": "An Apps portal into the Polkadot network",
   "main": "index.js",
   "homepage": ".",
@@ -13,10 +13,10 @@
   "license": "Apache-2.0",
   "dependencies": {
     "@babel/polyfill": "^7.6.0",
-    "@babel/runtime": "^7.6.2",
-    "@polkadot/react-components": "^0.36.0-beta.65",
-    "@polkadot/react-signer": "^0.36.0-beta.65",
-    "@polkadot/ui-assets": "^0.46.0-beta.0",
+    "@babel/runtime": "^7.6.3",
+    "@polkadot/react-components": "^0.36.0-beta.101",
+    "@polkadot/react-signer": "^0.36.0-beta.101",
+    "@polkadot/ui-assets": "^0.46.0-beta.16",
     "query-string": "^6.8.3"
   }
 }

+ 0 - 0
packages/apps/public/locales/en/accounts.json → packages/apps/public/locales/en/app-123code.json


+ 84 - 0
packages/apps/public/locales/en/app-accounts.json

@@ -0,0 +1,84 @@
+{
+  "account forgotten": "account forgotten",
+  "Forget this account": "Forget this account",
+  "Create a backup file for this account": "Create a backup file for this account",
+  "Change this account's password": "Change this account's password",
+  "send": "send",
+  "Send funds from this account": "Send funds from this account",
+  "It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -": "It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -",
+  "Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.": "Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.",
+  "Learn more...": "Learn more...",
+  "My accounts": "My accounts",
+  "Vanity address": "Vanity address",
+  "account backed up": "account backed up",
+  "Backup account": "Backup account",
+  "Cancel": "Cancel",
+  "Download": "Download",
+  "An encrypted backup file will be created once you have pressed the \"Download\" button. This can be used to re-import your account on any other machine.": "An encrypted backup file will be created once you have pressed the \"Download\" button. This can be used to re-import your account on any other machine.",
+  "Save this backup file in a secure location. Additionally, the password associated with this account is needed together with this backup file in order to restore your account.": "Save this backup file in a secure location. Additionally, the password associated with this account is needed together with this backup file in order to restore your account.",
+  "The account password as specified when creating the account. This is used to encrypt the backup file and subsequently decrypt it when restoring the account.": "The account password as specified when creating the account. This is used to encrypt the backup file and subsequently decrypt it when restoring the account.",
+  "password": "password",
+  "password changed": "password changed",
+  "Change account password": "Change account password",
+  "Change": "Change",
+  "This will apply to any future use of this account as stored on this browser. Ensure that you securely store this new password and that it is strong and unique to the account.": "This will apply to any future use of this account as stored on this browser. Ensure that you securely store this new password and that it is strong and unique to the account.",
+  "The existing account password as specified when this account was created or when it was last changed.": "The existing account password as specified when this account was created or when it was last changed.",
+  "your current password": "your current password",
+  "The new account password. Once set, all future account unlocks will be performed with this new password.": "The new account password. Once set, all future account unlocks will be performed with this new password.",
+  "your new password": "your new password",
+  "created account": "created account",
+  "Add an account via seed": "Add an account via seed",
+  "Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g \"name_you_want - stash\".": "Name given to this account. You can edit it. To use the account to validate or nominate, it is a good practice to append the function of the account in the name, e.g \"name_you_want - stash\".",
+  "name": "name",
+  "The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the \"--key\" parameter of your node.": "The private key for your account is derived from this seed. This seed must be kept secret as anyone in its possession has access to the funds of this account. If you validate, use the seed of the session account as the \"--key\" parameter of your node.",
+  "mnemonic seed": "mnemonic seed",
+  "development seed": "development seed",
+  "seed (hex or string)": "seed (hex or string)",
+  "Development": "Development",
+  "Mnemonic": "Mnemonic",
+  "Raw seed": "Raw seed",
+  "This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).": "This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).",
+  "Advanced creation options": "Advanced creation options",
+  "Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use \"ed25519\".": "Determines what cryptography will be used to create this account. Note that to validate on Polkadot, the session account must use \"ed25519\".",
+  "keypair crypto type": "keypair crypto type",
+  "You can set a custom derivation path for this account using the following syntax \"/<soft-key>//<hard-key>\". The \"/<soft-key>\" and \"//<hard-key>\" may be repeated and mixed`.": "You can set a custom derivation path for this account using the following syntax \"/<soft-key>//<hard-key>\". The \"/<soft-key>\" and \"//<hard-key>\" may be repeated and mixed`.",
+  "secret derivation path": "secret derivation path",
+  "Save": "Save",
+  "Important notice": "Important notice",
+  "We will provide you with a generated backup file after your account is created. As long as you have access to your account you can always download this file later by clicking on \"Backup\" button from the Accounts section.": "We will provide you with a generated backup file after your account is created. As long as you have access to your account you can always download this file later by clicking on \"Backup\" button from the Accounts section.",
+  "Please make sure to save this file in a secure location as it is required, together with your password, to restore your account.": "Please make sure to save this file in a secure location as it is required, together with your password, to restore your account.",
+  "Create and backup account": "Create and backup account",
+  "account restored": "account restored",
+  "Add via backup file": "Add via backup file",
+  "Restore": "Restore",
+  "Select the JSON key file that was downloaded when you created the account. This JSON file contains your private key encrypted with your password.": "Select the JSON key file that was downloaded when you created the account. This JSON file contains your private key encrypted with your password.",
+  "backup file": "backup file",
+  "Type the password chosen at the account creation. It was used to encrypt your account's private key in the backup file.": "Type the password chosen at the account creation. It was used to encrypt your account's private key in the backup file.",
+  "Add account via Qr": "Add account via Qr",
+  "Name given to this account. You can change it at any point in the future.": "Name given to this account. You can change it at any point in the future.",
+  "Create": "Create",
+  "available ": "available ",
+  "Send funds": "Send funds",
+  "The account you will send funds from.": "The account you will send funds from.",
+  "send from account": "send from account",
+  "Select a contact or paste the address you want to send funds to.": "Select a contact or paste the address you want to send funds to.",
+  "send to address": "send to address",
+  "Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.": "Type the amount you want to transfer. Note that you can select the unit on the right e.g sending 1 milli is equivalent to sending 0.001.",
+  "amount": "amount",
+  "Make Transfer": "Make Transfer",
+  "Add account": "Add account",
+  "Restore JSON": "Restore JSON",
+  "Add via Qr": "Add via Qr",
+  "Query Ledger": "Query Ledger",
+  "No account yet?": "No account yet?",
+  "Stop generation": "Stop generation",
+  "Start generation": "Start generation",
+  "Type here what you would like your address to contain. This tool will generate the keys and show the associated addresses that best match your search. You can use \"?\" as a wildcard for a character.": "Type here what you would like your address to contain. This tool will generate the keys and show the associated addresses that best match your search. You can use \"?\" as a wildcard for a character.",
+  "Search for": "Search for",
+  "Should the search be case sensitive, e.g if you select \"no\" your search for \"Some\" may return addresses containing \"somE\" or \"sOme\"...": "Should the search be case sensitive, e.g if you select \"no\" your search for \"Some\" may return addresses containing \"somE\" or \"sOme\"...",
+  "case sensitive": "case sensitive",
+  "Evaluated {{count}} keys in {{elapsed}}s ({{avg}} keys/s)": "Evaluated {{count}} keys in {{elapsed}}s ({{avg}} keys/s)",
+  "You can set a custom derivation path for this account using the following syntax \"/<soft-key>//<hard-key>///<password>\". The \"/<soft-key>\" and \"//<hard-key>\" may be repeated and mixed`. The \"///password\" is optional and should only occur once.": "You can set a custom derivation path for this account using the following syntax \"/<soft-key>//<hard-key>///<password>\". The \"/<soft-key>\" and \"//<hard-key>\" may be repeated and mixed`. The \"///password\" is optional and should only occur once.",
+  "//hard/soft///password": "//hard/soft///password",
+  "My memo": "My memo"
+}

+ 19 - 0
packages/apps/public/locales/en/app-address-book.json

@@ -0,0 +1,19 @@
+{
+  "address forgotten": "address forgotten",
+  "Forget this address": "Forget this address",
+  "deposit": "deposit",
+  "Send funds to this address": "Send funds to this address",
+  "My contacts": "My contacts",
+  "address edited": "address edited",
+  "address created": "address created",
+  "Add an address": "Add an address",
+  "Paste here the address of the contact you want to add to your address book.": "Paste here the address of the contact you want to add to your address book.",
+  "address": "address",
+  "Type the name of your contact. This name will be used across all the apps. It can be edited later on.": "Type the name of your contact. This name will be used across all the apps. It can be edited later on.",
+  "name": "name",
+  "Cancel": "Cancel",
+  "Save": "Save",
+  "Add contact": "Add contact",
+  "No contacts found.": "No contacts found.",
+  "View memo": "View memo"
+}

+ 15 - 0
packages/apps/public/locales/en/app-claims.json

@@ -0,0 +1,15 @@
+{
+  "Your Ethereum account": "Your Ethereum account",
+  "has a valid claim for": "has a valid claim for",
+  "does not appear to have a valid claim. Please double check that you have signed the transaction correctly on the correct ETH account.": "does not appear to have a valid claim. Please double check that you have signed the transaction correctly on the correct ETH account.",
+  "Redeem": "Redeem",
+  "1. Select your {{chain}} account": "1. Select your {{chain}} account",
+  "The account you want to claim to.": "The account you want to claim to.",
+  "claim to account": "claim to account",
+  "Continue": "Continue",
+  "2. Sign ETH transaction": "2. Sign ETH transaction",
+  "copied": "copied",
+  "click to copy": "click to copy",
+  "Copy the above string and sign an Ethereum transaction with the account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below": "Copy the above string and sign an Ethereum transaction with the account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below",
+  "Confirm claim": "Confirm claim"
+}

+ 61 - 0
packages/apps/public/locales/en/app-contracts.json

@@ -0,0 +1,61 @@
+{
+  "You are using an ABI with an outdated format. Please generate a new one.": "You are using an ABI with an outdated format. Please generate a new one.",
+  "invalid ABI file selected": "invalid ABI file selected",
+  "click to select or drag and drop a JSON ABI file": "click to select or drag and drop a JSON ABI file",
+  "The code hash for the on-chain deployed code.": "The code hash for the on-chain deployed code.",
+  "code hash": "code hash",
+  "Save": "Save",
+  "Add an existing code hash": "Add an existing code hash",
+  "ABI": "ABI",
+  "Forget this code hash": "Forget this code hash",
+  "deploy": "deploy",
+  "Deploy this code hash as a smart contract": "Deploy this code hash as a smart contract",
+  "No code hashes available": "No code hashes available",
+  "Upload WASM": "Upload WASM",
+  "The compiled WASM for the contract that you wish to deploy. Each unique code blob will be attached with a code hash that can be used to create new instances.": "The compiled WASM for the contract that you wish to deploy. Each unique code blob will be attached with a code hash that can be used to create new instances.",
+  "compiled contract WASM": "compiled contract WASM",
+  "The code is not recognized as being in valid WASM format": "The code is not recognized as being in valid WASM format",
+  "Upload": "Upload",
+  "Unable to find on-chain WASM code for the supplied codeHash": "Unable to find on-chain WASM code for the supplied codeHash",
+  "The codeHash is not a valid hex hash": "The codeHash is not a valid hex hash",
+  "The address for the deployed contract instance.": "The address for the deployed contract instance.",
+  "contract address": "contract address",
+  "Add an existing contract": "Add an existing contract",
+  "Call a contract": "Call a contract",
+  "Specify the user account to use for this contract call. And fees will be deducted from this account.": "Specify the user account to use for this contract call. And fees will be deducted from this account.",
+  "call from account": "call from account",
+  "A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.": "A deployed contract that has either been deployed or attached. The address and ABI are used to construct the parameters.",
+  "contract to use": "contract to use",
+  "The message to send to this contract. Parameters are adjusted based on the ABI provided.": "The message to send to this contract. Parameters are adjusted based on the ABI provided.",
+  "message to send": "message to send",
+  "The allotted value for this contract, i.e. the amount transferred to the contract as part of this call.": "The allotted value for this contract, i.e. the amount transferred to the contract as part of this call.",
+  "value": "value",
+  "The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.": "The maximum amount of gas that can be used by this deployment, if the code requires more, the deployment will fail.",
+  "maximum gas allowed": "maximum gas allowed",
+  "Cancel": "Cancel",
+  "Call": "Call",
+  "address forgotten": "address forgotten",
+  "Messages": "Messages",
+  "Forget this contract": "Forget this contract",
+  "execute": "execute",
+  "Call a method on this contract": "Call a method on this contract",
+  "No contracts available": "No contracts available",
+  "Deploy a code hash": "Deploy a code hash",
+  "Unable to find deployed contract code at the specified address": "Unable to find deployed contract code at the specified address",
+  "The value is not in a valid address format": "The value is not in a valid address format",
+  "The contract WASM previously deployed. Internally this is identified by the hash of the code, as either created or attached.": "The contract WASM previously deployed. Internally this is identified by the hash of the code, as either created or attached.",
+  "code for this contract": "code for this contract",
+  "The deployment constructor information for this contract, as provided by the ABI.": "The deployment constructor information for this contract, as provided by the ABI.",
+  "The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.": "The allotted endowment for this contract, i.e. the amount transferred to the contract upon instantiation.",
+  "endowment": "endowment",
+  "Deploy": "Deploy",
+  "Specify the user account to use for this deployment. And fees will be deducted from this account.": "Specify the user account to use for this deployment. And fees will be deducted from this account.",
+  "deployment account": "deployment account",
+  "Confirm ABI removal": "Confirm ABI removal",
+  "You are about to remove this code's ABI. Once completed, should you need to access it again, you will have to manually re-upload it.": "You are about to remove this code's ABI. Once completed, should you need to access it again, you will have to manually re-upload it.",
+  "This operation does not impact the associated on-chain code or any of its contracts.": "This operation does not impact the associated on-chain code or any of its contracts.",
+  "Remove": "Remove",
+  "The maximum amount of gas that can be used by this call. If the code requires more, the call will fail.": "The maximum amount of gas that can be used by this call. If the code requires more, the call will fail.",
+  "Call results": "Call results",
+  "constructor ": "constructor "
+}

+ 34 - 0
packages/apps/public/locales/en/app-council.json

@@ -0,0 +1,34 @@
+{
+  "Council overview": "Council overview",
+  "Motions": "Motions",
+  "No council motions": "No council motions",
+  "ayes ({{ayes}}/{{threshold}} to approve)": "ayes ({{ayes}}/{{threshold}} to approve)",
+  "Aye": "Aye",
+  "nays ({{nays}})": "nays ({{nays}})",
+  "Nay": "Nay",
+  "Propose a council motion": "Propose a council motion",
+  "threshold": "threshold",
+  "The minimum number of council votes required to approve this motion": "The minimum number of council votes required to approve this motion",
+  "Positive number between 1 and {{memberCount}}": "Positive number between 1 and {{memberCount}}",
+  "proposal": "proposal",
+  "candidate": "candidate",
+  "active until": "active until",
+  "council member": "council member",
+  "No members found": "No members found",
+  "members": "members",
+  "candidates": "candidates",
+  "Submit your council candidacy": "Submit your council candidacy",
+  "Candidate account": "Candidate account",
+  "This account will be nominated to fill the council slot you specify.": "This account will be nominated to fill the council slot you specify.",
+  "Submit candidacy": "Submit candidacy",
+  "seats": "seats",
+  "voting round": "voting round",
+  "term duration": "term duration",
+  "Vote for current candidates": "Vote for current candidates",
+  "Voting account": "Voting account",
+  "This account will be use to approve or disapprove each candidate.": "This account will be use to approve or disapprove each candidate.",
+  "Vote": "Vote",
+  "You have already voted in this round": "You have already voted in this round",
+  "Retract vote": "Retract vote",
+  "No vote": "No vote"
+}

+ 0 - 0
packages/apps/public/locales/en/addresses.json → packages/apps/public/locales/en/app-dashboard.json


+ 34 - 0
packages/apps/public/locales/en/app-democracy.json

@@ -0,0 +1,34 @@
+{
+  "Democracy overview": "Democracy overview",
+  "depositors": "depositors",
+  "balance": "balance",
+  "No available proposals": "No available proposals",
+  "proposals": "proposals",
+  "Submit proposal": "Submit proposal",
+  "propose": "propose",
+  "The amount that will be bonded to submit the proposal": "The amount that will be bonded to submit the proposal",
+  "value": "value",
+  "ending at": "ending at",
+  "block #{{blockNumber}}, {{remaining}} blocks remaining": "block #{{blockNumber}}, {{remaining}} blocks remaining",
+  "activate at (if passed)": "activate at (if passed)",
+  "block #{{blockNumber}}": "block #{{blockNumber}}",
+  "vote threshold": "vote threshold",
+  "No available referendums": "No available referendums",
+  "referendum": "referendum",
+  "Second proposal": "Second proposal",
+  "Select the account you wish to second with. This will lock your funds until the proposal is either approved or rejected": "Select the account you wish to second with. This will lock your funds until the proposal is either approved or rejected",
+  "second with account": "second with account",
+  "Cancel": "Cancel",
+  "Second": "Second",
+  "referenda": "referenda",
+  "active": "active",
+  "launch period": "launch period",
+  "Vote on proposal": "Vote on proposal",
+  "Select the account you wish to vote with. You can approve \"aye\" or deny \"nay\" the proposal.": "Select the account you wish to vote with. You can approve \"aye\" or deny \"nay\" the proposal.",
+  "vote with account": "vote with account",
+  "Select your vote preferences for this proposal, either to approve or disapprove": "Select your vote preferences for this proposal, either to approve or disapprove",
+  "record my vote as": "record my vote as",
+  "Aye, I approve": "Aye, I approve",
+  "Nay, I do not approve": "Nay, I do not approve",
+  "Vote": "Vote"
+}

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