diff --git a/js/src/dapps/tokenreg/Tokens/Token/add-meta.js b/js/src/dapps/tokenreg/Tokens/Token/add-meta.js
index 89da76701..0f73179d9 100644
--- a/js/src/dapps/tokenreg/Tokens/Token/add-meta.js
+++ b/js/src/dapps/tokenreg/Tokens/Token/add-meta.js
@@ -19,7 +19,7 @@ import { Dialog, RaisedButton, FlatButton, SelectField, MenuItem } from 'materia
import AddIcon from 'material-ui/svg-icons/content/add';
import InputText from '../../Inputs/Text';
-import { ADDRESS_TYPE } from '../../Inputs/validation';
+import { ADDRESS_TYPE, URL_TYPE } from '../../Inputs/validation';
import styles from './token.css';
@@ -128,6 +128,22 @@ export default class AddMeta extends Component {
renderForm () {
const selectedMeta = metaDataKeys[this.state.metaKeyIndex];
+ const metaLabel = selectedMeta.label.toLowerCase();
+ let metaType;
+
+ switch (selectedMeta.validation) {
+ case ADDRESS_TYPE:
+ metaType = 'Address';
+ break;
+
+ case URL_TYPE:
+ metaType = 'URL';
+ break;
+
+ default:
+ metaType = 'URL Hint';
+ break;
+ }
return (
@@ -145,7 +161,7 @@ export default class AddMeta extends Component {
{
const { index } = this.props;
+ const { form, metaKeyIndex } = this.state;
+
+ const selectedMeta = metaDataKeys[metaKeyIndex];
const keyIndex = this.state.metaKeyIndex;
const key = metaDataKeys[keyIndex].value;
+ const value = form.value;
+ const validationType = selectedMeta.validation;
this.props.handleAddMeta(
index,
key,
- this.state.form.value
+ value,
+ validationType
);
this.setState({ complete: true });
diff --git a/js/src/dapps/tokenreg/Tokens/Token/tokenContainer.js b/js/src/dapps/tokenreg/Tokens/Token/tokenContainer.js
index 2de03fe9e..dc15f68d9 100644
--- a/js/src/dapps/tokenreg/Tokens/Token/tokenContainer.js
+++ b/js/src/dapps/tokenreg/Tokens/Token/tokenContainer.js
@@ -61,8 +61,8 @@ const mapDispatchToProps = (dispatch) => {
dispatch(unregisterToken(index));
},
- handleAddMeta: (index, key, value) => {
- dispatch(addTokenMeta(index, key, value));
+ handleAddMeta: (index, key, value, validationType) => {
+ dispatch(addTokenMeta(index, key, value, validationType));
}
};
};
diff --git a/js/src/dapps/tokenreg/Tokens/actions.js b/js/src/dapps/tokenreg/Tokens/actions.js
index b32b86905..6b7983b0d 100644
--- a/js/src/dapps/tokenreg/Tokens/actions.js
+++ b/js/src/dapps/tokenreg/Tokens/actions.js
@@ -14,7 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-import { getTokenTotalSupply } from '../utils';
+import { URL_TYPE } from '../Inputs/validation';
+import { getTokenTotalSupply, urlToHash } from '../utils';
const { bytesToHex } = window.parity.api.util;
@@ -178,40 +179,63 @@ export const queryTokenMeta = (index, query) => (dispatch, getState) => {
});
};
-export const addTokenMeta = (index, key, value) => (dispatch, getState) => {
+export const addTokenMeta = (index, key, value, validationType) => (dispatch, getState) => {
const state = getState();
+
const contractInstance = state.status.contract.instance;
+ const ghhInstance = state.status.githubhint.instance;
+
const token = state.tokens.tokens.find(t => t.index === index);
const options = { from: token.owner };
- const values = [ index, key, value ];
+ let valuesPromise;
- contractInstance
- .setMeta
- .estimateGas(options, values)
- .then((gasEstimate) => {
- options.gas = gasEstimate.mul(1.2).toFixed(0);
- return contractInstance.setMeta.postTransaction(options, values);
+ // Get the right values (could be a hashed URL from GHH)
+ if (validationType === URL_TYPE) {
+ valuesPromise = addGithubhintURL(ghhInstance, options, value)
+ .then((hash) => [ index, key, hash ]);
+ } else {
+ valuesPromise = Promise.resolve([ index, key, value ]);
+ }
+
+ return valuesPromise
+ .then((values) => {
+ return contractInstance
+ .setMeta
+ .estimateGas(options, values)
+ .then((gasEstimate) => {
+ options.gas = gasEstimate.mul(1.2).toFixed(0);
+ return contractInstance.setMeta.postTransaction(options, values);
+ });
})
.catch((e) => {
console.error(`addTokenMeta: #${index} error`, e);
});
};
-export const addGithubhintURL = (from, key, url) => (dispatch, getState) => {
- const state = getState();
- const contractInstance = state.status.githubhint.instance;
- const options = { from };
- const values = [ key, url ];
+export const addGithubhintURL = (ghhInstance, _options, url) => {
+ return urlToHash(ghhInstance, url)
+ .then((result) => {
+ const { hash, registered } = result;
- contractInstance
- .hintURL
- .estimateGas(options, values)
- .then((gasEstimate) => {
- options.gas = gasEstimate.mul(1.2).toFixed(0);
- return contractInstance.hintURL.postTransaction(options, values);
- })
- .catch((e) => {
- console.error('addGithubhintURL error', e);
+ if (registered) {
+ return hash;
+ }
+
+ const options = { from: _options.from };
+ const values = [ hash, url ];
+
+ ghhInstance
+ .hintURL
+ .estimateGas(options, values)
+ .then((gasEstimate) => {
+ options.gas = gasEstimate.mul(1.2).toFixed(0);
+ return ghhInstance.hintURL.postTransaction(options, values);
+ })
+ .catch((error) => {
+ console.error(`registering "${url}" to GHH`, error);
+ });
+
+ return hash;
});
};
diff --git a/js/src/dapps/tokenreg/constants.js b/js/src/dapps/tokenreg/constants.js
index 3d56545bd..a4d2e3938 100644
--- a/js/src/dapps/tokenreg/constants.js
+++ b/js/src/dapps/tokenreg/constants.js
@@ -14,13 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-import { HEX_TYPE, ADDRESS_TYPE } from './Inputs/validation';
+import { URL_TYPE, ADDRESS_TYPE } from './Inputs/validation';
export const metaDataKeys = [
{
label: 'Image',
value: 'IMG',
- validation: HEX_TYPE
+ validation: URL_TYPE
},
{
label: 'Address',
diff --git a/js/src/dapps/tokenreg/parity.js b/js/src/dapps/tokenreg/parity.js
index 6c861780a..7118ce087 100644
--- a/js/src/dapps/tokenreg/parity.js
+++ b/js/src/dapps/tokenreg/parity.js
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see .
-const { api } = window.parity;
+const api = window.parent.secureApi;
export {
api
diff --git a/js/src/dapps/tokenreg/utils.js b/js/src/dapps/tokenreg/utils.js
index 1dbd67687..82d71a6d0 100644
--- a/js/src/dapps/tokenreg/utils.js
+++ b/js/src/dapps/tokenreg/utils.js
@@ -18,6 +18,45 @@ import { api } from './parity';
import { eip20 as eip20Abi } from '~/contracts/abi';
+export const INVALID_URL_HASH = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470';
+export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
+
+/**
+ * Convert the given URL to a content hash,
+ * and checks if it is already registered in GHH
+ */
+export const urlToHash = (ghhInstance, url) => {
+ if (!url || !url.length) {
+ return Promise.resolve(null);
+ }
+
+ return api.parity
+ .hashContent(url)
+ .catch((error) => {
+ const message = error.text || error.message || error.toString();
+
+ throw new Error(`${message} (${url})`);
+ })
+ .then((contentHash) => {
+ console.log('lookupHash', url, contentHash);
+
+ if (contentHash === INVALID_URL_HASH) {
+ throw new Error(`"${url}" is not a valid URL`);
+ }
+
+ return ghhInstance.entries
+ .call({}, [contentHash])
+ .then(([accountSlashRepo, commit, contentHashOwner]) => {
+ const registered = (contentHashOwner !== ZERO_ADDRESS);
+
+ return {
+ hash: contentHash,
+ registered
+ };
+ });
+ });
+};
+
export const getTokenTotalSupply = (tokenAddress) => {
return api
.eth
diff --git a/js/src/views/Dapps/builtin.json b/js/src/views/Dapps/builtin.json
index 97ebcc0cc..673b4acd3 100644
--- a/js/src/views/Dapps/builtin.json
+++ b/js/src/views/Dapps/builtin.json
@@ -32,7 +32,8 @@
"description": "A registry of transactable tokens on the network",
"author": "Parity Team ",
"version": "1.0.0",
- "visible": true
+ "visible": true,
+ "secure": true
},
{
"id": "0xf49089046f53f5d2e5f3513c1c32f5ff57d986e46309a42d2b249070e4e72c46",