(Feature) `AreBallotParamsValid` check before creating of ballot for keys (#75)

* Show if ballot is finalized

* Display US date format

* Codacy suggestion: Expected { after 'if' condition. (curly)

* Time to start

* typo

* Ballot is inactive alert

* Display invalid key address in alert

* errors catches

* dynamic year loading for footer

* Another format expression

* Codacy suggestions: A function with a name starting with an uppercase letter should only be used as a constructor. (new-cap)

* Codacy suggestions: semilolons and doublequotes

* messages typo

* AreBallotParamsValid check before creating of ballot for keys

* Codacy suggestions: Expected '===' and instead saw '=='. (eqeqeq); Unexpected console statement. (no-console); Missing semicolon. (semi)
This commit is contained in:
Victor Baranov 2018-01-17 03:05:12 +03:00 committed by Roman Storm
parent 8a77e489ba
commit 236630fe23
12 changed files with 141 additions and 92 deletions

View File

@ -64,7 +64,6 @@ class App extends Component {
<Route path={`${commonStore.rootPath}/new`} render={this.onNewBallotRender}/>
{/*<Route path={`${commonStore.rootPath}/settings`} render={this.onSettingsRender}/>*/}
<Footer />
{/*<DevTools />*/}
</div>
);
}

View File

@ -4,6 +4,7 @@ import { observable, action, computed } from "mobx";
import { inject, observer } from "mobx-react";
import { toAscii } from "../helpers";
import { constants } from "../constants";
import { messages } from "../messages";
import swal from "sweetalert2";
const ACCEPT = 1;
@ -85,7 +86,6 @@ export class BallotCard extends React.Component {
this.endTime = moment.utc(endTime * 1000).format(USDateTimeFormat);
}
@action("Calculate time to start/finish")
calcTimeTo = () => {
const _now = moment();
@ -177,23 +177,27 @@ export class BallotCard extends React.Component {
}
vote = async ({choice}) => {
if (this.timeToStart.val > 0) {
swal("Warning!", messages.ballotIsNotActiveMsg(this.timeTo.displayValue), "warning");
return;
}
const { commonStore, contractsStore, id, votingType } = this.props;
const { push } = this.props.routing;
if (!contractsStore.isValidVotingKey) {
swal("Warning!", constants.INVALID_VOTING_KEY_MSG, "warning");
swal("Warning!", messages.invalidVotingKeyMsg(contractsStore.votingKey), "warning");
return;
}
commonStore.showLoading();
let isValidVote = await this.isValidaVote();
if (!isValidVote) {
commonStore.hideLoading();
swal("Warning!", constants.INVALID_VOTE_MSG, "warning");
swal("Warning!", messages.INVALID_VOTE_MSG, "warning");
return;
}
this.getContract(contractsStore, votingType).vote(id, choice, contractsStore.votingKey)
.on("receipt", () => {
commonStore.hideLoading();
swal("Congratulations!", constants.VOTED_SUCCESS_MSG, "success").then((result) => {
swal("Congratulations!", messages.VOTED_SUCCESS_MSG, "success").then((result) => {
push(`${commonStore.rootPath}`);
});
})
@ -205,27 +209,32 @@ export class BallotCard extends React.Component {
finalize = async (e) => {
if (this.isFinalized) { return; }
if (this.timeToStart.val > 0) {
swal("Warning!", messages.ballotIsNotActiveMsg(this.timeTo.displayValue), "warning");
return;
}
const { commonStore, contractsStore, id, votingType } = this.props;
const { push } = this.props.routing;
if (!contractsStore.isValidVotingKey) {
swal("Warning!", constants.INVALID_VOTING_KEY_MSG, "warning");
swal("Warning!", messages.invalidVotingKeyMsg(contractsStore.votingKey), "warning");
return;
}
if (this.isFinalized) {
swal("Warning!", constants.ALREADY_FINALIZED_MSG, "warning");
swal("Warning!", messages.ALREADY_FINALIZED_MSG, "warning");
return;
}
commonStore.showLoading();
let isActive = await this.isActive();
if (isActive) {
commonStore.hideLoading();
swal("Warning!", constants.INVALID_FINALIZE_MSG, "warning");
swal("Warning!", messages.INVALID_FINALIZE_MSG, "warning");
return;
}
this.getContract(contractsStore, votingType).finalize(id, contractsStore.votingKey)
.on("receipt", () => {
commonStore.hideLoading();
swal("Congratulations!", constants.FINALIZED_SUCCESS_MSG, "success").then((result) => {
swal("Congratulations!", messages.FINALIZED_SUCCESS_MSG, "success").then((result) => {
push(`${commonStore.rootPath}`);
});
})

View File

@ -1,10 +1,11 @@
import React from 'react';
import { Link } from 'react-router-dom';
import React from "react";
import moment from "moment";
import { Link } from "react-router-dom";
export const Footer = () => (
<footer className="footer">
<div className="container">
<p className="footer-rights">2017 POA Network. All rights reserved.</p>
<p className="footer-rights">{moment().format("YYYY")} POA Network. All rights reserved.</p>
<Link to="/poa-dapps-voting" className="footer-logo"></Link>
<div className="socials">
<a href="https://twitter.com/poanetwork" className="socials-i socials-i_twitter"></a>

View File

@ -7,7 +7,7 @@ import { KeysTypes } from './KeysTypes';
import { BallotKeysMetadata } from './BallotKeysMetadata';
import { BallotMinThresholdMetadata } from './BallotMinThresholdMetadata';
import { BallotProxyMetadata } from './BallotProxyMetadata';
import { constants } from "../constants";
import { messages } from "../messages";
@inject("commonStore", "ballotStore", "validatorStore", "contractsStore", "routing")
@observer
@ -32,7 +32,7 @@ export class NewBallot extends React.Component {
}
if (!isAfter) {
swal("Warning!", constants.END_TIME_SHOULD_BE_GREATER_THAN_NOW_MSG, "warning");
swal("Warning!", messages.END_TIME_SHOULD_BE_GREATER_THAN_NOW_MSG, "warning");
commonStore.hideLoading();
return false;
}
@ -49,14 +49,14 @@ export class NewBallot extends React.Component {
let isAffectedKeyAddress = contractsStore.web3Instance.isAddress(ballotStore.ballotKeys.affectedKey);
if (!isAffectedKeyAddress) {
swal("Warning!", constants.AFFECTED_KEY_IS_NOT_ADDRESS_MSG, "warning");
swal("Warning!", messages.AFFECTED_KEY_IS_NOT_ADDRESS_MSG, "warning");
commonStore.hideLoading();
return false;
}
let isMiningKeyAddress = contractsStore.web3Instance.isAddress(ballotStore.ballotKeys.miningKey.value);
if (!isMiningKeyAddress) {
swal("Warning!", constants.MINING_KEY_IS_NOT_ADDRESS_MSG, "warning");
swal("Warning!", messages.MINING_KEY_IS_NOT_ADDRESS_MSG, "warning");
commonStore.hideLoading();
return false;
}
@ -84,7 +84,7 @@ export class NewBallot extends React.Component {
let isAddress = contractsStore.web3Instance.isAddress(ballotStore.ballotProxy.proposedAddress);
if (!isAddress) {
swal("Warning!", constants.PROPOSED_ADDRESS_IS_NOT_ADDRESS_MSG, "warning");
swal("Warning!", messages.PROPOSED_ADDRESS_IS_NOT_ADDRESS_MSG, "warning");
commonStore.hideLoading();
return false;
}
@ -95,47 +95,41 @@ export class NewBallot extends React.Component {
createBallotForKeys = (curDateInSeconds) => {
const { ballotStore, contractsStore } = this.props;
const inputToMethod = [
curDateInSeconds,
ballotStore.endTimeUnix,
ballotStore.ballotKeys.affectedKey,
ballotStore.ballotKeys.keyType,
ballotStore.ballotKeys.miningKey.value,
ballotStore.ballotKeys.keysBallotType,
contractsStore.votingKey
];
let method = contractsStore.votingToChangeKeys.createVotingForKeys(
...inputToMethod
);
const inputToMethod = {
startTime: curDateInSeconds,
endTime: ballotStore.endTimeUnix,
affectedKey: ballotStore.ballotKeys.affectedKey,
affectedKeyType: ballotStore.ballotKeys.keyType,
miningKey: ballotStore.ballotKeys.miningKey.value,
ballotType: ballotStore.ballotKeys.keysBallotType,
sender: contractsStore.votingKey
};
let method = contractsStore.votingToChangeKeys.createVotingForKeys(inputToMethod);
return method;
}
createBallotForMinThreshold = (curDateInSeconds) => {
const { ballotStore, contractsStore } = this.props;
const inputToMethod = [
curDateInSeconds,
ballotStore.endTimeUnix,
ballotStore.ballotMinThreshold.proposedValue,
contractsStore.votingKey
];
let method = contractsStore.votingToChangeMinThreshold.createBallotToChangeThreshold(
...inputToMethod
);
const inputToMethod = {
startTime: curDateInSeconds,
endTime: ballotStore.endTimeUnix,
proposedValue: ballotStore.ballotMinThreshold.proposedValue,
sender: contractsStore.votingKey
};
let method = contractsStore.votingToChangeMinThreshold.createBallotToChangeThreshold(inputToMethod);
return method;
}
createBallotForProxy = (curDateInSeconds) => {
const { ballotStore, contractsStore } = this.props;
const inputToMethod = [
curDateInSeconds,
ballotStore.endTimeUnix,
ballotStore.ballotProxy.proposedAddress,
ballotStore.ballotProxy.contractType,
contractsStore.votingKey
];
let method = contractsStore.votingToChangeProxy.createBallotToChangeProxyAddress(
...inputToMethod
);
const inputToMethod = {
startTime: curDateInSeconds,
endTime: ballotStore.endTimeUnix,
proposedValue: ballotStore.ballotProxy.proposedAddress,
contractType: ballotStore.ballotProxy.contractType,
sender: contractsStore.votingKey
};
let method = contractsStore.votingToChangeProxy.createBallotToChangeProxyAddress(inputToMethod);
return method;
}
@ -146,11 +140,25 @@ export class NewBallot extends React.Component {
const isValidVotingKey = contractsStore.isValidVotingKey;
if (!isValidVotingKey) {
commonStore.hideLoading();
swal("Warning!", constants.INVALID_VOTING_KEY_MSG, "warning");
swal("Warning!", messages.invalidVotingKeyMsg(contractsStore.votingKey), "warning");
return;
}
const isFormValid = this.checkValidation();
if (isFormValid) {
if (ballotStore.ballotType === ballotStore.BallotType.keys) {
const inputToAreBallotParamsValid = {
affectedKey: ballotStore.ballotKeys.affectedKey,
affectedKeyType: ballotStore.ballotKeys.keyType,
miningKey: ballotStore.ballotKeys.miningKey.value,
ballotType: ballotStore.ballotKeys.keysBallotType
};
let areBallotParamsValid = await contractsStore.votingToChangeKeys.areBallotParamsValid(inputToAreBallotParamsValid);
if (!areBallotParamsValid) {
commonStore.hideLoading();
return swal("Warning!", "The ballot input params are invalid", "warning");
}
}
let methodToCreateBallot;
switch (ballotStore.ballotType) {
case ballotStore.BallotType.keys:
@ -170,7 +178,7 @@ export class NewBallot extends React.Component {
methodToCreateBallot(curDateInSeconds)
.on("receipt", () => {
commonStore.hideLoading();
swal("Congratulations!", constants.BALLOT_CREATED_SUCCESS_MSG, "success").then((result) => {
swal("Congratulations!", messages.BALLOT_CREATED_SUCCESS_MSG, "success").then((result) => {
push(`${commonStore.rootPath}`);
});
})

View File

@ -1,21 +1,4 @@
let constants = {};
constants.INVALID_VOTING_KEY_MSG = "The key is not valid voting Key! Please make sure you have loaded correct voting key in metamask";
constants.VOTED_SUCCESS_MSG = "You successfully voted";
constants.BALLOT_CREATED_SUCCESS_MSG = "You successfully created a new ballot";
constants.FINALIZED_SUCCESS_MSG = "You successfully finalized";
constants.ALREADY_FINALIZED_MSG = "This ballot is already finalized";
constants.INVALID_VOTE_MSG = "You can't vote on this ballot";
constants.INVALID_FINALIZE_MSG = "You can't finalize this ballot";
constants.AFFECTED_KEY_IS_NOT_ADDRESS_MSG = `Ballot affectedKey isn't address`;
constants.MINING_KEY_IS_NOT_ADDRESS_MSG = `Ballot miningKey isn't address`;
constants.PROPOSED_ADDRESS_IS_NOT_ADDRESS_MSG = `Ballot proposedAddress isn't address`;
constants.END_TIME_SHOULD_BE_GREATER_THAN_NOW_MSG = "Ballot end time should be greater than now";
constants.NO_METAMASK_MSG = `You haven't chosen any account in MetaMask.
Please, choose your voting key in MetaMask and reload the page.
Check POA Network <a href='https://github.com/poanetwork/wiki' target='blank'>wiki</a> for more info.`;
constants.WRONG_NETWORK_MSG = `You aren't connected to POA Network.
Please, switch on POA plugin and refresh the page.
Check POA Network <a href='https://github.com/poanetwork/wiki' target='blank'>wiki</a> for more info.`;
constants.CARD_FINALIZE_DESCRIPTION = "Finalization is available after ballot time is finished";
module.exports = {
constants

View File

@ -11,7 +11,7 @@ export default class VotingToChangeKeys {
}
//setters
createVotingForKeys(startTime, endTime, affectedKey, affectedKeyType, miningKey, ballotType, sender) {
createVotingForKeys({startTime, endTime, affectedKey, affectedKeyType, miningKey, ballotType, sender}) {
return this.votingToChangeKeysInstance.methods.createVotingForKeys(startTime, endTime, affectedKey, affectedKeyType, miningKey, ballotType).send({from: sender});
}
@ -24,6 +24,10 @@ export default class VotingToChangeKeys {
}
//getters
areBallotParamsValid({ballotType, affectedKey, affectedKeyType, miningKey}) {
return this.votingToChangeKeysInstance.methods.areBallotParamsValid(ballotType, affectedKey, affectedKeyType, miningKey).call();
}
getStartTime(_id) {
return this.votingToChangeKeysInstance.methods.getStartTime(_id).call();
}
@ -73,7 +77,13 @@ export default class VotingToChangeKeys {
}
async getValidatorActiveBallots(_votingKey) {
const miningKey = await this.getMiningByVotingKey(_votingKey);
let miningKey;
try {
miningKey = await this.getMiningByVotingKey(_votingKey);
}
catch(e) {
miningKey = "0x0000000000000000000000000000000000000000";
}
return await this.votingToChangeKeysInstance.methods.validatorActiveBallots(miningKey).call();
}

View File

@ -6,12 +6,12 @@ export default class VotingToChangeMinThreshold {
constructor({web3, netId}){
const {VOTING_TO_CHANGE_MIN_THRESHOLD} = networkAddresses(netId);
let web3_10 = new Web3(web3.currentProvider);
console.log('VotingToChangeMinThreshold ', VOTING_TO_CHANGE_MIN_THRESHOLD)
console.log('VotingToChangeMinThreshold ', VOTING_TO_CHANGE_MIN_THRESHOLD);
this.votingToChangeMinThresholdInstance = new web3_10.eth.Contract(votingToChangeMinThresholdABI, VOTING_TO_CHANGE_MIN_THRESHOLD);
}
//setters
createBallotToChangeThreshold(startTime, endTime, proposedValue, sender) {
createBallotToChangeThreshold({startTime, endTime, proposedValue, sender}) {
return this.votingToChangeMinThresholdInstance.methods.createBallotToChangeThreshold(startTime, endTime, proposedValue).send({from: sender})
}
@ -65,7 +65,13 @@ export default class VotingToChangeMinThreshold {
}
async getValidatorActiveBallots(_votingKey) {
const miningKey = await this.getMiningByVotingKey(_votingKey);
let miningKey;
try {
miningKey = await this.getMiningByVotingKey(_votingKey);
}
catch(e) {
miningKey = "0x0000000000000000000000000000000000000000";
}
return await this.votingToChangeMinThresholdInstance.methods.validatorActiveBallots(miningKey).call();
}

View File

@ -11,7 +11,7 @@ export default class VotingToChangeProxy {
}
//setters
createBallotToChangeProxyAddress(startTime, endTime, proposedValue, contractType, sender) {
createBallotToChangeProxyAddress({startTime, endTime, proposedValue, contractType, sender}) {
return this.votingToChangeProxyInstance.methods.createBallotToChangeProxyAddress(startTime, endTime, proposedValue, contractType).send({from: sender})
}
@ -69,7 +69,13 @@ export default class VotingToChangeProxy {
}
async getValidatorActiveBallots(_votingKey) {
const miningKey = await this.getMiningByVotingKey(_votingKey);
let miningKey;
try {
miningKey = await this.getMiningByVotingKey(_votingKey);
}
catch(e) {
miningKey = "0x0000000000000000000000000000000000000000";
}
return await this.votingToChangeProxyInstance.methods.validatorActiveBallots(miningKey).call();
}

View File

@ -1,4 +1,4 @@
import { constants } from "./constants";
import { messages } from "./messages";
let getWeb3 = () => {
return new Promise(function (resolve, reject) {
@ -26,13 +26,13 @@ let getWeb3 = () => {
break;
default:
netIdName = 'ERROR'
errorMsg = constants.WRONG_NETWORK_MSG
errorMsg = messages.WRONG_NETWORK_MSG
console.log('This is an unknown network.', netId)
}
document.title = `${netIdName} - POA Network Governance DApp`
var defaultAccount = web3.eth.defaultAccount || null;
if(defaultAccount === null){
reject({message: constants.NO_METAMASK_MSG})
reject({message: messages.NO_METAMASK_MSG})
}
if(errorMsg !== null){
reject({message: errorMsg})
@ -50,7 +50,7 @@ let getWeb3 = () => {
console.log('Injected web3 detected.');
} else {
reject({message: constants.NO_METAMASK_MSG})
reject({message: messages.NO_METAMASK_MSG})
console.error('Metamask not found');
}
})

View File

@ -51,9 +51,8 @@ class AppMainRouter extends Component {
}).catch((error) => {
commonStore.hideLoading();
swal({
icon: 'error',
title: 'Error',
html: error.message,
text: error.message,
type: 'error'
});
});

26
src/messages.js Normal file
View File

@ -0,0 +1,26 @@
let messages = {};
messages.invalidVotingKeyMsg = function(key) {
return `The key ${key} is not valid voting Key! Please make sure you have loaded correct voting key in metamask`;
};
messages.VOTED_SUCCESS_MSG = "You successfully voted";
messages.BALLOT_CREATED_SUCCESS_MSG = "You successfully created a new ballot";
messages.FINALIZED_SUCCESS_MSG = "You successfully finalized";
messages.ALREADY_FINALIZED_MSG = "This ballot is already finalized";
messages.INVALID_VOTE_MSG = "You can't vote on this ballot";
messages.INVALID_FINALIZE_MSG = "You can't finalize this ballot";
messages.AFFECTED_KEY_IS_NOT_ADDRESS_MSG = "Ballot affectedKey isn't address";
messages.MINING_KEY_IS_NOT_ADDRESS_MSG = "Ballot miningKey isn't address";
messages.PROPOSED_ADDRESS_IS_NOT_ADDRESS_MSG = "Ballot proposedAddress isn't address";
messages.END_TIME_SHOULD_BE_GREATER_THAN_NOW_MSG = "Ballot end time should be greater than now";
messages.NO_METAMASK_MSG = `You haven't chosen any account in MetaMask.
Please, choose your voting key in MetaMask and reload the page.
Check POA Network <a href='https://github.com/poanetwork/wiki' target='blank'>wiki</a> for more info.`;
messages.WRONG_NETWORK_MSG = `You aren't connected to POA Network.
Please, switch on POA plugin and refresh the page.
Check POA Network <a href='https://github.com/poanetwork/wiki' target='blank'>wiki</a> for more info.`;
messages.ballotIsNotActiveMsg = function(timeToStart) {
return `The ballot is not active yet. Time to start: ${timeToStart}`;
};
module.exports = {
messages
};

View File

@ -47,7 +47,7 @@ class ContractsStore {
return false
}
@action("get keys ballot threshold")
@action("Get keys ballot threshold")
getKeysBallotThreshold = async () => {
this.keysBallotThreshold = await this.ballotsStorage.ballotsStorageInstance.methods.getBallotThreshold(1).call();
}
@ -57,7 +57,7 @@ class ContractsStore {
this.minThresholdBallotThreshold = await this.ballotsStorage.ballotsStorageInstance.methods.getBallotThreshold(1).call();
}
@action("get proxy ballot threshold")
@action("Get proxy ballot threshold")
getProxyBallotThreshold = async () => {
this.proxyBallotThreshold = await this.ballotsStorage.ballotsStorageInstance.methods.getProxyThreshold().call();
}
@ -71,8 +71,8 @@ class ContractsStore {
@action("Set PoA Consensus contract")
setPoaConsensus = (web3Config) => {
this.poaConsensus = new PoaConsensus({
web3: web3Config.web3Instance,
netId: web3Config.netId
web3: web3Config.web3Instance,
netId: web3Config.netId
});
}
@ -87,32 +87,32 @@ class ContractsStore {
@action("Set VotingToChangeKeys contract")
setVotingToChangeKeys = (web3Config) => {
this.votingToChangeKeys = new VotingToChangeKeys({
web3: web3Config.web3Instance,
netId: web3Config.netId
web3: web3Config.web3Instance,
netId: web3Config.netId
});
}
@action("Set VotingToChangeMinThreshold contract")
setVotingToChangeMinThreshold = (web3Config) => {
this.votingToChangeMinThreshold = new VotingToChangeMinThreshold({
web3: web3Config.web3Instance,
netId: web3Config.netId
web3: web3Config.web3Instance,
netId: web3Config.netId
});
}
@action("Set VotingToChangeProxy contract")
setVotingToChangeProxy = (web3Config) => {
this.votingToChangeProxy = new VotingToChangeProxy({
web3: web3Config.web3Instance,
netId: web3Config.netId
web3: web3Config.web3Instance,
netId: web3Config.netId
});
}
@action("Set ValidatorMetadata contract")
setValidatorMetadata = (web3Config) => {
this.validatorMetadata = new ValidatorMetadata({
web3: web3Config.web3Instance,
netId: web3Config.netId
web3: web3Config.web3Instance,
netId: web3Config.netId
});
}
@ -178,6 +178,7 @@ class ContractsStore {
commonStore.hideLoading();
}
}
@action
async getValidatorActiveBallots() {
if(this.web3Instance && this.netId){
@ -189,6 +190,7 @@ class ContractsStore {
this.validatorLimits.proxy = await this.votingToChangeProxy.getBallotLimit(this.web3Instance.eth.defaultAccount);
}
}
@action
async getAllValidatorMetadata() {
const keys = await this.poaConsensus.getValidators();