From ae10d77396f452c3e17653797153a73018e4e809 Mon Sep 17 00:00:00 2001 From: Jakub Wojciechowski Date: Wed, 28 Jun 2017 16:32:04 +0100 Subject: [PATCH] initial commit --- .gitignore | 9 + LICENSE.txt | 21 + README.md | 82 +++ app/index.html | 122 +++++ app/javascripts/app.js | 154 ++++++ app/stylesheets/AdminLTE.min.css | 7 + app/stylesheets/app.css | 8 + contracts/ContractProvider.sol | 5 + contracts/Migrations.sol | 23 + contracts/Owned.sol | 16 + contracts/alice/AliceToken.sol | 71 +++ contracts/alice/Charity.sol | 105 ++++ contracts/alice/ImpactRegistry.sol | 141 +++++ contracts/alice/MockValidation.sol | 41 ++ contracts/alice/SimpleContractRegistry.sol | 13 + contracts/alice/Token.sol | 46 ++ migrations/1_initial_migration.js | 5 + migrations/2_alice_token.js | 17 + migrations/3_alice_charity_contracts.js | 42 ++ migrations/4_mock_validation.js | 7 + package.json | 18 + test/double-donations.js | 217 ++++++++ test/escape.js | 65 +++ test/impact.js | 577 +++++++++++++++++++++ test/mock-validation.js | 28 + test/multiple-donations.js | 99 ++++ test/single-donation.js | 138 +++++ truffle.js | 22 + 28 files changed, 2099 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 app/index.html create mode 100644 app/javascripts/app.js create mode 100644 app/stylesheets/AdminLTE.min.css create mode 100644 app/stylesheets/app.css create mode 100644 contracts/ContractProvider.sol create mode 100755 contracts/Migrations.sol create mode 100755 contracts/Owned.sol create mode 100644 contracts/alice/AliceToken.sol create mode 100644 contracts/alice/Charity.sol create mode 100644 contracts/alice/ImpactRegistry.sol create mode 100644 contracts/alice/MockValidation.sol create mode 100644 contracts/alice/SimpleContractRegistry.sol create mode 100644 contracts/alice/Token.sol create mode 100755 migrations/1_initial_migration.js create mode 100644 migrations/2_alice_token.js create mode 100644 migrations/3_alice_charity_contracts.js create mode 100644 migrations/4_mock_validation.js create mode 100644 package.json create mode 100644 test/double-donations.js create mode 100644 test/escape.js create mode 100644 test/impact.js create mode 100644 test/mock-validation.js create mode 100644 test/multiple-donations.js create mode 100644 test/single-donation.js create mode 100644 truffle.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4db89ec --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# Dependency directory +node_modules + +# Generated files +build +environments + +# IDE +.idea \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..9074570 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Alice Ltd. (Jakub Wojciechowski jakub@alice.si) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..28629c4 --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# Alice Smart Contracts + +This project is a collection of smart contracts that are used by . + +### Overview + +Smart contracts implement a Pay for success donation model. After a donor sends money to the campaign, a corresponding amount of Alice Tokens are being generated and credited to the Charity contract. Tokens are held in an escrow and are released only if a dedicated Validator confirms that the expected outcome has been achieved. Tokens are moved to the Beneficiary account after the validation is performed. Any outstanding tokens may be returned to donors and reused for future donations. + +### Installation +This project requires [node-js](https://github.com/nodejs/node) runtime and uses [truffle](https://github.com/trufflesuite/truffle) Ethereum smart contracts development framework. In order to run it, install truffle first: + + npm install -g truffle + +Then install all of the node-js dependencies + + npm install + +Connection to blockchain node is defined in truffle.js: + + networks: { + dev: { + network_id: "*", + gas: 4000000, + host: 'localhost', + port: '8545' + } + } + +We recommend using popular Ethereum test client [testrpc](https://github.com/ethereumjs/testrpc) as a default node: + + npm install -g ethereumjs-testrpc + +### Running tests + +To run all of the smart contract tests use following truffle command in your console: + + truffle test --network dev + +If you are using testrpc client remember to start it with sufficient number of test accounts: + + testrpc -a 100 + +### Demo dApp + +We created a demo dApp so you can interact and test smart contract in a visual environment rather than hacking console scripts. To run this mode please deploy the smart contracts to your blockchain network: + + truffle migrate --network dev + +... and then launch a demo server: + + truffle serve + +This demo dApp should be available at: http://localhost:8080/ a look like that: +![screenshot](https://s3.eu-west-2.amazonaws.com/alice-res/alice-dApp.png) + +## Contributions + +All comments, ideas for improvements and pull requests are welcomed. We want to improve the project based on feedback from the community. + +## License + +MIT License + +Copyright (c) 2017 Alice Ltd. (Jakub Wojciechowski jakub@alice.si) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/app/index.html b/app/index.html new file mode 100644 index 0000000..a3a039a --- /dev/null +++ b/app/index.html @@ -0,0 +1,122 @@ + + + + Alice + + + + + + + + + + +
+

+ Alice + Smart Contracts Demo +

+
+ +
+
+ +
+
+

Donor 1

+

LHC

+
+
+ +
+ Donate (20) + Reuse Unspent +
+ +
+
+

Donor 2

+

LHC

+
+
+ +
+ Donate (20) + Reuse Unspent +
+ +
+ + +
+ +
+
+

Contract

+

LHC

+

unspent accounts

+
+ Smart contract. Works as an escrow keeping donations before an outcome payment is authorized. +
+
+
+ +
+ Pay back +
+
+ +
+ +
+
+

Judge

+ +

External validator

+ +
+ Trusted organization (e.g. government agency) which authorize outcome payments. +
+
+
+ +
+ Tenancy agreement (200) + Employment contract (100) +
+
+
+ +
+
+

Beneficiary

+ +

LHC

+ +
+ Charity organization which use the money to provide help for homeless people. +
+ +
+
+ +
+
+
+ +
+ +
+
+

Blockchain logs

+
+ +
+ +
+ +
+ + + diff --git a/app/javascripts/app.js b/app/javascripts/app.js new file mode 100644 index 0000000..c6a5ea2 --- /dev/null +++ b/app/javascripts/app.js @@ -0,0 +1,154 @@ +var accounts; +var aliceAccount; +var donor1Account; +var donor2Account; +var beneficiaryAccount; +var judgeAccount; + +var TokenContract; +var CharityContract; +var ImpactContract; + +var balances = {}; + +function refreshBalance() { + showBalance(donor1Account, "balance_donor_1"); + showBalance(donor2Account, "balance_donor_2"); + showBalance(CharityContract.address, "balance_charity"); + showBalance(beneficiaryAccount, "balance_beneficiary"); +} + +function showBalance(account, element) { + TokenContract.balanceOf(account).then(function(value) { + var balance_element = document.getElementById(element); + balance_element.innerHTML = value.valueOf(); + balances[account] = value; + }); +} + +function showAllImpacts() { + showImpact('Tenancy'); + showImpact('Employment'); +} + +function showImpact(name) { + ImpactContract.getImpactCount.call(name).then(function(c) { + var count = c.valueOf(); + console.log(name + ' impact: ' + count); + for(var i=0; i < count; i++) { + (function(index) { + ImpactContract.getImpactDonor.call(name, i).then(function (address) { + console.log(name + ' address[' + index + ']: ' + address); + ImpactContract.getImpactValue.call(name, address).then(function (value) { + console.log(name + ' value[' + index + ']: ' + value); + }); + }); + })(i); + } + }); +} + +function donate(account, value) { + TokenContract.mint(CharityContract.address, value, {from: aliceAccount, gas: 1000000}).then(function(tx) { + return CharityContract.notify(account, value, {from: aliceAccount, gas: 1000000}) + .then(function () { + refreshBalance(); + return null; + }); + }) +} + +function reuseUnspent(account) { + TokenContract.transfer(CharityContract.address, balances[account], {from: account, gas: 1000000}) + .then(function(tx) { + return CharityContract.notify(account, balances[account], {from: aliceAccount, gas: 1000000}) + .then(function () { + console.log("Donated remaining funds: " + balances[account]); + refreshBalance(); + return null; + }); + }); +} + +function validateOutcome(name, value) { + return CharityContract.unlockOutcome(name, value, {from: judgeAccount, gas: 500000}).then(function() { + console.log("Validating outcome: " + name + " valued at: " + value); + refreshBalance(); + showAllImpacts(); + return linkImpact(name, value); + }); +} + +function payBackAll() { + payBack(donor1Account); + payBack(donor2Account); +} + +function payBack(account) { + CharityContract.payBack(account, {from: aliceAccount, gas: 1000000}).then(function() { + console.log("Payback done"); + refreshBalance(); + return null; + }); +} + +function linkImpact(name, outcomeValue) { + return ImpactContract.getImpactLinked.call(name, {from: aliceAccount}).then(function(val) { + console.log("Linked: " + val + " of: " + outcomeValue); + if (val < outcomeValue) { + console.log("Linking impact: " + val + " of: " + outcomeValue); + return ImpactContract.linkImpact(name, {from: aliceAccount, gas: 2000000}).then(function(tx) { + return linkImpact(name, outcomeValue); + }); + } + }); +} + +function mapAccounts(accounts) { + aliceAccount = accounts[0]; + judgeAccount = accounts[1]; + beneficiaryAccount = accounts[2]; + donor1Account = accounts[3]; + donor2Account = accounts[4]; +} + +function setupWeb3Filter() { + var filter = web3.eth.filter({}); + + filter.watch(function (error, log) { + console.log(log); + var logBox = document.getElementById("log"); + logBox.innerHTML += "Transaction hash: " + log.transactionHash + "
"; + }); +} + +window.onload = function() { + AliceToken.deployed() + .then(function (instance) { + TokenContract = instance; + console.log(TokenContract); + return Charity.deployed(); + }).then(function(instance) { + CharityContract = instance; + return ImpactRegistry.deployed(); + }).then(function(instance) { + ImpactContract = instance; + web3.eth.getAccounts(function(err, accs) { + if (err != null) { + alert("There was an error fetching your accounts."); + return; + } + + if (accs.length == 0) { + alert("Couldn't get any accounts! Make sure your Ethereum client is configured correctly."); + return; + } + + mapAccounts(accs); + refreshBalance(); + }); + }); + + setupWeb3Filter(); + +} diff --git a/app/stylesheets/AdminLTE.min.css b/app/stylesheets/AdminLTE.min.css new file mode 100644 index 0000000..863c490 --- /dev/null +++ b/app/stylesheets/AdminLTE.min.css @@ -0,0 +1,7 @@ +@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic);/*! + * AdminLTE v2.3.3 + * Author: Almsaeed Studio + * Website: Almsaeed Studio + * License: Open source - MIT + * Please visit http://opensource.org/licenses/MIT for more information +!*/html,body{min-height:100%}.layout-boxed html,.layout-boxed body{height:100%}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:'Source Sans Pro','Helvetica Neue',Helvetica,Arial,sans-serif;font-weight:400;overflow-x:hidden;overflow-y:auto}.wrapper{min-height:100%;position:relative;overflow:hidden}.wrapper:before,.wrapper:after{content:" ";display:table}.wrapper:after{clear:both}.layout-boxed .wrapper{max-width:1250px;margin:0 auto;min-height:100%;box-shadow:0 0 8px rgba(0,0,0,0.5);position:relative}.layout-boxed{background:url('../img/boxed-bg.jpg') repeat fixed}.content-wrapper,.right-side,.main-footer{-webkit-transition:-webkit-transform .3s ease-in-out,margin .3s ease-in-out;-moz-transition:-moz-transform .3s ease-in-out,margin .3s ease-in-out;-o-transition:-o-transform .3s ease-in-out,margin .3s ease-in-out;transition:transform .3s ease-in-out,margin .3s ease-in-out;margin-left:230px;z-index:820}.layout-top-nav .content-wrapper,.layout-top-nav .right-side,.layout-top-nav .main-footer{margin-left:0}@media (max-width:767px){.content-wrapper,.right-side,.main-footer{margin-left:0}}@media (min-width:768px){.sidebar-collapse .content-wrapper,.sidebar-collapse .right-side,.sidebar-collapse .main-footer{margin-left:0}}@media (max-width:767px){.sidebar-open .content-wrapper,.sidebar-open .right-side,.sidebar-open .main-footer{-webkit-transform:translate(230px, 0);-ms-transform:translate(230px, 0);-o-transform:translate(230px, 0);transform:translate(230px, 0)}}.content-wrapper,.right-side{min-height:100%;background-color:#ecf0f5;z-index:800}.main-footer{background:#fff;padding:15px;color:#444;border-top:1px solid #d2d6de}.fixed .main-header,.fixed .main-sidebar,.fixed .left-side{position:fixed}.fixed .main-header{top:0;right:0;left:0}.fixed .content-wrapper,.fixed .right-side{padding-top:50px}@media (max-width:767px){.fixed .content-wrapper,.fixed .right-side{padding-top:100px}}.fixed.layout-boxed .wrapper{max-width:100%}body.hold-transition .content-wrapper,body.hold-transition .right-side,body.hold-transition .main-footer,body.hold-transition .main-sidebar,body.hold-transition .left-side,body.hold-transition .main-header>.navbar,body.hold-transition .main-header .logo{-webkit-transition:none;-o-transition:none;transition:none}.content{min-height:250px;padding:15px;margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:'Source Sans Pro',sans-serif}a{color:#3c8dbc}a:hover,a:active,a:focus{outline:none;text-decoration:none;color:#72afd2}.page-header{margin:10px 0 20px 0;font-size:22px}.page-header>small{color:#666;display:block;margin-top:5px}.main-header{position:relative;max-height:100px;z-index:1030}.main-header>.navbar{-webkit-transition:margin-left .3s ease-in-out;-o-transition:margin-left .3s ease-in-out;transition:margin-left .3s ease-in-out;margin-bottom:0;margin-left:230px;border:none;min-height:50px;border-radius:0}.layout-top-nav .main-header>.navbar{margin-left:0}.main-header #navbar-search-input.form-control{background:rgba(255,255,255,0.2);border-color:transparent}.main-header #navbar-search-input.form-control:focus,.main-header #navbar-search-input.form-control:active{border-color:rgba(0,0,0,0.1);background:rgba(255,255,255,0.9)}.main-header #navbar-search-input.form-control::-moz-placeholder{color:#ccc;opacity:1}.main-header #navbar-search-input.form-control:-ms-input-placeholder{color:#ccc}.main-header #navbar-search-input.form-control::-webkit-input-placeholder{color:#ccc}.main-header .navbar-custom-menu,.main-header .navbar-right{float:right}@media (max-width:991px){.main-header .navbar-custom-menu a,.main-header .navbar-right a{color:inherit;background:transparent}}@media (max-width:767px){.main-header .navbar-right{float:none}.navbar-collapse .main-header .navbar-right{margin:7.5px -15px}.main-header .navbar-right>li{color:inherit;border:0}}.main-header .sidebar-toggle{float:left;background-color:transparent;background-image:none;padding:15px 15px;font-family:fontAwesome}.main-header .sidebar-toggle:before{content:"\f0c9"}.main-header .sidebar-toggle:hover{color:#fff}.main-header .sidebar-toggle:focus,.main-header .sidebar-toggle:active{background:transparent}.main-header .sidebar-toggle .icon-bar{display:none}.main-header .navbar .nav>li.user>a>.fa,.main-header .navbar .nav>li.user>a>.glyphicon,.main-header .navbar .nav>li.user>a>.ion{margin-right:5px}.main-header .navbar .nav>li>a>.label{position:absolute;top:9px;right:7px;text-align:center;font-size:9px;padding:2px 3px;line-height:.9}.main-header .logo{-webkit-transition:width .3s ease-in-out;-o-transition:width .3s ease-in-out;transition:width .3s ease-in-out;display:block;float:left;height:50px;font-size:20px;line-height:50px;text-align:center;width:230px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;padding:0 15px;font-weight:300;overflow:hidden}.main-header .logo .logo-lg{display:block}.main-header .logo .logo-mini{display:none}.main-header .navbar-brand{color:#fff}.content-header{position:relative;padding:15px 15px 0 15px}.content-header>h1{margin:0;font-size:24px}.content-header>h1>small{font-size:15px;display:inline-block;padding-left:4px;font-weight:300}.content-header>.breadcrumb{float:right;background:transparent;margin-top:0;margin-bottom:0;font-size:12px;padding:7px 5px;position:absolute;top:15px;right:10px;border-radius:2px}.content-header>.breadcrumb>li>a{color:#444;text-decoration:none;display:inline-block}.content-header>.breadcrumb>li>a>.fa,.content-header>.breadcrumb>li>a>.glyphicon,.content-header>.breadcrumb>li>a>.ion{margin-right:5px}.content-header>.breadcrumb>li+li:before{content:'>\00a0'}@media (max-width:991px){.content-header>.breadcrumb{position:relative;margin-top:5px;top:0;right:0;float:none;background:#d2d6de;padding-left:10px}.content-header>.breadcrumb li:before{color:#97a0b3}}.navbar-toggle{color:#fff;border:0;margin:0;padding:15px 15px}@media (max-width:991px){.navbar-custom-menu .navbar-nav>li{float:left}.navbar-custom-menu .navbar-nav{margin:0;float:left}.navbar-custom-menu .navbar-nav>li>a{padding-top:15px;padding-bottom:15px;line-height:20px}}@media (max-width:767px){.main-header{position:relative}.main-header .logo,.main-header .navbar{width:100%;float:none}.main-header .navbar{margin:0}.main-header .navbar-custom-menu{float:right}}@media (max-width:991px){.navbar-collapse.pull-left{float:none !important}.navbar-collapse.pull-left+.navbar-custom-menu{display:block;position:absolute;top:0;right:40px}}.main-sidebar,.left-side{position:absolute;top:0;left:0;padding-top:50px;min-height:100%;width:230px;z-index:810;-webkit-transition:-webkit-transform .3s ease-in-out,width .3s ease-in-out;-moz-transition:-moz-transform .3s ease-in-out,width .3s ease-in-out;-o-transition:-o-transform .3s ease-in-out,width .3s ease-in-out;transition:transform .3s ease-in-out,width .3s ease-in-out}@media (max-width:767px){.main-sidebar,.left-side{padding-top:100px}}@media (max-width:767px){.main-sidebar,.left-side{-webkit-transform:translate(-230px, 0);-ms-transform:translate(-230px, 0);-o-transform:translate(-230px, 0);transform:translate(-230px, 0)}}@media (min-width:768px){.sidebar-collapse .main-sidebar,.sidebar-collapse .left-side{-webkit-transform:translate(-230px, 0);-ms-transform:translate(-230px, 0);-o-transform:translate(-230px, 0);transform:translate(-230px, 0)}}@media (max-width:767px){.sidebar-open .main-sidebar,.sidebar-open .left-side{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}}.sidebar{padding-bottom:10px}.sidebar-form input:focus{border-color:transparent}.user-panel{position:relative;width:100%;padding:10px;overflow:hidden}.user-panel:before,.user-panel:after{content:" ";display:table}.user-panel:after{clear:both}.user-panel>.image>img{width:100%;max-width:45px;height:auto}.user-panel>.info{padding:5px 5px 5px 15px;line-height:1;position:absolute;left:55px}.user-panel>.info>p{font-weight:600;margin-bottom:9px}.user-panel>.info>a{text-decoration:none;padding-right:5px;margin-top:3px;font-size:11px}.user-panel>.info>a>.fa,.user-panel>.info>a>.ion,.user-panel>.info>a>.glyphicon{margin-right:3px}.sidebar-menu{list-style:none;margin:0;padding:0}.sidebar-menu>li{position:relative;margin:0;padding:0}.sidebar-menu>li>a{padding:12px 5px 12px 15px;display:block}.sidebar-menu>li>a>.fa,.sidebar-menu>li>a>.glyphicon,.sidebar-menu>li>a>.ion{width:20px}.sidebar-menu>li .label,.sidebar-menu>li .badge{margin-top:3px;margin-right:5px}.sidebar-menu li.header{padding:10px 25px 10px 15px;font-size:12px}.sidebar-menu li>a>.fa-angle-left{width:auto;height:auto;padding:0;margin-right:10px;margin-top:3px}.sidebar-menu li.active>a>.fa-angle-left{-webkit-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.sidebar-menu li.active>.treeview-menu{display:block}.sidebar-menu .treeview-menu{display:none;list-style:none;padding:0;margin:0;padding-left:5px}.sidebar-menu .treeview-menu .treeview-menu{padding-left:20px}.sidebar-menu .treeview-menu>li{margin:0}.sidebar-menu .treeview-menu>li>a{padding:5px 5px 5px 15px;display:block;font-size:14px}.sidebar-menu .treeview-menu>li>a>.fa,.sidebar-menu .treeview-menu>li>a>.glyphicon,.sidebar-menu .treeview-menu>li>a>.ion{width:20px}.sidebar-menu .treeview-menu>li>a>.fa-angle-left,.sidebar-menu .treeview-menu>li>a>.fa-angle-down{width:auto}@media (min-width:768px){.sidebar-mini.sidebar-collapse .content-wrapper,.sidebar-mini.sidebar-collapse .right-side,.sidebar-mini.sidebar-collapse .main-footer{margin-left:50px !important;z-index:840}.sidebar-mini.sidebar-collapse .main-sidebar{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0);width:50px !important;z-index:850}.sidebar-mini.sidebar-collapse .sidebar-menu>li{position:relative}.sidebar-mini.sidebar-collapse .sidebar-menu>li>a{margin-right:0}.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>span{border-top-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li:not(.treeview)>a>span{border-bottom-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu{padding-top:5px;padding-bottom:5px;border-bottom-right-radius:4px}.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>a>span:not(.pull-right),.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>.treeview-menu{display:block !important;position:absolute;width:180px;left:50px}.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>a>span{top:0;margin-left:-3px;padding:12px 5px 12px 20px;background-color:inherit}.sidebar-mini.sidebar-collapse .sidebar-menu>li:hover>.treeview-menu{top:44px;margin-left:0}.sidebar-mini.sidebar-collapse .main-sidebar .user-panel>.info,.sidebar-mini.sidebar-collapse .sidebar-form,.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>span,.sidebar-mini.sidebar-collapse .sidebar-menu>li>.treeview-menu,.sidebar-mini.sidebar-collapse .sidebar-menu>li>a>.pull-right,.sidebar-mini.sidebar-collapse .sidebar-menu li.header{display:none !important;-webkit-transform:translateZ(0)}.sidebar-mini.sidebar-collapse .main-header .logo{width:50px}.sidebar-mini.sidebar-collapse .main-header .logo>.logo-mini{display:block;margin-left:-15px;margin-right:-15px;font-size:18px}.sidebar-mini.sidebar-collapse .main-header .logo>.logo-lg{display:none}.sidebar-mini.sidebar-collapse .main-header .navbar{margin-left:50px}}.sidebar-menu,.main-sidebar .user-panel,.sidebar-menu>li.header{white-space:nowrap;overflow:hidden}.sidebar-menu:hover{overflow:visible}.sidebar-form,.sidebar-menu>li.header{overflow:hidden;text-overflow:clip}.sidebar-menu li>a{position:relative}.sidebar-menu li>a>.pull-right{position:absolute;right:10px;top:50%;margin-top:-7px}.control-sidebar-bg{position:fixed;z-index:1000;bottom:0}.control-sidebar-bg,.control-sidebar{top:0;right:-230px;width:230px;-webkit-transition:right .3s ease-in-out;-o-transition:right .3s ease-in-out;transition:right .3s ease-in-out}.control-sidebar{position:absolute;padding-top:50px;z-index:1010}@media (max-width:768px){.control-sidebar{padding-top:100px}}.control-sidebar>.tab-content{padding:10px 15px}.control-sidebar.control-sidebar-open,.control-sidebar.control-sidebar-open+.control-sidebar-bg{right:0}.control-sidebar-open .control-sidebar-bg,.control-sidebar-open .control-sidebar{right:0}@media (min-width:768px){.control-sidebar-open .content-wrapper,.control-sidebar-open .right-side,.control-sidebar-open .main-footer{margin-right:230px}}.nav-tabs.control-sidebar-tabs>li:first-of-type>a,.nav-tabs.control-sidebar-tabs>li:first-of-type>a:hover,.nav-tabs.control-sidebar-tabs>li:first-of-type>a:focus{border-left-width:0}.nav-tabs.control-sidebar-tabs>li>a{border-radius:0}.nav-tabs.control-sidebar-tabs>li>a,.nav-tabs.control-sidebar-tabs>li>a:hover{border-top:none;border-right:none;border-left:1px solid transparent;border-bottom:1px solid transparent}.nav-tabs.control-sidebar-tabs>li>a .icon{font-size:16px}.nav-tabs.control-sidebar-tabs>li.active>a,.nav-tabs.control-sidebar-tabs>li.active>a:hover,.nav-tabs.control-sidebar-tabs>li.active>a:focus,.nav-tabs.control-sidebar-tabs>li.active>a:active{border-top:none;border-right:none;border-bottom:none}@media (max-width:768px){.nav-tabs.control-sidebar-tabs{display:table}.nav-tabs.control-sidebar-tabs>li{display:table-cell}}.control-sidebar-heading{font-weight:400;font-size:16px;padding:10px 0;margin-bottom:10px}.control-sidebar-subheading{display:block;font-weight:400;font-size:14px}.control-sidebar-menu{list-style:none;padding:0;margin:0 -15px}.control-sidebar-menu>li>a{display:block;padding:10px 15px}.control-sidebar-menu>li>a:before,.control-sidebar-menu>li>a:after{content:" ";display:table}.control-sidebar-menu>li>a:after{clear:both}.control-sidebar-menu>li>a>.control-sidebar-subheading{margin-top:0}.control-sidebar-menu .menu-icon{float:left;width:35px;height:35px;border-radius:50%;text-align:center;line-height:35px}.control-sidebar-menu .menu-info{margin-left:45px;margin-top:3px}.control-sidebar-menu .menu-info>.control-sidebar-subheading{margin:0}.control-sidebar-menu .menu-info>p{margin:0;font-size:11px}.control-sidebar-menu .progress{margin:0}.control-sidebar-dark{color:#b8c7ce}.control-sidebar-dark,.control-sidebar-dark+.control-sidebar-bg{background:#222d32}.control-sidebar-dark .nav-tabs.control-sidebar-tabs{border-bottom:#1c2529}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a{background:#181f23;color:#b8c7ce}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:focus{border-left-color:#141a1d;border-bottom-color:#141a1d}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:focus,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:active{background:#1c2529}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li>a:hover{color:#fff}.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:hover,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:focus,.control-sidebar-dark .nav-tabs.control-sidebar-tabs>li.active>a:active{background:#222d32;color:#fff}.control-sidebar-dark .control-sidebar-heading,.control-sidebar-dark .control-sidebar-subheading{color:#fff}.control-sidebar-dark .control-sidebar-menu>li>a:hover{background:#1e282c}.control-sidebar-dark .control-sidebar-menu>li>a .menu-info>p{color:#b8c7ce}.control-sidebar-light{color:#5e5e5e}.control-sidebar-light,.control-sidebar-light+.control-sidebar-bg{background:#f9fafc;border-left:1px solid #d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs{border-bottom:#d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a{background:#e8ecf4;color:#444}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:focus{border-left-color:#d2d6de;border-bottom-color:#d2d6de}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:focus,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li>a:active{background:#eff1f7}.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:hover,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:focus,.control-sidebar-light .nav-tabs.control-sidebar-tabs>li.active>a:active{background:#f9fafc;color:#111}.control-sidebar-light .control-sidebar-heading,.control-sidebar-light .control-sidebar-subheading{color:#111}.control-sidebar-light .control-sidebar-menu{margin-left:-14px}.control-sidebar-light .control-sidebar-menu>li>a:hover{background:#f4f4f5}.control-sidebar-light .control-sidebar-menu>li>a .menu-info>p{color:#5e5e5e}.dropdown-menu{box-shadow:none;border-color:#eee}.dropdown-menu>li>a{color:#777}.dropdown-menu>li>a>.glyphicon,.dropdown-menu>li>a>.fa,.dropdown-menu>li>a>.ion{margin-right:10px}.dropdown-menu>li>a:hover{background-color:#e1e3e9;color:#333}.dropdown-menu>.divider{background-color:#eee}.navbar-nav>.notifications-menu>.dropdown-menu,.navbar-nav>.messages-menu>.dropdown-menu,.navbar-nav>.tasks-menu>.dropdown-menu{width:280px;padding:0 0 0 0;margin:0;top:100%}.navbar-nav>.notifications-menu>.dropdown-menu>li,.navbar-nav>.messages-menu>.dropdown-menu>li,.navbar-nav>.tasks-menu>.dropdown-menu>li{position:relative}.navbar-nav>.notifications-menu>.dropdown-menu>li.header,.navbar-nav>.messages-menu>.dropdown-menu>li.header,.navbar-nav>.tasks-menu>.dropdown-menu>li.header{border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0;background-color:#ffffff;padding:7px 10px;border-bottom:1px solid #f4f4f4;color:#444444;font-size:14px}.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px;font-size:12px;background-color:#fff;padding:7px 10px;border-bottom:1px solid #eeeeee;color:#444 !important;text-align:center}@media (max-width:991px){.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a{background:#fff !important;color:#444 !important}}.navbar-nav>.notifications-menu>.dropdown-menu>li.footer>a:hover,.navbar-nav>.messages-menu>.dropdown-menu>li.footer>a:hover,.navbar-nav>.tasks-menu>.dropdown-menu>li.footer>a:hover{text-decoration:none;font-weight:normal}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu,.navbar-nav>.messages-menu>.dropdown-menu>li .menu,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu{max-height:200px;margin:0;padding:0;list-style:none;overflow-x:hidden}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a{display:block;white-space:nowrap;border-bottom:1px solid #f4f4f4}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a:hover,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:hover,.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a:hover{background:#f4f4f4;text-decoration:none}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a{color:#444444;overflow:hidden;text-overflow:ellipsis;padding:10px}.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.glyphicon,.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.fa,.navbar-nav>.notifications-menu>.dropdown-menu>li .menu>li>a>.ion{width:20px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a{margin:0;padding:10px 10px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>div>img{margin:auto 10px auto auto;width:40px;height:40px}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>h4{padding:0;margin:0 0 0 45px;color:#444444;font-size:15px;position:relative}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>h4>small{color:#999999;font-size:10px;position:absolute;top:0;right:0}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a>p{margin:0 0 0 45px;font-size:12px;color:#888888}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:before,.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:after{content:" ";display:table}.navbar-nav>.messages-menu>.dropdown-menu>li .menu>li>a:after{clear:both}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a{padding:10px}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a>h3{font-size:14px;padding:0;margin:0 0 10px 0;color:#666666}.navbar-nav>.tasks-menu>.dropdown-menu>li .menu>li>a>.progress{padding:0;margin:0}.navbar-nav>.user-menu>.dropdown-menu{border-top-right-radius:0;border-top-left-radius:0;padding:1px 0 0 0;border-top-width:0;width:280px}.navbar-nav>.user-menu>.dropdown-menu,.navbar-nav>.user-menu>.dropdown-menu>.user-body{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.navbar-nav>.user-menu>.dropdown-menu>li.user-header{height:175px;padding:10px;text-align:center}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>img{z-index:5;height:90px;width:90px;border:3px solid;border-color:transparent;border-color:rgba(255,255,255,0.2)}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>p{z-index:5;color:#fff;color:rgba(255,255,255,0.8);font-size:17px;margin-top:10px}.navbar-nav>.user-menu>.dropdown-menu>li.user-header>p>small{display:block;font-size:12px}.navbar-nav>.user-menu>.dropdown-menu>.user-body{padding:15px;border-bottom:1px solid #f4f4f4;border-top:1px solid #dddddd}.navbar-nav>.user-menu>.dropdown-menu>.user-body:before,.navbar-nav>.user-menu>.dropdown-menu>.user-body:after{content:" ";display:table}.navbar-nav>.user-menu>.dropdown-menu>.user-body:after{clear:both}.navbar-nav>.user-menu>.dropdown-menu>.user-body a{color:#444 !important}@media (max-width:991px){.navbar-nav>.user-menu>.dropdown-menu>.user-body a{background:#fff !important;color:#444 !important}}.navbar-nav>.user-menu>.dropdown-menu>.user-footer{background-color:#f9f9f9;padding:10px}.navbar-nav>.user-menu>.dropdown-menu>.user-footer:before,.navbar-nav>.user-menu>.dropdown-menu>.user-footer:after{content:" ";display:table}.navbar-nav>.user-menu>.dropdown-menu>.user-footer:after{clear:both}.navbar-nav>.user-menu>.dropdown-menu>.user-footer .btn-default{color:#666666}@media (max-width:991px){.navbar-nav>.user-menu>.dropdown-menu>.user-footer .btn-default:hover{background-color:#f9f9f9}}.navbar-nav>.user-menu .user-image{float:left;width:25px;height:25px;border-radius:50%;margin-right:10px;margin-top:-2px}@media (max-width:767px){.navbar-nav>.user-menu .user-image{float:none;margin-right:0;margin-top:-8px;line-height:10px}}.open:not(.dropup)>.animated-dropdown-menu{backface-visibility:visible !important;-webkit-animation:flipInX .7s both;-o-animation:flipInX .7s both;animation:flipInX .7s both}@keyframes flipInX{0%{transform:perspective(400px) rotate3d(1, 0, 0, 90deg);transition-timing-function:ease-in;opacity:0}40%{transform:perspective(400px) rotate3d(1, 0, 0, -20deg);transition-timing-function:ease-in}60%{transform:perspective(400px) rotate3d(1, 0, 0, 10deg);opacity:1}80%{transform:perspective(400px) rotate3d(1, 0, 0, -5deg)}100%{transform:perspective(400px)}}@-webkit-keyframes flipInX{0%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, 90deg);-webkit-transition-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, -20deg);-webkit-transition-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, 10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotate3d(1, 0, 0, -5deg)}100%{-webkit-transform:perspective(400px)}}.navbar-custom-menu>.navbar-nav>li{position:relative}.navbar-custom-menu>.navbar-nav>li>.dropdown-menu{position:absolute;right:0;left:auto}@media (max-width:991px){.navbar-custom-menu>.navbar-nav{float:right}.navbar-custom-menu>.navbar-nav>li{position:static}.navbar-custom-menu>.navbar-nav>li>.dropdown-menu{position:absolute;right:5%;left:auto;border:1px solid #ddd;background:#fff}}.form-control{border-radius:0;box-shadow:none;border-color:#d2d6de}.form-control:focus{border-color:#3c8dbc;box-shadow:none}.form-control::-moz-placeholder,.form-control:-ms-input-placeholder,.form-control::-webkit-input-placeholder{color:#bbb;opacity:1}.form-control:not(select){-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-group.has-success label{color:#00a65a}.form-group.has-success .form-control{border-color:#00a65a;box-shadow:none}.form-group.has-success .help-block{color:#00a65a}.form-group.has-warning label{color:#f39c12}.form-group.has-warning .form-control{border-color:#f39c12;box-shadow:none}.form-group.has-warning .help-block{color:#f39c12}.form-group.has-error label{color:#dd4b39}.form-group.has-error .form-control{border-color:#dd4b39;box-shadow:none}.form-group.has-error .help-block{color:#dd4b39}.input-group .input-group-addon{border-radius:0;border-color:#d2d6de;background-color:#fff}.btn-group-vertical .btn.btn-flat:first-of-type,.btn-group-vertical .btn.btn-flat:last-of-type{border-radius:0}.icheck>label{padding-left:0}.form-control-feedback.fa{line-height:34px}.input-lg+.form-control-feedback.fa,.input-group-lg+.form-control-feedback.fa,.form-group-lg .form-control+.form-control-feedback.fa{line-height:46px}.input-sm+.form-control-feedback.fa,.input-group-sm+.form-control-feedback.fa,.form-group-sm .form-control+.form-control-feedback.fa{line-height:30px}.progress,.progress>.progress-bar{-webkit-box-shadow:none;box-shadow:none}.progress,.progress>.progress-bar,.progress .progress-bar,.progress>.progress-bar .progress-bar{border-radius:1px}.progress.sm,.progress-sm{height:10px}.progress.sm,.progress-sm,.progress.sm .progress-bar,.progress-sm .progress-bar{border-radius:1px}.progress.xs,.progress-xs{height:7px}.progress.xs,.progress-xs,.progress.xs .progress-bar,.progress-xs .progress-bar{border-radius:1px}.progress.xxs,.progress-xxs{height:3px}.progress.xxs,.progress-xxs,.progress.xxs .progress-bar,.progress-xxs .progress-bar{border-radius:1px}.progress.vertical{position:relative;width:30px;height:200px;display:inline-block;margin-right:10px}.progress.vertical>.progress-bar{width:100%;position:absolute;bottom:0}.progress.vertical.sm,.progress.vertical.progress-sm{width:20px}.progress.vertical.xs,.progress.vertical.progress-xs{width:10px}.progress.vertical.xxs,.progress.vertical.progress-xxs{width:3px}.progress-group .progress-text{font-weight:600}.progress-group .progress-number{float:right}.table tr>td .progress{margin:0}.progress-bar-light-blue,.progress-bar-primary{background-color:#3c8dbc}.progress-striped .progress-bar-light-blue,.progress-striped .progress-bar-primary{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-green,.progress-bar-success{background-color:#00a65a}.progress-striped .progress-bar-green,.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-aqua,.progress-bar-info{background-color:#00c0ef}.progress-striped .progress-bar-aqua,.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-yellow,.progress-bar-warning{background-color:#f39c12}.progress-striped .progress-bar-yellow,.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-red,.progress-bar-danger{background-color:#dd4b39}.progress-striped .progress-bar-red,.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.small-box{border-radius:2px;position:relative;display:block;margin-bottom:20px;box-shadow:0 1px 1px rgba(0,0,0,0.1)}.small-box>.inner{padding:10px}.small-box>.small-box-footer{position:relative;text-align:center;padding:3px 0;color:#fff;color:rgba(255,255,255,0.8);display:block;z-index:10;background:rgba(0,0,0,0.1);text-decoration:none}.small-box>.small-box-footer:hover{color:#fff;background:rgba(0,0,0,0.15)}.small-box h3{font-size:38px;font-weight:bold;margin:0 0 10px 0;white-space:nowrap;padding:0}.small-box p{font-size:15px}.small-box p>small{display:block;color:#f9f9f9;font-size:13px;margin-top:5px}.small-box h3,.small-box p{z-index:5}.small-box .icon{-webkit-transition:all .3s linear;-o-transition:all .3s linear;transition:all .3s linear;position:absolute;top:-10px;right:10px;z-index:0;font-size:90px;color:rgba(0,0,0,0.15)}.small-box:hover{text-decoration:none;color:#f9f9f9}.small-box:hover .icon{font-size:95px}@media (max-width:767px){.small-box{text-align:center}.small-box .icon{display:none}.small-box p{font-size:12px}}.box{position:relative;border-radius:3px;background:#ffffff;border-top:3px solid #d2d6de;margin-bottom:20px;width:100%;box-shadow:0 1px 1px rgba(0,0,0,0.1)}.box.box-primary{border-top-color:#3c8dbc}.box.box-info{border-top-color:#00c0ef}.box.box-danger{border-top-color:#dd4b39}.box.box-warning{border-top-color:#f39c12}.box.box-success{border-top-color:#00a65a}.box.box-default{border-top-color:#d2d6de}.box.collapsed-box .box-body,.box.collapsed-box .box-footer{display:none}.box .nav-stacked>li{border-bottom:1px solid #f4f4f4;margin:0}.box .nav-stacked>li:last-of-type{border-bottom:none}.box.height-control .box-body{max-height:300px;overflow:auto}.box .border-right{border-right:1px solid #f4f4f4}.box .border-left{border-left:1px solid #f4f4f4}.box.box-solid{border-top:0}.box.box-solid>.box-header .btn.btn-default{background:transparent}.box.box-solid>.box-header .btn:hover,.box.box-solid>.box-header a:hover{background:rgba(0,0,0,0.1)}.box.box-solid.box-default{border:1px solid #d2d6de}.box.box-solid.box-default>.box-header{color:#444;background:#d2d6de;background-color:#d2d6de}.box.box-solid.box-default>.box-header a,.box.box-solid.box-default>.box-header .btn{color:#444}.box.box-solid.box-primary{border:1px solid #3c8dbc}.box.box-solid.box-primary>.box-header{color:#fff;background:#3c8dbc;background-color:#3c8dbc}.box.box-solid.box-primary>.box-header a,.box.box-solid.box-primary>.box-header .btn{color:#fff}.box.box-solid.box-info{border:1px solid #00c0ef}.box.box-solid.box-info>.box-header{color:#fff;background:#00c0ef;background-color:#00c0ef}.box.box-solid.box-info>.box-header a,.box.box-solid.box-info>.box-header .btn{color:#fff}.box.box-solid.box-danger{border:1px solid #dd4b39}.box.box-solid.box-danger>.box-header{color:#fff;background:#dd4b39;background-color:#dd4b39}.box.box-solid.box-danger>.box-header a,.box.box-solid.box-danger>.box-header .btn{color:#fff}.box.box-solid.box-warning{border:1px solid #f39c12}.box.box-solid.box-warning>.box-header{color:#fff;background:#f39c12;background-color:#f39c12}.box.box-solid.box-warning>.box-header a,.box.box-solid.box-warning>.box-header .btn{color:#fff}.box.box-solid.box-success{border:1px solid #00a65a}.box.box-solid.box-success>.box-header{color:#fff;background:#00a65a;background-color:#00a65a}.box.box-solid.box-success>.box-header a,.box.box-solid.box-success>.box-header .btn{color:#fff}.box.box-solid>.box-header>.box-tools .btn{border:0;box-shadow:none}.box.box-solid[class*='bg']>.box-header{color:#fff}.box .box-group>.box{margin-bottom:5px}.box .knob-label{text-align:center;color:#333;font-weight:100;font-size:12px;margin-bottom:0.3em}.box>.overlay,.overlay-wrapper>.overlay,.box>.loading-img,.overlay-wrapper>.loading-img{position:absolute;top:0;left:0;width:100%;height:100%}.box .overlay,.overlay-wrapper .overlay{z-index:50;background:rgba(255,255,255,0.7);border-radius:3px}.box .overlay>.fa,.overlay-wrapper .overlay>.fa{position:absolute;top:50%;left:50%;margin-left:-15px;margin-top:-15px;color:#000;font-size:30px}.box .overlay.dark,.overlay-wrapper .overlay.dark{background:rgba(0,0,0,0.5)}.box-header:before,.box-body:before,.box-footer:before,.box-header:after,.box-body:after,.box-footer:after{content:" ";display:table}.box-header:after,.box-body:after,.box-footer:after{clear:both}.box-header{color:#444;display:block;padding:10px;position:relative}.box-header.with-border{border-bottom:1px solid #f4f4f4}.collapsed-box .box-header.with-border{border-bottom:none}.box-header>.fa,.box-header>.glyphicon,.box-header>.ion,.box-header .box-title{display:inline-block;font-size:18px;margin:0;line-height:1}.box-header>.fa,.box-header>.glyphicon,.box-header>.ion{margin-right:5px}.box-header>.box-tools{position:absolute;right:10px;top:5px}.box-header>.box-tools [data-toggle="tooltip"]{position:relative}.box-header>.box-tools.pull-right .dropdown-menu{right:0;left:auto}.btn-box-tool{padding:5px;font-size:12px;background:transparent;color:#97a0b3}.open .btn-box-tool,.btn-box-tool:hover{color:#606c84}.btn-box-tool.btn:active{box-shadow:none}.box-body{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px;padding:10px}.no-header .box-body{border-top-right-radius:3px;border-top-left-radius:3px}.box-body>.table{margin-bottom:0}.box-body .fc{margin-top:5px}.box-body .full-width-chart{margin:-19px}.box-body.no-padding .full-width-chart{margin:-9px}.box-body .box-pane{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:3px}.box-body .box-pane-right{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:0}.box-footer{border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px;border-top:1px solid #f4f4f4;padding:10px;background-color:#fff}.chart-legend{margin:10px 0}@media (max-width:991px){.chart-legend>li{float:left;margin-right:10px}}.box-comments{background:#f7f7f7}.box-comments .box-comment{padding:8px 0;border-bottom:1px solid #eee}.box-comments .box-comment:before,.box-comments .box-comment:after{content:" ";display:table}.box-comments .box-comment:after{clear:both}.box-comments .box-comment:last-of-type{border-bottom:0}.box-comments .box-comment:first-of-type{padding-top:0}.box-comments .box-comment img{float:left}.box-comments .comment-text{margin-left:40px;color:#555}.box-comments .username{color:#444;display:block;font-weight:600}.box-comments .text-muted{font-weight:400;font-size:12px}.todo-list{margin:0;padding:0;list-style:none;overflow:auto}.todo-list>li{border-radius:2px;padding:10px;background:#f4f4f4;margin-bottom:2px;border-left:2px solid #e6e7e8;color:#444}.todo-list>li:last-of-type{margin-bottom:0}.todo-list>li>input[type='checkbox']{margin:0 10px 0 5px}.todo-list>li .text{display:inline-block;margin-left:5px;font-weight:600}.todo-list>li .label{margin-left:10px;font-size:9px}.todo-list>li .tools{display:none;float:right;color:#dd4b39}.todo-list>li .tools>.fa,.todo-list>li .tools>.glyphicon,.todo-list>li .tools>.ion{margin-right:5px;cursor:pointer}.todo-list>li:hover .tools{display:inline-block}.todo-list>li.done{color:#999}.todo-list>li.done .text{text-decoration:line-through;font-weight:500}.todo-list>li.done .label{background:#d2d6de !important}.todo-list .danger{border-left-color:#dd4b39}.todo-list .warning{border-left-color:#f39c12}.todo-list .info{border-left-color:#00c0ef}.todo-list .success{border-left-color:#00a65a}.todo-list .primary{border-left-color:#3c8dbc}.todo-list .handle{display:inline-block;cursor:move;margin:0 5px}.chat{padding:5px 20px 5px 10px}.chat .item{margin-bottom:10px}.chat .item:before,.chat .item:after{content:" ";display:table}.chat .item:after{clear:both}.chat .item>img{width:40px;height:40px;border:2px solid transparent;border-radius:50%}.chat .item>.online{border:2px solid #00a65a}.chat .item>.offline{border:2px solid #dd4b39}.chat .item>.message{margin-left:55px;margin-top:-40px}.chat .item>.message>.name{display:block;font-weight:600}.chat .item>.attachment{border-radius:3px;background:#f4f4f4;margin-left:65px;margin-right:15px;padding:10px}.chat .item>.attachment>h4{margin:0 0 5px 0;font-weight:600;font-size:14px}.chat .item>.attachment>p,.chat .item>.attachment>.filename{font-weight:600;font-size:13px;font-style:italic;margin:0}.chat .item>.attachment:before,.chat .item>.attachment:after{content:" ";display:table}.chat .item>.attachment:after{clear:both}.box-input{max-width:200px}.modal .panel-body{color:#444}.info-box{display:block;min-height:90px;background:#fff;width:100%;box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:2px;margin-bottom:15px}.info-box small{font-size:14px}.info-box .progress{background:rgba(0,0,0,0.2);margin:5px -10px 5px -10px;height:2px}.info-box .progress,.info-box .progress .progress-bar{border-radius:0}.info-box .progress .progress-bar{background:#fff}.info-box-icon{border-top-left-radius:2px;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:2px;display:block;float:left;height:90px;width:90px;text-align:center;font-size:45px;line-height:90px;background:rgba(0,0,0,0.2)}.info-box-icon>img{max-width:100%}.info-box-content{padding:5px 10px;margin-left:90px}.info-box-number{display:block;font-weight:bold;font-size:18px}.progress-description,.info-box-text{display:block;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.info-box-text{text-transform:uppercase}.info-box-more{display:block}.progress-description{margin:0}.timeline{position:relative;margin:0 0 30px 0;padding:0;list-style:none}.timeline:before{content:'';position:absolute;top:0;bottom:0;width:4px;background:#ddd;left:31px;margin:0;border-radius:2px}.timeline>li{position:relative;margin-right:10px;margin-bottom:15px}.timeline>li:before,.timeline>li:after{content:" ";display:table}.timeline>li:after{clear:both}.timeline>li>.timeline-item{-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.1);box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px;margin-top:0;background:#fff;color:#444;margin-left:60px;margin-right:15px;padding:0;position:relative}.timeline>li>.timeline-item>.time{color:#999;float:right;padding:10px;font-size:12px}.timeline>li>.timeline-item>.timeline-header{margin:0;color:#555;border-bottom:1px solid #f4f4f4;padding:10px;font-size:16px;line-height:1.1}.timeline>li>.timeline-item>.timeline-header>a{font-weight:600}.timeline>li>.timeline-item>.timeline-body,.timeline>li>.timeline-item>.timeline-footer{padding:10px}.timeline>li>.fa,.timeline>li>.glyphicon,.timeline>li>.ion{width:30px;height:30px;font-size:15px;line-height:30px;position:absolute;color:#666;background:#d2d6de;border-radius:50%;text-align:center;left:18px;top:0}.timeline>.time-label>span{font-weight:600;padding:5px;display:inline-block;background-color:#fff;border-radius:4px}.timeline-inverse>li>.timeline-item{background:#f0f0f0;border:1px solid #ddd;-webkit-box-shadow:none;box-shadow:none}.timeline-inverse>li>.timeline-item>.timeline-header{border-bottom-color:#ddd}.btn{border-radius:3px;-webkit-box-shadow:none;box-shadow:none;border:1px solid transparent}.btn.uppercase{text-transform:uppercase}.btn.btn-flat{border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;border-width:1px}.btn:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:focus{outline:none}.btn.btn-file{position:relative;overflow:hidden}.btn.btn-file>input[type='file']{position:absolute;top:0;right:0;min-width:100%;min-height:100%;font-size:100px;text-align:right;opacity:0;filter:alpha(opacity=0);outline:none;background:white;cursor:inherit;display:block}.btn-default{background-color:#f4f4f4;color:#444;border-color:#ddd}.btn-default:hover,.btn-default:active,.btn-default.hover{background-color:#e7e7e7}.btn-primary{background-color:#3c8dbc;border-color:#367fa9}.btn-primary:hover,.btn-primary:active,.btn-primary.hover{background-color:#367fa9}.btn-success{background-color:#00a65a;border-color:#008d4c}.btn-success:hover,.btn-success:active,.btn-success.hover{background-color:#008d4c}.btn-info{background-color:#00c0ef;border-color:#00acd6}.btn-info:hover,.btn-info:active,.btn-info.hover{background-color:#00acd6}.btn-danger{background-color:#dd4b39;border-color:#d73925}.btn-danger:hover,.btn-danger:active,.btn-danger.hover{background-color:#d73925}.btn-warning{background-color:#f39c12;border-color:#e08e0b}.btn-warning:hover,.btn-warning:active,.btn-warning.hover{background-color:#e08e0b}.btn-outline{border:1px solid #fff;background:transparent;color:#fff}.btn-outline:hover,.btn-outline:focus,.btn-outline:active{color:rgba(255,255,255,0.7);border-color:rgba(255,255,255,0.7)}.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn[class*='bg-']:hover{-webkit-box-shadow:inset 0 0 100px rgba(0,0,0,0.2);box-shadow:inset 0 0 100px rgba(0,0,0,0.2)}.btn-app{border-radius:3px;position:relative;padding:15px 5px;margin:0 0 10px 10px;min-width:80px;height:60px;text-align:center;color:#666;border:1px solid #ddd;background-color:#f4f4f4;font-size:12px}.btn-app>.fa,.btn-app>.glyphicon,.btn-app>.ion{font-size:20px;display:block}.btn-app:hover{background:#f4f4f4;color:#444;border-color:#aaa}.btn-app:active,.btn-app:focus{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-app>.badge{position:absolute;top:-3px;right:-10px;font-size:10px;font-weight:400}.callout{border-radius:3px;margin:0 0 20px 0;padding:15px 30px 15px 15px;border-left:5px solid #eee}.callout a{color:#fff;text-decoration:underline}.callout a:hover{color:#eee}.callout h4{margin-top:0;font-weight:600}.callout p:last-child{margin-bottom:0}.callout code,.callout .highlight{background-color:#fff}.callout.callout-danger{border-color:#c23321}.callout.callout-warning{border-color:#c87f0a}.callout.callout-info{border-color:#0097bc}.callout.callout-success{border-color:#00733e}.alert{border-radius:3px}.alert h4{font-weight:600}.alert .icon{margin-right:10px}.alert .close{color:#000;opacity:.2;filter:alpha(opacity=20)}.alert .close:hover{opacity:.5;filter:alpha(opacity=50)}.alert a{color:#fff;text-decoration:underline}.alert-success{border-color:#008d4c}.alert-danger,.alert-error{border-color:#d73925}.alert-warning{border-color:#e08e0b}.alert-info{border-color:#00acd6}.nav>li>a:hover,.nav>li>a:active,.nav>li>a:focus{color:#444;background:#f7f7f7}.nav-pills>li>a{border-radius:0;border-top:3px solid transparent;color:#444}.nav-pills>li>a>.fa,.nav-pills>li>a>.glyphicon,.nav-pills>li>a>.ion{margin-right:5px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{border-top-color:#3c8dbc}.nav-pills>li.active>a{font-weight:600}.nav-stacked>li>a{border-radius:0;border-top:0;border-left:3px solid transparent;color:#444}.nav-stacked>li.active>a,.nav-stacked>li.active>a:hover{background:transparent;color:#444;border-top:0;border-left-color:#3c8dbc}.nav-stacked>li.header{border-bottom:1px solid #ddd;color:#777;margin-bottom:10px;padding:5px 10px;text-transform:uppercase}.nav-tabs-custom{margin-bottom:20px;background:#fff;box-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px}.nav-tabs-custom>.nav-tabs{margin:0;border-bottom-color:#f4f4f4;border-top-right-radius:3px;border-top-left-radius:3px}.nav-tabs-custom>.nav-tabs>li{border-top:3px solid transparent;margin-bottom:-2px;margin-right:5px}.nav-tabs-custom>.nav-tabs>li>a{color:#444;border-radius:0}.nav-tabs-custom>.nav-tabs>li>a.text-muted{color:#999}.nav-tabs-custom>.nav-tabs>li>a,.nav-tabs-custom>.nav-tabs>li>a:hover{background:transparent;margin:0}.nav-tabs-custom>.nav-tabs>li>a:hover{color:#999}.nav-tabs-custom>.nav-tabs>li:not(.active)>a:hover,.nav-tabs-custom>.nav-tabs>li:not(.active)>a:focus,.nav-tabs-custom>.nav-tabs>li:not(.active)>a:active{border-color:transparent}.nav-tabs-custom>.nav-tabs>li.active{border-top-color:#3c8dbc}.nav-tabs-custom>.nav-tabs>li.active>a,.nav-tabs-custom>.nav-tabs>li.active:hover>a{background-color:#fff;color:#444}.nav-tabs-custom>.nav-tabs>li.active>a{border-top-color:transparent;border-left-color:#f4f4f4;border-right-color:#f4f4f4}.nav-tabs-custom>.nav-tabs>li:first-of-type{margin-left:0}.nav-tabs-custom>.nav-tabs>li:first-of-type.active>a{border-left-color:transparent}.nav-tabs-custom>.nav-tabs.pull-right{float:none !important}.nav-tabs-custom>.nav-tabs.pull-right>li{float:right}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type{margin-right:0}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type>a{border-left-width:1px}.nav-tabs-custom>.nav-tabs.pull-right>li:first-of-type.active>a{border-left-color:#f4f4f4;border-right-color:transparent}.nav-tabs-custom>.nav-tabs>li.header{line-height:35px;padding:0 10px;font-size:20px;color:#444}.nav-tabs-custom>.nav-tabs>li.header>.fa,.nav-tabs-custom>.nav-tabs>li.header>.glyphicon,.nav-tabs-custom>.nav-tabs>li.header>.ion{margin-right:5px}.nav-tabs-custom>.tab-content{background:#fff;padding:10px;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.nav-tabs-custom .dropdown.open>a:active,.nav-tabs-custom .dropdown.open>a:focus{background:transparent;color:#999}.nav-tabs-custom.tab-primary>.nav-tabs>li.active{border-top-color:#3c8dbc}.nav-tabs-custom.tab-info>.nav-tabs>li.active{border-top-color:#00c0ef}.nav-tabs-custom.tab-danger>.nav-tabs>li.active{border-top-color:#dd4b39}.nav-tabs-custom.tab-warning>.nav-tabs>li.active{border-top-color:#f39c12}.nav-tabs-custom.tab-success>.nav-tabs>li.active{border-top-color:#00a65a}.nav-tabs-custom.tab-default>.nav-tabs>li.active{border-top-color:#d2d6de}.pagination>li>a{background:#fafafa;color:#666}.pagination.pagination-flat>li>a{border-radius:0 !important}.products-list{list-style:none;margin:0;padding:0}.products-list>.item{border-radius:3px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.1);box-shadow:0 1px 1px rgba(0,0,0,0.1);padding:10px 0;background:#fff}.products-list>.item:before,.products-list>.item:after{content:" ";display:table}.products-list>.item:after{clear:both}.products-list .product-img{float:left}.products-list .product-img img{width:50px;height:50px}.products-list .product-info{margin-left:60px}.products-list .product-title{font-weight:600}.products-list .product-description{display:block;color:#999;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.product-list-in-box>.item{-webkit-box-shadow:none;box-shadow:none;border-radius:0;border-bottom:1px solid #f4f4f4}.product-list-in-box>.item:last-of-type{border-bottom-width:0}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{border-top:1px solid #f4f4f4}.table>thead>tr>th{border-bottom:2px solid #f4f4f4}.table tr td .progress{margin-top:5px}.table-bordered{border:1px solid #f4f4f4}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #f4f4f4}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table.no-border,.table.no-border td,.table.no-border th{border:0}table.text-center,table.text-center td,table.text-center th{text-align:center}.table.align th{text-align:left}.table.align td{text-align:right}.label-default{background-color:#d2d6de;color:#444}.direct-chat .box-body{border-bottom-right-radius:0;border-bottom-left-radius:0;position:relative;overflow-x:hidden;padding:0}.direct-chat.chat-pane-open .direct-chat-contacts{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.direct-chat-messages{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0);padding:10px;height:250px;overflow:auto}.direct-chat-msg,.direct-chat-text{display:block}.direct-chat-msg{margin-bottom:10px}.direct-chat-msg:before,.direct-chat-msg:after{content:" ";display:table}.direct-chat-msg:after{clear:both}.direct-chat-messages,.direct-chat-contacts{-webkit-transition:-webkit-transform .5s ease-in-out;-moz-transition:-moz-transform .5s ease-in-out;-o-transition:-o-transform .5s ease-in-out;transition:transform .5s ease-in-out}.direct-chat-text{border-radius:5px;position:relative;padding:5px 10px;background:#d2d6de;border:1px solid #d2d6de;margin:5px 0 0 50px;color:#444}.direct-chat-text:after,.direct-chat-text:before{position:absolute;right:100%;top:15px;border:solid transparent;border-right-color:#d2d6de;content:' ';height:0;width:0;pointer-events:none}.direct-chat-text:after{border-width:5px;margin-top:-5px}.direct-chat-text:before{border-width:6px;margin-top:-6px}.right .direct-chat-text{margin-right:50px;margin-left:0}.right .direct-chat-text:after,.right .direct-chat-text:before{right:auto;left:100%;border-right-color:transparent;border-left-color:#d2d6de}.direct-chat-img{border-radius:50%;float:left;width:40px;height:40px}.right .direct-chat-img{float:right}.direct-chat-info{display:block;margin-bottom:2px;font-size:12px}.direct-chat-name{font-weight:600}.direct-chat-timestamp{color:#999}.direct-chat-contacts-open .direct-chat-contacts{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.direct-chat-contacts{-webkit-transform:translate(101%, 0);-ms-transform:translate(101%, 0);-o-transform:translate(101%, 0);transform:translate(101%, 0);position:absolute;top:0;bottom:0;height:250px;width:100%;background:#222d32;color:#fff;overflow:auto}.contacts-list>li{border-bottom:1px solid rgba(0,0,0,0.2);padding:10px;margin:0}.contacts-list>li:before,.contacts-list>li:after{content:" ";display:table}.contacts-list>li:after{clear:both}.contacts-list>li:last-of-type{border-bottom:none}.contacts-list-img{border-radius:50%;width:40px;float:left}.contacts-list-info{margin-left:45px;color:#fff}.contacts-list-name,.contacts-list-status{display:block}.contacts-list-name{font-weight:600}.contacts-list-status{font-size:12px}.contacts-list-date{color:#aaa;font-weight:normal}.contacts-list-msg{color:#999}.direct-chat-danger .right>.direct-chat-text{background:#dd4b39;border-color:#dd4b39;color:#fff}.direct-chat-danger .right>.direct-chat-text:after,.direct-chat-danger .right>.direct-chat-text:before{border-left-color:#dd4b39}.direct-chat-primary .right>.direct-chat-text{background:#3c8dbc;border-color:#3c8dbc;color:#fff}.direct-chat-primary .right>.direct-chat-text:after,.direct-chat-primary .right>.direct-chat-text:before{border-left-color:#3c8dbc}.direct-chat-warning .right>.direct-chat-text{background:#f39c12;border-color:#f39c12;color:#fff}.direct-chat-warning .right>.direct-chat-text:after,.direct-chat-warning .right>.direct-chat-text:before{border-left-color:#f39c12}.direct-chat-info .right>.direct-chat-text{background:#00c0ef;border-color:#00c0ef;color:#fff}.direct-chat-info .right>.direct-chat-text:after,.direct-chat-info .right>.direct-chat-text:before{border-left-color:#00c0ef}.direct-chat-success .right>.direct-chat-text{background:#00a65a;border-color:#00a65a;color:#fff}.direct-chat-success .right>.direct-chat-text:after,.direct-chat-success .right>.direct-chat-text:before{border-left-color:#00a65a}.users-list>li{width:25%;float:left;padding:10px;text-align:center}.users-list>li img{border-radius:50%;max-width:100%;height:auto}.users-list>li>a:hover,.users-list>li>a:hover .users-list-name{color:#999}.users-list-name,.users-list-date{display:block}.users-list-name{font-weight:600;color:#444;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.users-list-date{color:#999;font-size:12px}.carousel-control.left,.carousel-control.right{background-image:none}.carousel-control>.fa{font-size:40px;position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-20px}.modal{background:rgba(0,0,0,0.3)}.modal-content{border-radius:0;-webkit-box-shadow:0 2px 3px rgba(0,0,0,0.125);box-shadow:0 2px 3px rgba(0,0,0,0.125);border:0}@media (min-width:768px){.modal-content{-webkit-box-shadow:0 2px 3px rgba(0,0,0,0.125);box-shadow:0 2px 3px rgba(0,0,0,0.125)}}.modal-header{border-bottom-color:#f4f4f4}.modal-footer{border-top-color:#f4f4f4}.modal-primary .modal-header,.modal-primary .modal-footer{border-color:#307095}.modal-warning .modal-header,.modal-warning .modal-footer{border-color:#c87f0a}.modal-info .modal-header,.modal-info .modal-footer{border-color:#0097bc}.modal-success .modal-header,.modal-success .modal-footer{border-color:#00733e}.modal-danger .modal-header,.modal-danger .modal-footer{border-color:#c23321}.box-widget{border:none;position:relative}.widget-user .widget-user-header{padding:20px;height:120px;border-top-right-radius:3px;border-top-left-radius:3px}.widget-user .widget-user-username{margin-top:0;margin-bottom:5px;font-size:25px;font-weight:300;text-shadow:0 1px 1px rgba(0,0,0,0.2)}.widget-user .widget-user-desc{margin-top:0}.widget-user .widget-user-image{position:absolute;top:65px;left:50%;margin-left:-45px}.widget-user .widget-user-image>img{width:90px;height:auto;border:3px solid #fff}.widget-user .box-footer{padding-top:30px}.widget-user-2 .widget-user-header{padding:20px;border-top-right-radius:3px;border-top-left-radius:3px}.widget-user-2 .widget-user-username{margin-top:5px;margin-bottom:5px;font-size:25px;font-weight:300}.widget-user-2 .widget-user-desc{margin-top:0}.widget-user-2 .widget-user-username,.widget-user-2 .widget-user-desc{margin-left:75px}.widget-user-2 .widget-user-image>img{width:65px;height:auto;float:left}.mailbox-messages>.table{margin:0}.mailbox-controls{padding:5px}.mailbox-controls.with-border{border-bottom:1px solid #f4f4f4}.mailbox-read-info{border-bottom:1px solid #f4f4f4;padding:10px}.mailbox-read-info h3{font-size:20px;margin:0}.mailbox-read-info h5{margin:0;padding:5px 0 0 0}.mailbox-read-time{color:#999;font-size:13px}.mailbox-read-message{padding:10px}.mailbox-attachments li{float:left;width:200px;border:1px solid #eee;margin-bottom:10px;margin-right:10px}.mailbox-attachment-name{font-weight:bold;color:#666}.mailbox-attachment-icon,.mailbox-attachment-info,.mailbox-attachment-size{display:block}.mailbox-attachment-info{padding:10px;background:#f4f4f4}.mailbox-attachment-size{color:#999;font-size:12px}.mailbox-attachment-icon{text-align:center;font-size:65px;color:#666;padding:20px 10px}.mailbox-attachment-icon.has-img{padding:0}.mailbox-attachment-icon.has-img>img{max-width:100%;height:auto}.lockscreen{background:#d2d6de}.lockscreen-logo{font-size:35px;text-align:center;margin-bottom:25px;font-weight:300}.lockscreen-logo a{color:#444}.lockscreen-wrapper{max-width:400px;margin:0 auto;margin-top:10%}.lockscreen .lockscreen-name{text-align:center;font-weight:600}.lockscreen-item{border-radius:4px;padding:0;background:#fff;position:relative;margin:10px auto 30px auto;width:290px}.lockscreen-image{border-radius:50%;position:absolute;left:-10px;top:-25px;background:#fff;padding:5px;z-index:10}.lockscreen-image>img{border-radius:50%;width:70px;height:70px}.lockscreen-credentials{margin-left:70px}.lockscreen-credentials .form-control{border:0}.lockscreen-credentials .btn{background-color:#fff;border:0;padding:0 10px}.lockscreen-footer{margin-top:10px}.login-logo,.register-logo{font-size:35px;text-align:center;margin-bottom:25px;font-weight:300}.login-logo a,.register-logo a{color:#444}.login-page,.register-page{background:#d2d6de}.login-box,.register-box{width:360px;margin:7% auto}@media (max-width:768px){.login-box,.register-box{width:90%;margin-top:20px}}.login-box-body,.register-box-body{background:#fff;padding:20px;border-top:0;color:#666}.login-box-body .form-control-feedback,.register-box-body .form-control-feedback{color:#777}.login-box-msg,.register-box-msg{margin:0;text-align:center;padding:0 20px 20px 20px}.social-auth-links{margin:10px 0}.error-page{width:600px;margin:20px auto 0 auto}@media (max-width:991px){.error-page{width:100%}}.error-page>.headline{float:left;font-size:100px;font-weight:300}@media (max-width:991px){.error-page>.headline{float:none;text-align:center}}.error-page>.error-content{margin-left:190px;display:block}@media (max-width:991px){.error-page>.error-content{margin-left:0}}.error-page>.error-content>h3{font-weight:300;font-size:25px}@media (max-width:991px){.error-page>.error-content>h3{text-align:center}}.invoice{position:relative;background:#fff;border:1px solid #f4f4f4;padding:20px;margin:10px 25px}.invoice-title{margin-top:0}.profile-user-img{margin:0 auto;width:100px;padding:3px;border:3px solid #d2d6de}.profile-username{font-size:21px;margin-top:5px}.post{border-bottom:1px solid #d2d6de;margin-bottom:15px;padding-bottom:15px;color:#666}.post:last-of-type{border-bottom:0;margin-bottom:0;padding-bottom:0}.post .user-block{margin-bottom:15px}.btn-social{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.btn-social>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)}.btn-social.btn-lg{padding-left:61px}.btn-social.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em}.btn-social.btn-sm{padding-left:38px}.btn-social.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em}.btn-social.btn-xs{padding-left:30px}.btn-social.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em}.btn-social-icon{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:34px;width:34px;padding:0}.btn-social-icon>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)}.btn-social-icon.btn-lg{padding-left:61px}.btn-social-icon.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em}.btn-social-icon.btn-sm{padding-left:38px}.btn-social-icon.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em}.btn-social-icon.btn-xs{padding-left:30px}.btn-social-icon.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em}.btn-social-icon>:first-child{border:none;text-align:center;width:100%}.btn-social-icon.btn-lg{height:45px;width:45px;padding-left:0;padding-right:0}.btn-social-icon.btn-sm{height:30px;width:30px;padding-left:0;padding-right:0}.btn-social-icon.btn-xs{height:22px;width:22px;padding-left:0;padding-right:0}.btn-adn{color:#fff;background-color:#d87a68;border-color:rgba(0,0,0,0.2)}.btn-adn:focus,.btn-adn.focus{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)}.btn-adn:hover{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)}.btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)}.btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{background-image:none}.btn-adn .badge{color:#d87a68;background-color:#fff}.btn-bitbucket{color:#fff;background-color:#205081;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:focus,.btn-bitbucket.focus{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:hover{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{background-image:none}.btn-bitbucket .badge{color:#205081;background-color:#fff}.btn-dropbox{color:#fff;background-color:#1087dd;border-color:rgba(0,0,0,0.2)}.btn-dropbox:focus,.btn-dropbox.focus{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)}.btn-dropbox:hover{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)}.btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)}.btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{background-image:none}.btn-dropbox .badge{color:#1087dd;background-color:#fff}.btn-facebook{color:#fff;background-color:#3b5998;border-color:rgba(0,0,0,0.2)}.btn-facebook:focus,.btn-facebook.focus{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)}.btn-facebook:hover{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)}.btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)}.btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{background-image:none}.btn-facebook .badge{color:#3b5998;background-color:#fff}.btn-flickr{color:#fff;background-color:#ff0084;border-color:rgba(0,0,0,0.2)}.btn-flickr:focus,.btn-flickr.focus{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)}.btn-flickr:hover{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)}.btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)}.btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{background-image:none}.btn-flickr .badge{color:#ff0084;background-color:#fff}.btn-foursquare{color:#fff;background-color:#f94877;border-color:rgba(0,0,0,0.2)}.btn-foursquare:focus,.btn-foursquare.focus{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)}.btn-foursquare:hover{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)}.btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)}.btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{background-image:none}.btn-foursquare .badge{color:#f94877;background-color:#fff}.btn-github{color:#fff;background-color:#444;border-color:rgba(0,0,0,0.2)}.btn-github:focus,.btn-github.focus{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)}.btn-github:hover{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)}.btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)}.btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{background-image:none}.btn-github .badge{color:#444;background-color:#fff}.btn-google{color:#fff;background-color:#dd4b39;border-color:rgba(0,0,0,0.2)}.btn-google:focus,.btn-google.focus{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)}.btn-google:hover{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)}.btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)}.btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{background-image:none}.btn-google .badge{color:#dd4b39;background-color:#fff}.btn-instagram{color:#fff;background-color:#3f729b;border-color:rgba(0,0,0,0.2)}.btn-instagram:focus,.btn-instagram.focus{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)}.btn-instagram:hover{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)}.btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)}.btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{background-image:none}.btn-instagram .badge{color:#3f729b;background-color:#fff}.btn-linkedin{color:#fff;background-color:#007bb6;border-color:rgba(0,0,0,0.2)}.btn-linkedin:focus,.btn-linkedin.focus{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)}.btn-linkedin:hover{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)}.btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)}.btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{background-image:none}.btn-linkedin .badge{color:#007bb6;background-color:#fff}.btn-microsoft{color:#fff;background-color:#2672ec;border-color:rgba(0,0,0,0.2)}.btn-microsoft:focus,.btn-microsoft.focus{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)}.btn-microsoft:hover{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)}.btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)}.btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{background-image:none}.btn-microsoft .badge{color:#2672ec;background-color:#fff}.btn-openid{color:#fff;background-color:#f7931e;border-color:rgba(0,0,0,0.2)}.btn-openid:focus,.btn-openid.focus{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)}.btn-openid:hover{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)}.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)}.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{background-image:none}.btn-openid .badge{color:#f7931e;background-color:#fff}.btn-pinterest{color:#fff;background-color:#cb2027;border-color:rgba(0,0,0,0.2)}.btn-pinterest:focus,.btn-pinterest.focus{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)}.btn-pinterest:hover{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)}.btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)}.btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{background-image:none}.btn-pinterest .badge{color:#cb2027;background-color:#fff}.btn-reddit{color:#000;background-color:#eff7ff;border-color:rgba(0,0,0,0.2)}.btn-reddit:focus,.btn-reddit.focus{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)}.btn-reddit:hover{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)}.btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)}.btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{background-image:none}.btn-reddit .badge{color:#eff7ff;background-color:#000}.btn-soundcloud{color:#fff;background-color:#f50;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:focus,.btn-soundcloud.focus{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:hover{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{background-image:none}.btn-soundcloud .badge{color:#f50;background-color:#fff}.btn-tumblr{color:#fff;background-color:#2c4762;border-color:rgba(0,0,0,0.2)}.btn-tumblr:focus,.btn-tumblr.focus{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)}.btn-tumblr:hover{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)}.btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)}.btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{background-image:none}.btn-tumblr .badge{color:#2c4762;background-color:#fff}.btn-twitter{color:#fff;background-color:#55acee;border-color:rgba(0,0,0,0.2)}.btn-twitter:focus,.btn-twitter.focus{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)}.btn-twitter:hover{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)}.btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)}.btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{background-image:none}.btn-twitter .badge{color:#55acee;background-color:#fff}.btn-vimeo{color:#fff;background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)}.btn-vimeo:focus,.btn-vimeo.focus{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)}.btn-vimeo:hover{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)}.btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)}.btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{background-image:none}.btn-vimeo .badge{color:#1ab7ea;background-color:#fff}.btn-vk{color:#fff;background-color:#587ea3;border-color:rgba(0,0,0,0.2)}.btn-vk:focus,.btn-vk.focus{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)}.btn-vk:hover{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)}.btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)}.btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{background-image:none}.btn-vk .badge{color:#587ea3;background-color:#fff}.btn-yahoo{color:#fff;background-color:#720e9e;border-color:rgba(0,0,0,0.2)}.btn-yahoo:focus,.btn-yahoo.focus{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)}.btn-yahoo:hover{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)}.btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)}.btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{background-image:none}.btn-yahoo .badge{color:#720e9e;background-color:#fff}.fc-button{background:#f4f4f4;background-image:none;color:#444;border-color:#ddd;border-bottom-color:#ddd}.fc-button:hover,.fc-button:active,.fc-button.hover{background-color:#e9e9e9}.fc-header-title h2{font-size:15px;line-height:1.6em;color:#666;margin-left:10px}.fc-header-right{padding-right:10px}.fc-header-left{padding-left:10px}.fc-widget-header{background:#fafafa}.fc-grid{width:100%;border:0}.fc-widget-header:first-of-type,.fc-widget-content:first-of-type{border-left:0;border-right:0}.fc-widget-header:last-of-type,.fc-widget-content:last-of-type{border-right:0}.fc-toolbar{padding:10px;margin:0}.fc-day-number{font-size:20px;font-weight:300;padding-right:10px}.fc-color-picker{list-style:none;margin:0;padding:0}.fc-color-picker>li{float:left;font-size:30px;margin-right:5px;line-height:30px}.fc-color-picker>li .fa{-webkit-transition:-webkit-transform linear .3s;-moz-transition:-moz-transform linear .3s;-o-transition:-o-transform linear .3s;transition:transform linear .3s}.fc-color-picker>li .fa:hover{-webkit-transform:rotate(30deg);-ms-transform:rotate(30deg);-o-transform:rotate(30deg);transform:rotate(30deg)}#add-new-event{-webkit-transition:all linear .3s;-o-transition:all linear .3s;transition:all linear .3s}.external-event{padding:5px 10px;font-weight:bold;margin-bottom:4px;box-shadow:0 1px 1px rgba(0,0,0,0.1);text-shadow:0 1px 1px rgba(0,0,0,0.1);border-radius:3px;cursor:move}.external-event:hover{box-shadow:inset 0 0 90px rgba(0,0,0,0.2)}.select2-container--default.select2-container--focus,.select2-selection.select2-container--focus,.select2-container--default:focus,.select2-selection:focus,.select2-container--default:active,.select2-selection:active{outline:none}.select2-container--default .select2-selection--single,.select2-selection .select2-selection--single{border:1px solid #d2d6de;border-radius:0;padding:6px 12px;height:34px}.select2-container--default.select2-container--open{border-color:#3c8dbc}.select2-dropdown{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#3c8dbc;color:white}.select2-results__option{padding:6px 12px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{padding-left:0;padding-right:0;height:auto;margin-top:-4px}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:6px;padding-left:20px}.select2-container--default .select2-selection--single .select2-selection__arrow{height:28px;right:3px}.select2-container--default .select2-selection--single .select2-selection__arrow b{margin-top:0}.select2-dropdown .select2-search__field,.select2-search--inline .select2-search__field{border:1px solid #d2d6de}.select2-dropdown .select2-search__field:focus,.select2-search--inline .select2-search__field:focus{outline:none;border:1px solid #3c8dbc}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option[aria-selected=true],.select2-container--default .select2-results__option[aria-selected=true]:hover{color:#444}.select2-container--default .select2-selection--multiple{border:1px solid #d2d6de;border-radius:0}.select2-container--default .select2-selection--multiple:focus{border-color:#3c8dbc}.select2-container--default.select2-container--focus .select2-selection--multiple{border-color:#d2d6de}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#3c8dbc;border-color:#367fa9;padding:1px 10px;color:#fff}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{margin-right:5px;color:rgba(255,255,255,0.7)}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#fff}.select2-container .select2-selection--single .select2-selection__rendered{padding-right:10px}.pad{padding:10px}.margin{margin:10px}.margin-bottom{margin-bottom:20px}.margin-bottom-none{margin-bottom:0}.margin-r-5{margin-right:5px}.inline{display:inline}.description-block{display:block;margin:10px 0;text-align:center}.description-block.margin-bottom{margin-bottom:25px}.description-block>.description-header{margin:0;padding:0;font-weight:600;font-size:16px}.description-block>.description-text{text-transform:uppercase}.bg-red,.bg-yellow,.bg-aqua,.bg-blue,.bg-light-blue,.bg-green,.bg-navy,.bg-teal,.bg-olive,.bg-lime,.bg-orange,.bg-fuchsia,.bg-purple,.bg-maroon,.bg-black,.bg-red-active,.bg-yellow-active,.bg-aqua-active,.bg-blue-active,.bg-light-blue-active,.bg-green-active,.bg-navy-active,.bg-teal-active,.bg-olive-active,.bg-lime-active,.bg-orange-active,.bg-fuchsia-active,.bg-purple-active,.bg-maroon-active,.bg-black-active,.callout.callout-danger,.callout.callout-warning,.callout.callout-info,.callout.callout-success,.alert-success,.alert-danger,.alert-error,.alert-warning,.alert-info,.label-danger,.label-info,.label-warning,.label-primary,.label-success,.modal-primary .modal-body,.modal-primary .modal-header,.modal-primary .modal-footer,.modal-warning .modal-body,.modal-warning .modal-header,.modal-warning .modal-footer,.modal-info .modal-body,.modal-info .modal-header,.modal-info .modal-footer,.modal-success .modal-body,.modal-success .modal-header,.modal-success .modal-footer,.modal-danger .modal-body,.modal-danger .modal-header,.modal-danger .modal-footer{color:#fff !important}.bg-gray{color:#000;background-color:#d2d6de !important}.bg-gray-light{background-color:#f7f7f7}.bg-black{background-color:#111 !important}.bg-red,.callout.callout-danger,.alert-danger,.alert-error,.label-danger,.modal-danger .modal-body{background-color:#dd4b39 !important}.bg-yellow,.callout.callout-warning,.alert-warning,.label-warning,.modal-warning .modal-body{background-color:#f39c12 !important}.bg-aqua,.callout.callout-info,.alert-info,.label-info,.modal-info .modal-body{background-color:#00c0ef !important}.bg-blue{background-color:#0073b7 !important}.bg-light-blue,.label-primary,.modal-primary .modal-body{background-color:#3c8dbc !important}.bg-green,.callout.callout-success,.alert-success,.label-success,.modal-success .modal-body{background-color:#00a65a !important}.bg-navy{background-color:#001f3f !important}.bg-teal{background-color:#39cccc !important}.bg-olive{background-color:#3d9970 !important}.bg-lime{background-color:#01ff70 !important}.bg-orange{background-color:#ff851b !important}.bg-fuchsia{background-color:#f012be !important}.bg-purple{background-color:#605ca8 !important}.bg-maroon{background-color:#d81b60 !important}.bg-gray-active{color:#000;background-color:#b5bbc8 !important}.bg-black-active{background-color:#000 !important}.bg-red-active,.modal-danger .modal-header,.modal-danger .modal-footer{background-color:#d33724 !important}.bg-yellow-active,.modal-warning .modal-header,.modal-warning .modal-footer{background-color:#db8b0b !important}.bg-aqua-active,.modal-info .modal-header,.modal-info .modal-footer{background-color:#00a7d0 !important}.bg-blue-active{background-color:#005384 !important}.bg-light-blue-active,.modal-primary .modal-header,.modal-primary .modal-footer{background-color:#357ca5 !important}.bg-green-active,.modal-success .modal-header,.modal-success .modal-footer{background-color:#008d4c !important}.bg-navy-active{background-color:#001a35 !important}.bg-teal-active{background-color:#30bbbb !important}.bg-olive-active{background-color:#368763 !important}.bg-lime-active{background-color:#00e765 !important}.bg-orange-active{background-color:#ff7701 !important}.bg-fuchsia-active{background-color:#db0ead !important}.bg-purple-active{background-color:#555299 !important}.bg-maroon-active{background-color:#ca195a !important}[class^="bg-"].disabled{opacity:.65;filter:alpha(opacity=65)}.text-red{color:#dd4b39 !important}.text-yellow{color:#f39c12 !important}.text-aqua{color:#00c0ef !important}.text-blue{color:#0073b7 !important}.text-black{color:#111 !important}.text-light-blue{color:#3c8dbc !important}.text-green{color:#00a65a !important}.text-gray{color:#d2d6de !important}.text-navy{color:#001f3f !important}.text-teal{color:#39cccc !important}.text-olive{color:#3d9970 !important}.text-lime{color:#01ff70 !important}.text-orange{color:#ff851b !important}.text-fuchsia{color:#f012be !important}.text-purple{color:#605ca8 !important}.text-maroon{color:#d81b60 !important}.link-muted{color:#7a869d}.link-muted:hover,.link-muted:focus{color:#606c84}.link-black{color:#666}.link-black:hover,.link-black:focus{color:#999}.hide{display:none !important}.no-border{border:0 !important}.no-padding{padding:0 !important}.no-margin{margin:0 !important}.no-shadow{box-shadow:none !important}.list-unstyled,.chart-legend,.contacts-list,.users-list,.mailbox-attachments{list-style:none;margin:0;padding:0}.list-group-unbordered>.list-group-item{border-left:0;border-right:0;border-radius:0;padding-left:0;padding-right:0}.flat{border-radius:0 !important}.text-bold,.text-bold.table td,.text-bold.table th{font-weight:700}.text-sm{font-size:12px}.jqstooltip{padding:5px !important;width:auto !important;height:auto !important}.bg-teal-gradient{background:#39cccc !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #39cccc), color-stop(1, #7adddd)) !important;background:-ms-linear-gradient(bottom, #39cccc, #7adddd) !important;background:-moz-linear-gradient(center bottom, #39cccc 0, #7adddd 100%) !important;background:-o-linear-gradient(#7adddd, #39cccc) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#7adddd', endColorstr='#39cccc', GradientType=0) !important;color:#fff}.bg-light-blue-gradient{background:#3c8dbc !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #3c8dbc), color-stop(1, #67a8ce)) !important;background:-ms-linear-gradient(bottom, #3c8dbc, #67a8ce) !important;background:-moz-linear-gradient(center bottom, #3c8dbc 0, #67a8ce 100%) !important;background:-o-linear-gradient(#67a8ce, #3c8dbc) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#67a8ce', endColorstr='#3c8dbc', GradientType=0) !important;color:#fff}.bg-blue-gradient{background:#0073b7 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #0073b7), color-stop(1, #0089db)) !important;background:-ms-linear-gradient(bottom, #0073b7, #0089db) !important;background:-moz-linear-gradient(center bottom, #0073b7 0, #0089db 100%) !important;background:-o-linear-gradient(#0089db, #0073b7) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0089db', endColorstr='#0073b7', GradientType=0) !important;color:#fff}.bg-aqua-gradient{background:#00c0ef !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #00c0ef), color-stop(1, #14d1ff)) !important;background:-ms-linear-gradient(bottom, #00c0ef, #14d1ff) !important;background:-moz-linear-gradient(center bottom, #00c0ef 0, #14d1ff 100%) !important;background:-o-linear-gradient(#14d1ff, #00c0ef) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#14d1ff', endColorstr='#00c0ef', GradientType=0) !important;color:#fff}.bg-yellow-gradient{background:#f39c12 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #f39c12), color-stop(1, #f7bc60)) !important;background:-ms-linear-gradient(bottom, #f39c12, #f7bc60) !important;background:-moz-linear-gradient(center bottom, #f39c12 0, #f7bc60 100%) !important;background:-o-linear-gradient(#f7bc60, #f39c12) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f7bc60', endColorstr='#f39c12', GradientType=0) !important;color:#fff}.bg-purple-gradient{background:#605ca8 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #605ca8), color-stop(1, #9491c4)) !important;background:-ms-linear-gradient(bottom, #605ca8, #9491c4) !important;background:-moz-linear-gradient(center bottom, #605ca8 0, #9491c4 100%) !important;background:-o-linear-gradient(#9491c4, #605ca8) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#9491c4', endColorstr='#605ca8', GradientType=0) !important;color:#fff}.bg-green-gradient{background:#00a65a !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #00a65a), color-stop(1, #00ca6d)) !important;background:-ms-linear-gradient(bottom, #00a65a, #00ca6d) !important;background:-moz-linear-gradient(center bottom, #00a65a 0, #00ca6d 100%) !important;background:-o-linear-gradient(#00ca6d, #00a65a) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ca6d', endColorstr='#00a65a', GradientType=0) !important;color:#fff}.bg-red-gradient{background:#dd4b39 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #dd4b39), color-stop(1, #e47365)) !important;background:-ms-linear-gradient(bottom, #dd4b39, #e47365) !important;background:-moz-linear-gradient(center bottom, #dd4b39 0, #e47365 100%) !important;background:-o-linear-gradient(#e47365, #dd4b39) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e47365', endColorstr='#dd4b39', GradientType=0) !important;color:#fff}.bg-black-gradient{background:#111 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #111), color-stop(1, #2b2b2b)) !important;background:-ms-linear-gradient(bottom, #111, #2b2b2b) !important;background:-moz-linear-gradient(center bottom, #111 0, #2b2b2b 100%) !important;background:-o-linear-gradient(#2b2b2b, #111) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#2b2b2b', endColorstr='#111111', GradientType=0) !important;color:#fff}.bg-maroon-gradient{background:#d81b60 !important;background:-webkit-gradient(linear, left bottom, left top, color-stop(0, #d81b60), color-stop(1, #e73f7c)) !important;background:-ms-linear-gradient(bottom, #d81b60, #e73f7c) !important;background:-moz-linear-gradient(center bottom, #d81b60 0, #e73f7c 100%) !important;background:-o-linear-gradient(#e73f7c, #d81b60) !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#e73f7c', endColorstr='#d81b60', GradientType=0) !important;color:#fff}.description-block .description-icon{font-size:16px}.no-pad-top{padding-top:0}.position-static{position:static !important}.list-header{font-size:15px;padding:10px 4px;font-weight:bold;color:#666}.list-seperator{height:1px;background:#f4f4f4;margin:15px 0 9px 0}.list-link>a{padding:4px;color:#777}.list-link>a:hover{color:#222}.font-light{font-weight:300}.user-block:before,.user-block:after{content:" ";display:table}.user-block:after{clear:both}.user-block img{width:40px;height:40px;float:left}.user-block .username,.user-block .description,.user-block .comment{display:block;margin-left:50px}.user-block .username{font-size:16px;font-weight:600}.user-block .description{color:#999;font-size:13px}.user-block.user-block-sm .username,.user-block.user-block-sm .description,.user-block.user-block-sm .comment{margin-left:40px}.user-block.user-block-sm .username{font-size:14px}.img-sm,.img-md,.img-lg,.box-comments .box-comment img,.user-block.user-block-sm img{float:left}.img-sm,.box-comments .box-comment img,.user-block.user-block-sm img{width:30px !important;height:30px !important}.img-sm+.img-push{margin-left:40px}.img-md{width:60px;height:60px}.img-md+.img-push{margin-left:70px}.img-lg{width:100px;height:100px}.img-lg+.img-push{margin-left:110px}.img-bordered{border:3px solid #d2d6de;padding:3px}.img-bordered-sm{border:2px solid #d2d6de;padding:2px}.attachment-block{border:1px solid #f4f4f4;padding:5px;margin-bottom:10px;background:#f7f7f7}.attachment-block .attachment-img{max-width:100px;max-height:100px;height:auto;float:left}.attachment-block .attachment-pushed{margin-left:110px}.attachment-block .attachment-heading{margin:0}.attachment-block .attachment-text{color:#555}.connectedSortable{min-height:100px}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sort-highlight{background:#f4f4f4;border:1px dashed #ddd;margin-bottom:10px}.full-opacity-hover{opacity:.65;filter:alpha(opacity=65)}.full-opacity-hover:hover{opacity:1;filter:alpha(opacity=100)}.chart{position:relative;overflow:hidden;width:100%}.chart svg,.chart canvas{width:100% !important}@media print{.no-print,.main-sidebar,.left-side,.main-header,.content-header{display:none !important}.content-wrapper,.right-side,.main-footer{margin-left:0 !important;min-height:0 !important;-webkit-transform:translate(0, 0) !important;-ms-transform:translate(0, 0) !important;-o-transform:translate(0, 0) !important;transform:translate(0, 0) !important}.fixed .content-wrapper,.fixed .right-side{padding-top:0 !important}.invoice{width:100%;border:0;margin:0;padding:0}.invoice-col{float:left;width:33.3333333%}.table-responsive{overflow:auto}.table-responsive>.table tr th,.table-responsive>.table tr td{white-space:normal !important}} \ No newline at end of file diff --git a/app/stylesheets/app.css b/app/stylesheets/app.css new file mode 100644 index 0000000..b86a8c0 --- /dev/null +++ b/app/stylesheets/app.css @@ -0,0 +1,8 @@ +body { + padding: 20px; +} + +.content-header { + margin-bottom: 10px; + padding: 0px !important; +} \ No newline at end of file diff --git a/contracts/ContractProvider.sol b/contracts/ContractProvider.sol new file mode 100644 index 0000000..574715e --- /dev/null +++ b/contracts/ContractProvider.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.4.8; + +contract ContractProvider { + function contracts(bytes32 contractName) returns (address addr){} +} \ No newline at end of file diff --git a/contracts/Migrations.sol b/contracts/Migrations.sol new file mode 100755 index 0000000..8d6e950 --- /dev/null +++ b/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.2; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + modifier restricted() { + if (msg.sender == owner) _; + } + + function Migrations() { + owner = msg.sender; + } + + function setCompleted(uint completed) restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/contracts/Owned.sol b/contracts/Owned.sol new file mode 100755 index 0000000..550920c --- /dev/null +++ b/contracts/Owned.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.4.2; + +contract Owned { + + address owner; + + function Owned() { + owner = msg.sender; + } + + modifier onlyOwner { + if (msg.sender != owner) + throw; + _; + } +} diff --git a/contracts/alice/AliceToken.sol b/contracts/alice/AliceToken.sol new file mode 100644 index 0000000..72755bb --- /dev/null +++ b/contracts/alice/AliceToken.sol @@ -0,0 +1,71 @@ +/* +Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/issues/20 +*/ + +pragma solidity ^0.4.2; + +import "./Token.sol"; +import "../Owned.sol"; + +contract AliceToken is Token, Owned { + + string public name = "Alice Token"; + uint8 public decimals = 2; + string public symbol = "ALT"; + string public version = 'ALT 1.0'; + + + function transfer(address _to, uint256 _value) { + //Default assumes totalSupply can't be over max (2^256 - 1). + if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) { + balances[msg.sender] -= _value; + balances[_to] += _value; + Transfer(msg.sender, _to, _value); + } else { throw; } + } + + function transferFrom(address _from, address _to, uint256 _value) { + if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) { + balances[_to] += _value; + balances[_from] -= _value; + allowed[_from][msg.sender] -= _value; + Transfer(_from, _to, _value); + } else { throw; } + } + + function balanceOf(address _owner) constant returns (uint256 balance) { + return balances[_owner]; + } + + function approve(address _spender, uint256 _value) returns (bool success) { + allowed[msg.sender][_spender] = _value; + Approval(msg.sender, _spender, _value); + return true; + } + + function allowance(address _owner, address _spender) constant returns (uint256 remaining) { + return allowed[_owner][_spender]; + } + + function mint(address _to, uint256 _value) onlyOwner { + if (totalSupply + _value < totalSupply) throw; + totalSupply += _value; + balances[_to] += _value; + + MintEvent(_to, _value); + } + + function destroy(address _from, uint256 _value) onlyOwner { + if (balances[_from] < _value || _value < 0) throw; + totalSupply -= _value; + balances[_from] -= _value; + + DestroyEvent(_from, _value); + } + + mapping (address => uint256) balances; + mapping (address => mapping (address => uint256)) allowed; + + event MintEvent(address indexed to, uint value); + event DestroyEvent(address indexed from, uint value); +} \ No newline at end of file diff --git a/contracts/alice/Charity.sol b/contracts/alice/Charity.sol new file mode 100644 index 0000000..e640abc --- /dev/null +++ b/contracts/alice/Charity.sol @@ -0,0 +1,105 @@ +pragma solidity ^0.4.2; + +import "../Owned.sol"; +import "./ImpactRegistry.sol"; +import "../ContractProvider.sol"; + +contract Token {function transfer(address _to, uint256 _value);} + +contract Charity is Owned { + /* Public variables of the token */ + string public name; + address public judgeAddress; + address public beneficiaryAddress; + address public IMPACT_REGISTRY_ADDRESS; + address public CONTRACT_PROVIDER_ADDRESS; + + + /* This creates a map with donations per user */ + mapping (address => uint) accountBalances; + + /* Additional structure to help to iterate over donations */ + address[] accountIndex; + + /* Total amount of all of the donations */ + uint public total; + + /* This generates a public event on the blockchain that will notify clients */ + event OutcomeEvent(string name, uint value); + event DonationEvent(address indexed from, uint value); + + function Charity(string _name) { + name = _name; + } + + function setJudge(address _judgeAddress) onlyOwner { + judgeAddress = _judgeAddress; + } + + function setBeneficiary(address _beneficiaryAddress) onlyOwner { + beneficiaryAddress = _beneficiaryAddress; + } + + function setImpactRegistry(address impactRegistryAddress) onlyOwner { + IMPACT_REGISTRY_ADDRESS = impactRegistryAddress; + } + + function setContractProvider(address _contractProvider) onlyOwner { + CONTRACT_PROVIDER_ADDRESS = _contractProvider; + } + + function notify(address _from, uint _value) onlyOwner { + if (total + _value < total) + throw; + + total += _value; + ImpactRegistry(IMPACT_REGISTRY_ADDRESS).registerDonation(_from, _value); + DonationEvent(_from, _value); + } + + function fund(uint _value) onlyOwner { + if (total + _value < total) + throw; + + total += _value; + } + + function unlockOutcome(string _name, uint _value) { + if (msg.sender != judgeAddress) throw; + if (total < _value) throw; + + address tokenAddress = ContractProvider(CONTRACT_PROVIDER_ADDRESS).contracts("digitalGBP"); + Token(tokenAddress).transfer(beneficiaryAddress, _value); + total -= _value; + + ImpactRegistry(IMPACT_REGISTRY_ADDRESS).registerOutcome(_name, _value); + + OutcomeEvent(_name, _value); + } + + function payBack(address account) onlyOwner { + uint balance = getBalance(account); + if (balance > 0) { + address tokenAddress = ContractProvider(CONTRACT_PROVIDER_ADDRESS).contracts("digitalGBP"); + Token(tokenAddress).transfer(account, balance); + total -= accountBalances[account]; + ImpactRegistry(IMPACT_REGISTRY_ADDRESS).payBack(account); + } + } + + function getBalance(address donor) returns(uint) { + return ImpactRegistry(IMPACT_REGISTRY_ADDRESS).getBalance(donor); + } + + /* Extra security measure to save funds in case of critical error or attack */ + function escape(address escapeAddress) onlyOwner { + address tokenAddress = ContractProvider(CONTRACT_PROVIDER_ADDRESS).contracts("digitalGBP"); + Token(tokenAddress).transfer(escapeAddress, total); + total = 0; + } + + /* This unnamed function is called whenever someone tries to send ether to it */ + function () { + throw; // Prevents accidental sending of ether + } +} \ No newline at end of file diff --git a/contracts/alice/ImpactRegistry.sol b/contracts/alice/ImpactRegistry.sol new file mode 100644 index 0000000..faf2d11 --- /dev/null +++ b/contracts/alice/ImpactRegistry.sol @@ -0,0 +1,141 @@ +pragma solidity ^0.4.2; + +import "../Owned.sol"; + +contract ImpactRegistry is Owned { + + modifier onlyMaster { + if (msg.sender != owner && msg.sender != masterContract) + throw; + _; + } + + address public masterContract; + + /* This creates a map with donations per user */ + mapping (address => uint) accountBalances; + + /* Additional structure to help to iterate over donations */ + address[] accountIndex; + + uint public unit; + + struct Impact { + uint value; + uint linked; + uint accountCursor; + uint count; + mapping(uint => address) addresses; + mapping(address => uint) values; + } + + /* Structures that store a match between validated outcomes and donations */ + mapping (string => Impact) impact; + + + function ImpactRegistry(address _masterContract, uint _unit) { + masterContract = _masterContract; + unit = _unit; + } + + function registerDonation(address _from, uint _value) onlyMaster { + if (accountBalances[_from] == 0) { + accountIndex.push(_from); + } + + if (accountBalances[_from] + _value < accountBalances[_from]) + throw; + + accountBalances[_from] += _value; + } + + function setUnit(uint _value) onlyOwner { + unit = _value; + } + + function setMasterContract(address _contractAddress) onlyOwner { + masterContract = _contractAddress; + } + + function registerOutcome(string _name, uint _value) onlyMaster{ + impact[_name] = Impact(_value, 0, 0, 0); + } + + function linkImpact(string _name) onlyOwner { + uint left = impact[_name].value - impact[_name].linked; + if (left > 0) { + + uint i = impact[_name].accountCursor; + + if (accountBalances[accountIndex[i]] >= 0) { + /*Calculate shard */ + uint shard = accountBalances[accountIndex[i]]; + if (shard > left) { + shard = left; + } + + if (shard > unit) { + shard = unit; + } + + /* Update balances */ + accountBalances[accountIndex[i]] -= shard; + + /* Update impact */ + if (impact[_name].values[accountIndex[i]] == 0) { + impact[_name].addresses[impact[_name].count++] = accountIndex[i]; + } + + impact[_name].values[accountIndex[i]] += shard; + impact[_name].linked += shard; + + /* Move to next account removing empty ones */ + if (accountBalances[accountIndex[i]] == 0) { + accountIndex[i] = accountIndex[accountIndex.length-1]; + accountIndex.length = accountIndex.length - 1; + i--; + } + } + + /* Update cursor */ + + if (accountIndex.length > 0) { + i = (i + 1) % accountIndex.length; + } else { + i = 0; + } + + impact[_name].accountCursor = i; + } + } + + function payBack(address _account) onlyMaster{ + accountBalances[_account] = 0; + } + + function getBalance(address _donorAddress) returns(uint) { + return accountBalances[_donorAddress]; + } + + function getImpactCount(string outcome) returns(uint) { + return impact[outcome].count; + } + + function getImpactLinked(string outcome) returns(uint) { + return impact[outcome].linked; + } + + function getImpactDonor(string outcome, uint index) returns(address) { + return impact[outcome].addresses[index]; + } + + function getImpactValue(string outcome, address addr) returns(uint) { + return impact[outcome].values[addr]; + } + + /* This unnamed function is called whenever someone tries to send ether to it */ + function () { + throw; // Prevents accidental sending of ether + } + +} \ No newline at end of file diff --git a/contracts/alice/MockValidation.sol b/contracts/alice/MockValidation.sol new file mode 100644 index 0000000..f94c049 --- /dev/null +++ b/contracts/alice/MockValidation.sol @@ -0,0 +1,41 @@ +pragma solidity ^0.4.2; + +//Contract only for testing purposes. +//Don't connect to other contracts or use in a production environment. +//It registers and store performed validations. +contract MockValidation { + + event ValidationEvent(uint time, address indexed validator, string outcome, uint value); + + struct Validation { + uint time; + address validator; + string outcome; + uint value; + } + + Validation[] validations; + + function validate(string outcome, uint value) { + Validation memory validation = Validation(now, msg.sender, outcome, value); + validations.push(validation); + ValidationEvent(validation.time, validation.validator, validation.outcome, validation.value); + } + + function getValidationsCount() constant public returns(uint count) { + return validations.length; + } + + function getValidatorByIndex(uint index) constant public returns(address validator) { + return validations[index].validator; + } + + function getOutcomeByIndex(uint index) constant public returns(string outcome) { + return validations[index].outcome; + } + + function getValueByIndex(uint index) constant public returns(uint value) { + return validations[index].value; + } + +} \ No newline at end of file diff --git a/contracts/alice/SimpleContractRegistry.sol b/contracts/alice/SimpleContractRegistry.sol new file mode 100644 index 0000000..58a388b --- /dev/null +++ b/contracts/alice/SimpleContractRegistry.sol @@ -0,0 +1,13 @@ +pragma solidity ^0.4.2; + +import "../Owned.sol"; + +contract SimpleContractRegistry is Owned { + + mapping (bytes32 => address) public contracts; + + function registerContract(bytes32 _name, address _contractAddress) onlyOwner { + contracts[_name] = _contractAddress; + } + +} \ No newline at end of file diff --git a/contracts/alice/Token.sol b/contracts/alice/Token.sol new file mode 100644 index 0000000..2b828fb --- /dev/null +++ b/contracts/alice/Token.sol @@ -0,0 +1,46 @@ +// Abstract contract for the full ERC 20 Token standard +// https://github.com/ethereum/EIPs/issues/20 +pragma solidity ^0.4.2; + +contract Token { + /* This is a slight change to the ERC20 base standard. + function totalSupply() constant returns (uint256 supply); + is replaced with: + uint256 public totalSupply; + This automatically creates a getter function for the totalSupply. + This is moved to the base contract since public getter functions are not + currently recognised as an implementation of the matching abstract + function by the compiler. + */ + /// total amount of tokens + uint256 public totalSupply; + + /// @param _owner The address from which the balance will be retrieved + /// @return The balance + function balanceOf(address _owner) constant returns (uint256 balance); + + /// @notice send `_value` token to `_to` from `msg.sender` + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + function transfer(address _to, uint256 _value); + + /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from` + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + function transferFrom(address _from, address _to, uint256 _value); + + /// @notice `msg.sender` approves `_spender` to spend `_value` tokens + /// @param _spender The address of the account able to transfer the tokens + /// @param _value The amount of tokens to be approved for transfer + /// @return Whether the approval was successful or not + function approve(address _spender, uint256 _value) returns (bool success); + + /// @param _owner The address of the account owning tokens + /// @param _spender The address of the account able to transfer the tokens + /// @return Amount of remaining tokens allowed to spent + function allowance(address _owner, address _spender) constant returns (uint256 remaining); + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); +} \ No newline at end of file diff --git a/migrations/1_initial_migration.js b/migrations/1_initial_migration.js new file mode 100755 index 0000000..6e0a9f1 --- /dev/null +++ b/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +var Migrations = artifacts.require("Migrations"); + +module.exports = function(deployer) { + deployer.deploy(Migrations); +}; diff --git a/migrations/2_alice_token.js b/migrations/2_alice_token.js new file mode 100644 index 0000000..d0ea1d3 --- /dev/null +++ b/migrations/2_alice_token.js @@ -0,0 +1,17 @@ +var SimpleContractRegistry = artifacts.require("SimpleContractRegistry"); +var AliceToken = artifacts.require("AliceToken"); + +module.exports = function(deployer) { + deployer.deploy(SimpleContractRegistry + ).then(function() { + console.log("Simple contract registry deployed to: " + SimpleContractRegistry.address); + return deployer.deploy(AliceToken); + }).then(function() { + console.log("Alice Token deployed to: " + AliceToken.address); + return SimpleContractRegistry.deployed().then(function(instance) { + return instance.registerContract("digitalGBP", AliceToken.address); + }); + }).then(function() { + console.log("Alice token registered: " + AliceToken.address); + }) +}; diff --git a/migrations/3_alice_charity_contracts.js b/migrations/3_alice_charity_contracts.js new file mode 100644 index 0000000..4b71fd0 --- /dev/null +++ b/migrations/3_alice_charity_contracts.js @@ -0,0 +1,42 @@ +var Charity = artifacts.require("Charity"); +var ImpactRegistry = artifacts.require("ImpactRegistry"); +var SimpleContractRegistry = artifacts.require("SimpleContractRegistry"); + +module.exports = function(deployer, network, accounts) { + var judgeAccount = accounts[1]; + var beneficiaryAccount = accounts[2]; + var unit = 10; + + deployer.deploy(Charity, "London Homeless" + ).then(function() { + console.log("Charity deployed to: " + Charity.address); + return deployer.deploy(ImpactRegistry, Charity.address, 1000); + }).then(function() { + console.log("Unit set to : " + unit); + return ImpactRegistry.deployed().then(function(instance) { + return instance.setUnit(unit); + }); + }).then(function() { + console.log("Impact deployed to: " + ImpactRegistry.address); + return Charity.deployed().then(function(instance) { + return instance.setImpactRegistry(ImpactRegistry.address); + }); + }).then(function() { + console.log("Charity linked to impact: " + ImpactRegistry.address); + return Charity.deployed().then(function(instance) { + return instance.setJudge(judgeAccount); + }); + }).then(function() { + console.log("Charity linked to judge"); + return Charity.deployed().then(function(instance) { + return instance.setBeneficiary(beneficiaryAccount); + }); + }).then(function() { + console.log("Charity linked to beneficiary"); + return Charity.deployed().then(function(instance) { + return instance.setContractProvider(SimpleContractRegistry.address); + }); + }).then(function() { + console.log("Charity linked to Contract Provider"); + }); +}; \ No newline at end of file diff --git a/migrations/4_mock_validation.js b/migrations/4_mock_validation.js new file mode 100644 index 0000000..980fd55 --- /dev/null +++ b/migrations/4_mock_validation.js @@ -0,0 +1,7 @@ +var MockValidation = artifacts.require("MockValidation"); + +module.exports = function (deployer) { + deployer.deploy(MockValidation).then(function () { + console.log("Mock Validation deployed to: " + MockValidation.address); + }); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..102a7d4 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "alice-contracts", + "version": "0.0.1", + "author": "Jakub Wojciechowski", + "description": "Smart contracts for Alice Donation Platform", + "repository": { + "type": "git", + "url": "https://github.com/jakub-wojciechowski/alice-contracts.git" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/jakub-wojciechowski/alice-contracts/issues" + }, + "homepage": "https://github.com/jakub-wojciechowski/alice-contracts", + "dependencies": { + "truffle-default-builder": "^2.0.0" + } +} diff --git a/test/double-donations.js b/test/double-donations.js new file mode 100644 index 0000000..ea48337 --- /dev/null +++ b/test/double-donations.js @@ -0,0 +1,217 @@ +var Charity = artifacts.require("Charity"); +var SimpleContractRegistry = artifacts.require("SimpleContractRegistry"); +var AliceToken = artifacts.require("AliceToken"); +var ImpactRegistry = artifacts.require("ImpactRegistry"); + +contract('Double donations', function(accounts) { + var main = accounts[0]; + var donor1 = accounts[1]; + var donor2 = accounts[2]; + var judge = accounts[3]; + var beneficiary = accounts[4]; + var charity, token, contractProvider, impactRegistryRegistry; + + it("should link charity to judge", function(done) { + Charity.deployed().then(function(instance) { + charity = instance; + return charity.setJudge(judge, 10, {from: main}) + }).then(function() { + return charity.judgeAddress.call(); + }).then(function(address) { + return assert.equal(address, judge, "Judge address wasn't set correctly"); + }) + .then(done) + .catch(done); + }); + + it("should link charity to beneficiary", function(done) { + charity.setBeneficiary(beneficiary, 10, {from: main}).then(function() { + return charity.beneficiaryAddress.call(); + }).then(function(address) { + return assert.equal(address, beneficiary, "Beneficiary address wasn't set correctly"); + }) + .then(done) + .catch(done); + }); + + it("should link charity to contract provider", function(done) { + SimpleContractRegistry.deployed().then(function(instance) { + contractProvider = instance; + return charity.setContractProvider(contractProvider.address, {from: main}) + }).then(function() { + return charity.CONTRACT_PROVIDER_ADDRESS.call(); + }).then(function(address) { + return assert.equal(address, contractProvider.address, "Contract provider address wasn't set correctly"); + }) + .then(done) + .catch(done); + }); + + it("should link charity to impact registry", function(done) { + ImpactRegistry.deployed().then(function(instance) { + impactRegistry = instance; + return charity.setImpactRegistry(impactRegistry.address, {from: main}) + }).then(function() { + return charity.IMPACT_REGISTRY_ADDRESS.call(); + }).then(function(address) { + return assert.equal(address, impactRegistry.address, "Impact registry address wasn't set correctly"); + }) + .then(done) + .catch(done); + }); + + it("should notify from donor1", function (done) { + charity.notify(donor1, 10, {from: main}).then(function () { + return charity.total() + }).then(function (total) { + return assert.equal(total.valueOf(), 10, "10 wasn't in token charity balance after the first donation"); + }).then(function() { + return charity.getBalance.call(donor1) + }).then(function(balance) { + return assert.equal(balance.valueOf(), 10, "10 wasn't in internal balance"); + }) + .then(done) + .catch(done); + }); + + + it("should directly deposit to charity from second donor", function (done) { + charity.notify(donor2, 20, {from: main}).then(function () { + return charity.total() + }).then(function (total) { + return assert.equal(total.valueOf(), 30, "30 wasn't in token charity balance after the first donation"); + }).then(function() { + return charity.getBalance.call(donor2, {from: main}) + }).then(function(balance) { + return assert.equal(balance.valueOf(), 20, "20 wasn't in internal balance"); + }) + .then(done) + .catch(done); + }); + + it("should get token contract from registry", function(done) { + SimpleContractRegistry.deployed().then(function(registryInstance) { + return registryInstance.contracts.call('digitalGBP'); + }).then(function(address) { + token = AliceToken.at(address); + return assert.notEqual(token, undefined); + }) + .then(done) + .catch(done); + }); + + it("should mint tokens", function (done) { + //First donation + token.mint(charity.address, 30, {from: main}).then(function () { + return token.balanceOf.call(charity.address, {from: main}) + }).then(function (balance) { + return assert.equal(balance.valueOf(), 30, "30 wasn't minted"); + }) + .then(done) + .catch(done); + }); + + it("should unlock outcome", function (done) { + token.balanceOf.call(charity.address).then(function (balance) { + assert.equal(balance.valueOf(), 30, "30 wasn't in charity before unlocking outcome"); + }).then(function () { + return token.balanceOf.call(beneficiary); + }).then(function (balance) { + return assert.equal(balance.valueOf(), 0, "0 wasn't in beneficiary before unlocking outcome"); + }).then(function () { + return charity.unlockOutcome("Outcome", 25, {from: judge}); + }).then(function () { + return token.balanceOf.call(charity.address); + }).then(function (balance) { + return assert.equal(balance.valueOf(), 5, "0 wasn't in charity after unlocking outcome"); + }).then(function() { + return token.balanceOf.call(beneficiary); + }).then(function (balance) { + return assert.equal(balance.valueOf(), 25, "30 wasn't in beneficiary after unlocking outcome"); + }) + .then(done) + .catch(done); + }); + + it("should correctly set an unit of impact", function(done) { + impactRegistry.setUnit(10, {from: main}).then(function() { + return impactRegistry.unit.call(); + }).then(function(unit) { + return assert.equal(unit, 10, "Unit for impact reconciliation hasn't been set up correctly"); + }) + .then(done) + .catch(done); + }); + + it("should link impactRegistry", function (done) { + impactRegistry.linkImpact("Outcome", {from: main}).then(function () { + return impactRegistry.getImpactLinked.call("Outcome", {from: main}) + }).then(function(result) { + assert.equal(result.valueOf(), 10, "10 wasn't linked after first link call"); + return impactRegistry.getImpactCount.call("Outcome", {from: main}) + }).then(function(result) { + assert.equal(result.valueOf(), 1, "1 wasn't number of impactRegistry after first link call"); + return impactRegistry.getImpactDonor.call("Outcome", 0, {from: main}) + }).then(function(result) { + assert.equal(result, donor1, "donor1 wasn't address after first link call"); + return impactRegistry.getImpactValue.call("Outcome", donor1, {from: main}) + }).then(function(result) { + assert.equal(result.valueOf(), 10, "10 wasn't impactRegistry value after first link call"); + + return impactRegistry.linkImpact("Outcome", {from: main}) + }).then(function () { + return impactRegistry.getImpactLinked.call("Outcome", {from: main}) + }).then(function(result) { + assert.equal(result.valueOf(), 20, "20 wasn't linked after second link call"); + return impactRegistry.getImpactCount.call("Outcome", {from: main}) + }).then(function(result) { + assert.equal(result.valueOf(), 2, "2 wasn't number of impactRegistry after first link call"); + return impactRegistry.getImpactDonor.call("Outcome", 1, {from: main}) + }).then(function(result) { + assert.equal(result, donor2, "donor2 wasn't address after first link call"); + return impactRegistry.getImpactValue.call("Outcome", donor2, {from: main}) + }).then(function(result) { + assert.equal(result.valueOf(), 10, "10 wasn't impactRegistry value after first link call"); + + return impactRegistry.linkImpact("Outcome", {from: main}) + }).then(function () { + return impactRegistry.getImpactLinked.call("Outcome", {from: main}) + }).then(function(result) { + assert.equal(result.valueOf(), 25, "30 wasn't linked after second link call"); + return impactRegistry.getImpactCount.call("Outcome", {from: main}) + }).then(function(result) { + assert.equal(result.valueOf(), 2, "2 wasn't number of impactRegistry after first link call"); + return impactRegistry.getImpactDonor.call("Outcome", 1, {from: main}) + }).then(function(result) { + assert.equal(result, donor2, "donor2 wasn't address after first link call"); + return impactRegistry.getImpactValue.call("Outcome", donor2, {from: main}) + }).then(function (result) { + assert.equal(result.valueOf(), 15, "15 wasn't impactRegistry value after first link call"); + }) + .then(done) + .catch(done); + }); + + it("should pay back outstanding balance", function (done) { + token.balanceOf.call(charity.address).then(function (balance) { + assert.equal(balance.valueOf(), 5, "5 wasn't in charity before payback"); + }).then(function () { + return token.balanceOf.call(donor2); + }).then(function (balance) { + return assert.equal(balance.valueOf(), 0, "0 wasn't in donor before payback"); + }).then(function () { + return charity.payBack(donor2); + }).then(function () { + return token.balanceOf.call(charity.address); + }).then(function (balance) { + return assert.equal(balance.valueOf(), 0, "0 wasn't in charity after payback"); + }).then(function() { + return token.balanceOf.call(donor2); + }).then(function (balance) { + assert.equal(balance.valueOf(), 5, "5 wasn't in donor after payback"); + }) + .then(done) + .catch(done); + }); + +}); diff --git a/test/escape.js b/test/escape.js new file mode 100644 index 0000000..9729157 --- /dev/null +++ b/test/escape.js @@ -0,0 +1,65 @@ +var Charity = artifacts.require("Charity"); +var SimpleContractRegistry = artifacts.require("SimpleContractRegistry"); +var AliceToken = artifacts.require("AliceToken"); + +contract('Escape', function(accounts) { + var charity, contractProvider, token; + var main = accounts[0]; + var donor = accounts[1]; + var escapeAddress = accounts[2]; + + it("should link charity to contract provider", function(done) { + Charity.deployed().then(function(instance) { + charity = instance; + return SimpleContractRegistry.deployed() + }).then(function(instance) { + contractProvider = instance; + return charity.setContractProvider(contractProvider.address, {from: main}) + }).then(function() { + return charity.CONTRACT_PROVIDER_ADDRESS.call(); + }).then(function(address) { + return assert.equal(address, contractProvider.address, "Contract provider address wasn't set correctly"); + }) + .then(done) + .catch(done); + }); + + it("should get token contract from registry", function(done) { + SimpleContractRegistry.deployed().then(function(registryInstance) { + return registryInstance.contracts.call('digitalGBP'); + }).then(function(address) { + token = AliceToken.at(address); + return assert.notEqual(token, undefined); + }) + .then(done) + .catch(done); + }); + + it("should mint tokens to charity account", function (done) { + token.mint(charity.address, 30, {from: main, gas: 300000}).then(function () { + return token.balanceOf.call(charity.address, {from: main}) + }).then(function (balance) { + return assert.equal(balance.valueOf(), 30, "30 wasn't minted"); + }).then(function() { + return charity.notify(donor, 30, {from: main, gas: 3000000}) + }).then(function() { + return charity.total.call() + }).then(function(total) { + return assert.equal(total.valueOf(), 30, "30 wasn't minted"); + }) + .then(done) + .catch(done); + }); + + it("should allow escape to secure address", function (done) { + charity.escape(escapeAddress, {from: main}) + .then(function () { + return token.balanceOf.call(charity.address, {from: main}) + }).then(function (balance) { + return assert.equal(balance.valueOf(), 0, "charity balance should be empty after escape"); + }) + .then(done) + .catch(done); + }); + +}); diff --git a/test/impact.js b/test/impact.js new file mode 100644 index 0000000..633eae7 --- /dev/null +++ b/test/impact.js @@ -0,0 +1,577 @@ +var ImpactRegistry = artifacts.require("ImpactRegistry"); + +contract('Singe impactRegistry donation', function(accounts) { + var main = accounts[0]; + var donor1 = accounts[1]; + var master = accounts[2]; + var impactRegistry; + + it("should attach master contract", function(done) { + ImpactRegistry.deployed().then(function(instance) { + impactRegistry = instance; + return impactRegistry.setMasterContract(master, {from: main}); + }).then(function() { + return impactRegistry.masterContract(); + }).then(function(address) { + return assert.equal(address, master, "Master contract hasn't been set up correctly"); + }) + .then(done) + .catch(done); + }); + + it("should register impactRegistry from donor1", function (done) { + impactRegistry.registerDonation(donor1, 10, {from: master}).then(function () { + return impactRegistry.getBalance.call(donor1, {from: main}) + }).then(function (balance) { + return assert.equal(balance.valueOf(), 10, "10 wasn't in registry after donation"); + }) + .then(done) + .catch(done); + }); + + it("should register outcome", function (done) { + impactRegistry.registerOutcome("outcome", 10, {from: master}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + return assert.equal(count.valueOf(), 0, "Should be no impactRegistry before linking"); + }) + .then(done) + .catch(done); + }); + + it("should link impactRegistry", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 1, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 10, "Should link all of the impactRegistry"); + return impactRegistry.getImpactDonor.call("outcome", 0) + }).then(function (donor) { + assert.equal(donor, donor1, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor1) + }).then(function (val) { + assert.equal(val.valueOf(), 10, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor1); + }).then(function (balance) { + assert.equal(balance.valueOf(), 0, "Should be empty balance after cleaning account"); + }) + .then(done) + .catch(done); + }); + +}); + +contract('Donation below unit', function(accounts) { + var main = accounts[0]; + var donor1 = accounts[1]; + + it("should attach master contract", function(done) { + ImpactRegistry.deployed().then(function(instance) { + impactRegistry = instance; + return impactRegistry.setMasterContract(main, {from: main}); + }).then(function() { + return impactRegistry.masterContract(); + }).then(function(address) { + return assert.equal(address, main, "Master contract hasn't been set up correctly"); + }) + .then(done) + .catch(done); + }); + + it("should correctly set an unit of impact", function(done) { + impactRegistry.setUnit(10, {from: main}).then(function() { + return impactRegistry.unit.call(); + }).then(function(unit) { + return assert.equal(unit, 10, "Unit for impact reconciliation hasn't been set up correctly"); + }) + .then(done) + .catch(done); + }); + + it("should register impactRegistry from donor1", function (done) { + impactRegistry.registerDonation(donor1, 7, {from: main}).then(function () { + return impactRegistry.getBalance.call(donor1, {from: main}) + }).then(function (balance) { + return assert.equal(balance.valueOf(), 7, "7 wasn't in registry after donation"); + }) + .then(done) + .catch(done); + }); + + + it("should register outcome", function (done) { + impactRegistry.registerOutcome("outcome", 7, {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + return assert.equal(count.valueOf(), 0, "Should be no impactRegistry before linking"); + }) + .then(done) + .catch(done); + }); + + it("should link impactRegistry", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 1, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 7, "Should link all of the impactRegistry"); + return impactRegistry.getImpactDonor.call("outcome", 0) + }).then(function (donor) { + assert.equal(donor, donor1, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor1) + }).then(function (val) { + assert.equal(val.valueOf(), 7, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor1); + }).then(function (balance) { + assert.equal(balance.valueOf(), 0, "Should be empty balance after cleaning account"); + }) + .then(done) + .catch(done); + }); + + +}); + +contract('Donation above unit', function(accounts) { + var main = accounts[0]; + var donor1 = accounts[1]; + + it("should attach master contract", function(done) { + ImpactRegistry.deployed().then(function(instance) { + impactRegistry = instance; + return impactRegistry.setMasterContract(main, {from: main}); + }).then(function() { + return impactRegistry.masterContract(); + }).then(function(address) { + return assert.equal(address, main, "Master contract hasn't been set up correctly"); + }) + .then(done) + .catch(done); + }); + + it("should correctly set an unit of impact", function(done) { + impactRegistry.setUnit(10, {from: main}).then(function() { + return impactRegistry.unit.call(); + }).then(function(unit) { + return assert.equal(unit, 10, "Unit for impact reconciliation hasn't been set up correctly"); + }) + .then(done) + .catch(done); + }); + + it("should register impactRegistry from donor1", function (done) { + impactRegistry.registerDonation(donor1, 13, {from: main}).then(function () { + return impactRegistry.getBalance.call(donor1, {from: main}) + }).then(function (balance) { + return assert.equal(balance.valueOf(), 13, "13 wasn't in registry after donation"); + }) + .then(done) + .catch(done); + }); + + it("should register outcome", function (done) { + impactRegistry.registerOutcome("outcome", 13, {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + return assert.equal(count.valueOf(), 0, "Should be no impactRegistry before linking"); + }) + .then(done) + .catch(done); + }); + + + it("should link impactRegistry in first step", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 1, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 10, "Should link unit value"); + return impactRegistry.getImpactDonor.call("outcome", 0) + }).then(function (donor) { + assert.equal(donor, donor1, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor1) + }).then(function (val) { + assert.equal(val.valueOf(), 10, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor1); + }).then(function (balance) { + assert.equal(balance.valueOf(), 3, "Should have some balance after first step"); + }) + .then(done) + .catch(done); + }); + + it("should link the rest of the impactRegistry in second step", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 1, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 13, "Should link unit value"); + return impactRegistry.getImpactDonor.call("outcome", 0) + }).then(function (donor) { + assert.equal(donor, donor1, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor1) + }).then(function (val) { + assert.equal(val.valueOf(), 13, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor1); + }).then(function (balance) { + assert.equal(balance.valueOf(), 0, "Should have no balance after second"); + }) + .then(done) + .catch(done); + }); + + +}); + +contract('Two donations', function(accounts) { + var main = accounts[0]; + var donor1 = accounts[1]; + var donor2 = accounts[2]; + + it("should attach master contract", function(done) { + ImpactRegistry.deployed().then(function(instance) { + impactRegistry = instance; + return impactRegistry.setMasterContract(main, {from: main}); + }).then(function() { + return impactRegistry.masterContract(); + }).then(function(address) { + return assert.equal(address, main, "Master contract hasn't been set up correctly"); + }) + .then(done) + .catch(done); + }); + + it("should correctly set an unit of impact", function(done) { + impactRegistry.setUnit(10, {from: main}).then(function() { + return impactRegistry.unit.call(); + }).then(function(unit) { + return assert.equal(unit, 10, "Unit for impact reconciliation hasn't been set up correctly"); + }) + .then(done) + .catch(done); + }); + + it("should register impactRegistry from donor1", function (done) { + impactRegistry.registerDonation(donor1, 15, {from: main}).then(function () { + return impactRegistry.getBalance.call(donor1, {from: main}) + }).then(function (balance) { + return assert.equal(balance.valueOf(), 15, "15 wasn't in registry after donation"); + }) + .then(done) + .catch(done); + }); + + it("should register impactRegistry from donor2", function (done) { + impactRegistry.registerDonation(donor2, 20, {from: main}).then(function () { + return impactRegistry.getBalance.call(donor2, {from: main}) + }).then(function (balance) { + return assert.equal(balance.valueOf(), 20, "20 wasn't in registry after donation"); + }) + .then(done) + .catch(done); + }); + + it("should register outcome", function (done) { + impactRegistry.registerOutcome("outcome", 30, {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + return assert.equal(count.valueOf(), 0, "Should be no impactRegistry before linking"); + }) + .then(done) + .catch(done); + }); + + it("1 step (10 from donor1)", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 1, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 10, "Should link unit value"); + return impactRegistry.getImpactDonor.call("outcome", 0) + }).then(function (donor) { + assert.equal(donor, donor1, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor1) + }).then(function (val) { + assert.equal(val.valueOf(), 10, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor1); + }).then(function (balance) { + assert.equal(balance.valueOf(), 5, "Should have some balance after first step"); + }) + .then(done) + .catch(done); + }); + + + it("2 step (10 from donor2)", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 2, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 20, "Should link unit value"); + return impactRegistry.getImpactDonor.call("outcome", 1) + }).then(function (donor) { + assert.equal(donor, donor2, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor2) + }).then(function (val) { + assert.equal(val.valueOf(), 10, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor2); + }).then(function (balance) { + assert.equal(balance.valueOf(), 10, "Should have balance left for donor2"); + }) + .then(done) + .catch(done); + }); + + it("3 step (5 from donor1)", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 2, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 25, "Should link unit value"); + return impactRegistry.getImpactDonor.call("outcome", 0) + }).then(function (donor) { + assert.equal(donor, donor1, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor1) + }).then(function (val) { + assert.equal(val.valueOf(), 15, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor1); + }).then(function (balance) { + assert.equal(balance.valueOf(), 0, "Should have balance left for donor1"); + }) + .then(done) + .catch(done); + }); + + it("4 step (5 from donor2)", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 2, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 30, "Should link unit value"); + return impactRegistry.getImpactDonor.call("outcome", 1) + }).then(function (donor) { + assert.equal(donor, donor2, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor2) + }).then(function (val) { + assert.equal(val.valueOf(), 15, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor2); + }).then(function (balance) { + assert.equal(balance.valueOf(), 5, "Should have balance left for donor2"); + }) + .then(done) + .catch(done); + }); + + +}); + +contract('Two donations: 25 + 25', function(accounts) { + var main = accounts[0]; + var donor1 = accounts[1]; + var donor2 = accounts[2]; + var judge = accounts[3]; + + it("should attach master contract", function(done) { + ImpactRegistry.deployed().then(function(instance) { + impactRegistry = instance; + return impactRegistry.setMasterContract(main, {from: main}); + }).then(function() { + return impactRegistry.masterContract(); + }).then(function(address) { + return assert.equal(address, main, "Master contract hasn't been set up correctly"); + }) + .then(done) + .catch(done); + }); + + it("should correctly set an unit of impact", function(done) { + impactRegistry.setUnit(10, {from: main}).then(function() { + return impactRegistry.unit.call(); + }).then(function(unit) { + return assert.equal(unit, 10, "Unit for impact reconciliation hasn't been set up correctly"); + }) + .then(done) + .catch(done); + }); + + + it("should register impactRegistry from donor1", function (done) { + impactRegistry.registerDonation(donor1, 25, {from: main}).then(function () { + return impactRegistry.getBalance.call(donor1, {from: main}) + }).then(function (balance) { + return assert.equal(balance.valueOf(), 25, "25 wasn't in registry after donation"); + }) + .then(done) + .catch(done); + }); + + + it("should register impactRegistry from donor2", function (done) { + impactRegistry.registerDonation(donor2, 25, {from: main}).then(function () { + return impactRegistry.getBalance.call(donor2, {from: main}) + }).then(function (balance) { + return assert.equal(balance.valueOf(), 25, "25 wasn't in registry after donation"); + }) + .then(done) + .catch(done); + }); + + it("should register outcome", function (done) { + impactRegistry.registerOutcome("outcome", 50, {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + return assert.equal(count.valueOf(), 0, "Should be no impactRegistry before linking"); + }) + .then(done) + .catch(done); + }); + + it("1 step (10 from donor1)", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 1, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 10, "Should link unit value"); + return impactRegistry.getImpactDonor.call("outcome", 0) + }).then(function (donor) { + assert.equal(donor, donor1, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor1) + }).then(function (val) { + assert.equal(val.valueOf(), 10, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor1); + }).then(function (balance) { + assert.equal(balance.valueOf(), 15, "Should have some balance after first step"); + }) + .then(done) + .catch(done); + }); + + it("2 step (10 from donor2)", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 2, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 20, "Should link unit value"); + return impactRegistry.getImpactDonor.call("outcome", 1) + }).then(function (donor) { + assert.equal(donor, donor2, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor2) + }).then(function (val) { + assert.equal(val.valueOf(), 10, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor2); + }).then(function (balance) { + assert.equal(balance.valueOf(), 15, "Should have balance left for donor2"); + }) + .then(done) + .catch(done); + }); + + it("3 step (10 from donor1)", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 2, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 30, "Should link unit value"); + return impactRegistry.getImpactDonor.call("outcome", 0) + }).then(function (donor) { + assert.equal(donor, donor1, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor1) + }).then(function (val) { + assert.equal(val.valueOf(), 20, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor1); + }).then(function (balance) { + assert.equal(balance.valueOf(), 5, "Should have balance left for donor1"); + }) + .then(done) + .catch(done); + }); + + it("4 step (10 from donor2)", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 2, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 40, "Should link unit value"); + return impactRegistry.getImpactDonor.call("outcome", 1) + }).then(function (donor) { + assert.equal(donor, donor2, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor2) + }).then(function (val) { + assert.equal(val.valueOf(), 20, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor2); + }).then(function (balance) { + assert.equal(balance.valueOf(), 5, "Should have balance left for donor2"); + }) + .then(done) + .catch(done); + }); + + it("5 step (5 from donor1)", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 2, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 45, "Should link unit value"); + return impactRegistry.getImpactDonor.call("outcome", 0) + }).then(function (donor) { + assert.equal(donor, donor1, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor1) + }).then(function (val) { + assert.equal(val.valueOf(), 25, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor1); + }).then(function (balance) { + assert.equal(balance.valueOf(), 0, "Should have balance left for donor1"); + }) + .then(done) + .catch(done); + }); + + it("6 step (5 from donor2)", function (done) { + impactRegistry.linkImpact("outcome", {from: main}).then(function () { + return impactRegistry.getImpactCount.call("outcome", {from: main}) + }).then(function (count) { + assert.equal(count.valueOf(), 2, "Should have impactRegistry after linking"); + return impactRegistry.getImpactLinked.call("outcome") + }).then(function (linked) { + assert.equal(linked.valueOf(), 50, "Should link unit value"); + return impactRegistry.getImpactDonor.call("outcome", 1) + }).then(function (donor) { + assert.equal(donor, donor2, "Should set donor for impactRegistry"); + return impactRegistry.getImpactValue.call("outcome", donor2) + }).then(function (val) { + assert.equal(val.valueOf(), 25, "Should set value for impactRegistry"); + return impactRegistry.getBalance.call(donor2); + }).then(function (balance) { + assert.equal(balance.valueOf(), 0, "Should have balance left for donor2"); + }) + .then(done) + .catch(done); + }); + +}); diff --git a/test/mock-validation.js b/test/mock-validation.js new file mode 100644 index 0000000..cfc5e73 --- /dev/null +++ b/test/mock-validation.js @@ -0,0 +1,28 @@ +var MockValidation = artifacts.require("MockValidation"); + +contract('Mock Validation', function(accounts) { + var mockValidation; + + it("should validate", function(done) { + MockValidation.deployed().then(function(instance) { + mockValidation = instance; + return mockValidation.validate("Outcome", 100); + }).then(function() { + return mockValidation.getValidationsCount.call(); + }).then(function(count) { + assert.equal(count, 1, "Should register validation"); + return mockValidation.getValidatorByIndex.call(0); + }).then(function(validator) { + assert.equal(validator, accounts[0], "Should register correct validator"); + return mockValidation.getOutcomeByIndex.call(0); + }).then(function(outcome) { + assert.equal(outcome, "Outcome", "Should register correct outcome"); + return mockValidation.getValueByIndex.call(0); + }).then(function(value) { + return assert.equal(value, 100, "Should register correct value"); + }) + .then(done) + .catch(done); + }); + +}); diff --git a/test/multiple-donations.js b/test/multiple-donations.js new file mode 100644 index 0000000..6641a80 --- /dev/null +++ b/test/multiple-donations.js @@ -0,0 +1,99 @@ +var Charity = artifacts.require("Charity"); +var SimpleContractRegistry = artifacts.require("SimpleContractRegistry"); +var AliceToken = artifacts.require("AliceToken"); +var ImpactRegistry = artifacts.require("ImpactRegistry"); + +contract('Multiple donations', function(accounts) { + var main = accounts[0]; + var judge = accounts[3]; + var beneficiary = accounts[4]; + var charity, token, contractProvider, impactRegistryRegistry; + + it("should link charity to judge", function(done) { + Charity.deployed().then(function(instance) { + charity = instance; + return charity.setJudge(judge, 10, {from: main}) + }).then(function() { + return charity.judgeAddress.call(); + }).then(function(address) { + return assert.equal(address, judge, "Judge address wasn't set correctly"); + }) + .then(done) + .catch(done); + }); + + it("should link charity to beneficiary", function(done) { + charity.setBeneficiary(beneficiary, 10, {from: main}).then(function() { + return charity.beneficiaryAddress.call(); + }).then(function(address) { + return assert.equal(address, beneficiary, "Beneficiary address wasn't set correctly"); + }) + .then(done) + .catch(done); + }); + + it("should link charity to contract provider", function(done) { + SimpleContractRegistry.deployed().then(function(instance) { + contractProvider = instance; + return charity.setContractProvider(contractProvider.address, {from: main}) + }).then(function() { + return charity.CONTRACT_PROVIDER_ADDRESS.call(); + }).then(function(address) { + return assert.equal(address, contractProvider.address, "Contract provider address wasn't set correctly"); + }) + .then(done) + .catch(done); + }); + + it("should get token contract from registry", function(done) { + SimpleContractRegistry.deployed().then(function(registryInstance) { + return registryInstance.contracts.call('digitalGBP'); + }).then(function(address) { + token = AliceToken.at(address); + return assert.notEqual(token, undefined); + }) + .then(done) + .catch(done); + }); + + it("should donate from multiple accounts", function(done) { + + for (var i = 0; i < 50; i++) { + var account = accounts[5+i]; + charity.notify(account, 10, {from: main}); + } + + token.mint(charity.address, 500, {from: main}).then(function () { + return token.balanceOf.call(charity.address, {from: main}) + }).then(function (balance) { + return assert.equal(balance.valueOf(), 500, "500 wasn't minted"); + }) + .then(done) + .catch(done); + }); + + + it("should unlock outcome from multiple accounts", function (done) { + token.balanceOf.call(charity.address).then(function(balance) { + return assert.equal(balance.valueOf(), 500, "500 wasn't in charity before unlocking outcome"); + }).then(function() { + return token.balanceOf.call(beneficiary); + }).then(function(balance) { + return assert.equal(balance.valueOf(), 0, "0 wasn't in beneficiary before unlocking outcome"); + }).then(function() { + return charity.unlockOutcome("Outcome", 500, {from: judge}); + }).then(function() { + return token.balanceOf.call(charity.address); + }).then(function(balance) { + return assert.equal(balance.valueOf(), 0, "0 wasn't in charity after unlocking outcome"); + }).then(function() { + return token.balanceOf.call(beneficiary); + }).then(function(balance) { + return assert.equal(balance.valueOf(), 500, "500 wasn't in beneficiary after unlocking outcome"); + }) + .then(done) + .catch(done); + }); + + +}); \ No newline at end of file diff --git a/test/single-donation.js b/test/single-donation.js new file mode 100644 index 0000000..661934c --- /dev/null +++ b/test/single-donation.js @@ -0,0 +1,138 @@ +var Charity = artifacts.require("Charity"); +var SimpleContractRegistry = artifacts.require("SimpleContractRegistry"); +var AliceToken = artifacts.require("AliceToken"); +var ImpactRegistry = artifacts.require("ImpactRegistry"); + +contract('Single donation', function(accounts) { + var main = accounts[0]; + var donor = accounts[1]; + var judge = accounts[3]; + var beneficiary = accounts[4]; + var charity, token, contractProvider, impactRegistry; + + it("should link charity to judge", function(done) { + Charity.deployed().then(function(instance) { + charity = instance; + return charity.setJudge(judge, 10, {from: main}) + }).then(function() { + return charity.judgeAddress.call(); + }).then(function(address) { + return assert.equal(address, judge, "Judge address wasn't set correctly"); + }) + .then(done) + .catch(done); + }); + + it("should link charity to beneficiary", function(done) { + charity.setBeneficiary(beneficiary, 10, {from: main}).then(function() { + return charity.beneficiaryAddress.call(); + }).then(function(address) { + return assert.equal(address, beneficiary, "Beneficiary address wasn't set correctly"); + }) + .then(done) + .catch(done); + }); + + it("should link charity to contract provider", function(done) { + SimpleContractRegistry.deployed().then(function(instance) { + contractProvider = instance; + return charity.setContractProvider(contractProvider.address, {from: main}) + }).then(function() { + return charity.CONTRACT_PROVIDER_ADDRESS.call(); + }).then(function(address) { + return assert.equal(address, contractProvider.address, "Contract provider address wasn't set correctly"); + }) + .then(done) + .catch(done); + }); + + it("should link charity to impact registry", function(done) { + ImpactRegistry.deployed().then(function(instance) { + impactRegistry = instance; + return charity.setImpactRegistry(impactRegistry.address, {from: main}) + }).then(function() { + return charity.IMPACT_REGISTRY_ADDRESS.call(); + }).then(function(address) { + return assert.equal(address, impactRegistry.address, "Impact registry address wasn't set correctly"); + }) + .then(done) + .catch(done); + }); + + it("should update balance after notification", function (done) { + charity.notify(donor, 10, {from: main}).then(function () { + return charity.total() + }).then(function (total) { + return assert.equal(total.valueOf(), 10, "10 wasn't in token charity balance after the donation"); + }).then(function() { + return charity.getBalance.call(donor) + }).then(function(balance) { + return assert.equal(balance.valueOf(), 10, "10 wasn't in donor balance"); + }) + .then(done) + .catch(done); + }); + + it("should get token contract from registry", function(done) { + SimpleContractRegistry.deployed().then(function(registryInstance) { + return registryInstance.contracts.call('digitalGBP'); + }).then(function(address) { + token = AliceToken.at(address); + return assert.notEqual(token, undefined); + }) + .then(done) + .catch(done); + }); + + it("should mint tokens", function (done) { + token.mint(charity.address, 10, {from: main, gas: 300000}).then(function () { + return token.balanceOf.call(charity.address, {from: main}) + }).then(function (balance) { + return assert.equal(balance.valueOf(), 10, "10 wasn't minted"); + }) + .then(done) + .catch(done); + }); + + it("should unlock outcome", function (done) { + token.balanceOf.call(charity.address).then(function (balance) { + return assert.equal(balance.valueOf(), 10, "10 wasn't in charity before unlocking outcome"); + }).then(function () { + return token.balanceOf.call(beneficiary); + }).then(function (balance) { + return assert.equal(balance.valueOf(), 0, "0 wasn't in beneficiary before unlocking outcome"); + }).then(function () { + return charity.unlockOutcome("Outcome", 10, {from: judge}); + }).then(function() { + return token.balanceOf.call(charity.address); + }).then(function (balance) { + return assert.equal(balance.valueOf(), 0, "0 wasn't in charity after unlocking outcome"); + }).then(function () { + return token.balanceOf.call(beneficiary); + }).then(function (balance) { + return assert.equal(balance.valueOf(), 10, "10 wasn't in beneficiary after unlocking outcome"); + }) + .then(done) + .catch(done); + }); + + it("should link impact", function (done) { + impactRegistry.linkImpact("Outcome", {from: main}).then(function () { + return impactRegistry.getImpactLinked.call("Outcome", {from: main}) + }).then(function(result) { + assert.equal(result.valueOf(), 10, "10 wasn't linked after first link call"); + return impactRegistry.getImpactCount.call("Outcome", {from: main}) + }).then(function(result) { + assert.equal(result.valueOf(), 1, "1 wasn't number of impact after first link call"); + return impactRegistry.getImpactDonor.call("Outcome", 0, {from: main}) + }).then(function(result) { + assert.equal(result, donor, "donor address wasn't address after first link call"); + return impactRegistry.getImpactValue.call("Outcome", donor, {from: main}) + }).then(function(result) { + assert.equal(result.valueOf(), 10, "10 wasn't impact value after first link call"); + }) + .then(done) + .catch(done); + }); + +}); diff --git a/truffle.js b/truffle.js new file mode 100644 index 0000000..2742002 --- /dev/null +++ b/truffle.js @@ -0,0 +1,22 @@ +var DefaultBuilder = require("truffle-default-builder"); + +module.exports = { + build: new DefaultBuilder({ + "index.html": "index.html", + "app.js": [ + "javascripts/app.js" + ], + "app.css": [ + "stylesheets/app.css", + "stylesheets/AdminLTE.min.css" + ] + }), + networks: { + dev: { + network_id: "*", + gas: 4000000, + host: 'localhost', + port: '8545' + } + } +};