+ )
+ : null
+ }
);
}
diff --git a/js/src/modals/DeployContract/ErrorStep/errorStep.js b/js/src/ui/ScrollableText/index.js
similarity index 64%
rename from js/src/modals/DeployContract/ErrorStep/errorStep.js
rename to js/src/ui/ScrollableText/index.js
index 1a671dea1..959b338eb 100644
--- a/js/src/modals/DeployContract/ErrorStep/errorStep.js
+++ b/js/src/ui/ScrollableText/index.js
@@ -14,22 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-import React, { Component, PropTypes } from 'react';
-
-import styles from '../deployContract.css';
-
-export default class ErrorStep extends Component {
- static propTypes = {
- error: PropTypes.object
- }
-
- render () {
- const { error } = this.props;
-
- return (
-
- The contract deployment failed: { error.message }
-
- );
- }
-}
+export default from './scrollableText';
diff --git a/js/src/ui/ScrollableText/scrollableText.css b/js/src/ui/ScrollableText/scrollableText.css
new file mode 100644
index 000000000..50a9ea892
--- /dev/null
+++ b/js/src/ui/ScrollableText/scrollableText.css
@@ -0,0 +1,34 @@
+/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
+/* This file is part of Parity.
+/*
+/* Parity is free software: you can redistribute it and/or modify
+/* it under the terms of the GNU General Public License as published by
+/* the Free Software Foundation, either version 3 of the License, or
+/* (at your option) any later version.
+/*
+/* Parity is distributed in the hope that it will be useful,
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+/* GNU General Public License for more details.
+/*
+/* You should have received a copy of the GNU General Public License
+/* along with Parity. If not, see .
+*/
+
+.input {
+ background-color: transparent;
+ border: none;
+ box-sizing: border-box;
+ color: inherit;
+ cursor: text;
+ display: inline-block;
+ font-family: inherit;
+ font-size: inherit;
+ margin-left: 0.25em;
+ padding: 0;
+ width: 100%;
+
+ &.small {
+ width: 10em;
+ }
+}
diff --git a/js/src/ui/ScrollableText/scrollableText.js b/js/src/ui/ScrollableText/scrollableText.js
new file mode 100644
index 000000000..53668ff77
--- /dev/null
+++ b/js/src/ui/ScrollableText/scrollableText.js
@@ -0,0 +1,40 @@
+// Copyright 2015-2017 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+import React, { PropTypes } from 'react';
+
+import styles from './scrollableText.css';
+
+export default function ScrollableText ({ small = false, text }) {
+ const classes = [ styles.input ];
+
+ if (small) {
+ classes.push(styles.small);
+ }
+
+ return (
+
+ );
+}
+
+ScrollableText.propTypes = {
+ text: PropTypes.string.isRequired,
+ small: PropTypes.bool
+};
diff --git a/js/src/ui/ShortenedHash/shortenedHash.js b/js/src/ui/ShortenedHash/shortenedHash.js
index 36f138986..3ad265bee 100644
--- a/js/src/ui/ShortenedHash/shortenedHash.js
+++ b/js/src/ui/ShortenedHash/shortenedHash.js
@@ -36,7 +36,7 @@ export default class ShortenedHash extends Component {
}
return (
- { shortened }
+ { shortened }
);
}
}
diff --git a/js/src/ui/Title/title.js b/js/src/ui/Title/title.js
index 148398756..82abdb3cf 100644
--- a/js/src/ui/Title/title.js
+++ b/js/src/ui/Title/title.js
@@ -76,7 +76,7 @@ export default class Title extends Component {
renderSteps () {
const { activeStep, steps } = this.props;
- if (!steps) {
+ if (!steps || steps.length < 2) {
return;
}
diff --git a/js/src/ui/index.js b/js/src/ui/index.js
index d076986be..349cc7fe4 100644
--- a/js/src/ui/index.js
+++ b/js/src/ui/index.js
@@ -46,6 +46,7 @@ export Page from './Page';
export ParityBackground from './ParityBackground';
export Portal from './Portal';
export QrCode from './QrCode';
+export ScrollableText from './ScrollableText';
export SectionList from './SectionList';
export SelectionList from './SelectionList';
export ShortenedHash from './ShortenedHash';
diff --git a/js/src/util/tx.js b/js/src/util/tx.js
index 3347747d4..b56fc8b1f 100644
--- a/js/src/util/tx.js
+++ b/js/src/util/tx.js
@@ -16,6 +16,25 @@
import WalletsUtils from '~/util/wallets';
+export function trackRequest (api, options, statusCallback) {
+ const { requestId, transactionHash } = options;
+ const txHashPromise = transactionHash
+ ? Promise.resolve(transactionHash)
+ : api.pollMethod('parity_checkRequest', requestId);
+
+ return txHashPromise
+ .then((transactionHash) => {
+ statusCallback(null, { transactionHash });
+ return api.pollMethod('eth_getTransactionReceipt', transactionHash, isValidReceipt);
+ })
+ .then((transactionReceipt) => {
+ statusCallback(null, { transactionReceipt });
+ })
+ .catch((error) => {
+ statusCallback(error);
+ });
+}
+
const isValidReceipt = (receipt) => {
return receipt && receipt.blockNumber && receipt.blockNumber.gt(0);
};
@@ -73,100 +92,6 @@ export function postTransaction (_func, _options, _values = []) {
});
}
-export function deploy (contract, _options, values, metadata = {}, statecb = () => {}, skipGasEstimate = false) {
- const options = { ..._options };
- const { api } = contract;
- const address = options.from;
-
- return WalletsUtils
- .isWallet(api, address)
- .then((isWallet) => {
- if (!isWallet) {
- return contract.deploy(options, values, statecb, skipGasEstimate);
- }
-
- let gasEstPromise;
-
- if (skipGasEstimate) {
- gasEstPromise = Promise.resolve(null);
- } else {
- statecb(null, { state: 'estimateGas' });
-
- gasEstPromise = deployEstimateGas(contract, options, values)
- .then(([gasEst, gas]) => gas);
- }
-
- return gasEstPromise
- .then((gas) => {
- if (gas) {
- options.gas = gas.toFixed(0);
- }
-
- statecb(null, { state: 'postTransaction', gas: options.gas });
-
- return WalletsUtils.getDeployArgs(contract, options, values);
- })
- .then((callArgs) => {
- const { func, options, values } = callArgs;
-
- return func._postTransaction(options, values)
- .then((requestId) => {
- statecb(null, { state: 'checkRequest', requestId });
- return contract._pollCheckRequest(requestId);
- })
- .then((txhash) => {
- statecb(null, { state: 'getTransactionReceipt', txhash });
- return contract._pollTransactionReceipt(txhash, options.gas);
- })
- .then((receipt) => {
- if (receipt.gasUsed.eq(options.gas)) {
- throw new Error(`Contract not deployed, gasUsed == ${options.gas.toFixed(0)}`);
- }
-
- const logs = WalletsUtils.parseLogs(api, receipt.logs || []);
-
- const confirmationLog = logs.find((log) => log.event === 'ConfirmationNeeded');
- const transactionLog = logs.find((log) => log.event === 'SingleTransact');
-
- if (!confirmationLog && !transactionLog) {
- throw new Error('Something went wrong in the Wallet Contract (no logs have been emitted)...');
- }
-
- // Confirmations are needed from the other owners
- if (confirmationLog) {
- const operationHash = api.util.bytesToHex(confirmationLog.params.operation.value);
-
- // Add the contract to pending contracts
- WalletsUtils.addPendingContract(address, operationHash, metadata);
- statecb(null, { state: 'confirmationNeeded' });
- return;
- }
-
- // Set the contract address in the receip
- receipt.contractAddress = transactionLog.params.created.value;
-
- const contractAddress = receipt.contractAddress;
-
- statecb(null, { state: 'hasReceipt', receipt });
- contract._receipt = receipt;
- contract._address = contractAddress;
-
- statecb(null, { state: 'getCode' });
-
- return api.eth.getCode(contractAddress)
- .then((code) => {
- if (code === '0x') {
- throw new Error('Contract not deployed, getCode returned 0x');
- }
-
- statecb(null, { state: 'completed' });
- return contractAddress;
- });
- });
- });
- });
-}
-
export function deployEstimateGas (contract, _options, values) {
const options = { ..._options };
const { api } = contract;
@@ -192,6 +117,86 @@ export function deployEstimateGas (contract, _options, values) {
});
}
+export function deploy (contract, options, values, skipGasEstimate = false) {
+ const { api } = contract;
+ const address = options.from;
+
+ const gasEstPromise = skipGasEstimate
+ ? Promise.resolve(null)
+ : deployEstimateGas(contract, options, values).then(([gasEst, gas]) => gas);
+
+ return gasEstPromise
+ .then((gas) => {
+ if (gas) {
+ options.gas = gas.toFixed(0);
+ }
+
+ return WalletsUtils.isWallet(api, address);
+ })
+ .then((isWallet) => {
+ if (!isWallet) {
+ const encodedOptions = contract._encodeOptions(contract.constructors[0], options, values);
+
+ return api.parity.postTransaction(encodedOptions);
+ }
+
+ return WalletsUtils.getDeployArgs(contract, options, values)
+ .then((callArgs) => {
+ const { func, options, values } = callArgs;
+
+ return func._postTransaction(options, values);
+ });
+ });
+}
+
+export function parseTransactionReceipt (api, options, receipt) {
+ const { metadata } = options;
+ const address = options.from;
+
+ if (receipt.gasUsed.eq(options.gas)) {
+ const error = new Error(`Contract not deployed, gasUsed == ${options.gas.toFixed(0)}`);
+
+ return Promise.reject(error);
+ }
+
+ const logs = WalletsUtils.parseLogs(api, receipt.logs || []);
+
+ const confirmationLog = logs.find((log) => log.event === 'ConfirmationNeeded');
+ const transactionLog = logs.find((log) => log.event === 'SingleTransact');
+
+ if (!confirmationLog && !transactionLog && !receipt.contractAddress) {
+ const error = new Error('Something went wrong in the contract deployment...');
+
+ return Promise.reject(error);
+ }
+
+ // Confirmations are needed from the other owners
+ if (confirmationLog) {
+ const operationHash = api.util.bytesToHex(confirmationLog.params.operation.value);
+
+ // Add the contract to pending contracts
+ WalletsUtils.addPendingContract(address, operationHash, metadata);
+ return Promise.resolve(null);
+ }
+
+ if (transactionLog) {
+ // Set the contract address in the receipt
+ receipt.contractAddress = transactionLog.params.created.value;
+ }
+
+ const contractAddress = receipt.contractAddress;
+
+ return api.eth
+ .getCode(contractAddress)
+ .then((code) => {
+ if (code === '0x') {
+ throw new Error('Contract not deployed, getCode returned 0x');
+ }
+
+ return contractAddress;
+ });
+}
+
export function patchApi (api) {
api.patch = {
...api.patch,
diff --git a/js/src/util/validation.js b/js/src/util/validation.js
index 881e5f142..e0551a339 100644
--- a/js/src/util/validation.js
+++ b/js/src/util/validation.js
@@ -25,6 +25,7 @@ import { NULL_ADDRESS } from './constants';
export const ERRORS = {
invalidAddress: 'address is an invalid network address',
invalidAmount: 'the supplied amount should be a valid positive number',
+ invalidAmountDecimals: 'the supplied amount exceeds the allowed decimals',
duplicateAddress: 'the address is already in your address book',
invalidChecksum: 'address has failed the checksum formatting',
invalidName: 'name should not be blank and longer than 2',
@@ -48,6 +49,7 @@ export function validateAbi (abi) {
abiError = ERRORS.invalidAbi;
return {
+ error: abiError,
abi,
abiError,
abiParsed
@@ -66,6 +68,7 @@ export function validateAbi (abi) {
abiError = `${ERRORS.invalidAbi} (#${invalidIndex}: ${invalid.name || invalid.type})`;
return {
+ error: abiError,
abi,
abiError,
abiParsed
@@ -78,6 +81,7 @@ export function validateAbi (abi) {
}
return {
+ error: abiError,
abi,
abiError,
abiParsed
@@ -123,6 +127,7 @@ export function validateAddress (address) {
}
return {
+ error: addressError,
address,
addressError
};
@@ -138,6 +143,7 @@ export function validateCode (code) {
}
return {
+ error: codeError,
code,
codeError
};
@@ -149,6 +155,7 @@ export function validateName (name) {
: null;
return {
+ error: nameError,
name,
nameError
};
@@ -168,6 +175,27 @@ export function validatePositiveNumber (number) {
}
return {
+ error: numberError,
+ number,
+ numberError
+ };
+}
+
+export function validateDecimalsNumber (number, base = 1) {
+ let numberError = null;
+
+ try {
+ const s = new BigNumber(number).mul(base).toFixed();
+
+ if (s.indexOf('.') !== -1) {
+ numberError = ERRORS.invalidAmountDecimals;
+ }
+ } catch (e) {
+ numberError = ERRORS.invalidAmount;
+ }
+
+ return {
+ error: numberError,
number,
numberError
};
@@ -189,6 +217,7 @@ export function validateUint (value) {
}
return {
+ error: valueError,
value,
valueError
};
diff --git a/js/src/util/validation.spec.js b/js/src/util/validation.spec.js
index 3b78397b0..3b9c00a72 100644
--- a/js/src/util/validation.spec.js
+++ b/js/src/util/validation.spec.js
@@ -32,7 +32,8 @@ describe('util/validation', () => {
name: 'test',
inputs: [],
outputs: []
- }]
+ }],
+ error: null
});
});
@@ -47,7 +48,8 @@ describe('util/validation', () => {
name: 'test',
inputs: [],
outputs: []
- }]
+ }],
+ error: null
});
});
@@ -57,7 +59,8 @@ describe('util/validation', () => {
expect(validateAbi(abi)).to.deep.equal({
abi,
abiError: ERRORS.invalidAbi,
- abiParsed: null
+ abiParsed: null,
+ error: ERRORS.invalidAbi
});
});
@@ -67,7 +70,8 @@ describe('util/validation', () => {
expect(validateAbi(abi)).to.deep.equal({
abi,
abiError: ERRORS.invalidAbi,
- abiParsed: {}
+ abiParsed: {},
+ error: ERRORS.invalidAbi
});
});
@@ -77,7 +81,8 @@ describe('util/validation', () => {
expect(validateAbi(abi)).to.deep.equal({
abi,
abiError: `${ERRORS.invalidAbi} (#0: event)`,
- abiParsed: [{ type: 'event' }]
+ abiParsed: [{ type: 'event' }],
+ error: `${ERRORS.invalidAbi} (#0: event)`
});
});
@@ -87,7 +92,8 @@ describe('util/validation', () => {
expect(validateAbi(abi)).to.deep.equal({
abi,
abiError: `${ERRORS.invalidAbi} (#0: function)`,
- abiParsed: [{ type: 'function' }]
+ abiParsed: [{ type: 'function' }],
+ error: `${ERRORS.invalidAbi} (#0: function)`
});
});
@@ -97,7 +103,8 @@ describe('util/validation', () => {
expect(validateAbi(abi)).to.deep.equal({
abi,
abiError: `${ERRORS.invalidAbi} (#0: somethingElse)`,
- abiParsed: [{ type: 'somethingElse' }]
+ abiParsed: [{ type: 'somethingElse' }],
+ error: `${ERRORS.invalidAbi} (#0: somethingElse)`
});
});
});
@@ -108,7 +115,8 @@ describe('util/validation', () => {
expect(validateAddress(address)).to.deep.equal({
address,
- addressError: null
+ addressError: null,
+ error: null
});
});
@@ -117,14 +125,16 @@ describe('util/validation', () => {
expect(validateAddress(address.toLowerCase())).to.deep.equal({
address,
- addressError: null
+ addressError: null,
+ error: null
});
});
it('sets error on null addresses', () => {
expect(validateAddress(null)).to.deep.equal({
address: null,
- addressError: ERRORS.invalidAddress
+ addressError: ERRORS.invalidAddress,
+ error: ERRORS.invalidAddress
});
});
@@ -133,7 +143,8 @@ describe('util/validation', () => {
expect(validateAddress(address)).to.deep.equal({
address,
- addressError: ERRORS.invalidAddress
+ addressError: ERRORS.invalidAddress,
+ error: ERRORS.invalidAddress
});
});
});
@@ -142,35 +153,40 @@ describe('util/validation', () => {
it('validates hex code', () => {
expect(validateCode('0x123abc')).to.deep.equal({
code: '0x123abc',
- codeError: null
+ codeError: null,
+ error: null
});
});
it('validates hex code (non-prefix)', () => {
expect(validateCode('123abc')).to.deep.equal({
code: '123abc',
- codeError: null
+ codeError: null,
+ error: null
});
});
it('sets error on invalid code', () => {
expect(validateCode(null)).to.deep.equal({
code: null,
- codeError: ERRORS.invalidCode
+ codeError: ERRORS.invalidCode,
+ error: ERRORS.invalidCode
});
});
it('sets error on empty code', () => {
expect(validateCode('')).to.deep.equal({
code: '',
- codeError: ERRORS.invalidCode
+ codeError: ERRORS.invalidCode,
+ error: ERRORS.invalidCode
});
});
it('sets error on non-hex code', () => {
expect(validateCode('123hfg')).to.deep.equal({
code: '123hfg',
- codeError: ERRORS.invalidCode
+ codeError: ERRORS.invalidCode,
+ error: ERRORS.invalidCode
});
});
});
@@ -179,21 +195,24 @@ describe('util/validation', () => {
it('validates names', () => {
expect(validateName('Joe Bloggs')).to.deep.equal({
name: 'Joe Bloggs',
- nameError: null
+ nameError: null,
+ error: null
});
});
it('sets error on null names', () => {
expect(validateName(null)).to.deep.equal({
name: null,
- nameError: ERRORS.invalidName
+ nameError: ERRORS.invalidName,
+ error: ERRORS.invalidName
});
});
it('sets error on short names', () => {
expect(validateName(' 1 ')).to.deep.equal({
name: ' 1 ',
- nameError: ERRORS.invalidName
+ nameError: ERRORS.invalidName,
+ error: ERRORS.invalidName
});
});
});
@@ -202,35 +221,40 @@ describe('util/validation', () => {
it('validates numbers', () => {
expect(validatePositiveNumber(123)).to.deep.equal({
number: 123,
- numberError: null
+ numberError: null,
+ error: null
});
});
it('validates strings', () => {
expect(validatePositiveNumber('123')).to.deep.equal({
number: '123',
- numberError: null
+ numberError: null,
+ error: null
});
});
it('validates bignumbers', () => {
expect(validatePositiveNumber(new BigNumber(123))).to.deep.equal({
number: new BigNumber(123),
- numberError: null
+ numberError: null,
+ error: null
});
});
it('sets error on invalid numbers', () => {
expect(validatePositiveNumber(null)).to.deep.equal({
number: null,
- numberError: ERRORS.invalidAmount
+ numberError: ERRORS.invalidAmount,
+ error: ERRORS.invalidAmount
});
});
it('sets error on negative numbers', () => {
expect(validatePositiveNumber(-1)).to.deep.equal({
number: -1,
- numberError: ERRORS.invalidAmount
+ numberError: ERRORS.invalidAmount,
+ error: ERRORS.invalidAmount
});
});
});
@@ -239,42 +263,48 @@ describe('util/validation', () => {
it('validates numbers', () => {
expect(validateUint(123)).to.deep.equal({
value: 123,
- valueError: null
+ valueError: null,
+ error: null
});
});
it('validates strings', () => {
expect(validateUint('123')).to.deep.equal({
value: '123',
- valueError: null
+ valueError: null,
+ error: null
});
});
it('validates bignumbers', () => {
expect(validateUint(new BigNumber(123))).to.deep.equal({
value: new BigNumber(123),
- valueError: null
+ valueError: null,
+ error: null
});
});
it('sets error on invalid numbers', () => {
expect(validateUint(null)).to.deep.equal({
value: null,
- valueError: ERRORS.invalidNumber
+ valueError: ERRORS.invalidNumber,
+ error: ERRORS.invalidNumber
});
});
it('sets error on negative numbers', () => {
expect(validateUint(-1)).to.deep.equal({
value: -1,
- valueError: ERRORS.negativeNumber
+ valueError: ERRORS.negativeNumber,
+ error: ERRORS.negativeNumber
});
});
it('sets error on decimal numbers', () => {
expect(validateUint(3.1415927)).to.deep.equal({
value: 3.1415927,
- valueError: ERRORS.decimalNumber
+ valueError: ERRORS.decimalNumber,
+ error: ERRORS.decimalNumber
});
});
});
diff --git a/js/src/modals/DeployContract/ErrorStep/index.js b/js/src/views/Application/Requests/index.js
similarity index 95%
rename from js/src/modals/DeployContract/ErrorStep/index.js
rename to js/src/views/Application/Requests/index.js
index c7bff0fed..69692f645 100644
--- a/js/src/modals/DeployContract/ErrorStep/index.js
+++ b/js/src/views/Application/Requests/index.js
@@ -14,4 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-export default from './errorStep';
+export default from './requests';
diff --git a/js/src/views/Application/Requests/requests.css b/js/src/views/Application/Requests/requests.css
new file mode 100644
index 000000000..1ab1e41d3
--- /dev/null
+++ b/js/src/views/Application/Requests/requests.css
@@ -0,0 +1,127 @@
+/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
+/* This file is part of Parity.
+/*
+/* Parity is free software: you can redistribute it and/or modify
+/* it under the terms of the GNU General Public License as published by
+/* the Free Software Foundation, either version 3 of the License, or
+/* (at your option) any later version.
+/*
+/* Parity is distributed in the hope that it will be useful,
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+/* GNU General Public License for more details.
+/*
+/* You should have received a copy of the GNU General Public License
+/* along with Parity. If not, see .
+*/
+
+$baseColor: 255;
+$baseOpacity: 0.95;
+
+.requests {
+ align-items: flex-end;
+ bottom: 2em;
+ display: flex;
+ flex-direction: column;
+ position: fixed;
+ right: 0.175em;
+ z-index: 750;
+
+ * {
+ font-size: 0.85rem !important;
+ }
+}
+
+.request {
+ animation-fill-mode: forwards;
+ animation-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
+ background-color: rgba($baseColor, $baseColor, $baseColor, $baseOpacity);
+ color: black;
+ cursor: pointer;
+ margin-top: 0.5em;
+ opacity: 1;
+
+ &.hide {
+ animation-duration: 0.5s;
+ animation-name: fadeout;
+ }
+
+ .status {
+ padding: 0.5em;
+
+ &.error {
+ background-color: rgba(200, 40, 40, 0.95);
+ color: white;
+ }
+ }
+
+ .container {
+ display: flex;
+ flex-direction: row;
+ padding: 1em;
+
+ * {
+ color: black !important;
+ }
+ }
+
+ &:hover .container {
+ background-color: rgba($baseColor, $baseColor, $baseColor, 1);
+ }
+
+ p {
+ margin: 0;
+ }
+}
+
+@keyframes fadeout {
+ from {
+ display: block;
+ height: inherit;
+ opacity: 1;
+ }
+
+ 49% {
+ display: block;
+ height: inherit;
+ opacity: 0;
+ }
+
+ 98% {
+ display: block;
+ height: 0;
+ opacity: 0;
+ }
+
+ to {
+ display: none;
+ height: 0;
+ opacity: 0;
+ }
+}
+
+.identity {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ margin-right: 1em;
+
+ .icon {
+ margin-bottom: 0.5rem;
+ }
+}
+
+.inline {
+ align-items: center;
+ display: flex;
+ flex-direction: row;
+
+ .fill {
+ flex: 1 0 auto;
+ }
+}
+
+.hash {
+ margin-left: 0.25em;
+}
diff --git a/js/src/views/Application/Requests/requests.js b/js/src/views/Application/Requests/requests.js
new file mode 100644
index 000000000..b4e27b6fe
--- /dev/null
+++ b/js/src/views/Application/Requests/requests.js
@@ -0,0 +1,233 @@
+// Copyright 2015-2017 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+import { LinearProgress } from 'material-ui';
+import React, { Component, PropTypes } from 'react';
+import { FormattedMessage } from 'react-intl';
+import ReactDOM from 'react-dom';
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
+
+import { hideRequest } from '~/redux/providers/requestsActions';
+import { MethodDecoding, IdentityIcon, ScrollableText, ShortenedHash } from '~/ui';
+
+import styles from './requests.css';
+
+const ERROR_STATE = 'ERROR_STATE';
+const DONE_STATE = 'DONE_STATE';
+const WAITING_STATE = 'WAITING_STATE';
+
+class Requests extends Component {
+ static propTypes = {
+ requests: PropTypes.object.isRequired,
+ onHideRequest: PropTypes.func.isRequired
+ };
+
+ state = {
+ extras: {}
+ };
+
+ render () {
+ const { requests } = this.props;
+ const { extras } = this.state;
+
+ return (
+