diff --git a/js/src/3rdparty/email-verification/index.js b/js/src/3rdparty/email-verification/index.js
new file mode 100644
index 000000000..5f4885f3b
--- /dev/null
+++ b/js/src/3rdparty/email-verification/index.js
@@ -0,0 +1,53 @@
+// Copyright 2015, 2016 Ethcore (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 { stringify } from 'querystring';
+import React from 'react';
+
+import styles from './styles.css';
+
+export const howItWorks = (
+
+
The following steps will let you prove that you control both an account and an e-mail address.
+
+ - You send a verification request to a specific contract.
+ - Our server puts a puzzle into this contract.
+ - The code you receive via e-mail is the solution to this puzzle.
+
+
+);
+
+export const termsOfService = (
+
+);
+
+export const postToServer = (query, isTestnet = false) => {
+ const port = isTestnet ? 28443 : 18443;
+ query = stringify(query);
+ return fetch(`https://email-verification.parity.io:${port}/?` + query, {
+ method: 'POST', mode: 'cors', cache: 'no-store'
+ })
+ .then((res) => {
+ return res.json().then((data) => {
+ if (res.ok) {
+ return data.message;
+ }
+ throw new Error(data.message || 'unknown error');
+ });
+ });
+};
diff --git a/js/src/3rdparty/email-verification/styles.css b/js/src/3rdparty/email-verification/styles.css
new file mode 100644
index 000000000..daa4c605c
--- /dev/null
+++ b/js/src/3rdparty/email-verification/styles.css
@@ -0,0 +1,20 @@
+/* Copyright 2015, 2016 Ethcore (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 .
+*/
+
+.list li {
+ padding: .1em 0;
+}
diff --git a/js/src/3rdparty/sms-verification/index.js b/js/src/3rdparty/sms-verification/index.js
index c50b2331a..46faf084c 100644
--- a/js/src/3rdparty/sms-verification/index.js
+++ b/js/src/3rdparty/sms-verification/index.js
@@ -17,6 +17,19 @@
import { stringify } from 'querystring';
import React from 'react';
+import styles from './styles.css';
+
+export const howItWorks = (
+
+
The following steps will let you prove that you control both an account and a phone number.
+
+ - You send a verification request to a specific contract.
+ - Our server puts a puzzle into this contract.
+ - The code you receive via SMS is the solution to this puzzle.
+
+
+);
+
export const termsOfService = (
- This privacy notice relates to your use of the Parity SMS verification service. We take your privacy seriously and deal in an honest, direct and transparent way when it comes to your data.
diff --git a/js/src/3rdparty/sms-verification/styles.css b/js/src/3rdparty/sms-verification/styles.css
new file mode 100644
index 000000000..daa4c605c
--- /dev/null
+++ b/js/src/3rdparty/sms-verification/styles.css
@@ -0,0 +1,20 @@
+/* Copyright 2015, 2016 Ethcore (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 .
+*/
+
+.list li {
+ padding: .1em 0;
+}
diff --git a/js/src/contracts/abi/email-verification.json b/js/src/contracts/abi/email-verification.json
new file mode 100644
index 000000000..baa6db483
--- /dev/null
+++ b/js/src/contracts/abi/email-verification.json
@@ -0,0 +1 @@
+[{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"reverse","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_who","type":"address"},{"name":"_puzzle","type":"bytes32"},{"name":"_emailHash","type":"bytes32"}],"name":"puzzle","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_emailHash","type":"bytes32"}],"name":"request","outputs":[],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"_new","type":"uint256"}],"name":"setFee","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_code","type":"bytes32"}],"name":"confirm","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"drain","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"}],"name":"certified","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_who","type":"address"},{"name":"_field","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"},{"indexed":false,"name":"emailHash","type":"bytes32"}],"name":"Requested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"},{"indexed":true,"name":"emailHash","type":"bytes32"},{"indexed":false,"name":"puzzle","type":"bytes32"}],"name":"Puzzled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"}],"name":"Confirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"who","type":"address"}],"name":"Revoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"old","type":"address"},{"indexed":true,"name":"current","type":"address"}],"name":"NewOwner","type":"event"}]
\ No newline at end of file
diff --git a/js/src/contracts/abi/index.js b/js/src/contracts/abi/index.js
index f15765b1a..c8cb851dd 100644
--- a/js/src/contracts/abi/index.js
+++ b/js/src/contracts/abi/index.js
@@ -19,6 +19,7 @@ import basiccoin from './basiccoin.json';
import basiccoinmanager from './basiccoinmanager.json';
import dappreg from './dappreg.json';
import eip20 from './eip20.json';
+import emailverification from './email-verification.json';
import gavcoin from './gavcoin.json';
import githubhint from './githubhint.json';
import owned from './owned.json';
@@ -34,6 +35,7 @@ export {
basiccoinmanager,
dappreg,
eip20,
+ emailverification,
gavcoin,
githubhint,
owned,
diff --git a/js/src/modals/Verification/email-store.js b/js/src/modals/Verification/email-store.js
new file mode 100644
index 000000000..14179cfe2
--- /dev/null
+++ b/js/src/modals/Verification/email-store.js
@@ -0,0 +1,66 @@
+// Copyright 2015, 2016 Ethcore (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 { observable, computed, action } from 'mobx';
+
+import VerificationStore, {
+ LOADING, QUERY_DATA, QUERY_CODE, POSTED_CONFIRMATION, DONE
+} from './store';
+import { postToServer } from '../../3rdparty/email-verification';
+
+export default class EmailVerificationStore extends VerificationStore {
+ @observable email = '';
+
+ @computed get isEmailValid () {
+ // See https://davidcel.is/posts/stop-validating-email-addresses-with-regex/
+ return this.email && this.email.indexOf('@') >= 0;
+ }
+
+ @computed get isStepValid () {
+ if (this.step === DONE) {
+ return true;
+ }
+ if (this.error) {
+ return false;
+ }
+
+ switch (this.step) {
+ case LOADING:
+ return this.contract && this.fee && this.isVerified !== null && this.hasRequested !== null;
+ case QUERY_DATA:
+ return this.isEmailValid && this.consentGiven;
+ case QUERY_CODE:
+ return this.requestTx && this.isCodeValid === true;
+ case POSTED_CONFIRMATION:
+ return !!this.confirmationTx;
+ default:
+ return false;
+ }
+ }
+
+ constructor (api, account, isTestnet) {
+ return super(api, account, isTestnet, 'emailverification');
+ }
+
+ @action setEmail = (email) => {
+ this.email = email;
+ }
+
+ requestCode = () => {
+ const { email, account, isTestnet } = this;
+ return postToServer({ email, address: account }, isTestnet);
+ }
+}