├── .gitattributes ├── .gitignore ├── .idea ├── WikiGit.iml ├── jsLibraryMappings.xml ├── libraries │ ├── Generated_files.xml │ ├── meteor_packages_auto_import_browser.xml │ └── meteor_packages_auto_import_npm.xml ├── misc.xml ├── modules.xml ├── preferred-vcs.xml ├── vcs.xml ├── watcherTasks.xml └── workspace.xml ├── LICENSE ├── README.md ├── Technical Details.md ├── assets └── project_structure.png ├── client_ui ├── .meteor │ ├── .finished-upgraders │ ├── .gitignore │ ├── .id │ ├── packages │ ├── platforms │ ├── release │ └── versions ├── client │ ├── main.css │ ├── main.html │ └── main.js ├── imports │ ├── abi │ │ └── mainABI.json │ ├── daemon │ │ ├── git-ipfs.coffee │ │ ├── git-ipfs.js │ │ └── git-ipfs.js.map │ ├── objects │ │ └── dasp.coffee │ └── ui │ │ ├── dasp_dashboard.coffee │ │ ├── dasp_dashboard.css │ │ ├── dasp_dashboard.html │ │ ├── dasp_registry.coffee │ │ └── dasp_registry.html ├── package-lock.json └── package.json └── dev ├── build └── contracts │ ├── Dao.json │ ├── ERC20.json │ ├── GitHandler.json │ ├── Main.json │ ├── MemberHandler.json │ ├── Migrations.json │ ├── Module.json │ ├── TasksHandler.json │ ├── Vault.json │ └── a_dummy ├── contracts ├── dao.sol ├── main.sol ├── member_handler.sol ├── repo_handler.sol ├── tasks_handler.sol ├── token.sol ├── truffle_migrations.sol └── vault.sol ├── migrations ├── 1_initial_migration.js ├── 2_core_contracts.coffee ├── 2_core_contracts.js ├── 2_core_contracts.js.map └── config.json ├── package-lock.json ├── package.json ├── test ├── voting.coffee ├── voting.js └── voting.js.map └── truffle.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff: 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/dictionaries 10 | 11 | # Sensitive or high-churn files: 12 | .idea/**/dataSources/ 13 | .idea/**/dataSources.ids 14 | .idea/**/dataSources.xml 15 | .idea/**/dataSources.local.xml 16 | .idea/**/sqlDataSources.xml 17 | .idea/**/dynamic.xml 18 | .idea/**/uiDesigner.xml 19 | 20 | # Gradle: 21 | .idea/**/gradle.xml 22 | .idea/**/libraries 23 | 24 | # CMake 25 | cmake-build-debug/ 26 | 27 | # Mongo Explorer plugin: 28 | .idea/**/mongoSettings.xml 29 | 30 | ## File-based project format: 31 | *.iws 32 | 33 | ## Plugin-specific files: 34 | 35 | # IntelliJ 36 | out/ 37 | 38 | # mpeltonen/sbt-idea plugin 39 | .idea_modules/ 40 | 41 | # JIRA plugin 42 | atlassian-ide-plugin.xml 43 | 44 | # Cursive Clojure plugin 45 | .idea/replstate.xml 46 | 47 | # Crashlytics plugin (for Android Studio and IntelliJ) 48 | com_crashlytics_export_strings.xml 49 | crashlytics.properties 50 | crashlytics-build.properties 51 | fabric.properties 52 | 53 | -------------------------------------------------------------------------------- /.idea/WikiGit.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/libraries/Generated_files.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/libraries/meteor_packages_auto_import_browser.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/preferred-vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ApexVCS 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/watcherTasks.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 22 | 29 | 30 | 47 | 54 | 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WikiGit 2 | 3 | World's first ecosystem for decentralized innovation & cooperation, powered by Ethereum. 4 | 5 | **TL;DR:** WikiGit empowers projects with governing, crowdsourcing, and crowdfunding mechanisms, which form reinforcement loops that provide projects with rapid and sustainable growth from their moments of creation. 6 | 7 | Current version: 0.2.0 (Grasshopper) 8 | 9 | ## Introduction 10 | 11 | ### The Vision 12 | 13 | WikiGit implements an ecosystem for creating and interacting with what we call **Decentralized Autonomous Self-sustaining Projects (DASPs)**. In the simplest terms, a DASP is a reputation-based decentralized organization that comes with its own Git repository, governing mechanism, crowdfunding mechanism, and crowdsourcing mechanism. By offering any project with governance infrastructure as well as funding and recruiting resources which are comparable to those available to giant corporations, WikiGit will allow ingenious ideas to swiftly incarnate into full-fledged projects that are self-sustaining in both finances and talent, with minimal cost and hindrances, similar to how Wikipedia allowed an entire encyclopedia to spontaneously come into being. In fact, in the near future the WikiGit project itself will be a DASP powered by WikiGit. 14 | 15 | In its complete form, WikiGit will realize the following scenario: One or more person has a great idea, creates a DASP with a cost of ~US$1, writes a white paper or something similar that explains the idea, finds some likeminded people online to form an initial team, pitch in and/or crowdfund some Ether and use them to post tasks onto the crowdsourcing platform, personally contribute to the project and/or recruit some interested freelancers as part-time or full-time contributors; and voila, a self-sustaining project is born, with its own funding, government, contributors, freelancers, and shareholders, and the initial idea would blossom into something wonderful through the power of the crowd. 16 | 17 | ### The Project 18 | 19 |

Project Structure

20 | 21 | The WikiGit project consists of three parts: the ***back end***, which is a set of Ethereum smart contracts that govern all of the core logics in a DASP; the ***front end***, which is a client-side web app that acts as a UI for interacting with the back end as well as a relay for connecting a DASP with its Git repository hosted by IPFS; and the ***developer tools***, which is an application for deploying a DASP onto the Ethereum blockchain. A particular DASP is defined by its back end, and different DASPs usually differ in their back ends; the front end and the developer tools are shared by all DASPs, although different implementations can be made as long as they are compatible with the back end's API. 22 | 23 | ### Project State 24 | 25 | Currently, WikiGit is in its early stages and is being worked by a handful of people. We have finished a preliminary implementation of the back end, a partial implementation of the front end (specifically the UI and back end for interacting with the IPFS Git repo and the member manifest) and the back end of the developer tools. We will do my best to find some more contributors, and anyone is welcome to join! (\***cough**\*you\***cough**\*) 26 | 27 | ## Usage 28 | 29 | ### Front End UI 30 | 31 | #### Prerequisites 32 | 33 | * Node.js ^6.11.0 34 | * Meteor ^1.6 35 | * Coffeescript ^2.0.1 36 | * Browser with web3.js support 37 | 38 | #### Running 39 | 40 | Under /client_ui/, enter the following in command line: 41 | 42 | > npm install 43 | > 44 | > coffee -c . 45 | > 46 | > meteor run 47 | 48 | The UI would be running at http://localhost:3000 49 | 50 | Note: you need the back end smart contracts deployed on the current network first in order to use the UI. 51 | 52 | ### Developer Tools 53 | 54 | #### Prerequisites 55 | 56 | * Node.js ^6.11.0 57 | * Truffle ^4.0.1 58 | * Coffeescript ^2.0.1 59 | * TestRPC ^4.1.3 (or Geth) 60 | 61 | #### Running 62 | 63 | First, edit /dev/migrate/config.json to include desired migration parameters. Then, under /dev/, enter the following in command line: 64 | 65 | > testrpc -l 5500000 66 | 67 | Or, if you want to use Geth, 68 | 69 | > geth -rpc -unlock 0 70 | 71 | In a new command line instance, enter 72 | 73 | > npm install 74 | > 75 | > truffle migrate 76 | 77 | The contracts would be deployed onto the specified network. 78 | 79 | ## Technical Details 80 | 81 | Details of the project is in [Technical Details.md](https://github.com/Project-WikiGit/WikiGit/blob/master/Technical%20Details.md) 82 | 83 | --- 84 | 85 | ## The WikiGit Ecosystem, And Why It's Awesome 86 | 87 | The WikiGit ecosystem contains three major components: ***Self-governing Projects, a Crowdsourcing Platform, and a Crowdfunding Platform***. Each of the components have been implemented individually before, but the existing products all fail to cooperate and form a complete ecosystem: 88 | 89 | * **GitHub** has been the go-to place for hosting open source projects, with support for pull requests that utilize naïve crowdsourcing to help develop projects, as well as other project management tools (issues, projects, wiki, etc.) However, GitHub does not account for many other crucial parts of creating and maintaining a project, such as 90 | 91 | * obtaining funding; 92 | * making collective decisions that shape the future of a project; 93 | * providing financial incentives for contribution; 94 | * systematically recognizing someone's contribution to the project. 95 | 96 | * **Topcoder** is currently the prominent crowdsourcing platform for software development and UX design, where developers and designers compete to submit the best solution and receive the bounty. It has helped companies large and small to efficiently solve problems that they aren't familiar with/specialized in without the need to seek out full-time talents, as well as offers companies a new way of allocating project resources. However, Topcoder is less of a ***crowdsourcing*** platform and more of an ***outsourcing*** platform, because the companies and talents only have a tangential relationship, in that 97 | 98 | * talents can only work on relatively insignificant tasks nonessential to the companies' products, cannot continue working on projects they found interesting, and cannot join companies they found interesting; 99 | * companies do not have a formal channel for recruiting talent as full-time or part-time employees, and thus cannot rely on crowdsourcing as the main method for product development. 100 | 101 | * **Kickstarter** is one of the most successful crowdfunding platforms, where creators with brilliant ideas can fund their projects through the financial support of people who love their ideas, and ordinary users can easily fund projects they find interesting and receive numerous benefits depending on the amount of their donation. However, 102 | 103 | * Kickstarter patrons who have the necessary skills cannot easily contribute to the project through means other than financial support, which is bad for both the patrons and the creators; 104 | * project teams are not necessarily transparent in sharing the state of their projects; 105 | * there is no formal way for patrons to suggest how the projects should be developed and influence the outcome of the projects. 106 | 107 | * **Initial Coin Offering (ICO)** is a crowdfunding method not dissimilar to IPOs that recently emerged on the Ethereum blockchain, where project teams offer custom ERC20 tokens in exchange for Ether, a type of cryptocurrency. The tokens are usually claimed to have utility on the applications that the teams are building. Funders would judge a project's feasibility based on its whitepaper, the current state of the project, the roadmap, etc., and send Ether to the project's team during the ICO if they feel optimistic about the project's future. ICOs have seen amazing success, with small teams raising millions of US$ of funding, but it suffers many severe problems that most people in the space have observed: 108 | 109 | * any small team with a whitepaper and a good-looking website seem to have been able to raise millions, which many observers have claimed as "scammy", and in fact many of the projects *are* scams. This problem stems from the lack of transparency in the projects. 110 | * Most of the time, the applications that the project teams are building don't actually require a custom token to function, and Ether, the native currency of Ethereum, would easily suffice. This would lead to a fragmented blockchain, where a user must hold a type of token for each application one is using. 111 | * IPO usually occurs before a team has any presentable product, and the uncertainty would a) scare many investors away and b) make it difficult for investors to determine the feasibility of a project. 112 | 113 | 114 | WikiGit aims to solve all of the abovementioned problems through organically combining self-governing projects, crowdsourcing, and crowdfunding to form a vibrant ecosystem for innovation & cooperation, where 115 | 116 | * project teams can easily obtain funding, recruit talented people, and use bounties to crowdsource tasks; 117 | * talents can do freelancing, work on projects they're excited about without any barriers, and join any project team with minimal red-tape; 118 | * contributors and patrons can vote on proposals to make decisions for the project, their votes are weighted by the amount of their contribution and investment, and neither party would be able to dominate the other; 119 | * investors can invest in projects with minimal risk and uncertainty, as everything in a project is transparent and instead of a single million-dollar ICO, a project will have many small-scale coin offerings spread out across different phases of the project. 120 | 121 | The details of the WikiGit ecosystem will be explained below. 122 | 123 | ### Contribution-based Fair Governance 124 | 125 | In a DASP, members use voting sessions to make collective decisions about the project. Each member's vote is weighted not only by how much shares (tokens issued during coin offerings) they hold, but also the member's ***reputation score***: a metric for measuring how involved a member is with the project, determined mostly by **the number of tasks** a member has completed. 126 | 127 | Each member has two types of reputation: *good reputation* and *bad reputation*. A member can earn good reputation through completing tasks, and the amount rewarded depends on the difficulty of the task. A member would receive bad reputation when the member submits a task solution that's deemed malicious. A member's reputation score is specific to each DASP, but there will be a platform for viewing a user's reputation score in all of the DASPs they're in. 128 | 129 | The reputation score can be used by project teams to determine, say, whether or not to add a user as a full/part time member. The hope is that the reputation system can 130 | 131 | * incentivize users to contribute to projects they're passionate about, since a better reputation means more say in project decisions; 132 | * incentivize users to behave honestly, since even a small amount of bad reputation may discourage project teams from recruiting a user, thus tainting all the good reputation a user have earned; 133 | * **incentivize users to complete all the low-hanging tasks across DASPs to get a better overall reputation, so that** 134 | * **users are encouraged to discover different projects, and** 135 | * **even infant projects without existing funds can get tasks done by offering reputation rewards.** 136 | 137 | Of course, since reputation systems are complex and need thorough designing, the system described above is only an early version and may be subject to change. 138 | 139 | A voting-based governing system where both financial support and actual contribution to the project are taken into account can, perhaps, be truly "fair", in that both the investors' and the contributors' interests are represented, instead of only the investors' interests---what we have in today's corporate world. 140 | 141 | ### Zero-barrier Crowdsourcing & Talent Recruitment 142 | 143 | In WikiGit, a user can choose to complete any task on the crowdsourcing platform as a freelancer and get paid, without asking for any authorization etc.. This is the existing norm on crowdsourcing platforms, but what differentiates WikiGit is that WikiGit is project-oriented rather than task-oriented, meaning that the user can continue working on a particular project if they found it interesting, and even attempt to join the project team backed by all the reputation they earned through completing tasks. 144 | 145 | **Coupled with the fact that users are incentivized to discover projects and freelance for them (see *Contribution-based Fair Governance*, 3rd paragraph), the WikiGit ecosystem automatically pairs users with projects they would be interested in, which in turn solves the problem of finding talents for DASPs.** 146 | 147 | ### Transparent & Effective Coin Offerings 148 | 149 | WikiGit DASPs use coin offerings as the main method for funding the projects. Compared to the traditional projects that use ICOs to fund themselves after they have their white papers and minimalistic websites, DASPs have the following advantages when it comes to funding: 150 | 151 | * When evaluating the state of a project, investors can base their decisions on maximal information gathered from looking at the project's Git repo, checking out current task listings, and viewing the team members' reputation and contribution history. This ensures that investors can make far more educated and low-risk decisions, and that only honest projects with active development can stand out to investors. 152 | * In contrast, a traditional ICO only provides investors with the team's grand vision and some rough roadmap, and at most some generic descriptions of the work currently being done. Yikes. 153 | * DASPs can use multiple small-scale coin offerings spread throughout different phases of the project as their source of funding, rather than single million-dollar ICOs held before the project team had even made any tangible progress. It cannot be more obvious that investing in DASPs' coin offerings has far lower uncertainty and risk for investors than investing in ICOs. 154 | * The tokens offered by DASPs have intrinsic value, in that holders can use them as votes during DASP voting sessions and influence the future of the projects. 155 | * In contrast, the value of most ICO tokens come from vague promises of future utility in the dApps the teams may or may not be building, and most project teams can't even justify why their dApps can't just use Ether instead of their custom tokens. This trend can lead to the fragmentation of the Ethereum blockchain, which will force future users to hold a different token for each dApp they use. 156 | 157 | In conclusion, DASP coin offerings are low-risk and low-uncertainty for investors, and the transparency of DASPs ensures that only honest projects can get funding from investors. 158 | 159 | ### Customization & Expandability 160 | 161 | Virtually all parameters of a DASP---length of a voting session, rights of different member groups, etc.--- are designed to be customizable, in that all one needs to do to change a certain parameter is to start a voting session. Also, since DASPs are essentially smart contracts, WikiGit allows project teams to customize literally every last detail of their DASPs via altering the contract code, as long as the contracts remain compatible with the rest of the ecosystem. 162 | 163 | WikiGit also allows DASPs to plug in third party modules to expand their functionalities, so the possibilities are endless. Here are a few examples: 164 | 165 | * A DASP can connect its task listings with a third party crowdsourcing platform, so that its talent pool is larger. 166 | * A DASP can be connected to a prediction market that predicts which of the task solution submissions are likely to be accepted, so that the validators can have a heuristic for deciding which solutions are the best. 167 | * A DASP can connect its reputation system with its discussion forum so that users who contribute valuable content are rewarded with good reputation, and users who violate the forum policies receive bad reputation. 168 | 169 | We expect to see a new industry of DASPs that are dedicated to making custom modules, and it's possible that WikiGit will have a module manager similar to npm for Node.js and pip for Python. 170 | 171 | ## Contact 172 | 173 | Feel free to contact us at info@wikigit.org ! 174 | -------------------------------------------------------------------------------- /Technical Details.md: -------------------------------------------------------------------------------- 1 | ## Technical Details 2 | 3 | This document describes the details of the three components of WikiGit: the back end, the front end, and the developer tools. The back end is under [/dev/contracts/](/dev/contracts/), the front end is under [/client_ui/](/client_ui/), and the developer tools is under [/dev/](/dev/). 4 | 5 | This document is for WikiGit version **0.2.0 (Grasshopper)**. 6 | 7 | ### The Back End 8 | 9 | The back end includes a ***DAO*** with a fully fledged voting mechanism; a ***MemberHandler*** that acts as a member manifest, with different rights associated with each member group; a ***Vault*** that allows for the safe withdrawl of funds, as well as holding coin offerings; a ***TasksHandler*** for publishing tasks and accepting solutions on the crowdsourcing platform; and a ***RepoHandler*** for democratically determining the IPFS hash of the DASP's git repository. 10 | 11 | #### Modular Design 12 | 13 | All of the core smart contracts mentioned above were designed as individual modules. This design allows for both updating the code down the road via contract swapping, and introducing new functionalities via adding lightweight modules in order to prevent frequent updates of the heavyweight core contracts. Even though the frequent intercontract communication of a modularized design entails costing more gas during normal usage than a single-contract infrastructure, the benefits of this design means that it's a lot more easier to maintain, update, and debug such systems, which makes large-scale, complex DASPs possible. 14 | 15 | Also, the modularized design means that it is possible to swap out a core module for something that's compatible with any third-party app of your choosing. For instance, you can replace the TaskHandler module for a contract that publishes task listings onto a third-party platform. 16 | 17 | As of Grasshopper, upgrading a module without writing a specific migration script would result in the loss of all data stored in the module. We intend to address this issue later on. 18 | 19 | #### The Main Contract 20 | 21 | The main contract serves as an index for all of a DASP's modules. Modules can be added/removed through DAO votings. It is the only contract in the back end that cannot be upgraded, but it's simple enough that you won't need to. 22 | 23 | #### DAO 24 | 25 | The DAO module handles the voting mechanics of the DASP. Any member that has the right to do so can start voting sessions, vote on proposals, conclude voting sessions after the alotted time has passed, execute the proposal, and so on. 26 | 27 | The votes that each member have are weighted sums of multiple values: the member's reputation (determined by how many tasks they've completed) and the amount of DASP specific token they own. 28 | 29 | The attributes of a voting session are determined at DAO's creation, where one can set properties such as the quorum, minimum proportion of support votes required, the length of the voting session, and the weights involved in calculating each member's vote. 30 | 31 | The execution of a voting works as follows: when creating a voting, you can specify a transaction bytecode (via passing in its hash) and an address that will receive the transaction. After the voting has been passed, anyone can call the executeVoting() function to execute the transaction bytecode, provided that they have the bytecode that correspond to the stored hash. It's similar to the democracy contract at https://www.ethereum.org/dao . 32 | 33 | #### MemberHandler 34 | 35 | The MemberHandler module stores the information about members of the DASP, and about member groups with different rights in the DASP. It also implements basic member manipulation features, like adding, removing, and banning members. 36 | 37 | There are three groups of members: Team members, contributors, and pure shareholders. 38 | 39 | * Team members are the core contributors to the project, with the right to vote, start voting sessions, post tasks with bounties, submit task solutions, vote on task solutions, and provide IPFS hashes to the DASP's repo. 40 | 41 | * Contributors are people who contribute to the DASP part-time, with the right to vote, start voting sessions, post tasks without bounties, and submit task solutions. 42 | 43 | * Pure shareholders are people who own DASP specific tokens issued during coin offerings, and do not contribute to the DASP, with the right to vote and start voting sessions. 44 | 45 | In Grasshopper, the member groups and the associated rights can be modified, but this feature will not be included in the UI. 46 | 47 | #### Vault 48 | 49 | The Vault module implements the safe withdrawl of funds through freezing pending withdrawals for a certain period of time. During this time, the DAO can use a voting session to veto a malicious withdrawl. The freeze period can be different for withdrawals initiated by the DAO and withdrawls initiated by task posters who want to reward the submitter of solutions they accepted, so that you can shorten the freeze period for DAO withdrawals if you choose to trust DAO votings (you should). 50 | 51 | The Vault also implements ***coin offerings***, which means that during set periods of time, the Vault can tell a token contract to mint a certain amount of tokens proportional to the amount of Ether someone donates to the Vault contract, and send them to that person's address. 52 | 53 | #### TasksHandler 54 | 55 | TasksHandler handles functions such as posting tasks, submitting task solutions, and accepting task solutions. 56 | 57 | The life of a task is as follows: a member posts a task => interested members clone the repository => members complete the task => members upload the Git patch to IPFS => members submit the IPFS hash to the DASP => team members vote on solutions => A solution upvoted by more than 2/3 team members is accepted, while solutions downvoted by more than 2/3 team members are penalized => reward can be withdrawn by the winner after the freeze period => team members download patch, apply to repo, and upload to IPFS => team members update their version of the most up-to-date repo in RepoHandler, in the form of IPFS hashes => the IPFS hash with majority support is displayed as the repo's hash in the UI 58 | 59 | Completing tasks can not only provide financial rewards like Ether and tokens, but also provide good reputation. The amount of reputational reward you receive for completing a task is specified by the task's poster. On the flip side, all members also have a bad reputation attached to them. You receive bad reputation when you're penalized by the DASP for submitting malicious task solutions. Reputations are local to the DASP, but it's possible to build a platform for viewing an account's reputation among all DASPs. If a member behaves too maliciously in a DASP, the DAO can ban the member through a voting session. 60 | 61 | #### RepoHandler 62 | 63 | The purpose of RepoHandler has been explained in the TasksHandler section. 64 | 65 | 66 | 67 | For more information about the back end, just check out the code! All of the contracts were written with readability in mind and have been well commented, so it'd be a treat for those who prefer code to English. 68 | 69 | ### The Front End 70 | 71 | The front end is a client-side web app that will be hosted on the IPFS network. It will include a registry where users can discover projects, as well as the UI needed to interact with DASPs. 72 | 73 | The front end is based on Meteor and Node.js. It uses web3.js to communicate with a DASP's back end, and gift (a wrapper for the Git CLI) & js-ipfs-api to communicate with the DASP's Git repository on IPFS. 74 | 75 | ### Developer Tools 76 | 77 | The developer tools is used for deploying DASPs onto the Ethereum blockchain. It is based on Truffle and node.js. Currently, it can only be used via command line, but an UI will be built in the near future. -------------------------------------------------------------------------------- /assets/project_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-WikiGit/WikiGit/8ef5be8d3015d26685b34bbf414c65bd8d721eb2/assets/project_structure.png -------------------------------------------------------------------------------- /client_ui/.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | 1.3.0-split-minifiers-package 14 | 1.4.0-remove-old-dev-bundle-link 15 | 1.4.1-add-shell-server-package 16 | 1.4.3-split-account-service-packages 17 | 1.5-add-dynamic-import-package 18 | -------------------------------------------------------------------------------- /client_ui/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /client_ui/.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | 4jp2jziomsb0x59v7n 8 | -------------------------------------------------------------------------------- /client_ui/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | meteor-base@1.2.0 # Packages every Meteor app needs to have 8 | mobile-experience@1.0.5 # Packages for a great mobile UX 9 | mongo@1.3.0 # The database Meteor supports right now 10 | blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views 11 | reactive-var@1.0.11 # Reactive variable for tracker 12 | tracker@1.1.3 # Meteor's client-side reactive programming library 13 | 14 | standard-minifier-css@1.3.5 # CSS minifier run for production mode 15 | standard-minifier-js@2.2.0 # JS minifier run for production mode 16 | es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers 17 | ecmascript@0.9.0 # Enable ECMAScript2015+ syntax in app code 18 | shell-server@0.3.0 # Server-side component of the `meteor shell` command 19 | 20 | autopublish@1.0.7 # Publish all data to the clients (for prototyping) 21 | insecure@1.0.7 # Allow all DB writes from clients (for prototyping) 22 | -------------------------------------------------------------------------------- /client_ui/.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /client_ui/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.6 2 | -------------------------------------------------------------------------------- /client_ui/.meteor/versions: -------------------------------------------------------------------------------- 1 | allow-deny@1.1.0 2 | autopublish@1.0.7 3 | autoupdate@1.3.12 4 | babel-compiler@6.24.7 5 | babel-runtime@1.1.1 6 | base64@1.0.10 7 | binary-heap@1.0.10 8 | blaze@2.3.2 9 | blaze-html-templates@1.1.2 10 | blaze-tools@1.0.10 11 | boilerplate-generator@1.3.1 12 | caching-compiler@1.1.9 13 | caching-html-compiler@1.1.2 14 | callback-hook@1.0.10 15 | check@1.2.5 16 | ddp@1.4.0 17 | ddp-client@2.2.0 18 | ddp-common@1.3.0 19 | ddp-server@2.1.1 20 | deps@1.0.12 21 | diff-sequence@1.0.7 22 | dynamic-import@0.2.1 23 | ecmascript@0.9.0 24 | ecmascript-runtime@0.5.0 25 | ecmascript-runtime-client@0.5.0 26 | ecmascript-runtime-server@0.5.0 27 | ejson@1.1.0 28 | es5-shim@4.6.15 29 | geojson-utils@1.0.10 30 | hot-code-push@1.0.4 31 | html-tools@1.0.11 32 | htmljs@1.0.11 33 | http@1.3.0 34 | id-map@1.0.9 35 | insecure@1.0.7 36 | jquery@1.11.10 37 | launch-screen@1.1.1 38 | livedata@1.0.18 39 | logging@1.1.19 40 | meteor@1.8.2 41 | meteor-base@1.2.0 42 | minifier-css@1.2.16 43 | minifier-js@2.2.2 44 | minimongo@1.4.2 45 | mobile-experience@1.0.5 46 | mobile-status-bar@1.0.14 47 | modules@0.11.0 48 | modules-runtime@0.9.1 49 | mongo@1.3.0 50 | mongo-dev-server@1.1.0 51 | mongo-id@1.0.6 52 | npm-mongo@2.2.33 53 | observe-sequence@1.0.16 54 | ordered-dict@1.0.9 55 | promise@0.10.0 56 | random@1.0.10 57 | reactive-var@1.0.11 58 | reload@1.1.11 59 | retry@1.0.9 60 | routepolicy@1.0.12 61 | shell-server@0.3.0 62 | spacebars@1.0.15 63 | spacebars-compiler@1.1.3 64 | standard-minifier-css@1.3.5 65 | standard-minifier-js@2.2.2 66 | templating@1.3.2 67 | templating-compiler@1.3.3 68 | templating-runtime@1.3.2 69 | templating-tools@1.1.2 70 | tracker@1.1.3 71 | ui@1.0.13 72 | underscore@1.0.10 73 | url@1.1.0 74 | webapp@1.4.0 75 | webapp-hashing@1.0.9 76 | -------------------------------------------------------------------------------- /client_ui/client/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-WikiGit/WikiGit/8ef5be8d3015d26685b34bbf414c65bd8d721eb2/client_ui/client/main.css -------------------------------------------------------------------------------- /client_ui/client/main.html: -------------------------------------------------------------------------------- 1 | 2 | WikiGit 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /client_ui/client/main.js: -------------------------------------------------------------------------------- 1 | import '../imports/ui/dasp_dashboard.js'; -------------------------------------------------------------------------------- /client_ui/imports/abi/mainABI.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [ 5 | { 6 | "name": "", 7 | "type": "bytes32" 8 | } 9 | ], 10 | "name": "moduleAddresses", 11 | "outputs": [ 12 | { 13 | "name": "", 14 | "type": "address" 15 | } 16 | ], 17 | "payable": false, 18 | "type": "function" 19 | }, 20 | { 21 | "constant": true, 22 | "inputs": [], 23 | "name": "hasInitedABIs", 24 | "outputs": [ 25 | { 26 | "name": "", 27 | "type": "bool" 28 | } 29 | ], 30 | "payable": false, 31 | "type": "function" 32 | }, 33 | { 34 | "constant": true, 35 | "inputs": [ 36 | { 37 | "name": "", 38 | "type": "bytes32" 39 | } 40 | ], 41 | "name": "abiHashId", 42 | "outputs": [ 43 | { 44 | "name": "", 45 | "type": "uint256" 46 | } 47 | ], 48 | "payable": false, 49 | "type": "function" 50 | }, 51 | { 52 | "constant": true, 53 | "inputs": [], 54 | "name": "metadata", 55 | "outputs": [ 56 | { 57 | "name": "", 58 | "type": "string" 59 | } 60 | ], 61 | "payable": false, 62 | "type": "function" 63 | }, 64 | { 65 | "constant": true, 66 | "inputs": [ 67 | { 68 | "name": "", 69 | "type": "uint256" 70 | } 71 | ], 72 | "name": "moduleNames", 73 | "outputs": [ 74 | { 75 | "name": "", 76 | "type": "string" 77 | } 78 | ], 79 | "payable": false, 80 | "type": "function" 81 | }, 82 | { 83 | "constant": true, 84 | "inputs": [ 85 | { 86 | "name": "", 87 | "type": "uint256" 88 | } 89 | ], 90 | "name": "abiIPFSHashes", 91 | "outputs": [ 92 | { 93 | "name": "", 94 | "type": "bytes" 95 | } 96 | ], 97 | "payable": false, 98 | "type": "function" 99 | }, 100 | { 101 | "constant": false, 102 | "inputs": [ 103 | { 104 | "name": "index", 105 | "type": "uint256" 106 | } 107 | ], 108 | "name": "removeModuleAtIndex", 109 | "outputs": [], 110 | "payable": false, 111 | "type": "function" 112 | }, 113 | { 114 | "constant": false, 115 | "inputs": [ 116 | { 117 | "name": "modId", 118 | "type": "uint256" 119 | }, 120 | { 121 | "name": "abiHash", 122 | "type": "bytes" 123 | } 124 | ], 125 | "name": "initializeABIHashForMod", 126 | "outputs": [], 127 | "payable": false, 128 | "type": "function" 129 | }, 130 | { 131 | "constant": false, 132 | "inputs": [ 133 | { 134 | "name": "modName", 135 | "type": "string" 136 | }, 137 | { 138 | "name": "addr", 139 | "type": "address" 140 | }, 141 | { 142 | "name": "isNew", 143 | "type": "bool" 144 | } 145 | ], 146 | "name": "setModuleAddress", 147 | "outputs": [], 148 | "payable": false, 149 | "type": "function" 150 | }, 151 | { 152 | "constant": false, 153 | "inputs": [ 154 | { 155 | "name": "meta", 156 | "type": "string" 157 | } 158 | ], 159 | "name": "setMetadata", 160 | "outputs": [], 161 | "payable": false, 162 | "type": "function" 163 | }, 164 | { 165 | "constant": false, 166 | "inputs": [ 167 | { 168 | "name": "addrs", 169 | "type": "address[]" 170 | } 171 | ], 172 | "name": "initializeModuleAddresses", 173 | "outputs": [], 174 | "payable": false, 175 | "type": "function" 176 | }, 177 | { 178 | "constant": true, 179 | "inputs": [ 180 | { 181 | "name": "modHash", 182 | "type": "bytes32" 183 | } 184 | ], 185 | "name": "getABIHashForMod", 186 | "outputs": [ 187 | { 188 | "name": "abiHash", 189 | "type": "bytes" 190 | } 191 | ], 192 | "payable": false, 193 | "type": "function" 194 | }, 195 | { 196 | "constant": true, 197 | "inputs": [], 198 | "name": "hasInitedAddrs", 199 | "outputs": [ 200 | { 201 | "name": "", 202 | "type": "bool" 203 | } 204 | ], 205 | "payable": false, 206 | "type": "function" 207 | }, 208 | { 209 | "constant": false, 210 | "inputs": [ 211 | { 212 | "name": "modHash", 213 | "type": "bytes32" 214 | }, 215 | { 216 | "name": "abiHash", 217 | "type": "bytes" 218 | } 219 | ], 220 | "name": "setABIHashForMod", 221 | "outputs": [], 222 | "payable": false, 223 | "type": "function" 224 | }, 225 | { 226 | "inputs": [ 227 | { 228 | "name": "_metadata", 229 | "type": "string" 230 | }, 231 | { 232 | "name": "_abiIPFSHash", 233 | "type": "bytes" 234 | } 235 | ], 236 | "payable": false, 237 | "type": "constructor" 238 | }, 239 | { 240 | "payable": false, 241 | "type": "fallback" 242 | } 243 | ] -------------------------------------------------------------------------------- /client_ui/imports/daemon/git-ipfs.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | git-ipfs.coffee 3 | Created by Zefram Lou (Zebang Liu) as part of the WikiGit project. 4 | 5 | This file implements a daemon that listens for the TaskSolutionAccepted() event 6 | from the GitHandler module. Upon such an event, the daemon would clone the 7 | DASP's Git repo, pull from the updated repo where the task has been completed 8 | to merge the solution into the DASP's repo, publish the resulting repo onto IPFS, 9 | and send its IPFS multihash back to GitHandler as the current location of the DASP's repo. 10 | ### 11 | 12 | import {dasp} from '../ui/dasp_dashboard.js' 13 | 14 | #Import web3 15 | Web3 = require 'web3' 16 | web3 = window.web3 17 | if typeof web3 != undefined 18 | web3 = new Web3(web3.currentProvider) 19 | else 20 | web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) 21 | 22 | web3.eth.getAccounts().then( 23 | (accounts) -> 24 | web3.eth.defaultAccount = accounts[0] 25 | ) 26 | 27 | #Import node modules 28 | ipfsAPI = require 'ipfs-api' 29 | ipfs = ipfsAPI('ipfs.infura.io', '5001', {protocol: 'https'}) 30 | git = require 'gift' 31 | fs = require 'fs' 32 | keccak256 = require('js-sha3').keccak256 33 | 34 | #Helper functions 35 | hexToStr = (hex) -> 36 | hex = hex.substr(2) 37 | str = '' 38 | for i in [0..hex.length - 1] by 2 39 | str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)) 40 | return str 41 | 42 | export StartDaemon = () -> 43 | #Fetch contract abstractions 44 | tasksHandlerContract = dasp.contracts.tasks 45 | gitHandlerContract = dasp.contracts.git 46 | 47 | solutionAcceptedEvent = tasksHandlerContract.events.TaskSolutionAccepted() 48 | solutionAcceptedEvent.on('data', (event) -> 49 | patchIPFSHash = hexToStr event.returnValues.patchIPFSHash 50 | gitHandlerContract.methods.getCurrentIPFSHash().call().then( 51 | (result) -> 52 | masterIPFSHash = hexToStr result 53 | masterPath = "./tmp/#{masterIPFSHash}/" 54 | 55 | #Create repo directory if it doesn't exist 56 | if !fs.existsSync(masterPath) 57 | if !fs.existsSync('./tmp') 58 | fs.mkdirSync('./tmp') 59 | fs.mkdirSync(masterPath) 60 | 61 | #Clone the master 62 | git.clone("git@gateway.ipfs.io/ipfs/" + masterIPFSHash.toString(), masterPath, Number.POSITIVE_INFINITY, "master", (error, _repo) -> 63 | if error != null 64 | throw error 65 | repo = _repo 66 | #Add patched repo as remote 67 | repo.remote_add("solution", "gateway.ipfs.io/ipfs/#{patchIPFSHash}", (error) -> 68 | if error != null 69 | throw error 70 | #Pull the patched repo and merge with the master 71 | repo.pull("solution", "master", (error) -> 72 | if error != null 73 | throw error 74 | #Add new repo to the IPFS network 75 | ipfs.util.addFromFs(masterPath, {recursive: true}, (error, result) -> 76 | if error != null 77 | throw error 78 | for entry in result 79 | if entry.path is masterIPFSHash 80 | gitHandlerContract.methods.commitTaskSolutionToRepo( 81 | event.returnValues.taskId, 82 | event.returnValues.solId, 83 | entry.hash 84 | ).send() 85 | break 86 | ) 87 | ) 88 | ) 89 | ) 90 | ) 91 | ) 92 | return 93 | -------------------------------------------------------------------------------- /client_ui/imports/daemon/git-ipfs.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 2.0.1 2 | /* 3 | git-ipfs.coffee 4 | Created by Zefram Lou (Zebang Liu) as part of the WikiGit project. 5 | 6 | This file implements a daemon that listens for the TaskSolutionAccepted() event 7 | from the GitHandler module. Upon such an event, the daemon would clone the 8 | DASP's Git repo, pull from the updated repo where the task has been completed 9 | to merge the solution into the DASP's repo, publish the resulting repo onto IPFS, 10 | and send its IPFS multihash back to GitHandler as the current location of the DASP's repo. 11 | */ 12 | var Web3, fs, git, hexToStr, ipfs, ipfsAPI, keccak256, web3; 13 | 14 | import { 15 | dasp 16 | } from '../ui/dasp_dashboard.js'; 17 | 18 | //Import web3 19 | Web3 = require('web3'); 20 | 21 | web3 = window.web3; 22 | 23 | if (typeof web3 !== void 0) { 24 | web3 = new Web3(web3.currentProvider); 25 | } else { 26 | web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); 27 | } 28 | 29 | web3.eth.getAccounts().then(function(accounts) { 30 | return web3.eth.defaultAccount = accounts[0]; 31 | }); 32 | 33 | //Import node modules 34 | ipfsAPI = require('ipfs-api'); 35 | 36 | ipfs = ipfsAPI('ipfs.infura.io', '5001', { 37 | protocol: 'https' 38 | }); 39 | 40 | git = require('gift'); 41 | 42 | fs = require('fs'); 43 | 44 | keccak256 = require('js-sha3').keccak256; 45 | 46 | //Helper functions 47 | hexToStr = function(hex) { 48 | var i, j, ref, str; 49 | hex = hex.substr(2); 50 | str = ''; 51 | for (i = j = 0, ref = hex.length - 1; j <= ref; i = j += 2) { 52 | str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); 53 | } 54 | return str; 55 | }; 56 | 57 | export var StartDaemon = function() { 58 | var gitHandlerContract, solutionAcceptedEvent, tasksHandlerContract; 59 | //Fetch contract abstractions 60 | tasksHandlerContract = dasp.contracts.tasks; 61 | gitHandlerContract = dasp.contracts.git; 62 | solutionAcceptedEvent = tasksHandlerContract.events.TaskSolutionAccepted(); 63 | solutionAcceptedEvent.on('data', function(event) { 64 | var patchIPFSHash; 65 | patchIPFSHash = hexToStr(event.returnValues.patchIPFSHash); 66 | return gitHandlerContract.methods.getCurrentIPFSHash().call().then(function(result) { 67 | var masterIPFSHash, masterPath; 68 | masterIPFSHash = hexToStr(result); 69 | masterPath = `./tmp/${masterIPFSHash}/`; 70 | if (!fs.existsSync(masterPath)) { 71 | if (!fs.existsSync('./tmp')) { 72 | fs.mkdirSync('./tmp'); 73 | } 74 | fs.mkdirSync(masterPath); 75 | } 76 | //Clone the master 77 | return git.clone("git@gateway.ipfs.io/ipfs/" + masterIPFSHash.toString(), masterPath, Number.POSITIVE_INFINITY, "master", function(error, _repo) { 78 | var repo; 79 | if (error !== null) { 80 | throw error; 81 | } 82 | repo = _repo; 83 | //Add patched repo as remote 84 | return repo.remote_add("solution", `gateway.ipfs.io/ipfs/${patchIPFSHash}`, function(error) { 85 | if (error !== null) { 86 | throw error; 87 | } 88 | //Pull the patched repo and merge with the master 89 | return repo.pull("solution", "master", function(error) { 90 | if (error !== null) { 91 | throw error; 92 | } 93 | //Add new repo to the IPFS network 94 | return ipfs.util.addFromFs(masterPath, { 95 | recursive: true 96 | }, function(error, result) { 97 | var entry, j, len, results; 98 | if (error !== null) { 99 | throw error; 100 | } 101 | results = []; 102 | for (j = 0, len = result.length; j < len; j++) { 103 | entry = result[j]; 104 | if (entry.path === masterIPFSHash) { 105 | gitHandlerContract.methods.commitTaskSolutionToRepo(event.returnValues.taskId, event.returnValues.solId, entry.hash).send(); 106 | break; 107 | } else { 108 | results.push(void 0); 109 | } 110 | } 111 | return results; 112 | }); 113 | }); 114 | }); 115 | }); 116 | }); 117 | }); 118 | }; 119 | 120 | //# sourceMappingURL=git-ipfs.js.map 121 | -------------------------------------------------------------------------------- /client_ui/imports/daemon/git-ipfs.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "file": "git-ipfs.js", 4 | "sourceRoot": "", 5 | "sources": [ 6 | "git-ipfs.coffee" 7 | ], 8 | "names": [], 9 | "mappings": ";AAAA;;;;;;;;;;AAAA,IAAA;;AAWA,OAAA;EAAQ,IAAR;CAAA,MAAA,0BAXA;;;AAcA,IAAA,GAAO,OAAA,CAAQ,MAAR;;AACP,IAAA,GAAO,MAAM,CAAC;;AACd,IAAG,OAAO,IAAP,KAAe,MAAlB;EACE,IAAA,GAAO,IAAI,IAAJ,CAAS,IAAI,CAAC,eAAd,EADT;CAAA,MAAA;EAGE,IAAA,GAAO,IAAI,IAAJ,CAAS,IAAI,IAAI,CAAC,SAAS,CAAC,YAAnB,CAAgC,uBAAhC,CAAT,EAHT;;;AAKA,IAAI,CAAC,GAAG,CAAC,WAAT,CAAA,CAAsB,CAAC,IAAvB,CACE,QAAA,CAAC,QAAD,CAAA;SACE,IAAI,CAAC,GAAG,CAAC,cAAT,GAA0B,QAAS,CAAA,CAAA;AADrC,CADF,EArBA;;;AA2BA,OAAA,GAAU,OAAA,CAAQ,UAAR;;AACV,IAAA,GAAO,OAAA,CAAQ,gBAAR,EAA0B,MAA1B,EAAkC;EAAC,QAAA,EAAU;AAAX,CAAlC;;AACP,GAAA,GAAM,OAAA,CAAQ,MAAR;;AACN,EAAA,GAAK,OAAA,CAAQ,IAAR;;AACL,SAAA,GAAY,OAAA,CAAQ,SAAR,CAAkB,CAAC,UA/B/B;;;AAkCA,QAAA,GAAW,QAAA,CAAC,GAAD,CAAA;AACT,MAAA;EAAA,GAAA,GAAM,GAAG,CAAC,MAAJ,CAAW,CAAX;EACN,GAAA,GAAM;EACN,KAAS,qDAAT;IACE,GAAA,IAAO,MAAM,CAAC,YAAP,CAAoB,QAAA,CAAS,GAAG,CAAC,MAAJ,CAAW,CAAX,EAAc,CAAd,CAAT,EAA2B,EAA3B,CAApB;EADT;AAEA,SAAO;AALE;;AAOX,OAAA,IAAO,WAAP,GAAqB,QAAA,CAAA,CAAA;AAEnB,MAAA,+DAAA;;EAAA,oBAAA,GAAuB,IAAI,CAAC,SAAS,CAAC;EACtC,kBAAA,GAAqB,IAAI,CAAC,SAAS,CAAC;EAEpC,qBAAA,GAAwB,oBAAoB,CAAC,MAAM,CAAC,oBAA5B,CAAA;EACxB,qBAAqB,CAAC,EAAtB,CAAyB,MAAzB,EAAiC,QAAA,CAAC,KAAD,CAAA;AAC/B,QAAA;IAAA,aAAA,GAAgB,QAAA,CAAS,KAAK,CAAC,YAAY,CAAC,aAA5B;WAChB,kBAAkB,CAAC,OAAO,CAAC,kBAA3B,CAAA,CAA+C,CAAC,IAAhD,CAAA,CAAsD,CAAC,IAAvD,CACE,QAAA,CAAC,MAAD,CAAA;AACE,UAAA;MAAA,cAAA,GAAiB,QAAA,CAAS,MAAT;MACjB,UAAA,GAAa,CAAA,MAAA,CAAA,CAAS,cAAT,CAAwB,CAAxB;MAGb,IAAG,CAAC,EAAE,CAAC,UAAH,CAAc,UAAd,CAAJ;QACE,IAAG,CAAC,EAAE,CAAC,UAAH,CAAc,OAAd,CAAJ;UACE,EAAE,CAAC,SAAH,CAAa,OAAb,EADF;;QAEA,EAAE,CAAC,SAAH,CAAa,UAAb,EAHF;OAJA;;aAUA,GAAG,CAAC,KAAJ,CAAU,2BAAA,GAA8B,cAAc,CAAC,QAAf,CAAA,CAAxC,EAAmE,UAAnE,EAA+E,MAAM,CAAC,iBAAtF,EAAyG,QAAzG,EAAmH,QAAA,CAAC,KAAD,EAAQ,KAAR,CAAA;AACjH,YAAA;QAAA,IAAG,KAAA,KAAS,IAAZ;UACE,MAAM,MADR;;QAEA,IAAA,GAAO,MAFP;;eAIA,IAAI,CAAC,UAAL,CAAgB,UAAhB,EAA4B,CAAA,qBAAA,CAAA,CAAwB,aAAxB,CAAA,CAA5B,EAAqE,QAAA,CAAC,KAAD,CAAA;UACnE,IAAG,KAAA,KAAS,IAAZ;YACE,MAAM,MADR;WAAA;;iBAGA,IAAI,CAAC,IAAL,CAAU,UAAV,EAAsB,QAAtB,EAAgC,QAAA,CAAC,KAAD,CAAA;YAC9B,IAAG,KAAA,KAAS,IAAZ;cACE,MAAM,MADR;aAAA;;mBAGA,IAAI,CAAC,IAAI,CAAC,SAAV,CAAoB,UAApB,EAAgC;cAAC,SAAA,EAAW;YAAZ,CAAhC,EAAmD,QAAA,CAAC,KAAD,EAAQ,MAAR,CAAA;AACjD,kBAAA;cAAA,IAAG,KAAA,KAAS,IAAZ;gBACE,MAAM,MADR;;AAEA;cAAA,KAAA,wCAAA;;gBACE,IAAG,KAAK,CAAC,IAAN,KAAc,cAAjB;kBACE,kBAAkB,CAAC,OAAO,CAAC,wBAA3B,CACE,KAAK,CAAC,YAAY,CAAC,MADrB,EAEE,KAAK,CAAC,YAAY,CAAC,KAFrB,EAGE,KAAK,CAAC,IAHR,CAIC,CAAC,IAJF,CAAA;AAKA,wBANF;iBAAA,MAAA;uCAAA;;cADF,CAAA;;YAHiD,CAAnD;UAJ8B,CAAhC;QAJmE,CAArE;MALiH,CAAnH;IAXF,CADF;EAF+B,CAAjC;AANmB", 10 | "sourcesContent": [ 11 | "###\r\n git-ipfs.coffee\r\n Created by Zefram Lou (Zebang Liu) as part of the WikiGit project.\r\n\r\n This file implements a daemon that listens for the TaskSolutionAccepted() event\r\n from the GitHandler module. Upon such an event, the daemon would clone the\r\n DASP's Git repo, pull from the updated repo where the task has been completed\r\n to merge the solution into the DASP's repo, publish the resulting repo onto IPFS,\r\n and send its IPFS multihash back to GitHandler as the current location of the DASP's repo.\r\n###\r\n\r\nimport {dasp} from '../ui/dasp_dashboard.js'\r\n\r\n#Import web3\r\nWeb3 = require 'web3'\r\nweb3 = window.web3\r\nif typeof web3 != undefined\r\n web3 = new Web3(web3.currentProvider)\r\nelse\r\n web3 = new Web3(new Web3.providers.HttpProvider(\"http://localhost:8545\"))\r\n\r\nweb3.eth.getAccounts().then(\r\n (accounts) ->\r\n web3.eth.defaultAccount = accounts[0]\r\n)\r\n\r\n#Import node modules\r\nipfsAPI = require 'ipfs-api'\r\nipfs = ipfsAPI('ipfs.infura.io', '5001', {protocol: 'https'})\r\ngit = require 'gift'\r\nfs = require 'fs'\r\nkeccak256 = require('js-sha3').keccak256\r\n\r\n#Helper functions\r\nhexToStr = (hex) ->\r\n hex = hex.substr(2)\r\n str = ''\r\n for i in [0..hex.length - 1] by 2\r\n str += String.fromCharCode(parseInt(hex.substr(i, 2), 16))\r\n return str\r\n\r\nexport StartDaemon = () ->\r\n #Fetch contract abstractions\r\n tasksHandlerContract = dasp.contracts.tasks\r\n gitHandlerContract = dasp.contracts.git\r\n\r\n solutionAcceptedEvent = tasksHandlerContract.events.TaskSolutionAccepted()\r\n solutionAcceptedEvent.on('data', (event) ->\r\n patchIPFSHash = hexToStr event.returnValues.patchIPFSHash\r\n gitHandlerContract.methods.getCurrentIPFSHash().call().then(\r\n (result) ->\r\n masterIPFSHash = hexToStr result\r\n masterPath = \"./tmp/#{masterIPFSHash}/\"\r\n\r\n #Create repo directory if it doesn't exist\r\n if !fs.existsSync(masterPath)\r\n if !fs.existsSync('./tmp')\r\n fs.mkdirSync('./tmp')\r\n fs.mkdirSync(masterPath)\r\n\r\n #Clone the master\r\n git.clone(\"git@gateway.ipfs.io/ipfs/\" + masterIPFSHash.toString(), masterPath, Number.POSITIVE_INFINITY, \"master\", (error, _repo) ->\r\n if error != null\r\n throw error\r\n repo = _repo\r\n #Add patched repo as remote\r\n repo.remote_add(\"solution\", \"gateway.ipfs.io/ipfs/#{patchIPFSHash}\", (error) ->\r\n if error != null\r\n throw error\r\n #Pull the patched repo and merge with the master\r\n repo.pull(\"solution\", \"master\", (error) ->\r\n if error != null\r\n throw error\r\n #Add new repo to the IPFS network\r\n ipfs.util.addFromFs(masterPath, {recursive: true}, (error, result) ->\r\n if error != null\r\n throw error\r\n for entry in result\r\n if entry.path is masterIPFSHash\r\n gitHandlerContract.methods.commitTaskSolutionToRepo(\r\n event.returnValues.taskId,\r\n event.returnValues.solId,\r\n entry.hash\r\n ).send()\r\n break\r\n )\r\n )\r\n )\r\n )\r\n )\r\n )\r\n return\r\n" 12 | ] 13 | } -------------------------------------------------------------------------------- /client_ui/imports/objects/dasp.coffee: -------------------------------------------------------------------------------- 1 | #Import web3 2 | Web3 = require 'web3' 3 | web3 = window.web3 4 | if typeof web3 != undefined 5 | web3 = new Web3(web3.currentProvider) 6 | else 7 | web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) 8 | 9 | web3.eth.getAccounts().then( 10 | (accounts) -> 11 | web3.eth.defaultAccount = accounts[0] 12 | ) 13 | 14 | #Import node modules 15 | ipfsAPI = require 'ipfs-api' 16 | ipfs = ipfsAPI('ipfs.infura.io', '5001', {protocol: 'https'}) 17 | git = require 'gift' 18 | fs = require 'fs' 19 | bl = require 'bl' 20 | keccak256 = require('js-sha3').keccak256 21 | 22 | #Helper functions 23 | hexToStr = (hex) -> 24 | hex = hex.substr(2) 25 | str = '' 26 | for i in [0..hex.length - 1] by 2 27 | str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)) 28 | return str 29 | 30 | export DASP = () -> 31 | self = this 32 | self.metadata = null 33 | 34 | self.addrs = 35 | main: null 36 | dao: null 37 | member: null 38 | vault: null 39 | tasks: null 40 | git: null 41 | 42 | self.contracts = 43 | main: null 44 | dao: null 45 | member: null 46 | vault: null 47 | tasks: null 48 | git: null 49 | 50 | self.repoIPFSHash = null 51 | self.memberList = []; 52 | self.payBehaviorList = []; 53 | self.pendingWithdrawlList = []; 54 | self.pendingTokenWithdrawlList = []; 55 | 56 | self.initWithAddr = (addr, options, callback) -> 57 | self.addrs.main = addr 58 | mainAbi = (options && options.mainAbi) || require "../abi/mainABI.json" 59 | self.contracts.main = new web3.eth.Contract(mainAbi, self.addrs.main) 60 | moduleNames = (options && options.moduleNames) || ['DAO', 'MEMBER', 'VAULT', 'TASKS', 'GIT'] 61 | #Todo: read module names from main contract 62 | initMod = (mod) -> 63 | #Get module address 64 | return self.contracts.main.methods.moduleAddresses('0x' + keccak256(mod)).call().then( 65 | (result) -> 66 | lowerMod = mod.toLowerCase() 67 | self.addrs[lowerMod] = result 68 | #Get module ABI's IPFS hash 69 | return self.contracts.main.methods.getABIHashForMod('0x' + keccak256(mod)).call().then( 70 | (abiHash) -> 71 | return new Promise((fullfill, reject) -> 72 | #Get module ABI 73 | ipfs.files.cat(hexToStr(abiHash), 74 | (error, stream) -> 75 | if error != null 76 | if callback != null 77 | callback(error) 78 | reject(error) 79 | throw error 80 | stream.pipe(bl((error, data) -> 81 | if error != null 82 | if callback != null 83 | callback(error) 84 | reject(error) 85 | throw error 86 | abi = JSON.parse(data.toString()).abi 87 | #Initialize module contract 88 | self.contracts[lowerMod] = new web3.eth.Contract(abi, self.addrs[lowerMod]) 89 | 90 | fullfill() 91 | return 92 | )) 93 | return 94 | ) 95 | ) 96 | ) 97 | ) 98 | initAllMods = (initMod(mod) for mod in moduleNames) 99 | Promise.all(initAllMods).then( 100 | ()-> 101 | self.contracts.git.methods.getCurrentIPFSHash().call( 102 | (error, result) -> 103 | if error != null 104 | if callback != null 105 | callback(error) 106 | else 107 | self.repoIPFSHash = hexToStr result 108 | if callback != null 109 | callback(null) 110 | 111 | return 112 | ) 113 | ) 114 | return 115 | 116 | self.getRepoFile = (path, callback) -> 117 | fullPath = "#{self.repoIPFSHash}#{path}" 118 | ipfs.files.cat(fullPath, (err, stream) -> 119 | if err != null 120 | ipfs.ls(fullPath, (error, result) -> 121 | if error != null 122 | if callback != null 123 | callback(error, 'dir', null) 124 | else 125 | if callback != null 126 | callback(null, 'dir', result.Objects[0].Links) 127 | ) 128 | else 129 | stream.pipe(bl((error, data) -> 130 | if callback != null 131 | callback(null, 'file', data) 132 | )) 133 | ) 134 | 135 | return 136 | 137 | self.getMemberList = (callback) -> 138 | self.memberList = [] 139 | return self.contracts.member.methods.getMemberListCount().call().then( 140 | (memberCount) -> 141 | memberCount = +memberCount 142 | if memberCount == 0 143 | return 144 | getMember = (id) -> 145 | return self.contracts.member.methods.memberList(id).call().then( 146 | (member) -> 147 | return new Promise((fullfill, reject) -> 148 | self.memberList.push(member) 149 | if member.userAddress != '0x' 150 | fullfill() 151 | else 152 | if callback != null 153 | callback(new Error('Load Member Data Error'), null) 154 | reject() 155 | return 156 | ) 157 | ) 158 | getAllMembers = (getMember(id) for id in [1..memberCount-1]) 159 | return Promise.all(getAllMembers).then( 160 | () -> 161 | if callback != null 162 | callback(null, self.memberList) 163 | ) 164 | ) 165 | 166 | self.signUp = (type, userName, callback) -> 167 | ethFunction = null 168 | if type == 'freelancer' 169 | ethFunction = self.contracts.member.methods.setSelfAsFreelancer(userName) 170 | if type == 'shareholder' 171 | ethFunction = self.contracts.member.methods.setSelfAsPureShareholder(userName) 172 | 173 | if ethFunction == null 174 | callback(new Error('Invalid type')) 175 | return 176 | return web3.eth.getAccounts().then( 177 | (accounts) -> 178 | web3.eth.defaultAccount = accounts[0] 179 | return ethFunction.send({from: web3.eth.defaultAccount, gas: 1000000}).on( 180 | 'receipt', 181 | (receipt) -> 182 | if callback != null 183 | callback(null) 184 | ).on( 185 | 'error', 186 | (error) -> 187 | if callback != null 188 | callback(error) 189 | ) 190 | ) 191 | 192 | self.getVaultBalance = () -> 193 | return web3.eth.getBalance(self.addrs.vault) 194 | 195 | self.getAccounts = () -> 196 | return web3.eth.getAccounts().then( 197 | (accounts) -> 198 | web3.eth.defaultAccount = accounts[0] 199 | ) 200 | 201 | self.fundDasp = (amountInEther) -> 202 | return web3.eth.sendTransaction({from: web3.eth.defaultAccount, to: self.addrs.vault, value: amountInEther * Math.pow(10, 18)}) 203 | 204 | self.getPayBehaviorList = (callback) -> 205 | self.payBehaviorList = [] 206 | return self.contracts.vault.methods.getPayBehaviorListCount().call().then( 207 | (payBehaviorCount) -> 208 | payBehaviorCount = +payBehaviorCount 209 | if payBehaviorCount == 0 210 | return 211 | getPayBehavior = (id) -> 212 | return self.contracts.vault.methods.payBehaviorList(id).call().then( 213 | (payBehavior) -> 214 | return new Promise((fullfill, reject) -> 215 | self.payBehaviorList.push(payBehavior) 216 | if payBehavior.tokenAddress != '0x' 217 | fullfill() 218 | else 219 | if callback != null 220 | callback(new Error('Load Pay Behaviors Error')) 221 | reject() 222 | return 223 | ) 224 | ) 225 | getAllPayBehaviors = (getPayBehavior(id) for id in [1..payBehaviorCount-1]) 226 | return Promise.all(getAllPayBehaviors).then( 227 | () -> 228 | if callback != null 229 | callback(null, self.payBehaviorList) 230 | ) 231 | ) 232 | 233 | self.getPendingWithdrawlList = (callback) -> 234 | self.pendingWithdrawlList = [] 235 | return self.contracts.vault.methods.getPendingWithdrawlListCount().call().then( 236 | (pendingWithdrawlCount) -> 237 | pendingWithdrawlCount = +pendingWithdrawlCount 238 | if pendingWithdrawlCount == 0 239 | return 240 | getPendingWithdrawl = (id) -> 241 | return self.contracts.vault.methods.pendingWithdrawlList(id).call().then( 242 | (pendingWithdrawl) -> 243 | return new Promise((fullfill, reject) -> 244 | self.payBehaviorList.push(pendingWithdrawl) 245 | if pendingWithdrawl != null 246 | fullfill() 247 | else 248 | if callback != null 249 | callback(new Error('Load Pending Withdrawls Error'), null) 250 | reject() 251 | return 252 | ) 253 | ) 254 | getAllPendingWithdrawls = (getPendingWithdrawl(id) for id in [1..pendingWithdrawlCount-1]) 255 | return Promise.all(getAllPendingWithdrawls).then( 256 | () -> 257 | if callback != null 258 | callback(null, self.pendingWithdrawlList) 259 | ) 260 | ) 261 | 262 | self.getPendingTokenWithdrawlList = (callback) -> 263 | self.pendingTokenWithdrawlList = [] 264 | return self.contracts.vault.methods.getPendingTokenWithdrawlListCount().call().then( 265 | (pendingWithdrawlCount) -> 266 | pendingWithdrawlCount = +pendingWithdrawlCount 267 | if pendingWithdrawlCount == 0 268 | return 269 | getPendingWithdrawl = (id) -> 270 | return self.contracts.vault.methods.pendingTokenWithdrawlList(id).call().then( 271 | (pendingWithdrawl) -> 272 | return new Promise((fullfill, reject) -> 273 | self.payBehaviorList.push(pendingWithdrawl) 274 | if pendingWithdrawl != null 275 | fullfill() 276 | else 277 | if callback != null 278 | callback(new Error('Load Pending Token Withdrawls Error'), null) 279 | reject() 280 | return 281 | ) 282 | ) 283 | getAllPendingWithdrawls = (getPendingWithdrawl(id) for id in [1..pendingWithdrawlCount-1]) 284 | return Promise.all(getAllPendingWithdrawls).then( 285 | () -> 286 | if callback != null 287 | callback(null, self.pendingTokenWithdrawlList) 288 | ) 289 | ) 290 | 291 | return 292 | -------------------------------------------------------------------------------- /client_ui/imports/ui/dasp_dashboard.coffee: -------------------------------------------------------------------------------- 1 | import { Template } from 'meteor/templating' 2 | 3 | import {ReactiveVar} from 'meteor/reactive-var' 4 | 5 | import {DASP} from '../objects/dasp.js' 6 | 7 | import './dasp_dashboard.html' 8 | 9 | export DASP_Address = new ReactiveVar("") 10 | 11 | export dasp = new DASP() 12 | 13 | import {StartDaemon} from '../daemon/git-ipfs.js' 14 | 15 | showToastMsg = (msg) -> 16 | snackbarContainer = document.querySelector('#status_toast') 17 | snackbarContainer.MaterialSnackbar.showSnackbar({message: msg}) 18 | return 19 | 20 | copyTextToClipboard = (text) -> 21 | textArea = document.createElement("textarea") 22 | # *** This styling is an extra step which is likely not required. *** 23 | # Why is it here? To ensure: 24 | # 1. the element is able to have focus and selection. 25 | # 2. if element was to flash render it has minimal visual impact. 26 | # 3. less flakyness with selection and copying which **might** occur if 27 | # the textarea element is not visible. 28 | # The likelihood is the element won't even render, not even a flash, 29 | # so some of these are just precautions. However in IE the element 30 | # is visible whilst the popup box asking the user for permission for 31 | # the web page to copy to the clipboard. 32 | 33 | # Place in top-left corner of screen regardless of scroll position. 34 | textArea.style.position = 'fixed' 35 | textArea.style.top = 0 36 | textArea.style.left = 0 37 | 38 | # Ensure it has a small width and height. Setting to 1px / 1em 39 | # doesn't work as this gives a negative w/h on some browsers. 40 | textArea.style.width = '2em' 41 | textArea.style.height = '2em' 42 | 43 | # We don't need padding, reducing the size if it does flash render. 44 | textArea.style.padding = 0 45 | 46 | # Clean up any borders. 47 | textArea.style.border = 'none' 48 | textArea.style.outline = 'none' 49 | textArea.style.boxShadow = 'none' 50 | 51 | # Avoid flash of white box if rendered for any reason. 52 | textArea.style.background = 'transparent' 53 | 54 | textArea.value = text 55 | 56 | document.body.appendChild(textArea) 57 | 58 | textArea.select() 59 | 60 | try 61 | successful = document.execCommand('copy') 62 | msg = if successful then 'Copied Clone Address to Clipboard' else 'Oops, unable to copy' 63 | showToastMsg(msg) 64 | catch err 65 | showToastMsg('Oops, unable to copy') 66 | 67 | document.body.removeChild(textArea) 68 | return 69 | 70 | refreshDasp = () -> 71 | dasp.initWithAddr(DASP_Address.get(), null, (error) -> 72 | if error != null 73 | showToastMsg('Ethereum Connection Error') 74 | throw error 75 | #Start daemon 76 | StartDaemon() 77 | 78 | #Init repo tab 79 | dasp.getRepoFile('', (error, type, result) -> 80 | if error != null 81 | showToastMsg('List Repo Error') 82 | throw error 83 | currentFileList.set(result) 84 | ) 85 | 86 | #Init finances tab 87 | dasp.getVaultBalance().then( 88 | (result) -> 89 | vaultBalance.set(result / Math.pow(10, 18)) 90 | ) 91 | dasp.getPayBehaviorList((error, result) -> 92 | if error != null 93 | showToastMsg('Get Coin Offerings Error') 94 | throw error 95 | #Descending by startBlockNumber 96 | result.sort((a, b) -> 97 | return parseInt(b.startBlockNumber) - parseInt(a.startBlockNumber) 98 | ) 99 | payBehaviorList.set(result) 100 | ) 101 | dasp.getPendingWithdrawlList((error, result) -> 102 | if error != null 103 | showToastMsg('Get Pending Withdrawls Error') 104 | throw error 105 | dasp.getPendingTokenWithdrawlList((e, r) -> 106 | if error != null 107 | showToastMsg('Get Pending Token Withdrawls Error') 108 | throw error 109 | result.concat(r) 110 | ) 111 | #Descending by frozenUntilBlock 112 | result.sort((a, b) -> 113 | return parseInt(b.frozenUntilBlock) - parseInt(a.frozenUntilBlock) 114 | ) 115 | pendingWithdrawlList.set(result) 116 | ) 117 | 118 | 119 | #Init member tab 120 | dasp.getMemberList((error, result) -> 121 | if error != null 122 | showToastMsg('Load Member Data Error') 123 | throw error 124 | memberList.set(result) 125 | ) 126 | ) 127 | 128 | #Repo tab variables 129 | currentFileList = new ReactiveVar([]) 130 | currentRepoPath = new ReactiveVar("") 131 | displayFileList = new ReactiveVar(true) 132 | fileData = new ReactiveVar("") 133 | fileName = new ReactiveVar("") 134 | 135 | #Member tab variables 136 | memberList = new ReactiveVar([]) 137 | isSigningUp = new ReactiveVar(false) 138 | signUpType = new String() 139 | SIGNUP_STATUS_SHOWTIME = 3000 140 | 141 | #Finances tab variables 142 | vaultBalance = new ReactiveVar(0) 143 | isEnteringFundAmount = new ReactiveVar(false) 144 | payBehaviorList = new ReactiveVar([]) 145 | pendingWithdrawlList = new ReactiveVar([]) 146 | 147 | #Helpers 148 | 149 | Template.body.helpers( 150 | initialized: 151 | () -> 152 | return DASP_Address.get().length != 0 153 | ) 154 | 155 | Template.repo_tab.helpers( 156 | ls_file: 157 | () -> 158 | return currentFileList.get() 159 | 160 | display_file_list: 161 | () -> 162 | return displayFileList.get() 163 | 164 | file_data: 165 | () -> 166 | return fileData.get() 167 | 168 | file_name: 169 | () -> 170 | return fileName.get() 171 | 172 | file_download_url: 173 | () -> 174 | return "https://gateway.ipfs.io/ipfs/#{dasp.repoIPFSHash}#{currentRepoPath.get()}/#{fileName.get()}" 175 | 176 | current_path: 177 | () -> 178 | if currentRepoPath.get().length == 0 179 | return '/' 180 | return currentRepoPath.get() 181 | ) 182 | 183 | Template.tasks_tab.helpers( 184 | task_list: 185 | () -> 186 | 187 | ) 188 | 189 | Template.finances_tab.helpers( 190 | vault_balance: 191 | () -> 192 | return vaultBalance.get() 193 | 194 | not_entering_fund_amount: 195 | () -> 196 | return !isEnteringFundAmount.get() 197 | 198 | pay_behavior_list: 199 | () -> 200 | return payBehaviorList.get() 201 | 202 | pay_behavior_list_empty: 203 | () -> 204 | return payBehaviorList.get().length == 0 205 | 206 | pending_withdrawl_list: 207 | () -> 208 | return pendingWithdrawlList.get() 209 | 210 | pending_withdrawl_list_empty: 211 | () -> 212 | return pendingWithdrawlList.get().length == 0 213 | 214 | wei_to_ether: 215 | (wei) -> 216 | return +wei / Math.pow(10, 18) 217 | 218 | currency_symbol: 219 | (symbol) -> 220 | if symbol == null 221 | return 'ETH' 222 | return symbol 223 | ) 224 | 225 | Template.members_tab.helpers( 226 | member_list: 227 | () -> 228 | return memberList.get() 229 | 230 | not_signing_up: 231 | () -> 232 | return !isSigningUp.get() 233 | ) 234 | 235 | #Events 236 | 237 | Template.body.events( 238 | 'submit .dasp_addr_input': 239 | (event) -> 240 | # Prevent default browser form submit 241 | event.preventDefault() 242 | # Get value from form element 243 | target = event.target 244 | text = target.dasp_addr.value 245 | 246 | DASP_Address.set(text) 247 | 248 | refreshDasp() 249 | 250 | 'click .refresh_dasp': 251 | (event) -> 252 | refreshDasp() 253 | ) 254 | 255 | Template.repo_tab.events( 256 | 'dblclick .file_item': 257 | (event) -> 258 | item = this 259 | if item.Name == '..' 260 | currentRepoPath.set(currentRepoPath.get().slice(0, currentRepoPath.get().lastIndexOf('/'))) 261 | dasp.getRepoFile(currentRepoPath.get(), (error, type, result) -> 262 | if error != null 263 | showToastMsg('List Repo Error') 264 | throw error 265 | if currentRepoPath.get().length != 0 266 | upperDirItem = 267 | Name: '..' 268 | result.splice(0, 0, upperDirItem) 269 | currentFileList.set(result) 270 | return 271 | ) 272 | else 273 | dasp.getRepoFile("#{currentRepoPath.get()}/#{item.Name}", (error, type, result) -> 274 | if error != null 275 | showToastMsg('List Repo Error') 276 | throw error 277 | if type == 'dir' 278 | currentRepoPath.set(currentRepoPath.get() + "/" + item.Name) 279 | upperDirItem = 280 | Name: '..' 281 | result.splice(0, 0, upperDirItem) 282 | currentFileList.set(result) 283 | if type == 'file' 284 | displayFileList.set(false) 285 | fileName.set(item.Name) 286 | fileData.set(result) 287 | ) 288 | 289 | 'click .back_to_dir': 290 | (event) -> 291 | displayFileList.set(true) 292 | fileData.set("") 293 | 294 | 'click .upper_dir_btn': 295 | (event) -> 296 | currentRepoPath.set(currentRepoPath.get().slice(0, currentRepoPath.get().lastIndexOf('/'))) 297 | dasp.getRepoFile(currentRepoPath.get(), (error, type, result) -> 298 | if error != null 299 | showToastMsg('List Repo Error') 300 | throw error 301 | currentFileList.set(result) 302 | ) 303 | 304 | 'click .clone_repo_btn': 305 | (event) -> 306 | copyTextToClipboard("https://gateway.ipfs.io/ipfs/#{dasp.repoIPFSHash}/repo.git") 307 | ) 308 | 309 | Template.tasks_tab.events( 310 | 'click .post_task_btn': 311 | (event) -> 312 | 313 | ) 314 | 315 | Template.finances_tab.events( 316 | 'click .fund_dasp_btn': 317 | (event) -> 318 | isEnteringFundAmount.set(true) 319 | return 320 | 321 | 'click .cancel_funding': 322 | (event) -> 323 | isEnteringFundAmount.set(false) 324 | return 325 | 326 | 'submit .fund_amount_entry': 327 | (event) -> 328 | event.preventDefault() 329 | 330 | target = event.target 331 | fundAmount = +target.fund_amount.value 332 | 333 | isEnteringFundAmount.set(false) 334 | target.fund_amount.value = '' 335 | 336 | dasp.getAccounts().then( 337 | () -> 338 | dasp.fundDasp(fundAmount).on('receipt', (receipt) -> 339 | showToastMsg('Fund Success') 340 | refreshDasp() 341 | ).on('error', (error) -> 342 | showToastMsg('Fund Failed') 343 | throw error 344 | ) 345 | ) 346 | 347 | return 348 | ) 349 | 350 | Template.members_tab.events( 351 | 'click .signup_freelancer': 352 | (event) -> 353 | isSigningUp.set(true) 354 | signUpType = 'freelancer' 355 | 356 | 'click .signup_shareholder': 357 | (event) -> 358 | isSigningUp.set(true) 359 | signUpType = 'shareholder' 360 | 361 | 'click .cancel_signup': 362 | (event) -> 363 | isSigningUp.set(false) 364 | 365 | 'submit .signup_username': 366 | (event) -> 367 | event.preventDefault() 368 | 369 | target = event.target 370 | userName = target.username.value 371 | 372 | dasp.signUp(signUpType, userName, (error) -> 373 | if error != null 374 | showToastMsg('Sign Up Error') 375 | throw error 376 | else 377 | dasp.getMemberList((error, result) -> 378 | if error != null 379 | showToastMsg('Sign Up Error') 380 | throw error 381 | else 382 | memberList.set(result) 383 | showToastMsg('Sign Up Success') 384 | throw error 385 | ) 386 | ) 387 | isSigningUp.set(false) 388 | target.username.value = '' 389 | ) -------------------------------------------------------------------------------- /client_ui/imports/ui/dasp_dashboard.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Project-WikiGit/WikiGit/8ef5be8d3015d26685b34bbf414c65bd8d721eb2/client_ui/imports/ui/dasp_dashboard.css -------------------------------------------------------------------------------- /client_ui/imports/ui/dasp_dashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 | 10 | 11 | 12 | 13 |
14 | 17 |
18 | 19 |
20 |
21 | 22 |
23 | 24 |
25 |
26 | 27 |
28 | folder 29 |
30 | Repo 31 |
32 | 33 |
34 | thumbs_up_down 35 |
36 | Votings 37 |
38 | 39 |
40 | assignment_turned_in 41 |
42 | Tasks 43 |
44 | 45 |
46 | attach_money 47 |
48 | Finances 49 |
50 | 51 |
52 | supervisor_account 53 |
54 | Members 55 |
56 |
57 |
58 | {{> repo_tab}} 59 |
60 |
61 | 62 |
63 |
64 | {{> tasks_tab}} 65 |
66 |
67 | {{> finances_tab}} 68 |
69 |
70 | {{> members_tab}} 71 |
72 |
73 | 74 |
75 |

76 | Please enter a valid DASP address. 77 |

78 |
79 | 80 | 81 | 149 | 150 | 153 | 154 | 191 | 192 | 291 | 292 | 344 | 345 | -------------------------------------------------------------------------------- /client_ui/imports/ui/dasp_registry.coffee: -------------------------------------------------------------------------------- 1 | import 'dasp_registry.html' -------------------------------------------------------------------------------- /client_ui/imports/ui/dasp_registry.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /client_ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WikiGit", 3 | "private": true, 4 | "scripts": { 5 | "start": "meteor run" 6 | }, 7 | "dependencies": { 8 | "babel-runtime": "^6.26.0", 9 | "bl": "^1.2.1", 10 | "gift": "^0.10.0", 11 | "ipfs-api": "^15.0.1", 12 | "js-sha3": "^0.6.1", 13 | "material-design-icons": "^3.0.1", 14 | "meteor-node-stubs": "^0.2.11", 15 | "web3": "^1.0.0-beta.24" 16 | }, 17 | "devDependencies": { 18 | "coffeescript": "^2.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dev/build/contracts/Migrations.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Migrations", 3 | "abi": [ 4 | { 5 | "constant": false, 6 | "inputs": [ 7 | { 8 | "name": "new_address", 9 | "type": "address" 10 | } 11 | ], 12 | "name": "upgrade", 13 | "outputs": [], 14 | "payable": false, 15 | "stateMutability": "nonpayable", 16 | "type": "function" 17 | }, 18 | { 19 | "constant": true, 20 | "inputs": [], 21 | "name": "last_completed_migration", 22 | "outputs": [ 23 | { 24 | "name": "", 25 | "type": "uint256" 26 | } 27 | ], 28 | "payable": false, 29 | "stateMutability": "view", 30 | "type": "function" 31 | }, 32 | { 33 | "constant": true, 34 | "inputs": [], 35 | "name": "owner", 36 | "outputs": [ 37 | { 38 | "name": "", 39 | "type": "address" 40 | } 41 | ], 42 | "payable": false, 43 | "stateMutability": "view", 44 | "type": "function" 45 | }, 46 | { 47 | "constant": false, 48 | "inputs": [ 49 | { 50 | "name": "completed", 51 | "type": "uint256" 52 | } 53 | ], 54 | "name": "setCompleted", 55 | "outputs": [], 56 | "payable": false, 57 | "stateMutability": "nonpayable", 58 | "type": "function" 59 | }, 60 | { 61 | "inputs": [], 62 | "payable": false, 63 | "stateMutability": "nonpayable", 64 | "type": "constructor" 65 | } 66 | ], 67 | "bytecode": "0x6060604052341561000f57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102db8061005e6000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100a05780638da5cb5b146100c9578063fdacd5761461011e575b600080fd5b341561007257600080fd5b61009e600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610141565b005b34156100ab57600080fd5b6100b3610224565b6040518082815260200191505060405180910390f35b34156100d457600080fd5b6100dc61022a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561012957600080fd5b61013f600480803590602001909190505061024f565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415610220578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b151561020b57600080fd5b6102c65a03f1151561021c57600080fd5b5050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102ac57806001819055505b505600a165627a7a7230582058839a1a57398bca7cb3a6d808a01c39aceb6c82e527b4f4825027d1f6900c5f0029", 68 | "deployedBytecode": "0x606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100a05780638da5cb5b146100c9578063fdacd5761461011e575b600080fd5b341561007257600080fd5b61009e600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610141565b005b34156100ab57600080fd5b6100b3610224565b6040518082815260200191505060405180910390f35b34156100d457600080fd5b6100dc61022a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561012957600080fd5b61013f600480803590602001909190505061024f565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415610220578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b151561020b57600080fd5b6102c65a03f1151561021c57600080fd5b5050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102ac57806001819055505b505600a165627a7a7230582058839a1a57398bca7cb3a6d808a01c39aceb6c82e527b4f4825027d1f6900c5f0029", 69 | "sourceMap": "28:721:5:-;;;303:66;;;;;;;;351:10;343:5;;:18;;;;;;;;;;;;;;;;;;28:721;;;;;;", 70 | "deployedSourceMap": "28:721:5:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;568:178;;;;;;;;;;;;;;;;;;;;;;;;;;;;183:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;55:20;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;449:111;;;;;;;;;;;;;;;;;;;;;;;;;;568:178;635:19;279:5;;;;;;;;;;;265:19;;:10;:19;;;261:26;;;668:11;635:45;;691:8;:21;;;713:24;;691:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;261:26;568:178;;:::o;183:36::-;;;;:::o;55:20::-;;;;;;;;;;;;;:::o;449:111::-;279:5;;;;;;;;;;;265:19;;:10;:19;;;261:26;;;543:9;516:24;:36;;;;261:26;449:111;:::o", 71 | "source": "pragma solidity ^0.4.18;\r\n\r\ncontract Migrations {\r\n address public owner;\r\n\r\n // A function with the signature `last_completed_migration()`, returning a uint, is required.\r\n uint public last_completed_migration;\r\n\r\n modifier restricted() {\r\n if (msg.sender == owner) _;\r\n }\r\n\r\n function Migrations() public {\r\n owner = msg.sender;\r\n }\r\n\r\n // A function with the signature `setCompleted(uint)` is required.\r\n function setCompleted(uint completed) public restricted {\r\n last_completed_migration = completed;\r\n }\r\n\r\n function upgrade(address new_address) public restricted {\r\n Migrations upgraded = Migrations(new_address);\r\n upgraded.setCompleted(last_completed_migration);\r\n }\r\n}", 72 | "sourcePath": "D:\\WebstormProjects\\WikiGit\\dev\\contracts\\truffle_migrations.sol", 73 | "ast": { 74 | "attributes": { 75 | "absolutePath": "/D/WebstormProjects/WikiGit/dev/contracts/truffle_migrations.sol", 76 | "exportedSymbols": { 77 | "Migrations": [ 78 | 3444 79 | ] 80 | } 81 | }, 82 | "children": [ 83 | { 84 | "attributes": { 85 | "literals": [ 86 | "solidity", 87 | "^", 88 | "0.4", 89 | ".18" 90 | ] 91 | }, 92 | "id": 3389, 93 | "name": "PragmaDirective", 94 | "src": "0:24:5" 95 | }, 96 | { 97 | "attributes": { 98 | "baseContracts": [ 99 | null 100 | ], 101 | "contractDependencies": [ 102 | null 103 | ], 104 | "contractKind": "contract", 105 | "documentation": null, 106 | "fullyImplemented": true, 107 | "linearizedBaseContracts": [ 108 | 3444 109 | ], 110 | "name": "Migrations", 111 | "scope": 3445 112 | }, 113 | "children": [ 114 | { 115 | "attributes": { 116 | "constant": false, 117 | "name": "owner", 118 | "scope": 3444, 119 | "stateVariable": true, 120 | "storageLocation": "default", 121 | "type": "address", 122 | "value": null, 123 | "visibility": "public" 124 | }, 125 | "children": [ 126 | { 127 | "attributes": { 128 | "name": "address", 129 | "type": "address" 130 | }, 131 | "id": 3390, 132 | "name": "ElementaryTypeName", 133 | "src": "55:7:5" 134 | } 135 | ], 136 | "id": 3391, 137 | "name": "VariableDeclaration", 138 | "src": "55:20:5" 139 | }, 140 | { 141 | "attributes": { 142 | "constant": false, 143 | "name": "last_completed_migration", 144 | "scope": 3444, 145 | "stateVariable": true, 146 | "storageLocation": "default", 147 | "type": "uint256", 148 | "value": null, 149 | "visibility": "public" 150 | }, 151 | "children": [ 152 | { 153 | "attributes": { 154 | "name": "uint", 155 | "type": "uint256" 156 | }, 157 | "id": 3392, 158 | "name": "ElementaryTypeName", 159 | "src": "183:4:5" 160 | } 161 | ], 162 | "id": 3393, 163 | "name": "VariableDeclaration", 164 | "src": "183:36:5" 165 | }, 166 | { 167 | "attributes": { 168 | "name": "restricted", 169 | "visibility": "internal" 170 | }, 171 | "children": [ 172 | { 173 | "attributes": { 174 | "parameters": [ 175 | null 176 | ] 177 | }, 178 | "children": [], 179 | "id": 3394, 180 | "name": "ParameterList", 181 | "src": "247:2:5" 182 | }, 183 | { 184 | "children": [ 185 | { 186 | "attributes": { 187 | "falseBody": null 188 | }, 189 | "children": [ 190 | { 191 | "attributes": { 192 | "argumentTypes": null, 193 | "commonType": { 194 | "typeIdentifier": "t_address", 195 | "typeString": "address" 196 | }, 197 | "isConstant": false, 198 | "isLValue": false, 199 | "isPure": false, 200 | "lValueRequested": false, 201 | "operator": "==", 202 | "type": "bool" 203 | }, 204 | "children": [ 205 | { 206 | "attributes": { 207 | "argumentTypes": null, 208 | "isConstant": false, 209 | "isLValue": false, 210 | "isPure": false, 211 | "lValueRequested": false, 212 | "member_name": "sender", 213 | "referencedDeclaration": null, 214 | "type": "address" 215 | }, 216 | "children": [ 217 | { 218 | "attributes": { 219 | "argumentTypes": null, 220 | "overloadedDeclarations": [ 221 | null 222 | ], 223 | "referencedDeclaration": 4043, 224 | "type": "msg", 225 | "value": "msg" 226 | }, 227 | "id": 3395, 228 | "name": "Identifier", 229 | "src": "265:3:5" 230 | } 231 | ], 232 | "id": 3396, 233 | "name": "MemberAccess", 234 | "src": "265:10:5" 235 | }, 236 | { 237 | "attributes": { 238 | "argumentTypes": null, 239 | "overloadedDeclarations": [ 240 | null 241 | ], 242 | "referencedDeclaration": 3391, 243 | "type": "address", 244 | "value": "owner" 245 | }, 246 | "id": 3397, 247 | "name": "Identifier", 248 | "src": "279:5:5" 249 | } 250 | ], 251 | "id": 3398, 252 | "name": "BinaryOperation", 253 | "src": "265:19:5" 254 | }, 255 | { 256 | "id": 3399, 257 | "name": "PlaceholderStatement", 258 | "src": "286:1:5" 259 | } 260 | ], 261 | "id": 3400, 262 | "name": "IfStatement", 263 | "src": "261:26:5" 264 | } 265 | ], 266 | "id": 3401, 267 | "name": "Block", 268 | "src": "250:45:5" 269 | } 270 | ], 271 | "id": 3402, 272 | "name": "ModifierDefinition", 273 | "src": "228:67:5" 274 | }, 275 | { 276 | "attributes": { 277 | "constant": false, 278 | "implemented": true, 279 | "isConstructor": true, 280 | "modifiers": [ 281 | null 282 | ], 283 | "name": "Migrations", 284 | "payable": false, 285 | "scope": 3444, 286 | "stateMutability": "nonpayable", 287 | "superFunction": null, 288 | "visibility": "public" 289 | }, 290 | "children": [ 291 | { 292 | "attributes": { 293 | "parameters": [ 294 | null 295 | ] 296 | }, 297 | "children": [], 298 | "id": 3403, 299 | "name": "ParameterList", 300 | "src": "322:2:5" 301 | }, 302 | { 303 | "attributes": { 304 | "parameters": [ 305 | null 306 | ] 307 | }, 308 | "children": [], 309 | "id": 3404, 310 | "name": "ParameterList", 311 | "src": "332:0:5" 312 | }, 313 | { 314 | "children": [ 315 | { 316 | "children": [ 317 | { 318 | "attributes": { 319 | "argumentTypes": null, 320 | "isConstant": false, 321 | "isLValue": false, 322 | "isPure": false, 323 | "lValueRequested": false, 324 | "operator": "=", 325 | "type": "address" 326 | }, 327 | "children": [ 328 | { 329 | "attributes": { 330 | "argumentTypes": null, 331 | "overloadedDeclarations": [ 332 | null 333 | ], 334 | "referencedDeclaration": 3391, 335 | "type": "address", 336 | "value": "owner" 337 | }, 338 | "id": 3405, 339 | "name": "Identifier", 340 | "src": "343:5:5" 341 | }, 342 | { 343 | "attributes": { 344 | "argumentTypes": null, 345 | "isConstant": false, 346 | "isLValue": false, 347 | "isPure": false, 348 | "lValueRequested": false, 349 | "member_name": "sender", 350 | "referencedDeclaration": null, 351 | "type": "address" 352 | }, 353 | "children": [ 354 | { 355 | "attributes": { 356 | "argumentTypes": null, 357 | "overloadedDeclarations": [ 358 | null 359 | ], 360 | "referencedDeclaration": 4043, 361 | "type": "msg", 362 | "value": "msg" 363 | }, 364 | "id": 3406, 365 | "name": "Identifier", 366 | "src": "351:3:5" 367 | } 368 | ], 369 | "id": 3407, 370 | "name": "MemberAccess", 371 | "src": "351:10:5" 372 | } 373 | ], 374 | "id": 3408, 375 | "name": "Assignment", 376 | "src": "343:18:5" 377 | } 378 | ], 379 | "id": 3409, 380 | "name": "ExpressionStatement", 381 | "src": "343:18:5" 382 | } 383 | ], 384 | "id": 3410, 385 | "name": "Block", 386 | "src": "332:37:5" 387 | } 388 | ], 389 | "id": 3411, 390 | "name": "FunctionDefinition", 391 | "src": "303:66:5" 392 | }, 393 | { 394 | "attributes": { 395 | "constant": false, 396 | "implemented": true, 397 | "isConstructor": false, 398 | "name": "setCompleted", 399 | "payable": false, 400 | "scope": 3444, 401 | "stateMutability": "nonpayable", 402 | "superFunction": null, 403 | "visibility": "public" 404 | }, 405 | "children": [ 406 | { 407 | "children": [ 408 | { 409 | "attributes": { 410 | "constant": false, 411 | "name": "completed", 412 | "scope": 3423, 413 | "stateVariable": false, 414 | "storageLocation": "default", 415 | "type": "uint256", 416 | "value": null, 417 | "visibility": "internal" 418 | }, 419 | "children": [ 420 | { 421 | "attributes": { 422 | "name": "uint", 423 | "type": "uint256" 424 | }, 425 | "id": 3412, 426 | "name": "ElementaryTypeName", 427 | "src": "471:4:5" 428 | } 429 | ], 430 | "id": 3413, 431 | "name": "VariableDeclaration", 432 | "src": "471:14:5" 433 | } 434 | ], 435 | "id": 3414, 436 | "name": "ParameterList", 437 | "src": "470:16:5" 438 | }, 439 | { 440 | "attributes": { 441 | "parameters": [ 442 | null 443 | ] 444 | }, 445 | "children": [], 446 | "id": 3417, 447 | "name": "ParameterList", 448 | "src": "505:0:5" 449 | }, 450 | { 451 | "attributes": { 452 | "arguments": [ 453 | null 454 | ] 455 | }, 456 | "children": [ 457 | { 458 | "attributes": { 459 | "argumentTypes": null, 460 | "overloadedDeclarations": [ 461 | null 462 | ], 463 | "referencedDeclaration": 3402, 464 | "type": "modifier ()", 465 | "value": "restricted" 466 | }, 467 | "id": 3415, 468 | "name": "Identifier", 469 | "src": "494:10:5" 470 | } 471 | ], 472 | "id": 3416, 473 | "name": "ModifierInvocation", 474 | "src": "494:10:5" 475 | }, 476 | { 477 | "children": [ 478 | { 479 | "children": [ 480 | { 481 | "attributes": { 482 | "argumentTypes": null, 483 | "isConstant": false, 484 | "isLValue": false, 485 | "isPure": false, 486 | "lValueRequested": false, 487 | "operator": "=", 488 | "type": "uint256" 489 | }, 490 | "children": [ 491 | { 492 | "attributes": { 493 | "argumentTypes": null, 494 | "overloadedDeclarations": [ 495 | null 496 | ], 497 | "referencedDeclaration": 3393, 498 | "type": "uint256", 499 | "value": "last_completed_migration" 500 | }, 501 | "id": 3418, 502 | "name": "Identifier", 503 | "src": "516:24:5" 504 | }, 505 | { 506 | "attributes": { 507 | "argumentTypes": null, 508 | "overloadedDeclarations": [ 509 | null 510 | ], 511 | "referencedDeclaration": 3413, 512 | "type": "uint256", 513 | "value": "completed" 514 | }, 515 | "id": 3419, 516 | "name": "Identifier", 517 | "src": "543:9:5" 518 | } 519 | ], 520 | "id": 3420, 521 | "name": "Assignment", 522 | "src": "516:36:5" 523 | } 524 | ], 525 | "id": 3421, 526 | "name": "ExpressionStatement", 527 | "src": "516:36:5" 528 | } 529 | ], 530 | "id": 3422, 531 | "name": "Block", 532 | "src": "505:55:5" 533 | } 534 | ], 535 | "id": 3423, 536 | "name": "FunctionDefinition", 537 | "src": "449:111:5" 538 | }, 539 | { 540 | "attributes": { 541 | "constant": false, 542 | "implemented": true, 543 | "isConstructor": false, 544 | "name": "upgrade", 545 | "payable": false, 546 | "scope": 3444, 547 | "stateMutability": "nonpayable", 548 | "superFunction": null, 549 | "visibility": "public" 550 | }, 551 | "children": [ 552 | { 553 | "children": [ 554 | { 555 | "attributes": { 556 | "constant": false, 557 | "name": "new_address", 558 | "scope": 3443, 559 | "stateVariable": false, 560 | "storageLocation": "default", 561 | "type": "address", 562 | "value": null, 563 | "visibility": "internal" 564 | }, 565 | "children": [ 566 | { 567 | "attributes": { 568 | "name": "address", 569 | "type": "address" 570 | }, 571 | "id": 3424, 572 | "name": "ElementaryTypeName", 573 | "src": "585:7:5" 574 | } 575 | ], 576 | "id": 3425, 577 | "name": "VariableDeclaration", 578 | "src": "585:19:5" 579 | } 580 | ], 581 | "id": 3426, 582 | "name": "ParameterList", 583 | "src": "584:21:5" 584 | }, 585 | { 586 | "attributes": { 587 | "parameters": [ 588 | null 589 | ] 590 | }, 591 | "children": [], 592 | "id": 3429, 593 | "name": "ParameterList", 594 | "src": "624:0:5" 595 | }, 596 | { 597 | "attributes": { 598 | "arguments": [ 599 | null 600 | ] 601 | }, 602 | "children": [ 603 | { 604 | "attributes": { 605 | "argumentTypes": null, 606 | "overloadedDeclarations": [ 607 | null 608 | ], 609 | "referencedDeclaration": 3402, 610 | "type": "modifier ()", 611 | "value": "restricted" 612 | }, 613 | "id": 3427, 614 | "name": "Identifier", 615 | "src": "613:10:5" 616 | } 617 | ], 618 | "id": 3428, 619 | "name": "ModifierInvocation", 620 | "src": "613:10:5" 621 | }, 622 | { 623 | "children": [ 624 | { 625 | "attributes": { 626 | "assignments": [ 627 | 3431 628 | ] 629 | }, 630 | "children": [ 631 | { 632 | "attributes": { 633 | "constant": false, 634 | "name": "upgraded", 635 | "scope": 3443, 636 | "stateVariable": false, 637 | "storageLocation": "default", 638 | "type": "contract Migrations", 639 | "value": null, 640 | "visibility": "internal" 641 | }, 642 | "children": [ 643 | { 644 | "attributes": { 645 | "contractScope": null, 646 | "name": "Migrations", 647 | "referencedDeclaration": 3444, 648 | "type": "contract Migrations" 649 | }, 650 | "id": 3430, 651 | "name": "UserDefinedTypeName", 652 | "src": "635:10:5" 653 | } 654 | ], 655 | "id": 3431, 656 | "name": "VariableDeclaration", 657 | "src": "635:19:5" 658 | }, 659 | { 660 | "attributes": { 661 | "argumentTypes": null, 662 | "isConstant": false, 663 | "isLValue": false, 664 | "isPure": false, 665 | "isStructConstructorCall": false, 666 | "lValueRequested": false, 667 | "names": [ 668 | null 669 | ], 670 | "type": "contract Migrations", 671 | "type_conversion": true 672 | }, 673 | "children": [ 674 | { 675 | "attributes": { 676 | "argumentTypes": [ 677 | { 678 | "typeIdentifier": "t_address", 679 | "typeString": "address" 680 | } 681 | ], 682 | "overloadedDeclarations": [ 683 | null 684 | ], 685 | "referencedDeclaration": 3444, 686 | "type": "type(contract Migrations)", 687 | "value": "Migrations" 688 | }, 689 | "id": 3432, 690 | "name": "Identifier", 691 | "src": "657:10:5" 692 | }, 693 | { 694 | "attributes": { 695 | "argumentTypes": null, 696 | "overloadedDeclarations": [ 697 | null 698 | ], 699 | "referencedDeclaration": 3425, 700 | "type": "address", 701 | "value": "new_address" 702 | }, 703 | "id": 3433, 704 | "name": "Identifier", 705 | "src": "668:11:5" 706 | } 707 | ], 708 | "id": 3434, 709 | "name": "FunctionCall", 710 | "src": "657:23:5" 711 | } 712 | ], 713 | "id": 3435, 714 | "name": "VariableDeclarationStatement", 715 | "src": "635:45:5" 716 | }, 717 | { 718 | "children": [ 719 | { 720 | "attributes": { 721 | "argumentTypes": null, 722 | "isConstant": false, 723 | "isLValue": false, 724 | "isPure": false, 725 | "isStructConstructorCall": false, 726 | "lValueRequested": false, 727 | "names": [ 728 | null 729 | ], 730 | "type": "tuple()", 731 | "type_conversion": false 732 | }, 733 | "children": [ 734 | { 735 | "attributes": { 736 | "argumentTypes": [ 737 | { 738 | "typeIdentifier": "t_uint256", 739 | "typeString": "uint256" 740 | } 741 | ], 742 | "isConstant": false, 743 | "isLValue": false, 744 | "isPure": false, 745 | "lValueRequested": false, 746 | "member_name": "setCompleted", 747 | "referencedDeclaration": 3423, 748 | "type": "function (uint256) external" 749 | }, 750 | "children": [ 751 | { 752 | "attributes": { 753 | "argumentTypes": null, 754 | "overloadedDeclarations": [ 755 | null 756 | ], 757 | "referencedDeclaration": 3431, 758 | "type": "contract Migrations", 759 | "value": "upgraded" 760 | }, 761 | "id": 3436, 762 | "name": "Identifier", 763 | "src": "691:8:5" 764 | } 765 | ], 766 | "id": 3438, 767 | "name": "MemberAccess", 768 | "src": "691:21:5" 769 | }, 770 | { 771 | "attributes": { 772 | "argumentTypes": null, 773 | "overloadedDeclarations": [ 774 | null 775 | ], 776 | "referencedDeclaration": 3393, 777 | "type": "uint256", 778 | "value": "last_completed_migration" 779 | }, 780 | "id": 3439, 781 | "name": "Identifier", 782 | "src": "713:24:5" 783 | } 784 | ], 785 | "id": 3440, 786 | "name": "FunctionCall", 787 | "src": "691:47:5" 788 | } 789 | ], 790 | "id": 3441, 791 | "name": "ExpressionStatement", 792 | "src": "691:47:5" 793 | } 794 | ], 795 | "id": 3442, 796 | "name": "Block", 797 | "src": "624:122:5" 798 | } 799 | ], 800 | "id": 3443, 801 | "name": "FunctionDefinition", 802 | "src": "568:178:5" 803 | } 804 | ], 805 | "id": 3444, 806 | "name": "ContractDefinition", 807 | "src": "28:721:5" 808 | } 809 | ], 810 | "id": 3445, 811 | "name": "SourceUnit", 812 | "src": "0:749:5" 813 | }, 814 | "compiler": { 815 | "name": "solc", 816 | "version": "0.4.18+commit.9cf6e910.Emscripten.clang" 817 | }, 818 | "networks": {}, 819 | "schemaVersion": "1.0.1", 820 | "updatedAt": "2018-01-06T02:20:33.244Z" 821 | } -------------------------------------------------------------------------------- /dev/contracts/dao.sol: -------------------------------------------------------------------------------- 1 | /* 2 | dao.sol 3 | Created by Zefram Lou (Zebang Liu) as part of the WikiGit project. 4 | 5 | This file implements the mechanics of the DAO, including managing members, 6 | holding votings, modifying the bylaws and structure of the DAO, calling and 7 | managing other modules, allowing modules to access other modules, and so on. 8 | 9 | The DAO is the absolute source of power of the entire DAP. 10 | */ 11 | 12 | pragma solidity ^0.4.18; 13 | 14 | import './token.sol'; 15 | import './vault.sol'; 16 | import './tasks_handler.sol'; 17 | import './member_handler.sol'; 18 | 19 | contract Dao is Module { 20 | struct Voting { 21 | string name; 22 | string description; 23 | address creator; 24 | uint startBlockNumber; 25 | uint forVotes; 26 | uint againstVotes; 27 | uint votedMemberCount; 28 | bytes32 executionHash; //The hash of the transaction bytecode that is executed after the voting is passed. 29 | address executionTarget; //The address that the transaction would be sent to. 30 | mapping(address => bool) hasVoted; 31 | bool isInvalid; 32 | bool isPassed; 33 | } 34 | 35 | modifier notBanned { 36 | MemberHandler h = MemberHandler(moduleAddress('MEMBER')); 37 | require(! h.isBanned(msg.sender)); 38 | _; 39 | } 40 | //Should only be used in functions meant to be directly called by members and don't need any rights. 41 | 42 | modifier needsRight(string right) { 43 | MemberHandler h = MemberHandler(moduleAddress('MEMBER')); 44 | require(h.memberHasRight(msg.sender, right)); 45 | require(! h.isBanned(msg.sender)); //Makes function declarations more concise. 46 | _; 47 | } 48 | 49 | //Voting session parameters 50 | uint public quorumPercent; //Decimal (ex. 34.567%) 51 | uint public minForPercent; //Minimum proportion of for votes needed to pass the voting. Decimal. 52 | uint public activeTimeInBlocks; //The number of blocks for which the voting is active. 53 | uint public goodRepWeight; //Decimal 54 | uint public badRepWeight; //Decimal 55 | uint public tokenWeight; //Decimal 56 | 57 | Voting[] public votingList; 58 | 59 | event VotingCreated(uint votingId); 60 | event VotingConcluded(uint votingId, bool passed); 61 | 62 | //Initializing 63 | 64 | function Dao( 65 | address _mainAddr, 66 | uint _quorumPercent, 67 | uint _minForPercent, 68 | uint _activeTimeInBlocks, 69 | uint _goodRepWeight, 70 | uint _badRepWeight, 71 | uint _tokenWeight 72 | ) 73 | Module(_mainAddr) 74 | public 75 | { 76 | quorumPercent = _quorumPercent; 77 | minForPercent = _minForPercent; 78 | activeTimeInBlocks = _activeTimeInBlocks; 79 | goodRepWeight = _goodRepWeight; 80 | badRepWeight = _badRepWeight; 81 | tokenWeight = _tokenWeight; 82 | } 83 | 84 | //Voting 85 | 86 | function createVoting( 87 | string _name, 88 | string _description, 89 | uint _startBlockNumber, 90 | bytes32 _executionHash, 91 | address _executionTarget 92 | ) 93 | public 94 | needsRight('create_voting') 95 | { 96 | votingList.push(Voting({ 97 | name: _name, 98 | description: _description, 99 | creator: msg.sender, 100 | startBlockNumber: _startBlockNumber, 101 | executionHash: _executionHash, 102 | executionTarget: _executionTarget, 103 | forVotes: 0, 104 | againstVotes: 0, 105 | votedMemberCount: 0, 106 | isInvalid: false, 107 | isPassed:false 108 | })); 109 | 110 | VotingCreated(votingList.length - 1); 111 | } 112 | 113 | function invalidateVotingAtIndex(uint _index) public onlyMod('DAO') { 114 | require(_index < votingList.length); 115 | 116 | votingList[_index].isInvalid = true; 117 | } 118 | 119 | function vote(uint _votingId, bool _support) public needsRight('vote') { 120 | Voting storage voting = votingList[_votingId]; 121 | 122 | MemberHandler h = MemberHandler(moduleAddress('MEMBER')); 123 | var (,goodRep, badRep) = h.memberList(h.memberId(msg.sender)); 124 | 125 | require(!voting.isInvalid); 126 | require(block.number >= voting.startBlockNumber && block.number < voting.startBlockNumber + activeTimeInBlocks); 127 | require(!voting.hasVoted[msg.sender]); 128 | 129 | voting.hasVoted[msg.sender] = true; 130 | 131 | //Only team members count towards quorum 132 | if (h.memberHasRight(msg.sender, 'quorum_include')) { 133 | voting.votedMemberCount += 1; 134 | } 135 | 136 | int memberVotes = int(goodRepWeight * goodRep / 10**decimals) - int(badRepWeight * badRep / 10**decimals); 137 | Token token = Token(moduleAddress('TOKEN')); 138 | memberVotes += int(tokenWeight * token.balanceOf(msg.sender) / 10**(2 * decimals)); 139 | 140 | if (memberVotes < 0) { 141 | memberVotes = 0; 142 | } 143 | 144 | if (_support) { 145 | voting.forVotes += uint(memberVotes); 146 | } else { 147 | voting.againstVotes += uint(memberVotes); 148 | } 149 | } 150 | 151 | function concludeVoting(uint _votingId) public needsRight('vote') { 152 | Voting storage voting = votingList[_votingId]; 153 | require(!voting.isInvalid); 154 | voting.isInvalid = true; 155 | 156 | require(block.number >= voting.startBlockNumber + activeTimeInBlocks); 157 | 158 | MemberHandler h = MemberHandler(moduleAddress('MEMBER')); 159 | 160 | uint teamMemberCount = h.groupMemberCount(keccak256('team_member')); 161 | 162 | voting.isPassed = (voting.forVotes * 100 * 10**decimals / (voting.forVotes + voting.againstVotes) >= minForPercent) 163 | && (voting.votedMemberCount * 100 * 10**decimals / teamMemberCount >= quorumPercent); 164 | VotingConcluded(_votingId, voting.isPassed); 165 | } 166 | 167 | function executeVoting( 168 | uint _votingId, 169 | bytes _executionBytecode 170 | ) 171 | public 172 | needsRight('vote') 173 | { 174 | Voting storage voting = votingList[_votingId]; 175 | require(voting.isPassed); 176 | require(voting.executionHash == keccak256(_executionBytecode)); 177 | 178 | require(voting.executionTarget.call(_executionBytecode)); 179 | } 180 | 181 | function paySolutionReward(uint _taskId, uint _solId) public onlyMod('TASKS') { 182 | TasksHandler handler = TasksHandler(moduleAddress('TASKS')); 183 | var (_,,rewardInWeis, rewardTokenAmount, rewardGoodRep,) = handler.taskList(_taskId); 184 | var (__,submitter,) = handler.taskSolutionList(_taskId, _solId); 185 | 186 | //Reward in ether 187 | Vault vault = Vault(moduleAddress('VAULT')); 188 | vault.addPendingWithdrawl(rewardInWeis, submitter, true, true); 189 | 190 | //Reward in reputation 191 | paySolutionRewardGoodRep(submitter, rewardGoodRep); 192 | 193 | //Reward in tokens 194 | vault.addPendingWithdrawl(rewardTokenAmount, submitter, true, false); 195 | } 196 | 197 | //Split out as an independent function to prevent StackTooDeep error 198 | function paySolutionRewardGoodRep(address _submitter, uint _rewardGoodRep) internal { 199 | MemberHandler h = MemberHandler(moduleAddress('MEMBER')); 200 | h.incMemberGoodRep(_submitter, _rewardGoodRep); 201 | } 202 | 203 | /* 204 | Used for penalizing malicious solution submitters. 205 | */ 206 | function penalizeSolutionSubmitter( 207 | uint _taskId, 208 | uint _solId 209 | ) 210 | public 211 | onlyMod('TASKS') 212 | { 213 | TasksHandler handler = TasksHandler(moduleAddress('TASKS')); 214 | var (_,,,, penaltyBadRep,) = handler.taskList(_taskId); 215 | var (__,submitter,) = handler.taskSolutionList(_taskId, _solId); 216 | 217 | //Penalize reputation 218 | MemberHandler h = MemberHandler(moduleAddress('MEMBER')); 219 | h.incMemberBadRep(submitter, penaltyBadRep); 220 | } 221 | 222 | //Getters 223 | 224 | function getVotingListCount() public view returns(uint) { 225 | return votingList.length; 226 | } 227 | 228 | function vHasVoted(uint _votingId, address _addr) public view returns(bool) { 229 | return votingList[_votingId].hasVoted[_addr]; 230 | } 231 | 232 | //Fallback 233 | function() public { 234 | revert(); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /dev/contracts/main.sol: -------------------------------------------------------------------------------- 1 | /* 2 | main.sol 3 | Created by Zefram Lou (Zebang Liu) as part of the WikiGit project. 4 | 5 | This file implements the main contract of the DAP, or in other words 6 | the module manager. The main contract records the metadata of the DAP, 7 | as well as the names and addresses of all modules, including the DAO. 8 | It is the first contract created when initializing a DAP. Its records 9 | of the modules is the only place modules look to when trying to communicate 10 | with other modules, and this centralized index of modules allows individual 11 | module contracts to be updated down the road, which also means that 12 | the main contract is the only contract in the DAP that cannot be updated. 13 | */ 14 | 15 | pragma solidity ^0.4.18; 16 | 17 | contract Module { 18 | modifier onlyMod(string mod) { require(msg.sender == moduleAddress(mod)); _; } 19 | 20 | address public mainAddress; 21 | uint public constant decimals = 18; 22 | 23 | function Module(address _mainAddress) public { 24 | mainAddress = _mainAddress; 25 | } 26 | 27 | function moduleAddress(string mod) internal view returns(address addr) { 28 | Main main = Main(mainAddress); 29 | addr = main.moduleAddresses(keccak256(mod)); 30 | } 31 | } 32 | 33 | contract Main { 34 | mapping(bytes32 => address) public moduleAddresses; 35 | string[] public moduleNames; 36 | string public metadata; 37 | bool public hasInitedAddrs; 38 | bool public hasInitedABIs; 39 | address private creator; 40 | bytes[] public abiIPFSHashes; 41 | mapping(bytes32 => uint) public abiHashId; 42 | 43 | modifier onlyDao{ require(msg.sender == moduleAddresses['DAO']); _; } 44 | 45 | function Main(string _metadata, bytes _abiIPFSHash) public { 46 | metadata = _metadata; 47 | abiIPFSHashes.push(_abiIPFSHash); 48 | creator = msg.sender; 49 | } 50 | 51 | /* 52 | Used for initializing the module address index. As string[] variables can't be passed as function parameters, the addrs array 53 | must be ordered in the following way: 54 | DAO, MEMBER, VAULT, TASKS, GIT, TOKEN 55 | @param address[] addrs The array that stores the addresses of all initial modules. Must follow the specified format. 56 | */ 57 | 58 | function initializeModuleAddresses(address[] _addrs) public { 59 | require(!hasInitedAddrs); 60 | require(msg.sender == creator); 61 | hasInitedAddrs = true; 62 | moduleNames = ['DAO', 'MEMBER', 'VAULT', 'TASKS', 'GIT', 'TOKEN']; 63 | 64 | for (uint i = 0; i < moduleNames.length; i++) { 65 | moduleAddresses[keccak256(moduleNames[i])] = _addrs[i]; 66 | } 67 | } 68 | 69 | function initializeABIHashForMod(uint _modId, bytes _abiHash) public { 70 | require(msg.sender == creator); 71 | require(!hasInitedABIs); 72 | 73 | abiIPFSHashes.push(_abiHash); 74 | abiHashId[keccak256(moduleNames[_modId])] = abiIPFSHashes.length - 1; 75 | 76 | if (abiIPFSHashes.length >= 7) { 77 | hasInitedABIs = true; 78 | } 79 | } 80 | 81 | function getABIHashForMod(bytes32 _modHash) public view returns(bytes abiHash) { 82 | return abiIPFSHashes[abiHashId[_modHash]]; 83 | } 84 | 85 | function setABIHashForMod(bytes32 _modHash, bytes _abiHash) public onlyDao { 86 | abiIPFSHashes[abiHashId[_modHash]] = _abiHash; 87 | } 88 | 89 | function setModuleAddress( 90 | string _modName, 91 | address _addr, 92 | bool _isNew 93 | ) 94 | public 95 | onlyDao 96 | { 97 | moduleAddresses[keccak256(_modName)] = _addr; 98 | if (_isNew) { 99 | moduleNames.push(_modName); 100 | } 101 | } 102 | 103 | function removeModuleAtIndex(uint _index) public onlyDao { 104 | bytes32 nameHash = keccak256(moduleNames[_index]); 105 | delete moduleAddresses[nameHash]; 106 | delete moduleNames[_index]; 107 | delete abiIPFSHashes[_index]; 108 | delete abiHashId[nameHash]; 109 | } 110 | 111 | function setMetadata(string _meta) public onlyDao { 112 | metadata = _meta; 113 | } 114 | 115 | //Fallback 116 | function() public { 117 | revert(); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /dev/contracts/member_handler.sol: -------------------------------------------------------------------------------- 1 | /* 2 | member_handler.sol 3 | Created by Zefram Lou (Zebang Liu) as part of the WikiGit project. 4 | 5 | This file implements the member manifest and related functions. 6 | */ 7 | 8 | pragma solidity ^0.4.18; 9 | 10 | import 'zeppelin-solidity/contracts/math/SafeMath.sol'; 11 | import './main.sol'; 12 | import './dao.sol'; 13 | import './token.sol'; 14 | 15 | contract MemberHandler is Module { 16 | /* 17 | Defines a member of the DAO. 18 | */ 19 | struct Member { 20 | string userName; 21 | address userAddress; 22 | string groupName; //The member group that the member belongs to. 23 | uint goodRep; //Good reputation 24 | uint badRep; //Bad reputation 25 | } 26 | 27 | //Should only be used in functions meant to be directly called by members and don't need any rights. 28 | modifier notBanned { require(!isBanned[msg.sender]); _; } 29 | 30 | uint public memberCount; 31 | 32 | Member[] public memberList; 33 | 34 | //From member address to the index of the member in memberList. 35 | mapping(address => uint) public memberId; 36 | 37 | /* 38 | Defines the rights of members in each member group. 39 | keccak256(groupName) => (keccak256(rightName) => hasRight) 40 | */ 41 | mapping(bytes32 => mapping(bytes32 => bool)) public groupRights; 42 | 43 | mapping(bytes32 => uint) public groupMemberCount; //Group name's keccak256 hash to member count. 44 | 45 | mapping(address => bool) public isBanned; 46 | 47 | function MemberHandler(string _creatorUserName, address _mainAddr) Module(_mainAddr) public { 48 | memberList.push(Member('',0,'',0,0)); //Member at index 0 is reserved, for efficiently checking whether an address has already been registered. 49 | 50 | //Add msg.sender as member #1 51 | memberList.push(Member(_creatorUserName, msg.sender, 'team_member', 1, 0)); 52 | memberId[msg.sender] = 1; 53 | memberCount += 1; 54 | groupMemberCount[keccak256('team_member')] += 1; 55 | 56 | //Initialize group rights 57 | //Team member rights 58 | setGroupRight('team_member', 'create_voting', true); 59 | setGroupRight('team_member', 'submit_task', true); 60 | setGroupRight('team_member', 'submit_task_rewardless', true); 61 | setGroupRight('team_member', 'vote', true); 62 | setGroupRight('team_member', 'quorum_include', true); 63 | setGroupRight('team_member', 'submit_solution', true); 64 | setGroupRight('team_member', 'vote_solution', true); 65 | setGroupRight('team_member', 'accept_hash', true); 66 | 67 | //Part time contributor rights 68 | setGroupRight('contributor', 'create_voting', true); 69 | setGroupRight('contributor', 'submit_task_rewardless', true); 70 | setGroupRight('contributor', 'vote', true); 71 | setGroupRight('contributor', 'submit_solution', true); 72 | 73 | //Pure shareholder (shareholder who doesn't contribute) rights 74 | setGroupRight('pure_shareholder', 'vote', true); 75 | setGroupRight('pure_shareholder', 'create_voting', true); 76 | } 77 | 78 | //Member functions 79 | 80 | function addMember( 81 | string _userName, 82 | address _userAddress, 83 | string _groupName, 84 | uint _goodRep, 85 | uint _badRep 86 | ) 87 | public 88 | onlyMod('DAO') 89 | { 90 | require(memberId[_userAddress] == 0); //Prevent altering existing members. ID 0 is reserved for creator. 91 | memberList.push(Member({ 92 | userName: _userName, 93 | userAddress: _userAddress, 94 | groupName: _groupName, 95 | goodRep: _goodRep, 96 | badRep: _badRep 97 | })); 98 | memberId[_userAddress] = memberList.length - 1; 99 | memberCount += 1; 100 | groupMemberCount[keccak256(_groupName)] += 1; 101 | } 102 | 103 | //Used by freelancers to add themselves into the member list so that they can submit task solutions. 104 | function setSelfAsContributor(string userName) public notBanned { 105 | require(memberId[msg.sender] == 0); //Ensure user doesn't already exist 106 | 107 | memberId[msg.sender] = memberList.length; 108 | memberList.push(Member({ 109 | userName: userName, 110 | userAddress: msg.sender, 111 | groupName: 'freelancer', 112 | goodRep: 0, 113 | badRep: 0 114 | })); 115 | } 116 | 117 | //Used by shareholders who do not contribute to the project to add themselves into the member list, 118 | //so that they can vote. 119 | function setSelfAsPureShareholder(string _userName) public notBanned { 120 | require(memberId[msg.sender] == 0); //Ensure user doesn't already exist 121 | //Check if msg.sender has any voting shares 122 | Token token = Token(moduleAddress('TOKEN')); 123 | require(token.balanceOf(msg.sender) > 0); 124 | 125 | memberId[msg.sender] = memberList.length; 126 | memberList.push(Member({ 127 | userName: _userName, 128 | userAddress: msg.sender, 129 | groupName: 'pure_shareholder', 130 | goodRep: 0, 131 | badRep: 0 132 | })); 133 | 134 | memberCount += 1; 135 | groupMemberCount[keccak256('pure_shareholder')] += 1; 136 | } 137 | 138 | function removeMemberWithAddress(address _addr) public onlyMod('DAO') { 139 | uint index = memberId[_addr]; 140 | require(index != 0); //Ensure member exists. 141 | 142 | Member storage member = memberList[index]; 143 | require(keccak256(member.groupName) != keccak256('')); 144 | 145 | memberCount -= 1; 146 | groupMemberCount[keccak256(member.groupName)] -= 1; 147 | 148 | delete memberList[index]; 149 | delete memberId[_addr]; 150 | } 151 | 152 | function alterBannedStatus(address _addr, bool _newStatus) public onlyMod('DAO') { 153 | require(memberId[_addr] != 0); //Ensure member exists. 154 | 155 | Member storage member = getMemberAtAddress(_addr); 156 | if (_newStatus && !isBanned[_addr]) { 157 | groupMemberCount[keccak256(member.groupName)] -= 1; 158 | } else if (!_newStatus && isBanned[_addr]) { 159 | groupMemberCount[keccak256(member.groupName)] += 1; 160 | } 161 | isBanned[_addr] = _newStatus; 162 | } 163 | 164 | function incMemberGoodRep(address _addr, uint _amount) public onlyMod('DAO') { 165 | require(memberId[_addr] != 0); //Ensure member exists. 166 | 167 | Member storage member = getMemberAtAddress(_addr); 168 | member.goodRep += _amount; 169 | } 170 | 171 | function incMemberBadRep(address _addr, uint _amount) public onlyMod('DAO') { 172 | require(memberId[_addr] != 0); //Ensure member exists. 173 | 174 | Member storage member = getMemberAtAddress(_addr); 175 | member.badRep += _amount; 176 | } 177 | 178 | function changeMemberGroup(uint _id, string _newGroupName) public onlyMod('DAO') { 179 | groupMemberCount[keccak256(memberList[_id].groupName)] -= 1; 180 | groupMemberCount[keccak256(_newGroupName)] += 1; 181 | memberList[_id].groupName = _newGroupName; 182 | } 183 | 184 | function changeSelfName(string _newName) public notBanned { 185 | require(keccak256(getMemberAtAddress(msg.sender).groupName) != keccak256('')); 186 | getMemberAtAddress(msg.sender).userName = _newName; 187 | } 188 | 189 | function changeSelfAddress(address _newAddress) public notBanned { 190 | require(keccak256(getMemberAtAddress(msg.sender).groupName) != keccak256('')); 191 | getMemberAtAddress(msg.sender).userAddress = _newAddress; 192 | memberId[_newAddress] = memberId[msg.sender]; 193 | memberId[msg.sender] = 0; 194 | } 195 | 196 | //Do not confuse with setGroupRight(). This function allows votings to execute setGroupRight(). 197 | function setRightOfGroup( 198 | string groupName, 199 | string rightName, 200 | bool hasRight 201 | ) 202 | public 203 | onlyMod('DAO') 204 | { 205 | setGroupRight(groupName, rightName, hasRight); 206 | } 207 | 208 | //Getters 209 | 210 | function getMemberAtAddress(address _addr) internal view returns(Member storage) { 211 | return memberList[memberId[_addr]]; 212 | } 213 | 214 | function getGroupRight(string _groupName, string _right) public view returns(bool) { 215 | return groupRights[keccak256(_groupName)][keccak256(_right)]; 216 | } 217 | 218 | function memberHasRight(address _addr, string _right) public view returns(bool) { 219 | return getGroupRight(getMemberAtAddress(_addr).groupName, _right); 220 | } 221 | 222 | function memberGroupNameHash(address _addr) public view returns(bytes32) { 223 | return keccak256(getMemberAtAddress(_addr).groupName); 224 | } 225 | 226 | function getMemberListCount() public view returns(uint) { 227 | return memberList.length; 228 | } 229 | 230 | //Setters 231 | 232 | //Do not confuse with setRightOfGroup(). This is an internal helper function. 233 | function setGroupRight( 234 | string _groupName, 235 | string _right, 236 | bool _hasRight 237 | ) 238 | internal 239 | { 240 | groupRights[keccak256(_groupName)][keccak256(_right)] = _hasRight; 241 | } 242 | 243 | //Fallback 244 | function() public { 245 | revert(); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /dev/contracts/repo_handler.sol: -------------------------------------------------------------------------------- 1 | /* 2 | repo_handler.sol 3 | Created by Zefram Lou (Zebang Liu) as part of the WikiGit project. 4 | 5 | Records the IPFS hash of the DASP's repo. Team members are responsible for incorporating the latest task solutions 6 | into the current repo and updating their personal IPFS hash. The UI would display the hash with majority support as 7 | the repo's hash. 8 | */ 9 | 10 | pragma solidity ^0.4.18; 11 | 12 | import './main.sol'; 13 | import './member_handler.sol'; 14 | 15 | contract RepoHandler is Module { 16 | modifier needsRight(string right) { 17 | MemberHandler h = MemberHandler(moduleAddress('MEMBER')); 18 | require(h.memberHasRight(msg.sender, right)); 19 | require(! h.isBanned(msg.sender)); //Makes function declarations more concise. 20 | _; 21 | } 22 | 23 | mapping(address => bytes) personalIPFSHashes; 24 | 25 | function RepoHandler(address _mainAddr) Module(_mainAddr) public {} 26 | 27 | function setPersonalHash(bytes _hash) public needsRight('accept_hash') { 28 | personalIPFSHashes[msg.sender] = _hash; 29 | } 30 | 31 | //Fallback 32 | function() public { 33 | revert(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /dev/contracts/tasks_handler.sol: -------------------------------------------------------------------------------- 1 | /* 2 | tasks_handler.sol 3 | Created by Zefram Lou (Zebang Liu) as part of the WikiGit project. 4 | 5 | This file implements the mechanisms for posting task listings, submitting 6 | task solutions, and accepting task solutions as the final answers. It should 7 | be possible to modify this file to allow compatability with third-party freelancing 8 | platforms. 9 | */ 10 | 11 | pragma solidity ^0.4.18; 12 | 13 | import './main.sol'; 14 | import './dao.sol'; 15 | import './member_handler.sol'; 16 | 17 | contract TasksHandler is Module { 18 | enum TaskState { Open, Solved, Invalid } 19 | 20 | struct TaskListing { 21 | string metadata; //Metadata of the task. Format dependent on the higher level UI. 22 | address poster; 23 | uint rewardInWeis; 24 | uint rewardTokenAmount; //Amount of rewarded tokens 25 | uint rewardGoodRep; //Reward in good reputation. 26 | uint penaltyBadRep; //Penalty in bad reputation if a solution is deemed malicious. 27 | TaskState state; 28 | uint acceptedSolutionID; //Index of the accepted solution. 29 | mapping(address => bool) hasUpvoted; //Records whether a team member has upvoted a solution. 30 | mapping(address => bool) hasSubmitted; //Records whether a user has already submitted a solution. 31 | mapping(address => bool) hasBeenPenalized; //Recordes whether a user has been penalized for a malicious solution. 32 | mapping(address => uint) memberSolId; //From member address to index of member's solution submission. 33 | } 34 | 35 | /* 36 | Defines a task solution. 37 | */ 38 | struct TaskSolution { 39 | string metadata; //Metadata of the task. Format dependent on the higher level UI. 40 | address submitter; 41 | bytes patchIPFSHash; //IPFS hash of the Git patch. 42 | /* 43 | Solution voting allow members to express their evaluations of a solution. 44 | Solutions that receives upvote from more than 2/3 of team members will be accepted. 45 | Solutions that receives downvote from more than 2/3 of team members will be open to penalization. 46 | */ 47 | uint upvotes; 48 | uint downvotes; 49 | mapping(address => bool) hasDownvoted; 50 | } 51 | 52 | modifier needsRight(string right) { 53 | MemberHandler h = MemberHandler(moduleAddress('MEMBER')); 54 | require(h.memberHasRight(msg.sender, right)); 55 | require(! h.isBanned(msg.sender)); //Makes function declarations more concise. 56 | _; 57 | } 58 | 59 | TaskListing[] public taskList; 60 | TaskSolution[][] public taskSolutionList; 61 | 62 | /* 63 | Upper bounds for reward amounts. 64 | */ 65 | uint public rewardRepCap; 66 | uint public penaltyRepCap; 67 | uint public rewardWeiCap; 68 | uint public rewardTokenCap; 69 | 70 | event TaskSolutionAccepted(uint taskId, uint solutionId, bytes patchIPFSHash); 71 | 72 | function TasksHandler( 73 | address mainAddr, 74 | uint _rewardRepCap, 75 | uint _penaltyRepCap, 76 | uint _rewardWeiCap, 77 | uint _rewardTokenCap 78 | ) 79 | Module(mainAddr) 80 | public 81 | { 82 | //Initialize reward caps 83 | rewardRepCap = _rewardRepCap; 84 | penaltyRepCap = _penaltyRepCap; 85 | rewardWeiCap = _rewardWeiCap; 86 | rewardTokenCap = _rewardTokenCap; 87 | } 88 | 89 | function publishTaskListing( 90 | string _metadata, 91 | address _poster, 92 | uint _rewardInWeis, 93 | uint _rewardTokenAmount, 94 | uint _rewardGoodRep, 95 | uint _penaltyBadRep 96 | ) 97 | public 98 | needsRight('submit_task') 99 | { 100 | //Check if reward exceeds cap 101 | require(_rewardInWeis <= rewardWeiCap); 102 | require(_rewardTokenAmount <= rewardTokenCap); 103 | require(_rewardGoodRep <= rewardRepCap); 104 | require(_penaltyBadRep <= penaltyRepCap); 105 | 106 | taskList.push(TaskListing({ 107 | metadata: _metadata, 108 | poster: _poster, 109 | rewardInWeis: _rewardInWeis, 110 | rewardTokenAmount: _rewardTokenAmount, 111 | rewardGoodRep: _rewardGoodRep, 112 | penaltyBadRep: _penaltyBadRep, 113 | state: TaskState.Open, 114 | acceptedSolutionID: 0 115 | })); 116 | taskSolutionList.length += 1; 117 | } 118 | 119 | /* 120 | For part time contributors who don't have the right to post task listings with rewards. 121 | */ 122 | function publishRewardlessTaskListing( 123 | string _metadata, 124 | address _poster, 125 | uint _rewardGoodRep, 126 | uint _penaltyBadRep 127 | ) 128 | public 129 | needsRight('submit_task_rewardless') 130 | { 131 | require(_rewardGoodRep <= rewardRepCap); 132 | require(_penaltyBadRep <= penaltyRepCap); 133 | 134 | taskList.push(TaskListing({ 135 | metadata: _metadata, 136 | poster: _poster, 137 | rewardInWeis: 0, 138 | rewardTokenAmount: 0, 139 | rewardGoodRep: _rewardGoodRep, 140 | penaltyBadRep: _penaltyBadRep, 141 | state: TaskState.Open, 142 | acceptedSolutionID: 0 143 | })); 144 | 145 | taskSolutionList.length += 1; 146 | } 147 | 148 | function invalidateTaskListingAtIndex(uint _index) public onlyMod('DAO') { 149 | require(_index < taskList.length); 150 | require(taskList[_index].state == TaskState.Open); 151 | taskList[_index].state = TaskState.Invalid; 152 | } 153 | 154 | function submitSolution( 155 | uint _taskId, 156 | string _metadata, 157 | bytes _patchIPFSHash 158 | ) 159 | public 160 | needsRight('submit_solution') 161 | { 162 | require(_taskId < taskList.length); 163 | 164 | TaskListing storage task = taskList[_taskId]; 165 | 166 | require(task.state == TaskState.Open); 167 | 168 | if (! task.hasSubmitted[msg.sender]) { 169 | task.hasSubmitted[msg.sender] = true; 170 | taskSolutionList[_taskId].push(TaskSolution({ 171 | metadata: _metadata, 172 | submitter: msg.sender, 173 | patchIPFSHash: _patchIPFSHash, 174 | upvotes: 0, 175 | downvotes: 0 176 | })); 177 | taskList[_taskId].memberSolId[msg.sender] = taskSolutionList.length - 1; 178 | } else { 179 | uint solId = taskList[_taskId].memberSolId[msg.sender]; 180 | taskSolutionList[_taskId][solId].metadata = _metadata; 181 | taskSolutionList[_taskId][solId].patchIPFSHash = _patchIPFSHash; 182 | } 183 | } 184 | 185 | function voteOnSolution( 186 | uint _taskId, 187 | uint _solId, 188 | bool _isUpvote 189 | ) 190 | public 191 | needsRight('vote_solution') 192 | { 193 | require(_taskId < taskList.length); 194 | require(_solId < taskSolutionList[_taskId].length); 195 | require(taskList[_taskId].state == TaskState.Open); 196 | 197 | if (_isUpvote) { 198 | TaskListing storage task = taskList[_taskId]; 199 | require(! task.hasUpvoted[msg.sender]); 200 | sol.upvotes += 1; 201 | } else { 202 | TaskSolution storage sol = taskSolutionList[_taskId][_solId]; 203 | require(! sol.hasDownvoted[msg.sender]); 204 | sol.downvotes += 1; 205 | } 206 | } 207 | 208 | /* 209 | Accepts a solution and pays the rewards to the solution submitter. 210 | */ 211 | function acceptSolution(uint _taskId, uint _solId) public { 212 | require(_taskId < taskList.length); //Ensure that taskId is valid. 213 | TaskListing storage task = taskList[_taskId]; 214 | require(task.state == TaskState.Open); 215 | require(!task.hasBeenPenalized[taskSolutionList[_taskId][_solId].submitter]); 216 | 217 | require(_solId < taskSolutionList[_taskId].length); //Ensure that solId is valid. 218 | TaskSolution storage sol = taskSolutionList[_taskId][_solId]; 219 | 220 | MemberHandler h = MemberHandler(moduleAddress('MEMBER')); 221 | require(sol.upvotes * 3 >= h.groupMemberCount('team_member') * 2); //At least 2/3 of team members upvoted 222 | 223 | task.state = TaskState.Solved; 224 | task.acceptedSolutionID = _solId; 225 | 226 | //Broadcast acceptance 227 | TaskSolutionAccepted(_taskId, _solId, sol.patchIPFSHash); 228 | 229 | //Pay submitter of solution 230 | Dao dao = Dao(moduleAddress('DAO')); 231 | dao.paySolutionReward(_taskId, _solId); 232 | } 233 | 234 | function penalizeSolutionSubmitter(uint _taskId, uint _solId) public { 235 | require(_solId < taskSolutionList[_taskId].length); //Ensure that solId is valid. 236 | TaskSolution storage sol = taskSolutionList[_taskId][_solId]; 237 | 238 | require(!tHasBeenPenalized(_taskId, sol.submitter)); 239 | setPenalizedStatus(_taskId, sol.submitter, true); 240 | 241 | MemberHandler h = MemberHandler(moduleAddress('MEMBER')); 242 | require(sol.downvotes * 3 >= h.groupMemberCount('team_member') * 2); //At least 2/3 of team members downvoted 243 | 244 | Dao dao = Dao(moduleAddress('DAO')); 245 | dao.penalizeSolutionSubmitter(_taskId, _solId); 246 | } 247 | 248 | function setCap( 249 | string _capType, 250 | uint _newCap 251 | ) 252 | public 253 | onlyMod('DAO') 254 | { 255 | if (keccak256(_capType) == keccak256('wei')) { 256 | rewardWeiCap = _newCap; 257 | } else if (keccak256(_capType) == keccak256('good_rep')) { 258 | rewardRepCap = _newCap; 259 | } else if (keccak256(_capType) == keccak256('bad_rep')) { 260 | penaltyRepCap = _newCap; 261 | } else if (keccak256(_capType) == keccak256('token')) { 262 | rewardTokenCap = _newCap; 263 | } 264 | } 265 | 266 | function setPenalizedStatus(uint _taskId, address _memberAddr, bool _status) public onlyMod('DAO') { 267 | taskList[_taskId].hasBeenPenalized[_memberAddr] = _status; 268 | } 269 | 270 | //Getters 271 | 272 | function tHasSubmitted(uint _taskId, address _addr) public view returns(bool) { 273 | return taskList[_taskId].hasSubmitted[_addr]; 274 | } 275 | 276 | function tHasBeenPenalized(uint _taskId, address _addr) public view returns(bool) { 277 | return taskList[_taskId].hasBeenPenalized[_addr]; 278 | } 279 | 280 | function tMemberSolId(uint _taskId, address _addr) public view returns(uint) { 281 | return taskList[_taskId].memberSolId[_addr]; 282 | } 283 | 284 | function tHasUpvoted( 285 | uint _taskId, 286 | address _addr 287 | ) 288 | public 289 | view 290 | returns(bool) 291 | { 292 | return taskList[_taskId].hasUpvoted[_addr]; 293 | } 294 | 295 | function sHasDownvoted( 296 | uint _taskId, 297 | uint _solId, 298 | address _addr 299 | ) 300 | public 301 | view 302 | returns(bool) 303 | { 304 | return taskSolutionList[_taskId][_solId].hasDownvoted[_addr]; 305 | } 306 | 307 | //Fallback 308 | function() public { 309 | revert(); 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /dev/contracts/token.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.0; 2 | 3 | import './main.sol'; 4 | import 'zeppelin-solidity/contracts/token/MintableToken.sol'; 5 | 6 | contract Token is MintableToken, Module { 7 | string public name; 8 | string public symbol; 9 | //Decimals given in Module's definition 10 | 11 | function Token( 12 | address _mainAddr, 13 | string _name, 14 | string _symbol 15 | ) 16 | public 17 | Module(_mainAddr) 18 | { 19 | name = _name; 20 | symbol = _symbol; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dev/contracts/truffle_migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | contract Migrations { 4 | address public owner; 5 | 6 | // A function with the signature `last_completed_migration()`, returning a uint, is required. 7 | uint public last_completed_migration; 8 | 9 | modifier restricted() { 10 | if (msg.sender == owner) _; 11 | } 12 | 13 | function Migrations() public { 14 | owner = msg.sender; 15 | } 16 | 17 | // A function with the signature `setCompleted(uint)` is required. 18 | function setCompleted(uint completed) public restricted { 19 | last_completed_migration = completed; 20 | } 21 | 22 | function upgrade(address new_address) public restricted { 23 | Migrations upgraded = Migrations(new_address); 24 | upgraded.setCompleted(last_completed_migration); 25 | } 26 | } -------------------------------------------------------------------------------- /dev/contracts/vault.sol: -------------------------------------------------------------------------------- 1 | /* 2 | vault.sol 3 | Created by Zefram Lou (Zebang Liu) as part of the WikiGit project. 4 | 5 | This file implements the DASP's vault, or in other words the manager 6 | of the DAP's funds. It allows for the delayed withdrawl of ethers 7 | and/or ERC20 tokens (the delay prevents malicious withdrawls), and 8 | giving people who donate ethers to the vault custom tokens in return. 9 | The latter function can be used to implement things like ICOs and 10 | honorary tokens (such as the unicorn token), and more. 11 | */ 12 | 13 | pragma solidity ^0.4.18; 14 | 15 | import './token.sol'; 16 | import './main.sol'; 17 | 18 | contract Vault is Module { 19 | /* 20 | Defines how the vault will behave when a donor donates some ether. 21 | For each donation, the vault will grant multiplier * donationInWei / inputCurrencyPriceInWei 22 | tokens hosted at tokenAddress. 23 | The use of oracles is not yet implemented. 24 | */ 25 | struct CoinOffering { 26 | /* 27 | Number of tokens that would be granted to donor for each input currency unit. Decimal. 28 | */ 29 | uint multiplier; 30 | 31 | /* 32 | The pay behavior will only be valid if the current block number is 33 | larger than or equal to startBlockNumber. 34 | */ 35 | uint startBlockNumber; 36 | 37 | /* 38 | The pay behavior will only be valid if the current block number is 39 | less than untilBlockNumber. 40 | */ 41 | uint endBlockNumber; 42 | 43 | /* 44 | Implements a cap for the total amount of raised funds. 45 | */ 46 | uint raisedFundsInWeis; 47 | uint hardCapInWeis; 48 | } 49 | 50 | /* 51 | Defines a pending withdrawl of ether. Each withdrawl of funds is frozen for 52 | a period of time, when members scrutinize the validity of this withdrawl. 53 | If the pending withdrawl doesn't get invalidated by a voting, the payout function 54 | may be called by anyone to actualize the withdrawl. 55 | */ 56 | struct PendingWithdrawl { 57 | /* 58 | The amount of the withdrawl, in weis if it's an Ether withdrawl. 59 | */ 60 | uint amount; 61 | 62 | /* 63 | Address of the receipient. 64 | */ 65 | address to; 66 | 67 | /* 68 | The block number until which the actual withdrawl of funds cannot be made. 69 | */ 70 | uint frozenUntilBlock; 71 | 72 | bool isEther; 73 | 74 | /* 75 | Indicates whether this withdrawl is invalid. 76 | */ 77 | bool isInvalid; 78 | } 79 | 80 | CoinOffering public currentCoinOffering; 81 | PendingWithdrawl[] public pendingWithdrawlList; // List of pending withdrawls. 82 | 83 | uint public withdrawlFreezeTime; //The time for which a withdrawl requested by a DAO voting is frozen, in blocks. 84 | uint public rewardFreezeTime; //The time for which a withdrawl requested by a member rewarding someone who completed a task, in blocks. 85 | 86 | uint public frozenFunds; //The amount of ethers currently frozen, in weis. 87 | uint public frozenTokens; //The amount of tokens currently frozen. 88 | 89 | function Vault( 90 | address _mainAddr, 91 | uint _rewardFreezeTime, 92 | uint _withdrawlFreezeTime 93 | ) 94 | Module(_mainAddr) 95 | public 96 | { 97 | //Initialize withdrawl freeze times 98 | rewardFreezeTime = _rewardFreezeTime; 99 | withdrawlFreezeTime = _withdrawlFreezeTime; 100 | } 101 | 102 | //Withdrawl handlers 103 | 104 | function addPendingWithdrawl( 105 | uint _amount, 106 | address _to, 107 | bool _isReward, 108 | bool _isEther 109 | ) 110 | public 111 | onlyMod('DAO') 112 | { 113 | uint blocksUntilWithdrawl; 114 | if (_isReward) { 115 | blocksUntilWithdrawl = rewardFreezeTime; 116 | } else { 117 | blocksUntilWithdrawl = withdrawlFreezeTime; 118 | } 119 | 120 | //Check if vault has enough funds for the withdrawl 121 | //If so, freeze the fund that will be withdrawn 122 | if (_isEther) { 123 | uint availableFunds = this.balance - frozenFunds; 124 | require(availableFunds >= _amount); //Make sure there's enough free ether in the vault 125 | require(_to.balance + _amount > _to.balance); //Prevent overflow 126 | 127 | frozenFunds += _amount; //Freeze the pending withdrawl's amount. 128 | } else { 129 | Token token = Token(moduleAddress('TOKEN')); 130 | uint availableTokens = token.balanceOf(address(this)) - frozenTokens; 131 | require(availableTokens >= _amount); //Make sure there's enough unfrozen tokens in the vault 132 | require(token.balanceOf(_to) + _amount > token.balanceOf(_to)); //Prevent overflow 133 | 134 | frozenTokens += _amount; //Freezes the pending withdrawl's amount. 135 | } 136 | 137 | pendingWithdrawlList.push(PendingWithdrawl({ 138 | amount: _amount, 139 | to: _to, 140 | frozenUntilBlock: block.number + blocksUntilWithdrawl, 141 | isEther: _isEther, 142 | isInvalid: false 143 | })); 144 | } 145 | 146 | function payoutPendingWithdrawl(uint _id) public { 147 | require(_id < pendingWithdrawlList.length); //Ensure the id is valid. 148 | PendingWithdrawl storage w = pendingWithdrawlList[_id]; 149 | require(!w.isInvalid); //Ensure the withdrawl is valid. 150 | require(block.number >= w.frozenUntilBlock); //Ensure the vetting period has ended. 151 | 152 | w.isInvalid = true; 153 | 154 | if (w.isEther) { 155 | frozenFunds -= w.amount; //Defrost the frozen funds for payout. 156 | w.to.transfer(w.amount); 157 | } else { 158 | frozenTokens -= w.amount; //Defrost the frozen funds for payout. 159 | Token token = Token(moduleAddress('TOKEN')); 160 | token.transfer(w.to, w.amount); 161 | } 162 | } 163 | 164 | function invalidatePendingWithdrawl(uint _id) public onlyMod('DAO') { 165 | require(_id < pendingWithdrawlList.length); 166 | PendingWithdrawl storage w = pendingWithdrawlList[_id]; 167 | w.isInvalid = true; 168 | if (w.isEther) { 169 | frozenFunds -= w.amount; //Defrost the frozen funds. 170 | } else { 171 | frozenTokens -= w.amount; //Defrost the frozen funds. 172 | } 173 | } 174 | 175 | function changeFreezeTime(uint _newTime, bool _isReward) public onlyMod('DAO') { 176 | if (_isReward) { 177 | rewardFreezeTime = _newTime; 178 | } else { 179 | withdrawlFreezeTime = _newTime; 180 | } 181 | } 182 | 183 | //Coin offering manipulators. 184 | 185 | function startCoinOffering( 186 | uint _multiplier, 187 | uint _startBlockNumber, 188 | uint _endBlockNumber, 189 | uint _hardCapInWeis 190 | ) 191 | public 192 | onlyMod('DAO') 193 | { 194 | currentCoinOffering = CoinOffering({ 195 | multiplier: _multiplier, 196 | startBlockNumber: _startBlockNumber, 197 | endBlockNumber: _endBlockNumber, 198 | raisedFundsInWeis: 0, 199 | hardCapInWeis: _hardCapInWeis 200 | }); 201 | } 202 | 203 | //Handles incoming donation. 204 | function() public payable { 205 | if (currentCoinOffering.startBlockNumber < block.number && block.number < currentCoinOffering.endBlockNumber) { 206 | //Ensure cap won't be exceeded 207 | require(currentCoinOffering.raisedFundsInWeis + msg.value <= currentCoinOffering.hardCapInWeis); 208 | //Prevent overflow 209 | require(currentCoinOffering.raisedFundsInWeis + msg.value >= currentCoinOffering.raisedFundsInWeis); 210 | 211 | currentCoinOffering.raisedFundsInWeis += msg.value; 212 | 213 | Token token = Token(moduleAddress('TOKEN')); 214 | token.mint(msg.sender, currentCoinOffering.multiplier * msg.value / decimals); 215 | } 216 | } 217 | 218 | //Getters 219 | 220 | function getPendingWithdrawlListCount() public view returns(uint) { 221 | return pendingWithdrawlList.length; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /dev/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 1_initial_migration.js 3 | * Created by Zefram Lou (Zebang Liu) as part of the WikiGit project. 4 | * 5 | * This file deploys the initial migration contract required by Truffle. 6 | */ 7 | 8 | let truffle_migration = artifacts.require('Migrations'); 9 | 10 | module.exports = (deployer) => { 11 | deployer.deploy(truffle_migration); 12 | }; -------------------------------------------------------------------------------- /dev/migrations/2_core_contracts.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | 2_core_contracts.coffee 3 | Created by Zefram Lou (Zebang Liu) as part of the WikiGit project. 4 | 5 | This file defines the deployment process of the core contracts of 6 | the DASP. In addition, it initializes the DASP's Git repo, 7 | publishes it onto the IPFS network, and saves its hash in the 8 | GitHandler module. 9 | ### 10 | 11 | #Initialize contract abstractions 12 | main = artifacts.require 'Main' 13 | dao = artifacts.require 'Dao' 14 | member_handler = artifacts.require 'MemberHandler' 15 | vault = artifacts.require 'Vault' 16 | tasks_handler = artifacts.require 'TasksHandler' 17 | repo_handler = artifacts.require 'RepoHandler' 18 | token = artifacts.require 'Token' 19 | 20 | #Import node modules 21 | ipfsAPI = require 'ipfs-api' 22 | ipfs = ipfsAPI('ipfs.infura.io', '5001', {protocol: 'https'}) 23 | git = require 'gift' 24 | fs = require 'fs' 25 | 26 | #Import config 27 | config = require './config.json' 28 | 29 | module.exports = (deployer) -> 30 | abiPath = './build/contracts/' 31 | ipfs.util.addFromFs(abiPath, {recursive: true}, (error, abiFiles) -> 32 | if error != null 33 | throw error 34 | 35 | getABIHash = (modName) -> 36 | for f in abiFiles 37 | if f.path == "contracts/#{modName}.json" 38 | return f.hash 39 | 40 | mainHash = getABIHash('Main') 41 | #Deploy main contract 42 | deployer.deploy(main, config.main_metadata, mainHash).then( 43 | () -> 44 | repoPath = './tmp/repo.git' 45 | 46 | #Create repo directory if it doesn't exist 47 | if !fs.existsSync(repoPath) 48 | if !fs.existsSync('./tmp') 49 | fs.mkdirSync('./tmp') 50 | fs.mkdirSync(repoPath) 51 | 52 | newHash = '' 53 | #Initialize Git repo 54 | git.init(repoPath, true, (error, _repo) -> 55 | if error != null 56 | throw error 57 | #Add repo to the IPFS network 58 | ipfs.util.addFromFs(repoPath, {recursive: true}, (error, result) -> 59 | if error != null 60 | throw error 61 | #Get repo's IPFS multihash 62 | newHash = result[result.length - 1].hash 63 | #Deploy core modules 64 | deployer.deploy([ 65 | [dao, main.address], 66 | [member_handler, config.member_init_username, main.address], 67 | [vault, main.address], 68 | [tasks_handler, main.address], 69 | [repo_handler, main.address], 70 | [token, main.address] 71 | ]).then( 72 | () -> 73 | #Add core module addresses to main contract 74 | return main.deployed().then( 75 | (instance) -> 76 | return instance.initializeModuleAddresses([ 77 | dao.address, 78 | member_handler.address, 79 | vault.address, 80 | tasks_handler.address, 81 | repo_handler.address, 82 | token.address 83 | ]) 84 | ) 85 | ).then( 86 | () -> 87 | #Initialize the ABI hashes 88 | modAbsNames = ['Dao', 'MemberHandler', 'Vault', 'TasksHandler', 'RepoHandler', 'Token'] 89 | return main.deployed().then( 90 | (instance) -> 91 | initABIHashForMod = (modId) -> 92 | return instance.initializeABIHashForMod(modId, getABIHash(modAbsNames[modId])) 93 | return Promise.all(initABIHashForMod(modId) for modId in [0..5]) 94 | ) 95 | ) 96 | ) 97 | ) 98 | ) 99 | ) 100 | 101 | -------------------------------------------------------------------------------- /dev/migrations/2_core_contracts.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 2.1.0 2 | (function() { 3 | /* 4 | 2_core_contracts.coffee 5 | Created by Zefram Lou (Zebang Liu) as part of the WikiGit project. 6 | 7 | This file defines the deployment process of the core contracts of 8 | the DASP. In addition, it initializes the DASP's Git repo, 9 | publishes it onto the IPFS network, and saves its hash in the 10 | GitHandler module. 11 | */ 12 | var config, dao, fs, git, ipfs, ipfsAPI, main, member_handler, repo_handler, tasks_handler, token, vault; 13 | 14 | //Initialize contract abstractions 15 | main = artifacts.require('Main'); 16 | 17 | dao = artifacts.require('Dao'); 18 | 19 | member_handler = artifacts.require('MemberHandler'); 20 | 21 | vault = artifacts.require('Vault'); 22 | 23 | tasks_handler = artifacts.require('TasksHandler'); 24 | 25 | repo_handler = artifacts.require('RepoHandler'); 26 | 27 | token = artifacts.require('Token'); 28 | 29 | //Import node modules 30 | ipfsAPI = require('ipfs-api'); 31 | 32 | ipfs = ipfsAPI('ipfs.infura.io', '5001', { 33 | protocol: 'https' 34 | }); 35 | 36 | git = require('gift'); 37 | 38 | fs = require('fs'); 39 | 40 | //Import config 41 | config = require('./config.json'); 42 | 43 | module.exports = function(deployer) { 44 | var abiPath; 45 | abiPath = './build/contracts/'; 46 | return ipfs.util.addFromFs(abiPath, { 47 | recursive: true 48 | }, function(error, abiFiles) { 49 | var getABIHash, mainHash; 50 | if (error !== null) { 51 | throw error; 52 | } 53 | getABIHash = function(modName) { 54 | var f, i, len; 55 | for (i = 0, len = abiFiles.length; i < len; i++) { 56 | f = abiFiles[i]; 57 | if (f.path === `contracts/${modName}.json`) { 58 | return f.hash; 59 | } 60 | } 61 | }; 62 | mainHash = getABIHash('Main'); 63 | //Deploy main contract 64 | return deployer.deploy(main, config.main_metadata, mainHash).then(function() { 65 | var newHash, repoPath; 66 | repoPath = './tmp/repo.git'; 67 | if (!fs.existsSync(repoPath)) { 68 | if (!fs.existsSync('./tmp')) { 69 | fs.mkdirSync('./tmp'); 70 | } 71 | fs.mkdirSync(repoPath); 72 | } 73 | newHash = ''; 74 | //Initialize Git repo 75 | return git.init(repoPath, true, function(error, _repo) { 76 | if (error !== null) { 77 | throw error; 78 | } 79 | //Add repo to the IPFS network 80 | return ipfs.util.addFromFs(repoPath, { 81 | recursive: true 82 | }, function(error, result) { 83 | if (error !== null) { 84 | throw error; 85 | } 86 | //Get repo's IPFS multihash 87 | newHash = result[result.length - 1].hash; 88 | //Deploy core modules 89 | return deployer.deploy([[dao, main.address], [member_handler, config.member_init_username, main.address], [vault, main.address], [tasks_handler, main.address], [repo_handler, main.address], [token, main.address]]).then(function() { 90 | //Add core module addresses to main contract 91 | return main.deployed().then(function(instance) { 92 | return instance.initializeModuleAddresses([dao.address, member_handler.address, vault.address, tasks_handler.address, repo_handler.address, token.address]); 93 | }); 94 | }).then(function() { 95 | var modAbsNames; 96 | //Initialize the ABI hashes 97 | modAbsNames = ['Dao', 'MemberHandler', 'Vault', 'TasksHandler', 'RepoHandler', 'Token']; 98 | return main.deployed().then(function(instance) { 99 | var initABIHashForMod, modId; 100 | initABIHashForMod = function(modId) { 101 | return instance.initializeABIHashForMod(modId, getABIHash(modAbsNames[modId])); 102 | }; 103 | return Promise.all((function() { 104 | var i, results; 105 | results = []; 106 | for (modId = i = 0; i <= 5; modId = ++i) { 107 | results.push(initABIHashForMod(modId)); 108 | } 109 | return results; 110 | })()); 111 | }); 112 | }); 113 | }); 114 | }); 115 | }); 116 | }); 117 | }; 118 | 119 | }).call(this); 120 | 121 | //# sourceMappingURL=2_core_contracts.js.map 122 | -------------------------------------------------------------------------------- /dev/migrations/2_core_contracts.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "file": "2_core_contracts.js", 4 | "sourceRoot": "", 5 | "sources": [ 6 | "2_core_contracts.coffee" 7 | ], 8 | "names": [], 9 | "mappings": ";AAAA;EAAA;;;;;;;;;AAAA,MAAA,MAAA,EAAA,GAAA,EAAA,EAAA,EAAA,GAAA,EAAA,IAAA,EAAA,OAAA,EAAA,IAAA,EAAA,cAAA,EAAA,YAAA,EAAA,aAAA,EAAA,KAAA,EAAA,KAAA;;;EAWA,IAAA,GAAO,SAAS,CAAC,OAAV,CAAkB,MAAlB;;EACP,GAAA,GAAM,SAAS,CAAC,OAAV,CAAkB,KAAlB;;EACN,cAAA,GAAiB,SAAS,CAAC,OAAV,CAAkB,eAAlB;;EACjB,KAAA,GAAQ,SAAS,CAAC,OAAV,CAAkB,OAAlB;;EACR,aAAA,GAAgB,SAAS,CAAC,OAAV,CAAkB,cAAlB;;EAChB,YAAA,GAAe,SAAS,CAAC,OAAV,CAAkB,aAAlB;;EACf,KAAA,GAAQ,SAAS,CAAC,OAAV,CAAkB,OAAlB,EAjBR;;;EAoBA,OAAA,GAAU,OAAA,CAAQ,UAAR;;EACV,IAAA,GAAO,OAAA,CAAQ,gBAAR,EAA0B,MAA1B,EAAkC;IAAC,QAAA,EAAU;EAAX,CAAlC;;EACP,GAAA,GAAM,OAAA,CAAQ,MAAR;;EACN,EAAA,GAAK,OAAA,CAAQ,IAAR,EAvBL;;;EA0BA,MAAA,GAAS,OAAA,CAAQ,eAAR;;EAET,MAAM,CAAC,OAAP,GAAiB,QAAA,CAAC,QAAD,CAAA;AACf,QAAA;IAAA,OAAA,GAAU;WACV,IAAI,CAAC,IAAI,CAAC,SAAV,CAAoB,OAApB,EAA6B;MAAC,SAAA,EAAW;IAAZ,CAA7B,EAAgD,QAAA,CAAC,KAAD,EAAQ,QAAR,CAAA;AAC9C,UAAA,UAAA,EAAA;MAAA,IAAG,KAAA,KAAS,IAAZ;QACE,MAAM,MADR;;MAGA,UAAA,GAAa,QAAA,CAAC,OAAD,CAAA;AACX,YAAA,CAAA,EAAA,CAAA,EAAA;QAAA,KAAA,0CAAA;;UACE,IAAG,CAAC,CAAC,IAAF,KAAU,CAAA,UAAA,CAAA,CAAa,OAAb,CAAqB,KAArB,CAAb;AACE,mBAAO,CAAC,CAAC,KADX;;QADF;MADW;MAKb,QAAA,GAAW,UAAA,CAAW,MAAX,EARX;;aAUA,QAAQ,CAAC,MAAT,CAAgB,IAAhB,EAAsB,MAAM,CAAC,aAA7B,EAA4C,QAA5C,CAAqD,CAAC,IAAtD,CACE,QAAA,CAAA,CAAA;AACE,YAAA,OAAA,EAAA;QAAA,QAAA,GAAW;QAGX,IAAG,CAAC,EAAE,CAAC,UAAH,CAAc,QAAd,CAAJ;UACE,IAAG,CAAC,EAAE,CAAC,UAAH,CAAc,OAAd,CAAJ;YACE,EAAE,CAAC,SAAH,CAAa,OAAb,EADF;;UAEA,EAAE,CAAC,SAAH,CAAa,QAAb,EAHF;;QAKA,OAAA,GAAU,GARV;;eAUA,GAAG,CAAC,IAAJ,CAAS,QAAT,EAAmB,IAAnB,EAAyB,QAAA,CAAC,KAAD,EAAQ,KAAR,CAAA;UACvB,IAAG,KAAA,KAAS,IAAZ;YACE,MAAM,MADR;WAAA;;iBAGA,IAAI,CAAC,IAAI,CAAC,SAAV,CAAoB,QAApB,EAA8B;YAAC,SAAA,EAAW;UAAZ,CAA9B,EAAiD,QAAA,CAAC,KAAD,EAAQ,MAAR,CAAA;YAC/C,IAAG,KAAA,KAAS,IAAZ;cACE,MAAM,MADR;aAAA;;YAGA,OAAA,GAAU,MAAO,CAAA,MAAM,CAAC,MAAP,GAAgB,CAAhB,CAAkB,CAAC,KAHpC;;mBAKA,QAAQ,CAAC,MAAT,CAAgB,CACd,CAAC,GAAD,EAAM,IAAI,CAAC,OAAX,CADc,EAEd,CAAC,cAAD,EAAiB,MAAM,CAAC,oBAAxB,EAA8C,IAAI,CAAC,OAAnD,CAFc,EAGd,CAAC,KAAD,EAAQ,IAAI,CAAC,OAAb,CAHc,EAId,CAAC,aAAD,EAAgB,IAAI,CAAC,OAArB,CAJc,EAKd,CAAC,YAAD,EAAe,IAAI,CAAC,OAApB,CALc,EAMd,CAAC,KAAD,EAAQ,IAAI,CAAC,OAAb,CANc,CAAhB,CAOE,CAAC,IAPH,CAQE,QAAA,CAAA,CAAA,EAAA;;AAEE,qBAAO,IAAI,CAAC,QAAL,CAAA,CAAe,CAAC,IAAhB,CACL,QAAA,CAAC,QAAD,CAAA;AACE,uBAAO,QAAQ,CAAC,yBAAT,CAAmC,CACxC,GAAG,CAAC,OADoC,EAExC,cAAc,CAAC,OAFyB,EAGxC,KAAK,CAAC,OAHkC,EAIxC,aAAa,CAAC,OAJ0B,EAKxC,YAAY,CAAC,OAL2B,EAMxC,KAAK,CAAC,OANkC,CAAnC;cADT,CADK;YAFT,CARF,CAqBC,CAAC,IArBF,CAsBE,QAAA,CAAA,CAAA;AAEE,kBAAA,WAAA;;cAAA,WAAA,GAAc,CAAC,KAAD,EAAQ,eAAR,EAAyB,OAAzB,EAAkC,cAAlC,EAAkD,aAAlD,EAAiE,OAAjE;AACd,qBAAO,IAAI,CAAC,QAAL,CAAA,CAAe,CAAC,IAAhB,CACL,QAAA,CAAC,QAAD,CAAA;AACE,oBAAA,iBAAA,EAAA;gBAAA,iBAAA,GAAoB,QAAA,CAAC,KAAD,CAAA;AAClB,yBAAO,QAAQ,CAAC,uBAAT,CAAiC,KAAjC,EAAwC,UAAA,CAAW,WAAY,CAAA,KAAA,CAAvB,CAAxC;gBADW;AAEpB,uBAAO,OAAO,CAAC,GAAR;;AAAY;kBAAA,KAAsC,kCAAtC;iCAAA,iBAAA,CAAkB,KAAlB;kBAAA,CAAA;;oBAAZ;cAHT,CADK;YAHT,CAtBF;UAN+C,CAAjD;QAJuB,CAAzB;MAXF,CADF;IAX8C,CAAhD;EAFe;AA5BjB", 10 | "sourcesContent": [ 11 | "###\r\n 2_core_contracts.coffee\r\n Created by Zefram Lou (Zebang Liu) as part of the WikiGit project.\r\n\r\n This file defines the deployment process of the core contracts of\r\n the DASP. In addition, it initializes the DASP's Git repo,\r\n publishes it onto the IPFS network, and saves its hash in the\r\n GitHandler module.\r\n###\r\n\r\n#Initialize contract abstractions\r\nmain = artifacts.require 'Main'\r\ndao = artifacts.require 'Dao'\r\nmember_handler = artifacts.require 'MemberHandler'\r\nvault = artifacts.require 'Vault'\r\ntasks_handler = artifacts.require 'TasksHandler'\r\nrepo_handler = artifacts.require 'RepoHandler'\r\ntoken = artifacts.require 'Token'\r\n\r\n#Import node modules\r\nipfsAPI = require 'ipfs-api'\r\nipfs = ipfsAPI('ipfs.infura.io', '5001', {protocol: 'https'})\r\ngit = require 'gift'\r\nfs = require 'fs'\r\n\r\n#Import config\r\nconfig = require './config.json'\r\n\r\nmodule.exports = (deployer) ->\r\n abiPath = './build/contracts/'\r\n ipfs.util.addFromFs(abiPath, {recursive: true}, (error, abiFiles) ->\r\n if error != null\r\n throw error\r\n\r\n getABIHash = (modName) ->\r\n for f in abiFiles\r\n if f.path == \"contracts/#{modName}.json\"\r\n return f.hash\r\n\r\n mainHash = getABIHash('Main')\r\n #Deploy main contract\r\n deployer.deploy(main, config.main_metadata, mainHash).then(\r\n () ->\r\n repoPath = './tmp/repo.git'\r\n\r\n #Create repo directory if it doesn't exist\r\n if !fs.existsSync(repoPath)\r\n if !fs.existsSync('./tmp')\r\n fs.mkdirSync('./tmp')\r\n fs.mkdirSync(repoPath)\r\n\r\n newHash = ''\r\n #Initialize Git repo\r\n git.init(repoPath, true, (error, _repo) ->\r\n if error != null\r\n throw error\r\n #Add repo to the IPFS network\r\n ipfs.util.addFromFs(repoPath, {recursive: true}, (error, result) ->\r\n if error != null\r\n throw error\r\n #Get repo's IPFS multihash\r\n newHash = result[result.length - 1].hash\r\n #Deploy core modules\r\n deployer.deploy([\r\n [dao, main.address],\r\n [member_handler, config.member_init_username, main.address],\r\n [vault, main.address],\r\n [tasks_handler, main.address],\r\n [repo_handler, main.address],\r\n [token, main.address]\r\n ]).then(\r\n () ->\r\n #Add core module addresses to main contract\r\n return main.deployed().then(\r\n (instance) ->\r\n return instance.initializeModuleAddresses([\r\n dao.address,\r\n member_handler.address,\r\n vault.address,\r\n tasks_handler.address,\r\n repo_handler.address,\r\n token.address\r\n ])\r\n )\r\n ).then(\r\n () ->\r\n #Initialize the ABI hashes\r\n modAbsNames = ['Dao', 'MemberHandler', 'Vault', 'TasksHandler', 'RepoHandler', 'Token']\r\n return main.deployed().then(\r\n (instance) ->\r\n initABIHashForMod = (modId) ->\r\n return instance.initializeABIHashForMod(modId, getABIHash(modAbsNames[modId]))\r\n return Promise.all(initABIHashForMod(modId) for modId in [0..5])\r\n )\r\n )\r\n )\r\n )\r\n )\r\n )\r\n\r\n" 12 | ] 13 | } -------------------------------------------------------------------------------- /dev/migrations/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "main_metadata": "Test Metadata", 3 | "member_init_username": "Test Username" 4 | } -------------------------------------------------------------------------------- /dev/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WikiGit_Dev", 3 | "private": true, 4 | "dependencies": { 5 | "babel-runtime": "^6.26.0", 6 | "gift": "^0.10.0", 7 | "ipfs-api": "^15.1.0", 8 | "js-sha3": "^0.6.1", 9 | "zeppelin-solidity": "^1.5.0" 10 | }, 11 | "devDependencies": { 12 | "coffeescript": "^2.0.1", 13 | "truffle": "^4.0.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /dev/test/voting.coffee: -------------------------------------------------------------------------------- 1 | Dao = artifacts.require 'Dao' 2 | MemberHandler = artifacts.require 'MemberHandler' 3 | daoAddr = '0xbcfc50e23f9938a6376083dbb9392271dcca45af' 4 | memAddr = '0x7b657f1a84693d70fea10d99a290c873fdbe5b5d' 5 | contract('Dao', 6 | (accounts) -> 7 | account1 = accounts[0]; 8 | account2 = accounts[1]; 9 | it('create voting', 10 | () -> 11 | return Dao.at(daoAddr).then( 12 | (instance) -> 13 | return instance.createVoting( 14 | 'Test Voting', 15 | 'For testing the creation of votings', 16 | 0, 17 | 100, 18 | [], 19 | "0x0" 20 | ) 21 | ) 22 | ) 23 | it('has right', 24 | () -> 25 | return MemberHandler.at(memAddr).then( 26 | (instance) -> 27 | return instance.memberHasRight(account1, 'vote') 28 | ) 29 | ) 30 | it('vote on voting', 31 | () -> 32 | return Dao.at(daoAddr).then( 33 | (instance) -> 34 | return instance.vote( 35 | 5, 36 | true 37 | ) 38 | ) 39 | ) 40 | return 41 | ) -------------------------------------------------------------------------------- /dev/test/voting.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.12.6 2 | (function() { 3 | var Dao, MemberHandler, daoAddr, memAddr; 4 | 5 | Dao = artifacts.require('Dao'); 6 | 7 | MemberHandler = artifacts.require('MemberHandler'); 8 | 9 | daoAddr = '0xbcfc50e23f9938a6376083dbb9392271dcca45af'; 10 | 11 | memAddr = '0x7b657f1a84693d70fea10d99a290c873fdbe5b5d'; 12 | 13 | contract('Dao', function(accounts) { 14 | var account1, account2; 15 | account1 = accounts[0]; 16 | account2 = accounts[1]; 17 | it('create voting', function() { 18 | return Dao.at(daoAddr).then(function(instance) { 19 | return instance.createVoting('Test Voting', 'For testing the creation of votings', 0, 100, [], "0x0"); 20 | }); 21 | }); 22 | it('has right', function() { 23 | return MemberHandler.at(memAddr).then(function(instance) { 24 | return instance.memberHasRight(account1, 'vote'); 25 | }); 26 | }); 27 | it('vote on voting', function() { 28 | return Dao.at(daoAddr).then(function(instance) { 29 | return instance.vote(5, true); 30 | }); 31 | }); 32 | }); 33 | 34 | }).call(this); 35 | 36 | //# sourceMappingURL=voting.js.map 37 | -------------------------------------------------------------------------------- /dev/test/voting.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "file": "voting.js", 4 | "sourceRoot": "", 5 | "sources": [ 6 | "voting.coffee" 7 | ], 8 | "names": [], 9 | "mappings": ";AAAA;AAAA,MAAA;;EAAA,GAAA,GAAM,SAAS,CAAC,OAAV,CAAkB,KAAlB;;EACN,aAAA,GAAgB,SAAS,CAAC,OAAV,CAAkB,eAAlB;;EAChB,OAAA,GAAU;;EACV,OAAA,GAAU;;EACV,QAAA,CAAS,KAAT,EACE,SAAC,QAAD;AACE,QAAA;IAAA,QAAA,GAAW,QAAS,CAAA,CAAA;IACpB,QAAA,GAAW,QAAS,CAAA,CAAA;IACpB,EAAA,CAAG,eAAH,EACE,SAAA;AACE,aAAO,GAAG,CAAC,EAAJ,CAAO,OAAP,CAAe,CAAC,IAAhB,CACL,SAAC,QAAD;AACE,eAAO,QAAQ,CAAC,YAAT,CACL,aADK,EAEL,qCAFK,EAGL,CAHK,EAIL,GAJK,EAKL,EALK,EAML,KANK;MADT,CADK;IADT,CADF;IAcA,EAAA,CAAG,WAAH,EACE,SAAA;AACE,aAAO,aAAa,CAAC,EAAd,CAAiB,OAAjB,CAAyB,CAAC,IAA1B,CACL,SAAC,QAAD;AACE,eAAO,QAAQ,CAAC,cAAT,CAAwB,QAAxB,EAAkC,MAAlC;MADT,CADK;IADT,CADF;IAOA,EAAA,CAAG,gBAAH,EACE,SAAA;AACE,aAAO,GAAG,CAAC,EAAJ,CAAO,OAAP,CAAe,CAAC,IAAhB,CACL,SAAC,QAAD;AACE,eAAO,QAAQ,CAAC,IAAT,CACL,CADK,EAEL,IAFK;MADT,CADK;IADT,CADF;EAxBF,CADF;AAJA" 10 | } -------------------------------------------------------------------------------- /dev/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | development: { 4 | host: "localhost", 5 | port: 8545, 6 | network_id: "*", // Match any network id 7 | gas: 3500000 8 | } 9 | } 10 | }; 11 | --------------------------------------------------------------------------------