Merge pull request #27 from MyEtherWallet/donation_addresses

Fix instances where donation addresses were hardcoded
This commit is contained in:
Daniel Ternyak 2017-07-03 19:27:22 -05:00 committed by GitHub
commit ceb520d051
4 changed files with 308 additions and 296 deletions

View File

@ -2,37 +2,38 @@
import React from 'react'; import React from 'react';
import translate from 'translations'; import translate from 'translations';
import { donationAddressMap } from 'config/data';
export default class Donate extends React.Component { export default class Donate extends React.Component {
props: { props: {
onDonate: (address: string, amount: string, unit: string) => void onDonate: (address: string, amount: string, unit: string) => void
}; };
state: { state: {
clicked: boolean clicked: boolean
} = { } = {
clicked: false clicked: false
}; };
render() { render() {
return ( return (
<div className="well"> <div className="well">
<p> <p>
{translate('sidebar_donation')} {translate('sidebar_donation')}
</p> </p>
<a className="btn btn-primary btn-block" onClick={this.onClick}> <a className="btn btn-primary btn-block" onClick={this.onClick}>
{translate('sidebar_donate')} {translate('sidebar_donate')}
</a> </a>
{this.state.clicked && {this.state.clicked &&
<div className="text-success text-center marg-v-sm"> <div className="text-success text-center marg-v-sm">
{translate('sidebar_thanks')} {translate('sidebar_thanks')}
</div>} </div>}
</div> </div>
); );
} }
onClick = () => { onClick = () => {
// FIXME move to config // FIXME move to config
this.props.onDonate('0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8', '1', 'ETH'); this.props.onDonate(donationAddressMap.ETH, '1', 'ETH');
this.setState({ clicked: true }); this.setState({ clicked: true });
}; };
} }

View File

@ -5,266 +5,275 @@ import PropTypes from 'prop-types';
import translate from 'translations'; import translate from 'translations';
import { UnlockHeader } from 'components/ui'; import { UnlockHeader } from 'components/ui';
import { import {
Donate, Donate,
DataField, DataField,
CustomMessage, CustomMessage,
GasField, GasField,
AmountField, AmountField,
AddressField AddressField
} from './components'; } from './components';
import pickBy from 'lodash/pickBy'; import pickBy from 'lodash/pickBy';
// import type { Transaction } from './types'; // import type { Transaction } from './types';
import customMessages from './messages'; import customMessages from './messages';
import { donationAddressMap } from 'config/data';
type State = { type State = {
hasQueryString: boolean, hasQueryString: boolean,
readOnly: boolean, readOnly: boolean,
to: string, to: string,
value: string, value: string,
unit: string, unit: string,
gasLimit: string, gasLimit: string,
data: string, data: string,
gasChanged: boolean gasChanged: boolean
}; };
function getParam(query: { [string]: string }, key: string) { function getParam(query: { [string]: string }, key: string) {
const keys = Object.keys(query); const keys = Object.keys(query);
const index = keys.findIndex(k => k.toLowerCase() === key.toLowerCase()); const index = keys.findIndex(k => k.toLowerCase() === key.toLowerCase());
if (index === -1) { if (index === -1) {
return null; return null;
} }
return query[keys[index]]; return query[keys[index]];
} }
// TODO query string // TODO query string
// TODO how to handle DATA? // TODO how to handle DATA?
export class SendTransaction extends React.Component { export class SendTransaction extends React.Component {
static propTypes = { static propTypes = {
location: PropTypes.object.isRequired location: PropTypes.object.isRequired
}; };
props: { props: {
location: { location: {
query: { query: {
[string]: string [string]: string
} }
} }
}; };
state: State = { state: State = {
hasQueryString: false, hasQueryString: false,
readOnly: false, readOnly: false,
// FIXME use correct defaults // FIXME use correct defaults
to: '', to: '',
value: '999.11', value: '999.11',
unit: 'ether', unit: 'ether',
gasLimit: '21000', gasLimit: '21000',
data: '', data: '',
gasChanged: false gasChanged: false
}; };
componentDidMount() { componentDidMount() {
const queryPresets = pickBy(this.parseQuery()); const queryPresets = pickBy(this.parseQuery());
if (Object.keys(queryPresets).length) { if (Object.keys(queryPresets).length) {
this.setState({ ...queryPresets, hasQueryString: true }); this.setState({ ...queryPresets, hasQueryString: true });
}
this.setState(pickBy(queryPresets));
} }
render() { this.setState(pickBy(queryPresets));
const unlocked = true; //wallet != null }
const unitReadable = 'UNITREADABLE';
const nodeUnit = 'NODEUNIT';
const hasEnoughBalance = false;
const { to, value, unit, gasLimit, data, readOnly, hasQueryString } = this.state;
const customMessage = customMessages.find(m => m.to === to);
// tokens render() {
// ng-show="token.balance!=0 && token.balance!='loading' || token.type!=='default' || tokenVisibility=='shown'" const unlocked = true; //wallet != null
const unitReadable = 'UNITREADABLE';
const nodeUnit = 'NODEUNIT';
const hasEnoughBalance = false;
const {
to,
value,
unit,
gasLimit,
data,
readOnly,
hasQueryString
} = this.state;
const customMessage = customMessages.find(m => m.to === to);
return ( // tokens
<section className="container" style={{ minHeight: '50%' }}> // ng-show="token.balance!=0 && token.balance!='loading' || token.type!=='default' || tokenVisibility=='shown'"
<div className="tab-content">
<main className="tab-pane active" ng-controller="sendTxCtrl">
{hasQueryString && return (
<div className="alert alert-info"> <section className="container" style={{ minHeight: '50%' }}>
<p> <div className="tab-content">
{translate('WARN_Send_Link')} <main className="tab-pane active">
</p>
</div>}
<UnlockHeader title={'NAV_SendEther'} /> {hasQueryString &&
<div className="alert alert-info">
<p>
{translate('WARN_Send_Link')}
</p>
</div>}
{unlocked && <UnlockHeader title={'NAV_SendEther'} />
<article className="row">
{'' /* <!-- Sidebar --> */}
<section className="col-sm-4">
<div style={{ maxWidth: 350 }}>
{'' /* <wallet-balance-drtv /> */}
<hr />
<Donate onDonate={this.onNewTx} />
</div>
</section>
<section className="col-sm-8"> {unlocked &&
{readOnly && <article className="row">
!hasEnoughBalance && {'' /* <!-- Sidebar --> */}
<div className="row form-group"> <section className="col-sm-4">
<div className="alert alert-danger col-xs-12 clearfix"> <div style={{ maxWidth: 350 }}>
<strong> {'' /* <wallet-balance-drtv /> */}
Warning! You do not have enough funds to <hr />
complete this swap. <Donate onDonate={this.onNewTx} />
</strong> </div>
{' '} </section>
<br />
Please add more funds or access a different wallet.
</div>
</div>}
<div className="row form-group"> <section className="col-sm-8">
<h4 className="col-xs-12"> {readOnly &&
{translate('SEND_trans')} !hasEnoughBalance &&
</h4> <div className="row form-group">
</div> <div className="alert alert-danger col-xs-12 clearfix">
<AddressField <strong>
placeholder="0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8" Warning! You do not have enough funds to
value={this.state.to} complete this swap.
onChange={readOnly ? null : this.onAddressChange} </strong>
/> {' '}
<AmountField <br />
value={value} Please add more funds or access a different wallet.
unit={unit} </div>
onChange={readOnly ? void 0 : this.onAmountChange} </div>}
/>
<GasField
value={gasLimit}
onChange={readOnly ? void 0 : this.onGasChange}
/>
{unit === 'ether' &&
<DataField
value={data}
onChange={readOnly ? void 0 : this.onDataChange}
/>}
<CustomMessage message={customMessage} />
<div className="row form-group"> <div className="row form-group">
<div className="col-xs-12 clearfix"> <h4 className="col-xs-12">
<a {translate('SEND_trans')}
className="btn btn-info btn-block" </h4>
onClick={this.generateTx} </div>
> <AddressField
{translate('SEND_generate')} placeholder={donationAddressMap.ETH}
</a> value={this.state.to}
</div> onChange={readOnly ? null : this.onAddressChange}
</div> />
<AmountField
value={value}
unit={unit}
onChange={readOnly ? void 0 : this.onAmountChange}
/>
<GasField
value={gasLimit}
onChange={readOnly ? void 0 : this.onGasChange}
/>
{unit === 'ether' &&
<DataField
value={data}
onChange={readOnly ? void 0 : this.onDataChange}
/>}
<CustomMessage message={customMessage} />
<div className="row form-group" ng-show="showRaw"> <div className="row form-group">
<div className="col-sm-6"> <div className="col-xs-12 clearfix">
<label translate="SEND_raw"> Raw Transaction </label> <a
<textarea className="form-control" rows="4" readOnly> className="btn btn-info btn-block"
{'' /*rawTx*/} onClick={this.generateTx}
</textarea> >
</div> {translate('SEND_generate')}
<div className="col-sm-6"> </a>
<label translate="SEND_signed"> </div>
{' '}Signed Transaction{' '} </div>
</label>
<textarea className="form-control" rows="4" readOnly>
{'' /*signedTx*/}
</textarea>
</div>
</div>
<div className="form-group" ng-show="showRaw"> <div className="row form-group" ng-show="showRaw">
<a <div className="col-sm-6">
className="btn btn-primary btn-block col-sm-11" <label translate="SEND_raw"> Raw Transaction </label>
data-toggle="modal" <textarea className="form-control" rows="4" readOnly>
data-target="#sendTransaction" {'' /*rawTx*/}
translate="SEND_trans" </textarea>
> </div>
{' '}Send Transaction{' '} <div className="col-sm-6">
</a> <label translate="SEND_signed">
</div> {' '}Signed Transaction{' '}
</label>
<textarea className="form-control" rows="4" readOnly>
{'' /*signedTx*/}
</textarea>
</div>
</div>
</section> <div className="form-group" ng-show="showRaw">
{'' /* <!-- / Content --> */} <a
{ className="btn btn-primary btn-block col-sm-11"
'' /* @@if (site === 'mew' ) { @@include( './sendTx-content.tpl', { "site": "mew" } ) } data-toggle="modal"
data-target="#sendTransaction"
translate="SEND_trans"
>
{' '}Send Transaction{' '}
</a>
</div>
</section>
{'' /* <!-- / Content --> */}
{
'' /* @@if (site === 'mew' ) { @@include( './sendTx-content.tpl', { "site": "mew" } ) }
@@if (site === 'cx' ) { @@include( './sendTx-content.tpl', { "site": "cx" } ) } @@if (site === 'cx' ) { @@include( './sendTx-content.tpl', { "site": "cx" } ) }
@@if (site === 'mew' ) { @@include( './sendTx-modal.tpl', { "site": "mew" } ) } @@if (site === 'mew' ) { @@include( './sendTx-modal.tpl', { "site": "mew" } ) }
@@if (site === 'cx' ) { @@include( './sendTx-modal.tpl', { "site": "cx" } ) } */ @@if (site === 'cx' ) { @@include( './sendTx-modal.tpl', { "site": "cx" } ) } */
} }
</article>} </article>}
</main> </main>
</div> </div>
</section> </section>
); );
}
parseQuery() {
const query = this.props.location.query;
const to = getParam(query, 'to');
const data = getParam(query, 'data');
// FIXME validate token against presets
const unit = getParam(query, 'tokenSymbol');
const value = getParam(query, 'value');
let gasLimit = getParam(query, 'gas');
if (gasLimit === null) {
gasLimit = getParam(query, 'limit');
} }
const readOnly = getParam(query, 'readOnly') == null ? false : true;
parseQuery() { return { to, data, value, unit, gasLimit, readOnly };
const query = this.props.location.query; }
const to = getParam(query, 'to');
const data = getParam(query, 'data');
// FIXME validate token against presets
const unit = getParam(query, 'tokenSymbol');
const value = getParam(query, 'value');
let gasLimit = getParam(query, 'gas');
if (gasLimit === null) {
gasLimit = getParam(query, 'limit');
}
const readOnly = getParam(query, 'readOnly') == null ? false : true;
return { to, data, value, unit, gasLimit, readOnly }; // FIXME use mkTx instead or something that could take care of default gas/data and whatnot,
// FIXME also should it reset gasChanged?
onNewTx = (
address: string,
amount: string,
unit: string,
data: string = '',
gasLimit: string = '21000'
) => {
this.setState({
to: address,
value: amount,
unit,
data,
gasLimit,
gasChanged: false
});
};
onAddressChange = (value: string) => {
this.setState({
to: value
});
};
onDataChange = (value: string) => {
if (this.state.unit !== 'ether') {
return;
} }
this.setState({
...this.state,
data: value
});
};
// FIXME use mkTx instead or something that could take care of default gas/data and whatnot, onGasChange = (value: string) => {
// FIXME also should it reset gasChanged? this.setState({ gasLimit: value, gasChanged: true });
onNewTx = ( };
address: string,
amount: string,
unit: string,
data: string = '',
gasLimit: string = '21000'
) => {
this.setState({
to: address,
value: amount,
unit,
data,
gasLimit,
gasChanged: false
});
};
onAddressChange = (value: string) => { onAmountChange = (value: string, unit: string) => {
this.setState({ this.setState({
to: value value,
}); unit
}; });
};
onDataChange = (value: string) => {
if (this.state.unit !== 'ether') {
return;
}
this.setState({
...this.state,
data: value
});
};
onGasChange = (value: string) => {
this.setState({ gasLimit: value, gasChanged: true });
};
onAmountChange = (value: string, unit: string) => {
this.setState({
value,
unit
});
};
} }
// export connected version // export connected version
export default SendTransaction; export default SendTransaction;

View File

@ -1,32 +1,33 @@
// @flow // @flow
import { donationAddressMap } from 'config/data';
export default [ export default [
{ {
// donation address example // donation address example
to: '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8', to: donationAddressMap.ETH,
gasLimit: 21000, gasLimit: 21000,
data: '', data: '',
msg: 'Thank you for donating to MyEtherWallet. TO THE MOON!' msg: 'Thank you for donating to MyEtherWallet. TO THE MOON!'
}, },
{ {
// BAT // BAT
to: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF', to: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF',
gasLimit: 200000, gasLimit: 200000,
data: '0xb4427263', data: '0xb4427263',
msg: 'BAT. THE SALE IS OVER. STOP CLOGGING THE BLOCKCHAIN PLEASE' msg: 'BAT. THE SALE IS OVER. STOP CLOGGING THE BLOCKCHAIN PLEASE'
}, },
{ {
// BANCOR // BANCOR
to: '0x00000', to: '0x00000',
gasLimit: 200000, gasLimit: 200000,
data: '', data: '',
msg: 'Bancor. Starts June XX, 2017.' msg: 'Bancor. Starts June XX, 2017.'
}, },
{ {
// Moeda // Moeda
to: '0x4870E705a3def9DDa6da7A953D1cd3CCEDD08573', to: '0x4870E705a3def9DDa6da7A953D1cd3CCEDD08573',
gasLimit: 200000, gasLimit: 200000,
data: '', data: '',
msg: 'Moeda. Ends at block 4,111,557.' msg: 'Moeda. Ends at block 4,111,557.'
} }
]; ];

View File

@ -4,30 +4,31 @@ import { delay } from 'redux-saga';
import { cacheEnsAddress } from 'actions/ens'; import { cacheEnsAddress } from 'actions/ens';
import type { ResolveEnsNameAction } from 'actions/ens'; import type { ResolveEnsNameAction } from 'actions/ens';
import { getEnsAddress } from 'selectors/ens'; import { getEnsAddress } from 'selectors/ens';
import { donationAddressMap } from 'config/data';
function* resolveEns(action: ResolveEnsNameAction) { function* resolveEns(action: ResolveEnsNameAction) {
const ensName = action.payload; const ensName = action.payload;
// FIXME Add resolve logic // FIXME Add resolve logic
//// _ens.getAddress(scope.addressDrtv.ensAddressField, function(data) { //// _ens.getAddress(scope.addressDrtv.ensAddressField, function(data) {
// if (data.error) uiFuncs.notifier.danger(data.msg); // if (data.error) uiFuncs.notifier.danger(data.msg);
// else if (data.data == '0x0000000000000000000000000000000000000000' || data.data == '0x') { // else if (data.data == '0x0000000000000000000000000000000000000000' || data.data == '0x') {
// setValue('0x0000000000000000000000000000000000000000'); // setValue('0x0000000000000000000000000000000000000000');
// scope.addressDrtv.derivedAddress = '0x0000000000000000000000000000000000000000'; // scope.addressDrtv.derivedAddress = '0x0000000000000000000000000000000000000000';
// scope.addressDrtv.showDerivedAddress = true; // scope.addressDrtv.showDerivedAddress = true;
// } else { // } else {
// setValue(data.data); // setValue(data.data);
// scope.addressDrtv.derivedAddress = ethUtil.toChecksumAddress(data.data); // scope.addressDrtv.derivedAddress = ethUtil.toChecksumAddress(data.data);
// scope.addressDrtv.showDerivedAddress = true; // scope.addressDrtv.showDerivedAddress = true;
const cachedEnsAddress = yield select(getEnsAddress, ensName); const cachedEnsAddress = yield select(getEnsAddress, ensName);
if (cachedEnsAddress) { if (cachedEnsAddress) {
return; return;
} }
yield call(delay, 1000); yield call(delay, 1000);
yield put(cacheEnsAddress(ensName, '0x7cB57B5A97eAbe94205C07890BE4c1aD31E486A8')); yield put(cacheEnsAddress(ensName, donationAddressMap.ETH));
} }
export default function* notificationsSaga() { export default function* notificationsSaga() {
yield takeEvery('ENS_RESOLVE', resolveEns); yield takeEvery('ENS_RESOLVE', resolveEns);
} }