Add Token image from URL (#4916)

This commit is contained in:
Nicolas Gotchac 2017-03-15 16:49:47 +01:00 committed by Gav Wood
parent f8aec7571f
commit a25d935a1d
7 changed files with 118 additions and 32 deletions

View File

@ -19,7 +19,7 @@ import { Dialog, RaisedButton, FlatButton, SelectField, MenuItem } from 'materia
import AddIcon from 'material-ui/svg-icons/content/add'; import AddIcon from 'material-ui/svg-icons/content/add';
import InputText from '../../Inputs/Text'; import InputText from '../../Inputs/Text';
import { ADDRESS_TYPE } from '../../Inputs/validation'; import { ADDRESS_TYPE, URL_TYPE } from '../../Inputs/validation';
import styles from './token.css'; import styles from './token.css';
@ -128,6 +128,22 @@ export default class AddMeta extends Component {
renderForm () { renderForm () {
const selectedMeta = metaDataKeys[this.state.metaKeyIndex]; 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 ( return (
<div> <div>
@ -145,7 +161,7 @@ export default class AddMeta extends Component {
<InputText <InputText
key={ selectedMeta.value } key={ selectedMeta.value }
floatingLabelText={ `${selectedMeta.label} value` } floatingLabelText={ `${selectedMeta.label} value` }
hintText={ `The value of the ${selectedMeta.label.toLowerCase()} (${selectedMeta.validation === ADDRESS_TYPE ? 'Address' : 'Url Hint'})` } hintText={ `The value of the ${metaLabel} (${metaType})` }
validationType={ selectedMeta.validation } validationType={ selectedMeta.validation }
onChange={ this.onChange } onChange={ this.onChange }
@ -174,14 +190,20 @@ export default class AddMeta extends Component {
onAdd = () => { onAdd = () => {
const { index } = this.props; const { index } = this.props;
const { form, metaKeyIndex } = this.state;
const selectedMeta = metaDataKeys[metaKeyIndex];
const keyIndex = this.state.metaKeyIndex; const keyIndex = this.state.metaKeyIndex;
const key = metaDataKeys[keyIndex].value; const key = metaDataKeys[keyIndex].value;
const value = form.value;
const validationType = selectedMeta.validation;
this.props.handleAddMeta( this.props.handleAddMeta(
index, index,
key, key,
this.state.form.value value,
validationType
); );
this.setState({ complete: true }); this.setState({ complete: true });

View File

@ -61,8 +61,8 @@ const mapDispatchToProps = (dispatch) => {
dispatch(unregisterToken(index)); dispatch(unregisterToken(index));
}, },
handleAddMeta: (index, key, value) => { handleAddMeta: (index, key, value, validationType) => {
dispatch(addTokenMeta(index, key, value)); dispatch(addTokenMeta(index, key, value, validationType));
} }
}; };
}; };

View File

@ -14,7 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { getTokenTotalSupply } from '../utils'; import { URL_TYPE } from '../Inputs/validation';
import { getTokenTotalSupply, urlToHash } from '../utils';
const { bytesToHex } = window.parity.api.util; 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 state = getState();
const contractInstance = state.status.contract.instance; const contractInstance = state.status.contract.instance;
const ghhInstance = state.status.githubhint.instance;
const token = state.tokens.tokens.find(t => t.index === index); const token = state.tokens.tokens.find(t => t.index === index);
const options = { from: token.owner }; const options = { from: token.owner };
const values = [ index, key, value ]; let valuesPromise;
contractInstance // 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 .setMeta
.estimateGas(options, values) .estimateGas(options, values)
.then((gasEstimate) => { .then((gasEstimate) => {
options.gas = gasEstimate.mul(1.2).toFixed(0); options.gas = gasEstimate.mul(1.2).toFixed(0);
return contractInstance.setMeta.postTransaction(options, values); return contractInstance.setMeta.postTransaction(options, values);
});
}) })
.catch((e) => { .catch((e) => {
console.error(`addTokenMeta: #${index} error`, e); console.error(`addTokenMeta: #${index} error`, e);
}); });
}; };
export const addGithubhintURL = (from, key, url) => (dispatch, getState) => { export const addGithubhintURL = (ghhInstance, _options, url) => {
const state = getState(); return urlToHash(ghhInstance, url)
const contractInstance = state.status.githubhint.instance; .then((result) => {
const options = { from }; const { hash, registered } = result;
const values = [ key, url ];
contractInstance if (registered) {
return hash;
}
const options = { from: _options.from };
const values = [ hash, url ];
ghhInstance
.hintURL .hintURL
.estimateGas(options, values) .estimateGas(options, values)
.then((gasEstimate) => { .then((gasEstimate) => {
options.gas = gasEstimate.mul(1.2).toFixed(0); options.gas = gasEstimate.mul(1.2).toFixed(0);
return contractInstance.hintURL.postTransaction(options, values); return ghhInstance.hintURL.postTransaction(options, values);
}) })
.catch((e) => { .catch((error) => {
console.error('addGithubhintURL error', e); console.error(`registering "${url}" to GHH`, error);
});
return hash;
}); });
}; };

View File

@ -14,13 +14,13 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { HEX_TYPE, ADDRESS_TYPE } from './Inputs/validation'; import { URL_TYPE, ADDRESS_TYPE } from './Inputs/validation';
export const metaDataKeys = [ export const metaDataKeys = [
{ {
label: 'Image', label: 'Image',
value: 'IMG', value: 'IMG',
validation: HEX_TYPE validation: URL_TYPE
}, },
{ {
label: 'Address', label: 'Address',

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
const { api } = window.parity; const api = window.parent.secureApi;
export { export {
api api

View File

@ -18,6 +18,45 @@ import { api } from './parity';
import { eip20 as eip20Abi } from '~/contracts/abi'; 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) => { export const getTokenTotalSupply = (tokenAddress) => {
return api return api
.eth .eth

View File

@ -32,7 +32,8 @@
"description": "A registry of transactable tokens on the network", "description": "A registry of transactable tokens on the network",
"author": "Parity Team <admin@ethcore.io>", "author": "Parity Team <admin@ethcore.io>",
"version": "1.0.0", "version": "1.0.0",
"visible": true "visible": true,
"secure": true
}, },
{ {
"id": "0xf49089046f53f5d2e5f3513c1c32f5ff57d986e46309a42d2b249070e4e72c46", "id": "0xf49089046f53f5d2e5f3513c1c32f5ff57d986e46309a42d2b249070e4e72c46",