- Total Steak -
-- 1,351.0000… -
-- Available Steak -
-- 1,337.0000… -
-- This text proposal (#1) was submitted at block #120 and voting started at block #135. + Submitted a few seconds ago. Voting started a few seconds ago
diff --git a/CHANGELOG.md b/CHANGELOG.md index ffe893b39a..4e8cb5a7e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * [\#1464](https://github.com/cosmos/voyager/issues/1464) Added governance transactions to tx history page @fedekunze * [\#1401](https://github.com/cosmos/voyager/issues/1401) Display governance proposals index. @fedekunze * [\#1472](https://github.com/cosmos/voyager/issues/1472) Added mock functionality for redelegation @fedekunze + @faboweb +* [\#1402](https://github.com/cosmos/voyager/issues/1402) Proposal creation through modal @fedekunze + @NodeGuy + @jbibla * [\#1501](https://github.com/cosmos/voyager/issues/1501) Vote on proposals through modal @fedekunze + @jbibla * [\#1567](https://github.com/cosmos/voyager/pull/1567) Various refactors on `main`, `node` and `lcdClient`. @NodeGuy * [\#1502](https://github.com/cosmos/voyager/issues/1502) A page for each proposal. @jbibla @@ -46,6 +47,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * [\#1410](https://github.com/cosmos/voyager/issues/1410) removed end undelegations as not needed in the SDK anymore * [\#1543](https://github.com/cosmos/voyager/issues/1543) updated unit tests to use `describe` and `it` instead of `test` @fedekunze * [\#1377](https://github.com/cosmos/voyager/issues/1377) deleted AppFooter page @fedekunze +* [\#1582](https://github.com/cosmos/voyager/issues/1582) proposals now use time instead of block number @fedekunze ### Fixed @@ -64,6 +66,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * [\#1131](https://github.com/cosmos/voyager/issues/1131) Display only error message on notifications @fedekunze * [\#1440](https://github.com/cosmos/voyager/issues/1440) Fixed an error that prevented disconnecting from the RPC websocket if it wasn't defined @fedekunze * [\#1460](https://github.com/cosmos/voyager/issues/1460) Removing release-candidate tag when publishing @faboweb +* [\#1402](https://github.com/cosmos/voyager/issues/1402) Fixed minor issues in voting and deposit @fedekunze * [\#1574](https://github.com/cosmos/voyager/issues/1574) Fixed typo on mocked governance txs that prevented the execution of the query @fedekunze * [\#1575](https://github.com/cosmos/voyager/issues/1575) Fixed tags on governace transactions query @fedekunze diff --git a/app/src/renderer/components/common/PagePreferences.vue b/app/src/renderer/components/common/PagePreferences.vue index 9c5a54c67a..a4091a87d4 100644 --- a/app/src/renderer/components/common/PagePreferences.vue +++ b/app/src/renderer/components/common/PagePreferences.vue @@ -1,6 +1,8 @@ tm-page(data-title="Preferences") - div(slot="menu"): vm-tool-bar + template(slot="menu-body") + tm-balance + vm-tool-bar tm-part(title='Settings') tm-list-item(type="field" title="Select network to connect to") @@ -48,11 +50,13 @@ tm-page(data-title="Preferences") import { mapGetters } from "vuex" import { TmListItem, TmBtn, TmPage, TmPart, TmField } from "@tendermint/ui" import VmToolBar from "common/VmToolBar" +import TmBalance from "common/TmBalance" import TmModal from "common/TmModal" export default { name: `page-preferences`, components: { + TmBalance, TmBtn, TmField, TmListItem, diff --git a/app/src/renderer/components/common/TmBalance.vue b/app/src/renderer/components/common/TmBalance.vue index 4e11640f07..fba073cbd9 100644 --- a/app/src/renderer/components/common/TmBalance.vue +++ b/app/src/renderer/components/common/TmBalance.vue @@ -26,6 +26,8 @@ :class="{'tab-selected': $route.name === tab}", ) router-link(:to="{name: tab}") {{ tab }} + + slot diff --git a/app/src/renderer/components/governance/LiProposal.vue b/app/src/renderer/components/governance/LiProposal.vue index 9950dc6d86..f8efe69198 100644 --- a/app/src/renderer/components/governance/LiProposal.vue +++ b/app/src/renderer/components/governance/LiProposal.vue @@ -3,9 +3,9 @@ tr.li-proposal td.li-proposal__value span.validator-profile__status(v-bind:class="status.color" v-tooltip.top="status.message") h2 - router-link(:to="{ name: 'Proposal', params: { proposalId, proposal, status }}") {{ proposal.title }} + router-link(:to="{ name: 'Proposal', params: { proposalId: proposal.proposal_id, proposal, status }}") {{ proposal.title }} p {{ description }} - td {{ submitBlock }} + td {{ `#` + proposal.proposal_id }} td.li-proposal__value.yes {{ proposal.tally_result.yes }} td.li-proposal__value.no {{ proposal.tally_result.no }} td.li-proposal__value.no_with_veto {{ proposal.tally_result.no_with_veto }} @@ -13,7 +13,6 @@ tr.li-proposal + + diff --git a/app/src/renderer/components/governance/ModalVote.vue b/app/src/renderer/components/governance/ModalVote.vue index 13174ac3d9..77cbc96b2b 100644 --- a/app/src/renderer/components/governance/ModalVote.vue +++ b/app/src/renderer/components/governance/ModalVote.vue @@ -12,29 +12,29 @@ tm-form-group.modal-vote-form-group.options tm-btn#vote-yes( - @click.native="vote('yes')" - :class="[option === `yes` ? 'active' : '']" + @click.native="vote('Yes')" + :class="[option === `Yes` ? 'active' : '']" color="secondary" value="Yes" size="md") tm-btn#vote-no( - @click.native="vote('no')" - :class="[option === `no` ? 'active' : '']" + @click.native="vote('No')" + :class="[option === `No` ? 'active' : '']" color="secondary" value="No" size="md") tm-btn#vote-veto( - @click.native="vote('no_with_veto')" - :class="[option === `no_with_veto` ? 'active' : '']" + @click.native="vote('NoWithVeto')" + :class="[option === `NoWithVeto` ? 'active' : '']" color="secondary" value="No With Veto" size="md") tm-btn#vote-abstain( - @click.native="vote('abstain')" - :class="[option === `abstain` ? 'active' : '']" + @click.native="vote('Abstain')" + :class="[option === `Abstain` ? 'active' : '']" color="secondary" value="Abstain" size="md") @@ -56,10 +56,10 @@ import Modal from "common/TmModal" import { TmBtn, TmField, TmFormGroup, TmFormMsg } from "@tendermint/ui" const isValid = option => - option === `yes` || - option === `no` || - option === `no_with_veto` || - option === `abstain` + option === `Yes` || + option === `No` || + option === `NoWithVeto` || + option === `Abstain` export default { name: `modal-vote`, diff --git a/app/src/renderer/components/governance/PageGovernance.vue b/app/src/renderer/components/governance/PageGovernance.vue index 907919166d..b013d14be9 100644 --- a/app/src/renderer/components/governance/PageGovernance.vue +++ b/app/src/renderer/components/governance/PageGovernance.vue @@ -1,16 +1,19 @@ tm-page(data-title='Governance').governance template(slot="menu-body") - tm-balance(:tabs="tabs") + tm-balance(:tabs="tabs"): tm-btn#propose-btn(value="Create Proposal" color="primary" @click.native="onPropose") - div(slot="menu"): vm-tool-bar - router-link(to="/governance/proposals/new" exact v-tooltip.bottom="'New Proposal'") - i.material-icons add - a(@click='setSearch()' v-tooltip.bottom="'Search'") - i.search.material-icons search + vm-tool-bar + a(@click='setSearch()' v-tooltip.bottom="'Search'") + i.search.material-icons search modal-search(type="proposals") - + modal-propose( + v-if="showModalPropose" + v-on:createProposal="propose" + :showModalPropose.sync="showModalPropose" + :denom="bondingDenom.toLowerCase()" + ) router-view @@ -18,9 +21,10 @@ tm-page(data-title='Governance').governance import { mapGetters } from "vuex" import DataEmptySearch from "common/TmDataEmptySearch" import ModalSearch from "common/TmModalSearch" +import ModalPropose from "./ModalPropose" import VmToolBar from "common/VmToolBar" import TmBalance from "common/TmBalance" -import { TmPage, TmDataEmpty, TmDataLoading } from "@tendermint/ui" +import { TmPage, TmDataEmpty, TmDataLoading, TmBtn } from "@tendermint/ui" export default { name: `page-governance`, components: { @@ -29,16 +33,48 @@ export default { TmDataEmpty, DataEmptySearch, ModalSearch, + ModalPropose, TmPage, + TmBtn, VmToolBar }, computed: { - ...mapGetters([`config`, `proposals`, `filters`]) + // TODO: get min deposit denom from gov params + ...mapGetters([`proposals`, `filters`, `bondingDenom`]) }, data: () => ({ - tabs: [`Proposals`] + query: ``, + tabs: [`Proposals`], + showModalPropose: false }), methods: { + onPropose() { + this.showModalPropose = true + }, + async propose({ title, description, type, amount }) { + try { + await this.$store.dispatch(`submitProposal`, { + title, + description, + type, + initial_deposit: [ + { + denom: this.bondingDenom.toLowerCase(), + amount: String(amount) + } + ] + }) + this.$store.commit(`notify`, { + title: `Successful proposal submission!`, + body: `You have successfully submitted a new ${type.toLowerCase()} proposal` + }) + } catch ({ message }) { + this.$store.commit(`notifyError`, { + title: `Error while submitting a new ${type.toLowerCase()} proposal`, + body: message + }) + } + }, setSearch(bool = !this.filters[`proposals`].search.visible) { this.$store.commit(`setSearchVisible`, [`proposals`, bool]) } diff --git a/app/src/renderer/components/governance/PageProposal.vue b/app/src/renderer/components/governance/PageProposal.vue index 13653e0423..13222e7a51 100644 --- a/app/src/renderer/components/governance/PageProposal.vue +++ b/app/src/renderer/components/governance/PageProposal.vue @@ -13,14 +13,14 @@ tm-page(data-title='Proposal') .top.column div.validator-profile__status-and-title span.validator-profile__status(v-bind:class="status.color" v-tooltip.top="status.message") - .validator-profile__header__name__title {{ proposal.title }} + .validator-profile__header__name__title {{ proposal.title }} {{ `(#` + proposal.proposal_id + `)`}} .column.validator-profile__header__actions tm-btn#vote-btn(v-if="status.button === 'vote'" value="Vote" color="primary" @click.native="onVote") tm-btn#deposit-btn(v-if="status.button === 'deposit'" value="Deposit" color="primary" @click.native="onDeposit") tm-btn(v-if="!status.button" disabled value="Deposit / Vote" color="primary") .row.description - p This {{ proposalType }} proposal ({{ `#` + proposal.proposal_id }}) was submitted at block {{ submitBlock }} and voting started at {{ voteBlock }}. + p Submitted {{ submittedAgo }}. {{ proposal.proposal_status === `DepositPeriod` ? `Deposit ends ` + depositEndsIn : `Voting started ` + votingStartedAgo }} .row.validator-profile__header__data.votes dl.colored_dl @@ -47,10 +47,11 @@ tm-page(data-title='Proposal') modal-deposit( v-if="showModalDeposit" - v-on:castVote="deposit" - :showModalVote.sync="showModalVote" + v-on:submitDeposit="deposit" + :showModalDeposit.sync="showModalDeposit" :proposalId="proposal.proposal_id" :proposalTitle="proposal.title" + :denom="bondingDenom.toLowerCase()" ) modal-vote( @@ -63,6 +64,7 @@ tm-page(data-title='Proposal') diff --git a/app/src/renderer/components/governance/TableProposals.vue b/app/src/renderer/components/governance/TableProposals.vue index 80f6823fe7..d5dcfa189a 100644 --- a/app/src/renderer/components/governance/TableProposals.vue +++ b/app/src/renderer/components/governance/TableProposals.vue @@ -20,7 +20,6 @@ import { includes, orderBy } from "lodash" import LiProposal from "./LiProposal" import { TmDataEmpty, TmDataLoading } from "@tendermint/ui" import DataEmptySearch from "common/TmDataEmptySearch" -import { ratToBigNumber } from "scripts/common" import ModalSearch from "common/TmModalSearch" import PanelSort from "staking/PanelSort" import VmToolBar from "common/VmToolBar" @@ -54,18 +53,12 @@ export default { let copiedProposals = JSON.parse(JSON.stringify(this.proposals)) return Object.values(copiedProposals).map(p => { - p.tally_result.yes = Math.round( - ratToBigNumber(p.tally_result.yes).toNumber() - ) - p.tally_result.no = Math.round( - ratToBigNumber(p.tally_result.no).toNumber() - ) + p.tally_result.yes = Math.round(parseFloat(p.tally_result.yes)) + p.tally_result.no = Math.round(parseFloat(p.tally_result.no)) p.tally_result.no_with_veto = Math.round( - ratToBigNumber(p.tally_result.no_with_veto).toNumber() - ) - p.tally_result.abstain = Math.round( - ratToBigNumber(p.tally_result.abstain).toNumber() + parseFloat(p.tally_result.no_with_veto) ) + p.tally_result.abstain = Math.round(parseFloat(p.tally_result.abstain)) return p }) }, @@ -95,10 +88,10 @@ export default { class: `proposal_title` }, { - title: `Submitted Block`, - value: `submit_block`, - tooltip: `Block height when proposal was submitted`, - class: `submit_block` + title: `Proposal id`, + value: `proposal_id`, + tooltip: `Id of the proposal`, + class: `proposal_id` }, { title: `Yes`, @@ -141,7 +134,6 @@ export default { Mousetrap.bind([`command+f`, `ctrl+f`], () => this.setSearch(true)) Mousetrap.bind([`command+n`, `ctrl+n`], () => this.newProposal()) Mousetrap.bind(`esc`, () => this.setSearch(false)) - this.$store.dispatch(`getProposals`) } } diff --git a/app/src/renderer/components/staking/PageStaking.vue b/app/src/renderer/components/staking/PageStaking.vue index ee6e4054a8..f17fef20b5 100644 --- a/app/src/renderer/components/staking/PageStaking.vue +++ b/app/src/renderer/components/staking/PageStaking.vue @@ -3,11 +3,11 @@ tm-page(data-title="Staking").staking template(slot="menu-body") tm-balance(:tabs="tabs") - div(slot="menu"): vm-tool-bar - a(@click='connected && updateDelegates()' v-tooltip.bottom="'Refresh'" :disabled="!connected") - i.material-icons refresh - a(@click='setSearch()' v-tooltip.bottom="'Search'") - i.search.material-icons search + vm-tool-bar + a(@click='connected && updateDelegates()' v-tooltip.bottom="'Refresh'" :disabled="!connected") + i.material-icons refresh + a(@click='setSearch()' v-tooltip.bottom="'Search'") + i.search.material-icons search modal-search(type="delegates") diff --git a/app/src/renderer/components/wallet/PageTransactions.vue b/app/src/renderer/components/wallet/PageTransactions.vue index dda26ed415..186dce239a 100644 --- a/app/src/renderer/components/wallet/PageTransactions.vue +++ b/app/src/renderer/components/wallet/PageTransactions.vue @@ -1,7 +1,7 @@ tm-page(data-title='Transactions') - template(slot="menu-body"): tm-balance - div(slot="menu") + template(slot="menu-body") + tm-balance vm-tool-bar a(@click='connected && refreshTransactions()' v-tooltip.bottom="'Refresh'" :disabled="!connected") i.material-icons refresh diff --git a/app/src/renderer/components/wallet/PageWallet.vue b/app/src/renderer/components/wallet/PageWallet.vue index cf64514de7..6b381d5bfe 100644 --- a/app/src/renderer/components/wallet/PageWallet.vue +++ b/app/src/renderer/components/wallet/PageWallet.vue @@ -1,7 +1,7 @@ tm-page(data-title="Wallet") - template(slot="menu-body", v-if="config.devMode"): tm-balance - div(slot="menu") + template(slot="menu-body") + tm-balance vm-tool-bar a(@click='connected && updateBalances()' v-tooltip.bottom="'Refresh'" :disabled="!connected") i.material-icons refresh diff --git a/app/src/renderer/routes.js b/app/src/renderer/routes.js index ea6a021c01..3784c9f84a 100644 --- a/app/src/renderer/routes.js +++ b/app/src/renderer/routes.js @@ -21,10 +21,6 @@ export default [ } ] }, - { - path: `/governance/proposals/new`, - component: governance(`ProposalsNewText`) - }, { path: `/governance/:proposalId`, name: `Proposal`, diff --git a/app/src/renderer/styles/app.styl b/app/src/renderer/styles/app.styl index cd3c760e8b..f2ed9e13af 100644 --- a/app/src/renderer/styles/app.styl +++ b/app/src/renderer/styles/app.styl @@ -107,6 +107,9 @@ input.tm-field border-width 1px padding 0 2rem +.tm-page-header-body + position relative + .tm-li-container margin-right 1rem diff --git a/app/src/renderer/vuex/modules/delegation.js b/app/src/renderer/vuex/modules/delegation.js index 9d46f93e99..0157a64ca4 100644 --- a/app/src/renderer/vuex/modules/delegation.js +++ b/app/src/renderer/vuex/modules/delegation.js @@ -222,38 +222,6 @@ export default ({ node }) => { dispatch(`updateDelegates`) }, 5000) } - // deprecated - // async endUnbonding({ rootState, state, dispatch, commit }, validatorAddr) { - // try { - // await dispatch(`sendTx`, { - // type: `updateDelegations`, - // to: rootState.wallet.address, // TODO strange syntax - // complete_unbondings: [ - // { - // delegator_addr: rootState.wallet.address, - // validator_addr: validatorAddr - // } - // ] - // }) - - // let balance = state.unbondingDelegations[validatorAddr].balance - // commit(`setUnbondingDelegations`, { - // validator_addr: validatorAddr, - // balance: { amount: 0 } - // }) - // commit(`notify`, { - // title: `Ending undelegation successful`, - // body: `You successfully undelegated ${balance.amount} ${ - // balance.denom - // }s from ${validatorAddr}` - // }) - // } catch (err) { - // commit(`notifyError`, { - // title: `Ending undelegation failed`, - // body: err - // }) - // } - // } } return { diff --git a/app/src/renderer/vuex/modules/governance/deposits.js b/app/src/renderer/vuex/modules/governance/deposits.js index 98898ccfce..c07f29c6ca 100644 --- a/app/src/renderer/vuex/modules/governance/deposits.js +++ b/app/src/renderer/vuex/modules/governance/deposits.js @@ -20,27 +20,19 @@ export default ({ node }) => { }, async submitDeposit( { - rootState: { config, wallet }, + rootState: { wallet }, dispatch }, - proposalId, - depositAmount + { proposal_id, amount } ) { - const denom = config.bondingDenom.toLowerCase() await dispatch(`sendTx`, { type: `submitProposalDeposit`, - proposal_id: proposalId, + to: proposal_id, + proposal_id, depositer: wallet.address, - amount: [ - { - denom: denom, - amount: depositAmount - } - ] + amount }) - setTimeout(async () => { - dispatch(`getProposalDeposits`, proposalId) - }, 5000) + await dispatch(`getProposalDeposits`, proposal_id) } } return { diff --git a/app/src/renderer/vuex/modules/governance/proposals.js b/app/src/renderer/vuex/modules/governance/proposals.js index ffb030b92a..c27d1f5034 100644 --- a/app/src/renderer/vuex/modules/governance/proposals.js +++ b/app/src/renderer/vuex/modules/governance/proposals.js @@ -37,19 +37,17 @@ export default ({ node }) => { rootState: { wallet }, dispatch }, - proposal + { title, description, type, initial_deposit } ) { await dispatch(`sendTx`, { type: `submitProposal`, proposer: wallet.address, - proposal_type: proposal.proposal_type, - title: proposal.title, - description: proposal.description, - initial_deposit: proposal.initial_deposit + proposal_type: type, + title, + description, + initial_deposit }) - setTimeout(async () => { - dispatch(`getProposals`) - }, 5000) + await dispatch(`getProposals`) } } return { diff --git a/app/src/renderer/vuex/modules/governance/votes.js b/app/src/renderer/vuex/modules/governance/votes.js index 25f04523a9..e24e35104c 100644 --- a/app/src/renderer/vuex/modules/governance/votes.js +++ b/app/src/renderer/vuex/modules/governance/votes.js @@ -18,14 +18,15 @@ export default ({ node }) => { commit(`setProposalVotes`, proposalId, votes) state.loading = false }, - async submitVote({ rootState, dispatch }, proposalId, option) { + async submitVote({ rootState, dispatch }, { proposal_id, option }) { await dispatch(`sendTx`, { + to: proposal_id, type: `submitProposalVote`, - proposal_id: proposalId, + proposal_id, voter: rootState.wallet.address, option }) - dispatch(`getProposalVotes`, proposalId) + await dispatch(`getProposalVotes`, proposal_id) } } return { diff --git a/test/e2e/common.js b/test/e2e/common.js index 8d6e0dab47..d47c76727c 100644 --- a/test/e2e/common.js +++ b/test/e2e/common.js @@ -11,6 +11,7 @@ function sleep(ms) { module.exports = { async closeNotifications(app) { // close notifications as they overlay the menu button + console.log(`closing notifications`) await sleep(100) while (await app.client.isExisting(`.tm-notification`)) { await app.client.$(`.tm-notification`).click() @@ -61,6 +62,7 @@ module.exports = { async navigateToPreferences(app) { await module.exports.openMenu(app) // click link + await app.client.waitForExist(`#settings`, 1000) await app.client.$(`#settings`).click() console.log(`navigated to preferences`) }, diff --git a/test/e2e/launch.js b/test/e2e/launch.js index 81130efd8e..2e70499993 100644 --- a/test/e2e/launch.js +++ b/test/e2e/launch.js @@ -232,6 +232,12 @@ async function writeLogs(app, location) { async function startApp(app, awaitingSelector = `.tm-session`) { console.log(`Starting app`) await app.start() + if (!app.browserWindow) { + console.log(`No browser window`) + return + } + app.browserWindow.setBounds({ x: 0, y: 0, width: 1600, height: 1024 }) + app.browserWindow.setSize(1600, 1024) await app.client.waitForExist(awaitingSelector, 10 * 1000).catch(async e => { await handleCrash(app, e) @@ -370,6 +376,8 @@ module.exports = { console.log(`restarting app`) await stop(app) await startApp(app, awaitingSelector) + app.browserWindow.setBounds({ x: 0, y: 0, width: 1600, height: 1024 }) + app.browserWindow.setSize(1600, 1024) }, refresh: async function(app, awaitingSelector = `.tm-session-title=Sign In`) { console.log(`refreshing app`) diff --git a/test/e2e/signin.js b/test/e2e/signin.js index 36e9bc9801..886f0b16b4 100644 --- a/test/e2e/signin.js +++ b/test/e2e/signin.js @@ -14,9 +14,10 @@ let { openMenu, login, sleep } = require(`./common.js`) test(`sign in`, async function(t) { let { app } = await getApp(t) - await refresh(app) let el = (...args) => app.client.$(...args) + app.browserWindow.setBounds({ x: 0, y: 0, width: 1600, height: 1024 }) + app.browserWindow.setSize(1600, 1024) // clicking the button does fail in webdriver as there is no actual click handler on the button async function clickContinue() { return app.client.submitForm(`.tm-session form`) @@ -164,10 +165,11 @@ test(`sign in`, async function(t) { t.test(`sign out`, async function(t) { await refresh(app) + app.browserWindow.setBounds({ x: 0, y: 0, width: 1600, height: 1024 }) + app.browserWindow.setSize(1600, 1024) await login(app, `testkey`) await app.client.waitForExist(`#signOut-btn`, 1000) await app.client.$(`#signOut-btn`).click() - await app.client.waitForExist(`.tm-session`, 1000) t.end() diff --git a/test/unit/specs/components/common/__snapshots__/PagePreferences.spec.js.snap b/test/unit/specs/components/common/__snapshots__/PagePreferences.spec.js.snap index e3e0f4a20c..a952c6c688 100644 --- a/test/unit/specs/components/common/__snapshots__/PagePreferences.spec.js.snap +++ b/test/unit/specs/components/common/__snapshots__/PagePreferences.spec.js.snap @@ -16,66 +16,121 @@ exports[`PagePreferences has the expected html structure if connected 1`] = ` > - - -
- This text proposal (#1) was submitted at block #120 and voting started at block #135. + Submitted a few seconds ago. Voting started a few seconds ago