From 56394877130efe1a3e291542be4ee0e8b050c60d Mon Sep 17 00:00:00 2001 From: Vadim Arasev Date: Mon, 17 Sep 2018 19:17:56 +0300 Subject: [PATCH] (Feature) Add voting to manage emission funds --- src/assets/App.css | 2 +- .../stylesheets/application/_controls.scss | 8 + src/components/BallotCard.jsx | 219 +++++++++++++----- src/components/BallotEmissionFundsCard.jsx | 32 +++ .../BallotEmissionFundsMetadata.jsx | 120 ++++++++++ src/components/BallotKeysCard.jsx | 17 -- src/components/Ballots.jsx | 8 + src/components/NewBallot.jsx | 179 ++++++++++---- src/constants.js | 4 +- src/contracts/EmissionFunds.contract.js | 15 ++ .../VotingToManageEmissionFunds.contract.js | 84 +++++++ src/index.js | 13 +- src/messages.js | 6 +- src/stores/BallotStore.js | 15 +- src/stores/BallotsStore.js | 12 - src/stores/ContractsStore.js | 65 +++++- 16 files changed, 645 insertions(+), 154 deletions(-) create mode 100644 src/components/BallotEmissionFundsCard.jsx create mode 100644 src/components/BallotEmissionFundsMetadata.jsx create mode 100644 src/contracts/EmissionFunds.contract.js create mode 100644 src/contracts/VotingToManageEmissionFunds.contract.js diff --git a/src/assets/App.css b/src/assets/App.css index 1419fcc..d79dac8 100644 --- a/src/assets/App.css +++ b/src/assets/App.css @@ -1 +1 @@ -.footer{left:0;right:0}.new-form,.settings,.ballots-i{background-color:#fff;border-radius:6px;border:1px solid #e4e4e4;margin-bottom:30px;padding:30px;transition:0.15s box-shadow}@media screen and (max-width: 768px){.new-form,.settings,.ballots-i{margin-bottom:15px;padding:15px}}input,textarea,select{transition:0.3s background-color, 0.3s border-color;background-color:#fff;border-radius:3px;border:1px solid #e4e4e4;box-sizing:border-box;color:#444;font-size:14px;height:40px;outline:none;padding:0 10px;width:100%}input:hover,textarea:hover,select:hover{border-color:#8e959d;color:#202d3c}input:focus,textarea:focus,select:focus{border-color:#5c34a2;color:#202d3c}.sokol input:focus,.sokol textarea:focus,.sokol select:focus{border-color:#6ac9b9}button,.ballots-i--vote,.ballots-footer-finalize{cursor:pointer;display:inline-block;transition:0.3s opacity;box-sizing:border-box;border:0;outline:none;padding:0 15px;text-transform:uppercase;line-height:36px;font-size:13px;font-weight:bold}button:hover,.ballots-i--vote:hover,.ballots-footer-finalize:hover{opacity:0.9;text-decoration:none}html,body,p,h1,h2,h3{margin:0;padding:0;font-weight:normal}html{display:flex;min-height:100%}body{-webkit-font-smoothing:antialiased;background-color:#ffffff;box-sizing:border-box;color:#333;font-family:'Nunito', sans-serif;font-size:14px;padding-bottom:66px;position:relative;width:100%}@media screen and (max-width: 768px){body{padding-bottom:132px}}@media screen and (max-width: 767px){body{padding-bottom:132px}}a{color:#5c34a2;text-decoration:none}a:hover{text-decoration:underline}a .sokol{color:#6ac9b9}hr{display:block;width:100%;height:1px;margin:10px 0 30px;border:0;background-color:#e4e4e4}.center{text-align:center}.left{float:left;width:48%}.right{float:right;width:48%}@media screen and (max-width: 767px){.left,.right{float:none;width:100%}}.hidden{overflow:hidden}.display-none{display:none}.container{width:960px;margin:0 auto;box-sizing:border-box}@media screen and (max-width: 960px){.container{width:100%;padding:0 10px}}.title{margin-top:30px;margin-bottom:30px;color:#38454f;font-size:30px;font-weight:normal}@media screen and (max-width: 768px){.title{font-size:28px}}@media screen and (max-width: 767px){.title{font-size:26px}}button,input,textarea{outline:none}.btn{background-position:calc(100% - 15px) 50%;background-repeat:no-repeat;border-radius:5px;border:0;color:#fff;cursor:pointer;font-weight:bold;font-size:14px;line-height:42px;outline:none;padding:0 15px;text-decoration:none;text-transform:uppercase;transition:0.15s background-color}.btn.btn-primary{background-color:#5c34a2;box-shadow:0px 5px 10px 0 rgba(92,52,162,0.3)}.btn.btn-primary:hover{background-color:#46287b}.btn.btn-success{background-color:#60db97;box-shadow:0px 5px 10px 0 rgba(96,219,151,0.3)}.btn.btn-success:hover{background-color:#36d27c}.btn.btn-danger{background-color:#f21b57;box-shadow:0px 5px 10px 0 rgba(242,27,87,0.3)}.btn.btn-danger:hover{background-color:#ce0c42}.btn.btn-freeze{background-color:#50bbda;box-shadow:0px 5px 10px 0 rgba(80,187,218,0.3)}.btn.btn-freeze:hover{background-color:#2ba8cc}.sokol .btn.btn-primary{background-color:#6ac9b9;box-shadow:0px 5px 10px 0 rgba(106,201,185,0.3)}.sokol .btn.btn-primary:hover{background-color:#45bba7}.sokol .btn.btn-success{background-color:#6ac9b9;box-shadow:0px 5px 10px 0 rgba(96,219,151,0.3)}.sokol .btn.btn-success:hover{background-color:#45bba7}.sokol .btn.btn-danger{background-color:#f24c67;box-shadow:0px 5px 10px 0 rgba(242,76,103,0.33)}.sokol .btn.btn-danger:hover{background-color:#ef1c3f}.sokol .btn.btn-freeze{background-color:#50bbda;box-shadow:0px 5px 10px 0 rgba(80,187,218,0.3)}.sokol .btn.btn-freeze:hover{background-color:#2ba8cc}.btn.btn-new{background-image:url("../assets/images/icons/icon-add.svg");background-size:14px 14px;padding-right:45px}.btn.btn-load-more{background-image:url("../assets/images/icons/icon-load-more.svg");border-radius:5px;border:2px solid #5c34a2;font-size:17px;font-weight:400;background-size:14px 14px;padding-right:45px}.sokol .btn.btn-load-more{background-image:url("../assets/images/icons/icon-load-more-sokol.svg");border-color:#6ac9b9}.btn.btn-transparent{color:#5c34a2;background-color:transparent}.btn.btn-transparent:hover{opacity:0.8}.sokol .btn.btn-transparent{color:#6ac9b9}.btn[disabled],.sokol .btn[disabled],.btn.disabled,.sokol .btn.disabled{color:#333333;cursor:default;background-color:#f0f0f0;box-shadow:none}.btn[disabled]:hover,.sokol .btn[disabled]:hover,.btn.disabled:hover,.sokol .btn.disabled:hover{background-color:#f0f0f0}.btn.btn-finalize{background-image:url("../assets/images/icons/icon-finalize-white.svg");background-size:14px 14px;padding-right:45px}.btn.btn-finalize[disabled],.btn.btn-finalize.disabled{background-image:url("../assets/images/icons/icon-finalize-black.svg")}.btn.no-shadow{box-shadow:none}.btn.text-lowercase{text-transform:lowercase}.btn.text-uppercase{text-transform:uppercase}.btn.text-capitalize{text-transform:capitalize}label{color:#333;display:inline-block;font-size:13px;line-height:1.2;margin-bottom:15px}input:focus,textarea:focus{border-color:#fff}input[type='radio'],textarea[type='radio']{display:none}textarea{width:100%;padding-top:10px;padding-bottom:10px;height:70px;resize:none}select{appearance:none;background-image:url(./images/select.png);background-position:right 13px center;background-repeat:no-repeat;padding-right:30px}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){select{background-image:url("./images/select@2x.png");background-size:8px 4px}}button{background-color:rgba(92,52,162,0.1);color:#5c34a2}button.load-more{margin-bottom:50px}@media screen and (max-width: 960px){button.load-more{margin-bottom:80px}}@media screen and (max-width: 768px){button.load-more{margin-top:80px}}.radio-button{position:relative}.radio-button-label{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none;border-radius:3px;border:1px solid #e4e4e4;color:#333;cursor:pointer;font-size:14px;font-weight:400;line-height:50px;margin:0;outline:none;padding:0 15px;text-decoration:none;text-transform:capitalize;text-align:center;transition:0.25s background-color;position:relative;z-index:1}.radio-button input[type='radio']{height:100%;position:absolute;visibility:hidden;width:100%;z-index:12}input[type='radio']:checked+.radio-button-label{background-color:#5c34a2;border-color:#5c34a2;box-shadow:0px 5px 10px 0 rgba(92,52,162,0.3);color:#fff}.sokol input[type='radio']:checked+.radio-button-label{background-color:#6ac9b9;border-color:#6ac9b9;box-shadow:0px 5px 10px 0 rgba(106,201,185,0.3)}.keys-radio-button-tr{display:flex;margin-bottom:30px}@media screen and (max-width: 768px){.keys-radio-button-tr{flex-direction:column}}.keys-radio-button-td{min-width:30%;margin-right:5%}.keys-radio-button-td:last-child{margin-right:0}.keys-radio-button-td .radio-button-label{display:flex;justify-content:center}@media screen and (max-width: 768px){.keys-radio-button-td{margin-bottom:15px;margin-right:0;min-width:100%}}.form-el{margin-bottom:30px}.hint{color:#777;font-size:13px;font-weight:400;line-height:1.38;padding-top:12px;margin:0;word-break:break-word}.footer{background-color:#5c34a2;bottom:0;padding:18px 0;position:absolute}.footer.sokol{background-color:#6ac9b9}.footer .container{align-items:center;display:flex;flex-wrap:wrap;justify-content:space-between}@media screen and (max-width: 768px){.footer .container{flex-direction:column}}.footer .socials{order:2}@media screen and (max-width: 768px){.footer .socials{margin-top:15px;order:1}}.footer-logo{background-image:url("../assets/images/logos/logo-poa.svg");background-repeat:no-repeat;background-size:contain;display:block;height:18px;left:0;order:0;width:139px}.footer-logo.sokol{background-image:url("../assets/images/logos/logo-sokol.svg");width:144px}.footer-rights{color:#fff;font-size:12px;order:1;text-align:center}@media screen and (max-width: 768px){.footer-rights{line-height:normal;margin-top:15px;order:2;width:100%}}.header{background-color:#5c34a2;position:relative;width:100%;z-index:9999}@media screen and (max-width: 767px){.header{left:0;right:0;position:fixed;top:0;z-index:124}}.header .container{align-items:center;display:flex;flex-wrap:wrap;justify-content:space-between}@media screen and (max-width: 767px){.header .container{width:93%;padding:22px 0}}.header.sokol{background-color:#6ac9b9}.header-logo{display:block;height:26px}.header .links-container{align-items:center;display:flex;justify-content:space-between;height:70px;margin-left:auto}@media screen and (max-width: 767px){.header .links-container{display:none}}@media screen and (max-width: 768px){.header .links-container{justify-content:flex-end;width:auto}}.header .links-container-mobile{display:none}@media screen and (max-width: 767px){.header .links-container-mobile{align-items:center;display:flex;flex-direction:column;height:100%;justify-content:space-evenly}}.header .link{color:#fff;font-size:14px;font-weight:700;line-height:18px;margin-left:34px;opacity:0.8;text-decoration:none;transition:opacity linear 0.1s}.header .link:hover,.header .link.active{opacity:1}@media screen and (max-width: 767px){.header .link{margin-left:0}}.header .link-icon{height:18px;width:18px;margin-right:10px;float:left;background-position:50% 50%;background-repeat:no-repeat}.header .link-icon-all{background-image:url("../assets/images/icons/icon-all.svg")}.header .link-icon-active{background-image:url("../assets/images/icons/icon-active.svg")}.header .link-icon-to-finalize{background-image:url("../assets/images/icons/icon-to-finalize.svg")}.header.sokol .link-icon-all{background-image:url("../assets/images/icons/icon-all-sokol.svg")}.header.sokol .link-icon-active{background-image:url("../assets/images/icons/icon-active-sokol.svg")}.header.sokol .link-icon-to-finalize{background-image:url("../assets/images/icons/icon-to-finalize-sokol.svg")}.header .mobile-menu{display:none}@media screen and (max-width: 767px){.header .mobile-menu{display:block;order:3}}.header .mobile-menu .mobile-menu-icon{width:18px;height:14px}.header .mobile-menu .mobile-menu-open-icon{width:18px;height:18px}@media screen and (max-width: 767px){.header .header-mobile-menu-container{background-color:#45277a;height:220px}}@media screen and (max-width: 767px){.header.sokol .header-mobile-menu-container{background-color:#41b19e}}.header .Select{margin-left:25px}@media screen and (max-width: 767px){.header .Select{margin-left:0;max-width:125px;order:2}.header .Select .Select-control{max-width:125px}}.header .btn.btn-new-ballot{height:36px;line-height:36px;opacity:1}.header .btn.btn-new-ballot .link-icon{display:none}.header.sokol .btn.btn-new-ballot{background-color:#fff;background-image:url("../assets/images/icons/icon-add-sokol.svg");color:#6ac9b9}.header.sokol .btn.btn-new-ballot:hover{opacity:0.9}.select-network-id{background:#fff;width:40px}.info-container{background-color:#f8f8f8;margin-bottom:30px;margin-left:-30px;margin-right:-30px;padding-bottom:30px;padding-left:30px;padding-right:30px;padding-top:30px}@media screen and (max-width: 768px){.info-container{margin-left:-15px;margin-right:-15px;padding-left:15px;padding-right:15px}}.info{color:#000}.ballots .info{background-position:0 2px;background-repeat:no-repeat;color:#333333;font-size:14px;font-weight:normal;letter-spacing:normal;line-height:1.71;margin-bottom:20px;max-width:100%;min-height:32px;padding-left:30px;text-align:left;word-break:break-word}.ballots .info:last-child{margin-bottom:0}.ballots .info-minimum{background-image:url("../assets/images/icons/icon-min.svg");background-size:18px 18px}.ballots .info-details{background-image:url("../assets/images/icons/icon-info.svg");background-size:10px 18px;position:relative}.ballots .info-details.collapsed{height:90px;overflow:hidden}.ballots .info-details .toggle-show{color:#5c34a2;cursor:pointer;display:inline-block;margin-left:5px}.sokol .ballots .info-details .toggle-show{color:#6ac9b9}.new-form .info{padding:30px}.info-title{color:#333;font-size:16px;font-weight:400;margin-bottom:30px;padding-left:25px;position:relative}.info-title:before{background-image:url("../assets/images/icons/icon-info.svg");background-position:50% 50%;background-repeat:no-repeat;background-size:10px 18px;content:'';height:32px;left:0;position:absolute;top:50%;transform:translateY(-50%);width:10px}.info-i{color:#777;font-size:14px;line-height:1.71;margin-bottom:20px;padding-left:25px;position:relative}.info-i:before{background-color:#333;border-radius:50%;content:'';height:6px;left:0;position:absolute;top:8px;width:6px}.app-container{padding-bottom:30px;padding-top:40px;position:relative}.app-container.app-container-open-mobile-menu:before{background-color:rgba(78,44,137,0.8);bottom:0;content:'';display:none;left:0;position:fixed;right:0;top:290px;z-index:1234}@media screen and (max-width: 767px){.app-container.app-container-open-mobile-menu:before{display:block}}.app-container.app-container-open-mobile-menu.sokol:before{background-color:rgba(106,201,185,0.8)}@media screen and (max-width: 767px){#root,.app-container{height:100%}.app-container{padding-left:10px;padding-right:10px}}@media screen and (max-width: 767px){.content{padding-top:120px}}.content.content-menu-open{padding-top:290px}.clearfix::after{content:"";clear:both;display:table}@keyframes fadeOut{0%{opacity:.2}20%{opacity:1;transform:scale(1)}100%{opacity:.2;transform:scale(0.3)}}.loading{display:flex;justify-content:space-between;left:50%;margin:-30px 0 0 -111.5px;padding-top:50px;position:absolute;top:50%;width:206px}.loading:before{background-image:url("../assets/images/logos/logo_loader.svg");background-position:0 0;background-repeat:no-repeat;content:'';left:0;position:absolute;top:0;height:100px;width:206px}.loading-container{bottom:0;left:0;position:fixed;right:0;top:0;z-index:1000000}.loading-i{animation-duration:2s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:fadeOut;animation-timing-function:linear;background-color:#fff;border-radius:50%;height:9px;opacity:.2;width:9px}.loading-i:nth-child(2){animation-delay:.1s}.loading-i:nth-child(3){animation-delay:.2s}.loading-i:nth-child(4){animation-delay:.3s}.loading-i:nth-child(5){animation-delay:.4s}.loading-i:nth-child(6){animation-delay:.5s}.main-title-container{-ms-flex-pack:justify;display:-ms-flexbox;display:flex;justify-content:space-between;margin-bottom:40px}.main-title-container .main-title{font-size:24px;color:#333;line-height:38px}@media screen and (max-width: 767px){.main-title-container{-ms-flex-direction:column;flex-direction:column}.main-title-container .search-input{width:100%}}.new-form{display:flex;overflow:hidden;padding:0}@media screen and (max-width: 768px){.new-form{display:block}}.new-form-side{box-sizing:border-box;padding-bottom:30px;padding-top:30px}@media screen and (max-width: 768px){.new-form-side{width:100% !important}}.new-form-side_left{background-color:#f8f8f8;padding-top:0;width:30%}@media screen and (max-width: 768px){.new-form-side_left{margin-bottom:30px;padding-bottom:0}}.new-form-side_right{border-left:1px solid #e4e4e4;padding-left:30px;padding-right:30px;width:70%}@media screen and (max-width: 768px){.new-form-side_right{border:0;padding-top:0}}.new .add-ballot{white-space:nowrap}@media screen and (max-width: 767px){.new .add-ballot{margin-top:20px}}.search-container{background-color:#502d8d;box-sizing:border-box;height:50px;padding:10px 0 0 0}.search-container.sokol{background-color:#41b19e}@media screen and (max-width: 767px){.search-container{left:0;position:fixed;right:0;top:70px;z-index:123}}input.search-input{background-color:transparent;background-image:url("../assets/images/icons/icon-search.svg");background-position:0 50%;background-repeat:no-repeat;background-size:16px 16px;border-radius:0;border:none;box-sizing:border-box;display:block;font-size:14px;font-weight:400;height:30px;outline:none;padding-left:30px;width:100%}input.search-input,input.search-input:focus input.search-input:hover,input.search-input:active{color:#fff}.sokol input.search-input{background-image:url("../assets/images/icons/icon-search-sokol.svg")}input.search-input::-webkit-input-placeholder{color:rgba(255,255,255,0.5) !important;font-size:14px !important}input.search-input:-moz-placeholder{color:rgba(255,255,255,0.5) !important;font-size:14px !important;opacity:1}input.search-input::-moz-placeholder{color:rgba(255,255,255,0.5) !important;font-size:14px !important;opacity:1}input.search-input:-ms-input-placeholder{color:rgba(255,255,255,0.5) !important;font-size:14px !important}.settings{max-width:600px;margin:0 auto}.settings-title{margin-bottom:20px;color:#38454f;text-align:center;font-size:24px}.socials-item:not(:first-child){margin-left:10px}.socials-i{background-color:#ffffff;border-radius:3px;display:inline-block;font-size:0;height:30px;position:relative;transition:0.3s background-color;vertical-align:top;width:30px}.socials-i:not(:first-child){margin-left:10px}.socials-i:hover{background-color:rgba(255,255,255,0.4)}.socials-i:before{background-position:50% 50%;background-repeat:no-repeat;background-size:contain;content:'';height:16px;left:50%;position:absolute;top:50%;transform:translate(-50%, -50%);width:16px}.socials-i_github:before{background-image:url("../assets/images/socials/git.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.socials-i_github:before{background-image:url("../assets/images/socials/git@2x.png");background-size:100% 100%}}.socials-i_telegram:before{background-image:url("../assets/images/socials/telegram.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.socials-i_telegram:before{background-image:url("../assets/images/socials/telegram@2x.png");background-size:100% 100%}}.socials-i_twitter:before{background-image:url("../assets/images/socials/tw.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.socials-i_twitter:before{background-image:url("../assets/images/socials/tw@2x.png");background-size:100% 100%}}.socials-i_poa:before{background-image:url("../assets/images/socials/poa.png");height:20px;width:20px}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.socials-i_poa:before{background-image:url("../assets/images/socials/poa@2x.png");background-size:100% 100%}}.sokol .socials .socials-i_github:before{background-image:url("../assets/images/socials/git-sokol.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.sokol .socials .socials-i_github:before{background-image:url("../assets/images/socials/git-sokol@2x.png");background-size:100% 100%}}.sokol .socials .socials-i_telegram:before{background-image:url("../assets/images/socials/telegram-sokol.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.sokol .socials .socials-i_telegram:before{background-image:url("../assets/images/socials/telegram-sokol@2x.png");background-size:100% 100%}}.sokol .socials .socials-i_twitter:before{background-image:url("../assets/images/socials/tw-sokol.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.sokol .socials .socials-i_twitter:before{background-image:url("../assets/images/socials/tw-sokol@2x.png");background-size:100% 100%}}.sokol .socials .socials-i_poa:before{background-image:url("../assets/images/socials/poa-sokol.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.sokol .socials .socials-i_poa:before{background-image:url("../assets/images/socials/poa-sokol@2x.png");background-size:100% 100%}}.vote-scale{background-color:#f0f0f0;border-radius:5px;clear:left;height:10px;margin-top:34px;overflow:hidden}.vote-scale-not-finalized{background-color:#ffffff}.vote-scale--fill{height:100%;border-radius:3px}.vote-scale--fill_no{background-color:#f21b57}.sokol .vote-scale--fill_no{background-color:#f24c67}.vote-scale--fill_yes{background-color:#60db97}.sokol .vote-scale--fill_yes{background-color:#6ac9b9}.vote-scale--fill_send{background-color:#60db97}.sokol .vote-scale--fill_send{background-color:#6ac9b9}.vote-scale--fill_burn{background-color:#f21b57}.sokol .vote-scale--fill_burn{background-color:#f24c67}.vote-scale--fill_freeze{background-color:#50bbda}.sokol .vote-scale--fill_freeze{background-color:#50bbda}.vote-scale--container{width:100%}.vote-scale--value{float:left;font-size:12px}.vote-scale--votes,.vote-scale--percentage{float:right;font-size:13px}.vote-scale--votes{color:#333;margin-left:10px}.vote-scale--percentage{color:#000;font-weight:700}@keyframes Select-animation-spin{to{transform:rotate(1turn)}}@-webkit-keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn)}}.Select{position:relative}.Select input::-webkit-contacts-auto-fill-button,.Select input::-webkit-credentials-auto-fill-button{display:none !important}.Select input::-ms-clear{display:none !important}.Select input::-ms-reveal{display:none !important}.Select,.Select div,.Select input,.Select span{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.Select.is-disabled .Select-arrow-zone{cursor:default;pointer-events:none}.Select.is-disabled>.Select-control{background-color:#f9f9f9}.Select.is-disabled>.Select-control:hover{box-shadow:none}.Select.is-searchable.is-open>.Select-control{cursor:text}.Select.is-searchable.is-focused:not(.is-open)>.Select-control{cursor:text}.Select.is-open>.Select-control{border-bottom-right-radius:0;border-bottom-left-radius:0;background:#fff;border-color:#cbcbcb #e4e4e4 #f1f1f1}.Select.is-open>.Select-control .Select-arrow{top:-2px;border-color:transparent transparent #999;border-width:0 5px 5px}.Select.is-focused>.Select-control{background:#fff}.Select.is-focused:not(.is-open)>.Select-control{border-color:#5c34a2 #673ab5 #673ab5;box-shadow:none}.Select.has-value.is-clearable.Select--single>.Select-control .Select-value{padding-right:44px}.Select.has-value.Select--single>.Select-control .Select-value .Select-value-label,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value .Select-value-label{color:#333}.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label{cursor:pointer;text-decoration:none}.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:hover,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:hover,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus{color:#8e959d;outline:none;text-decoration:underline}.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus{background:#fff}.Select.has-value.is-pseudo-focused .Select-input{opacity:0}.Select.is-open .Select-arrow,.Select .Select-arrow-zone:hover>.Select-arrow{border-top-color:#666}.Select.Select--rtl{direction:rtl;text-align:right}.Select-control{background-color:#fff;border-collapse:separate;border-color:#f1f1f1 #e4e4e4 #cbcbcb;border-radius:3px;border-spacing:0;border:1px solid #e4e4e4;color:#333;cursor:default;display:table;height:40px;outline:none;overflow:hidden;position:relative;width:100%}.Select-control:hover{box-shadow:none}.Select-control .Select-input:focus{outline:none;background:#fff}.Select-placeholder,.Select--single>.Select-control .Select-value{bottom:0;color:#333;left:0;line-height:38px;padding-left:15px;padding-right:15px;position:absolute;right:0;top:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.Select-input{height:38px;padding-left:15px;padding-right:15px;vertical-align:middle}.Select-input>input{background:none transparent;border:0 none;box-shadow:none;cursor:default;display:inline-block;font-family:inherit;font-size:inherit;margin:0;outline:none;line-height:17px;padding:10px 0 14px;-webkit-appearance:none}.is-focused .Select-input>input{cursor:text}.Select-control:not(.is-searchable)>.Select-input{outline:none}.Select-loading-zone{cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:16px}.Select-loading{-webkit-animation:Select-animation-spin 400ms infinite linear;-o-animation:Select-animation-spin 400ms infinite linear;animation:Select-animation-spin 400ms infinite linear;width:16px;height:16px;box-sizing:border-box;border-radius:50%;border:2px solid #e4e4e4;border-right-color:#333;display:inline-block;position:relative;vertical-align:middle}.Select-clear-zone{-webkit-animation:Select-animation-fadeIn 200ms;-o-animation:Select-animation-fadeIn 200ms;animation:Select-animation-fadeIn 200ms;color:#999;cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:19px}.Select-clear-zone:hover{color:#D0021B}.Select-clear{display:inline-block;font-size:20px;line-height:1}.Select--multi .Select-clear-zone{width:19px}.Select--multi .Select-multi-value-wrapper{display:inline-block}.Select .Select-aria-only{position:absolute;display:inline-block;height:1px;width:1px;margin:-1px;clip:rect(0, 0, 0, 0);overflow:hidden;float:left}.Select-arrow-zone{cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:25px;padding-right:5px}.Select--rtl .Select-arrow-zone{padding-right:0;padding-left:5px}.Select-arrow{border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 2.5px;display:inline-block;height:0;width:0;position:relative}@-webkit-keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}@keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}.Select-menu-outer{border-bottom-right-radius:3px;border-bottom-left-radius:3px;background-color:#fff;border:1px solid #e4e4e4;border-top-color:#f2f2f2;box-shadow:none;box-sizing:border-box;margin-top:-1px;max-height:200px;position:absolute;top:100%;width:100%;z-index:1000;-webkit-overflow-scrolling:touch}.Select-menu{max-height:198px;overflow-y:auto}.Select-option{box-sizing:border-box;background-color:#fff;color:#666;cursor:pointer;display:block;padding:8px 15px}.Select-option:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.Select-option.is-selected{background-color:#f5faff;color:#333}.Select-option.is-focused{background-color:#f2f9fc;color:#333}.Select-option.is-disabled{color:#ccc;cursor:default}.Select-noresults{box-sizing:border-box;color:#999;cursor:default;display:block;padding:8px 15px}.Select--multi .Select-input{vertical-align:middle;margin-left:15px;padding:0}.Select--multi.Select--rtl .Select-input{margin-left:0;margin-right:15px}.Select--multi.has-value .Select-input{margin-left:5px}.Select--multi .Select-value{background-color:#f2f9fc;border-radius:3px;border:1px solid #e5eef9;color:#08c;display:inline-block;font-size:.9em;margin-left:5px;margin-top:5px;vertical-align:top}.Select--multi .Select-value-icon,.Select--multi .Select-value-label{display:inline-block;vertical-align:middle}.Select--multi .Select-value-label{border-bottom-right-radius:3px;border-top-right-radius:3px;cursor:default;padding:2px 5px}.Select--multi a.Select-value-label{color:#08c;cursor:pointer;text-decoration:none}.Select--multi a.Select-value-label:hover{text-decoration:underline}.Select--multi .Select-value-icon{cursor:pointer;border-bottom-left-radius:3px;border-top-left-radius:3px;border-right:1px solid #e5eef9;padding:1px 5px 3px}.Select--multi .Select-value-icon:hover,.Select--multi .Select-value-icon:focus{background-color:#ddeff7;color:#0077b3}.Select--multi .Select-value-icon:active{background-color:#e5eef9}.Select--multi.Select--rtl .Select-value{margin-left:0;margin-right:5px}.Select--multi.Select--rtl .Select-value-icon{border-right:none;border-left:1px solid #e5eef9}.Select--multi.is-disabled .Select-value{background-color:#fcfcfc;border:1px solid #e3e3e3;color:#333}.Select--multi.is-disabled .Select-value-icon{cursor:not-allowed;border-right:1px solid #e3e3e3}.Select--multi.is-disabled .Select-value-icon:hover,.Select--multi.is-disabled .Select-value-icon:focus,.Select--multi.is-disabled .Select-value-icon:active{background-color:#fcfcfc}@keyframes Select-animation-spin{to{transform:rotate(1turn)}}@-webkit-keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn)}}.ballot-types{background-color:#fff;border-bottom:1px solid #e4e4e4;padding:20px 30px 10px}.ballot-types-i{color:#333;cursor:pointer;font-size:16px;font-weight:400;margin-bottom:10px;position:relative;line-height:30px}.ballot-types-i:before{background-color:#5c34a2;border-bottom-right-radius:5px;border-top-right-radius:5px;content:'';height:30px;left:-30px;opacity:0;position:absolute;top:50%;transform:translateY(-50%);width:4px}.sokol .ballot-types-i:before{background-color:#6ac9b9}.ballot-types-i_active{color:#5c34a2}.ballot-types-i_active:before{opacity:1}.sokol .ballot-types-i_active{color:#6ac9b9}.ballots-i--vote,.ballots-footer-finalize{border-radius:2px;padding:0 13px;font-size:13px;font-weight:bold}.ballots .title{margin-bottom:50px}@media screen and (max-width: 768px){.ballots .title{margin-bottom:15px}}@media screen and (max-width: 767px){.ballots .title{margin-bottom:10px}}.ballots-about{margin-top:-8px}.ballots-i{background-color:#fff}.ballots-i-scale{border-top:1px solid #e4e4e4;display:flex;margin-bottom:30px;margin-top:30px;padding:30px 0 0 0}@media screen and (max-width: 768px){.ballots-i-scale{display:block;margin-top:15px;padding:15px 0 0 0}}@media screen and (max-width: 767px){.ballots-i-scale{margin-top:10px;padding:10px 0 0 0}}.ballots-i-scale-column{display:flex;flex-grow:1;justify-content:space-between;margin-right:40px}@media screen and (max-width: 768px){.ballots-i-scale-column{margin-bottom:30px;margin-right:0;overflow:hidden;width:100%}}.ballots-i-scale-column:last-child{margin-right:0}@media screen and (max-width: 768px){.ballots-i-scale-column.reverse-responsive{flex-direction:row-reverse}}.ballots-i--name{overflow:hidden;text-overflow:ellipsis}.ballots-i--see-all-proposal{display:inline-block;margin-top:5px}.ballots-i--vote-label{color:#333;cursor:default;font-size:14px;font-weight:700;line-height:1.2}@media screen and (max-width: 767px){.ballots-i--vote-label{margin-bottom:25px}}.ballots-i--vote-label-right{margin-left:auto !important}.ballots-i--vote_btn{line-height:44px;min-width:55px;text-transform:capitalize}.ballots-i--vote_btn.xl{min-width:78px}.ballots-i--vote_btn.m-r-20{margin-right:20px}.ballots-i--vote_btn.m-l-20{margin-left:20px}@media screen and (max-width: 768px){.ballots-i--vote_btn.m-r-20,.ballots-i--vote_btn.m-l-20{margin-left:0;margin-right:20px}}textarea{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;overflow:auto;outline:none;border-radius:3px;width:100%}.color-primary{color:#5c34a2}.sokol .color-primary{color:#6ac9b9}.color-danger{color:#f21b57}.sokol .color-danger{color:#f24c67}.color-success{color:#60db97}.sokol .color-success{color:#6ac9b9}@media screen and (max-width: 768px){.ballots-about{display:table;width:100%}}.ballots-about p{line-height:28px}.ballots-about-i{position:relative;display:inline-block;vertical-align:top;box-sizing:border-box}@media screen and (max-width: 768px){.ballots-about-i{display:table-row;width:100% !important}}.ballots-about-i .ballots-about-td-value{padding-right:12px}.ballots-about-i_name{width:12%}.ballots-about-i_action{width:8%}.ballots-about-i_type{width:8%}.ballots-about-i_proposal{width:30%}.ballots-about-i_key{width:26%;word-break:break-all}.ballots-about-i_key_wide{width:50%;word-break:break-all}.ballots-about-i_proposed_receiver{width:45%;word-break:break-all}.ballots-about-i_funds_amount{width:23%;word-break:break-all}.ballots-about-i_proposed-min-threshold{width:30%;word-break:break-all}.ballots-about-i_contract-type{width:25%;word-break:break-all}.ballots-about-i_proposed-address{width:30%;word-break:break-all}.ballots-about-i_time{width:20%}.ballots-about-i--title{color:#777;font-size:13px;font-weight:400;text-align:left}.ballots-about-i:first-child .ballots-about-td-value{border-left:none;padding-left:0}.ballots-about-i:last-child .ballots-about-td-value{border-right:none;padding-right:0}.ballots-about-td.ballots-about-td-value{color:#333;font-size:14px}.ballots-about-i_action .ballots-about-td.ballots-about-td-value,.ballots-about-i_type .ballots-about-td.ballots-about-td-value{text-transform:capitalize}@media screen and (max-width: 768px){.ballots-about-td.ballots-about-td-value{padding-left:0;padding-right:0}}@media screen and (max-width: 768px){.ballots-about-td{display:table-cell;vertical-align:top}}@media screen and (max-width: 768px){.ballots-about-td:first-child{padding-right:10px}}@media screen and (max-width: 768px){.ballots-about-i:not(:last-child) .ballots-about-td{padding-bottom:10px}}.ballots-footer{display:flex;align-items:center;justify-content:space-between}.ballots-footer-left{display:inline-flex;align-items:center}@media screen and (max-width: 767px){.ballots-footer-left{display:block}.ballots-footer-left .btn{display:block;margin:0 0 15px;width:100%}}@media screen and (max-width: 768px){.ballots-footer{padding-top:15px}}@media screen and (max-width: 767px){.ballots-footer{flex-direction:column-reverse;padding-top:0}}.ballots-footer p{color:#777;font-size:14px;font-stretch:normal;font-style:normal;font-weight:normal;line-height:18px;max-width:340px;padding-left:15px}.ballots-footer-finalize{margin-right:20px;background-color:rgba(8,179,242,0.1);white-space:nowrap}.ballots-footer-finalize-finalized{background-color:rgba(109,46,174,0.1);color:#6d2eae;cursor:default}@media screen and (max-width: 767px){.ballots-footer-finalize{width:100%;margin-right:0;margin-top:10px;text-align:center}} +.footer{left:0;right:0}.new-form,.settings,.ballots-i{background-color:#fff;border-radius:6px;border:1px solid #e4e4e4;margin-bottom:30px;padding:30px;transition:0.15s box-shadow}@media screen and (max-width: 768px){.new-form,.settings,.ballots-i{margin-bottom:15px;padding:15px}}input,textarea,select{transition:0.3s background-color, 0.3s border-color;background-color:#fff;border-radius:3px;border:1px solid #e4e4e4;box-sizing:border-box;color:#444;font-size:14px;height:40px;outline:none;padding:0 10px;width:100%}input:hover,textarea:hover,select:hover{border-color:#8e959d;color:#202d3c}input:focus,textarea:focus,select:focus{border-color:#5c34a2;color:#202d3c}.sokol input:focus,.sokol textarea:focus,.sokol select:focus{border-color:#6ac9b9}button,.ballots-i--vote,.ballots-footer-finalize{cursor:pointer;display:inline-block;transition:0.3s opacity;box-sizing:border-box;border:0;outline:none;padding:0 15px;text-transform:uppercase;line-height:36px;font-size:13px;font-weight:bold}button:hover,.ballots-i--vote:hover,.ballots-footer-finalize:hover{opacity:0.9;text-decoration:none}html,body,p,h1,h2,h3{margin:0;padding:0;font-weight:normal}html{display:flex;min-height:100%}body{-webkit-font-smoothing:antialiased;background-color:#ffffff;box-sizing:border-box;color:#333;font-family:'Nunito', sans-serif;font-size:14px;padding-bottom:66px;position:relative;width:100%}@media screen and (max-width: 768px){body{padding-bottom:132px}}@media screen and (max-width: 767px){body{padding-bottom:132px}}a{color:#5c34a2;text-decoration:none}a:hover{text-decoration:underline}a .sokol{color:#6ac9b9}hr{display:block;width:100%;height:1px;margin:10px 0 30px;border:0;background-color:#e4e4e4}.center{text-align:center}.left{float:left;width:48%}.right{float:right;width:48%}@media screen and (max-width: 767px){.left,.right{float:none;width:100%}}.hidden{overflow:hidden}.display-none{display:none}.container{width:960px;margin:0 auto;box-sizing:border-box}@media screen and (max-width: 960px){.container{width:100%;padding:0 10px}}.title{margin-top:30px;margin-bottom:30px;color:#38454f;font-size:30px;font-weight:normal}@media screen and (max-width: 768px){.title{font-size:28px}}@media screen and (max-width: 767px){.title{font-size:26px}}button,input,textarea{outline:none}.btn{background-position:calc(100% - 15px) 50%;background-repeat:no-repeat;border-radius:5px;border:0;color:#fff;cursor:pointer;font-weight:bold;font-size:14px;line-height:42px;outline:none;padding:0 15px;text-decoration:none;text-transform:uppercase;transition:0.15s background-color}.btn.btn-primary{background-color:#5c34a2;box-shadow:0px 5px 10px 0 rgba(92,52,162,0.3)}.btn.btn-primary:hover{background-color:#46287b}.btn.btn-success{background-color:#60db97;box-shadow:0px 5px 10px 0 rgba(96,219,151,0.3)}.btn.btn-success:hover{background-color:#36d27c}.btn.btn-danger{background-color:#f21b57;box-shadow:0px 5px 10px 0 rgba(242,27,87,0.3)}.btn.btn-danger:hover{background-color:#ce0c42}.btn.btn-freeze{background-color:#50bbda;box-shadow:0px 5px 10px 0 rgba(80,187,218,0.3)}.btn.btn-freeze:hover{background-color:#2ba8cc}.sokol .btn.btn-primary{background-color:#6ac9b9;box-shadow:0px 5px 10px 0 rgba(106,201,185,0.3)}.sokol .btn.btn-primary:hover{background-color:#45bba7}.sokol .btn.btn-success{background-color:#6ac9b9;box-shadow:0px 5px 10px 0 rgba(96,219,151,0.3)}.sokol .btn.btn-success:hover{background-color:#45bba7}.sokol .btn.btn-danger{background-color:#f24c67;box-shadow:0px 5px 10px 0 rgba(242,76,103,0.33)}.sokol .btn.btn-danger:hover{background-color:#ef1c3f}.sokol .btn.btn-freeze{background-color:#50bbda;box-shadow:0px 5px 10px 0 rgba(80,187,218,0.3)}.sokol .btn.btn-freeze:hover{background-color:#2ba8cc}.btn.btn-new{background-image:url("../assets/images/icons/icon-add.svg");background-size:14px 14px;padding-right:45px}.btn.btn-load-more{background-image:url("../assets/images/icons/icon-load-more.svg");border-radius:5px;border:2px solid #5c34a2;font-size:17px;font-weight:400;background-size:14px 14px;padding-right:45px}.sokol .btn.btn-load-more{background-image:url("../assets/images/icons/icon-load-more-sokol.svg");border-color:#6ac9b9}.btn.btn-transparent{color:#5c34a2;background-color:transparent}.btn.btn-transparent:hover{opacity:0.8}.sokol .btn.btn-transparent{color:#6ac9b9}.btn[disabled],.sokol .btn[disabled],.btn.disabled,.sokol .btn.disabled{color:#333333;cursor:default;background-color:#f0f0f0;box-shadow:none}.btn[disabled]:hover,.sokol .btn[disabled]:hover,.btn.disabled:hover,.sokol .btn.disabled:hover{background-color:#f0f0f0}.btn.btn-finalize{background-image:url("../assets/images/icons/icon-finalize-white.svg");background-size:14px 14px;padding-right:45px}.btn.btn-finalize[disabled],.btn.btn-finalize.disabled{background-image:url("../assets/images/icons/icon-finalize-black.svg")}.btn.no-shadow{box-shadow:none}.btn.text-lowercase{text-transform:lowercase}.btn.text-uppercase{text-transform:uppercase}.btn.text-capitalize{text-transform:capitalize}label{color:#333;display:inline-block;font-size:13px;line-height:1.2;margin-bottom:15px}input:focus,textarea:focus{border-color:#fff}input[type='radio'],textarea[type='radio']{display:none}textarea{width:100%;padding-top:10px;padding-bottom:10px;height:70px;resize:none}select{appearance:none;background-image:url(./images/select.png);background-position:right 13px center;background-repeat:no-repeat;padding-right:30px}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){select{background-image:url("./images/select@2x.png");background-size:8px 4px}}button{background-color:rgba(92,52,162,0.1);color:#5c34a2}button.load-more{margin-bottom:50px}@media screen and (max-width: 960px){button.load-more{margin-bottom:80px}}@media screen and (max-width: 768px){button.load-more{margin-top:80px}}.radio-button{position:relative}.radio-button-label{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none;border-radius:3px;border:1px solid #e4e4e4;color:#333;cursor:pointer;font-size:14px;font-weight:400;line-height:50px;margin:0;outline:none;padding:0 15px;text-decoration:none;text-transform:capitalize;text-align:center;transition:0.25s background-color;position:relative;z-index:1}.radio-button input[type='radio']{height:100%;position:absolute;visibility:hidden;width:100%;z-index:12}input[type='radio']:checked+.radio-button-label{background-color:#5c34a2;border-color:#5c34a2;box-shadow:0px 5px 10px 0 rgba(92,52,162,0.3);color:#fff}.sokol input[type='radio']:checked+.radio-button-label{background-color:#6ac9b9;border-color:#6ac9b9;box-shadow:0px 5px 10px 0 rgba(106,201,185,0.3)}.keys-radio-button-tr{display:flex;margin-bottom:30px}@media screen and (max-width: 768px){.keys-radio-button-tr{flex-direction:column}}.keys-radio-button-td{min-width:30%;margin-right:5%}.keys-radio-button-td:last-child{margin-right:0}.keys-radio-button-td .radio-button-label{display:flex;justify-content:center}@media screen and (max-width: 768px){.keys-radio-button-td{margin-bottom:15px;margin-right:0;min-width:100%}}.form-el{margin-bottom:30px}.hint{color:#777;font-size:13px;font-weight:400;line-height:1.38;padding-top:12px;margin:0;word-break:break-word}.hint a{color:#5c34a2}.sokol .hint a{color:#6ac9b9}.footer{background-color:#5c34a2;bottom:0;padding:18px 0;position:absolute}.footer.sokol{background-color:#6ac9b9}.footer .container{align-items:center;display:flex;flex-wrap:wrap;justify-content:space-between}@media screen and (max-width: 768px){.footer .container{flex-direction:column}}.footer .socials{order:2}@media screen and (max-width: 768px){.footer .socials{margin-top:15px;order:1}}.footer-logo{background-image:url("../assets/images/logos/logo-poa.svg");background-repeat:no-repeat;background-size:contain;display:block;height:18px;left:0;order:0;width:139px}.footer-logo.sokol{background-image:url("../assets/images/logos/logo-sokol.svg");width:144px}.footer-rights{color:#fff;font-size:12px;order:1;text-align:center}@media screen and (max-width: 768px){.footer-rights{line-height:normal;margin-top:15px;order:2;width:100%}}.header{background-color:#5c34a2;position:relative;width:100%;z-index:9999}@media screen and (max-width: 767px){.header{left:0;right:0;position:fixed;top:0;z-index:124}}.header .container{align-items:center;display:flex;flex-wrap:wrap;justify-content:space-between}@media screen and (max-width: 767px){.header .container{width:93%;padding:22px 0}}.header.sokol{background-color:#6ac9b9}.header-logo{display:block;height:26px}.header .links-container{align-items:center;display:flex;justify-content:space-between;height:70px;margin-left:auto}@media screen and (max-width: 767px){.header .links-container{display:none}}@media screen and (max-width: 768px){.header .links-container{justify-content:flex-end;width:auto}}.header .links-container-mobile{display:none}@media screen and (max-width: 767px){.header .links-container-mobile{align-items:center;display:flex;flex-direction:column;height:100%;justify-content:space-evenly}}.header .link{color:#fff;font-size:14px;font-weight:700;line-height:18px;margin-left:34px;opacity:0.8;text-decoration:none;transition:opacity linear 0.1s}.header .link:hover,.header .link.active{opacity:1}@media screen and (max-width: 767px){.header .link{margin-left:0}}.header .link-icon{height:18px;width:18px;margin-right:10px;float:left;background-position:50% 50%;background-repeat:no-repeat}.header .link-icon-all{background-image:url("../assets/images/icons/icon-all.svg")}.header .link-icon-active{background-image:url("../assets/images/icons/icon-active.svg")}.header .link-icon-to-finalize{background-image:url("../assets/images/icons/icon-to-finalize.svg")}.header.sokol .link-icon-all{background-image:url("../assets/images/icons/icon-all-sokol.svg")}.header.sokol .link-icon-active{background-image:url("../assets/images/icons/icon-active-sokol.svg")}.header.sokol .link-icon-to-finalize{background-image:url("../assets/images/icons/icon-to-finalize-sokol.svg")}.header .mobile-menu{display:none}@media screen and (max-width: 767px){.header .mobile-menu{display:block;order:3}}.header .mobile-menu .mobile-menu-icon{width:18px;height:14px}.header .mobile-menu .mobile-menu-open-icon{width:18px;height:18px}@media screen and (max-width: 767px){.header .header-mobile-menu-container{background-color:#45277a;height:220px}}@media screen and (max-width: 767px){.header.sokol .header-mobile-menu-container{background-color:#41b19e}}.header .Select{margin-left:25px}@media screen and (max-width: 767px){.header .Select{margin-left:0;max-width:125px;order:2}.header .Select .Select-control{max-width:125px}}.header .btn.btn-new-ballot{height:36px;line-height:36px;opacity:1}.header .btn.btn-new-ballot .link-icon{display:none}.header.sokol .btn.btn-new-ballot{background-color:#fff;background-image:url("../assets/images/icons/icon-add-sokol.svg");color:#6ac9b9}.header.sokol .btn.btn-new-ballot:hover{opacity:0.9}.select-network-id{background:#fff;width:40px}.info-container{background-color:#f8f8f8;margin-bottom:30px;margin-left:-30px;margin-right:-30px;padding-bottom:30px;padding-left:30px;padding-right:30px;padding-top:30px}@media screen and (max-width: 768px){.info-container{margin-left:-15px;margin-right:-15px;padding-left:15px;padding-right:15px}}.info{color:#000}.ballots .info{background-position:0 2px;background-repeat:no-repeat;color:#333333;font-size:14px;font-weight:normal;letter-spacing:normal;line-height:1.71;margin-bottom:20px;max-width:100%;min-height:32px;padding-left:30px;text-align:left;word-break:break-word}.ballots .info:last-child{margin-bottom:0}.ballots .info-minimum{background-image:url("../assets/images/icons/icon-min.svg");background-size:18px 18px}.ballots .info-details{background-image:url("../assets/images/icons/icon-info.svg");background-size:10px 18px;position:relative}.ballots .info-details.collapsed{height:90px;overflow:hidden}.ballots .info-details .toggle-show{color:#5c34a2;cursor:pointer;display:inline-block;margin-left:5px}.sokol .ballots .info-details .toggle-show{color:#6ac9b9}.new-form .info{padding:30px}.info-title{color:#333;font-size:16px;font-weight:400;margin-bottom:30px;padding-left:25px;position:relative}.info-title:before{background-image:url("../assets/images/icons/icon-info.svg");background-position:50% 50%;background-repeat:no-repeat;background-size:10px 18px;content:'';height:32px;left:0;position:absolute;top:50%;transform:translateY(-50%);width:10px}.info-i{color:#777;font-size:14px;line-height:1.71;margin-bottom:20px;padding-left:25px;position:relative}.info-i:before{background-color:#333;border-radius:50%;content:'';height:6px;left:0;position:absolute;top:8px;width:6px}.app-container{padding-bottom:30px;padding-top:40px;position:relative}.app-container.app-container-open-mobile-menu:before{background-color:rgba(78,44,137,0.8);bottom:0;content:'';display:none;left:0;position:fixed;right:0;top:290px;z-index:1234}@media screen and (max-width: 767px){.app-container.app-container-open-mobile-menu:before{display:block}}.app-container.app-container-open-mobile-menu.sokol:before{background-color:rgba(106,201,185,0.8)}@media screen and (max-width: 767px){#root,.app-container{height:100%}.app-container{padding-left:10px;padding-right:10px}}@media screen and (max-width: 767px){.content{padding-top:120px}}.content.content-menu-open{padding-top:290px}.clearfix::after{content:"";clear:both;display:table}@keyframes fadeOut{0%{opacity:.2}20%{opacity:1;transform:scale(1)}100%{opacity:.2;transform:scale(0.3)}}.loading{display:flex;justify-content:space-between;left:50%;margin:-30px 0 0 -111.5px;padding-top:50px;position:absolute;top:50%;width:206px}.loading:before{background-image:url("../assets/images/logos/logo_loader.svg");background-position:0 0;background-repeat:no-repeat;content:'';left:0;position:absolute;top:0;height:100px;width:206px}.loading-container{bottom:0;left:0;position:fixed;right:0;top:0;z-index:1000000}.loading-i{animation-duration:2s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:fadeOut;animation-timing-function:linear;background-color:#fff;border-radius:50%;height:9px;opacity:.2;width:9px}.loading-i:nth-child(2){animation-delay:.1s}.loading-i:nth-child(3){animation-delay:.2s}.loading-i:nth-child(4){animation-delay:.3s}.loading-i:nth-child(5){animation-delay:.4s}.loading-i:nth-child(6){animation-delay:.5s}.main-title-container{-ms-flex-pack:justify;display:-ms-flexbox;display:flex;justify-content:space-between;margin-bottom:40px}.main-title-container .main-title{font-size:24px;color:#333;line-height:38px}@media screen and (max-width: 767px){.main-title-container{-ms-flex-direction:column;flex-direction:column}.main-title-container .search-input{width:100%}}.new-form{display:flex;overflow:hidden;padding:0}@media screen and (max-width: 768px){.new-form{display:block}}.new-form-side{box-sizing:border-box;padding-bottom:30px;padding-top:30px}@media screen and (max-width: 768px){.new-form-side{width:100% !important}}.new-form-side_left{background-color:#f8f8f8;padding-top:0;width:30%}@media screen and (max-width: 768px){.new-form-side_left{margin-bottom:30px;padding-bottom:0}}.new-form-side_right{border-left:1px solid #e4e4e4;padding-left:30px;padding-right:30px;width:70%}@media screen and (max-width: 768px){.new-form-side_right{border:0;padding-top:0}}.new .add-ballot{white-space:nowrap}@media screen and (max-width: 767px){.new .add-ballot{margin-top:20px}}.search-container{background-color:#502d8d;box-sizing:border-box;height:50px;padding:10px 0 0 0}.search-container.sokol{background-color:#41b19e}@media screen and (max-width: 767px){.search-container{left:0;position:fixed;right:0;top:70px;z-index:123}}input.search-input{background-color:transparent;background-image:url("../assets/images/icons/icon-search.svg");background-position:0 50%;background-repeat:no-repeat;background-size:16px 16px;border-radius:0;border:none;box-sizing:border-box;display:block;font-size:14px;font-weight:400;height:30px;outline:none;padding-left:30px;width:100%}input.search-input,input.search-input:focus input.search-input:hover,input.search-input:active{color:#fff}.sokol input.search-input{background-image:url("../assets/images/icons/icon-search-sokol.svg")}input.search-input::-webkit-input-placeholder{color:rgba(255,255,255,0.5) !important;font-size:14px !important}input.search-input:-moz-placeholder{color:rgba(255,255,255,0.5) !important;font-size:14px !important;opacity:1}input.search-input::-moz-placeholder{color:rgba(255,255,255,0.5) !important;font-size:14px !important;opacity:1}input.search-input:-ms-input-placeholder{color:rgba(255,255,255,0.5) !important;font-size:14px !important}.settings{max-width:600px;margin:0 auto}.settings-title{margin-bottom:20px;color:#38454f;text-align:center;font-size:24px}.socials-item:not(:first-child){margin-left:10px}.socials-i{background-color:#ffffff;border-radius:3px;display:inline-block;font-size:0;height:30px;position:relative;transition:0.3s background-color;vertical-align:top;width:30px}.socials-i:not(:first-child){margin-left:10px}.socials-i:hover{background-color:rgba(255,255,255,0.4)}.socials-i:before{background-position:50% 50%;background-repeat:no-repeat;background-size:contain;content:'';height:16px;left:50%;position:absolute;top:50%;transform:translate(-50%, -50%);width:16px}.socials-i_github:before{background-image:url("../assets/images/socials/git.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.socials-i_github:before{background-image:url("../assets/images/socials/git@2x.png");background-size:100% 100%}}.socials-i_telegram:before{background-image:url("../assets/images/socials/telegram.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.socials-i_telegram:before{background-image:url("../assets/images/socials/telegram@2x.png");background-size:100% 100%}}.socials-i_twitter:before{background-image:url("../assets/images/socials/tw.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.socials-i_twitter:before{background-image:url("../assets/images/socials/tw@2x.png");background-size:100% 100%}}.socials-i_poa:before{background-image:url("../assets/images/socials/poa.png");height:20px;width:20px}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.socials-i_poa:before{background-image:url("../assets/images/socials/poa@2x.png");background-size:100% 100%}}.sokol .socials .socials-i_github:before{background-image:url("../assets/images/socials/git-sokol.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.sokol .socials .socials-i_github:before{background-image:url("../assets/images/socials/git-sokol@2x.png");background-size:100% 100%}}.sokol .socials .socials-i_telegram:before{background-image:url("../assets/images/socials/telegram-sokol.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.sokol .socials .socials-i_telegram:before{background-image:url("../assets/images/socials/telegram-sokol@2x.png");background-size:100% 100%}}.sokol .socials .socials-i_twitter:before{background-image:url("../assets/images/socials/tw-sokol.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.sokol .socials .socials-i_twitter:before{background-image:url("../assets/images/socials/tw-sokol@2x.png");background-size:100% 100%}}.sokol .socials .socials-i_poa:before{background-image:url("../assets/images/socials/poa-sokol.png")}@media (min--moz-device-pixel-ratio: 1.3), (-o-min-device-pixel-ratio: 2.6 / 2), (-webkit-min-device-pixel-ratio: 1.3), (min-device-pixel-ratio: 1.3), (min-resolution: 1.3dppx){.sokol .socials .socials-i_poa:before{background-image:url("../assets/images/socials/poa-sokol@2x.png");background-size:100% 100%}}.vote-scale{background-color:#f0f0f0;border-radius:5px;clear:left;height:10px;margin-top:34px;overflow:hidden}.vote-scale-not-finalized{background-color:#ffffff}.vote-scale--fill{height:100%;border-radius:3px}.vote-scale--fill_no{background-color:#f21b57}.sokol .vote-scale--fill_no{background-color:#f24c67}.vote-scale--fill_yes{background-color:#60db97}.sokol .vote-scale--fill_yes{background-color:#6ac9b9}.vote-scale--fill_send{background-color:#60db97}.sokol .vote-scale--fill_send{background-color:#6ac9b9}.vote-scale--fill_burn{background-color:#f21b57}.sokol .vote-scale--fill_burn{background-color:#f24c67}.vote-scale--fill_freeze{background-color:#50bbda}.sokol .vote-scale--fill_freeze{background-color:#50bbda}.vote-scale--container{width:100%}.vote-scale--value{float:left;font-size:12px}.vote-scale--votes,.vote-scale--percentage{float:right;font-size:13px}.vote-scale--votes{color:#333;margin-left:10px}.vote-scale--percentage{color:#000;font-weight:700}@keyframes Select-animation-spin{to{transform:rotate(1turn)}}@-webkit-keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn)}}.Select{position:relative}.Select input::-webkit-contacts-auto-fill-button,.Select input::-webkit-credentials-auto-fill-button{display:none !important}.Select input::-ms-clear{display:none !important}.Select input::-ms-reveal{display:none !important}.Select,.Select div,.Select input,.Select span{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.Select.is-disabled .Select-arrow-zone{cursor:default;pointer-events:none}.Select.is-disabled>.Select-control{background-color:#f9f9f9}.Select.is-disabled>.Select-control:hover{box-shadow:none}.Select.is-searchable.is-open>.Select-control{cursor:text}.Select.is-searchable.is-focused:not(.is-open)>.Select-control{cursor:text}.Select.is-open>.Select-control{border-bottom-right-radius:0;border-bottom-left-radius:0;background:#fff;border-color:#cbcbcb #e4e4e4 #f1f1f1}.Select.is-open>.Select-control .Select-arrow{top:-2px;border-color:transparent transparent #999;border-width:0 5px 5px}.Select.is-focused>.Select-control{background:#fff}.Select.is-focused:not(.is-open)>.Select-control{border-color:#5c34a2 #673ab5 #673ab5;box-shadow:none}.Select.has-value.is-clearable.Select--single>.Select-control .Select-value{padding-right:44px}.Select.has-value.Select--single>.Select-control .Select-value .Select-value-label,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value .Select-value-label{color:#333}.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label{cursor:pointer;text-decoration:none}.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:hover,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:hover,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus{color:#8e959d;outline:none;text-decoration:underline}.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus{background:#fff}.Select.has-value.is-pseudo-focused .Select-input{opacity:0}.Select.is-open .Select-arrow,.Select .Select-arrow-zone:hover>.Select-arrow{border-top-color:#666}.Select.Select--rtl{direction:rtl;text-align:right}.Select-control{background-color:#fff;border-collapse:separate;border-color:#f1f1f1 #e4e4e4 #cbcbcb;border-radius:3px;border-spacing:0;border:1px solid #e4e4e4;color:#333;cursor:default;display:table;height:40px;outline:none;overflow:hidden;position:relative;width:100%}.Select-control:hover{box-shadow:none}.Select-control .Select-input:focus{outline:none;background:#fff}.Select-placeholder,.Select--single>.Select-control .Select-value{bottom:0;color:#333;left:0;line-height:38px;padding-left:15px;padding-right:15px;position:absolute;right:0;top:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.Select-input{height:38px;padding-left:15px;padding-right:15px;vertical-align:middle}.Select-input>input{background:none transparent;border:0 none;box-shadow:none;cursor:default;display:inline-block;font-family:inherit;font-size:inherit;margin:0;outline:none;line-height:17px;padding:10px 0 14px;-webkit-appearance:none}.is-focused .Select-input>input{cursor:text}.Select-control:not(.is-searchable)>.Select-input{outline:none}.Select-loading-zone{cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:16px}.Select-loading{-webkit-animation:Select-animation-spin 400ms infinite linear;-o-animation:Select-animation-spin 400ms infinite linear;animation:Select-animation-spin 400ms infinite linear;width:16px;height:16px;box-sizing:border-box;border-radius:50%;border:2px solid #e4e4e4;border-right-color:#333;display:inline-block;position:relative;vertical-align:middle}.Select-clear-zone{-webkit-animation:Select-animation-fadeIn 200ms;-o-animation:Select-animation-fadeIn 200ms;animation:Select-animation-fadeIn 200ms;color:#999;cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:19px}.Select-clear-zone:hover{color:#D0021B}.Select-clear{display:inline-block;font-size:20px;line-height:1}.Select--multi .Select-clear-zone{width:19px}.Select--multi .Select-multi-value-wrapper{display:inline-block}.Select .Select-aria-only{position:absolute;display:inline-block;height:1px;width:1px;margin:-1px;clip:rect(0, 0, 0, 0);overflow:hidden;float:left}.Select-arrow-zone{cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:25px;padding-right:5px}.Select--rtl .Select-arrow-zone{padding-right:0;padding-left:5px}.Select-arrow{border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 2.5px;display:inline-block;height:0;width:0;position:relative}@-webkit-keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}@keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}.Select-menu-outer{border-bottom-right-radius:3px;border-bottom-left-radius:3px;background-color:#fff;border:1px solid #e4e4e4;border-top-color:#f2f2f2;box-shadow:none;box-sizing:border-box;margin-top:-1px;max-height:200px;position:absolute;top:100%;width:100%;z-index:1000;-webkit-overflow-scrolling:touch}.Select-menu{max-height:198px;overflow-y:auto}.Select-option{box-sizing:border-box;background-color:#fff;color:#666;cursor:pointer;display:block;padding:8px 15px}.Select-option:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.Select-option.is-selected{background-color:#f5faff;color:#333}.Select-option.is-focused{background-color:#f2f9fc;color:#333}.Select-option.is-disabled{color:#ccc;cursor:default}.Select-noresults{box-sizing:border-box;color:#999;cursor:default;display:block;padding:8px 15px}.Select--multi .Select-input{vertical-align:middle;margin-left:15px;padding:0}.Select--multi.Select--rtl .Select-input{margin-left:0;margin-right:15px}.Select--multi.has-value .Select-input{margin-left:5px}.Select--multi .Select-value{background-color:#f2f9fc;border-radius:3px;border:1px solid #e5eef9;color:#08c;display:inline-block;font-size:.9em;margin-left:5px;margin-top:5px;vertical-align:top}.Select--multi .Select-value-icon,.Select--multi .Select-value-label{display:inline-block;vertical-align:middle}.Select--multi .Select-value-label{border-bottom-right-radius:3px;border-top-right-radius:3px;cursor:default;padding:2px 5px}.Select--multi a.Select-value-label{color:#08c;cursor:pointer;text-decoration:none}.Select--multi a.Select-value-label:hover{text-decoration:underline}.Select--multi .Select-value-icon{cursor:pointer;border-bottom-left-radius:3px;border-top-left-radius:3px;border-right:1px solid #e5eef9;padding:1px 5px 3px}.Select--multi .Select-value-icon:hover,.Select--multi .Select-value-icon:focus{background-color:#ddeff7;color:#0077b3}.Select--multi .Select-value-icon:active{background-color:#e5eef9}.Select--multi.Select--rtl .Select-value{margin-left:0;margin-right:5px}.Select--multi.Select--rtl .Select-value-icon{border-right:none;border-left:1px solid #e5eef9}.Select--multi.is-disabled .Select-value{background-color:#fcfcfc;border:1px solid #e3e3e3;color:#333}.Select--multi.is-disabled .Select-value-icon{cursor:not-allowed;border-right:1px solid #e3e3e3}.Select--multi.is-disabled .Select-value-icon:hover,.Select--multi.is-disabled .Select-value-icon:focus,.Select--multi.is-disabled .Select-value-icon:active{background-color:#fcfcfc}@keyframes Select-animation-spin{to{transform:rotate(1turn)}}@-webkit-keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn)}}.ballot-types{background-color:#fff;border-bottom:1px solid #e4e4e4;padding:20px 30px 10px}.ballot-types-i{color:#333;cursor:pointer;font-size:16px;font-weight:400;margin-bottom:10px;position:relative;line-height:30px}.ballot-types-i:before{background-color:#5c34a2;border-bottom-right-radius:5px;border-top-right-radius:5px;content:'';height:30px;left:-30px;opacity:0;position:absolute;top:50%;transform:translateY(-50%);width:4px}.sokol .ballot-types-i:before{background-color:#6ac9b9}.ballot-types-i_active{color:#5c34a2}.ballot-types-i_active:before{opacity:1}.sokol .ballot-types-i_active{color:#6ac9b9}.ballots-i--vote,.ballots-footer-finalize{border-radius:2px;padding:0 13px;font-size:13px;font-weight:bold}.ballots .title{margin-bottom:50px}@media screen and (max-width: 768px){.ballots .title{margin-bottom:15px}}@media screen and (max-width: 767px){.ballots .title{margin-bottom:10px}}.ballots-about{margin-top:-8px}.ballots-i{background-color:#fff}.ballots-i-scale{border-top:1px solid #e4e4e4;display:flex;margin-bottom:30px;margin-top:30px;padding:30px 0 0 0}@media screen and (max-width: 768px){.ballots-i-scale{display:block;margin-top:15px;padding:15px 0 0 0}}@media screen and (max-width: 767px){.ballots-i-scale{margin-top:10px;padding:10px 0 0 0}}.ballots-i-scale-column{display:flex;flex-grow:1;justify-content:space-between;margin-right:40px}@media screen and (max-width: 768px){.ballots-i-scale-column{margin-bottom:30px;margin-right:0;overflow:hidden;width:100%}}.ballots-i-scale-column:last-child{margin-right:0}@media screen and (max-width: 768px){.ballots-i-scale-column.reverse-responsive{flex-direction:row-reverse}}.ballots-i--name{overflow:hidden;text-overflow:ellipsis}.ballots-i--see-all-proposal{display:inline-block;margin-top:5px}.ballots-i--vote-label{color:#333;cursor:default;font-size:14px;font-weight:700;line-height:1.2}@media screen and (max-width: 767px){.ballots-i--vote-label{margin-bottom:25px}}.ballots-i--vote-label-right{margin-left:auto !important}.ballots-i--vote_btn{line-height:44px;min-width:55px;text-transform:capitalize}.ballots-i--vote_btn.xl{min-width:78px}.ballots-i--vote_btn.m-r-20{margin-right:20px}.ballots-i--vote_btn.m-l-20{margin-left:20px}@media screen and (max-width: 768px){.ballots-i--vote_btn.m-r-20,.ballots-i--vote_btn.m-l-20{margin-left:0;margin-right:20px}}textarea{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;overflow:auto;outline:none;border-radius:3px;width:100%}.color-primary{color:#5c34a2}.sokol .color-primary{color:#6ac9b9}.color-danger{color:#f21b57}.sokol .color-danger{color:#f24c67}.color-success{color:#60db97}.sokol .color-success{color:#6ac9b9}@media screen and (max-width: 768px){.ballots-about{display:table;width:100%}}.ballots-about p{line-height:28px}.ballots-about-i{position:relative;display:inline-block;vertical-align:top;box-sizing:border-box}@media screen and (max-width: 768px){.ballots-about-i{display:table-row;width:100% !important}}.ballots-about-i .ballots-about-td-value{padding-right:12px}.ballots-about-i_name{width:12%}.ballots-about-i_action{width:8%}.ballots-about-i_type{width:8%}.ballots-about-i_proposal{width:30%}.ballots-about-i_key{width:26%;word-break:break-all}.ballots-about-i_key_wide{width:50%;word-break:break-all}.ballots-about-i_proposed_receiver{width:45%;word-break:break-all}.ballots-about-i_funds_amount{width:23%;word-break:break-all}.ballots-about-i_proposed-min-threshold{width:30%;word-break:break-all}.ballots-about-i_contract-type{width:25%;word-break:break-all}.ballots-about-i_proposed-address{width:30%;word-break:break-all}.ballots-about-i_time{width:20%}.ballots-about-i--title{color:#777;font-size:13px;font-weight:400;text-align:left}.ballots-about-i:first-child .ballots-about-td-value{border-left:none;padding-left:0}.ballots-about-i:last-child .ballots-about-td-value{border-right:none;padding-right:0}.ballots-about-td.ballots-about-td-value{color:#333;font-size:14px}.ballots-about-i_action .ballots-about-td.ballots-about-td-value,.ballots-about-i_type .ballots-about-td.ballots-about-td-value{text-transform:capitalize}@media screen and (max-width: 768px){.ballots-about-td.ballots-about-td-value{padding-left:0;padding-right:0}}@media screen and (max-width: 768px){.ballots-about-td{display:table-cell;vertical-align:top}}@media screen and (max-width: 768px){.ballots-about-td:first-child{padding-right:10px}}@media screen and (max-width: 768px){.ballots-about-i:not(:last-child) .ballots-about-td{padding-bottom:10px}}.ballots-footer{display:flex;align-items:center;justify-content:space-between}.ballots-footer-left{display:inline-flex;align-items:center}@media screen and (max-width: 767px){.ballots-footer-left{display:block}.ballots-footer-left .btn{display:block;margin:0 0 15px;width:100%}}@media screen and (max-width: 768px){.ballots-footer{padding-top:15px}}@media screen and (max-width: 767px){.ballots-footer{flex-direction:column-reverse;padding-top:0}}.ballots-footer p{color:#777;font-size:14px;font-stretch:normal;font-style:normal;font-weight:normal;line-height:18px;max-width:340px;padding-left:15px}.ballots-footer-finalize{margin-right:20px;background-color:rgba(8,179,242,0.1);white-space:nowrap}.ballots-footer-finalize-finalized{background-color:rgba(109,46,174,0.1);color:#6d2eae;cursor:default}@media screen and (max-width: 767px){.ballots-footer-finalize{width:100%;margin-right:0;margin-top:10px;text-align:center}} diff --git a/src/assets/stylesheets/application/_controls.scss b/src/assets/stylesheets/application/_controls.scss index 765f492..c832a2b 100644 --- a/src/assets/stylesheets/application/_controls.scss +++ b/src/assets/stylesheets/application/_controls.scss @@ -136,4 +136,12 @@ button { padding-top: 12px; margin: 0; word-break: break-word; + + a { + color: $primary-color; + + .sokol & { + color: $primary-color-sokol; + } + } } diff --git a/src/components/BallotCard.jsx b/src/components/BallotCard.jsx index ddf54cf..7b44d80 100644 --- a/src/components/BallotCard.jsx +++ b/src/components/BallotCard.jsx @@ -8,6 +8,9 @@ import swal from 'sweetalert2' const ACCEPT = 1 const REJECT = 2 +const SEND = 1 +const BURN = 2 +const FREEZE = 3 const USDateTimeFormat = 'MM/DD/YYYY h:mm:ss A' const maxDetailsLength = 500 @@ -102,6 +105,60 @@ export class BallotCard extends React.Component { return votesPercents } + @computed + get votesBurnNumber() { + let votes = this.burnVotes + if (isNaN(votes)) votes = 0 + return votes + } + + @computed + get votesBurnPercents() { + if (this.totalVoters <= 0) { + return 0 + } + + let votesPercents = Math.round((this.votesBurnNumber / this.totalVoters) * 100) + if (isNaN(votesPercents)) votesPercents = 0 + return votesPercents + } + + @computed + get votesFreezeNumber() { + let votes = this.freezeVotes + if (isNaN(votes)) votes = 0 + return votes + } + + @computed + get votesFreezePercents() { + if (this.totalVoters <= 0) { + return 0 + } + + let votesPercents = Math.round((this.votesFreezeNumber / this.totalVoters) * 100) + if (isNaN(votesPercents)) votesPercents = 0 + return votesPercents + } + + @computed + get votesSendNumber() { + let votes = this.sendVotes + if (isNaN(votes)) votes = 0 + return votes + } + + @computed + get votesSendPercents() { + if (this.totalVoters <= 0) { + return 0 + } + + let votesPercents = Math.round((this.votesSendNumber / this.totalVoters) * 100) + if (isNaN(votesPercents)) votesPercents = 0 + return votesPercents + } + @action('Calculate time to start/finish') calcTimeTo = () => { const _now = moment() @@ -202,8 +259,18 @@ export class BallotCard extends React.Component { async tx => { const ballotInfo = await contract.getBallotInfo(id, contractsStore.votingKey) - this.totalVoters = Number(ballotInfo.totalVoters) - this.progress = Number(ballotInfo.progress) + if (ballotInfo.hasOwnProperty('totalVoters')) { + this.totalVoters = Number(ballotInfo.totalVoters) + } else { + this.burnVotes = ballotInfo.burnVotes + this.freezeVotes = ballotInfo.freezeVotes + this.sendVotes = ballotInfo.sendVotes + this.totalVoters = + Number(ballotInfo.burnVotes) + Number(ballotInfo.freezeVotes) + Number(ballotInfo.sendVotes) + } + if (ballotInfo.hasOwnProperty('progress')) { + this.progress = Number(ballotInfo.progress) + } this.isFinalized = Boolean(ballotInfo.isFinalized) if (ballotInfo.hasOwnProperty('canBeFinalizedNow')) { this.canBeFinalized = Boolean(ballotInfo.canBeFinalizedNow) @@ -212,8 +279,16 @@ export class BallotCard extends React.Component { } this.hasAlreadyVoted = true - ballotsStore.ballotCards[pos].props.votingState.totalVoters = this.totalVoters - ballotsStore.ballotCards[pos].props.votingState.progress = this.progress + if (ballotInfo.hasOwnProperty('totalVoters')) { + ballotsStore.ballotCards[pos].props.votingState.totalVoters = this.totalVoters + } else { + ballotsStore.ballotCards[pos].props.votingState.burnVotes = this.burnVotes + ballotsStore.ballotCards[pos].props.votingState.freezeVotes = this.freezeVotes + ballotsStore.ballotCards[pos].props.votingState.sendVotes = this.sendVotes + } + if (ballotInfo.hasOwnProperty('progress')) { + ballotsStore.ballotCards[pos].props.votingState.progress = this.progress + } ballotsStore.ballotCards[pos].props.votingState.isFinalized = this.isFinalized ballotsStore.ballotCards[pos].props.votingState.canBeFinalized = this.canBeFinalized ballotsStore.ballotCards[pos].props.votingState.hasAlreadyVoted = this.hasAlreadyVoted @@ -315,6 +390,8 @@ export class BallotCard extends React.Component { return contractsStore.votingToChangeMinThreshold case 'votingToChangeProxy': return contractsStore.votingToChangeProxy + case 'votingToManageEmissionFunds': + return contractsStore.votingToManageEmissionFunds case 'validatorMetadata': return contractsStore.validatorMetadata default: @@ -330,6 +407,8 @@ export class BallotCard extends React.Component { return contractsStore.minThresholdBallotThreshold case 'votingToChangeProxy': return contractsStore.proxyBallotThreshold + case 'votingToManageEmissionFunds': + return contractsStore.emissionFundsBallotThreshold default: return contractsStore.keysBallotThreshold } @@ -346,9 +425,18 @@ export class BallotCard extends React.Component { this.creator = votingState.creator this.creatorMiningKey = votingState.creatorMiningKey // getTotalVoters - this.totalVoters = Number(votingState.totalVoters) + if (votingState.hasOwnProperty('totalVoters')) { + this.totalVoters = Number(votingState.totalVoters) + } else { + this.burnVotes = Number(votingState.burnVotes) + this.freezeVotes = Number(votingState.freezeVotes) + this.sendVotes = Number(votingState.sendVotes) + this.totalVoters = this.burnVotes + this.freezeVotes + this.sendVotes + } // getProgress - this.progress = Number(votingState.progress) + if (votingState.hasOwnProperty('progress')) { + this.progress = Number(votingState.progress) + } // getIsFinalized this.isFinalized = votingState.isFinalized // canBeFinalizedNow @@ -397,6 +485,8 @@ export class BallotCard extends React.Component { return 'Keys' case 'votingToChangeProxy': return 'Proxy' + case 'votingToManageEmissionFunds': + return 'EmissionFunds' default: return '' } @@ -425,66 +515,25 @@ export class BallotCard extends React.Component { ) : ( '' ) - return ( -
-
-
-
-

Proposer

-
-
-

{this.creator}

-
-
- {children} -
-
-

Ballot Time

-
-
-

{this.startTime}

-

- {this.timeTo.displayValue} ({this.timeTo.title}) -

-
-
-
- {/* TODO: Send / Burn / Freeze */} - {/*
-
- -
-

{this.votesForNumber} Votes

-

{this.votesForPercents}%

-
-
-
-
-
+ let votingScale + if (votingType === 'votingToManageEmissionFunds') { + votingScale = ( +
-

{this.votesAgainstNumber} Votes

-

{this.votesAgainstPercents}%

+

{this.votesBurnNumber} Votes

+

{this.votesBurnPercents}%

@@ -492,24 +541,45 @@ export class BallotCard extends React.Component {
-

{this.votesAgainstNumber} Votes

-

{this.votesAgainstPercents}%

+

{this.votesFreezeNumber} Votes

+

{this.votesFreezePercents}%

-
*/} - {/* No / yes */} +
+ +
+

{this.votesSendNumber} Votes

+

{this.votesSendPercents}%

+
+
+
+
+
+
+ ) + } else { + votingScale = (
+ ) + } + return ( +
+
+
+
+

Proposer

+
+
+

{this.creator}

+
+
+ {children} +
+
+

Ballot Time

+
+
+

{this.startTime}

+

+ {this.timeTo.displayValue} ({this.timeTo.title}) +

+
+
+
+ {votingScale}
Minimum {threshold} from {contractsStore.validatorsLength} validators are required to pass the proposal diff --git a/src/components/BallotEmissionFundsCard.jsx b/src/components/BallotEmissionFundsCard.jsx new file mode 100644 index 0000000..d1f41de --- /dev/null +++ b/src/components/BallotEmissionFundsCard.jsx @@ -0,0 +1,32 @@ +import React from 'react' +import { inject, observer } from 'mobx-react' +import { BallotCard } from './BallotCard.jsx' + +@inject('contractsStore') +@observer +export class BallotEmissionFundsCard extends React.Component { + render() { + let { id, votingState, pos, contractsStore } = this.props + const amount = contractsStore.web3Instance.fromWei(votingState.amount, 'ether') + return ( + +
+
+

Proposed Receiver

+
+
+

{votingState.receiver}

+
+
+
+
+

Funds Amount

+
+
+

{amount} POA

+
+
+
+ ) + } +} diff --git a/src/components/BallotEmissionFundsMetadata.jsx b/src/components/BallotEmissionFundsMetadata.jsx new file mode 100644 index 0000000..d92b412 --- /dev/null +++ b/src/components/BallotEmissionFundsMetadata.jsx @@ -0,0 +1,120 @@ +import React from 'react' +import { observable, action } from 'mobx' +import { inject, observer } from 'mobx-react' +import moment from 'moment' + +@inject('ballotStore', 'contractsStore') +@observer +export class BallotEmissionFundsMetadata extends React.Component { + @observable emissionFundsBalance + @observable noActiveBallotExists + @observable beginDateTime + @observable endDateTime + + @action('Get EmissionFunds balance') + getEmissionFundsBalance = async () => { + this.emissionFundsBalance = 'Loading...' + this.emissionFundsBalance = await this.props.contractsStore.emissionFunds.balance() + } + + @action('Get VotingToManageEmissionFunds.noActiveBallotExists') + getNoActiveBallotExists = async () => { + this.noActiveBallotExists = await this.props.contractsStore.votingToManageEmissionFunds.noActiveBallotExists() + } + + @action('Get beginDateTime and endDateTime') + getDateTimeLimits = async () => { + const { votingToManageEmissionFunds } = this.props.contractsStore + const dateTimeFormat = 'MM/DD/YYYY HH:mm' + + this.beginDateTime = '...loading date...' + this.endDateTime = '...loading date...' + + let emissionReleaseTime = Number(await votingToManageEmissionFunds.emissionReleaseTime()) + let emissionReleaseThreshold = 0 + const currentTime = Number(await votingToManageEmissionFunds.getTime()) + const distributionThreshold = Number(await votingToManageEmissionFunds.distributionThreshold()) + if (currentTime > emissionReleaseTime) { + emissionReleaseThreshold = Number(await votingToManageEmissionFunds.emissionReleaseThreshold()) + const diff = Math.floor((currentTime - emissionReleaseTime) / emissionReleaseThreshold) + if (diff > 0) { + emissionReleaseTime += emissionReleaseThreshold * diff + } + } + + const releasePlusDistribution = emissionReleaseTime + distributionThreshold + + if (currentTime < releasePlusDistribution) { + this.beginDateTime = moment.unix(emissionReleaseTime).format(dateTimeFormat) + this.endDateTime = moment.unix(releasePlusDistribution).format(dateTimeFormat) + } else { + if (emissionReleaseThreshold === 0) { + emissionReleaseThreshold = Number(await votingToManageEmissionFunds.emissionReleaseThreshold()) + } + const futureEmissionReleaseTime = emissionReleaseTime + emissionReleaseThreshold + this.beginDateTime = moment.unix(futureEmissionReleaseTime).format(dateTimeFormat) + this.endDateTime = moment.unix(futureEmissionReleaseTime + distributionThreshold).format(dateTimeFormat) + } + } + + constructor(props) { + super(props) + this.getEmissionFundsBalance() + this.getNoActiveBallotExists() + this.getDateTimeLimits() + } + + render() { + const { ballotStore, contractsStore } = this.props + let note, explorerLink + if (this.noActiveBallotExists === true) { + note = ( +

+ The ballot can be created starting from {this.beginDateTime} and will end on {this.endDateTime}. +

+ ) + } else if (this.noActiveBallotExists !== true) { + note =

To be able to create a new ballot, the previous ballot of this type must be finalized.

+ } + if (contractsStore.netId === '77') { + explorerLink = `https://sokol-explorer.poa.network/account/${contractsStore.emissionFunds.address}` + } else { + explorerLink = `https://poaexplorer.com/address/${contractsStore.emissionFunds.address}` + } + return ( +
+
+
+
+ + ballotStore.changeBallotMetadata(e, 'receiver', 'ballotEmissionFunds')} + /> +

+ The address to which the funds will be sent if `Send` operation obtains the majority of votes. +

+
+
+
+
+ + +

+ Current balance of  + + EmissionFunds contract + . +

+
+
+
+
+ {note} +
+
+ ) + } +} diff --git a/src/components/BallotKeysCard.jsx b/src/components/BallotKeysCard.jsx index 07e485e..8978d9a 100644 --- a/src/components/BallotKeysCard.jsx +++ b/src/components/BallotKeysCard.jsx @@ -63,23 +63,6 @@ export class BallotKeysCard extends React.Component {
{miningKeyDiv} - {/* TODO: New ballot type */} - {/*
-
-

Proposed Receiver

-
-
-

0x4432c441EE96ef387CEC496709967Be6E27f57C8

-
-
-
-
-

Funds Amount

-
-
-

10000 POA

-
-
*/} ) } diff --git a/src/components/Ballots.jsx b/src/components/Ballots.jsx index 580637b..1af9cc0 100644 --- a/src/components/Ballots.jsx +++ b/src/components/Ballots.jsx @@ -116,6 +116,14 @@ export class Ballots extends React.Component { ) { continue } + } else if (contractType === ballotStore.BallotType.emissionFunds) { + if ( + String(votingState.receiver) + .toLowerCase() + .includes(searchTerm) + ) { + continue + } } ballotCards.splice(i--, 1) diff --git a/src/components/NewBallot.jsx b/src/components/NewBallot.jsx index 9acf9cb..adea84c 100644 --- a/src/components/NewBallot.jsx +++ b/src/components/NewBallot.jsx @@ -7,6 +7,7 @@ import { KeysTypes } from './KeysTypes.jsx' import { BallotKeysMetadata } from './BallotKeysMetadata.jsx' import { BallotMinThresholdMetadata } from './BallotMinThresholdMetadata.jsx' import { BallotProxyMetadata } from './BallotProxyMetadata.jsx' +import { BallotEmissionFundsMetadata } from './BallotEmissionFundsMetadata.jsx' import { messages } from '../messages' import { constants } from '../constants' import { sendTransactionByVotingKey } from '../helpers' @@ -44,40 +45,42 @@ export class NewBallot extends React.Component { return false } - const minBallotDurationInHours = constants.minBallotDurationInDays * 24 - const startTime = this.getStartTimeUnix() - const minEndTime = moment - .utc() - .add(minBallotDurationInHours, 'hours') - .format() - let neededMinutes = moment(minEndTime).diff(moment(ballotStore.endTime), 'minutes') - let neededHours = Math.floor(neededMinutes / 60) - let duration = moment.unix(ballotStore.endTimeUnix).diff(moment.unix(startTime), 'hours') + if (!ballotStore.isBallotForEmissionFunds) { + const minBallotDurationInHours = constants.minBallotDurationInDays * 24 + const startTime = this.getStartTimeUnix() + const minEndTime = moment + .utc() + .add(minBallotDurationInHours, 'hours') + .format() + let neededMinutes = moment(minEndTime).diff(moment(ballotStore.endTime), 'minutes') + let neededHours = Math.floor(neededMinutes / 60) + let duration = moment.unix(ballotStore.endTimeUnix).diff(moment.unix(startTime), 'hours') - if (duration < 0) { - duration = 0 - } + if (duration < 0) { + duration = 0 + } - if (neededMinutes > 0) { - neededMinutes = Math.abs(neededHours * 60 - neededMinutes) - swal( - 'Warning!', - messages.SHOULD_BE_MORE_THAN_MIN_DURATION(minBallotDurationInHours, duration, neededHours, neededMinutes), - 'warning' - ) - commonStore.hideLoading() - return false - } + if (neededMinutes > 0) { + neededMinutes = Math.abs(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 + 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) { @@ -132,7 +135,7 @@ export class NewBallot extends React.Component { } } - let isAddress = contractsStore.web3Instance.isAddress(ballotStore.ballotProxy.proposedAddress) + const isAddress = contractsStore.web3Instance.isAddress(ballotStore.ballotProxy.proposedAddress) if (!isAddress) { swal('Warning!', messages.PROPOSED_ADDRESS_IS_NOT_ADDRESS_MSG, 'warning') @@ -141,7 +144,28 @@ export class NewBallot extends React.Component { } } - if (!ballotStore.isBallotForKey && !ballotStore.isBallotForMinThreshold && !ballotStore.isBallotForProxy) { + if (ballotStore.isBallotForEmissionFunds) { + if (ballotStore.ballotEmissionFunds.receiver.length === 0) { + swal('Warning!', `Address of funds receiver is empty`, 'warning') + commonStore.hideLoading() + return false + } + + const isAddress = contractsStore.web3Instance.isAddress(ballotStore.ballotEmissionFunds.receiver) + + if (!isAddress) { + swal('Warning!', messages.PROPOSED_ADDRESS_IS_NOT_ADDRESS_MSG, 'warning') + commonStore.hideLoading() + return false + } + } + + if ( + !ballotStore.isBallotForKey && + !ballotStore.isBallotForMinThreshold && + !ballotStore.isBallotForProxy && + !ballotStore.isBallotForEmissionFunds + ) { swal('Warning!', messages.BALLOT_TYPE_IS_EMPTY_MSG, 'warning') commonStore.hideLoading() return false @@ -150,11 +174,11 @@ export class NewBallot extends React.Component { return true } - createBallotForKeys = startTime => { + createBallotForKeys = (startTime, endTime) => { const { ballotStore, contractsStore } = this.props const inputToMethod = { startTime: startTime, - endTime: ballotStore.endTimeUnix, + endTime: endTime, affectedKey: ballotStore.ballotKeys.affectedKey, affectedKeyType: ballotStore.ballotKeys.keyType, newVotingKey: ballotStore.ballotKeys.newVotingKey, @@ -176,22 +200,22 @@ export class NewBallot extends React.Component { return data } - createBallotForMinThreshold = startTime => { + createBallotForMinThreshold = (startTime, endTime) => { const { ballotStore, contractsStore } = this.props const inputToMethod = { startTime: startTime, - endTime: ballotStore.endTimeUnix, + endTime: endTime, proposedValue: ballotStore.ballotMinThreshold.proposedValue, memo: ballotStore.memo } return contractsStore.votingToChangeMinThreshold.createBallot(inputToMethod) } - createBallotForProxy = startTime => { + createBallotForProxy = (startTime, endTime) => { const { ballotStore, contractsStore } = this.props const inputToMethod = { startTime: startTime, - endTime: ballotStore.endTimeUnix, + endTime: endTime, proposedValue: ballotStore.ballotProxy.proposedAddress, contractType: ballotStore.ballotProxy.contractType, memo: ballotStore.memo @@ -199,6 +223,17 @@ export class NewBallot extends React.Component { return contractsStore.votingToChangeProxy.createBallot(inputToMethod) } + createBallotForEmissionFunds = (startTime, endTime) => { + const { ballotStore, contractsStore } = this.props + const inputToMethod = { + startTime: startTime, + endTime: endTime, + receiver: ballotStore.ballotEmissionFunds.receiver, + memo: ballotStore.memo + } + return contractsStore.votingToManageEmissionFunds.createBallot(inputToMethod) + } + onClick = async () => { const { commonStore, contractsStore, ballotStore, ballotsStore } = this.props const { push } = this.props.routing @@ -240,6 +275,42 @@ export class NewBallot extends React.Component { } } + let startTime = this.getStartTimeUnix() + let endTime = ballotStore.endTimeUnix + + if (ballotStore.ballotType === ballotStore.BallotType.emissionFunds) { + const votingContract = contractsStore.votingToManageEmissionFunds + let emissionReleaseTime = Number(await votingContract.emissionReleaseTime()) + const currentTime = Number(await votingContract.getTime()) + if (currentTime > emissionReleaseTime) { + const emissionReleaseThreshold = Number(await votingContract.emissionReleaseThreshold()) + const diff = Math.floor((currentTime - emissionReleaseTime) / emissionReleaseThreshold) + if (diff > 0) { + emissionReleaseTime += emissionReleaseThreshold * diff + } + } + + if (currentTime < emissionReleaseTime) { + const emissionReleaseTimeString = moment + .unix(emissionReleaseTime) + .utc() + .format('MMM Do YYYY, h:mm:ss a') + swal('Warning!', messages.EMISSION_RELEASE_TIME_IN_FUTURE(emissionReleaseTimeString), 'warning') + return + } + + const noActiveBallotExists = await votingContract.noActiveBallotExists() + if (!noActiveBallotExists) { + swal('Warning!', messages.PREVIOUS_BALLOT_NOT_FINALIZED, 'warning') + return + } + + const distributionThreshold = Number(await votingContract.distributionThreshold()) + + startTime = currentTime + constants.startTimeOffsetInMinutes * 60 + endTime = emissionReleaseTime + distributionThreshold + } + let methodToCreateBallot let contractType let contractInstance @@ -259,16 +330,19 @@ export class NewBallot extends React.Component { contractType = 'votingToChangeProxy' contractInstance = contractsStore.votingToChangeProxy.votingToChangeProxyInstance break + case ballotStore.BallotType.emissionFunds: + methodToCreateBallot = this.createBallotForEmissionFunds + contractType = 'votingToManageEmissionFunds' + contractInstance = contractsStore.votingToManageEmissionFunds.instance + break default: break } - const startTime = this.getStartTimeUnix() - sendTransactionByVotingKey( this.props, contractInstance.options.address, - methodToCreateBallot(startTime), + methodToCreateBallot(startTime, endTime), async tx => { const events = await contractInstance.getPastEvents('BallotCreated', { fromBlock: tx.blockNumber, @@ -322,9 +396,23 @@ export class NewBallot extends React.Component { metadata = minThreshold = contractsStore.proxyBallotThreshold break + case ballotStore.BallotType.emissionFunds: + metadata = + minThreshold = contractsStore.emissionFundsBallotThreshold + break default: break } + const emissionFundsManagementBallot = contractsStore.votingToManageEmissionFunds ? ( +
ballotStore.changeBallotType(e, ballotStore.BallotType.emissionFunds)} + > + Emission Funds Ballot +
+ ) : ( + '' + ) return (
@@ -348,12 +436,13 @@ export class NewBallot extends React.Component { > Modify Proxy Contract Ballot
+ {emissionFundsManagementBallot}
-

Information of the ballot

+

Limits of the ballot

- Minimum {minThreshold} from {contractsStore.validatorsLength} - validators are required to pass the proposal
+ Minimum {minThreshold} from {contractsStore.validatorsLength} validators are required to pass the  + proposal
You can create {contractsStore.validatorLimits.keys} ballot(s) for keys
diff --git a/src/constants.js b/src/constants.js index eaef903..8f7a7b1 100644 --- a/src/constants.js +++ b/src/constants.js @@ -6,11 +6,13 @@ constants.ABIsSources = { KeysManager: 'KeysManager.abi.json', PoaNetworkConsensus: 'PoaNetworkConsensus.abi.json', BallotStorage: 'BallotsStorage.abi.json', + EmissionFunds: 'EmissionFunds.abi.json', ProxyStorage: 'ProxyStorage.abi.json', ValidatorMetadata: 'ValidatorMetadata.abi.json', VotingToChangeKeys: 'VotingToChangeKeys.abi.json', VotingToChangeMinThreshold: 'VotingToChangeMinThreshold.abi.json', - VotingToChangeProxyAddress: 'VotingToChangeProxyAddress.abi.json' + VotingToChangeProxyAddress: 'VotingToChangeProxyAddress.abi.json', + VotingToManageEmissionFunds: 'VotingToManageEmissionFunds.abi.json' } constants.NEW_MINING_KEY = { label: 'New Mining Key', diff --git a/src/contracts/EmissionFunds.contract.js b/src/contracts/EmissionFunds.contract.js new file mode 100644 index 0000000..57160d2 --- /dev/null +++ b/src/contracts/EmissionFunds.contract.js @@ -0,0 +1,15 @@ +import Web3 from 'web3' +import { networkAddresses } from './addresses' + +export default class EmissionFunds { + async init({ web3, netId }) { + const { EMISSION_FUNDS_ADDRESS } = networkAddresses(netId) + console.log('EmissionFunds address', EMISSION_FUNDS_ADDRESS) + this.web3_10 = new Web3(web3.currentProvider) + this.address = EMISSION_FUNDS_ADDRESS + } + + balance() { + return this.web3_10.eth.getBalance(this.address) + } +} diff --git a/src/contracts/VotingToManageEmissionFunds.contract.js b/src/contracts/VotingToManageEmissionFunds.contract.js new file mode 100644 index 0000000..95382e7 --- /dev/null +++ b/src/contracts/VotingToManageEmissionFunds.contract.js @@ -0,0 +1,84 @@ +import Web3 from 'web3' +import { networkAddresses } from './addresses' +import helpers from './helpers' + +export default class VotingToManageEmissionFunds { + async init({ web3, netId }) { + const { VOTING_TO_MANAGE_EMISSION_FUNDS_ADDRESS } = networkAddresses(netId) + console.log('VotingToManageEmissionFunds address', VOTING_TO_MANAGE_EMISSION_FUNDS_ADDRESS) + const web3_10 = new Web3(web3.currentProvider) + + const branch = helpers.getBranch(netId) + + const votingToManageEmissionFundsABI = await helpers.getABI(branch, 'VotingToManageEmissionFunds') + + this.instance = new web3_10.eth.Contract(votingToManageEmissionFundsABI, VOTING_TO_MANAGE_EMISSION_FUNDS_ADDRESS) + this.address = VOTING_TO_MANAGE_EMISSION_FUNDS_ADDRESS + } + + // setters + cancelNewBallot() { + return this.instance.methods.cancelNewBallot().encodeABI() + } + + createBallot({ startTime, endTime, receiver, memo }) { + return this.instance.methods.createBallot(startTime, endTime, receiver, memo).encodeABI() + } + + finalize(_id) { + return this.instance.methods.finalize(_id).encodeABI() + } + + vote(_id, choice) { + return this.instance.methods.vote(_id, choice).encodeABI() + } + + // getters + ballotCancelingThreshold() { + return this.instance.methods.ballotCancelingThreshold().call() + } + + canBeFinalizedNow(_id) { + return this.instance.methods.canBeFinalizedNow(_id).call() + } + + distributionThreshold() { + return this.instance.methods.distributionThreshold().call() + } + + emissionReleaseThreshold() { + return this.instance.methods.emissionReleaseThreshold().call() + } + + emissionReleaseTime() { + return this.instance.methods.emissionReleaseTime().call() + } + + getBallotInfo(_id, _votingKey) { + return this.instance.methods.getBallotInfo(_id).call() + } + + getTime() { + return this.instance.methods.getTime().call() + } + + hasAlreadyVoted(_id, _votingKey) { + return this.instance.methods.hasAlreadyVoted(_id, _votingKey).call() + } + + isActive(_id) { + return this.instance.methods.isActive(_id).call() + } + + isValidVote(_id, _votingKey) { + return this.instance.methods.isValidVote(_id, _votingKey).call() + } + + nextBallotId() { + return this.instance.methods.nextBallotId().call() + } + + noActiveBallotExists() { + return this.instance.methods.noActiveBallotExists().call() + } +} diff --git a/src/index.js b/src/index.js index a2f4603..704618c 100644 --- a/src/index.js +++ b/src/index.js @@ -49,7 +49,7 @@ class AppMainRouter extends Component { let setVotingToChangeProxy = contractsStore.setVotingToChangeProxy(web3Config) let setValidatorMetadata = contractsStore.setValidatorMetadata(web3Config) - await Promise.all([ + let promises = [ setPoaConsensus, setBallotsStorage, setKeysManager, @@ -58,7 +58,15 @@ class AppMainRouter extends Component { setVotingToChangeMinThreshold, setVotingToChangeProxy, setValidatorMetadata - ]) + ] + + if (web3Config.netId === '77') { + // if we're in Sokol + promises.push(contractsStore.setEmissionFunds(web3Config)) + promises.push(contractsStore.setVotingToManageEmissionFunds(web3Config)) + } + + await Promise.all(promises) await contractsStore.setMiningKey(web3Config) await contractsStore.setVotingKey(web3Config) @@ -67,7 +75,6 @@ class AppMainRouter extends Component { await contractsStore.getAllBallots() contractsStore.getKeysBallotThreshold() - contractsStore.getMinThresholdBallotThreshold() contractsStore.getProxyBallotThreshold() contractsStore.getBallotsLimits() console.log('votingKey', contractsStore.votingKey) diff --git a/src/messages.js b/src/messages.js index 66bf507..b856e06 100644 --- a/src/messages.js +++ b/src/messages.js @@ -10,7 +10,7 @@ messages.INVALID_VOTE_MSG = "You can't vote on this ballot" messages.INVALID_FINALIZE_MSG = "You can't finalize this ballot" messages.AFFECTED_KEY_IS_NOT_ADDRESS_MSG = "Ballot affectedKey isn't address" messages.MINING_KEY_IS_NOT_ADDRESS_MSG = "Ballot miningKey isn't address" -messages.PROPOSED_ADDRESS_IS_NOT_ADDRESS_MSG = "Ballot proposedAddress isn't address" +messages.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.NO_METAMASK_MSG = `You haven't chosen any account in MetaMask. @@ -30,6 +30,10 @@ messages.SHOULD_BE_MORE_THAN_MIN_DURATION = (minDuration, duration, neededHours, messages.SHOULD_BE_LESS_OR_EQUAL_14_DAYS = duration => { return `Ballot end time should not be more than 14 days from now in UTC time. Current duration is ${duration} hours.` } +messages.EMISSION_RELEASE_TIME_IN_FUTURE = emissionReleaseTime => { + return `You cannot create ballot right now. You'll be able to do that after ${emissionReleaseTime} UTC.` +} +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.` messages.VOTE_FAILED_TX = `Your transaction was failed. Please make sure you haven't already voted for this ballot. diff --git a/src/stores/BallotStore.js b/src/stores/BallotStore.js index a4141ae..bbf316a 100644 --- a/src/stores/BallotStore.js +++ b/src/stores/BallotStore.js @@ -6,7 +6,8 @@ class BallotStore { BallotType = { keys: 1, minThreshold: 2, - proxy: 3 + proxy: 3, + emissionFunds: 4 } KeysBallotType = { add: 1, @@ -34,6 +35,8 @@ class BallotStore { @observable ballotKeys @observable ballotMinThreshold @observable ballotProxy + @observable ballotEmissionFunds + @observable memo constructor() { @@ -59,6 +62,11 @@ class BallotStore { proposedAddress: '', contractType: '' } + + this.ballotEmissionFunds = { + receiver: '' + } + this.memo = '' } @@ -83,6 +91,11 @@ class BallotStore { return this.ballotType === this.BallotType.proxy } + @computed + get isBallotForEmissionFunds() { + return this.ballotType === this.BallotType.emissionFunds + } + @computed get isAddKeysBallotType() { return this.ballotKeys.keysBallotType === this.KeysBallotType.add diff --git a/src/stores/BallotsStore.js b/src/stores/BallotsStore.js index 16daf83..a6ba107 100644 --- a/src/stores/BallotsStore.js +++ b/src/stores/BallotsStore.js @@ -1,21 +1,9 @@ import { observable } from 'mobx' class BallotsStore { - @observable activeKeysBallotsLength - @observable activeMinThresholdBallotsLength - @observable activeProxyBallotsLength @observable ballotCards - @observable activeMinThresholdBallotsIDs - @observable activeProxyBallotsIDs - constructor() { - this.activeKeysBallotsLength = 0 - this.activeMinThresholdBallotsLength = 0 - this.activeProxyBallotsLength = 0 - - this.activeMinThresholdBallotsIDs = [] - this.activeProxyBallotsIDs = [] this.ballotCards = [] } } diff --git a/src/stores/ContractsStore.js b/src/stores/ContractsStore.js index b7b9a82..71eba21 100644 --- a/src/stores/ContractsStore.js +++ b/src/stores/ContractsStore.js @@ -3,11 +3,13 @@ import React from 'react' import PoaConsensus from '../contracts/PoaConsensus.contract' import BallotsStorage from '../contracts/BallotsStorage.contract' +import EmissionFunds from '../contracts/EmissionFunds.contract' import KeysManager from '../contracts/KeysManager.contract' import ProxyStorage from '../contracts/ProxyStorage.contract' import VotingToChangeKeys from '../contracts/VotingToChangeKeys.contract' import VotingToChangeMinThreshold from '../contracts/VotingToChangeMinThreshold.contract' import VotingToChangeProxy from '../contracts/VotingToChangeProxy.contract' +import VotingToManageEmissionFunds from '../contracts/VotingToManageEmissionFunds.contract' import ValidatorMetadata from '../contracts/ValidatorMetadata.contract' import ballotStore from './BallotStore' import ballotsStore from './BallotsStore' @@ -15,6 +17,7 @@ import commonStore from './CommonStore' import { BallotKeysCard } from '../components/BallotKeysCard.jsx' import { BallotMinThresholdCard } from '../components/BallotMinThresholdCard.jsx' import { BallotProxyCard } from '../components/BallotProxyCard.jsx' +import { BallotEmissionFundsCard } from '../components/BallotEmissionFundsCard.jsx' import { constants } from '../constants' import 'babel-polyfill' @@ -22,11 +25,13 @@ import 'babel-polyfill' class ContractsStore { @observable poaConsensus @observable ballotsStorage + @observable emissionFunds @observable keysManager @observable proxyStorage @observable votingToChangeKeys @observable votingToChangeMinThreshold @observable votingToChangeProxy + @observable votingToManageEmissionFunds @observable validatorMetadata @observable votingKey @observable miningKey @@ -55,18 +60,13 @@ class ContractsStore { @action('Get keys ballot threshold') getKeysBallotThreshold = async () => { this.keysBallotThreshold = await this.ballotsStorage.ballotsStorageInstance.methods.getBallotThreshold(1).call() - } - - @action('Get min threshold ballot threshold') - async getMinThresholdBallotThreshold() { - this.minThresholdBallotThreshold = await this.ballotsStorage.ballotsStorageInstance.methods - .getBallotThreshold(1) - .call() + this.minThresholdBallotThreshold = this.keysBallotThreshold } @action('Get proxy ballot threshold') getProxyBallotThreshold = async () => { this.proxyBallotThreshold = await this.ballotsStorage.ballotsStorageInstance.methods.getProxyThreshold().call() + this.emissionFundsBallotThreshold = this.proxyBallotThreshold } @action('Set web3Instance') @@ -93,6 +93,15 @@ class ContractsStore { }) } + @action('Set EmissionFunds contract') + setEmissionFunds = async web3Config => { + this.emissionFunds = new EmissionFunds() + await this.emissionFunds.init({ + web3: web3Config.web3Instance, + netId: web3Config.netId + }) + } + @action('Set KeysManager contract') setKeysManager = async web3Config => { this.keysManager = new KeysManager() @@ -138,6 +147,15 @@ class ContractsStore { }) } + @action('Set VotingToManageEmissionFunds contract') + setVotingToManageEmissionFunds = async web3Config => { + this.votingToManageEmissionFunds = new VotingToManageEmissionFunds() + await this.votingToManageEmissionFunds.init({ + web3: web3Config.web3Instance, + netId: web3Config.netId + }) + } + @action('Set ValidatorMetadata contract') setValidatorMetadata = async web3Config => { this.validatorMetadata = new ValidatorMetadata() @@ -171,12 +189,19 @@ class ContractsStore { getAllBallots = async () => { let keysNextBallotId = 0, minThresholdNextBallotId = 0, - proxyNextBallotId = 0 + proxyNextBallotId = 0, + emissionFundsNextBallotId = 0 try { - ;[keysNextBallotId, minThresholdNextBallotId, proxyNextBallotId] = await this.getAllBallotsNextIDs() + ;[ + keysNextBallotId, + minThresholdNextBallotId, + proxyNextBallotId, + emissionFundsNextBallotId + ] = await this.getAllBallotsNextIDs() keysNextBallotId = Number(keysNextBallotId) minThresholdNextBallotId = Number(minThresholdNextBallotId) proxyNextBallotId = Number(proxyNextBallotId) + emissionFundsNextBallotId = Number(emissionFundsNextBallotId) } catch (e) { console.log(e.message) } @@ -184,10 +209,12 @@ class ContractsStore { const allKeysPromise = this.getCards(keysNextBallotId, 'votingToChangeKeys') const allMinThresholdPromise = this.getCards(minThresholdNextBallotId, 'votingToChangeMinThreshold') const allProxyPromise = this.getCards(proxyNextBallotId, 'votingToChangeProxy') + const allEmissionFundsPromise = this.getCards(emissionFundsNextBallotId, 'votingToManageEmissionFunds') - await Promise.all([allKeysPromise, allMinThresholdPromise, allProxyPromise]) + await Promise.all([allKeysPromise, allMinThresholdPromise, allProxyPromise, allEmissionFundsPromise]) - const allBallotsIDsLength = keysNextBallotId + minThresholdNextBallotId + proxyNextBallotId + const allBallotsIDsLength = + keysNextBallotId + minThresholdNextBallotId + proxyNextBallotId + emissionFundsNextBallotId if (allBallotsIDsLength === 0) { commonStore.hideLoading() @@ -315,6 +342,17 @@ class ContractsStore { /> ) break + case 'votingToManageEmissionFunds': + card = ( + + ) + break default: break } @@ -333,7 +371,10 @@ class ContractsStore { const keysNextBallotId = this.votingToChangeKeys.nextBallotId() const minThresholdNextBallotId = this.votingToChangeMinThreshold.nextBallotId() const proxyNextBallotId = this.votingToChangeProxy.nextBallotId() - return Promise.all([keysNextBallotId, minThresholdNextBallotId, proxyNextBallotId]) + const emissionFundsNextBallotId = this.votingToManageEmissionFunds + ? this.votingToManageEmissionFunds.nextBallotId() + : 0 + return Promise.all([keysNextBallotId, minThresholdNextBallotId, proxyNextBallotId, emissionFundsNextBallotId]) } @action