Compare commits
26 Commits
58add8ac21
...
c69f9d3c08
Author | SHA1 | Date |
---|---|---|
dependabot[bot] | c69f9d3c08 | |
varasev | e3f8b7de19 | |
varasev | 8cafb1fd7f | |
POA | 356baf1386 | |
PoaMan | 5e34b51b30 | |
PoaMan | 889fa30e7c | |
varasev | 89feb07a88 | |
Vadim | a1bde70929 | |
Max Alekseenko | cbf63a0b9d | |
Max Alekseenko | e569caf9f6 | |
varasev | 5bb4a3d5ed | |
Vadim | 94ef8f009d | |
Victor Baranov | 7769fd21cf | |
Vadim | 617152a903 | |
varasev | 23ee507584 | |
Vadim | 8f7279519e | |
varasev | 557bc54778 | |
Max Alekseenko | 8248992d46 | |
Max Alekseenko | b314def1a9 | |
phahulin | 0d04cebeb5 | |
Vadim | 893620c76b | |
Max Alekseenko | 051b1a1d57 | |
Max Alekseenko | c8047f9520 | |
Max Alekseenko | 9559fe4273 | |
varasev | 795472ec57 | |
Max Alekseenko | 1cecd5d169 |
|
@ -3,7 +3,6 @@
|
|||
## Base supported networks
|
||||
|
||||
- Core POA network (RPC endpoint: `https://core.poa.network`)
|
||||
- xDai chain (RPC endpoint: `https://dai.poa.network`)
|
||||
- Sokol testnet (RPC endpoint: `https://sokol.poa.network`)
|
||||
- Kovan testnet (RPC endpoint: `https://kovan.infura.io/`)
|
||||
|
||||
|
@ -24,7 +23,7 @@ Please check related posts in wiki [Governance Overview](https://github.com/poan
|
|||
## Configuration
|
||||
Governance DApp is configured with [POA Network governance contracts](https://github.com/poanetwork/poa-network-consensus-contracts)
|
||||
|
||||
There are contracts' addresses for [Sokol](https://github.com/poanetwork/poa-chain-spec/blob/sokol/contracts.json), [Core](https://github.com/poanetwork/poa-chain-spec/blob/core/contracts.json), [xDai](https://github.com/poanetwork/poa-chain-spec/blob/dai/contracts.json), and [Kovan](https://github.com/poanetwork/poa-chain-spec/blob/kovan/contracts.json).
|
||||
There are contracts' addresses for [Sokol](https://github.com/poanetwork/poa-chain-spec/blob/sokol/contracts.json), [Core](https://github.com/poanetwork/poa-chain-spec/blob/core/contracts.json), and [Kovan](https://github.com/poanetwork/poa-chain-spec/blob/kovan/contracts.json).
|
||||
|
||||
|
||||
## Building from source
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -64,7 +64,7 @@
|
|||
"url-loader": "0.6.2",
|
||||
"web3": "1.0.0-beta.34",
|
||||
"webpack": "3.12.0",
|
||||
"webpack-dev-server": "2.11.2",
|
||||
"webpack-dev-server": "3.1.11",
|
||||
"webpack-manifest-plugin": "1.3.2",
|
||||
"whatwg-fetch": "2.0.3"
|
||||
},
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicons/favicon.ico">
|
||||
<link href="https://fonts.googleapis.com/css?family=Nunito:300,400,700" rel="stylesheet">
|
||||
<title>POA Network Governance DApp</title>
|
||||
<title>POA Governance DApp</title>
|
||||
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyD97qDOBYZ2fH86Wq1vzhDOiSUsZGVqbVQ&libraries=places"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
67
src/App.js
67
src/App.js
|
@ -5,7 +5,8 @@ import { Route, Redirect } from 'react-router-dom'
|
|||
import { constants } from './utils/constants'
|
||||
import { getNetworkBranch } from './utils/utils'
|
||||
import { inject, observer } from 'mobx-react'
|
||||
import { messages } from './utils/messages'
|
||||
import messages from './utils/messages'
|
||||
import { enableWallet } from './utils/getWeb3'
|
||||
|
||||
import './assets/stylesheets/index.css'
|
||||
|
||||
|
@ -41,16 +42,32 @@ class App extends Component {
|
|||
onNewBallotRender = () => {
|
||||
const { commonStore, contractsStore } = this.props
|
||||
|
||||
if (!contractsStore.web3Instance) {
|
||||
if (!commonStore.loading) {
|
||||
swal({
|
||||
title: 'Error',
|
||||
html: messages.NO_METAMASK_MSG,
|
||||
icon: 'error',
|
||||
type: 'error'
|
||||
if (!commonStore.loading) {
|
||||
enableWallet(contractsStore.updateKeys)
|
||||
.then(() => {
|
||||
if (!contractsStore.injectedWeb3) {
|
||||
swal({
|
||||
title: 'Error',
|
||||
html: messages.NO_METAMASK_MSG,
|
||||
type: 'error'
|
||||
})
|
||||
} else if (!contractsStore.networkMatch) {
|
||||
swal({
|
||||
title: 'Warning!',
|
||||
html: messages.networkMatchError(contractsStore.netId),
|
||||
type: 'warning'
|
||||
})
|
||||
} else if (!contractsStore.isValidVotingKey) {
|
||||
swal({
|
||||
title: 'Warning!',
|
||||
html: messages.invalidVotingKeyMsg(contractsStore.votingKey),
|
||||
type: 'warning'
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
swal('Error', error.message, 'error')
|
||||
})
|
||||
}
|
||||
return null
|
||||
}
|
||||
return <NewBallot networkBranch={this.getVotingNetworkBranch()} />
|
||||
}
|
||||
|
@ -60,8 +77,7 @@ class App extends Component {
|
|||
}
|
||||
|
||||
onSearch = e => {
|
||||
const { commonStore } = this.props
|
||||
commonStore.setSearchTerm(e.target.value.toLowerCase())
|
||||
this.setSearchTerm(e.target.value)
|
||||
}
|
||||
|
||||
hideSearch = () => {
|
||||
|
@ -85,13 +101,28 @@ class App extends Component {
|
|||
return 'All'
|
||||
}
|
||||
|
||||
setSearchTerm = term => {
|
||||
const { commonStore } = this.props
|
||||
commonStore.setSearchTerm(term.toLowerCase())
|
||||
if (this.refs.searchBar) {
|
||||
this.refs.searchBar.setSearchTerm(term)
|
||||
}
|
||||
}
|
||||
|
||||
onNetworkChange = e => {
|
||||
this.setSearchTerm('')
|
||||
this.props.onNetworkChange(e)
|
||||
}
|
||||
|
||||
isNewBallotPage() {
|
||||
return `${constants.rootPath}/new` === this.props.location.pathname
|
||||
}
|
||||
|
||||
render() {
|
||||
const { commonStore, contractsStore } = this.props
|
||||
const networkBranch = this.getVotingNetworkBranch()
|
||||
const networkBranch = commonStore.loadingNetworkBranch
|
||||
? commonStore.loadingNetworkBranch
|
||||
: this.getVotingNetworkBranch()
|
||||
|
||||
return networkBranch ? (
|
||||
<div
|
||||
|
@ -104,10 +135,18 @@ class App extends Component {
|
|||
baseRootPath={commonStore.rootPath}
|
||||
netId={contractsStore.netId}
|
||||
networkBranch={networkBranch}
|
||||
onChange={this.onNetworkChange}
|
||||
onMenuToggle={this.toggleMobileMenu}
|
||||
showMobileMenu={this.state.showMobileMenu}
|
||||
/>
|
||||
{this.hideSearch() ? null : <SearchBar networkBranch={networkBranch} onSearch={this.onSearch} />}
|
||||
{this.hideSearch() ? null : (
|
||||
<SearchBar
|
||||
networkBranch={networkBranch}
|
||||
onSearch={this.onSearch}
|
||||
searchTerm={commonStore.searchTerm}
|
||||
ref="searchBar"
|
||||
/>
|
||||
)}
|
||||
<MainTitle text={this.getTitle()} />
|
||||
<section
|
||||
className={`lo-App_Content lo-App_Content-${networkBranch} ${
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
height: $header-height;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
|
@ -58,4 +57,4 @@
|
|||
@media (min-width: $breakpoint-md) {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.nl-IconNetwork_Path {
|
||||
@include menu-icon-colors();
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
.NetworkSelect {
|
||||
display: flex;
|
||||
@media (min-width: $breakpoint-md) {
|
||||
position: relative;
|
||||
margin-left: 34px;
|
||||
&:hover {
|
||||
.NetworkSelect_List {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sw-Header_Content .NetworkSelect {
|
||||
display: none;
|
||||
@media (min-width: $breakpoint-md) {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.NetworkSelect_Top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: .7;
|
||||
transition: .15s ease-in;
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
@media (min-width: $breakpoint-md) {
|
||||
.nl-IconNetwork_Arrow {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nl-NavigationLinks_Link.opacityFull {
|
||||
opacity: 1;
|
||||
flex-direction: column;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.nl-IconNetwork_Arrow {
|
||||
margin-left: 10px;
|
||||
transition: .15s ease-in;
|
||||
path {
|
||||
fill: #fff;
|
||||
opacity: .52;
|
||||
}
|
||||
}
|
||||
|
||||
.NetworkSelect_List {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
@media (min-width: $breakpoint-md) {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
background-color: #fff;
|
||||
width: 180px;
|
||||
border-radius: 4px 0 4px 4px;
|
||||
box-shadow: 0 5px 10px rgba(#000, .05);
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
li.currentNetwork {
|
||||
@media (max-width: $breakpoint-md) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
button {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
transition: .15s ease-in;
|
||||
height: 50px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
@media (min-width: $breakpoint-md) {
|
||||
padding: 8px 18px;
|
||||
height: auto;
|
||||
color: $base-text-color;
|
||||
justify-content: flex-start;
|
||||
&:hover {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
.top-Select {
|
||||
flex-grow: 1;
|
||||
max-width: 140px;
|
||||
|
||||
@media (min-width: $breakpoint-xl) {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@
|
|||
@import "IconGithub";
|
||||
@import "IconLoadMore";
|
||||
@import "IconMobileMenu";
|
||||
@import "IconNetwork";
|
||||
@import "IconPOA";
|
||||
@import "IconTelegram";
|
||||
@import "IconToFinalize";
|
||||
|
@ -47,9 +48,11 @@
|
|||
@import "NewBallot";
|
||||
@import "NewBallotMenu";
|
||||
@import "NewBallotMenuInfo";
|
||||
@import "NetworkSelect";
|
||||
@import "SearchBar";
|
||||
@import "Select";
|
||||
@import "Separator";
|
||||
@import "SocialIcons";
|
||||
@import "Validator";
|
||||
@import "VoteProgressBar";
|
||||
@import "Votes";
|
||||
@import "Votes";
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -7,9 +7,10 @@ import { BallotInfoContainer } from '../BallotInfoContainer'
|
|||
import { Votes } from '../Votes'
|
||||
import { getNetworkBranch } from '../../utils/utils'
|
||||
import { inject, observer } from 'mobx-react'
|
||||
import { messages } from '../../utils/messages'
|
||||
import messages from '../../utils/messages'
|
||||
import { observable, action, computed } from 'mobx'
|
||||
import { sendTransactionByVotingKey } from '../../utils/helpers'
|
||||
import { enableWallet } from '../../utils/getWeb3'
|
||||
|
||||
const ACCEPT = 1
|
||||
const REJECT = 2
|
||||
|
@ -59,6 +60,7 @@ export class BallotCard extends React.Component {
|
|||
@observable memo
|
||||
@observable quorumState
|
||||
@observable minBallotDuration
|
||||
@observable minThreshold
|
||||
|
||||
@computed
|
||||
get cancelOrFinalizeButtonDisplayName() {
|
||||
|
@ -302,6 +304,9 @@ export class BallotCard extends React.Component {
|
|||
this.getHasAlreadyVoted()
|
||||
}
|
||||
|
||||
// minThreshold
|
||||
this.getMinThreshold()
|
||||
|
||||
if (votingType === 'votingToManageEmissionFunds') {
|
||||
this.getQuorumState()
|
||||
}
|
||||
|
@ -317,9 +322,19 @@ export class BallotCard extends React.Component {
|
|||
return formattedMs
|
||||
}
|
||||
|
||||
@action('ballot min threshold of voters')
|
||||
getMinThreshold = async () => {
|
||||
const { contractsStore, id, votingType } = this.props
|
||||
this.minThreshold = await this.getContract(contractsStore, votingType).getMinThresholdOfVoters(id)
|
||||
}
|
||||
|
||||
@action('validator has already voted')
|
||||
getHasAlreadyVoted = async () => {
|
||||
const { contractsStore, id, votingType } = this.props
|
||||
if (contractsStore.miningKey === '0x0000000000000000000000000000000000000000') {
|
||||
this.hasAlreadyVoted = false
|
||||
return
|
||||
}
|
||||
let _hasAlreadyVoted = false
|
||||
try {
|
||||
_hasAlreadyVoted = await this.getContract(contractsStore, votingType).hasAlreadyVoted(
|
||||
|
@ -360,6 +375,27 @@ export class BallotCard extends React.Component {
|
|||
this.quorumState = await this.repeatGetProperty(contractsStore, votingType, id, 'getQuorumState', 0)
|
||||
}
|
||||
|
||||
networkAndKeyValidation = async () => {
|
||||
const { contractsStore } = this.props
|
||||
try {
|
||||
await enableWallet(contractsStore.updateKeys)
|
||||
} catch (error) {
|
||||
swal('Error', error.message, 'error')
|
||||
return false
|
||||
}
|
||||
if (contractsStore.isEmptyVotingKey) {
|
||||
swal('Warning!', messages.NO_METAMASK_MSG, 'warning')
|
||||
return false
|
||||
} else if (!contractsStore.networkMatch) {
|
||||
swal('Warning!', messages.networkMatchError(contractsStore.netId), 'warning')
|
||||
return false
|
||||
} else if (!contractsStore.isValidVotingKey) {
|
||||
swal('Warning!', messages.invalidVotingKeyMsg(contractsStore.votingKey), 'warning')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
vote = async ({ choice }) => {
|
||||
if (this.isCanceled) {
|
||||
swal('Warning!', messages.INVALID_VOTE_MSG, 'warning')
|
||||
|
@ -369,15 +405,13 @@ export class BallotCard extends React.Component {
|
|||
swal('Warning!', messages.ballotIsNotActiveMsg(this.timeTo.displayValue), 'warning')
|
||||
return
|
||||
}
|
||||
const { commonStore, contractsStore, id, votingType, ballotsStore, pos } = this.props
|
||||
const { push } = this.props.routing
|
||||
if (!contractsStore.votingKey) {
|
||||
swal('Warning!', messages.NO_METAMASK_MSG, 'warning')
|
||||
return
|
||||
} else if (!contractsStore.isValidVotingKey) {
|
||||
swal('Warning!', messages.invalidVotingKeyMsg(contractsStore.votingKey), 'warning')
|
||||
|
||||
if (!(await this.networkAndKeyValidation())) {
|
||||
return
|
||||
}
|
||||
|
||||
const { commonStore, contractsStore, id, votingType, ballotsStore, pos } = this.props
|
||||
const { push } = this.props.routing
|
||||
commonStore.showLoading()
|
||||
let isValidVote = await this.isValidVote()
|
||||
if (!isValidVote) {
|
||||
|
@ -456,6 +490,11 @@ export class BallotCard extends React.Component {
|
|||
const { votingState, contractsStore, commonStore, ballotsStore, votingType, id, pos } = this.props
|
||||
const { push } = this.props.routing
|
||||
const contract = this.getContract(contractsStore, votingType)
|
||||
|
||||
if (!(await this.networkAndKeyValidation())) {
|
||||
return
|
||||
}
|
||||
|
||||
let canCancel = true
|
||||
|
||||
if (!this.timeToCancel.val) {
|
||||
|
@ -508,13 +547,11 @@ export class BallotCard extends React.Component {
|
|||
}
|
||||
const { commonStore, contractsStore, id, votingType, ballotsStore, pos } = this.props
|
||||
const { push } = this.props.routing
|
||||
if (!contractsStore.votingKey) {
|
||||
swal('Warning!', messages.NO_METAMASK_MSG, 'warning')
|
||||
return
|
||||
} else if (!contractsStore.isValidVotingKey) {
|
||||
swal('Warning!', messages.invalidVotingKeyMsg(contractsStore.votingKey), 'warning')
|
||||
|
||||
if (!(await this.networkAndKeyValidation())) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.isFinalized) {
|
||||
swal('Warning!', messages.ALREADY_FINALIZED_MSG, 'warning')
|
||||
return
|
||||
|
@ -600,21 +637,6 @@ export class BallotCard extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
getThreshold(contractsStore, votingType) {
|
||||
switch (votingType) {
|
||||
case 'votingToChangeKeys':
|
||||
return contractsStore.keysBallotThreshold
|
||||
case 'votingToChangeMinThreshold':
|
||||
return contractsStore.minThresholdBallotThreshold
|
||||
case 'votingToChangeProxy':
|
||||
return contractsStore.proxyBallotThreshold
|
||||
case 'votingToManageEmissionFunds':
|
||||
return contractsStore.emissionFundsBallotThreshold
|
||||
default:
|
||||
return contractsStore.keysBallotThreshold
|
||||
}
|
||||
}
|
||||
|
||||
getMinBallotDuration(contractsStore, votingType) {
|
||||
switch (votingType) {
|
||||
case 'votingToChangeKeys':
|
||||
|
@ -672,10 +694,9 @@ export class BallotCard extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
let { contractsStore, votingType, children } = this.props
|
||||
let { votingType, children } = this.props
|
||||
let votes
|
||||
|
||||
const threshold = this.getThreshold(contractsStore, votingType)
|
||||
const networkBranch = this.getVotingNetworkBranch()
|
||||
|
||||
if (votingType === 'votingToManageEmissionFunds') {
|
||||
|
@ -734,12 +755,7 @@ export class BallotCard extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
<Votes networkBranch={networkBranch} votes={votes} />
|
||||
<BallotInfoContainer
|
||||
memo={this.memo}
|
||||
networkBranch={networkBranch}
|
||||
threshold={threshold}
|
||||
validatorsLength={contractsStore.validatorsLength}
|
||||
/>
|
||||
<BallotInfoContainer memo={this.memo} networkBranch={networkBranch} threshold={this.minThreshold} />
|
||||
<BallotFooter
|
||||
buttonState={this.cancelOrFinalizeButtonState}
|
||||
buttonText={this.cancelOrFinalizeButtonDisplayName}
|
||||
|
|
|
@ -18,7 +18,7 @@ export class BallotInfoContainer extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
let { memo = '', threshold, validatorsLength, networkBranch } = this.props
|
||||
let { memo = '', threshold, networkBranch } = this.props
|
||||
let toggleShowMore =
|
||||
memo.length > MAX_DETAILS_LENGTH ? (
|
||||
<span
|
||||
|
@ -34,7 +34,7 @@ export class BallotInfoContainer extends React.Component {
|
|||
return (
|
||||
<div className="bc-BallotInfoContainer">
|
||||
<div className="bc-BallotInfoContainer_Info bc-BallotInfoContainer_Info-minimum">
|
||||
Minimum {threshold} from {validatorsLength} validators are required to pass the proposal
|
||||
Minimum {threshold} validators are required to pass the proposal
|
||||
</div>
|
||||
<div
|
||||
className={`bc-BallotInfoContainer_Info bc-BallotInfoContainer_Info-details ${
|
||||
|
|
|
@ -45,7 +45,7 @@ export class BallotKeysMetadata extends React.Component {
|
|||
<FormSelect
|
||||
disabled={ballotStore.isNewValidatorPersonalData}
|
||||
hint="Mining key address of validator to vote for.<br />Example: 0xc70760D23557A4FDE612C0bE63b26EBD023C51Ee."
|
||||
id="mining-key"
|
||||
id="mining-key-select"
|
||||
name="form-field-name"
|
||||
networkBranch={networkBranch}
|
||||
onChange={ballotStore.setMiningKey}
|
||||
|
|
|
@ -2,14 +2,12 @@ import React from 'react'
|
|||
import { FormInput } from '../FormInput'
|
||||
import { FormSelect } from '../FormSelect'
|
||||
import { inject, observer } from 'mobx-react'
|
||||
import { constants } from '../../utils/constants'
|
||||
import { getNetworkName } from '../../utils/utils'
|
||||
|
||||
@inject('ballotStore', 'contractsStore')
|
||||
@inject('ballotStore')
|
||||
@observer
|
||||
export class BallotProxyMetadata extends React.Component {
|
||||
render() {
|
||||
const { ballotStore, contractsStore, networkBranch } = this.props
|
||||
const { ballotStore, networkBranch } = this.props
|
||||
let options = [
|
||||
/*0*/ { value: '', label: '' },
|
||||
/*1*/ { value: '1', label: ballotStore.ProxyBallotType[1] }, // KeysManager
|
||||
|
|
|
@ -1,20 +1,31 @@
|
|||
import React from 'react'
|
||||
|
||||
import { ButtonNewBallot } from '../ButtonNewBallot'
|
||||
import { IconMobileMenu } from '../IconMobileMenu'
|
||||
import { Logo } from '../Logo'
|
||||
import { MobileMenuLinks } from '../MobileMenuLinks'
|
||||
import { NavigationLinks } from '../NavigationLinks'
|
||||
import NetworkSelect from '../NetworkSelect'
|
||||
|
||||
export const Header = ({ baseRootPath = '', networkBranch = undefined, onMenuToggle, showMobileMenu = false }) => {
|
||||
export const Header = ({
|
||||
baseRootPath = '',
|
||||
networkBranch = undefined,
|
||||
onChange,
|
||||
onMenuToggle,
|
||||
showMobileMenu = false
|
||||
}) => {
|
||||
return (
|
||||
<header className={`sw-Header sw-Header-${networkBranch} ${showMobileMenu ? 'sw-Header-menu-open' : ''}`}>
|
||||
{showMobileMenu ? <MobileMenuLinks networkBranch={networkBranch} onClick={onMenuToggle} /> : null}
|
||||
{showMobileMenu ? (
|
||||
<MobileMenuLinks networkBranch={networkBranch} onClick={onMenuToggle} onNetworkChange={onChange} />
|
||||
) : null}
|
||||
<div className="sw-Header_Content">
|
||||
<Logo networkBranch={networkBranch} href={baseRootPath} />
|
||||
<div className="sw-Header_Links">
|
||||
<NavigationLinks networkBranch={networkBranch} />
|
||||
<ButtonNewBallot networkBranch={networkBranch} />
|
||||
</div>
|
||||
<NetworkSelect networkBranch={networkBranch} onChange={onChange} />
|
||||
<IconMobileMenu networkBranch={networkBranch} isOpen={showMobileMenu} onClick={onMenuToggle} />
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react'
|
||||
import xDaiLogo from './xdai.svg'
|
||||
//import xDaiLogo from './xdai.svg'
|
||||
import poaLogo from './core.svg'
|
||||
import sokolLogo from './sokol.svg'
|
||||
import kovanLogo from './kovan.svg'
|
||||
|
@ -9,7 +9,7 @@ const getLogoSrc = networkBranch => {
|
|||
{
|
||||
core: poaLogo,
|
||||
sokol: sokolLogo,
|
||||
dai: xDaiLogo,
|
||||
//dai: xDaiLogo,
|
||||
kovan: kovanLogo
|
||||
}[networkBranch] || poaLogo
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||
import { LogoPOA } from '../LogoPOA'
|
||||
import { LogoSokol } from '../LogoSokol'
|
||||
import { LogoKovan } from '../LogoKovan'
|
||||
import { LogoDai } from '../LogoDai'
|
||||
//import { LogoDai } from '../LogoDai'
|
||||
import { constants } from '../../utils/constants'
|
||||
|
||||
export const Logo = ({ href = null, extraClass = '', networkBranch = '' }) => {
|
||||
|
@ -11,8 +11,8 @@ export const Logo = ({ href = null, extraClass = '', networkBranch = '' }) => {
|
|||
return <LogoSokol href={href} extraClass={extraClass} />
|
||||
case constants.KOVAN:
|
||||
return <LogoKovan href={href} extraClass={extraClass} />
|
||||
case constants.DAI:
|
||||
return <LogoDai href={href} extraClass={extraClass} />
|
||||
//case constants.DAI:
|
||||
// return <LogoDai href={href} extraClass={extraClass} />
|
||||
default:
|
||||
return <LogoPOA href={href} extraClass={extraClass} />
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import React from 'react'
|
||||
import { ButtonNewBallot } from '../ButtonNewBallot'
|
||||
import { NavigationLinks } from '../NavigationLinks'
|
||||
import NetworkSelect from '../NetworkSelect'
|
||||
|
||||
export const MobileMenuLinks = ({ onClick, networkBranch }) => {
|
||||
export const MobileMenuLinks = ({ onClick, networkBranch, onNetworkChange }) => {
|
||||
return (
|
||||
<div className={`hd-MobileMenuLinks hd-MobileMenuLinks-${networkBranch}`} onClick={onClick}>
|
||||
<NavigationLinks networkBranch={networkBranch} />
|
||||
<ButtonNewBallot networkBranch={networkBranch} />
|
||||
<NetworkSelect networkBranch={networkBranch} onChange={onNetworkChange} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import React, { Component } from 'react'
|
||||
import { constants } from '../../utils/constants'
|
||||
|
||||
export default class NetworkSelect extends Component {
|
||||
changeNetworkRPC(e) {
|
||||
e.preventDefault()
|
||||
let getCurrentClickedLink = e.target.innerHTML
|
||||
let getCurrentClickedLinkId = ''
|
||||
for (const _netId in constants.NETWORKS) {
|
||||
if (constants.NETWORKS[_netId].FULLNAME === getCurrentClickedLink) {
|
||||
getCurrentClickedLinkId = _netId
|
||||
}
|
||||
}
|
||||
this.props.onChange({ value: getCurrentClickedLinkId })
|
||||
}
|
||||
|
||||
render() {
|
||||
let networkFullNames = []
|
||||
let currentNetworkFullName = ''
|
||||
|
||||
const networks = constants.NETWORKS
|
||||
|
||||
let netIds = []
|
||||
Object.keys(networks)
|
||||
.sort((a, b) => (networks[a].SORTORDER > networks[b].SORTORDER ? 1 : -1))
|
||||
.forEach(function(_netId) {
|
||||
netIds.push(_netId)
|
||||
})
|
||||
|
||||
let selectedNetworkIndex = -1
|
||||
|
||||
netIds.forEach(_netId => {
|
||||
networkFullNames.push(networks[_netId].FULLNAME)
|
||||
if (networks[_netId].BRANCH === this.props.networkBranch) {
|
||||
currentNetworkFullName = networks[_netId].FULLNAME
|
||||
selectedNetworkIndex = networkFullNames.length - 1
|
||||
}
|
||||
})
|
||||
|
||||
const listItems = networkFullNames.map((name, index) => {
|
||||
let className = ''
|
||||
if (index === selectedNetworkIndex) {
|
||||
className = 'currentNetwork'
|
||||
}
|
||||
return (
|
||||
<li key={name.toString()} className={className}>
|
||||
<button onClick={e => this.changeNetworkRPC(e)}>{name}</button>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={`NetworkSelect nl-NavigationLinks_Link opacityFull`}>
|
||||
<div className={`NetworkSelect_Top`}>
|
||||
<svg className={`nl-IconNetwork`} xmlns="http://www.w3.org/2000/svg" width="18" height="18">
|
||||
<path
|
||||
className={`nl-IconNetwork_Path nl-IconNetwork_Path-${this.props.networkBranch}`}
|
||||
d="M9 18a9 9 0 0 1-9-9 9 9 0 0 1 9-9 9 9 0 0 1 9 9 9 9 0 0 1-9 9zm6.923-8h-1.974c-.116 1.85-.525 3.539-1.167 4.876A6.993 6.993 0 0 0 15.923 10zM9 16c1.51 0 2.747-2.612 2.957-6H6.043c.21 3.388 1.447 6 2.957 6zm-3.782-1.124C4.576 13.539 4.167 11.85 4.051 10H2.077a6.993 6.993 0 0 0 3.141 4.876zM2.077 8h1.974c.116-1.85.525-3.538 1.167-4.876A6.993 6.993 0 0 0 2.077 8zM9 2C7.49 2 6.253 4.612 6.043 8h5.914C11.747 4.612 10.51 2 9 2zm3.782 1.124C13.424 4.462 13.833 6.15 13.949 8h1.974a6.993 6.993 0 0 0-3.141-4.876z"
|
||||
/>
|
||||
</svg>
|
||||
<span className={`nl-NavigationLinks_Text nl-NavigationLinks_Text-${this.props.networkBranch}`}>
|
||||
{currentNetworkFullName}
|
||||
</span>
|
||||
<svg className={`nl-IconNetwork_Arrow`} xmlns="http://www.w3.org/2000/svg" width="8" height="4">
|
||||
<path d="M0 0h8L4 4 0 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<ul className={`NetworkSelect_List`}>{listItems}</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -15,8 +15,9 @@ import { Validator } from '../Validator'
|
|||
import { constants } from '../../utils/constants'
|
||||
import { getNetworkBranch } from '../../utils/utils'
|
||||
import { inject, observer } from 'mobx-react'
|
||||
import { messages } from '../../utils/messages'
|
||||
import messages from '../../utils/messages'
|
||||
import { sendTransactionByVotingKey } from '../../utils/helpers'
|
||||
import { enableWallet } from '../../utils/getWeb3'
|
||||
|
||||
@inject('commonStore', 'ballotStore', 'validatorStore', 'contractsStore', 'routing', 'ballotsStore')
|
||||
@observer
|
||||
|
@ -40,9 +41,10 @@ export class NewBallot extends React.Component {
|
|||
}
|
||||
|
||||
checkValidation() {
|
||||
const { commonStore, contractsStore, ballotStore, validatorStore } = this.props
|
||||
const { commonStore, contractsStore, ballotStore } = this.props
|
||||
|
||||
// Temporarily commented (until we implement https://github.com/poanetwork/poa-dapps-voting/issues/120)
|
||||
// const { validatorStore } = this.props
|
||||
// if (ballotStore.isNewValidatorPersonalData) {
|
||||
// for (let validatorProp in validatorStore) {
|
||||
// if (validatorStore[validatorProp].length === 0) {
|
||||
|
@ -252,9 +254,19 @@ export class NewBallot extends React.Component {
|
|||
const { commonStore, contractsStore, ballotStore, ballotsStore } = this.props
|
||||
const { push } = this.props.routing
|
||||
|
||||
if (!contractsStore.votingKey) {
|
||||
try {
|
||||
await enableWallet(contractsStore.updateKeys)
|
||||
} catch (error) {
|
||||
swal('Error', error.message, 'error')
|
||||
return
|
||||
}
|
||||
|
||||
if (contractsStore.isEmptyVotingKey) {
|
||||
swal('Warning!', messages.NO_METAMASK_MSG, 'warning')
|
||||
return
|
||||
} else if (!contractsStore.networkMatch) {
|
||||
swal('Warning!', messages.networkMatchError(contractsStore.netId), 'warning')
|
||||
return
|
||||
} else if (!contractsStore.isValidVotingKey) {
|
||||
swal('Warning!', messages.invalidVotingKeyMsg(contractsStore.votingKey), 'warning')
|
||||
return
|
||||
|
|
|
@ -8,11 +8,11 @@ export const NewBallotMenuInfo = ({ minThreshold, validatorsLength, keys, valida
|
|||
<li className="mn-NewBallotMenuInfo_ListItem">
|
||||
Minimum {minThreshold} from {validatorsLength} validators are required to pass the proposal
|
||||
</li>
|
||||
<li className="mn-NewBallotMenuInfo_ListItem">You can create {keys} ballot(s) for keys</li>
|
||||
<li className="mn-NewBallotMenuInfo_ListItem">You can create {Number(keys)} ballot(s) for keys</li>
|
||||
<li className="mn-NewBallotMenuInfo_ListItem">
|
||||
You can create {validatorLimitsMinThreshold} ballot(s) for consensus
|
||||
You can create {Number(validatorLimitsMinThreshold)} ballot(s) for consensus
|
||||
</li>
|
||||
<li className="mn-NewBallotMenuInfo_ListItem">You can create {proxy} ballot(s) for proxy</li>
|
||||
<li className="mn-NewBallotMenuInfo_ListItem">You can create {Number(proxy)} ballot(s) for proxy</li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,16 +1,36 @@
|
|||
import React from 'react'
|
||||
|
||||
export const SearchBar = ({ extraClassName = '', networkBranch = false, onSearch }) => {
|
||||
return (
|
||||
<div className={`sw-SearchBar sw-SearchBar-${networkBranch} ${extraClassName}`}>
|
||||
<div className="sw-SearchBar_Content">
|
||||
<input
|
||||
className={`sw-SearchBar_Input sw-SearchBar_Input-${networkBranch}`}
|
||||
onChange={onSearch}
|
||||
placeholder="Search..."
|
||||
type="search"
|
||||
/>
|
||||
export class SearchBar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = { searchTerm: '' }
|
||||
}
|
||||
|
||||
setSearchTerm(searchTerm) {
|
||||
this.setState({ searchTerm })
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { searchTerm } = this.props
|
||||
if (searchTerm !== undefined) {
|
||||
this.setSearchTerm(searchTerm)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { networkBranch, onSearch } = this.props
|
||||
return (
|
||||
<div className={`sw-SearchBar sw-SearchBar-${networkBranch}`}>
|
||||
<div className="sw-SearchBar_Content">
|
||||
<input
|
||||
className={`sw-SearchBar_Input sw-SearchBar_Input-${networkBranch}`}
|
||||
onChange={onSearch}
|
||||
placeholder="Search..."
|
||||
type="search"
|
||||
value={this.state.searchTerm}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react'
|
||||
import { FormAutocomplete } from '../FormAutocomplete'
|
||||
import { FormInput } from '../FormInput'
|
||||
import { FormSelect } from '../FormSelect'
|
||||
//import { FormAutocomplete } from '../FormAutocomplete'
|
||||
//import { FormInput } from '../FormInput'
|
||||
//import { FormSelect } from '../FormSelect'
|
||||
import { constants } from '../../utils/constants'
|
||||
import { geocodeByAddress } from 'react-places-autocomplete'
|
||||
import { inject, observer } from 'mobx-react'
|
||||
|
@ -64,6 +64,7 @@ export class Validator extends React.Component {
|
|||
|
||||
render() {
|
||||
return null // Temporarily empty (until we implement https://github.com/poanetwork/poa-dapps-voting/issues/120)
|
||||
/*
|
||||
const { validatorStore, networkBranch } = this.props
|
||||
const inputProps = {
|
||||
value: validatorStore.address,
|
||||
|
@ -190,5 +191,6 @@ export class Validator extends React.Component {
|
|||
</div>
|
||||
</div>
|
||||
)
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,10 @@ export default class VotingToChangeKeys {
|
|||
return this.instance.methods.votingState(_id).call()
|
||||
}
|
||||
|
||||
getMinThresholdOfVoters(_id) {
|
||||
return this.instance.methods.getMinThresholdOfVoters(_id).call()
|
||||
}
|
||||
|
||||
hasAlreadyVoted(_id, votingKey) {
|
||||
return this.instance.methods.hasAlreadyVoted(_id, votingKey).call()
|
||||
}
|
||||
|
@ -86,7 +90,7 @@ export default class VotingToChangeKeys {
|
|||
return _limitPerValidator - _activeBallots
|
||||
}
|
||||
|
||||
async minBallotDuration() {
|
||||
return await this.instance.methods.minBallotDuration().call()
|
||||
minBallotDuration() {
|
||||
return this.instance.methods.minBallotDuration().call()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,10 @@ export default class VotingToChangeMinThreshold {
|
|||
return this.instance.methods.votingState(_id).call()
|
||||
}
|
||||
|
||||
getMinThresholdOfVoters(_id) {
|
||||
return this.instance.methods.getMinThresholdOfVoters(_id).call()
|
||||
}
|
||||
|
||||
hasAlreadyVoted(_id, votingKey) {
|
||||
return this.instance.methods.hasAlreadyVoted(_id, votingKey).call()
|
||||
}
|
||||
|
@ -78,7 +82,7 @@ export default class VotingToChangeMinThreshold {
|
|||
return _limitPerValidator - _activeBallots
|
||||
}
|
||||
|
||||
async minBallotDuration() {
|
||||
return await this.instance.methods.minBallotDuration().call()
|
||||
minBallotDuration() {
|
||||
return this.instance.methods.minBallotDuration().call()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,10 @@ export default class VotingToChangeProxy {
|
|||
return this.instance.methods.votingState(_id).call()
|
||||
}
|
||||
|
||||
getMinThresholdOfVoters(_id) {
|
||||
return this.instance.methods.getMinThresholdOfVoters(_id).call()
|
||||
}
|
||||
|
||||
hasAlreadyVoted(_id, votingKey) {
|
||||
return this.instance.methods.hasAlreadyVoted(_id, votingKey).call()
|
||||
}
|
||||
|
@ -74,7 +78,7 @@ export default class VotingToChangeProxy {
|
|||
return _limitPerValidator - _activeBallots
|
||||
}
|
||||
|
||||
async minBallotDuration() {
|
||||
return await this.instance.methods.minBallotDuration().call()
|
||||
minBallotDuration() {
|
||||
return this.instance.methods.minBallotDuration().call()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,10 @@ export default class VotingToManageEmissionFunds {
|
|||
return this.instance.methods.getBallotInfo(_id).call()
|
||||
}
|
||||
|
||||
getMinThresholdOfVoters(_id) {
|
||||
return this.instance.methods.getMinThresholdOfVoters(_id).call()
|
||||
}
|
||||
|
||||
getQuorumState(_id) {
|
||||
return this.instance.methods.getQuorumState(_id).call()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { constants } from '../utils/constants'
|
||||
import { messages } from '../utils/messages'
|
||||
import messages from '../utils/messages'
|
||||
import swal from 'sweetalert2'
|
||||
|
||||
function addressesURL(branch) {
|
||||
|
|
121
src/index.js
121
src/index.js
|
@ -16,6 +16,8 @@ import { Router, Route } from 'react-router-dom'
|
|||
import { RouterStore, syncHistoryWithStore } from 'mobx-react-router'
|
||||
import { constants } from './utils/constants'
|
||||
import { getContractsAddresses } from './contracts/addresses'
|
||||
import { getNetworkBranch } from './utils/utils'
|
||||
import messages from './utils/messages'
|
||||
|
||||
const browserHistory = createBrowserHistory()
|
||||
const routingStore = new RouterStore()
|
||||
|
@ -32,57 +34,24 @@ class AppMainRouter extends Component {
|
|||
super(props)
|
||||
commonStore.showLoading()
|
||||
|
||||
getWeb3()
|
||||
window.addEventListener('load', () => this.initChain())
|
||||
}
|
||||
|
||||
initChain = () => {
|
||||
const netId = window.sessionStorage.netId
|
||||
getWeb3(netId, contractsStore.updateKeys)
|
||||
.then(async web3Config => {
|
||||
await getContractsAddresses(constants.NETWORKS[web3Config.netId].BRANCH)
|
||||
|
||||
contractsStore.setWeb3Instance(web3Config)
|
||||
|
||||
const setPoaConsensus = contractsStore.setPoaConsensus(web3Config)
|
||||
const setBallotsStorage = contractsStore.setBallotsStorage(web3Config)
|
||||
const setKeysManager = contractsStore.setKeysManager(web3Config)
|
||||
const setProxyStorage = contractsStore.setProxyStorage(web3Config)
|
||||
const setVotingToChangeKeys = contractsStore.setVotingToChangeKeys(web3Config)
|
||||
const setVotingToChangeMinThreshold = contractsStore.setVotingToChangeMinThreshold(web3Config)
|
||||
const setVotingToChangeProxy = contractsStore.setVotingToChangeProxy(web3Config)
|
||||
const setValidatorMetadata = contractsStore.setValidatorMetadata(web3Config)
|
||||
|
||||
let promises = [
|
||||
setPoaConsensus,
|
||||
setBallotsStorage,
|
||||
setKeysManager,
|
||||
setProxyStorage,
|
||||
setVotingToChangeKeys,
|
||||
setVotingToChangeMinThreshold,
|
||||
setVotingToChangeProxy,
|
||||
setValidatorMetadata
|
||||
]
|
||||
|
||||
const networkName = constants.NETWORKS[web3Config.netId].NAME.toLowerCase()
|
||||
if (networkName === constants.CORE || networkName === constants.SOKOL) {
|
||||
// if we're in Core or Sokol
|
||||
promises.push(contractsStore.setEmissionFunds(web3Config))
|
||||
promises.push(contractsStore.setVotingToManageEmissionFunds(web3Config))
|
||||
}
|
||||
|
||||
await Promise.all(promises)
|
||||
|
||||
await contractsStore.setMiningKey(web3Config)
|
||||
await contractsStore.setVotingKey(web3Config)
|
||||
|
||||
contractsStore.getKeysBallotThreshold()
|
||||
contractsStore.getProxyBallotThreshold()
|
||||
contractsStore.getBallotCancelingThreshold()
|
||||
|
||||
await contractsStore.getBallotsLimits()
|
||||
|
||||
await contractsStore.getAllValidatorMetadata()
|
||||
await contractsStore.getAllBallots()
|
||||
|
||||
console.log('votingKey', contractsStore.votingKey)
|
||||
console.log('miningKey', contractsStore.miningKey)
|
||||
|
||||
await this.initialize(web3Config)
|
||||
commonStore.hideLoading()
|
||||
if (web3Config.netId === 99) {
|
||||
// if it's POA Core network
|
||||
const currentTimestamp = Math.floor(Date.now() / 1000)
|
||||
swal({
|
||||
title: 'Attention',
|
||||
html: generateElement(currentTimestamp < 1651698000 ? messages.poaGnoMerging : messages.poaGnoMerged),
|
||||
type: 'warning'
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error.message)
|
||||
|
@ -90,17 +59,67 @@ class AppMainRouter extends Component {
|
|||
swal({
|
||||
title: 'Error',
|
||||
html: generateElement(error.message),
|
||||
icon: 'error',
|
||||
type: 'error'
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
initialize = async web3Config => {
|
||||
await getContractsAddresses(constants.NETWORKS[web3Config.netId].BRANCH)
|
||||
|
||||
contractsStore.setWeb3Instance(web3Config)
|
||||
|
||||
const setPoaConsensus = contractsStore.setPoaConsensus(web3Config)
|
||||
const setBallotsStorage = contractsStore.setBallotsStorage(web3Config)
|
||||
const setKeysManager = contractsStore.setKeysManager(web3Config)
|
||||
const setProxyStorage = contractsStore.setProxyStorage(web3Config)
|
||||
const setVotingToChangeKeys = contractsStore.setVotingToChangeKeys(web3Config)
|
||||
const setVotingToChangeMinThreshold = contractsStore.setVotingToChangeMinThreshold(web3Config)
|
||||
const setVotingToChangeProxy = contractsStore.setVotingToChangeProxy(web3Config)
|
||||
const setValidatorMetadata = contractsStore.setValidatorMetadata(web3Config)
|
||||
|
||||
let promises = [
|
||||
setPoaConsensus,
|
||||
setBallotsStorage,
|
||||
setKeysManager,
|
||||
setProxyStorage,
|
||||
setVotingToChangeKeys,
|
||||
setVotingToChangeMinThreshold,
|
||||
setVotingToChangeProxy,
|
||||
setValidatorMetadata
|
||||
]
|
||||
|
||||
const networkName = constants.NETWORKS[web3Config.netId].NAME.toLowerCase()
|
||||
if (networkName === constants.CORE || networkName === constants.SOKOL) {
|
||||
// if we're in Core or Sokol
|
||||
promises.push(contractsStore.setEmissionFunds(web3Config))
|
||||
promises.push(contractsStore.setVotingToManageEmissionFunds(web3Config))
|
||||
}
|
||||
|
||||
await Promise.all(promises)
|
||||
|
||||
await contractsStore.updateKeys(web3Config.defaultAccount)
|
||||
|
||||
await contractsStore.getMinBallotDurationsAndThresholds()
|
||||
|
||||
await contractsStore.getAllValidatorMetadata()
|
||||
await contractsStore.getAllBallots()
|
||||
}
|
||||
|
||||
onNetworkChange = e => {
|
||||
commonStore.showLoading(getNetworkBranch(e.value))
|
||||
window.localStorage.netId = e.value
|
||||
window.sessionStorage.netId = e.value
|
||||
contractsStore.resetContracts()
|
||||
ballotsStore.reset()
|
||||
this.initChain()
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Provider {...stores}>
|
||||
<Router history={history}>
|
||||
<Route component={App} />
|
||||
<Route component={props => <App onNetworkChange={this.onNetworkChange} {...props} />} />
|
||||
</Router>
|
||||
</Provider>
|
||||
)
|
||||
|
|
|
@ -4,6 +4,10 @@ class BallotsStore {
|
|||
@observable ballotCards
|
||||
|
||||
constructor() {
|
||||
this.reset()
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.ballotCards = []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { observable, action } from 'mobx'
|
|||
|
||||
class CommonStore {
|
||||
@observable loading
|
||||
@observable loadingNetworkBranch
|
||||
@observable rootPath
|
||||
@observable isActiveFilter
|
||||
@observable isToFinalizeFilter
|
||||
|
@ -10,6 +11,7 @@ class CommonStore {
|
|||
|
||||
constructor() {
|
||||
this.loading = false
|
||||
this.loadingNetworkBranch = null
|
||||
this.isActiveFilter = false
|
||||
this.isToFinalizeFilter = false
|
||||
this.rootPath = '/poa-dapps-voting'
|
||||
|
@ -17,13 +19,15 @@ class CommonStore {
|
|||
}
|
||||
|
||||
@action('show loading')
|
||||
showLoading() {
|
||||
showLoading(loadingNetworkBranch) {
|
||||
this.loading = true
|
||||
this.loadingNetworkBranch = loadingNetworkBranch
|
||||
}
|
||||
|
||||
@action('hide loading')
|
||||
hideLoading() {
|
||||
this.loading = false
|
||||
this.loadingNetworkBranch = null
|
||||
}
|
||||
|
||||
@action('set search term')
|
||||
|
|
|
@ -46,44 +46,49 @@ class ContractsStore {
|
|||
@observable minBallotDuration
|
||||
@observable validatorsMetadata
|
||||
@observable netId
|
||||
@observable injectedWeb3
|
||||
|
||||
constructor() {
|
||||
this.votingKey = null
|
||||
this.miningKey = null
|
||||
this.votingKey = '0x0000000000000000000000000000000000000000'
|
||||
this.miningKey = '0x0000000000000000000000000000000000000000'
|
||||
this.validatorsMetadata = {}
|
||||
this.validatorLimits = { keys: null, minThreshold: null, proxy: null }
|
||||
this.minBallotDuration = { keys: 0, minThreshold: 0, proxy: 0 }
|
||||
this.injectedWeb3 = false
|
||||
}
|
||||
|
||||
@computed
|
||||
get isEmptyVotingKey() {
|
||||
return !this.votingKey || this.votingKey === '0x0000000000000000000000000000000000000000'
|
||||
}
|
||||
|
||||
@computed
|
||||
get isValidVotingKey() {
|
||||
if (this.isEmptyVotingKey) return false
|
||||
if (this.miningKey && this.miningKey !== '0x0000000000000000000000000000000000000000') return true
|
||||
return false
|
||||
}
|
||||
|
||||
@action('Get keys ballot threshold')
|
||||
getKeysBallotThreshold = async () => {
|
||||
this.keysBallotThreshold = await this.ballotsStorage.instance.methods.getBallotThreshold(1).call()
|
||||
this.minThresholdBallotThreshold = this.keysBallotThreshold
|
||||
}
|
||||
|
||||
@action('Get proxy ballot threshold')
|
||||
getProxyBallotThreshold = async () => {
|
||||
this.proxyBallotThreshold = await this.ballotsStorage.instance.methods.getProxyThreshold().call()
|
||||
this.emissionFundsBallotThreshold = this.proxyBallotThreshold
|
||||
}
|
||||
|
||||
@action('Get ballot canceling threshold')
|
||||
getBallotCancelingThreshold = async () => {
|
||||
this.ballotCancelingThreshold = this.votingToManageEmissionFunds
|
||||
? Number(await this.votingToManageEmissionFunds.ballotCancelingThreshold())
|
||||
: 0
|
||||
}
|
||||
|
||||
@action('Set web3Instance')
|
||||
setWeb3Instance = web3Config => {
|
||||
this.web3Instance = web3Config.web3Instance
|
||||
this.netId = web3Config.netId
|
||||
this.injectedWeb3 = web3Config.injectedWeb3
|
||||
this.networkMatch = web3Config.networkMatch
|
||||
}
|
||||
|
||||
@action('Reset contracts')
|
||||
resetContracts = () => {
|
||||
this.poaConsensus = null
|
||||
this.ballotsStorage = null
|
||||
this.emissionFunds = null
|
||||
this.keysManager = null
|
||||
this.proxyStorage = null
|
||||
this.votingToChangeKeys = null
|
||||
this.votingToChangeMinThreshold = null
|
||||
this.votingToChangeProxy = null
|
||||
this.votingToManageEmissionFunds = null
|
||||
this.validatorMetadata = null
|
||||
}
|
||||
|
||||
@action('Set PoA Consensus contract')
|
||||
|
@ -182,18 +187,38 @@ class ContractsStore {
|
|||
}
|
||||
|
||||
@action('Set voting key')
|
||||
setVotingKey = web3Config => {
|
||||
this.votingKey = web3Config.defaultAccount
|
||||
setVotingKey = account => {
|
||||
this.votingKey = account
|
||||
}
|
||||
|
||||
@action('Set mining key')
|
||||
setMiningKey = async web3Config => {
|
||||
try {
|
||||
this.miningKey = await this.keysManager.instance.methods.miningKeyByVoting(web3Config.defaultAccount).call()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
this.miningKey = '0x0000000000000000000000000000000000000000'
|
||||
setMiningKey = async account => {
|
||||
let miningKey = '0x0000000000000000000000000000000000000000'
|
||||
if (account && account !== '0x0000000000000000000000000000000000000000') {
|
||||
try {
|
||||
miningKey = await this.keysManager.instance.methods.miningKeyByVoting(account).call()
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
this.miningKey = miningKey
|
||||
}
|
||||
|
||||
@action('Update keys')
|
||||
updateKeys = async account => {
|
||||
account = account || '0x0000000000000000000000000000000000000000'
|
||||
|
||||
if (this.votingKey && this.votingKey.toLowerCase() === account.toLowerCase()) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setVotingKey(account)
|
||||
await this.setMiningKey(account)
|
||||
|
||||
console.log('votingKey', this.votingKey)
|
||||
console.log('miningKey', this.miningKey)
|
||||
|
||||
await this.getBallotsLimits()
|
||||
}
|
||||
|
||||
@action('Get all keys ballots')
|
||||
|
@ -217,12 +242,26 @@ class ContractsStore {
|
|||
console.log(e.message)
|
||||
}
|
||||
|
||||
const allKeysPromise = this.getCards(keysNextBallotId, 'votingToChangeKeys')
|
||||
const allMinThresholdPromise = this.getCards(minThresholdNextBallotId, 'votingToChangeMinThreshold')
|
||||
const allProxyPromise = this.getCards(proxyNextBallotId, 'votingToChangeProxy')
|
||||
const allEmissionFundsPromise = this.getCards(emissionFundsNextBallotId, 'votingToManageEmissionFunds')
|
||||
const [keysBallots, minThresholdBallots, proxyBallots, emissionFundsBallots] = await Promise.all([
|
||||
this.getBallots(keysNextBallotId, 'votingToChangeKeys'),
|
||||
this.getBallots(minThresholdNextBallotId, 'votingToChangeMinThreshold'),
|
||||
this.getBallots(proxyNextBallotId, 'votingToChangeProxy'),
|
||||
this.getBallots(emissionFundsNextBallotId, 'votingToManageEmissionFunds')
|
||||
])
|
||||
|
||||
await Promise.all([allKeysPromise, allMinThresholdPromise, allProxyPromise, allEmissionFundsPromise])
|
||||
const ballots = [...keysBallots, ...minThresholdBallots, ...proxyBallots, ...emissionFundsBallots]
|
||||
ballotsStore.ballotCards = this.mapBallotsToCards(ballots)
|
||||
|
||||
const finalizedOrCancelled = item => item.isFinalized || item.isCanceled
|
||||
window.localStorage.setItem(
|
||||
`ballots-${this.netId}`,
|
||||
JSON.stringify({
|
||||
votingToChangeKeys: keysBallots.filter(finalizedOrCancelled),
|
||||
votingToChangeMinThreshold: minThresholdBallots.filter(finalizedOrCancelled),
|
||||
votingToChangeProxy: proxyBallots.filter(finalizedOrCancelled),
|
||||
votingToManageEmissionFunds: emissionFundsBallots.filter(finalizedOrCancelled)
|
||||
})
|
||||
)
|
||||
|
||||
const allBallotsIDsLength =
|
||||
keysNextBallotId + minThresholdNextBallotId + proxyNextBallotId + emissionFundsNextBallotId
|
||||
|
@ -309,72 +348,59 @@ class ContractsStore {
|
|||
return votingState
|
||||
}
|
||||
|
||||
getCard = async (id, contractType) => {
|
||||
mapBallotsToCards = ballots => {
|
||||
return ballots.map((ballot, pos) => {
|
||||
let component
|
||||
let params = {
|
||||
id: ballot.id,
|
||||
key: ballot.type + ballot.id,
|
||||
pos,
|
||||
votingState: ballot
|
||||
}
|
||||
switch (ballot.type) {
|
||||
case 'votingToChangeKeys':
|
||||
component = <BallotKeysCard {...params} type={ballotStore.BallotType.keys} />
|
||||
break
|
||||
case 'votingToChangeMinThreshold':
|
||||
component = <BallotMinThresholdCard {...params} type={ballotStore.BallotType.minThreshold} />
|
||||
break
|
||||
case 'votingToChangeProxy':
|
||||
component = <BallotProxyCard {...params} type={ballotStore.BallotType.proxy} />
|
||||
break
|
||||
case 'votingToManageEmissionFunds':
|
||||
component = <BallotEmissionFundsCard {...params} type={ballotStore.BallotType.emissionFunds} />
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
return component
|
||||
})
|
||||
}
|
||||
|
||||
getBallot = async (id, contractType) => {
|
||||
let votingState
|
||||
try {
|
||||
votingState = await this[contractType].getBallotInfo(id, this.votingKey)
|
||||
votingState = this.fillCardVotingState(votingState, contractType)
|
||||
votingState.type = contractType
|
||||
votingState.id = id
|
||||
} catch (e) {
|
||||
console.log(e.message)
|
||||
}
|
||||
|
||||
let card
|
||||
switch (contractType) {
|
||||
case 'votingToChangeKeys':
|
||||
card = (
|
||||
<BallotKeysCard
|
||||
id={id}
|
||||
type={ballotStore.BallotType.keys}
|
||||
key={ballotsStore.ballotCards.length}
|
||||
pos={ballotsStore.ballotCards.length}
|
||||
votingState={votingState}
|
||||
/>
|
||||
)
|
||||
break
|
||||
case 'votingToChangeMinThreshold':
|
||||
card = (
|
||||
<BallotMinThresholdCard
|
||||
id={id}
|
||||
type={ballotStore.BallotType.minThreshold}
|
||||
key={ballotsStore.ballotCards.length}
|
||||
pos={ballotsStore.ballotCards.length}
|
||||
votingState={votingState}
|
||||
/>
|
||||
)
|
||||
break
|
||||
case 'votingToChangeProxy':
|
||||
card = (
|
||||
<BallotProxyCard
|
||||
id={id}
|
||||
type={ballotStore.BallotType.proxy}
|
||||
key={ballotsStore.ballotCards.length}
|
||||
pos={ballotsStore.ballotCards.length}
|
||||
votingState={votingState}
|
||||
/>
|
||||
)
|
||||
break
|
||||
case 'votingToManageEmissionFunds':
|
||||
card = (
|
||||
<BallotEmissionFundsCard
|
||||
id={id}
|
||||
type={ballotStore.BallotType.emissionFunds}
|
||||
key={ballotsStore.ballotCards.length}
|
||||
pos={ballotsStore.ballotCards.length}
|
||||
votingState={votingState}
|
||||
/>
|
||||
)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
return card
|
||||
return votingState
|
||||
}
|
||||
|
||||
getCards = async (nextBallotId, contractType) => {
|
||||
for (let id = nextBallotId - 1; id >= 0; id--) {
|
||||
ballotsStore.ballotCards.push(await this.getCard(id, contractType))
|
||||
}
|
||||
getBallots = async (nextBallotId, contractType) => {
|
||||
const ballotsObject = JSON.parse(window.localStorage.getItem(`ballots-${this.netId}`) || '{}')
|
||||
const existingBallots = ballotsObject[contractType] || []
|
||||
const existingBallotsIds = existingBallots.map(item => item.id)
|
||||
const allBallotsIds = Array(nextBallotId)
|
||||
.fill(undefined)
|
||||
.map((item, index) => index)
|
||||
const newBallotsIds = allBallotsIds.filter(item => !existingBallotsIds.includes(item))
|
||||
const promises = newBallotsIds.map(id => this.getBallot(id, contractType))
|
||||
const newBallots = await Promise.all(promises)
|
||||
return existingBallots.concat(newBallots)
|
||||
}
|
||||
|
||||
@action('Get all keys next ballot ids')
|
||||
|
@ -392,41 +418,63 @@ class ContractsStore {
|
|||
async getBallotsLimits() {
|
||||
return new Promise(async resolve => {
|
||||
if (this.web3Instance && this.netId) {
|
||||
const limitPerValidator = await this.ballotsStorage.instance.methods.getBallotLimitPerValidator().call()
|
||||
let keysLimit = 0
|
||||
let minThresholdLimit = 0
|
||||
let proxyLimit = 0
|
||||
|
||||
const getKeysLimit = await this.votingToChangeKeys.getBallotLimit(this.miningKey, limitPerValidator)
|
||||
const getMinThresholdLimit = await this.votingToChangeMinThreshold.getBallotLimit(
|
||||
this.miningKey,
|
||||
limitPerValidator
|
||||
)
|
||||
const getProxyLimit = await this.votingToChangeProxy.getBallotLimit(this.miningKey, limitPerValidator)
|
||||
if (this.isValidVotingKey) {
|
||||
const limitPerValidator = await this.ballotsStorage.instance.methods.getBallotLimitPerValidator().call()
|
||||
keysLimit = await this.votingToChangeKeys.getBallotLimit(this.miningKey, limitPerValidator)
|
||||
minThresholdLimit = await this.votingToChangeMinThreshold.getBallotLimit(this.miningKey, limitPerValidator)
|
||||
proxyLimit = await this.votingToChangeProxy.getBallotLimit(this.miningKey, limitPerValidator)
|
||||
}
|
||||
|
||||
const getKeysMinBallotDuration = await this.votingToChangeKeys.minBallotDuration()
|
||||
const getMinThresholdMinBallotDuration = await this.votingToChangeMinThreshold.minBallotDuration()
|
||||
const getProxyMinBallotDuration = await this.votingToChangeProxy.minBallotDuration()
|
||||
this.validatorLimits.keys = keysLimit
|
||||
this.validatorLimits.minThreshold = minThresholdLimit
|
||||
this.validatorLimits.proxy = proxyLimit
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
|
||||
@action
|
||||
async getMinBallotDurationsAndThresholds() {
|
||||
return new Promise(async resolve => {
|
||||
if (this.web3Instance && this.netId) {
|
||||
const getKeysMinBallotDuration = this.votingToChangeKeys.minBallotDuration()
|
||||
const getMinThresholdMinBallotDuration = this.votingToChangeMinThreshold.minBallotDuration()
|
||||
const getProxyMinBallotDuration = this.votingToChangeProxy.minBallotDuration()
|
||||
|
||||
const getBallotThreshold = this.ballotsStorage.instance.methods.getBallotThreshold(1).call()
|
||||
const getProxyThreshold = this.ballotsStorage.instance.methods.getProxyThreshold().call()
|
||||
const getBallotCancelingThreshold = this.votingToManageEmissionFunds
|
||||
? this.votingToManageEmissionFunds.ballotCancelingThreshold()
|
||||
: 0
|
||||
|
||||
await Promise.all([
|
||||
getKeysLimit,
|
||||
getMinThresholdLimit,
|
||||
getProxyLimit,
|
||||
getKeysMinBallotDuration,
|
||||
getMinThresholdMinBallotDuration,
|
||||
getProxyMinBallotDuration
|
||||
getProxyMinBallotDuration,
|
||||
getBallotThreshold,
|
||||
getProxyThreshold,
|
||||
getBallotCancelingThreshold
|
||||
]).then(
|
||||
([
|
||||
keysLimit,
|
||||
minThresholdLimit,
|
||||
proxyLimit,
|
||||
keysMinBallotDuration,
|
||||
minThresholdMinBallotDuration,
|
||||
proxyMinBallotDuration
|
||||
proxyMinBallotDuration,
|
||||
keysBallotThreshold,
|
||||
proxyBallotThreshold,
|
||||
cancelingThreshold
|
||||
]) => {
|
||||
this.validatorLimits.keys = keysLimit
|
||||
this.validatorLimits.minThreshold = minThresholdLimit
|
||||
this.validatorLimits.proxy = proxyLimit
|
||||
this.minBallotDuration.keys = keysMinBallotDuration
|
||||
this.minBallotDuration.minThreshold = minThresholdMinBallotDuration
|
||||
this.minBallotDuration.proxy = proxyMinBallotDuration
|
||||
this.keysBallotThreshold = keysBallotThreshold
|
||||
this.minThresholdBallotThreshold = keysBallotThreshold
|
||||
this.proxyBallotThreshold = proxyBallotThreshold
|
||||
this.emissionFundsBallotThreshold = proxyBallotThreshold
|
||||
this.ballotCancelingThreshold = cancelingThreshold
|
||||
resolve()
|
||||
}
|
||||
)
|
||||
|
|
|
@ -53,33 +53,29 @@ constants.navigationData = [
|
|||
|
||||
constants.SOKOL = 'sokol'
|
||||
constants.CORE = 'core'
|
||||
constants.DAI = 'dai'
|
||||
constants.KOVAN = 'kovan'
|
||||
|
||||
constants.NETWORKS = {
|
||||
'42': {
|
||||
NAME: 'Kovan',
|
||||
FULLNAME: 'Kovan Testnet',
|
||||
RPC: 'https://kovan.infura.io/v3/1125fe73d87c4e5396678f4e3089b3dd',
|
||||
BRANCH: constants.KOVAN,
|
||||
TESTNET: true
|
||||
SORTORDER: 3
|
||||
},
|
||||
'77': {
|
||||
NAME: 'Sokol',
|
||||
FULLNAME: 'Sokol Testnet',
|
||||
RPC: 'https://sokol.poa.network',
|
||||
BRANCH: constants.SOKOL,
|
||||
TESTNET: true
|
||||
SORTORDER: 4
|
||||
},
|
||||
'99': {
|
||||
NAME: 'Core',
|
||||
FULLNAME: 'POA Core',
|
||||
RPC: 'https://core.poa.network',
|
||||
BRANCH: constants.CORE,
|
||||
TESTNET: false
|
||||
},
|
||||
'100': {
|
||||
NAME: 'Dai',
|
||||
RPC: 'https://dai.poa.network',
|
||||
BRANCH: constants.DAI,
|
||||
TESTNET: false
|
||||
SORTORDER: 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,80 +1,143 @@
|
|||
import Web3 from 'web3'
|
||||
import { messages } from './messages'
|
||||
import helpers from './helpers'
|
||||
import { constants } from './constants'
|
||||
import { netIdByName } from './helpers'
|
||||
import messages from './messages'
|
||||
|
||||
let getWeb3 = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Wait for loading completion to avoid race conditions with web3 injection timing.
|
||||
window.addEventListener('load', async () => {
|
||||
let web3 = null
|
||||
const defaultNetId = helpers.netIdByBranch(constants.CORE)
|
||||
|
||||
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
|
||||
if (window.ethereum) {
|
||||
web3 = new Web3(window.ethereum)
|
||||
console.log('Injected web3 detected.')
|
||||
try {
|
||||
await window.ethereum.enable()
|
||||
} catch (e) {
|
||||
console.error('User denied account access')
|
||||
reject({ message: messages.USER_DENIED_ACCOUNT_ACCESS })
|
||||
return
|
||||
}
|
||||
} else if (typeof window.web3 !== 'undefined') {
|
||||
web3 = new Web3(window.web3.currentProvider)
|
||||
console.log('Injected web3 detected.')
|
||||
}
|
||||
|
||||
let errorMsg = null
|
||||
let netIdName
|
||||
let netId
|
||||
let defaultAccount = null
|
||||
|
||||
if (web3) {
|
||||
netId = await web3.eth.net.getId()
|
||||
console.log('netId', netId)
|
||||
|
||||
if (!(netId in constants.NETWORKS)) {
|
||||
netIdName = 'ERROR'
|
||||
errorMsg = messages.WRONG_NETWORK_MSG
|
||||
console.log('This is an unknown network.')
|
||||
} else {
|
||||
netIdName = constants.NETWORKS[netId].NAME
|
||||
console.log(`This is ${netIdName}`)
|
||||
}
|
||||
|
||||
const accounts = await web3.eth.getAccounts()
|
||||
|
||||
defaultAccount = accounts[0] || null
|
||||
} else {
|
||||
// Fallback to local if no web3 injection.
|
||||
|
||||
console.log('No web3 instance injected, using Local web3.')
|
||||
console.error('Metamask not found')
|
||||
|
||||
netId = netIdByName(constants.CORE)
|
||||
|
||||
const network = constants.NETWORKS[netId]
|
||||
|
||||
web3 = new Web3(new Web3.providers.HttpProvider(network.RPC))
|
||||
netIdName = network.NAME
|
||||
}
|
||||
|
||||
document.title = `${netIdName} - POA Network Governance DApp`
|
||||
|
||||
if (errorMsg !== null) {
|
||||
reject({ message: errorMsg })
|
||||
return
|
||||
}
|
||||
|
||||
resolve({
|
||||
web3Instance: web3,
|
||||
netIdName,
|
||||
netId,
|
||||
defaultAccount
|
||||
})
|
||||
})
|
||||
})
|
||||
async function getAccounts(web3) {
|
||||
let accounts
|
||||
if (window.ethereum) {
|
||||
accounts = await window.ethereum.request({ method: 'eth_accounts' })
|
||||
} else {
|
||||
accounts = await web3.eth.getAccounts()
|
||||
}
|
||||
return accounts
|
||||
}
|
||||
|
||||
export default getWeb3
|
||||
async function getNetId(web3) {
|
||||
let netId
|
||||
if (window.ethereum) {
|
||||
const { chainId } = window.ethereum
|
||||
netId = web3.utils.isHex(chainId) ? web3.utils.hexToNumber(chainId) : chainId
|
||||
} else {
|
||||
netId = await web3.eth.net.getId()
|
||||
}
|
||||
return netId
|
||||
}
|
||||
|
||||
export async function enableWallet(updateKeys) {
|
||||
if (window.ethereum) {
|
||||
try {
|
||||
await window.ethereum.request({ method: 'eth_requestAccounts' })
|
||||
} catch (e) {
|
||||
await updateKeys(null)
|
||||
throw Error(messages.USER_DENIED_ACCOUNT_ACCESS)
|
||||
}
|
||||
|
||||
const web3 = new Web3(window.ethereum)
|
||||
const accounts = await getAccounts(web3)
|
||||
|
||||
await updateKeys(accounts[0])
|
||||
}
|
||||
}
|
||||
|
||||
export default async function getWeb3(netId, updateKeys) {
|
||||
let web3 = null
|
||||
|
||||
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
|
||||
if (window.ethereum) {
|
||||
web3 = new Web3(window.ethereum)
|
||||
console.log('Injected web3 detected.')
|
||||
if (!window.ethereum.autoRefreshOnNetworkChange) {
|
||||
window.ethereum.on('chainChanged', () => {
|
||||
window.location.reload()
|
||||
})
|
||||
}
|
||||
} else if (window.web3) {
|
||||
web3 = new Web3(window.web3.currentProvider)
|
||||
console.log('Injected web3 detected.')
|
||||
}
|
||||
|
||||
if (!netId) {
|
||||
// Load for the first time in the current browser's session
|
||||
if (web3) {
|
||||
// MetaMask (or another plugin) is injected
|
||||
netId = await getNetId(web3)
|
||||
if (!(netId in constants.NETWORKS)) {
|
||||
// If plugin's netId is unsupported, try to use
|
||||
// the previously chosen netId
|
||||
netId = window.localStorage.netId
|
||||
}
|
||||
} else {
|
||||
// MetaMask (or another plugin) is not injected,
|
||||
// so try to use the previously chosen netId
|
||||
netId = window.localStorage.netId
|
||||
}
|
||||
if (!(netId in constants.NETWORKS)) {
|
||||
// If plugin's netId and/or previously chosen netId are not supported,
|
||||
// fallback to default netId
|
||||
netId = defaultNetId
|
||||
}
|
||||
window.localStorage.netId = netId
|
||||
window.sessionStorage.netId = netId
|
||||
}
|
||||
|
||||
netId = Number(netId)
|
||||
|
||||
const network = constants.NETWORKS[netId]
|
||||
const injectedWeb3 = web3 !== null
|
||||
let netIdName = network.NAME
|
||||
let defaultAccount = null
|
||||
let networkMatch = false
|
||||
|
||||
if (web3) {
|
||||
const accounts = await getAccounts(web3)
|
||||
defaultAccount = accounts[0] || null
|
||||
|
||||
if (!defaultAccount) {
|
||||
console.log('Unlock your wallet')
|
||||
}
|
||||
|
||||
let currentAccount = defaultAccount ? defaultAccount.toLowerCase() : null
|
||||
function onUpdateAccount(account) {
|
||||
if (account && account !== currentAccount) {
|
||||
currentAccount = account
|
||||
updateKeys(account)
|
||||
}
|
||||
}
|
||||
if (window.ethereum) {
|
||||
window.ethereum.on('accountsChanged', accs => {
|
||||
const account = accs && accs.length > 0 ? accs[0].toLowerCase() : null
|
||||
onUpdateAccount(account)
|
||||
})
|
||||
} else if (web3.currentProvider.publicConfigStore) {
|
||||
web3.currentProvider.publicConfigStore.on('update', obj => {
|
||||
const account = obj.selectedAddress ? obj.selectedAddress.toLowerCase() : null
|
||||
onUpdateAccount(account)
|
||||
})
|
||||
}
|
||||
|
||||
const web3NetId = await getNetId(web3)
|
||||
if (web3NetId === netId) {
|
||||
networkMatch = true
|
||||
} else {
|
||||
web3 = null
|
||||
}
|
||||
}
|
||||
|
||||
if (!web3) {
|
||||
web3 = new Web3(new Web3.providers.HttpProvider(network.RPC))
|
||||
}
|
||||
|
||||
document.title = `${netIdName} - POA Governance DApp`
|
||||
|
||||
return {
|
||||
web3Instance: web3,
|
||||
netId,
|
||||
netIdName,
|
||||
injectedWeb3,
|
||||
defaultAccount,
|
||||
networkMatch
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,10 +58,10 @@ function sendTransactionByVotingKey(props, to, data, cb, warning) {
|
|||
)
|
||||
}
|
||||
|
||||
function netIdByName(netName) {
|
||||
const netNameLowerCase = netName.toLowerCase()
|
||||
function netIdByBranch(branch) {
|
||||
const branchLowerCase = branch.toLowerCase()
|
||||
for (let netId in constants.NETWORKS) {
|
||||
if (constants.NETWORKS[netId].NAME.toLowerCase() === netNameLowerCase) {
|
||||
if (constants.NETWORKS[netId].BRANCH.toLowerCase() === branchLowerCase) {
|
||||
return netId
|
||||
}
|
||||
}
|
||||
|
@ -71,5 +71,5 @@ function netIdByName(netName) {
|
|||
module.exports = {
|
||||
toAscii,
|
||||
sendTransactionByVotingKey,
|
||||
netIdByName
|
||||
netIdByBranch
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { getNetworkFullName } from './utils'
|
||||
|
||||
let messages = {}
|
||||
messages.invalidVotingKeyMsg = key => {
|
||||
return `The key ${key} is not valid voting Key! Please make sure you have loaded correct voting key in Metamask`
|
||||
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'
|
||||
|
@ -16,12 +18,7 @@ messages.PROPOSED_ADDRESS_IS_NOT_ADDRESS_MSG = "Proposed address isn't address"
|
|||
messages.END_TIME_SHOULD_BE_GREATER_THAN_NOW_MSG = 'Ballot end time should be greater than now'
|
||||
messages.BALLOT_TYPE_IS_EMPTY_MSG = 'Ballot type is empty'
|
||||
messages.USER_DENIED_ACCOUNT_ACCESS = 'You have denied access to your accounts'
|
||||
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.NO_METAMASK_MSG = 'Your MetaMask is locked or not installed.'
|
||||
messages.ballotIsNotActiveMsg = timeToStart => {
|
||||
return `The ballot is not active yet. Time to start: ${timeToStart}`
|
||||
}
|
||||
|
@ -38,17 +35,26 @@ messages.EMISSION_RELEASE_TIME_IN_FUTURE = emissionReleaseTime => {
|
|||
}
|
||||
messages.PREVIOUS_BALLOT_NOT_FINALIZED = 'Previous ballot should be finalized first.'
|
||||
messages.BALLOT_CREATE_FAILED_TX = `Your transaction was failed. Please make sure you set correct parameters for ballot creation.
|
||||
Make sure you don't have Transaction Error. Exception thrown in contract code message in Metamask before you sign it.`
|
||||
Make sure you don't have Transaction Error. Exception thrown in contract code message in MetaMask before you sign it.`
|
||||
messages.VOTE_FAILED_TX = `Your transaction was failed. Please make sure you haven't already voted for this ballot.
|
||||
Make sure you don't have Transaction Error. Exception thrown in contract code message in Metamask before you sign it.`
|
||||
Make sure you don't have Transaction Error. Exception thrown in contract code message in MetaMask before you sign it.`
|
||||
messages.FINALIZE_FAILED_TX = `Your transaction was failed. Make sure you don't have Transaction Error.
|
||||
Exception thrown in contract code message in Metamask before you sign it.`
|
||||
Exception thrown in contract code message in MetaMask before you sign it.`
|
||||
messages.CANCEL_BALLOT_FAILED_TX = `Your transaction was failed. Make sure you don't have Transaction Error.
|
||||
Exception thrown in contract code message in Metamask before you sign it.`
|
||||
Exception thrown in contract code message in MetaMask before you sign it.`
|
||||
messages.DESCRIPTION_IS_EMPTY = 'Description cannot be empty'
|
||||
messages.wrongRepo = repo => {
|
||||
return `There is no contracts.json in configured repo ${repo}`
|
||||
}
|
||||
module.exports = {
|
||||
messages
|
||||
messages.networkMatchError = function(netId) {
|
||||
const networkName = getNetworkFullName(Number(netId))
|
||||
return `Networks in DApp and MetaMask do not match. Switch MetaMask to <b>${networkName}</b> or change the network in DApp.`
|
||||
}
|
||||
|
||||
messages.poaGnoMerging =
|
||||
'POA is joining the Gnosis Chain ecosystem, and token holders can now swap POA for STAKE and then STAKE for GNO on the Gnosis Chain! More info and instructions <a href="https://www.poa.network/" target="_blank">here</a>.'
|
||||
|
||||
messages.poaGnoMerged =
|
||||
'POA Network merged with the Gnosis Chain.<br /><a href="https://www.poa.network/" target="_blank">More information</a> about the merger.'
|
||||
|
||||
export default messages
|
||||
|
|
|
@ -1,19 +1,11 @@
|
|||
import { constants } from './constants'
|
||||
|
||||
export const isTestnet = netId => {
|
||||
return netId in constants.NETWORKS && constants.NETWORKS[netId].TESTNET
|
||||
}
|
||||
|
||||
export const isValidNetwork = netId => {
|
||||
return netId in constants.NETWORKS
|
||||
}
|
||||
|
||||
export const getNetworkBranch = netId => {
|
||||
return constants.NETWORKS[netId].BRANCH
|
||||
}
|
||||
|
||||
export const getNetworkName = netId => {
|
||||
return constants.NETWORKS[netId].NAME
|
||||
export const getNetworkFullName = netId => {
|
||||
return constants.NETWORKS[netId].FULLNAME
|
||||
}
|
||||
|
||||
export const scrollToTop = () => {
|
||||
|
|
Loading…
Reference in New Issue