388 lines
15 KiB
JavaScript
388 lines
15 KiB
JavaScript
import React from 'react';
|
|
import { inject, observer } from "mobx-react";
|
|
import moment from 'moment';
|
|
import swal from 'sweetalert2';
|
|
import { Validator } from './Validator';
|
|
import { KeysTypes } from './KeysTypes';
|
|
import { BallotKeysMetadata } from './BallotKeysMetadata';
|
|
import { BallotMinThresholdMetadata } from './BallotMinThresholdMetadata';
|
|
import { BallotProxyMetadata } from './BallotProxyMetadata';
|
|
import { messages } from "../messages";
|
|
import { constants } from "../constants";
|
|
@inject("commonStore", "ballotStore", "validatorStore", "contractsStore", "routing", "ballotsStore")
|
|
@observer
|
|
export class NewBallot extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.onClick = this.onClick.bind(this);
|
|
}
|
|
|
|
checkValidation() {
|
|
const { commonStore, contractsStore, ballotStore, validatorStore } = this.props;
|
|
|
|
if (ballotStore.isNewValidatorPersonalData) {
|
|
for (let validatorProp in validatorStore) {
|
|
if (validatorStore[validatorProp].length === 0) {
|
|
swal("Warning!", `Validator ${validatorProp} is empty`, "warning");
|
|
commonStore.hideLoading();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!ballotStore.memo) {
|
|
swal("Warning!", messages.DESCRIPTION_IS_EMPTY, "warning");
|
|
commonStore.hideLoading();
|
|
return false;
|
|
}
|
|
|
|
const minBallotDurationInHours = constants.minBallotDurationInDays * 24;
|
|
const minEndTime = moment.utc().add(minBallotDurationInHours, 'hours').format();
|
|
let neededMinutes = moment(minEndTime).diff(moment(ballotStore.endTime), 'minutes');
|
|
let neededHours = Math.round(neededMinutes / 60);
|
|
let duration = minBallotDurationInHours - neededHours;
|
|
|
|
if (neededMinutes > 0) {
|
|
neededMinutes = neededHours * 60 - neededMinutes;
|
|
swal("Warning!", messages.SHOULD_BE_MORE_THAN_MIN_DURATION(minBallotDurationInHours, duration, neededHours, neededMinutes), "warning");
|
|
commonStore.hideLoading();
|
|
return false;
|
|
}
|
|
|
|
const twoWeeks = moment.utc().add(14, 'days').format();
|
|
let exceededMinutes = moment(ballotStore.endTime).diff(moment(twoWeeks), 'minutes');
|
|
if (exceededMinutes > 0) {
|
|
swal("Warning!", messages.SHOULD_BE_LESS_OR_EQUAL_14_DAYS(duration), "warning");
|
|
commonStore.hideLoading();
|
|
return false;
|
|
}
|
|
|
|
if (ballotStore.isBallotForKey) {
|
|
for (let ballotKeysProp in ballotStore.ballotKeys) {
|
|
if (ballotKeysProp === 'newVotingKey' || ballotKeysProp === 'newPayoutKey') {
|
|
continue;
|
|
}
|
|
if (!ballotStore.ballotKeys[ballotKeysProp]) {
|
|
swal("Warning!", `Ballot ${ballotKeysProp} is empty`, "warning");
|
|
commonStore.hideLoading();
|
|
return false;
|
|
}
|
|
if (ballotStore.ballotKeys[ballotKeysProp].length === 0) {
|
|
swal("Warning!", `Ballot ${ballotKeysProp} is empty`, "warning");
|
|
commonStore.hideLoading();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
let isAffectedKeyAddress = contractsStore.web3Instance.isAddress(ballotStore.ballotKeys.affectedKey);
|
|
|
|
if (!isAffectedKeyAddress) {
|
|
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!", messages.MINING_KEY_IS_NOT_ADDRESS_MSG, "warning");
|
|
commonStore.hideLoading();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (ballotStore.isBallotForMinThreshold) {
|
|
for (let ballotMinThresholdProp in ballotStore.ballotMinThreshold) {
|
|
if (ballotStore.ballotMinThreshold[ballotMinThresholdProp].length === 0) {
|
|
swal("Warning!", `Ballot ${ballotMinThresholdProp} is empty`, "warning");
|
|
commonStore.hideLoading();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ballotStore.isBallotForProxy) {
|
|
for (let ballotProxyProp in ballotStore.ballotProxy) {
|
|
if (ballotStore.ballotProxy[ballotProxyProp].length === 0) {
|
|
swal("Warning!", `Ballot ${ballotProxyProp} is empty`, "warning");
|
|
commonStore.hideLoading();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
let isAddress = contractsStore.web3Instance.isAddress(ballotStore.ballotProxy.proposedAddress);
|
|
|
|
if (!isAddress) {
|
|
swal("Warning!", messages.PROPOSED_ADDRESS_IS_NOT_ADDRESS_MSG, "warning");
|
|
commonStore.hideLoading();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!ballotStore.isBallotForKey && !ballotStore.isBallotForMinThreshold && !ballotStore.isBallotForProxy) {
|
|
swal("Warning!", messages.BALLOT_TYPE_IS_EMPTY_MSG, "warning");
|
|
commonStore.hideLoading();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
createBallotForKeys = (startTime) => {
|
|
const { ballotStore, contractsStore } = this.props;
|
|
const inputToMethod = {
|
|
startTime: startTime,
|
|
endTime: ballotStore.endTimeUnix,
|
|
affectedKey: ballotStore.ballotKeys.affectedKey,
|
|
affectedKeyType: ballotStore.ballotKeys.keyType,
|
|
newVotingKey: ballotStore.ballotKeys.newVotingKey,
|
|
newPayoutKey: ballotStore.ballotKeys.newPayoutKey,
|
|
miningKey: ballotStore.ballotKeys.miningKey.value,
|
|
ballotType: ballotStore.ballotKeys.keysBallotType,
|
|
sender: contractsStore.votingKey,
|
|
memo: ballotStore.memo
|
|
};
|
|
let method;
|
|
if (
|
|
inputToMethod.ballotType === ballotStore.KeysBallotType.add &&
|
|
inputToMethod.affectedKeyType === ballotStore.KeyType.mining &&
|
|
(inputToMethod.newVotingKey || inputToMethod.newPayoutKey)
|
|
) {
|
|
method = contractsStore.votingToChangeKeys.createBallotToAddNewValidator(inputToMethod);
|
|
} else {
|
|
method = contractsStore.votingToChangeKeys.createBallot(inputToMethod);
|
|
}
|
|
return method;
|
|
}
|
|
|
|
createBallotForMinThreshold = (startTime) => {
|
|
const { ballotStore, contractsStore } = this.props;
|
|
const inputToMethod = {
|
|
startTime: startTime,
|
|
endTime: ballotStore.endTimeUnix,
|
|
proposedValue: ballotStore.ballotMinThreshold.proposedValue,
|
|
sender: contractsStore.votingKey,
|
|
memo: ballotStore.memo
|
|
};
|
|
let method = contractsStore.votingToChangeMinThreshold.createBallot(inputToMethod);
|
|
return method;
|
|
}
|
|
|
|
createBallotForProxy = (startTime) => {
|
|
const { ballotStore, contractsStore } = this.props;
|
|
const inputToMethod = {
|
|
startTime: startTime,
|
|
endTime: ballotStore.endTimeUnix,
|
|
proposedValue: ballotStore.ballotProxy.proposedAddress,
|
|
contractType: ballotStore.ballotProxy.contractType,
|
|
sender: contractsStore.votingKey,
|
|
memo: ballotStore.memo
|
|
};
|
|
let method = contractsStore.votingToChangeProxy.createBallot(inputToMethod);
|
|
return method;
|
|
}
|
|
|
|
onClick = async () => {
|
|
const { commonStore, contractsStore, ballotStore, ballotsStore } = this.props;
|
|
const { push } = this.props.routing;
|
|
commonStore.showLoading();
|
|
const isValidVotingKey = contractsStore.isValidVotingKey;
|
|
if (!isValidVotingKey) {
|
|
commonStore.hideLoading();
|
|
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;
|
|
let contractType;
|
|
switch (ballotStore.ballotType) {
|
|
case ballotStore.BallotType.keys:
|
|
methodToCreateBallot = this.createBallotForKeys;
|
|
contractType = "votingToChangeKeys";
|
|
break;
|
|
case ballotStore.BallotType.minThreshold:
|
|
methodToCreateBallot = this.createBallotForMinThreshold;
|
|
contractType = "votingToChangeMinThreshold";
|
|
break;
|
|
case ballotStore.BallotType.proxy:
|
|
methodToCreateBallot = this.createBallotForProxy;
|
|
contractType = "votingToChangeProxy";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
const startTime = moment.utc().add(constants.startTimeOffsetInMinutes, 'minutes').unix();
|
|
methodToCreateBallot(startTime)
|
|
.on("receipt", async (tx) => {
|
|
commonStore.hideLoading();
|
|
if (tx.status === true || tx.status === '0x1') {
|
|
const newId = Number(tx.events.BallotCreated.returnValues.id);
|
|
const card = await contractsStore.getCard(newId, contractType);
|
|
ballotsStore.ballotCards.push(card);
|
|
|
|
swal("Congratulations!", messages.BALLOT_CREATED_SUCCESS_MSG, "success").then((result) => {
|
|
push(`${commonStore.rootPath}`);
|
|
window.scrollTo(0, 0);
|
|
});
|
|
} else {
|
|
swal("Warning!", messages.BALLOT_CREATE_FAILED_TX, "warning");
|
|
}
|
|
})
|
|
.on("error", (e) => {
|
|
commonStore.hideLoading();
|
|
swal("Error!", e.message, "error");
|
|
});
|
|
}
|
|
}
|
|
|
|
menuItemActive = (ballotType) => {
|
|
const { ballotStore } = this.props;
|
|
if (ballotType == ballotStore.ballotType) {
|
|
return 'ballot-types-i ballot-types-i_active';
|
|
} else {
|
|
return 'ballot-types-i';
|
|
}
|
|
}
|
|
|
|
render() {
|
|
const { contractsStore, ballotStore } = this.props;
|
|
let validator = ballotStore.isNewValidatorPersonalData ? <Validator />: "";
|
|
let keysTypes = ballotStore.isBallotForKey ? <KeysTypes />: "";
|
|
let metadata
|
|
let minThreshold = 0;
|
|
switch (ballotStore.ballotType) {
|
|
case ballotStore.BallotType.keys:
|
|
metadata = <BallotKeysMetadata />;
|
|
minThreshold = contractsStore.keysBallotThreshold;
|
|
break;
|
|
case ballotStore.BallotType.minThreshold:
|
|
metadata = <BallotMinThresholdMetadata />;
|
|
minThreshold = contractsStore.minThresholdBallotThreshold;
|
|
break;
|
|
case ballotStore.BallotType.proxy:
|
|
metadata = <BallotProxyMetadata />;
|
|
minThreshold = contractsStore.proxyBallotThreshold;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return (
|
|
<section className="container new">
|
|
<h1 className="title">New Ballot</h1>
|
|
<form action="" className="new-form">
|
|
<div className="new-form-side new-form-side_left">
|
|
<div className="ballot-types">
|
|
<div
|
|
className={this.menuItemActive(ballotStore.BallotType.keys)}
|
|
onClick={(e) => ballotStore.changeBallotType(e, ballotStore.BallotType.keys)}
|
|
>
|
|
Validator Management Ballot
|
|
</div>
|
|
<div
|
|
className={this.menuItemActive(ballotStore.BallotType.minThreshold)}
|
|
onClick={(e) => ballotStore.changeBallotType(e, ballotStore.BallotType.minThreshold)}
|
|
>
|
|
Consenus Threshold Ballot
|
|
</div>
|
|
<div
|
|
className={this.menuItemActive(ballotStore.BallotType.proxy)}
|
|
onClick={(e) => ballotStore.changeBallotType(e, ballotStore.BallotType.proxy)}
|
|
>
|
|
Modify Proxy Contract Ballot
|
|
</div>
|
|
</div>
|
|
<div className="info">
|
|
<p className="info-title">Information of the ballot</p>
|
|
<div className="info-i">
|
|
Minimum {minThreshold} from {contractsStore.validatorsLength} validators required to pass the proposal<br />
|
|
</div>
|
|
<div className="info-i">
|
|
You can create {contractsStore.validatorLimits.keys} ballot for keys<br />
|
|
</div>
|
|
<div className="info-i">
|
|
You can create {contractsStore.validatorLimits.minThreshold} ballot for consensus<br />
|
|
</div>
|
|
<div className="info-i">
|
|
You can create {contractsStore.validatorLimits.proxy} ballot for proxy<br />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="new-form-side new-form-side_right">
|
|
<div className="form-el">
|
|
<label>Description of the ballot</label>
|
|
<div>
|
|
<textarea rows="4"
|
|
value={ballotStore.memo}
|
|
onChange={(e) => ballotStore.setMemo(e)}
|
|
></textarea>
|
|
</div>
|
|
</div>
|
|
<hr />
|
|
<div className="hidden">
|
|
<div className="left">
|
|
<div className="radio-container">
|
|
<input type="radio" name="ballot-type" id="ballot-for-validators"
|
|
value={ballotStore.BallotType.keys}
|
|
checked={ballotStore.isBallotForKey}
|
|
onChange={e => ballotStore.changeBallotType(e, ballotStore.BallotType.keys)}
|
|
/>
|
|
<label htmlFor="ballot-for-validators" className="radio">Validator Management Ballot</label>
|
|
<p className="hint">
|
|
Ballot to add, remove or swap any type of key for existing or new validators.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="right">
|
|
<div className="radio-container">
|
|
<input type="radio" name="ballot-type" id="ballot-for-consensus"
|
|
value={ballotStore.BallotType.minThreshold}
|
|
checked={ballotStore.isBallotForMinThreshold}
|
|
onChange={e => ballotStore.changeBallotType(e, ballotStore.BallotType.minThreshold)}
|
|
/>
|
|
<label htmlFor="ballot-for-consensus" className="radio">Consenus Threshold Ballot</label>
|
|
<p className="hint">
|
|
Ballot to change the minimum threshold for consensus to vote for keys.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className="left">
|
|
<div className="radio-container">
|
|
<input type="radio" name="ballot-type" id="ballot-for-proxy"
|
|
value={ballotStore.BallotType.proxy}
|
|
checked={ballotStore.isBallotForProxy}
|
|
onChange={e => ballotStore.changeBallotType(e, ballotStore.BallotType.proxy)}
|
|
/>
|
|
<label htmlFor="ballot-for-proxy" className="radio">Modify Proxy Contract Ballot</label>
|
|
<p className="hint">
|
|
Ballot to change one of the proxy contracts.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<hr />
|
|
{validator}
|
|
{keysTypes}
|
|
{metadata}
|
|
<button type="button" className="add-ballot" onClick={e => this.onClick(e)}>Add ballot</button>
|
|
</div>
|
|
</form>
|
|
</section>
|
|
);
|
|
}
|
|
}
|