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",