├── .gitignore ├── .openzeppelin ├── dev-1581905823258.json └── project.json ├── COPYING ├── README.md ├── TaskList.sol ├── api ├── .spectral.yml ├── assets │ └── images │ │ ├── .DS_Store │ │ └── studio-overview.png ├── docs │ ├── introduction.md │ ├── markdown │ │ ├── basic-syntax.md │ │ └── stoplight-flavored-markdown.md │ └── ui-overview.md ├── models │ ├── asset.v1.yaml │ ├── assetdetail.v1.yaml │ ├── assetmeta.v1.yaml │ ├── engagement.v1.yaml │ ├── engagementcontract.v1.yaml │ ├── stake.v1.yaml │ ├── stakecontract.v1.yaml │ └── user.v1.yaml └── reference │ └── metis.v1.yaml ├── contracts ├── ABDKMath.sol ├── Cashier.sol ├── ComVault.sol ├── Crowd.sol ├── DAC.sol ├── IDAC.sol ├── IMSC.sol ├── IMToken.sol ├── IMetis.sol ├── IRegistrar.sol ├── MSC.sol ├── MSC2.sol ├── MSCMulti.sol ├── MToken.sol ├── MathHelper.sol ├── Metis.sol ├── MetisNFT.sol ├── MetisToken.sol ├── Migrations.sol ├── MultiSig.sol ├── MultiSigMinter.sol ├── MultiSigWallet.sol ├── MultiSigWalletWithDailyLimit.sol ├── Registrar.sol ├── TaskList.sol ├── TaskList2.sol └── TokenVault.sol ├── docs ├── Metis - Technical Fundamentals Whitepaper V02.pdf ├── Metis Whitepaper v3.3.pdf └── api.md ├── licenses └── ABDK_LICENSE.md ├── migrations ├── 1_initial_migration.js ├── 2_deploy_mis_token.js ├── 3_deploy_multi_sig.js ├── 4_deploy_crowd_contract.js ├── 5_deploy_NFT.js ├── 6_deployCachier.js ├── 7_deployVault.js └── 8_deployComVault.js ├── networks.js ├── package-lock.json ├── package.json ├── scripts ├── getmapslot.js ├── test.js └── vaultnew.js ├── test ├── comvault.js ├── crowd.js ├── mis.js ├── msc.js ├── msc2.js ├── multisig.js ├── nft.js └── vault.js ├── tokenlist └── toptoken.json ├── truffle-config-mvm.js ├── truffle-config-normal.js ├── truffle-config-ovm.js └── truffle-config.js /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules/ 3 | backup/ 4 | scripts/investors 5 | .openzeppelin/.session 6 | accounts.in 7 | addresses.txt 8 | newaccount 9 | .secret 10 | .env 11 | -------------------------------------------------------------------------------- /.openzeppelin/dev-1581905823258.json: -------------------------------------------------------------------------------- 1 | { 2 | "contracts": {}, 3 | "solidityLibs": {}, 4 | "proxies": {}, 5 | "manifestVersion": "2.2" 6 | } 7 | -------------------------------------------------------------------------------- /.openzeppelin/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifestVersion": "2.2", 3 | "contracts": { 4 | "MetisToken": "MetisToken" 5 | }, 6 | "dependencies": {}, 7 | "name": "metis", 8 | "version": "1.0.0", 9 | "compiler": { 10 | "compilerSettings": { 11 | "optimizer": { 12 | "enabled": false, 13 | "runs": "200" 14 | } 15 | }, 16 | "typechain": { 17 | "enabled": false 18 | }, 19 | "manager": "openzeppelin", 20 | "solcVersion": "0.5.16", 21 | "artifactsDir": "build/contracts", 22 | "contractsDir": "contracts" 23 | }, 24 | "telemetryOptIn": false 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Metis(WIP) 2 | 3 | Metis is a Layer2 protocol, devoting to build up a technical and organizational infrastructure to run Web3 economy. 4 | 5 | With Metis, every individual, community, builders or projects can take the benefits of low gas cost, high scalability, rich functionalities, and easy of use UX/UI, to create their decentralized company (DAC) on the blockchain, launch decentralized applications (DApps), and manage the business operations via the DAC structure in a decentralized way. 6 | 7 | To achieve sophisticated collaborative outcomes and successfully deploy a framework that involves many on-chain operations requiring timely responses, a layer 2 solution is needed for Metis to achieve its mission. 8 | 9 | Currently, here are some significant issues with many popular optimistic rollup layer 2 solutions: 10 | 11 | 1.The standard layer 2 design is usually very centralized. Many of these constructs duplicate a centralized structure from a single sequencer, which relies on the verification mechanism to deter malicious players from fraudulent behaviors. This design leads to the second problem. 12 | 2.It usually takes a long time to finalize a transaction. Especially when there is a need to withdraw from the layer 2 construct to layer 1. The time to finalize the transaction can take many days. 13 | 3.This, in turn, will cause a bottleneck on layer 2 again. Because of the way many layer 2 solutions are constructed, the throughput on Layer 2 will eventually be limited by the centralized stack's power. If the goal is to bring DApps to the mainstream, we must allow the layer 2 solutions to scale. 14 | 15 | 16 | From the technical perspective, we hope to address the problem via below R&D and optimization of Optimistic Rollup. 17 | 1.Supporting multiple virtual machines running to avoid the throughput bottleneck. 18 | 2.Supporting the microservice framework for easy extension of toolkit, builders can plug/unplug different microservice tools to build their applications. 19 | 3.Leveraging IPFS to protect sensitive data, lower the cost and increase the efficiency. 20 | 4.L2 rangers to shorten the fraud-proof window required. 21 | 5.We think Web3 is a whole economy, so every DAC should have its own business sustainability. That’s why we support DApps/communities to launch their own tokens, to incentivize the internal economic activities. 22 | 23 | From the organizational perspective, we define DAC as a collective of individuals to achieve goals, so how to build up trust, and how to manage decentralized collaborations in decentralized environments where no prior trust foundation has been built are the key issues to solve. And we introduce Optimistic Governance (OG), aiming to solve the trust issue dwelling among distributed community members. OG leverages staking bonds as the commitment to fulfill the obligations, pulling back the bonds as the penalty for defaults, which will help distrustful collaborators to build up trust, confirm collaborative relationship, and validate the computation results or deliverables. We also define key elements that form an individual in the Web3 world, which include wallet, reputation power(recorded in NFT), attributes(recorded in NFT), etc. 24 | 25 | So with the technical and organizational constructs of Metis, we hope to enable individuals/communities to open and run their business on the blockchain, with Optimistic Governance to build up trust and protect their interest, on-chain tools to implement collaborations, which will open a new space for the general public to adopt blockchain. And fundamentally, to enable Ethereum to become the infrastructure of supporting value creation (collaborations), while not just payment. 26 | -------------------------------------------------------------------------------- /TaskList.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.22 <0.6.0; 2 | 3 | contract TaskList { 4 | 5 | 6 | enum STATUS {NONE, OPEN, EXECUTING, REVIEW, REJECT, DONE} 7 | 8 | struct Task { 9 | string infourl; // wiki link to the task details 10 | uint timestamp; //timestamp of the last status update 11 | STATUS status; 12 | uint expiry; // must finish before the expiry 13 | uint stakereq; // stake requirement 14 | address taskowner; // owner of this task 15 | address delegate; // taker of the task; 16 | string resulturl; 17 | uint prize; 18 | } 19 | 20 | enum ROLE {NONE, TASKOWNER, SERVICE, ADMIN} 21 | 22 | address owner; 23 | mapping (address => Task[]) public tasklist; 24 | mapping (address => bool) taskownerlist; 25 | mapping (address => bool) servicelist; 26 | mapping (address => bool) adminlist; 27 | 28 | address[] public taskaddresses; 29 | 30 | constructor() public { 31 | owner = msg.sender; 32 | } 33 | 34 | function transferOwner (address newOwner) public { 35 | if (msg.sender == owner) { 36 | owner = newOwner; 37 | } 38 | } 39 | 40 | function addTaskOwner (address entity) public { 41 | if (msg.sender == owner || adminlist[msg.sender]) { 42 | taskownerlist[entity] = true; 43 | } 44 | } 45 | 46 | function addService (address entity) public { 47 | if (msg.sender == owner || adminlist[msg.sender]) { 48 | servicelist[entity] = true; 49 | } 50 | } 51 | 52 | function addAdmin (address entity) public { 53 | if (msg.sender == owner) { 54 | adminlist[entity] = true; 55 | } 56 | } 57 | /// add a new task to the list 58 | /// pass address(0) in delegate to open the task for all 59 | function addTask (string memory infourl, uint expiry, uint prize, address delegate) public returns (int) { 60 | if (taskownerlist[msg.sender] == false) { 61 | return -1; 62 | } 63 | Task[] storage tasks = tasklist[msg.sender]; 64 | uint index = 0; 65 | 66 | for (index = 0; index < tasks.length; index++) { 67 | if (tasks[index].status == STATUS.DONE) break; 68 | } 69 | 70 | Task memory task; 71 | 72 | task.infourl = infourl; 73 | task.timestamp = block.timestamp; 74 | task.expiry = expiry; 75 | task.prize = prize; 76 | task.taskowner = msg.sender; 77 | task.delegate = delegate; 78 | task.status = STATUS.OPEN; 79 | if (index == tasks.length) { 80 | if (index == 0) { 81 | // new owner 82 | taskaddresses.push(msg.sender); 83 | } 84 | tasks.push(task); 85 | } else { 86 | tasks[index] = task; 87 | } 88 | return int(index); 89 | } 90 | 91 | /// take the task 92 | function takeTask (address taskowner, uint index) public returns (bool) { 93 | if (servicelist[msg.sender] == false) { 94 | return false; 95 | } 96 | if (index >= tasklist[taskowner].length) return false; 97 | Task storage task = tasklist[taskowner][index]; 98 | if (task.status == STATUS.OPEN && (task.delegate == address(0) || task.delegate == msg.sender)) { 99 | task.timestamp = block.timestamp; 100 | task.delegate = msg.sender; 101 | task.status = STATUS.EXECUTING; 102 | return true; 103 | } 104 | return false; 105 | } 106 | 107 | /// put the task to review 108 | function finshTask (address taskowner, uint index, string memory resulturl) public returns (bool) { 109 | if (servicelist[msg.sender] == false) { 110 | return false; 111 | } 112 | if (index >= tasklist[taskowner].length) return false; 113 | Task storage task = tasklist[taskowner][index]; 114 | if (task.delegate == msg.sender && (task.status == STATUS.EXECUTING || task.status == STATUS.REJECT)) { 115 | task.resulturl = resulturl; 116 | task.status = STATUS.REVIEW; 117 | return true; 118 | } 119 | return false; 120 | } 121 | 122 | /// review verdict 123 | function reviewTask(uint index, bool verdict) public returns (bool) { 124 | if (taskownerlist[msg.sender] == false) return false; 125 | if (index >= tasklist[msg.sender].length) return false; 126 | Task storage task = tasklist[msg.sender][index]; 127 | if (task.status == STATUS.REVIEW) { 128 | if (verdict == true) { 129 | task.status = STATUS.DONE; 130 | } 131 | else { 132 | task.status = STATUS.REJECT; 133 | } 134 | return true; 135 | } 136 | return false; 137 | } 138 | 139 | function getNumTaskLists() public view returns (uint) { 140 | return taskaddresses.length; 141 | } 142 | 143 | function getNumTaskByAddress(address taskowner) public view returns (uint) { 144 | return tasklist[taskowner].length; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /api/.spectral.yml: -------------------------------------------------------------------------------- 1 | extends: spectral:oas 2 | rules: 3 | # change this rule's severity to info 4 | operation-description: info 5 | -------------------------------------------------------------------------------- /api/assets/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MetisProtocol/metis/c26c47b6afee6eb7c98eaa433c6c5509c17ff626/api/assets/images/.DS_Store -------------------------------------------------------------------------------- /api/assets/images/studio-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MetisProtocol/metis/c26c47b6afee6eb7c98eaa433c6c5509c17ff626/api/assets/images/studio-overview.png -------------------------------------------------------------------------------- /api/docs/introduction.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | 3 | Studio is our next generation app for API design, modeling, and technical writing. 4 | 5 | > A primary goal of Studio is to enrich, not replace, your existing workflows. It works offline, with folders and files on your computer, just like your favorite IDEs. 6 | 7 | Read the full [Studio Docs](https://stoplight.io/p/docs/gh/stoplightio/studio). 8 | 9 | Here is some of what it can do: 10 | 11 | - **OpenAPI v2 and v3:** Form based design mode means you don't need to be an OpenAPI expert to get started. Studio also supports OpenAPI autocomplete in write mode, a read mode to visualize HTTP operations and models, mocking, and linting for OpenAPI v2 and v3. 12 | - **Standalone JSON Schema Modeling:** Studio encourages you to split your models into separate files, and then makes it easy to create `$refs` between them. 13 | - **Stoplight Flavored Markdown:** [SMD](./markdown/stoplight-flavored-markdown.md) is an optional, lightweight extension to regular markdown. It enables a few advanced features such as tabs and callouts. 14 | - **Combine Reference and Implementation:** Since Studio works with your local filesystem, you can open up your API projects and start adding docs and designs alongside the actual implementation they are meant to describe. Once you're done, check it all into git with your favorite git client! 15 | - **Manage Mock Servers:** Studio automatically starts local mock servers for every API defined in your project, and keeps those mock servers up to date as you change your designs. 16 | 17 | ### A Note on this Personal Space 18 | 19 | This initial Studio project template was cloned from the [Studio Templates](https://github.com/stoplightio/studio-templates) Github repository. If you find an error, or have an idea on how to improve this getting started template, please let us know in the issues section of that repository. 20 | 21 | We plan to add additional sections on Prism (mocking), Spectral (linting), working with the filesystem, Git, and more in the future. 22 | 23 | This template includes a couple of example APIs (Petstore and To-dos). You can create a new project, or open another folder on your computer, by clicking the `Project Selector` dropdown in the top left of the Studio UI. -------------------------------------------------------------------------------- /api/docs/markdown/basic-syntax.md: -------------------------------------------------------------------------------- 1 | # Markdown Basics 2 | 3 | ### What is Markdown? 4 | 5 | > Markdown is a text-to-HTML conversion tool for web writers. 6 | > 7 | > Markdown allows you to write using an easy-to-read, easy-to-write plain text format, then convert it to structurally valid XHTML (or HTML). 8 | 9 | For example, this entire page was created using Markdown! 10 | 11 | Below is a quick reference of all the Markdown syntax that is supported by Stoplight. 12 | 13 | *All of the notes, descriptions, and content fields in the Stoplight editors support use of [Stoplight Flavored Markdown](./stoplight-flavored-markdown.md). SMD supports more advanced features like themable callouts and embedded json schema models.* 14 | 15 | ### Table of Contents 16 | 17 | - [Headers](#headers) 18 | - [Emphasis](#emphasis) 19 | - [Lists](#lists) 20 | - [Images](#images) 21 | - [Code and Syntax Highlighting](#code-and-syntax-highlighting) 22 | - [Tables](#tables) 23 | - [Blockquotes](#blockquotes) 24 | - [Inline HTML](#inline-html) 25 | - [Horizontal Rule](#horizontal-rule) 26 | - [Line Breaks](#line-breaks) 27 | - [Videos](#videos) 28 | 29 | ## Headers 30 | 31 | ```md 32 | # H1 33 | ## H2 34 | ### H3 35 | #### H4 36 | ##### H5 37 | ###### H6 38 | ``` 39 | 40 | # H1 41 | 42 | ## H2 43 | 44 | ### H3 45 | 46 | #### H4 47 | 48 | ##### H5 49 | 50 | ###### H6 51 | 52 | ## Emphasis 53 | 54 | ```md 55 | Emphasis, aka italics, with *asterisks* or _underscores_. 56 | 57 | Strong emphasis, aka bold, with **asterisks** or __underscores__. 58 | 59 | Combined emphasis with **asterisks and _underscores_**. 60 | 61 | Strikethrough uses two tildes. ~~Scratch this.~~ 62 | ``` 63 | 64 | Emphasis, aka italics, with _asterisks_ or _underscores_. 65 | 66 | Strong emphasis, aka bold, with **asterisks** or **underscores**. 67 | 68 | Combined emphasis with **asterisks and _underscores_**. 69 | 70 | Strikethrough uses two tildes. ~~Scratch this.~~ 71 | 72 | ## Lists 73 | 74 | > In this example, leading and trailing spaces are shown with with dots: ⋅⋅⋅ 75 | 76 | ```md 77 | 1. First ordered list item 78 | 2. Another item 79 | ⋅⋅- Unordered sub-list 80 | 1. Actual numbers don't matter, just that it's a number 81 | ⋅⋅1. Ordered sub-list 82 | 4. And another item 83 | 84 | ⋅⋅⋅You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown). 85 | ``` 86 | 87 | 1. First ordered list item 88 | 2. Another item 89 | - Unordered sub-list 90 | 3. Actual numbers don't matter, just that it's a number 91 | 1. Ordered sub-list 92 | 4. And another item 93 | 94 | You can have properly indented paragraphs within list items. Notice the blank line above, and the leading spaces (at least one, but we'll use three here to also align the raw Markdown). 95 | 96 | ## Links 97 | 98 | ```md 99 | There are two ways to create links: 100 | 101 | [I'm a basic link](https://www.google.com) 102 | 103 | [Hover over me to see my link title](https://www.google.com "Google's Homepage") 104 | 105 | [I'm a relative link to the ./stoplight-flavored-markdown.md file](./stoplight-flavored-markdown.md) 106 | ``` 107 | 108 | There are two ways to create links: 109 | 110 | [I'm a basic link](https://www.google.com) 111 | 112 | [Hover over me to see my link title](https://www.google.com "Google's Homepage") 113 | 114 | [I'm a relative link to the ./stoplight-flavored-markdown.md file](./stoplight-flavored-markdown.md) 115 | 116 | ## Images 117 | 118 | ```md 119 | Here's our logo (hover to see the title text): 120 | 121 | ![Stoplight Logo](https://pbs.twimg.com/profile_images/641056907474538498/qIbg0pZP_bigger.png "Stoplight Logo") 122 | ``` 123 | 124 | Here's our logo (hover to see the title text): 125 | 126 | ![Stoplight Logo](https://pbs.twimg.com/profile_images/641056907474538498/qIbg0pZP_bigger.png "Stoplight Logo") 127 | 128 | ## Code and Syntax Highlighting 129 | 130 | Inline `code` has `back-ticks` around it. 131 | 132 | Blocks of code are either fenced by lines with three back-ticks, or are indented with four spaces. We recommend only using the fenced code blocks -- they're easier to use and support syntax highlighting. 133 | 134 | 135 | 136 | > In the examples below, remove the `\` that precedes the three backticks at the start and end of the javascript code fence before using. 137 | 138 | ``` 139 | \```javascript 140 | var s = "JavaScript syntax highlighting"; 141 | alert(s); 142 | \``` 143 | ``` 144 | 145 | ```javascript 146 | var s = "JavaScript syntax highlighting"; 147 | alert(s); 148 | ``` 149 | 150 | Use language tags to change the syntax highlighting: 151 | 152 | ``` 153 | \```json 154 | { 155 | "JSON": "Syntax Highlighting" 156 | } 157 | \``` 158 | ``` 159 | 160 | ```json 161 | { 162 | "JSON": "Syntax Highlighting" 163 | } 164 | ``` 165 | 166 | ## Tables 167 | 168 | ```md 169 | Colons can be used to align columns. 170 | 171 | | Tables | Are | Cool | 172 | | ------------- | :-----------: | ----: | 173 | | col 3 is | right-aligned | $1600 | 174 | | col 2 is | centered | $12 | 175 | | zebra stripes | are neat | $1 | 176 | 177 | There must be at least 3 dashes separating each header cell. The outer pipes (|) are optional, and you don't need to make the raw Markdown line up prettily. You can also use inline Markdown. 178 | 179 | | Markdown | Less | Pretty | 180 | | -------- | --------- | ---------- | 181 | | _Still_ | `renders` | **nicely** | 182 | | 1 | 2 | 3 | 183 | ``` 184 | 185 | Colons can be used to align columns. 186 | 187 | | Tables | Are | Cool | 188 | | ------------- | :-----------: | ----: | 189 | | col 3 is | right-aligned | $1600 | 190 | | col 2 is | centered | $12 | 191 | | zebra stripes | are neat | $1 | 192 | 193 | There must be at least 3 dashes separating each header cell. The outer pipes (|) are optional, and you don't need to make the raw Markdown line up prettily. You can also use inline Markdown. 194 | 195 | | Markdown | Less | Pretty | 196 | | -------- | --------- | ---------- | 197 | | _Still_ | `renders` | **nicely** | 198 | | 1 | 2 | 3 | 199 | 200 | ## Blockquotes 201 | 202 | ```md 203 | > Blockquotes are very handy in email to emulate reply text. 204 | > This line is part of the same quote. 205 | 206 | Quote break. 207 | 208 | > This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can _put_ **Markdown** into a blockquote. 209 | ``` 210 | 211 | > Blockquotes are very handy in email to emulate reply text. 212 | > This line is part of the same quote. 213 | 214 | Quote break. 215 | 216 | > This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can _put_ **Markdown** into a blockquote. 217 | 218 | ## Horizontal Rule 219 | 220 | Three or more asterisks... 221 | 222 | ```md 223 | Before. 224 | 225 | * * * 226 | 227 | After. 228 | ``` 229 | 230 | Before. 231 | 232 | * * * 233 | 234 | After. 235 | 236 | ### Credits 237 | 238 | Most of this information was pulled from [Adam Pritchard's Mardkown Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet). Thank you Adam for putting together this cheatsheet! -------------------------------------------------------------------------------- /api/docs/markdown/stoplight-flavored-markdown.md: -------------------------------------------------------------------------------- 1 | # Stoplight Flavored Markdown 2 | 3 | Stoplight flavored markdown (SMD for short) was created with a couple of guiding principles in mind: 4 | 5 | 1. SMD is human readable. A human with a simple text editor can easily read and write smd. 6 | 2. SMD degrades gracefully. For example, SMD documents rendered by `github.com` should be clean and readable. No ugly templating syntax, etc. 7 | 8 | **The Approach:** 9 | 10 | 1. Stoplight flavored markdown extends github flavor markdown with inline comment annotations. 11 | 2. The value inside of the annotations is a yaml object, and the annotation affects the markdown block that directly follows it in the document. 12 | 13 | By leveraging comments to store annotations, Stoplight flavored markdown degrades gracefully to any other markdown renderer (Github, for example). 14 | 15 | ## Callouts 16 | 17 | A callout is a md block quote with an optional annotation that indicates intent. 18 | 19 | ```md 20 | 21 | 22 | > ### Danger Will Robinson! 23 | > 24 | > Here is my danger callout! 25 | 26 | 27 | 28 | > ### Watch Out! 29 | > 30 | > Here is my warning callout! 31 | 32 | 33 | 34 | > ### Mission Accomplished! 35 | > 36 | > Here is my success callout! 37 | 38 | 39 | 40 | > ### A thing to know 41 | > 42 | > Here is my info callout 43 | ``` 44 | 45 | 46 | 47 | > ### Danger Will Robinson! 48 | > 49 | > Here is my danger callout! 50 | 51 | 52 | 53 | > ### Watch Out! 54 | > 55 | > Here is my warning callout! 56 | 57 | 58 | 59 | > ### Mission Accomplished! 60 | > 61 | > Here is my success callout! 62 | 63 | 64 | 65 | > ### A thing to know 66 | > 67 | > Here is my info callout 68 | 69 | ## Code Blocks 70 | 71 | A smd code block is md code fence with an optional annotation to tweak the presentation of the code block. 72 | 73 | 74 | 75 | > In the examples below, remove the `\` that precedes the three backticks at the start and end of the javascript code fence before using. 76 | 77 | ````md 78 | 83 | 84 | \```javascript 85 | function fibonacci(num){ 86 | var a = 1, b = 0, temp; 87 | 88 | while (num >= 0){ 89 | temp = a; 90 | a = a + b; 91 | b = temp; 92 | num--; 93 | } 94 | 95 | return b; 96 | } 97 | \``` 98 | ```` 99 | 100 | 105 | 106 | ```javascript 107 | function fibonacci(num){ 108 | var a = 1, b = 0, temp; 109 | 110 | while (num >= 0){ 111 | temp = a; 112 | a = a + b; 113 | b = temp; 114 | num--; 115 | } 116 | 117 | return b; 118 | } 119 | ``` 120 | 121 | ## Tables 122 | 123 | Use a type annotation to add a title to a table. 124 | 125 | ```md 126 | 127 | 128 | | Tables | Are | Cool | 129 | | ------------- | :-----------: | ----: | 130 | | col 3 is | right-aligned | $1600 | 131 | | col 2 is | centered | $12 | 132 | | zebra stripes | are neat | $1 | 133 | ``` 134 | 135 | 136 | 137 | | Tables | Are | Cool | 138 | | ------------- | :-----------: | ----: | 139 | | col 3 is | right-aligned | $1600 | 140 | | col 2 is | centered | $12 | 141 | | zebra stripes | are neat | $1 | 142 | 143 | ## JSON Schema 144 | 145 | A JSON schema block is a `json` code block with an additional `json_schema` language tag. The contents of the code fence should be the JSON schema object to be rendered. The primary language tag can be `yaml`, `yml`, or `json`. 146 | 147 | 148 | 149 | > In the examples below, remove the `\` that precedes the three backticks at the start and end of the json code fence before using. 150 | 151 | ````md 152 | \```json json_schema 153 | { 154 | "title": "User", 155 | "type": "object", 156 | "properties": { 157 | "id": { 158 | "type": "string" 159 | }, 160 | "name": { 161 | "type": "string", 162 | "description": "The user's full name." 163 | }, 164 | "age": { 165 | "type": "number", 166 | "minimum": 0, 167 | "maximum": 150 168 | } 169 | }, 170 | "required": [ 171 | "id", 172 | "name" 173 | ] 174 | } 175 | \``` 176 | ```` 177 | 178 | ```json json_schema 179 | { 180 | "title": "User", 181 | "type": "object", 182 | "properties": { 183 | "id": { 184 | "type": "string" 185 | }, 186 | "name": { 187 | "type": "string", 188 | "description": "The user's full name." 189 | }, 190 | "age": { 191 | "type": "number", 192 | "minimum": 0, 193 | "maximum": 150 194 | } 195 | }, 196 | "required": [ 197 | "id", 198 | "name" 199 | ] 200 | } 201 | ``` 202 | 203 | ## Tabs 204 | 205 | A tab container is a `tab` annotation, followed by the tab content, and closed by a final `tab-end` annotation. 206 | 207 | 208 | 209 | > Tab containers cannot be nested. 210 | 211 | ````md 212 | 216 | 217 | \```json json_schema 218 | { 219 | "title": "User", 220 | "type": "object", 221 | "properties": { 222 | "id": { 223 | "type": "string" 224 | }, 225 | "name": { 226 | "type": "string", 227 | "description": "The user's full name." 228 | }, 229 | "age": { 230 | "type": "number", 231 | "minimum": 0, 232 | "maximum": 150 233 | } 234 | }, 235 | "required": [ 236 | "id", 237 | "name" 238 | ] 239 | } 240 | \``` 241 | 242 | 246 | 247 | \```json 248 | { 249 | "id": "xxx", 250 | "name": "Chris", 251 | "age": 27 252 | } 253 | \``` 254 | 255 | 256 | ```` 257 | 258 | 262 | 263 | ```json json_schema 264 | { 265 | "title": "User", 266 | "type": "object", 267 | "properties": { 268 | "id": { 269 | "type": "string" 270 | }, 271 | "name": { 272 | "type": "string", 273 | "description": "The user's full name." 274 | }, 275 | "age": { 276 | "type": "number", 277 | "minimum": 0, 278 | "maximum": 150 279 | } 280 | }, 281 | "required": [ 282 | "id", 283 | "name" 284 | ] 285 | } 286 | ``` 287 | 288 | 292 | 293 | ```json 294 | { 295 | "id": "xxx", 296 | "name": "Chris", 297 | "age": 27 298 | } 299 | ``` 300 | 301 | 302 | 303 | ## HTTP Request Maker 304 | 305 | The HTTP Request block allows you to embed example requests directly in your articles. 306 | 307 | The HTTP Request block is a `json` or `yaml` code block with an additional `http` language tag. The contents of the code fence should be a HTTP request object (format described below). The Stoplight Studio markdown preview panel includes an embedded editor to help you put together 308 | 309 | 310 | 311 | > In the examples below, remove the `\` that precedes the three backticks at the start and end of the yaml code fence before using. 312 | 313 | ``` 314 | \```yaml http 315 | { 316 | "method": "get", 317 | "url": "http://todos.stoplight.io/todos" 318 | } 319 | \``` 320 | ``` 321 | 322 | **Renders an embedded request maker!** 323 | 324 | ```yaml http 325 | { 326 | "method": "get", 327 | "url": "http://todos.stoplight.io/todos" 328 | } 329 | ``` 330 | 331 | **HTTP request object format:** 332 | 333 | ```json json_schema 334 | { 335 | "title": "HTTP Request Object", 336 | "description": "This object describes the example request that you would like to embed.", 337 | "type": "object", 338 | "properties": { 339 | "method": { 340 | "type": "string", 341 | "enum": ["get", "post", "put", "patch", "delete", "options", "head"] 342 | }, 343 | "url": { 344 | "type": "string" 345 | }, 346 | "query": { 347 | "type": "object" 348 | }, 349 | "headers": { 350 | "type": "object" 351 | }, 352 | "body": { 353 | "type": ["object", "string"] 354 | } 355 | }, 356 | "required": [ 357 | "method", 358 | "url" 359 | ] 360 | } 361 | ``` 362 | 363 | * * * 364 | 365 | _FIN._ -------------------------------------------------------------------------------- /api/docs/ui-overview.md: -------------------------------------------------------------------------------- 1 | # UI Overview 2 | 3 | ![](../assets/images/studio-overview.png) 4 | 5 | 1. **Project Actions:** Contains actions to help you add new assets to your project, such as APIs, models, articles, etc. 6 | 2. **Project Trees:** The project sidebar tree tabs provide different views into your project. Switch between a view of API related assets, docs, and all of the files/folders in your project. 7 | 3. **Primary Panel:** You can switch which mode is shown on the primary panel by clicking the mode selector in the top right. You can show the form editor, docs view, or code editor here. You can also click the expand button to go full screen with the panel. 8 | 4. **Secondary Panel:** The secondary panel allows you to view two different modes side by side. In this screenshot, we've configured Studio to show the form editor in the primary panel, and the code editor in the secondary panel. 9 | 5. **Settings & Login:** Most of Studio can be used offline & while logged out. A few features, such as git, require you to login. 10 | 6. **Git:** If the project you are working with is connected to a Git repository, you can quickly switch branches by clicking on these buttons. 11 | 7. **Crumbs:** These crumbs give you an at a glance look at where you are in your project. 12 | 8. **Spectral & Prism:** Click these buttons to open the validations and mocking panels. -------------------------------------------------------------------------------- /api/models/asset.v1.yaml: -------------------------------------------------------------------------------- 1 | title: asset 2 | type: object 3 | properties: 4 | id: 5 | type: string 6 | meta: 7 | $ref: ./assetmeta.v1.yaml 8 | detail: 9 | $ref: ./assetdetail.v1.yaml 10 | -------------------------------------------------------------------------------- /api/models/assetdetail.v1.yaml: -------------------------------------------------------------------------------- 1 | title: assetdetail 2 | type: object 3 | properties: 4 | contract: 5 | type: string 6 | description: the address of the asset smart contract 7 | -------------------------------------------------------------------------------- /api/models/assetmeta.v1.yaml: -------------------------------------------------------------------------------- 1 | title: assetmeta 2 | type: object 3 | properties: 4 | owner: 5 | type: string 6 | name: 7 | type: string 8 | version: 9 | type: string 10 | value: 11 | type: string 12 | status: 13 | type: string 14 | enum: 15 | - public 16 | - private 17 | - deprecated 18 | lastmodified: 19 | type: string 20 | format: date-time 21 | metadata: 22 | type: string 23 | type: 24 | type: string 25 | numref: 26 | type: integer 27 | -------------------------------------------------------------------------------- /api/models/engagement.v1.yaml: -------------------------------------------------------------------------------- 1 | title: engagement 2 | type: object 3 | description: describe an engagement 4 | properties: 5 | id: 6 | type: string 7 | contract: 8 | $ref: ./engagementcontract.v1.yaml 9 | client: 10 | type: string 11 | provider: 12 | type: string 13 | pubkey: 14 | type: string 15 | stake: 16 | type: string 17 | payment: 18 | type: string 19 | -------------------------------------------------------------------------------- /api/models/engagementcontract.v1.yaml: -------------------------------------------------------------------------------- 1 | title: engagementcontract 2 | type: object 3 | properties: 4 | address: 5 | type: string 6 | timestamp: 7 | type: string 8 | index: 9 | type: number 10 | -------------------------------------------------------------------------------- /api/models/stake.v1.yaml: -------------------------------------------------------------------------------- 1 | title: stake 2 | type: object 3 | x-tags: 4 | - Common 5 | x-examples: {} 6 | description: describe a stake contract info 7 | properties: 8 | id: 9 | type: string 10 | contract: 11 | $ref: ./stakecontract.v1.yaml 12 | stakeamount: 13 | type: string 14 | parties: 15 | type: array 16 | items: 17 | type: string 18 | required: 19 | - stakeamount 20 | -------------------------------------------------------------------------------- /api/models/stakecontract.v1.yaml: -------------------------------------------------------------------------------- 1 | title: stakecontract 2 | type: object 3 | properties: 4 | address: 5 | type: string 6 | timestamp: 7 | type: string 8 | -------------------------------------------------------------------------------- /api/models/user.v1.yaml: -------------------------------------------------------------------------------- 1 | title: user 2 | type: object 3 | properties: 4 | id: 5 | type: string 6 | name: 7 | type: string 8 | email: 9 | type: string 10 | metainfo: 11 | type: string 12 | balance: 13 | type: string 14 | level: 15 | type: number 16 | skillset: 17 | type: array 18 | items: 19 | type: string 20 | language: 21 | type: array 22 | items: 23 | type: string 24 | -------------------------------------------------------------------------------- /contracts/Cashier.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 4 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | 7 | contract Cashier is Ownable { 8 | 9 | IERC20 token_; 10 | using SafeMath for uint256; 11 | enum PAYTYPE {undefined, ETHER, TOKEN} 12 | uint256 totalToken_; 13 | uint256 totalEther_; 14 | 15 | struct PAYMENT { 16 | PAYTYPE ptype; 17 | uint256 amount; 18 | } 19 | mapping(string=>PAYMENT) public payments_; 20 | 21 | constructor( 22 | address token 23 | ) 24 | public 25 | { 26 | token_ = IERC20(token); 27 | } 28 | 29 | function payToken(string calldata invoiceid, uint256 amount) external { 30 | require(payments_[invoiceid].ptype != PAYTYPE.ETHER, "payment already started with ether"); 31 | require(token_.transferFrom(msg.sender, address(this), amount), "token transfer failed"); 32 | payments_[invoiceid].ptype = PAYTYPE.TOKEN; 33 | payments_[invoiceid].amount += amount; 34 | totalToken_ += amount; 35 | } 36 | 37 | function payEther(string calldata invoiceid) external payable{ 38 | require(payments_[invoiceid].ptype != PAYTYPE.TOKEN, "payment already started with token"); 39 | totalEther_ += msg.value; 40 | } 41 | 42 | function withdraw(address payable target) external onlyOwner { 43 | uint amounta = totalToken_; 44 | uint amountb = totalEther_; 45 | totalToken_ = 0; 46 | totalEther_ = 0; 47 | require(token_.transfer(target, amounta), "token transfer failed"); 48 | (bool success, ) = target.call.value(amountb)(''); 49 | require(success, "ether tranfer failed"); 50 | } 51 | 52 | function withdrawTokenAmount(address target, uint256 amount) external onlyOwner { 53 | require(token_.transfer(target, amount), "token transfer failed"); 54 | } 55 | 56 | function withdrawEtherAmount(address payable target, uint256 amount) external onlyOwner { 57 | (bool success, ) = target.call.value(amount)(''); 58 | require(success, "ether tranfer failed"); 59 | } 60 | 61 | function checkPayment(string calldata invoiceid) external view returns (PAYTYPE ptype, uint256 amount){ 62 | return (payments_[invoiceid].ptype, payments_[invoiceid].amount); 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /contracts/ComVault.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 4 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import "./MathHelper.sol"; 7 | 8 | contract ComVault is Ownable { 9 | IERC20 _metisToken; 10 | using SafeMath for uint256; 11 | enum STATUS {undefined, ARRANGED, FUNDED} 12 | event NEW(address target, uint256 amount, uint256 metisAmount); 13 | event CLAIM(address operator, uint256 amount); 14 | event TGE(uint256 timestamp, uint256 tge); 15 | uint256 _tge; 16 | uint256 _interval; 17 | address _tokenaddr; 18 | 19 | struct ARRANGEMENT{ 20 | uint256 amount; 21 | uint256 targetAmount; 22 | uint256 metisAmount; 23 | uint256 metisPaid; 24 | uint claimIndex; 25 | STATUS aStatus; 26 | } 27 | mapping(address=>ARRANGEMENT) public arrangements_; 28 | 29 | constructor( 30 | address metisToken 31 | ) 32 | public 33 | { 34 | _metisToken = IERC20(metisToken); 35 | _interval = 30 days; 36 | _tokenaddr = metisToken; 37 | } 38 | 39 | function setTge(uint256 tge) external onlyOwner { 40 | //require(_tge == 0, 'TGE is already set'); 41 | emit TGE(_tge, tge); 42 | _tge = tge; 43 | } 44 | function _add(address target, uint256 targetamount, uint256 metisAmount) internal { 45 | ARRANGEMENT storage a = arrangements_[target]; 46 | //require (a.aStatus == STATUS.undefined, "target already arranged"); 47 | a.targetAmount = targetamount; 48 | a.aStatus = STATUS.FUNDED; 49 | a.amount = targetamount; 50 | a.metisAmount = metisAmount; 51 | emit NEW(target, targetamount, metisAmount); 52 | } 53 | 54 | function addNew(address target, uint256 targetamount, uint256 metisAmount) external onlyOwner { 55 | _add(target, targetamount, metisAmount); 56 | } 57 | 58 | function addNewBatch(address[] calldata targets, uint256[] calldata amounts, uint256[] calldata metisAmounts) external onlyOwner { 59 | require(targets.length == amounts.length, "amount length mistmatch"); 60 | 61 | for (uint i = 0; i < targets.length; ++i) { 62 | _add(targets[i], amounts[i], metisAmounts[i]); 63 | } 64 | } 65 | 66 | function claim() external { 67 | require(_tge > 0, "TGE not set"); 68 | require(now >= _tge, "TGE has not arrived yet"); 69 | 70 | ARRANGEMENT storage a = arrangements_[msg.sender]; 71 | 72 | require(a.aStatus == STATUS.FUNDED, "not funded"); 73 | require(a.metisPaid < a.metisAmount, "all paid"); 74 | 75 | uint256 totalAmount = 0; 76 | uint256 curIndex = (now - _tge) / _interval ; 77 | 78 | if (a.claimIndex == 0 && a.metisPaid == 0) { 79 | //first time, after TGE unlock 10% 80 | totalAmount = totalAmount.add(MathHelper.mulDiv(a.metisAmount, 1, 10)); 81 | } 82 | 83 | if (curIndex > 12) { 84 | totalAmount = a.metisAmount.sub(a.metisPaid); 85 | } else if (curIndex > a.claimIndex ) { 86 | totalAmount = totalAmount.add(MathHelper.mulDiv(MathHelper.mulDiv(a.metisAmount, 9, 10), curIndex - a.claimIndex , 12)); 87 | } 88 | a.metisPaid = a.metisPaid.add(totalAmount); 89 | a.claimIndex = curIndex; 90 | 91 | //require(totalAmount > 0, "Nothing to claim"); 92 | require(_metisToken.transfer(msg.sender, totalAmount), "TRANSFER_FAILED"); 93 | 94 | emit CLAIM(msg.sender, totalAmount); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /contracts/Crowd.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 4 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | 7 | contract Crowd is Ownable { 8 | 9 | IERC20 token_; 10 | using SafeMath for uint256; 11 | 12 | constructor( 13 | address token 14 | ) 15 | public 16 | { 17 | token_ = IERC20(token); 18 | } 19 | 20 | function distribute(address[] calldata targets, uint[] calldata amounts) external onlyOwner { 21 | require(targets.length == amounts.length, "not matching"); 22 | for (uint256 i = 0; i < targets.length; ++i) { 23 | require(token_.transfer(targets[i], amounts[i]), "failed"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/DAC.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/access/Roles.sol"; 4 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import "./MathHelper.sol"; 7 | import "./IDAC.sol"; 8 | import "./IMetis.sol"; 9 | import "./TaskList.sol"; 10 | 11 | /** 12 | * @dev implementation of MSC 13 | */ 14 | contract DAC is IDAC, Ownable{ 15 | using SafeMath for uint256; 16 | 17 | IMetis private _metis; 18 | TaskList public _taskList; 19 | 20 | using Roles for Roles.Role; 21 | enum DACStatus { Pending, Effective, Closed } 22 | enum MemberStatus { Active, Blocked } 23 | 24 | event Transaction (address operator, address from, address to, uint256 amount, bytes msg1, bytes msg2); 25 | 26 | Roles.Role private _adminRole; 27 | address _creator; 28 | address _metisAddr; 29 | string _name; 30 | string _symbol; 31 | address _business; 32 | 33 | struct Profile { 34 | uint256 reputation; 35 | uint256 locked; 36 | uint256 unlocked; 37 | uint256 dividend; 38 | uint256 lastLockRatio; 39 | uint256 lastUpdateTS; 40 | uint256 numComplaintsReceived; 41 | uint256 numFinishedTasks; 42 | } 43 | 44 | mapping(address => Profile) public _members; 45 | address[] public _memberArray; 46 | uint256 public _dividendPool; 47 | uint256 public _totalRep = 0; 48 | 49 | /** 50 | * @dev participants cannot be empty 51 | */ 52 | constructor( 53 | address creator, 54 | address metisAddr, 55 | string memory name, 56 | string memory symbol, 57 | uint256 stake, 58 | address business 59 | ) Ownable() public { 60 | _metisAddr = metisAddr; 61 | _name = name; 62 | _creator = creator; 63 | _symbol = symbol; 64 | _business = business; 65 | _metis = IMetis(metisAddr); 66 | Profile storage p = _members[creator]; 67 | p.lastUpdateTS = now; 68 | p.unlocked = stake; 69 | _adminRole.add(creator); 70 | _memberArray.push(creator); 71 | 72 | _taskList = new TaskList(); 73 | } 74 | 75 | /** 76 | * @dev commit funds to the dac. 77 | */ 78 | function() external payable { 79 | } 80 | 81 | /** 82 | * @dev commit funds to the dac. 83 | & @param sender address of sender 84 | */ 85 | function stake(address sender) public payable{ 86 | Profile storage p = _members[sender]; 87 | 88 | uint256 newNumTokens = _metis.stake.value(msg.value)(sender); 89 | 90 | if (p.lastUpdateTS == 0) { 91 | // new memeber 92 | p.lastLockRatio = 100; //always assume 100% locked 93 | p.locked = newNumTokens; 94 | } else { 95 | uint256 locked = MathHelper.mulDiv(newNumTokens, p.lastLockRatio,100); 96 | p.locked = p.locked.add(locked); 97 | p.unlocked = p.unlocked.add(newNumTokens.sub(locked)); 98 | } 99 | updateBalance(sender); 100 | emit Transaction(sender, sender, address(this), msg.value, "Stake", ""); 101 | } 102 | 103 | /** 104 | * @dev return the current balance of the sender 105 | */ 106 | function getBalance() public view returns (uint256 locked, uint256 unlocked) { 107 | Profile memory p = _members[msg.sender]; 108 | locked = p.locked; 109 | unlocked = p.unlocked; 110 | } 111 | 112 | function updateReputation(address member) public { 113 | Profile storage p = _members[member]; 114 | _totalRep = _totalRep.sub(p.reputation); 115 | p.reputation = p.locked.div(10^12).add( 116 | MathHelper.mulDiv(p.numFinishedTasks.sub(p.numComplaintsReceived),10, p.numFinishedTasks.add(1))); 117 | _totalRep = _totalRep.add(p.reputation); 118 | } 119 | 120 | /** 121 | * @dev return the current balance of the sender 122 | */ 123 | function updateBalance() public { 124 | updateBalance(msg.sender); 125 | } 126 | 127 | /** 128 | * @dev return the current balance of the sender 129 | */ 130 | function updateBalance(address sender) public { 131 | Profile memory p = _members[sender]; 132 | uint lockRatio = _metis.lockRatioOf(address(this)); 133 | if (lockRatio != p.lastLockRatio) { 134 | uint256 newLocked = MathHelper.mulDiv(MathHelper.mulDiv(p.locked, 100, p.lastLockRatio), lockRatio, 100); 135 | uint256 diff = p.locked.sub(newLocked); 136 | p.locked = newLocked; 137 | p.unlocked = p.unlocked.add(diff); 138 | p.lastLockRatio = lockRatio; 139 | updateReputation(sender); 140 | } 141 | p.lastUpdateTS = now; 142 | } 143 | 144 | /** 145 | * @dev withdraw the unlocked token 146 | * The sender must have enough fund pledged. Contract will be closed and selfdestruct if all funds are withdrawn 147 | * The facilitator will be paid with the gas free up after selfdestruct 148 | */ 149 | function withdraw(uint256 amount) public { 150 | Profile storage p = _members[msg.sender]; 151 | require(p.reputation > 0, "NOT_A_MEMBER"); 152 | 153 | uint256 locked; 154 | uint256 unlocked; 155 | 156 | updateBalance(); 157 | (locked, unlocked) = getBalance(); 158 | require(unlocked >= amount, "INSUFFICIENT_BALANCE"); 159 | 160 | p.unlocked = p.unlocked.sub(amount); 161 | IERC20(_metis.getTokenAddr()).transfer(msg.sender, amount); 162 | emit Transaction(msg.sender, address(this), msg.sender, amount, "Withdraw", ""); 163 | } 164 | 165 | /** 166 | * @dev tax and dividents from transactions 167 | * @param transType 0 if staking or 1 if paying 168 | */ 169 | function newTransaction(address taskOwner, address taskTaker, uint transType) public payable returns(uint256 newValue){ 170 | uint256 newNumTokens; 171 | 172 | (newNumTokens, newValue) = _metis.newTransaction.value(msg.value)(msg.sender); 173 | 174 | msg.sender.transfer(newValue); 175 | 176 | if (transType == 0) { 177 | Profile storage p = _members[taskOwner]; 178 | p.unlocked = p.unlocked.add(newNumTokens); 179 | } else if (transType == 1) { 180 | Profile storage ownerP = _members[taskOwner]; 181 | Profile storage workerP =_members[taskTaker]; 182 | require(ownerP.lastUpdateTS > 0, "Invalid Owner"); 183 | require(workerP.lastUpdateTS > 0, "Invalid Worker"); 184 | uint256 ownerAdd = MathHelper.mulDiv(newNumTokens, 25, 100); 185 | uint256 workerAdd = MathHelper.mulDiv(newNumTokens, 55, 100); 186 | ownerP.unlocked = ownerP.unlocked.add(ownerAdd); 187 | workerP.unlocked = workerP.unlocked.add(workerAdd); 188 | _dividendPool = _dividendPool.add(newNumTokens.sub(ownerAdd).sub(workerAdd)); 189 | updateBalance(taskOwner); 190 | updateBalance(taskTaker); 191 | } 192 | } 193 | 194 | /** 195 | * @dev distribute dividends 196 | */ 197 | function payDividend() public { 198 | require(_dividendPool > 0, "Nothing dividends pay"); 199 | uint256 dividendPool = _dividendPool; 200 | for (uint i = 0; i < _memberArray.length; ++i) 201 | { 202 | Profile storage p = _members[_memberArray[i]]; 203 | uint256 dividend = MathHelper.mulDiv(p.reputation, dividendPool, _totalRep); 204 | p.unlocked = p.unlocked.add(dividend); 205 | p.dividend = p.dividend.add(dividend); 206 | _dividendPool = _dividendPool.sub(dividend); 207 | } 208 | } 209 | 210 | /** 211 | * @dev update all balances 212 | */ 213 | function updateAllBalances() public { 214 | for (uint i = 0; i < _memberArray.length; ++i) 215 | { 216 | updateBalance(_memberArray[i]); 217 | } 218 | } 219 | 220 | function createTask(string memory infourl, uint256 expiry, uint256 prize, uint256 stakereq) public { 221 | Profile memory p = _members[msg.sender]; 222 | require(p.lastUpdateTS > 0, "Invalid member"); 223 | _taskList.addTask(msg.sender, infourl, expiry, prize, stakereq); 224 | } 225 | 226 | function takeTask(address taskOwner, uint index) public { 227 | Profile memory p = _members[msg.sender]; 228 | require(p.lastUpdateTS > 0, "Invalid member"); 229 | _taskList.takeTask(taskOwner, index, msg.sender); 230 | } 231 | 232 | function getTaxRate() public view returns (uint256) { 233 | return _metis.getTaxRate(); 234 | } 235 | 236 | function getCreator() public view returns (address) { 237 | return _creator; 238 | } 239 | 240 | function setMetis(address metis) public onlyOwner { 241 | _metis = IMetis(metis); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /contracts/IDAC.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/access/Roles.sol"; 4 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import "./MathHelper.sol"; 7 | import "./IDAC.sol"; 8 | import "./IMetis.sol"; 9 | import "./TaskList.sol"; 10 | 11 | /** 12 | * @dev implementation of MSC 13 | */ 14 | interface IDAC { 15 | using SafeMath for uint256; 16 | 17 | /** 18 | * @dev commit funds to the dac. 19 | & @param sender address of sender 20 | */ 21 | function stake(address sender) payable external; 22 | 23 | /** 24 | * @dev return the current balance of the sender 25 | */ 26 | function getBalance() external view returns (uint256 locked, uint256 unlocked); 27 | 28 | function updateReputation(address member) external; 29 | 30 | /** 31 | * @dev return the current balance of the sender 32 | */ 33 | function updateBalance() external; 34 | 35 | /** 36 | * @dev withdraw the unlocked token 37 | * The sender must have enough fund pledged. Contract will be closed and selfdestruct if all funds are withdrawn 38 | * The facilitator will be paid with the gas free up after selfdestruct 39 | */ 40 | function withdraw(uint256 amount) external; 41 | 42 | /** 43 | * @dev tax and dividents from transactions 44 | * @param taskOwner sender address 45 | * @param taskTaker address 46 | * @param transType 0 if staking or 1 if paying 47 | */ 48 | function newTransaction(address taskOwner, address taskTaker, uint transType) external payable returns (uint256); 49 | 50 | /** 51 | * @dev distribute dividends 52 | */ 53 | function payDividend() external; 54 | 55 | /** 56 | * @dev update all balances 57 | */ 58 | function updateAllBalances() external; 59 | 60 | function createTask(string calldata infourl, uint256 expiry, uint256 prize, uint256 stakereq) external; 61 | 62 | function takeTask(address taskOwner, uint index) external; 63 | 64 | function getTaxRate() external view returns (uint256); 65 | function getCreator() external view returns (address); 66 | } 67 | -------------------------------------------------------------------------------- /contracts/IMSC.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC777/ERC777.sol"; 4 | import "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; 5 | import "@openzeppelin/contracts/token/ERC777/IERC777Sender.sol"; 6 | import "@openzeppelin/contracts/access/Roles.sol"; 7 | 8 | /** 9 | * @dev interface of MSC 10 | */ 11 | interface IMSC { 12 | 13 | event Transaction (address operator, address from, address to, uint256 amount, bytes msg1, bytes msg2); 14 | event ContractClose(address initiator, uint lastStatusChange, uint numWantedout, bytes msg); 15 | event ContractDispute(address operator, uint lastStatusChange, bytes msg); 16 | event ResolutionRequested(address initiator, uint lastStatusChange); 17 | event DisputeResolution(address facilitator, uint lastStatusChange, address[] participants, uint[] values); 18 | 19 | /** 20 | * @dev commit funds to the contract. participants can keep committing after the pledge ammount is reached 21 | * @param amount amount of fund to commit 22 | * The sender must authorized this contract to be the operator of senders account before committing 23 | */ 24 | function commit(uint256 amount) external; 25 | 26 | /** 27 | * @dev transfer pledge value from one participant to another 28 | * @param to target address 29 | * @param amount amount of fund to transfer 30 | * The sender must have enough fund pledged. The method does not trigger status changes. 31 | * The transaction is not allowed if the contract is in the middle of a dispute. 32 | */ 33 | function send(address to, uint256 amount) external; 34 | 35 | /** 36 | * @dev withdraw the pledged fund 37 | * The sender must have enough fund pledged. Contract will be closed if all funds are withdrawn 38 | */ 39 | function withdraw() external; 40 | 41 | /** 42 | * @dev signal a participant wants to exit 43 | * once an exit is signaled, the contract will wait pledgePeriod * 1 days for other participants to react 44 | * if other participants also signal exits, the contract will change the status to complete and allow withdraws 45 | * the other participants can choose to dispute, which blocks the withdraws until the dispute is resolved 46 | * if no dispute is resolved after the pledge period expires, the contract will automatically set to complete 47 | * and open to withdraws. 48 | * if exit is already signaled, the call may still cause status update if the pledge period expires 49 | */ 50 | function iwantout() external; 51 | 52 | /** 53 | * @dev raise a dispute 54 | * only one participant can raise the dispute. it will put the contract status to dispute, blocking all 55 | * further transactions. 56 | */ 57 | function dispute() external; 58 | 59 | /** 60 | * @dev request a facilitator to resolve the dispute*/ 61 | function resolutionRequest() external; 62 | /** 63 | * @dev cancel a dispute 64 | * the method also reset the status change. the original dispute period continues. 65 | */ 66 | function withdrawDispute() external; 67 | 68 | /** 69 | * @dev resolve a dispute 70 | * @param participants the list of addresses of participants involved in the resolution 71 | * @param values the list of resolved values after the resolution 72 | * Only the facilitator can resolve a dispute. 73 | * The total amount of values cannot exceed the total funds pledged. The contract status will set to Completed, 74 | * allowing withdraws. 75 | */ 76 | function resolveDispute(address[] calldata participants, uint256[] calldata values) external; 77 | 78 | } 79 | -------------------------------------------------------------------------------- /contracts/IMToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | interface IMToken { 4 | function mint(address target, uint256 amount) external; 5 | function burn(address target, uint256 amount) external; 6 | function addMinter(address minter) external; 7 | function removeMinter(address minter) external; 8 | } 9 | -------------------------------------------------------------------------------- /contracts/IMetis.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/math/SafeMath.sol"; 4 | 5 | /** 6 | * @dev interface of MSC 7 | */ 8 | interface IMetis { 9 | using SafeMath for uint256; 10 | 11 | 12 | event Transaction (address operator, address from, address to, uint256 amount, bytes msg1, bytes msg2); 13 | struct Balance{ 14 | address dacAddress; 15 | uint256 lockedValue; 16 | uint256 unlockedValue; 17 | } 18 | 19 | /** 20 | * @dev commit funds to the contract. participants can keep committing after the pledge ammount is reached 21 | * The sender must authorized this contract to be the operator of senders account before committing 22 | */ 23 | function stake(address sender) external payable returns(uint256); 24 | 25 | /** 26 | * @dev dispense unlocked the token to the recipient 27 | * @param recipient recipient address 28 | * @param amount amount of unlocked M Tokens to dispense 29 | */ 30 | function dispense(address recipient, uint256 amount) external; 31 | 32 | function newTransaction(address sender) external payable returns(uint256, uint256); 33 | 34 | function getNumTokens(address dacAddr) external view returns (uint256); 35 | function lockRatioOf(address dacAddr) external view returns (uint ratio); 36 | function getTokenAddr() external view returns (address); 37 | function getTaxRate() external view returns (uint256); 38 | function getBalance(address dacAddr) external view returns (uint256); 39 | } 40 | -------------------------------------------------------------------------------- /contracts/IRegistrar.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.22 <0.6.0; 2 | 3 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 4 | import "@openzeppelin/contracts/math/SafeMath.sol"; 5 | 6 | interface IRegistrar { 7 | 8 | using SafeMath for uint256; 9 | 10 | function setMetisAddr(address metis) external; 11 | 12 | function createDAC (address owner, string calldata name, string calldata symbol, uint256 stake, address business) external returns(address); 13 | function isActive(address dacAddr) external view returns(bool); 14 | 15 | function migrateDAC (address dacAddr) external; 16 | 17 | function closeDAC (address dacAddr) external; 18 | 19 | function getLastDAC() external view returns(address); 20 | } 21 | -------------------------------------------------------------------------------- /contracts/MSC.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/access/Roles.sol"; 4 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import "./IMSC.sol"; 7 | import "./IDAC.sol"; 8 | 9 | /** 10 | * @dev implementation of MSC 11 | */ 12 | contract MSC is Ownable { 13 | using SafeMath for uint256; 14 | 15 | using Roles for Roles.Role; 16 | enum ContractStatus { Pending, SemiCommitted, Effective, Completed, Dispute, Requested, Closed } 17 | enum ParticipantStatus { Pending,Committed, Wantout, Completed, Dispute, Closed } 18 | 19 | event Transaction (address operator, address from, address to, uint256 amount, bytes msg1, bytes msg2); 20 | event ContractClose(address initiator, uint lastStatusChange, uint numWantedout, bytes msg); 21 | event ContractDispute(address operator, uint lastStatusChange, bytes msg); 22 | event ResolutionRequested(address initiator, uint lastStatusChange); 23 | event DisputeResolution(address facilitator, uint lastStatusChange, address[] participants, uint[] values); 24 | 25 | struct Pledge { 26 | uint256 value; 27 | uint256 stakedValue; 28 | ParticipantStatus status; 29 | } 30 | 31 | Roles.Role private _participants; 32 | address public _starter; // starter of the contract 33 | address public _taker; 34 | address private _tokenAddr; //token contract address 35 | uint private lastStatusChange; 36 | uint private disputeRollBackDays; 37 | 38 | uint private numCommitted; 39 | uint private numWithdraws; 40 | uint private numWantedout; 41 | 42 | uint256 public pledgeAmount; 43 | uint256 public _prizeAmount; 44 | uint256 public _afterTax; //afterTax value of the current transaction. 45 | uint public disputePeriod; 46 | address public disputeInitiator; 47 | address payable public facilitator; 48 | address public _dacAddr; // address to the dac 49 | 50 | ContractStatus public contractStatus; 51 | 52 | mapping(address => Pledge) public parties; 53 | mapping(address => bool) public withdraworder; 54 | 55 | /** 56 | * @dev participants cannot be empty 57 | * @param facilitator_param address of the facilitator, who can resolve disputes. must be payable 58 | * @param period period in days of time one can raise disputes after a participant requests the exit 59 | * @param tokenAddr token contract address 60 | */ 61 | constructor( 62 | address starter, 63 | address taker, 64 | address payable facilitator_param, 65 | uint period, 66 | address tokenAddr, 67 | uint256 stakeAmount, 68 | uint256 prizeAmount, 69 | address dacAddr 70 | ) 71 | public 72 | { 73 | _participants.add(starter); 74 | _starter = starter; 75 | if (taker != address(0)) { 76 | _participants.add(taker); 77 | _taker = taker; 78 | } 79 | 80 | facilitator = facilitator_param; 81 | _tokenAddr = tokenAddr; 82 | pledgeAmount = stakeAmount; 83 | _prizeAmount = prizeAmount; 84 | disputePeriod = period; 85 | _dacAddr = dacAddr; 86 | } 87 | 88 | /** 89 | * @dev add a participant. only possible when the contract is still in pending state 90 | * @param p the participant 91 | */ 92 | function assignTaker(address p) public onlyOwner { 93 | require(contractStatus < ContractStatus.Effective, "STATUS_IS_NOT_PENDING"); 94 | require(!(_participants.has(p)), "Already a participant"); 95 | _participants.add(p); 96 | _taker = p; 97 | } 98 | 99 | /** 100 | * @dev commit funds to the contract. participants can keep committing after the pledge ammount is reached 101 | * @param amount amount of fund to commit 102 | * @param from the address of sender 103 | * The sender must authorized this contract to be the operator of senders account before committing 104 | */ 105 | function _commit(address from, uint256 amount) private { 106 | Pledge storage p = parties[from]; 107 | p.value = p.value.add(amount); 108 | p.stakedValue = p.stakedValue.add(amount); 109 | uint256 threshold = pledgeAmount; 110 | if (from == _starter) { 111 | threshold = pledgeAmount.add(_prizeAmount); 112 | } 113 | if (from != _starter && p.value >= threshold && p.status == ParticipantStatus.Pending) { 114 | p.status = ParticipantStatus.Committed; 115 | numCommitted++; 116 | if (numCommitted == 2) { 117 | contractStatus = ContractStatus.Effective; 118 | } else { 119 | contractStatus = ContractStatus.SemiCommitted; 120 | } 121 | } 122 | lastStatusChange = now; 123 | } 124 | 125 | /** 126 | * @dev commit funds to the contract. participants can keep committing after the pledge ammount is reached 127 | * @param amount amount of fund to commit 128 | * The sender must authorized this contract to be the operator of senders account before committing 129 | */ 130 | function commit(uint256 amount, address sender) public { 131 | // Only participants are allowed 132 | require(amount > 0, "AMOUNT_NOT_GREATER_THAN_ZERO"); 133 | require(_participants.has(sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 134 | 135 | _commit(sender, amount); 136 | } 137 | 138 | function() external payable { 139 | } 140 | 141 | /** 142 | * @dev transfer pledge value from one participant to another 143 | * @param to target address 144 | * @param amount amount of fund to transfer 145 | * The sender must have enough fund pledged. The method does not trigger status changes. 146 | * The transaction is not allowed if the contract is in the middle of a dispute. 147 | */ 148 | function send(address to, uint256 amount) public { 149 | 150 | Pledge storage p = parties[msg.sender]; 151 | Pledge storage targetP = parties[to]; 152 | 153 | // Only participants are allowed 154 | require(amount > 0, "AMOUNT_NOT_GREATER_THAN_ZERO"); 155 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 156 | require(_participants.has(to), "TARGET_DOES_NOT_HAVE_PARTICIPANT_ROLE"); 157 | require(contractStatus != ContractStatus.Dispute, "CONTRACT_IS_IN_DISPUTE"); 158 | require(p.value >= amount, "INSUFFICIENT_BALANCE"); 159 | 160 | p.value -= amount; 161 | targetP.value += amount; 162 | emit Transaction(msg.sender, msg.sender, to, amount, "", "Transfer"); 163 | } 164 | 165 | /** 166 | * @dev withdraw the pledged fund 167 | * The sender must have enough fund pledged. Contract will be closed and selfdestruct if all funds are withdrawn 168 | * The facilitator will be paid with the gas free up after selfdestruct 169 | */ 170 | function withdraw(address to) public { 171 | Pledge storage p = parties[to]; 172 | require(contractStatus == ContractStatus.Completed, "STATUS_IS_NOT_COMPLETED"); 173 | require(p.value > 0, "AMOUNT_NOT_GREATER_THAN_ZERO"); 174 | 175 | p.status = ParticipantStatus.Closed; 176 | lastStatusChange = now; 177 | numWithdraws++; 178 | 179 | uint256 valueToSend = IDAC(_dacAddr).newTransaction.value(p.value)(_starter, _taker, 1); 180 | // _afterTax was refreshed when metis send the eth back. 181 | 182 | p.value = 0; 183 | msg.sender.transfer(valueToSend); 184 | 185 | // indicate withdraw order is initiated. otherwise, the send will be blocked 186 | withdraworder[msg.sender] = true; 187 | 188 | if ( numWithdraws == 2 ) { 189 | contractStatus = ContractStatus.Closed; 190 | //selfdestruct(facilitator); 191 | } 192 | } 193 | 194 | /** 195 | * @dev signal a participant wants to exit 196 | * once an exit is signaled, the contract will wait pledgePeriod * 1 days for other participants to react 197 | * if other participants also signal exits, the contract will change the status to complete and allow withdraws 198 | * the other participants can choose to dispute, which blocks the withdraws until the dispute is resolved 199 | * if no dispute is resolved after the pledge period expires, the contract will automatically set to complete 200 | * and open to withdraws. 201 | */ 202 | function iwantout() public { 203 | Pledge storage p = parties[msg.sender]; 204 | // Only participants are allowed 205 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 206 | require(contractStatus == ContractStatus.Effective, "STATUS_IS_NOT_EFFECTIVE"); 207 | require(p.status == ParticipantStatus.Committed || p.status == ParticipantStatus.Wantout, "STATUS_NOT_IN_COMMITTED_OR_WANTOUT"); 208 | 209 | if (p.status != ParticipantStatus.Wantout) { 210 | p.status = ParticipantStatus.Wantout; 211 | numWantedout++; 212 | lastStatusChange = now; 213 | } 214 | if (numWantedout == 2) { 215 | contractStatus = ContractStatus.Completed; 216 | emit ContractClose(msg.sender, lastStatusChange, numWantedout, "All Agreed"); 217 | } else if (now >= lastStatusChange + disputePeriod * 1 days && contractStatus != ContractStatus.Dispute) { 218 | contractStatus = ContractStatus.Completed; 219 | emit ContractClose(msg.sender, lastStatusChange, numWantedout, "Dispute Expired"); 220 | } 221 | } 222 | 223 | /** 224 | * @dev raise a dispute 225 | * only one participant can raise the dispute. it will put the contract status to dispute, blocking all 226 | * further transactions. 227 | */ 228 | function dispute() public { 229 | Pledge storage p = parties[msg.sender]; 230 | // Only participants are allowed 231 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 232 | require(contractStatus == ContractStatus.Effective, "STATUS_IS_NOT_EFFECTIVE"); 233 | require(p.status == ParticipantStatus.Committed, "STATUS_NOT_IN_COMMITTED"); 234 | 235 | disputeInitiator = msg.sender; 236 | p.status = ParticipantStatus.Dispute; 237 | contractStatus = ContractStatus.Dispute; 238 | 239 | // store how many days spent since the exit was raised 240 | disputeRollBackDays = now - lastStatusChange; 241 | emit ContractDispute(msg.sender, lastStatusChange, "Initiated"); 242 | lastStatusChange = now; 243 | } 244 | 245 | /** 246 | * @dev cancel a dispute 247 | * the method also reset the status change. the original dispute period continues. 248 | */ 249 | function withdrawDispute() public { 250 | Pledge storage p = parties[msg.sender]; 251 | // Only participants are allowed 252 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 253 | require(disputeInitiator == msg.sender, "CALLER_DID_NOT_INITIATE_DISPUTE"); 254 | require(contractStatus == ContractStatus.Dispute, "STATUS_IS_NOT_DISPUTE"); 255 | require(p.status == ParticipantStatus.Dispute, "STATUS_NOT_IN_DISPUTE"); 256 | 257 | p.status = ParticipantStatus.Committed; 258 | contractStatus = ContractStatus.Effective; 259 | emit ContractDispute(msg.sender, lastStatusChange, "Withdrawn"); 260 | 261 | // restore the date so the dispute period continues after the withdraw 262 | lastStatusChange = now - disputeRollBackDays; 263 | } 264 | 265 | /** 266 | * @dev request a facilitator to resolve the dispute 267 | */ 268 | function resolutionRequest() public { 269 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 270 | require(contractStatus == ContractStatus.Dispute, "STATUS_IS_NOT_DISPUTE"); 271 | 272 | emit ResolutionRequested(msg.sender, lastStatusChange); 273 | contractStatus = ContractStatus.Requested; 274 | } 275 | /** 276 | * @dev resolve a dispute 277 | * @param participants the list of addresses of participants involved in the resolution 278 | * @param values the list of resolved values after the resolution 279 | * Only the facilitator can resolve a dispute upon request 280 | * The total amount of values cannot exceed the total funds pledged. The contract status will set to Completed, 281 | * allowing withdraws. 282 | */ 283 | function resolveDispute(address[] memory participants, uint256[] memory values) public { 284 | // Only participants are allowed 285 | require(facilitator == msg.sender, "DOES_NOT_HAVE_FACILITATOR_ROLE"); 286 | require(contractStatus == ContractStatus.Requested, "STATUS_IS_NOT_REQUESTED"); 287 | uint256 totalvalue = 0; 288 | contractStatus = ContractStatus.Completed; 289 | for (uint256 i = 0; i < participants.length; ++i) { 290 | Pledge storage p = parties[participants[i]]; 291 | p.status = ParticipantStatus.Completed; 292 | p.value = values[i]; 293 | totalvalue += p.value; 294 | } 295 | 296 | emit DisputeResolution(msg.sender, lastStatusChange, participants, values); 297 | lastStatusChange = now; 298 | } 299 | 300 | } 301 | -------------------------------------------------------------------------------- /contracts/MSC2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 4 | import "@openzeppelin/contracts/access/Roles.sol"; 5 | import "./IMSC.sol"; 6 | 7 | /** 8 | * @dev implementation of MSC 9 | */ 10 | contract MSC2 { 11 | using Roles for Roles.Role; 12 | enum ContractStatus { Pending, Effective, Completed, Dispute, Requested, Closed } 13 | enum ParticipantStatus { Pending,Committed, Wantout, Completed, Dispute, Closed } 14 | 15 | event Transaction (address operator, address from, address to, uint256 amount, bytes msg1, bytes msg2); 16 | event Give (address from, address to, uint256 amount); 17 | event ContractClose(address initiator, uint lastStatusChange, uint numWantedout, bytes msg); 18 | event ContractDispute(address operator, uint lastStatusChange, bytes msg); 19 | event ResolutionRequested(address initiator, uint lastStatusChange); 20 | event DisputeResolution(address facilitator, uint lastStatusChange, address[] participants, uint[] values); 21 | 22 | struct Pledge { 23 | uint256 value; 24 | ParticipantStatus status; 25 | } 26 | 27 | Roles.Role private _participants; 28 | address private _tokenAddr; //token contract address 29 | IERC20 private _token; 30 | uint private lastStatusChange; 31 | uint private disputeRollBackDays; 32 | uint private numCommitted; 33 | uint private numWantedout; 34 | uint256 public pledgeAmount; 35 | uint public disputePeriod; 36 | address public disputeInitiator; 37 | address public facilitator; 38 | 39 | ContractStatus public contractStatus; 40 | 41 | mapping(address => Pledge) public parties; 42 | address[] public participantsArray; 43 | 44 | /** 45 | * @dev participants cannot be empty 46 | * @param participants list of participants 47 | * @param facilitator_param address of the facilitator, who can resolve disputes. must be payable 48 | * @param period period in days of time one can raise disputes after a participant requests the exit 49 | * @param tokenAddr token contract address 50 | * @param amount amount of pledge requred 51 | */ 52 | constructor( 53 | address[] memory participants, 54 | address facilitator_param, 55 | uint period, 56 | address tokenAddr, 57 | uint256 amount 58 | ) 59 | public 60 | { 61 | participantsArray = participants; 62 | for (uint256 i = 0; i < participants.length; ++i) { 63 | _participants.add(participants[i]); 64 | } 65 | facilitator = facilitator_param; 66 | _tokenAddr = tokenAddr; 67 | _token = IERC20(tokenAddr); 68 | pledgeAmount = amount; 69 | disputePeriod = period; 70 | } 71 | 72 | /** 73 | * @dev commit funds to the contract. participants can keep committing after the pledge ammount is reached 74 | * @param amount amount of fund to commit 75 | & @param from the address of sender 76 | * The sender must authorized this contract to be the operator of senders account before committing 77 | */ 78 | function _commit(address from, uint256 amount) private { 79 | Pledge storage p = parties[from]; 80 | p.value += amount; 81 | if (p.value >= pledgeAmount && p.status == ParticipantStatus.Pending) { 82 | p.status = ParticipantStatus.Committed; 83 | numCommitted++; 84 | if (numCommitted == participantsArray.length) { 85 | contractStatus = ContractStatus.Effective; 86 | } 87 | } 88 | lastStatusChange = now; 89 | //emit Transaction (msg.sender, from, address(this), amount, "", ""); 90 | } 91 | 92 | /** 93 | * @dev commit funds to the contract. participants can keep committing after the pledge ammount is reached 94 | * @param amount amount of fund to commit 95 | * The sender must authorized this contract to be the operator of senders account before committing 96 | */ 97 | function commit(uint256 amount) public { 98 | 99 | // Only participants are allowed 100 | //require(amount > 0, "AMOUNT_NOT_GREATER_THAN_ZERO"); 101 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 102 | require(_token.balanceOf(msg.sender) >= amount, "INSUFFICIENT_BALANCE"); 103 | 104 | _token.transferFrom(msg.sender, address(this), amount); 105 | 106 | _commit(msg.sender, amount); 107 | } 108 | 109 | /** 110 | * @dev transfer pledge value from one participant to another 111 | * @param to target address 112 | * @param amount amount of fund to transfer 113 | * The sender must have enough fund pledged. The method does not trigger status changes. 114 | * The transaction is not allowed if the contract is in the middle of a dispute. 115 | */ 116 | 117 | function give(address to, uint256 amount) public { 118 | 119 | Pledge storage p = parties[msg.sender]; 120 | Pledge storage targetP = parties[to]; 121 | 122 | // Only participants are allowed 123 | require(amount > 0, "AMOUNT_NOT_GREATER_THAN_ZERO"); 124 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 125 | require(_participants.has(to), "TARGET_DOES_NOT_HAVE_PARTICIPANT_ROLE"); 126 | require(contractStatus != ContractStatus.Dispute, "CONTRACT_IS_IN_DISPUTE"); 127 | require(p.value >= amount, "INSUFFICIENT_BALANCE"); 128 | 129 | p.value -= amount; 130 | targetP.value += amount; 131 | emit Give(msg.sender, to, amount); 132 | } 133 | 134 | /** 135 | * @dev withdraw the pledged fund 136 | * The sender must have enough fund pledged. Contract will be closed and selfdestruct if all funds are withdrawn 137 | * The facilitator will be paid with the gas free up after selfdestruct 138 | */ 139 | function withdraw() public { 140 | Pledge storage p = parties[msg.sender]; 141 | // Only participants are allowed 142 | require(contractStatus == ContractStatus.Completed, "STATUS_IS_NOT_COMPLETED"); 143 | require(p.value > 0, "AMOUNT_NOT_GREATER_THAN_ZERO"); 144 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 145 | require(_token.balanceOf(address(this)) >= p.value, "INSUFFICIENT_BALANCE"); 146 | 147 | p.status = ParticipantStatus.Closed; 148 | require(_token.transferFrom(address(this), msg.sender,p.value), "token withdraw failed"); 149 | 150 | p.value = 0; 151 | lastStatusChange = now; 152 | 153 | if (_token.balanceOf(address(this)) == 0) { 154 | contractStatus = ContractStatus.Closed; 155 | } 156 | } 157 | 158 | 159 | /** 160 | * @dev signal a participant wants to exit 161 | * once an exit is signaled, the contract will wait pledgePeriod * 1 days for other participants to react 162 | * if other participants also signal exits, the contract will change the status to complete and allow withdraws 163 | * the other participants can choose to dispute, which blocks the withdraws until the dispute is resolved 164 | * if no dispute is resolved after the pledge period expires, the contract will automatically set to complete 165 | * and open to withdraws. 166 | */ 167 | 168 | function iwantout() public { 169 | Pledge storage p = parties[msg.sender]; 170 | // Only participants are allowed 171 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 172 | require(contractStatus == ContractStatus.Effective, "STATUS_IS_NOT_EFFECTIVE"); 173 | require(p.status == ParticipantStatus.Committed || p.status == ParticipantStatus.Wantout, "STATUS_NOT_IN_COMMITTED_OR_WANTOUT"); 174 | 175 | if (p.status != ParticipantStatus.Wantout) { 176 | p.status = ParticipantStatus.Wantout; 177 | numWantedout++; 178 | //lastStatusChange = now; 179 | } 180 | if (numWantedout == participantsArray.length) { 181 | contractStatus = ContractStatus.Completed; 182 | emit ContractClose(msg.sender, lastStatusChange, numWantedout, "All Agreed"); 183 | } else if (now >= lastStatusChange + disputePeriod * 1 days && contractStatus != ContractStatus.Dispute) { 184 | contractStatus = ContractStatus.Completed; 185 | emit ContractClose(msg.sender, lastStatusChange, numWantedout, "Dispute Expired"); 186 | } 187 | } 188 | 189 | 190 | /** 191 | * @dev raise a dispute 192 | * only one participant can raise the dispute. it will put the contract status to dispute, blocking all 193 | * further transactions. 194 | */ 195 | 196 | function dispute() public { 197 | Pledge storage p = parties[msg.sender]; 198 | // Only participants are allowed 199 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 200 | require(contractStatus == ContractStatus.Effective, "STATUS_IS_NOT_EFFECTIVE"); 201 | require(p.status == ParticipantStatus.Committed, "STATUS_NOT_IN_COMMITTED"); 202 | 203 | disputeInitiator = msg.sender; 204 | p.status = ParticipantStatus.Dispute; 205 | contractStatus = ContractStatus.Dispute; 206 | 207 | // store how many days spent since the exit was raised 208 | disputeRollBackDays = now - lastStatusChange; 209 | emit ContractDispute(msg.sender, lastStatusChange, "Initiated"); 210 | lastStatusChange = now; 211 | } 212 | 213 | /** 214 | * @dev cancel a dispute 215 | * the method also reset the status change. the original dispute period continues. 216 | */ 217 | 218 | function withdrawDispute() public { 219 | Pledge storage p = parties[msg.sender]; 220 | // Only participants are allowed 221 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 222 | require(disputeInitiator == msg.sender, "CALLER_DID_NOT_INITIATE_DISPUTE"); 223 | require(contractStatus == ContractStatus.Dispute, "STATUS_IS_NOT_DISPUTE"); 224 | require(p.status == ParticipantStatus.Dispute, "STATUS_NOT_IN_DISPUTE"); 225 | 226 | p.status = ParticipantStatus.Committed; 227 | contractStatus = ContractStatus.Effective; 228 | emit ContractDispute(msg.sender, lastStatusChange, "Withdrawn"); 229 | 230 | // restore the date so the dispute period continues after the withdraw 231 | lastStatusChange = now - disputeRollBackDays; 232 | } 233 | 234 | /** 235 | * @dev request a facilitator to resolve the dispute 236 | */ 237 | 238 | function resolutionRequest() public { 239 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 240 | require(contractStatus == ContractStatus.Dispute, "STATUS_IS_NOT_DISPUTE"); 241 | 242 | emit ResolutionRequested(msg.sender, lastStatusChange); 243 | contractStatus = ContractStatus.Requested; 244 | } 245 | /** 246 | * @dev resolve a dispute 247 | * @param participants the list of addresses of participants involved in the resolution 248 | * @param values the list of resolved values after the resolution 249 | * Only the facilitator can resolve a dispute upon request 250 | * The total amount of values cannot exceed the total funds pledged. The contract status will set to Completed, 251 | * allowing withdraws. 252 | */ 253 | function resolveDispute(address[] memory participants, uint256[] memory values) public { 254 | // Only participants are allowed 255 | require(facilitator == msg.sender, "DOES_NOT_HAVE_FACILITATOR_ROLE"); 256 | require(contractStatus == ContractStatus.Requested, "STATUS_IS_NOT_REQUESTED"); 257 | uint256 totalvalue = 0; 258 | contractStatus = ContractStatus.Completed; 259 | for (uint256 i = 0; i < participants.length; ++i) { 260 | Pledge storage p = parties[participants[i]]; 261 | p.status = ParticipantStatus.Completed; 262 | p.value = values[i]; 263 | totalvalue += p.value; 264 | } 265 | 266 | // just to make sure the resolution is valid 267 | require(_token.balanceOf(address(this)) >= totalvalue, "INSUFFICIENT_BALANCE"); 268 | 269 | emit DisputeResolution(msg.sender, lastStatusChange, participants, values); 270 | lastStatusChange = now; 271 | } 272 | 273 | } 274 | -------------------------------------------------------------------------------- /contracts/MSCMulti.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/access/Roles.sol"; 4 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import "./IMSC.sol"; 7 | import "./IDAC.sol"; 8 | 9 | /** 10 | * @dev implementation of MSC 11 | */ 12 | contract MSCMulti is Ownable { 13 | using SafeMath for uint256; 14 | 15 | using Roles for Roles.Role; 16 | enum ContractStatus { Pending, SemiCommitted, Effective, Completed, Dispute, Requested, Closed } 17 | enum ParticipantStatus { Pending,Committed, Wantout, Completed, Dispute, Closed } 18 | 19 | event Transaction (address operator, address from, address to, uint256 amount, bytes msg1, bytes msg2); 20 | event ContractClose(address initiator, uint lastStatusChange, uint numWantedout, bytes msg); 21 | event ContractDispute(address operator, uint lastStatusChange, bytes msg); 22 | event ResolutionRequested(address initiator, uint lastStatusChange); 23 | event DisputeResolution(address facilitator, uint lastStatusChange, address[] participants, uint[] values); 24 | 25 | struct Pledge { 26 | uint256 value; 27 | uint256 stakedValue; 28 | ParticipantStatus status; 29 | } 30 | 31 | Roles.Role private _participants; 32 | address public _starter; // starter of the contract 33 | address public _taker; 34 | address private _tokenAddr; //token contract address 35 | uint private lastStatusChange; 36 | uint private disputeRollBackDays; 37 | uint private numCommitted; 38 | uint private numWantedout; 39 | uint256 public pledgeAmount; 40 | uint256 public _prizeAmount; 41 | uint256 public _afterTax; //afterTax value of the current transaction. 42 | uint public disputePeriod; 43 | uint public numWithdraws; 44 | address public disputeInitiator; 45 | address payable public facilitator; 46 | address public _dacAddr; // address to the dac 47 | 48 | ContractStatus public contractStatus; 49 | 50 | mapping(address => Pledge) public parties; 51 | mapping(address => bool) public withdraworder; 52 | address[] public participantsArray; 53 | 54 | /** 55 | * @dev participants cannot be empty 56 | * @param participants list of participants 57 | * @param facilitator_param address of the facilitator, who can resolve disputes. must be payable 58 | * @param period period in days of time one can raise disputes after a participant requests the exit 59 | * @param tokenAddr token contract address 60 | */ 61 | constructor( 62 | address starter, 63 | address[] memory participants, 64 | address payable facilitator_param, 65 | uint period, 66 | address tokenAddr, 67 | uint256 stakeAmount, 68 | uint256 prizeAmount, 69 | address dacAddr 70 | ) 71 | public 72 | { 73 | _participants.add(starter); 74 | _starter = starter; 75 | participantsArray = participants; 76 | for (uint256 i = 0; i < participants.length; ++i) { 77 | _participants.add(participants[i]); 78 | } 79 | facilitator = facilitator_param; 80 | _tokenAddr = tokenAddr; 81 | pledgeAmount = stakeAmount; 82 | _prizeAmount = prizeAmount; 83 | disputePeriod = period; 84 | _dacAddr = dacAddr; 85 | } 86 | 87 | /** 88 | * @dev add a participant. only possible when the contract is still in pending state 89 | * @param p the participant 90 | */ 91 | function addParticipant(address p) public onlyOwner { 92 | require(contractStatus < ContractStatus.Effective, "STATUS_IS_NOT_PENDING"); 93 | require(!(_participants.has(msg.sender)), "Already a participant"); 94 | _participants.add(p); 95 | participantsArray.push(p); 96 | } 97 | 98 | /** 99 | * @dev commit funds to the contract. participants can keep committing after the pledge ammount is reached 100 | * @param amount amount of fund to commit 101 | * @param from the address of sender 102 | * The sender must authorized this contract to be the operator of senders account before committing 103 | */ 104 | function _commit(address from, uint256 amount) private { 105 | Pledge storage p = parties[from]; 106 | p.value = p.value.add(amount); 107 | p.stakedValue = p.stakedValue.add(amount); 108 | uint256 threshold = pledgeAmount; 109 | if (from == _starter) { 110 | threshold = pledgeAmount.add(_prizeAmount); 111 | } 112 | if (from != _starter && p.value >= threshold && p.status == ParticipantStatus.Pending) { 113 | p.status = ParticipantStatus.Committed; 114 | numCommitted++; 115 | if (numCommitted == participantsArray.length) { 116 | contractStatus = ContractStatus.Effective; 117 | } else { 118 | contractStatus = ContractStatus.SemiCommitted; 119 | } 120 | } 121 | lastStatusChange = now; 122 | } 123 | 124 | /** 125 | * @dev commit funds to the contract. participants can keep committing after the pledge ammount is reached 126 | * @param amount amount of fund to commit 127 | * The sender must authorized this contract to be the operator of senders account before committing 128 | */ 129 | function commit(uint256 amount, address sender) public payable{ 130 | // Only participants are allowed 131 | require(amount > 0, "AMOUNT_NOT_GREATER_THAN_ZERO"); 132 | require(_participants.has(sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 133 | 134 | _commit(sender, amount); 135 | } 136 | 137 | function() external payable { 138 | // this means we have the after tax ether value 139 | if (msg.sender == _dacAddr) { 140 | _afterTax = msg.value; 141 | } 142 | } 143 | 144 | /** 145 | * @dev transfer pledge value from one participant to another 146 | * @param to target address 147 | * @param amount amount of fund to transfer 148 | * The sender must have enough fund pledged. The method does not trigger status changes. 149 | * The transaction is not allowed if the contract is in the middle of a dispute. 150 | */ 151 | function send(address to, uint256 amount) public { 152 | 153 | Pledge storage p = parties[msg.sender]; 154 | Pledge storage targetP = parties[to]; 155 | 156 | // Only participants are allowed 157 | require(amount > 0, "AMOUNT_NOT_GREATER_THAN_ZERO"); 158 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 159 | require(_participants.has(to), "TARGET_DOES_NOT_HAVE_PARTICIPANT_ROLE"); 160 | require(contractStatus != ContractStatus.Dispute, "CONTRACT_IS_IN_DISPUTE"); 161 | require(p.value >= amount, "INSUFFICIENT_BALANCE"); 162 | 163 | p.value -= amount; 164 | targetP.value += amount; 165 | emit Transaction(msg.sender, msg.sender, to, amount, "", "Transfer"); 166 | } 167 | 168 | /** 169 | * @dev withdraw the pledged fund 170 | * The sender must have enough fund pledged. Contract will be closed and selfdestruct if all funds are withdrawn 171 | * The facilitator will be paid with the gas free up after selfdestruct 172 | */ 173 | function withdraw(address to) public { 174 | Pledge storage p = parties[to]; 175 | require(contractStatus == ContractStatus.Completed, "STATUS_IS_NOT_COMPLETED"); 176 | require(p.value > 0, "AMOUNT_NOT_GREATER_THAN_ZERO"); 177 | 178 | p.status = ParticipantStatus.Closed; 179 | lastStatusChange = now; 180 | numWithdraws++; 181 | 182 | IDAC(_dacAddr).newTransaction.value(p.value)(_starter, msg.sender, 1); 183 | uint256 valueToSend = _afterTax; 184 | 185 | p.value = 0; 186 | msg.sender.transfer(valueToSend); 187 | 188 | // indicate withdraw order is initiated. otherwise, the send will be blocked 189 | withdraworder[msg.sender] = true; 190 | 191 | if ( numWithdraws == participantsArray.length ) { 192 | contractStatus = ContractStatus.Closed; 193 | //selfdestruct(facilitator); 194 | } 195 | } 196 | 197 | /** 198 | * @dev signal a participant wants to exit 199 | * once an exit is signaled, the contract will wait pledgePeriod * 1 days for other participants to react 200 | * if other participants also signal exits, the contract will change the status to complete and allow withdraws 201 | * the other participants can choose to dispute, which blocks the withdraws until the dispute is resolved 202 | * if no dispute is resolved after the pledge period expires, the contract will automatically set to complete 203 | * and open to withdraws. 204 | */ 205 | function iwantout() public { 206 | Pledge storage p = parties[msg.sender]; 207 | // Only participants are allowed 208 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 209 | require(contractStatus == ContractStatus.Effective, "STATUS_IS_NOT_EFFECTIVE"); 210 | require(p.status == ParticipantStatus.Committed || p.status == ParticipantStatus.Wantout, "STATUS_NOT_IN_COMMITTED_OR_WANTOUT"); 211 | 212 | if (p.status != ParticipantStatus.Wantout) { 213 | p.status = ParticipantStatus.Wantout; 214 | numWantedout++; 215 | lastStatusChange = now; 216 | } 217 | if (numWantedout == participantsArray.length) { 218 | contractStatus = ContractStatus.Completed; 219 | emit ContractClose(msg.sender, lastStatusChange, numWantedout, "All Agreed"); 220 | } else if (now >= lastStatusChange + disputePeriod * 1 days && contractStatus != ContractStatus.Dispute) { 221 | contractStatus = ContractStatus.Completed; 222 | emit ContractClose(msg.sender, lastStatusChange, numWantedout, "Dispute Expired"); 223 | } 224 | } 225 | 226 | /** 227 | * @dev raise a dispute 228 | * only one participant can raise the dispute. it will put the contract status to dispute, blocking all 229 | * further transactions. 230 | */ 231 | function dispute() public { 232 | Pledge storage p = parties[msg.sender]; 233 | // Only participants are allowed 234 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 235 | require(contractStatus == ContractStatus.Effective, "STATUS_IS_NOT_EFFECTIVE"); 236 | require(p.status == ParticipantStatus.Committed, "STATUS_NOT_IN_COMMITTED"); 237 | 238 | disputeInitiator = msg.sender; 239 | p.status = ParticipantStatus.Dispute; 240 | contractStatus = ContractStatus.Dispute; 241 | 242 | // store how many days spent since the exit was raised 243 | disputeRollBackDays = now - lastStatusChange; 244 | emit ContractDispute(msg.sender, lastStatusChange, "Initiated"); 245 | lastStatusChange = now; 246 | } 247 | 248 | /** 249 | * @dev cancel a dispute 250 | * the method also reset the status change. the original dispute period continues. 251 | */ 252 | function withdrawDispute() public { 253 | Pledge storage p = parties[msg.sender]; 254 | // Only participants are allowed 255 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 256 | require(disputeInitiator == msg.sender, "CALLER_DID_NOT_INITIATE_DISPUTE"); 257 | require(contractStatus == ContractStatus.Dispute, "STATUS_IS_NOT_DISPUTE"); 258 | require(p.status == ParticipantStatus.Dispute, "STATUS_NOT_IN_DISPUTE"); 259 | 260 | p.status = ParticipantStatus.Committed; 261 | contractStatus = ContractStatus.Effective; 262 | emit ContractDispute(msg.sender, lastStatusChange, "Withdrawn"); 263 | 264 | // restore the date so the dispute period continues after the withdraw 265 | lastStatusChange = now - disputeRollBackDays; 266 | } 267 | 268 | /** 269 | * @dev request a facilitator to resolve the dispute 270 | */ 271 | function resolutionRequest() public { 272 | require(_participants.has(msg.sender), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 273 | require(contractStatus == ContractStatus.Dispute, "STATUS_IS_NOT_DISPUTE"); 274 | 275 | emit ResolutionRequested(msg.sender, lastStatusChange); 276 | contractStatus = ContractStatus.Requested; 277 | } 278 | /** 279 | * @dev resolve a dispute 280 | * @param participants the list of addresses of participants involved in the resolution 281 | * @param values the list of resolved values after the resolution 282 | * Only the facilitator can resolve a dispute upon request 283 | * The total amount of values cannot exceed the total funds pledged. The contract status will set to Completed, 284 | * allowing withdraws. 285 | */ 286 | function resolveDispute(address[] memory participants, uint256[] memory values) public { 287 | // Only participants are allowed 288 | require(facilitator == msg.sender, "DOES_NOT_HAVE_FACILITATOR_ROLE"); 289 | require(contractStatus == ContractStatus.Requested, "STATUS_IS_NOT_REQUESTED"); 290 | uint256 totalvalue = 0; 291 | contractStatus = ContractStatus.Completed; 292 | for (uint256 i = 0; i < participants.length; ++i) { 293 | Pledge storage p = parties[participants[i]]; 294 | p.status = ParticipantStatus.Completed; 295 | p.value = values[i]; 296 | totalvalue += p.value; 297 | } 298 | 299 | emit DisputeResolution(msg.sender, lastStatusChange, participants, values); 300 | lastStatusChange = now; 301 | } 302 | 303 | } 304 | -------------------------------------------------------------------------------- /contracts/MToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 4 | import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol"; 5 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 6 | import "@openzeppelin/contracts/math/SafeMath.sol"; 7 | import "@openzeppelin/contracts/access/Roles.sol"; 8 | 9 | contract MToken is ERC20, ERC20Detailed, Ownable { 10 | using Roles for Roles.Role; 11 | 12 | Roles.Role private _minters; 13 | using SafeMath for uint256; 14 | 15 | address[] minters_; 16 | uint256 maxSupply_; 17 | 18 | constructor( 19 | address[] memory minters, 20 | uint256 maxSupply 21 | ) 22 | ERC20Detailed("Metis Token", "Metis", 18) 23 | public 24 | { 25 | for (uint256 i = 0; i < minters.length; ++i) { 26 | _minters.add(minters[i]); 27 | } 28 | minters_ = minters; 29 | maxSupply_ = maxSupply; 30 | } 31 | 32 | function mint(address target, uint256 amount) external { 33 | require(_minters.has(msg.sender), "ONLY_MINTER_ALLOWED_TO_DO_THIS"); 34 | require(SafeMath.add(totalSupply(), amount) <= maxSupply_, "EXCEEDING_MAX_SUPPLY"); 35 | _mint(target, amount); 36 | } 37 | 38 | function burn(address target, uint256 amount) external { 39 | require(_minters.has(msg.sender), "ONLY_MINTER_ALLOWED_TO_DO_THIS"); 40 | _burn(target, amount); 41 | } 42 | function addMinter(address minter) external onlyOwner { 43 | require(!_minters.has(minter), "HAVE_MINTER_ROLE_ALREADY"); 44 | _minters.add(minter); 45 | minters_.push(minter); 46 | } 47 | 48 | 49 | function removeMinter(address minter) external onlyOwner { 50 | require(_minters.has(msg.sender), "HAVE_MINTER_ROLE_ALREADY"); 51 | _minters.remove(minter); 52 | uint256 i; 53 | for (i = 0; i < minters_.length; ++i) { 54 | if (minters_[i] == minter) { 55 | minters_[i] = address(0); 56 | break; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /contracts/MathHelper.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | library MathHelper { 3 | 4 | // full precision multiplication. no overflow 5 | function fullMul (uint x, uint y) 6 | internal pure returns (uint l, uint h) 7 | { 8 | uint mm = mulmod (x, y, uint (-1)); 9 | l = x * y; 10 | h = mm - l; 11 | if (mm < l) h -= 1; 12 | } 13 | 14 | // return x*y/z 15 | function mulDiv (uint x, uint y, uint z) internal pure returns (uint) { 16 | (uint l, uint h) = fullMul (x, y); 17 | require (h < z); 18 | uint mm = mulmod (x, y, z); 19 | if (mm > l) h -= 1; 20 | l -= mm; 21 | uint pow2 = z & -z; 22 | z /= pow2; 23 | l /= pow2; 24 | l += h * ((-pow2) / pow2 + 1); 25 | uint r = 1; 26 | r *= 2 - z * r; 27 | r *= 2 - z * r; 28 | r *= 2 - z * r; 29 | r *= 2 - z * r; 30 | r *= 2 - z * r; 31 | r *= 2 - z * r; 32 | r *= 2 - z * r; 33 | r *= 2 - z * r; 34 | return l * r; 35 | } 36 | 37 | // x^n 38 | function pow (uint x, uint n) 39 | internal pure returns (uint r) { 40 | r = 1.0; 41 | while (n > 0) { 42 | if (n % 2 == 1) { 43 | r *= x; 44 | n -= 1; 45 | } else { 46 | x *= x; 47 | n /= 2; 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /contracts/Metis.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/access/Roles.sol"; 4 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import "./MToken.sol"; 7 | import "./MathHelper.sol"; 8 | import "./IRegistrar.sol"; 9 | import "./IMetis.sol"; 10 | import "./IDAC.sol"; 11 | import "./ABDKMath.sol"; 12 | 13 | /** 14 | * @dev interface of MSC 15 | */ 16 | contract Metis is IMetis, Ownable{ 17 | using SafeMath for uint256; 18 | event Transaction (address operator, address from, address to, uint256 amount, bytes msg1, bytes msg2); 19 | event Parameter(address operator, address dacAddr, uint256 from, uint256 to, bytes msg1); 20 | 21 | uint public _version = 1; //metis core version 22 | 23 | address public _tokenAddr; 24 | MToken private _token; 25 | address private _registrarAddr; 26 | IRegistrar private _registrar; 27 | address private _genesisDac; 28 | 29 | //global settings 30 | uint256 public _lockRatio = 95; // 95% 31 | uint256 public _taxRate = 30; // 30% 32 | 33 | uint256 public _curPrice = 1e15; // 0.001 * 10^18 34 | 35 | mapping(address=>uint256) public _zvalues; //64x64 fixed point numbers 36 | mapping(address=>uint256) public _tvls; //per DAC 37 | mapping(address=>uint256) public _preTvls;// per DAC. last Tvl before the unlock threshold 38 | mapping(address=>uint256) public _lockRatios; //per dac 39 | mapping(address=>uint256) public _eths; //per dac 40 | 41 | constructor(address tokenAddr, address registrarAddr) public { 42 | _tokenAddr = tokenAddr; 43 | _registrarAddr = registrarAddr; 44 | _registrar = IRegistrar(_registrarAddr); 45 | _genesisDac = _registrar.createDAC(msg.sender, "_GENESIS", "OG", 0, address(0)); 46 | _token = MToken(_tokenAddr); 47 | emit Transaction (msg.sender, msg.sender, _genesisDac, 0, "Genesis","Create"); 48 | } 49 | 50 | function isDacRegistered(address dac) public view returns (bool) { 51 | return _registrar.isActive(dac); 52 | } 53 | 54 | function createDAC(string memory name, string memory symbol, address business, uint256 stake) public { 55 | require (business != address(0), "0 address not supported"); 56 | //require (_token.transferFrom(msg.sender, stake), "Stake transfer failed"); 57 | address dacAddr = _registrar.createDAC(msg.sender, name, symbol, stake, business); 58 | //_stake(msg.sender, dacAddr, stake); 59 | emit Transaction (msg.sender, msg.sender, dacAddr, stake, "DAC", "NEW"); 60 | } 61 | 62 | function stake(address sender) public payable returns (uint256){ 63 | return _stake(sender, msg.sender, msg.value); 64 | } 65 | /** 66 | * @dev commit funds to the contract. participants can keep committing after the pledge ammount is reached 67 | * The sender must authorized this contract to be the operator of senders account before committing 68 | */ 69 | function _stake(address sender, address dacAddr, uint256 value) private returns(uint256 tokenMinted){ 70 | require(isDacRegistered(dacAddr), "Invalid DAC"); 71 | 72 | _eths[_genesisDac].add(value); 73 | _eths[dacAddr].add(value); 74 | 75 | tokenMinted = MathHelper.mulDiv(value, 10^18, _curPrice); //value * 10^18 / curPrice 76 | uint256 tokenLocked = MathHelper.mulDiv(tokenMinted, lockRatioOf(dacAddr), 100); 77 | 78 | //mint the token 79 | _token.mint(dacAddr, tokenMinted); 80 | 81 | // increase the tvl. but store the existing value for later restore. 82 | uint256 oldTvlDac = _tvls[dacAddr]; 83 | _tvls[dacAddr] = _tvls[dacAddr].add(tokenLocked); 84 | uint256 preTvl = _tvls[_genesisDac]; 85 | _tvls[_genesisDac] = _tvls[_genesisDac].add(tokenLocked); 86 | 87 | //refresh the zvalue. zvalue is calculated before the unlock modification. 88 | uint multiplier = 0; 89 | _zvalues[dacAddr] = MathHelper.mulDiv(_tvls[dacAddr], 100, _tvls[_genesisDac]); 90 | if (_zvalues[dacAddr] < 2) { // z < 20% 91 | multiplier = 15; //1.5 92 | }else if (_zvalues[dacAddr] > 5) { // z > 50% 93 | multiplier = 7; //0.7 94 | } else { 95 | multiplier = 10; //1 96 | } 97 | 98 | // the following loop will unlcok the tokens in stages 99 | while (true) { 100 | uint256 newTvl = MathHelper.mulDiv(multiplier, _preTvls[dacAddr], 10); 101 | if (newTvl < _tvls[dacAddr]) { 102 | _lockRatios[dacAddr] = MathHelper.mulDiv(_lockRatio, _lockRatios[dacAddr], 100); 103 | _preTvls[dacAddr] = MathHelper.mulDiv(_lockRatio, newTvl,100); 104 | _tvls[dacAddr] = MathHelper.mulDiv(_lockRatio, _tvls[dacAddr], 100); 105 | } else { 106 | break; 107 | } 108 | } 109 | 110 | // the tvl of the dac has been modified. recalculate the total tvl. 111 | _tvls[_genesisDac] = preTvl.sub(oldTvlDac).add(_tvls[dacAddr]); 112 | emit Parameter(sender, dacAddr, preTvl, _tvls[_genesisDac], "TVL"); 113 | 114 | //finally, calculate the new price 115 | //Pt = Pt-1 * exp(tvl/pretvl)^0.05 116 | uint256 prePrice = _curPrice; 117 | _curPrice = ABDKMath.mulu( ABDKMath.exp( 118 | ABDKMath.mul(ABDKMath.divu(_tvls[_genesisDac], preTvl), ABDKMath.divu(5, 100)) 119 | ), _curPrice); 120 | 121 | emit Parameter(sender, dacAddr, prePrice, _curPrice, "Price"); 122 | 123 | } 124 | 125 | /** 126 | * @dev dispense unlocked the token to the recipient 127 | * @param dacAddr DAC address 128 | */ 129 | function lockRatioOf(address dacAddr) view public returns (uint ratio){ 130 | require(isDacRegistered(dacAddr), "Invalid DAC"); 131 | ratio = _lockRatios[dacAddr]; 132 | } 133 | 134 | /** 135 | * @dev process the transaction fee 136 | */ 137 | function newTransaction(address sender) public payable returns (uint256 numTokens, uint256 afterTax){ 138 | uint256 value = msg.value; 139 | uint256 tax = MathHelper.mulDiv(value, _taxRate, 100); 140 | address dacAddr = msg.sender; 141 | 142 | require(isDacRegistered(dacAddr), "Invalid DAC"); 143 | 144 | numTokens = MathHelper.mulDiv(value.sub(tax), 10^18, _curPrice); 145 | _token.mint(dacAddr, numTokens); 146 | 147 | afterTax = value.sub(tax); 148 | 149 | msg.sender.transfer(afterTax); 150 | 151 | emit Transaction(dacAddr, sender, _genesisDac, tax, "TAX Deposited", "ETH"); 152 | emit Transaction(dacAddr, sender, _genesisDac, numTokens, "TAX Token Minted", "MToken"); 153 | } 154 | 155 | 156 | function getNumTokens(address dacAddr) public view returns (uint256){ 157 | return _token.balanceOf(dacAddr); 158 | } 159 | 160 | 161 | /** 162 | * migrate a DAC from a previous version of metis 163 | * @param source the older metis 164 | * @param dacAddr the dac to be migrated 165 | */ 166 | function migrateDAC(address dacAddr, address source) public { 167 | require(isDacRegistered(dacAddr), "Invalid DAC"); 168 | require(msg.sender == IDAC(dacAddr).getCreator(), "only the creator can initiate a migration"); 169 | // this is the first version. there for there is nothing to be migrated. 170 | require(source != address(0), "Invalid source"); 171 | _registrar.migrateDAC(dacAddr); 172 | } 173 | 174 | 175 | function getTokenAddr() public view returns (address) { 176 | return _tokenAddr; 177 | } 178 | 179 | function getBalance(address dacAddr) public view returns (uint256) { 180 | require(isDacRegistered(dacAddr), "Invalid DAC"); 181 | return _eths[dacAddr]; 182 | } 183 | 184 | function getTaxRate() public view returns (uint256) { 185 | return _taxRate; 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /contracts/MetisNFT.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC721/ERC721Full.sol"; 4 | import "@openzeppelin/contracts/token/ERC721/ERC721MetadataMintable.sol"; 5 | import "@openzeppelin/contracts/token/ERC721/ERC721Pausable.sol"; 6 | import "@openzeppelin/contracts/token/ERC721/ERC721Burnable.sol"; 7 | import "@openzeppelin/contracts/drafts/Counters.sol"; 8 | 9 | contract MetisNFT is ERC721Full, ERC721MetadataMintable, ERC721Pausable, ERC721Burnable { 10 | using Counters for Counters.Counter; 11 | enum QUALITY {SILVER, GOLD, PLATIUM, DIAMOND, GENESIS} 12 | Counters.Counter private _tokenIds; 13 | mapping(uint256=>string) public _md5s; 14 | mapping(uint256=>QUALITY) public _quality; 15 | event AWARD(address operator, address to, uint256 tokenID, QUALITY quality, string md5, string tokenURI); 16 | event UPDATE(address operator, uint256 tokenID, string md5); 17 | event BASEURI(address operator, string baseURI); 18 | 19 | constructor() ERC721Full("Metis Badge", "MB") public { 20 | } 21 | 22 | function awardItem(address player, QUALITY quality, string memory tokenMetaDataURI, string memory md5) public onlyMinter returns (uint256) { 23 | _tokenIds.increment(); 24 | 25 | uint256 newItemId = _tokenIds.current(); 26 | mintWithTokenURI(player, newItemId, tokenMetaDataURI); 27 | _md5s[newItemId] = md5; 28 | _quality[newItemId] = quality; 29 | emit AWARD(msg.sender, player, newItemId, quality, md5, tokenMetaDataURI); 30 | 31 | return newItemId; 32 | } 33 | 34 | function updateMD5(uint256 tokenID, string memory md5) public onlyMinter { 35 | _md5s[tokenID] = md5; 36 | emit UPDATE(msg.sender, tokenID, md5); 37 | } 38 | 39 | function md5(uint256 tokenID) public view returns(string memory) { 40 | return _md5s[tokenID]; 41 | } 42 | 43 | function quality(uint256 tokenID) public view returns(QUALITY) { 44 | return _quality[tokenID]; 45 | } 46 | 47 | function setBaseURI(string memory baseURI) public onlyMinter { 48 | _setBaseURI(baseURI); 49 | emit BASEURI(msg.sender, baseURI); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /contracts/MetisToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC777/ERC777.sol"; 4 | import "@openzeppelin/contracts/access/Roles.sol"; 5 | 6 | contract MetisToken is ERC777 { 7 | using Roles for Roles.Role; 8 | 9 | Roles.Role private _minters; 10 | Roles.Role private _burners; 11 | 12 | enum ProposalType {undefined, MINT, BURN} 13 | 14 | event ProposalEvent(address operator, uint256 proposalNo, bytes32 msg); 15 | 16 | struct Proposal { 17 | address target; 18 | uint256 amount; 19 | ProposalType ptype; 20 | mapping (address=>bool) approvals; 21 | } 22 | 23 | Proposal[] public proposals; 24 | address[] minters_; 25 | address[] burners_; 26 | 27 | constructor( 28 | uint256 initialSupply, 29 | address[] memory minters, 30 | address[] memory burners, 31 | address[] memory defaultOperators 32 | ) 33 | //ERC777("M Token", "M", defaultOperators) 34 | public 35 | { 36 | for (uint256 i = 0; i < minters.length; ++i) { 37 | _minters.add(minters[i]); 38 | } 39 | for (uint256 i = 0; i < burners.length; ++i) { 40 | _burners.add(burners[i]); 41 | } 42 | minters_ = minters; 43 | burners_ = burners; 44 | _mint(msg.sender, msg.sender, initialSupply, "", ""); 45 | } 46 | 47 | function proposeMint(address target, uint256 amount) public { 48 | // Only minters can mint 49 | require(_minters.has(msg.sender), "DOES_NOT_HAVE_MINTER_ROLE"); 50 | Proposal memory p; 51 | p.target = target; 52 | p.amount = amount; 53 | p.ptype = ProposalType.MINT; 54 | proposals.push(p); 55 | emit ProposalEvent(msg.sender, proposals.length - 1, "New Proposal"); 56 | } 57 | 58 | function signMint(uint256 pos) public { 59 | // Only minters can mint 60 | require(_minters.has(msg.sender), "DOES_NOT_HAVE_MINTER_ROLE"); 61 | require(pos < proposals.length, "INVALID_PROPOSAL"); 62 | 63 | Proposal storage p = proposals[pos]; 64 | require(p.ptype == ProposalType.MINT, "WRONG_TYPE"); 65 | 66 | p.approvals[msg.sender] = true; 67 | emit ProposalEvent(msg.sender, pos, "Sign"); 68 | 69 | uint256 i; 70 | for (i = 0; i < minters_.length; ++i) { 71 | if (p.approvals[minters_[i]] == false) { 72 | break; 73 | } 74 | } 75 | if (i == minters_.length) { 76 | _mint(msg.sender, p.target, p.amount, "", "Approved mint"); 77 | } 78 | } 79 | 80 | function proposeBurn(address target, uint256 amount) public { 81 | // Only minters can mint 82 | require(_minters.has(msg.sender), "DOES_NOT_HAVE_MINTER_ROLE"); 83 | Proposal memory p; 84 | p.target = target; 85 | p.amount = amount; 86 | p.ptype = ProposalType.BURN; 87 | proposals.push(p); 88 | emit ProposalEvent(msg.sender, proposals.length - 1, "New Burn Proposal"); 89 | } 90 | 91 | function signBurn(uint256 pos) public { 92 | // Only minters can mint 93 | require(_burners.has(msg.sender), "DOES_NOT_HAVE_BURNER_ROLE"); 94 | require(pos < proposals.length, "INVALID_PROPOSAL"); 95 | 96 | Proposal storage p = proposals[pos]; 97 | require(p.ptype == ProposalType.BURN, "WRONG_TYPE"); 98 | p.approvals[msg.sender] = true; 99 | emit ProposalEvent(msg.sender, pos, "Sign"); 100 | 101 | uint256 i; 102 | for (i = 0; i < burners_.length; ++i) { 103 | if (p.approvals[burners_[i]] == false) { 104 | break; 105 | } 106 | } 107 | if (i == burners_.length) { 108 | _burn(msg.sender, p.target, p.amount, "", "Approved burn"); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.0; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | function setCompleted(uint completed) public restricted { 15 | last_completed_migration = completed; 16 | } 17 | 18 | function upgrade(address new_address) public restricted { 19 | Migrations upgraded = Migrations(new_address); 20 | upgraded.setCompleted(last_completed_migration); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/MultiSig.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC777/ERC777.sol"; 4 | import "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; 5 | import "@openzeppelin/contracts/token/ERC777/IERC777Sender.sol"; 6 | import "@openzeppelin/contracts/access/Roles.sol"; 7 | 8 | /** 9 | * @dev implementation of MSC 10 | */ 11 | contract MultiSig is IERC777Recipient, IERC777Sender{ 12 | IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); 13 | bytes32 constant private TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient"); 14 | 15 | using Roles for Roles.Role; 16 | 17 | event Propose (address operator, address to, uint256 amount, bytes msg1, bytes msg2); 18 | event Transaction (address operator, address from, address to, uint256 amount, bytes msg1, bytes msg2); 19 | 20 | struct Proposal{ 21 | uint256 value; 22 | mapping(address => bool) approvals; 23 | uint256 numApproves; 24 | } 25 | 26 | Roles.Role private _participants; 27 | address private _tokenAddr; //token contract address 28 | IERC777 private _token; 29 | 30 | mapping(address => Proposal) public proposals; 31 | address[] public participantsArray; 32 | 33 | /** 34 | * @dev participants cannot be empty 35 | * @param participants list of participants 36 | * @param tokenAddr token contract address 37 | */ 38 | constructor( 39 | address[] memory participants, 40 | address tokenAddr 41 | ) 42 | public 43 | { 44 | _tokenAddr = tokenAddr; 45 | _token = IERC777(tokenAddr); 46 | _erc1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this)); 47 | 48 | participantsArray = participants; 49 | for (uint256 i = 0; i < participants.length; ++i) { 50 | _participants.add(participants[i]); 51 | _token.authorizeOperator(participants[i]); 52 | } 53 | } 54 | 55 | /** 56 | * @dev propose a transfer 57 | * if all participants submitted the same proposal, the proposal is approved. the token transfer will be executed 58 | * send a proposal with a different amount will override the previous one and reset the approvals 59 | */ 60 | function _propose(address operator, address to, uint256 amount, bytes memory usermsg) private { 61 | Proposal storage p = proposals[to]; 62 | // Only participants are allowed 63 | require(_participants.has(operator), "DOES_NOT_HAVE_PARTICIPANT_ROLE"); 64 | require(amount > 0, "AMOUNT_NOT_GREATER_THAN_ZERO"); 65 | require(_token.balanceOf(address(this)) >= amount, "INSUFFICIENT_BALANCE"); 66 | 67 | // if someone propose a new amount, override 68 | if (p.value != amount) { 69 | for (uint256 i = 0; i < participantsArray.length; ++i) { 70 | // clear the approval 71 | p.approvals[participantsArray[i]] = false; 72 | } 73 | //init the rest; 74 | p.numApproves = 1; 75 | p.approvals[operator] = true; 76 | p.value = amount; 77 | emit Propose (operator, to, amount, "New Proposal", usermsg); 78 | } else { 79 | if (p.approvals[operator] == false) { 80 | p.numApproves++; 81 | p.approvals[operator] = true; 82 | emit Propose (operator, to, amount, "Approve", usermsg); 83 | } 84 | } 85 | 86 | if (p.numApproves == participantsArray.length) { 87 | _token.send(to, amount, "Approved Transfer"); 88 | } 89 | } 90 | function propose(address to, uint256 amount, bytes memory usermsg) public{ 91 | _propose(msg.sender, to, amount, usermsg); 92 | } 93 | 94 | function tokensReceived ( 95 | address operator, 96 | address from, 97 | address to, 98 | uint256 amount, 99 | bytes calldata userData, 100 | bytes calldata operatorData 101 | ) external { 102 | require(msg.sender == _tokenAddr, "Invalid token"); 103 | 104 | emit Transaction(operator, from, to, amount, userData, operatorData); 105 | } 106 | 107 | function tokensToSend ( 108 | address operator, 109 | address from, 110 | address to, 111 | uint256 amount, 112 | bytes calldata userData, 113 | bytes calldata operatorData 114 | ) external { 115 | require(_token.balanceOf(address(this)) >= amount, "INSUFFICIENT_BALANCE"); 116 | require(amount > 0, "AMOUNT_IS_ZERO"); 117 | require(from == address(this), "SOURCE_IS_NOT_MYSELF"); 118 | require(msg.sender == _tokenAddr, "Invalid token"); 119 | 120 | Proposal storage p = proposals[to]; 121 | 122 | require(p.numApproves == participantsArray.length, "INCOMPLETE_APPROVAL"); 123 | require(p.value == amount, "VALUE_UNMATCHED_WITH_PROPOSAL"); 124 | 125 | for (uint256 i = 0; i < participantsArray.length; ++i) { 126 | require(p.approvals[participantsArray[i]], "UNAPPROED_BY_SOMEONE"); 127 | // clear the approval 128 | p.approvals[participantsArray[i]] = false; 129 | } 130 | //clear the rest; 131 | p.numApproves = 0; 132 | p.value = 0; 133 | 134 | emit Transaction(operator, from, to, amount, userData, operatorData); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /contracts/MultiSigMinter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 4 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 5 | import "@openzeppelin/contracts/access/Roles.sol"; 6 | import "./IMToken.sol"; 7 | 8 | contract MultiSigMinter is Ownable { 9 | using Roles for Roles.Role; 10 | 11 | Roles.Role private _minters; 12 | 13 | enum ProposalType {undefined, MINT, BURN} 14 | 15 | event ProposalEvent(address operator, uint256 proposalNo, bytes32 msg); 16 | 17 | struct Proposal { 18 | address target; 19 | uint256 amount; 20 | ProposalType ptype; 21 | mapping (address=>bool) approvals; 22 | bool finish; 23 | } 24 | 25 | Proposal[] public proposals; 26 | address[] minters_; 27 | IMToken token_; 28 | 29 | constructor( 30 | address[] memory minters, 31 | address token 32 | ) 33 | public 34 | { 35 | for (uint256 i = 0; i < minters.length; ++i) { 36 | _minters.add(minters[i]); 37 | } 38 | minters_ = minters; 39 | token_ = IMToken(token); 40 | } 41 | 42 | function addMinter(address minter) external onlyOwner { 43 | require(!_minters.has(minter), "HAVE_MINTER_ROLE_ALREADY"); 44 | _minters.add(minter); 45 | minters_.push(minter); 46 | } 47 | 48 | 49 | function removeMinter(address minter) external onlyOwner { 50 | require(_minters.has(msg.sender), "HAVE_MINTER_ROLE_ALREADY"); 51 | _minters.remove(minter); 52 | uint256 i; 53 | for (i = 0; i < minters_.length; ++i) { 54 | if (minters_[i] == minter) { 55 | minters_[i] = address(0); 56 | break; 57 | } 58 | } 59 | } 60 | function proposeMint(address target, uint256 amount) external{ 61 | // Only minters can mint 62 | require(_minters.has(msg.sender), "DOES_NOT_HAVE_MINTER_ROLE"); 63 | Proposal memory p; 64 | p.target = target; 65 | p.amount = amount; 66 | p.ptype = ProposalType.MINT; 67 | p.finish = false; 68 | proposals.push(p); 69 | emit ProposalEvent(msg.sender, proposals.length - 1, "New Proposal"); 70 | } 71 | 72 | function signMint(uint256 pos) external { 73 | // Only minters can mint 74 | require(_minters.has(msg.sender), "DOES_NOT_HAVE_MINTER_ROLE"); 75 | require(pos < proposals.length, "INVALID_PROPOSAL"); 76 | 77 | Proposal storage p = proposals[pos]; 78 | require(p.ptype == ProposalType.MINT, "WRONG_TYPE"); 79 | require(p.finish == false, "MINTED"); 80 | 81 | p.approvals[msg.sender] = true; 82 | emit ProposalEvent(msg.sender, pos, "Sign"); 83 | 84 | uint256 i; 85 | for (i = 0; i < minters_.length; ++i) { 86 | if (minters_[i] == address(0)) { 87 | continue; 88 | } 89 | if (p.approvals[minters_[i]] == false) { 90 | break; 91 | } 92 | } 93 | if (i == minters_.length) { 94 | p.finish = true; 95 | token_.mint(p.target, p.amount); 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /contracts/MultiSigWalletWithDailyLimit.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | import "./MultiSigWallet.sol"; 3 | 4 | 5 | /// @title Multisignature wallet with daily limit - Allows an owner to withdraw a daily limit without multisig. 6 | /// @author Stefan George - 7 | contract MultiSigWalletWithDailyLimit is MultiSigWallet { 8 | 9 | /* 10 | * Events 11 | */ 12 | event DailyLimitChange(uint dailyLimit); 13 | 14 | /* 15 | * Storage 16 | */ 17 | uint public dailyLimit; 18 | uint public lastDay; 19 | uint public spentToday; 20 | 21 | /* 22 | * Public functions 23 | */ 24 | /// @dev Contract constructor sets initial owners, required number of confirmations and daily withdraw limit. 25 | /// @param _owners List of initial owners. 26 | /// @param _required Number of required confirmations. 27 | /// @param _dailyLimit Amount in wei, which can be withdrawn without confirmations on a daily basis. 28 | constructor(address[] memory _owners, uint _required, uint _dailyLimit) 29 | public 30 | MultiSigWallet(_owners, _required) 31 | { 32 | dailyLimit = _dailyLimit; 33 | } 34 | 35 | /// @dev Allows to change the daily limit. Transaction has to be sent by wallet. 36 | /// @param _dailyLimit Amount in wei. 37 | function changeDailyLimit(uint _dailyLimit) 38 | public 39 | onlyWallet 40 | { 41 | dailyLimit = _dailyLimit; 42 | emit DailyLimitChange(_dailyLimit); 43 | } 44 | 45 | /// @dev Allows anyone to execute a confirmed transaction or ether withdraws until daily limit is reached. 46 | /// @param transactionId Transaction ID. 47 | function executeTransaction(uint transactionId) 48 | public 49 | ownerExists(msg.sender) 50 | confirmed(transactionId, msg.sender) 51 | notExecuted(transactionId) 52 | { 53 | Transaction storage txn = transactions[transactionId]; 54 | bool _confirmed = isConfirmed(transactionId); 55 | if (_confirmed || txn.data.length == 0 && isUnderLimit(txn.value)) { 56 | txn.executed = true; 57 | if (!_confirmed) 58 | spentToday += txn.value; 59 | if (external_call(txn.destination, txn.value, txn.data.length, txn.data)) 60 | emit Execution(transactionId); 61 | else { 62 | emit ExecutionFailure(transactionId); 63 | txn.executed = false; 64 | if (!_confirmed) 65 | spentToday -= txn.value; 66 | } 67 | } 68 | } 69 | 70 | /* 71 | * Internal functions 72 | */ 73 | /// @dev Returns if amount is within daily limit and resets spentToday after one day. 74 | /// @param amount Amount to withdraw. 75 | /// @return Returns if amount is under daily limit. 76 | function isUnderLimit(uint amount) 77 | internal 78 | returns (bool) 79 | { 80 | if (now > lastDay + 24 hours) { 81 | lastDay = now; 82 | spentToday = 0; 83 | } 84 | if (spentToday + amount > dailyLimit || spentToday + amount < spentToday) 85 | return false; 86 | return true; 87 | } 88 | 89 | /* 90 | * Web3 call functions 91 | */ 92 | /// @dev Returns maximum withdraw amount. 93 | /// @return Returns amount. 94 | function calcMaxWithdraw() 95 | public 96 | view 97 | returns (uint) 98 | { 99 | if (now > lastDay + 24 hours) 100 | return dailyLimit; 101 | if (dailyLimit < spentToday) 102 | return 0; 103 | return dailyLimit - spentToday; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /contracts/Registrar.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.22 <0.6.0; 2 | 3 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 4 | import "@openzeppelin/contracts/math/SafeMath.sol"; 5 | import "./DAC.sol"; 6 | 7 | contract Registrar is Ownable{ 8 | 9 | using SafeMath for uint256; 10 | 11 | uint public _version = 1; 12 | 13 | event NewDAC(address owner, address dacAddr); 14 | event FinishTask(address owner, uint256 index); 15 | event ReviewTask(address owner, uint256 index, bool verdict); 16 | 17 | enum STATUS {NONE, ACTIVE, DISPUTE, CLOSED} 18 | 19 | 20 | mapping (address => STATUS) public _dacList; 21 | mapping (string => bool) public _nameList; 22 | address[] _dacArray; 23 | address _metis; 24 | 25 | constructor() public { 26 | } 27 | 28 | function setMetisAddr(address metis) public onlyOwner { 29 | _metis = metis; 30 | } 31 | 32 | /// add a new task to the list 33 | /// pass address(0) in delegate to open the task for all 34 | function createDAC (address owner, string memory name, string memory symbol, uint256 stake, address business) public returns (address newDac){ 35 | 36 | require(msg.sender == _metis, "only metis can create DAC"); 37 | require(_nameList[name] == false, "The name is already taken"); 38 | 39 | _nameList[name] = true; 40 | 41 | //deploy MSC 42 | newDac = address(new DAC(owner, _metis, name, symbol, stake, business)); 43 | _dacList[newDac] = STATUS.ACTIVE; 44 | _dacArray.push(newDac); 45 | 46 | emit NewDAC(owner, newDac); 47 | } 48 | function isActive(address dacAddr) view public returns(bool){ 49 | return _dacList[dacAddr] == STATUS.ACTIVE; 50 | } 51 | 52 | /// take the task 53 | function migrateDAC (address payable dacAddr) public { 54 | require(msg.sender == _metis, "only the current metis can call this method"); 55 | require(isActive(dacAddr), "This DAC is not active"); 56 | 57 | DAC(dacAddr).setMetis(_metis); 58 | } 59 | 60 | /// take the task 61 | function closeDAC (address dacAddr) public { 62 | require(msg.sender == _metis, "only the current metis can call this method"); 63 | require(isActive(dacAddr), "This DAC is not active"); 64 | 65 | _dacList[dacAddr] = STATUS.CLOSED; 66 | } 67 | 68 | function getLastDAC() view public returns(address) { 69 | return _dacArray[_dacArray.length - 1]; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /contracts/TaskList.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.22 <0.6.0; 2 | 3 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 4 | import "@openzeppelin/contracts/math/SafeMath.sol"; 5 | import "./MSC.sol"; 6 | 7 | contract TaskList is Ownable{ 8 | 9 | using SafeMath for uint256; 10 | 11 | event NewTask(address owner, uint256 index); 12 | event FinishTask(address owner, uint256 index); 13 | event ReviewTask(address owner, uint256 index, bool verdict); 14 | 15 | enum STATUS {NONE, OPEN, STAKING, EXECUTING, REVIEW, REJECT, DONE} 16 | 17 | struct Task { 18 | string infourl; // wiki link to the task details 19 | uint256 timestamp; //timestamp of the last status update 20 | STATUS status; 21 | uint256 expiry; // must finish before the expiry 22 | uint256 stakereq; // stake requirement 23 | address taskowner; // owner of this task 24 | address delegate; // taker of the task; 25 | string resulturl; 26 | uint256 prize; 27 | MSC msc; 28 | } 29 | 30 | mapping (address => Task[]) public tasklist; 31 | 32 | constructor() public { 33 | } 34 | 35 | /// add a new task to the list 36 | /// pass address(0) in delegate to open the task for all 37 | function addTask (address owner, string memory infourl, uint256 expiry, uint256 prize, uint256 stakereq) public onlyOwner { 38 | Task[] storage tasks = tasklist[owner]; 39 | 40 | Task memory task; 41 | 42 | task.infourl = infourl; 43 | task.timestamp = block.timestamp; 44 | task.expiry = expiry; 45 | task.prize = prize; 46 | task.stakereq = stakereq; 47 | task.taskowner = owner; 48 | task.status = STATUS.OPEN; 49 | tasks.push(task); 50 | 51 | //deploy MSC 52 | task.msc = new MSC(owner, address(0) , msg.sender, 30 days, address(this), stakereq, prize, msg.sender); 53 | 54 | emit NewTask(owner, tasks.length - 1); 55 | } 56 | 57 | /// take the task 58 | function takeTask (address taskowner, uint256 index, address taker) public onlyOwner { 59 | require(index < tasklist[taskowner].length, "Index out of bound"); 60 | 61 | Task storage task = tasklist[taskowner][index]; 62 | require(task.status == STATUS.OPEN, "Task is not open"); 63 | require(task.msc.contractStatus() == MSC.ContractStatus.Pending, "Contract is not open"); 64 | 65 | task.timestamp = block.timestamp; 66 | task.delegate = taker; 67 | task.status = STATUS.STAKING; 68 | 69 | task.msc.assignTaker(taker); 70 | } 71 | 72 | /// reopen the task 73 | function reopenTask (address taskowner, uint256 index) public { 74 | require(index < tasklist[taskowner].length, "Index out of bound"); 75 | require(taskowner == msg.sender, "Not a task owner"); 76 | 77 | Task storage task = tasklist[taskowner][index]; 78 | require(task.status == STATUS.STAKING, "Task is not in staking"); 79 | require(task.msc.contractStatus() == MSC.ContractStatus.Pending, "Contract is not open"); 80 | task.timestamp = block.timestamp; 81 | task.delegate = address(0); 82 | task.status = STATUS.OPEN; 83 | } 84 | 85 | /// reopen the task 86 | function startTask (address taskowner, uint256 index) public { 87 | require(index < tasklist[taskowner].length, "Index out of bound"); 88 | require(taskowner == msg.sender, "Not a task owner"); 89 | 90 | Task storage task = tasklist[taskowner][index]; 91 | require (task.status == STATUS.STAKING, "Task is not in staking"); 92 | require(task.msc.contractStatus() == MSC.ContractStatus.Effective, "Contract is not effective"); 93 | task.timestamp = block.timestamp; 94 | task.status = STATUS.EXECUTING; 95 | } 96 | 97 | /// put the task to review 98 | function finshTask (address taskowner, uint256 index, string memory resulturl) public { 99 | require(index < tasklist[taskowner].length, "Index out of bound"); 100 | 101 | Task storage task = tasklist[taskowner][index]; 102 | require(task.delegate == msg.sender, "Not a task taker"); 103 | require (task.status == STATUS.EXECUTING || task.status == STATUS.REJECT, "Task is not ready for review"); 104 | require(task.msc.contractStatus() == MSC.ContractStatus.Effective, "Contract is not effective"); 105 | task.timestamp = block.timestamp; 106 | task.status = STATUS.REVIEW; 107 | task.resulturl = resulturl; 108 | emit FinishTask(taskowner, index); 109 | } 110 | 111 | /// review verdict 112 | function reviewTask(uint256 index, bool verdict) public { 113 | address taskowner = msg.sender; 114 | 115 | require(index < tasklist[taskowner].length, "Index out of bound"); 116 | 117 | Task storage task = tasklist[msg.sender][index]; 118 | require (task.status == STATUS.REVIEW, "Task is not in review-pending"); 119 | require(task.msc.contractStatus() == MSC.ContractStatus.Effective, "Contract is not effective"); 120 | 121 | if (verdict == true) { 122 | task.status = STATUS.DONE; 123 | emit ReviewTask(taskowner, index, true); 124 | } 125 | else { 126 | task.status = STATUS.REJECT; 127 | emit ReviewTask(taskowner, index, false); 128 | } 129 | } 130 | 131 | function getNumTaskByAddress(address taskowner) public view returns (uint) { 132 | return tasklist[taskowner].length; 133 | } 134 | 135 | function getMSCByTask(address taskowner, uint index) public view returns(address) { 136 | return address(tasklist[taskowner][index].msc); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /contracts/TaskList2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.22 <0.6.0; 2 | 3 | contract TaskList2 { 4 | 5 | 6 | enum STATUS {NONE, OPEN, EXECUTING, REVIEW, REJECT, DONE} 7 | 8 | struct Task { 9 | string infourl; // wiki link to the task details 10 | uint timestamp; //timestamp of the last status update 11 | STATUS status; 12 | uint expiry; // must finish before the expiry 13 | uint stakereq; // stake requirement 14 | address taskowner; // owner of this task 15 | address delegate; // taker of the task; 16 | string resulturl; 17 | uint prize; 18 | } 19 | 20 | enum ROLE {NONE, TASKOWNER, SERVICE, ADMIN} 21 | 22 | address owner; 23 | mapping (address => Task[]) public tasklist; 24 | mapping (address => bool) taskownerlist; 25 | mapping (address => bool) servicelist; 26 | mapping (address => bool) adminlist; 27 | 28 | address[] public taskaddresses; 29 | 30 | constructor() public { 31 | owner = msg.sender; 32 | } 33 | 34 | function transferOwner (address newOwner) public { 35 | if (msg.sender == owner) { 36 | owner = newOwner; 37 | } 38 | } 39 | 40 | function addTaskOwner (address entity) public { 41 | if (msg.sender == owner || adminlist[msg.sender]) { 42 | taskownerlist[entity] = true; 43 | } 44 | } 45 | 46 | function addService (address entity) public { 47 | if (msg.sender == owner || adminlist[msg.sender]) { 48 | servicelist[entity] = true; 49 | } 50 | } 51 | 52 | function addAdmin (address entity) public { 53 | if (msg.sender == owner) { 54 | adminlist[entity] = true; 55 | } 56 | } 57 | /// add a new task to the list 58 | /// pass address(0) in delegate to open the task for all 59 | function addTask (string memory infourl, uint expiry, uint prize, address delegate) public returns (int) { 60 | if (taskownerlist[msg.sender] == false) { 61 | return -1; 62 | } 63 | Task[] storage tasks = tasklist[msg.sender]; 64 | uint index = 0; 65 | 66 | for (index = 0; index < tasks.length; index++) { 67 | if (tasks[index].status == STATUS.DONE) break; 68 | } 69 | 70 | Task memory task; 71 | 72 | task.infourl = infourl; 73 | task.timestamp = block.timestamp; 74 | task.expiry = expiry; 75 | task.prize = prize; 76 | task.taskowner = msg.sender; 77 | task.delegate = delegate; 78 | task.status = STATUS.OPEN; 79 | if (index == tasks.length) { 80 | if (index == 0) { 81 | // new owner 82 | taskaddresses.push(msg.sender); 83 | } 84 | tasks.push(task); 85 | } else { 86 | tasks[index] = task; 87 | } 88 | return int(index); 89 | } 90 | 91 | /// take the task 92 | function takeTask (address taskowner, uint index) public returns (bool) { 93 | if (servicelist[msg.sender] == false) { 94 | return false; 95 | } 96 | if (index >= tasklist[taskowner].length) return false; 97 | Task storage task = tasklist[taskowner][index]; 98 | if (task.status == STATUS.OPEN && (task.delegate == address(0) || task.delegate == msg.sender)) { 99 | task.timestamp = block.timestamp; 100 | task.delegate = msg.sender; 101 | task.status = STATUS.EXECUTING; 102 | return true; 103 | } 104 | return false; 105 | } 106 | 107 | /// put the task to review 108 | function finshTask (address taskowner, uint index, string memory resulturl) public returns (bool) { 109 | if (servicelist[msg.sender] == false) { 110 | return false; 111 | } 112 | if (index >= tasklist[taskowner].length) return false; 113 | Task storage task = tasklist[taskowner][index]; 114 | if (task.delegate == msg.sender && (task.status == STATUS.EXECUTING || task.status == STATUS.REJECT)) { 115 | task.resulturl = resulturl; 116 | task.status = STATUS.REVIEW; 117 | return true; 118 | } 119 | return false; 120 | } 121 | 122 | /// review verdict 123 | function reviewTask(uint index, bool verdict) public returns (bool) { 124 | if (taskownerlist[msg.sender] == false) return false; 125 | if (index >= tasklist[msg.sender].length) return false; 126 | Task storage task = tasklist[msg.sender][index]; 127 | if (task.status == STATUS.REVIEW) { 128 | if (verdict == true) { 129 | task.status = STATUS.DONE; 130 | } 131 | else { 132 | task.status = STATUS.REJECT; 133 | } 134 | return true; 135 | } 136 | return false; 137 | } 138 | 139 | function getNumTaskLists() public view returns (uint) { 140 | return taskaddresses.length; 141 | } 142 | 143 | function getNumTaskByAddress(address taskowner) public view returns (uint) { 144 | return tasklist[taskowner].length; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /contracts/TokenVault.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 6 | import "@openzeppelin/contracts/math/SafeMath.sol"; 7 | 8 | contract TokenVault is Ownable { 9 | 10 | IERC20 token_; 11 | using SafeMath for uint; 12 | enum STATUS {undefined, ARRANGED, PAID} 13 | event CLAIM(address operator, uint amount); 14 | uint8 init_; 15 | 16 | mapping(address=>uint[]) public arrangements_; 17 | mapping(uint8=>uint[]) public timestamps_; 18 | mapping(address=>uint8) public index_; 19 | 20 | constructor( 21 | address token 22 | ) 23 | public 24 | { 25 | token_ = IERC20(token); 26 | timestamps_[0] = [1620903600,1628812800,1636761600,1644710400,1652400000]; 27 | timestamps_[1] = [1620903600,1623542400,1626134400,1628812800,1631491200,1634083200,1636761600,1639353600,1642032000,1644710400,1647129600,1649808000,1652400000]; 28 | timestamps_[2] = [1636761600,1644710400,1652400000,1660348800,1668297600,1676246400,1683936000,1691884800]; 29 | timestamps_[3] = [1620903600]; 30 | timestamps_[4] = [1620903600,1628812800,1636761600,1644710400,1652400000,1660348800,1668297600,1676246400,1683936000]; 31 | timestamps_[5] = [1636761600,1652400000,1660348800,1668297600,1676246400,1683936000]; 32 | timestamps_[6] = [1620903600,1628812800,1636761600,1644710400,1652400000,1623542400,1626134400,1631491200,1634083200,1639353600,1642032000,1647129600,1649808000]; 33 | //special timestamp 34 | timestamps_[7] = [10,20,99999999999]; 35 | } 36 | 37 | function _add(address target, uint[] memory inputs) internal { 38 | delete arrangements_[target]; 39 | for (uint i = 0; i < inputs.length; ++i) { 40 | arrangements_[target].push(inputs[i]); 41 | } 42 | } 43 | function addNew(address target, uint[] calldata inputs) external onlyOwner{ 44 | _add(target, inputs); 45 | } 46 | 47 | function addNewBatch(address[] calldata targets, uint[][] calldata inputs) external onlyOwner{ 48 | for (uint i = 0; i < inputs.length; ++i) { 49 | _add(targets[i], inputs[i]); 50 | } 51 | } 52 | 53 | function getAmountIndex(uint8 ts, uint8 i) internal pure returns(uint index){ 54 | if (ts == 5) { 55 | if (i == 0) { 56 | index = 0; 57 | } else if (i == 1) { 58 | index = 1; 59 | } else { 60 | index = 2; 61 | } 62 | } else if (ts == 6) { 63 | if (i == 0) { 64 | index = 0; 65 | } else if (i % 3 == 0) { 66 | index = 2; 67 | } else { 68 | index = 1; 69 | } 70 | } else if (ts == 2 || ts == 3) { 71 | index = 0; 72 | } else { 73 | if (i == 0) { 74 | index = 0; 75 | } else { 76 | index = 1; 77 | } 78 | } 79 | 80 | } 81 | function claim() external { 82 | uint[] memory a = arrangements_[msg.sender]; 83 | uint8 ts = uint8(a[a.length - 1]); 84 | uint[] memory tslist = timestamps_[ts]; 85 | uint8 startIndex = index_[msg.sender]; 86 | uint totalAmount = 0; 87 | 88 | for (uint8 i = startIndex; i < tslist.length; ++i) { 89 | if (now >= tslist[i]) { 90 | totalAmount = totalAmount.add(a[getAmountIndex(ts, i)]); 91 | index_[msg.sender] = i + 1; 92 | } else { 93 | break; 94 | } 95 | } 96 | require(token_.transfer(msg.sender, totalAmount), "TRANSFER_FAILED"); 97 | emit CLAIM(msg.sender, totalAmount); 98 | } 99 | 100 | function checkArrangements() external view returns (string memory result){ 101 | 102 | uint[] memory a = arrangements_[msg.sender]; 103 | uint8 ts = uint8(a[a.length - 1]); 104 | uint[] memory tslist = timestamps_[ts]; 105 | uint8 startIndex = index_[msg.sender]; 106 | 107 | for(uint8 i = 0; i < tslist.length; ++i) { 108 | string memory cur; 109 | if (i < startIndex) { 110 | cur = string(abi.encodePacked("Amount:", uint2str(a[getAmountIndex(ts, i)]), " PAID|")); 111 | } else { 112 | cur = string(abi.encodePacked("Amount:", uint2str(a[getAmountIndex(ts, i)]), " Available on TS ", uint2str(tslist[i]), " LOCKED|")); 113 | } 114 | result = string(abi.encodePacked(result, "\n", cur)); 115 | } 116 | } 117 | 118 | function uint2str(uint _i) internal pure returns (string memory _uintAsString) { 119 | if (_i == 0) { 120 | return "0"; 121 | } 122 | uint j = _i; 123 | uint len; 124 | while (j != 0) { 125 | len++; 126 | j /= 10; 127 | } 128 | bytes memory bstr = new bytes(len); 129 | uint k = len - 1; 130 | while (_i != 0) { 131 | bstr[k--] = byte(uint8(48 + _i % 10)); 132 | _i /= 10; 133 | } 134 | return string(bstr); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /docs/Metis - Technical Fundamentals Whitepaper V02.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MetisProtocol/metis/c26c47b6afee6eb7c98eaa433c6c5509c17ff626/docs/Metis - Technical Fundamentals Whitepaper V02.pdf -------------------------------------------------------------------------------- /docs/Metis Whitepaper v3.3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MetisProtocol/metis/c26c47b6afee6eb7c98eaa433c6c5509c17ff626/docs/Metis Whitepaper v3.3.pdf -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | # The response structure 2 | The response structure is 3 | ```json 4 | { 5 | "code": 0, 6 | "data": "", 7 | "msg": "" 8 | } 9 | ``` 10 | The response is ok when the code is 200, otherwise it is wrong, you can handle the error code using the [Error code table] 11 | 12 | 13 | # API list 14 | ## OAuth2 get access token by code 15 | /api/v1/oauth2/access_token?app_id=${}&code=${}&app_key={} GET 16 | ```json 17 | { 18 | "access_token": "", 19 | "refresh_token": "", 20 | "expires_in": 1800 21 | } 22 | ``` 23 | 24 | ## OAuth2 refresh token 25 | /api/v1/oauth2/access_token?app_id=${}&refresh_token=${} GET 26 | ```json 27 | { 28 | "access_token": "", 29 | "refresh_token": "", 30 | "expires_in": 1800 31 | } 32 | ``` 33 | 34 | ## Send transaction 35 | /api/v1/oauth2/send_tx POST 36 | 37 | Request params with access token on http header named Access-Token 38 | 39 | ```json 40 | { 41 | "chainid": "", 42 | "domain": "", 43 | "function": "", 44 | "args": [] 45 | } 46 | ``` 47 | 48 | response when function is view or pure 49 | ```json 50 | { 51 | "eth_address": "", 52 | "contract_address": "", 53 | "nonce": 0, 54 | "chainid": "", 55 | "domain": "", 56 | "function": "", 57 | "args": [], 58 | "result": "", 59 | "data": "ok", 60 | "act": "SUCCESS" 61 | } 62 | ``` 63 | 64 | response when function is others 65 | ```json 66 | { 67 | "eth_address": "", 68 | "contract_address": "", 69 | "nonce": 0, 70 | "chainid": "", 71 | "domain": "", 72 | "function": "", 73 | "args": [], 74 | "gas": "0", 75 | "gas_price": "0 wei", 76 | "fee": "0 MET", 77 | "data": "ok", 78 | "act": "SIGN" 79 | } 80 | ``` 81 | 82 | ## Send transaction by owner 83 | /api/v1/oauth2/send_tx_owner POST 84 | 85 | http header 86 | 87 | | Param | 88 | | -------- | 89 | | appid | 90 | | appkey | 91 | | username | 92 | 93 | Request params 94 | ```json 95 | { 96 | "chainid": "", 97 | "domain": "", 98 | "function": "", 99 | "args": [] 100 | } 101 | ``` 102 | 103 | response when function is view or pure 104 | ```json 105 | { 106 | "tx": "0x00", 107 | "chainid": "", 108 | "domain": "", 109 | "result": "", 110 | "act": "CREATE" 111 | } 112 | ``` 113 | 114 | 115 | ## Query transaction 116 | /api/v1/oauth2/query_tx POST 117 | 118 | Request params with access token on http header named Access-Token 119 | ```json 120 | { 121 | "chainid": "", 122 | "tx": "" 123 | } 124 | ``` 125 | 126 | response 127 | ```json 128 | { 129 | "tx": "", 130 | "status": "IN_PROGRESS FAILED SUCCEED", 131 | "chainid": "", 132 | "domain": "", 133 | "data": [], 134 | "act": "SUCCESS" 135 | } 136 | ``` 137 | 138 | 139 | # Error code table 140 | 141 | | Code | Meaning | 142 | | -------- | -----: | 143 | | 20001 | App id is empty | 144 | | 20002 | App key is empty | 145 | | 20003 | App not existed | 146 | | 20004 | Code is empty | 147 | | 20005 | Code not existed | 148 | | 20006 | Refresh token is empty | 149 | | 20007 | Refresh token not existed | 150 | | 20008 | Access token is empty | 151 | | 20009 | Access token not existed | 152 | | 20010 | User not existed | 153 | | 20011 | ChainId is empty | 154 | | 20012 | Domain is empty | 155 | | 20013 | Domain not existed | 156 | | 20014 | Function is empty | 157 | | 20015 | Args not valid | 158 | | 20016 | Function not existed | 159 | | 20017 | Contract execute error | 160 | | 20018 | User balance not enough | 161 | | 20019 | Connect to chain rpc error | 162 | | 20020 | Tx is empty | 163 | | 20021 | Transaction does not existed | 164 | | 20022 | Domain abi is empty | 165 | 👋 166 | -------------------------------------------------------------------------------- /licenses/ABDK_LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, [ABDK Consulting](https://abdk.consulting/) 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 3. All advertising materials mentioning features or use of this software must 13 | display the following acknowledgement: This product includes software 14 | developed by ABDK Consulting. 15 | 4. Neither the name of ABDK Consulting nor the names of its contributors may be 16 | used to endorse or promote products derived from this software without 17 | specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY ABDK CONSULTING ''AS IS'' AND ANY EXPRESS OR 20 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 21 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 22 | SHALL ABDK CONSULTING BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 24 | OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 27 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 28 | OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /migrations/2_deploy_mis_token.js: -------------------------------------------------------------------------------- 1 | const MIS = artifacts.require("MToken"); 2 | //const METIS = artifacts.require("MetisToken"); 3 | const MULTI = artifacts.require("MultiSigMinter"); 4 | require('@openzeppelin/test-helpers/configure')({ provider: web3.currentProvider, environment: 'truffle' }); 5 | 6 | const { singletons } = require('@openzeppelin/test-helpers'); 7 | const BN = require('bignumber.js'); 8 | 9 | module.exports = async function(deployer, network, accounts) { 10 | if (network === 'development') { 11 | // In a test environment an ERC777 token requires deploying an ERC1820 registry 12 | //await singletons.ERC1820Registry(accounts[0]); 13 | } 14 | 15 | if (network === 'development') { 16 | await deployer.deploy(MIS, [], new BN("500000e18")); 17 | const token = await MIS.deployed(); 18 | //const token2 = await METIS.deployed(0, [accounts[0], accounts[1]], [accounts[0], accounts[1]],[accounts[0]]); 19 | await deployer.deploy(MULTI, [accounts[0], accounts[1]],token.address); 20 | const minter = await MULTI.deployed(); 21 | await token.addMinter(minter.address); 22 | } else { 23 | await deployer.deploy(MIS, [], new BN("10000000e18")); 24 | //const token = await MIS.at('0x9E32b13ce7f2E80A01932B42553652E053D6ed8e'); 25 | const token = await MIS.deployed(); 26 | await deployer.deploy(MULTI, ['0xFA30D7D32288C2F27cD5a099dB7507B085b36071','0xAbe26fAeE77419897090bC4c5A112D463443a662'],token.address); 27 | const minter = await MULTI.deployed(); 28 | await token.addMinter(minter.address); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /migrations/3_deploy_multi_sig.js: -------------------------------------------------------------------------------- 1 | const BigNumber = require('bignumber.js'); 2 | const MultisigWalletWithDailyLimit = artifacts.require('MultiSigWalletWithDailyLimit.sol') 3 | const MultisigWalletWithoutDailyLimit = artifacts.require('MultiSigWallet.sol') 4 | 5 | module.exports = async function(deployer, network, accounts) { 6 | if (network === 'development') { 7 | deployer.deploy(MultisigWalletWithoutDailyLimit, [accounts[0], accounts[1]], 2); 8 | } else { 9 | deployer.deploy(MultisigWalletWithoutDailyLimit, ['0xFA30D7D32288C2F27cD5a099dB7507B085b36071','0xAbe26fAeE77419897090bC4c5A112D463443a662'], 2); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /migrations/4_deploy_crowd_contract.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const csv = require('csv-parser'); 3 | const MIS = artifacts.require("MToken"); 4 | const MULTI = artifacts.require("MultiSigMinter"); 5 | const CROWD = artifacts.require("Crowd"); 6 | require('@openzeppelin/test-helpers/configure')({ provider: web3.currentProvider, environment: 'truffle' }); 7 | 8 | const { singletons } = require('@openzeppelin/test-helpers'); 9 | 10 | const BN = require('bignumber.js'); 11 | total=new BN("100e18"); 12 | 13 | module.exports = async function(deployer, network, accounts) { 14 | const token = await MIS.deployed(); 15 | await deployer.deploy(CROWD,token.address); 16 | if (network === 'development') { 17 | let multi = await MULTI.deployed(); 18 | let crowd = await CROWD.deployed(); 19 | let result = await multi.proposeMint(crowd.address, total, { from: accounts[0]}); 20 | let pos = result.logs[0].args.proposalNo; 21 | await multi.signMint(pos, {from: accounts[0]}); 22 | await multi.signMint(pos, {from: accounts[1]}); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /migrations/5_deploy_NFT.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const csv = require('csv-parser'); 3 | const NFT = artifacts.require("MetisNFT"); 4 | require('@openzeppelin/test-helpers/configure')({ provider: web3.currentProvider, environment: 'truffle' }); 5 | 6 | const { singletons } = require('@openzeppelin/test-helpers'); 7 | 8 | const BN = require('bignumber.js'); 9 | 10 | module.exports = async function(deployer, network, accounts) { 11 | await deployer.deploy(NFT); 12 | }; 13 | -------------------------------------------------------------------------------- /migrations/6_deployCachier.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const csv = require('csv-parser'); 3 | const CASHIER = artifacts.require("Cashier"); 4 | const MT = artifacts.require("MToken"); 5 | require('@openzeppelin/test-helpers/configure')({ provider: web3.currentProvider, environment: 'truffle' }); 6 | 7 | const { singletons } = require('@openzeppelin/test-helpers'); 8 | 9 | const BN = require('bignumber.js'); 10 | 11 | module.exports = async function(deployer, network, accounts) { 12 | await deployer.deploy(CASHIER, MT.address); 13 | }; 14 | -------------------------------------------------------------------------------- /migrations/7_deployVault.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const csv = require('csv-parser'); 3 | const V = artifacts.require("TokenVault"); 4 | const MT = artifacts.require("MToken"); 5 | require('@openzeppelin/test-helpers/configure')({ provider: web3.currentProvider, environment: 'truffle' }); 6 | 7 | const { singletons } = require('@openzeppelin/test-helpers'); 8 | 9 | const BN = require('bignumber.js'); 10 | 11 | module.exports = async function(deployer, network, accounts) { 12 | await deployer.deploy(V, MT.address); 13 | }; 14 | -------------------------------------------------------------------------------- /migrations/8_deployComVault.js: -------------------------------------------------------------------------------- 1 | //const fs = require('fs'); 2 | //const csv = require('csv-parser'); 3 | const C = artifacts.require("ComVault"); 4 | const MT = artifacts.require("MToken"); 5 | 6 | require('@openzeppelin/test-helpers/configure')({ provider: web3.currentProvider, environment: 'truffle' }); 7 | 8 | const { singletons } = require('@openzeppelin/test-helpers'); 9 | 10 | const BN = require('bignumber.js'); 11 | 12 | module.exports = async function(deployer, network, accounts) { 13 | await deployer.deploy(C, MT.address); 14 | }; 15 | -------------------------------------------------------------------------------- /networks.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | development: { 4 | protocol: 'http', 5 | host: 'localhost', 6 | port: 7545, 7 | gas: 5000000, 8 | gasPrice: 5e9, 9 | networkId: '*', 10 | }, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "metis", 3 | "version": "1.0.0", 4 | "description": "metis project", 5 | "main": "index.js", 6 | "dependencies": { 7 | "@eth-optimism/solc": "0.5.16-alpha.7", 8 | "@metis.io/solc": "^0.6.12-alpha.2", 9 | "@openzeppelin/contracts": "^2.5.1", 10 | "@openzeppelin/test-helpers": "^0.5.10", 11 | "@umaprotocol/truffle-ledger-provider": "^1.0.5", 12 | "bignumber": "^1.1.0", 13 | "csv-parser": "^3.0.0", 14 | "dotenv": "^8.2.0", 15 | "regenerator-runtime": "^0.13.7", 16 | "web3": "^1.2.11", 17 | "web3-eth-contract": "^1.2.11" 18 | }, 19 | "devDependencies": { 20 | "truffle-hdwallet-provider": "^1.0.17" 21 | }, 22 | "scripts": { 23 | "test": "echo \"Error: no test specified\" && exit 1", 24 | "env": "npx ganache-cli --deterministic & > /dev/null" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/bossyuansu/metis.git" 29 | }, 30 | "author": "Yuan Su", 31 | "license": "GPL-3.0-or-later", 32 | "bugs": { 33 | "url": "https://github.com/bossyuansu/metis/issues" 34 | }, 35 | "homepage": "https://github.com/bossyuansu/metis#readme" 36 | } 37 | -------------------------------------------------------------------------------- /scripts/getmapslot.js: -------------------------------------------------------------------------------- 1 | // Contracts 2 | const Vault = artifacts.require("TokenVault") 3 | const fs = require('fs'); 4 | // 5 | // Utils 6 | const ether = (n) => { 7 | return new web3.utils.BN( 8 | web3.utils.toWei(n.toString(), 'ether') 9 | ) 10 | } 11 | 12 | const standardizeInput = input => 13 | web3.utils.toHex(input).replace('0x', '').padStart(64, '0') 14 | 15 | const getMappingSlot = (mappingSlot, key) => { 16 | const mappingSlotPadded = standardizeInput(mappingSlot) 17 | const keyPadded = standardizeInput(key) 18 | const slot = web3.utils.sha3(keyPadded.concat(mappingSlotPadded), { 19 | encoding: 'hex' 20 | }) 21 | 22 | return slot 23 | } 24 | 25 | const getMappingStorage = async (address, mappingSlot, key) => { 26 | const mappingKeySlot = getMappingSlot(mappingSlot.toString(), key) 27 | const complexStorage = await web3.eth.getStorageAt(address, mappingKeySlot) 28 | return complexStorage 29 | } 30 | module.exports = async function(callback) { 31 | try { 32 | // Fetch accounts from wallet - these are unlocked 33 | console.log(getMappingSlot(1, 'OVM_L1CrossDomainMessenger')); 34 | 35 | } 36 | catch(error) { 37 | console.log(error) 38 | } 39 | 40 | callback() 41 | } 42 | 43 | -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | // Contracts 2 | // 3 | // Utils 4 | const ether = (n) => { 5 | return new web3.utils.BN( 6 | web3.utils.toWei(n.toString(), 'ether') 7 | ) 8 | } 9 | 10 | module.exports = async function(callback) { 11 | try { 12 | // Fetch accounts from wallet - these are unlocked 13 | 14 | for (i = 0; i < 50; i++) { 15 | const account = await web3.eth.accounts.create(); 16 | console.log(account.address + "," + account.privateKey); 17 | } 18 | } 19 | catch(error) { 20 | console.log(error) 21 | } 22 | 23 | callback() 24 | } 25 | 26 | -------------------------------------------------------------------------------- /scripts/vaultnew.js: -------------------------------------------------------------------------------- 1 | // Contracts 2 | const Vault = artifacts.require("TokenVault") 3 | // 4 | // Utils 5 | const ether = (n) => { 6 | return new web3.utils.BN( 7 | web3.utils.toWei(n.toString(), 'ether') 8 | ) 9 | } 10 | 11 | module.exports = async function(callback) { 12 | try { 13 | 14 | // Fetch the deployed exchange 15 | const vault = await Vault.deployed() 16 | 17 | await vault.init1(); 18 | await vault.init2(); 19 | 20 | } 21 | catch(error) { 22 | console.log(error) 23 | } 24 | 25 | callback() 26 | } 27 | 28 | -------------------------------------------------------------------------------- /test/comvault.js: -------------------------------------------------------------------------------- 1 | //const { singletons, BN, expectEvent } = require('@openzeppelin/test-helpers'); 2 | 3 | const MIS = artifacts.require("MToken"); 4 | const MULTI = artifacts.require("MultiSigMinter"); 5 | const VAULT = artifacts.require("ComVault"); 6 | 7 | const PREFIX = "Returned error: VM Exception while processing transaction: "; 8 | 9 | async function tryCatch(promise, message) { 10 | try { 11 | await promise; 12 | throw null; 13 | } 14 | catch (error) { 15 | assert(error, "Expected an error but did not get one"); 16 | assert(error.message.startsWith(PREFIX + message), "Expected an error starting with '" + PREFIX + message + "' but got '" + error.message + "' instead"); 17 | } 18 | }; 19 | 20 | catchRevert = async function(promise) {await tryCatch(promise, "revert")}; 21 | 22 | contract("Vault Test", async accounts => { 23 | it("mint", async() => { 24 | let token = await MIS.deployed(); 25 | let vault = await VAULT.deployed(); 26 | await token.addMinter(accounts[0]); 27 | await token.mint(vault.address, 100000); 28 | await token.mint(accounts[2], 100000); 29 | assert.equal(await token.balanceOf.call(vault.address), 100000, "Vault should have 100000 balance"); 30 | assert.equal(await token.balanceOf.call(accounts[2]), 100000, "account 2 should have 100000 balance"); 31 | }); 32 | it("security", async() => { 33 | let vault = await VAULT.deployed(); 34 | await catchRevert(vault.addNew(accounts[2], 10, 10, {from: accounts[2]})); 35 | await catchRevert(vault.addNewBatch([accounts[2]], [10], [10],{from: accounts[2]})); 36 | await catchRevert(vault.setTge(0,{from: accounts[2]})); 37 | await catchRevert(vault.withdrawFund(accounts[0],{from: accounts[2]})); 38 | 39 | }); 40 | it("addNew", async() => { 41 | let token = await MIS.deployed(); 42 | let vault = await VAULT.deployed(); 43 | 44 | await vault.addNew(accounts[2], 100000, 100000); 45 | let a = await vault.arrangements_.call(accounts[2], {from:accounts[2]}); 46 | assert.equal(a.targetAmount, 100000); 47 | assert.equal(a.amount, 0); 48 | assert.equal(a.aStatus, 1); 49 | assert.equal(a.metisAmount, 100000); 50 | assert.equal(a.metisPaid, 0); 51 | }); 52 | 53 | it("fund", async() => { 54 | let token = await MIS.deployed(); 55 | let vault = await VAULT.deployed(); 56 | 57 | assert.equal(await token.balanceOf.call(accounts[2]), 100000, "Account 1 should still have 0 balance"); 58 | await token.approve(vault.address, 100000, {from:accounts[2]}); 59 | await vault.fund(accounts[2], 100000, {from:accounts[2]}); 60 | assert.equal(await token.balanceOf.call(accounts[2]), 0, "Account 2 should still have 0 balance"); 61 | 62 | let a = await vault.arrangements_.call(accounts[2], {from:accounts[2]}); 63 | assert.equal(a.targetAmount, 100000); 64 | assert.equal(a.amount, 100000); 65 | assert.equal(a.aStatus, 2); 66 | assert.equal(a.metisAmount, 100000); 67 | assert.equal(a.metisPaid, 0); 68 | }); 69 | 70 | it("claim", async() => { 71 | let token = await MIS.deployed(); 72 | let vault = await VAULT.deployed(); 73 | 74 | let date = (new Date()).getTime(); 75 | let timestamp = Math.floor(date / 1000); 76 | await vault.setTge(timestamp); 77 | 78 | assert.equal(await token.balanceOf.call(accounts[2]), 0, "Account 2 should still have 0 balance"); 79 | 80 | await vault.claim({from: accounts[2]}); 81 | assert.equal(await token.balanceOf.call(accounts[2]), 8333, "Account 2 should have 8333 balance"); 82 | 83 | await catchRevert(vault.claim({from: accounts[3]})); 84 | }); 85 | 86 | }); 87 | -------------------------------------------------------------------------------- /test/crowd.js: -------------------------------------------------------------------------------- 1 | //const { singletons, BN, expectEvent } = require('@openzeppelin/test-helpers'); 2 | 3 | const MIS = artifacts.require("MToken"); 4 | const MULTI = artifacts.require("MultiSigMinter"); 5 | const Web3 = require("web3"); 6 | const web3 = new Web3(); 7 | const BN = require('bignumber.js'); 8 | total=new BN("100e18"); 9 | 10 | 11 | const PREFIX = "Returned error: VM Exception while processing transaction: "; 12 | 13 | async function tryCatch(promise, message) { 14 | try { 15 | await promise; 16 | throw null; 17 | } 18 | catch (error) { 19 | assert(error, "Expected an error but did not get one"); 20 | assert(error.message.startsWith(PREFIX + message), "Expected an error starting with '" + PREFIX + message + "' but got '" + error.message + "' instead"); 21 | } 22 | }; 23 | 24 | catchRevert = async function(promise) {await tryCatch(promise, "revert")}; 25 | 26 | contract("MToken Test", async accounts => { 27 | it("create accounts", async() => { 28 | 29 | let token = await MIS.deployed(); 30 | await token.addMinter(accounts[0]); 31 | 32 | total = 2000; 33 | tamount = 0; 34 | let rnlist = [] 35 | 36 | for (i = 0; i < total; ++i) { 37 | rn = Math.random(); 38 | tamount += rn; 39 | rnlist.push(rn); 40 | } 41 | for (i = 0; i < total; ++i) { 42 | account = web3.eth.accounts.create(); 43 | console.log(account.address, account.privateKey); 44 | } 45 | 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/mis.js: -------------------------------------------------------------------------------- 1 | //const { singletons, BN, expectEvent } = require('@openzeppelin/test-helpers'); 2 | 3 | const MIS = artifacts.require("MToken"); 4 | const MULTI = artifacts.require("MultiSigMinter"); 5 | 6 | const PREFIX = "Returned error: VM Exception while processing transaction: "; 7 | 8 | async function tryCatch(promise, message) { 9 | try { 10 | await promise; 11 | throw null; 12 | } 13 | catch (error) { 14 | assert(error, "Expected an error but did not get one"); 15 | assert(error.message.startsWith(PREFIX + message), "Expected an error starting with '" + PREFIX + message + "' but got '" + error.message + "' instead"); 16 | } 17 | }; 18 | 19 | catchRevert = async function(promise) {await tryCatch(promise, "revert")}; 20 | 21 | contract("MToken Test", async accounts => { 22 | it("mint", async() => { 23 | let token = await MIS.deployed(); 24 | let multi = await MULTI.deployed(); 25 | await multi.addMinter(accounts[2]); 26 | let result = await multi.proposeMint(accounts[1], 100000, { from: accounts[0]}); 27 | 28 | let pos = result.logs[0].args.proposalNo; 29 | assert.equal(await token.balanceOf.call(accounts[1]), 0, "Account 1 should still have 0 balance"); 30 | await multi.signMint(pos, {from: accounts[0]}); 31 | assert.equal(await token.balanceOf.call(accounts[1]), 0, "Account 1 should still have 0 balance"); 32 | await multi.signMint(pos, {from: accounts[1]}); 33 | assert.equal(await token.balanceOf.call(accounts[1]), 0, "Account 1 should still have 0 balance"); 34 | await multi.signMint(pos, {from: accounts[2]}); 35 | assert.equal(await token.balanceOf.call(accounts[1]), 100000, "Account 1 should still have 100000 balance"); 36 | }); 37 | it("mintbymetis", async() => { 38 | let token = await MIS.deployed(); 39 | await token.addMinter(accounts[0]); 40 | 41 | assert.equal(await token.balanceOf.call(accounts[2]), 0, "Account 1 should still have 0 balance"); 42 | await token.mint(accounts[2], 100000, {from: accounts[0]}); 43 | assert.equal(await token.balanceOf.call(accounts[2]), 100000, "Account 1 should still have 100000 balance"); 44 | await catchRevert(token.mint(accounts[2], 1000000, {from: accounts[0]})); 45 | await token.removeMinter(accounts[0]); 46 | await catchRevert(token.mint(accounts[2], 100000, {from: accounts[0]})); 47 | }); 48 | 49 | it("mint2", async() => { 50 | let token = await MIS.deployed(); 51 | let multi = await MULTI.deployed(); 52 | await multi.removeMinter(accounts[2]); 53 | await catchRevert(multi.proposeMint(accounts[1], 100000, { from: accounts[2]})); 54 | await catchRevert(multi.signMint(0, {from: accounts[2]})); 55 | }); 56 | 57 | it("burn", async() => { 58 | let token = await MIS.deployed(); 59 | let multi = await MULTI.deployed(); 60 | let result = await multi.proposeBurn(accounts[1], 100000, { from: accounts[0]}); 61 | let pos = result.logs[0].args.proposalNo; 62 | assert.equal(await token.balanceOf.call(accounts[1]), 100000, "Account 1 should still have 100000 balance"); 63 | await multi.signBurn(pos, {from: accounts[0]}); 64 | assert.equal(await token.balanceOf.call(accounts[1]), 100000, "Account 1 should still have 100000 balance"); 65 | await multi.signBurn(pos, {from: accounts[1]}); 66 | assert.equal(await token.balanceOf.call(accounts[1]), 0, "Account 1 should still have 0 balance"); 67 | }); 68 | 69 | it("burn2", async() => { 70 | let token = await MIS.deployed(); 71 | let multi = await MULTI.deployed(); 72 | await catchRevert(multi.proposeBurn(accounts[1], 100000, { from: accounts[2]})); 73 | await catchRevert(multi.signBurn(0, {from: accounts[2]})); 74 | }); 75 | 76 | 77 | }); 78 | -------------------------------------------------------------------------------- /test/msc2.js: -------------------------------------------------------------------------------- 1 | //const { singletons, BN, expectEvent } = require('@openzeppelin/test-helpers'); 2 | 3 | const MSC = artifacts.require("MSC2"); 4 | const MIS = artifacts.require("MToken"); 5 | 6 | const PREFIX = "Returned error: VM Exception while processing transaction: "; 7 | 8 | async function tryCatch(promise, message) { 9 | try { 10 | await promise; 11 | throw null; 12 | } 13 | catch (error) { 14 | assert(error, "Expected an error but did not get one"); 15 | assert(error.message.startsWith(PREFIX + message), "Expected an error starting with '" + PREFIX + message + "' but got '" + error.message + "' instead"); 16 | } 17 | }; 18 | 19 | catchRevert = async function(promise) {await tryCatch(promise, "revert")}; 20 | 21 | contract("MSC Test", async accounts => { 22 | it("initial setup, fund distribution", async() => { 23 | let token = await MIS.deployed(); 24 | let m = await MSC.deployed(); 25 | await token.addMinter(accounts[0]); 26 | await token.mint(accounts[0], 100000); 27 | assert.equal(await token.balanceOf.call(accounts[0]), 100000, "account 2 should have 100000 balance"); 28 | 29 | }); 30 | 31 | it("call commit directly. ", async() => { 32 | let msc = await MSC.deployed(); 33 | let token = await MIS.deployed(); 34 | let initialBalance = await token.balanceOf.call(msc.address); 35 | assert.equal(initialBalance, 0); 36 | 37 | await token.approve(msc.address, 5000, {from : accounts[0]}); 38 | await msc.commit(5000, {from: accounts[0]}); 39 | 40 | let balance = await token.balanceOf.call(msc.address) 41 | assert.equal(balance, 5000); 42 | 43 | let {value, status} = await msc.parties(accounts[0]); 44 | assert.equal(value, 5000, "Account 0 should have pledged 5000"); 45 | assert.equal(status, 0, "Account 0 should still in pending"); 46 | let contractStatus = await msc.contractStatus(); 47 | assert.equal(contractStatus, 1, "Contract should still be in progress"); 48 | }); 49 | 50 | 51 | }); 52 | -------------------------------------------------------------------------------- /test/multisig.js: -------------------------------------------------------------------------------- 1 | //const { singletons, BN, expectEvent } = require('@openzeppelin/test-helpers'); 2 | 3 | const MIS = artifacts.require("MetisToken"); 4 | const MULTI = artifacts.require("MultiSig"); 5 | 6 | const PREFIX = "Returned error: VM Exception while processing transaction: "; 7 | 8 | async function tryCatch(promise, message) { 9 | try { 10 | await promise; 11 | throw null; 12 | } 13 | catch (error) { 14 | assert(error, "Expected an error but did not get one"); 15 | assert(error.message.startsWith(PREFIX + message), "Expected an error starting with '" + PREFIX + message + "' but got '" + error.message + "' instead"); 16 | } 17 | }; 18 | 19 | catchRevert = async function(promise) {await tryCatch(promise, "revert")}; 20 | 21 | contract("MultiSig Test", async accounts => { 22 | it("mint", async() => { 23 | let token = await MIS.deployed(); 24 | let multi = await MULTI.deployed(); 25 | let result = await token.proposeMint(multi.address, 100000, { from: accounts[0]}); 26 | let pos = result.logs[0].args.proposalNo; 27 | await token.signMint(pos, {from: accounts[0]}); 28 | await token.signMint(pos, {from: accounts[1]}); 29 | assert.equal(await token.balanceOf.call(multi.address), 100000, "Account should have 100000 balance"); 30 | }); 31 | 32 | it("propose", async() => { 33 | let token = await MIS.deployed(); 34 | let multi = await MULTI.deployed(); 35 | 36 | await multi.propose(accounts[1], 100000, web3.utils.toHex("test"), { from: accounts[0]}); 37 | 38 | assert.equal(await token.balanceOf.call(multi.address), 100000, "contract account should still have 100000 balance"); 39 | assert.equal(await token.balanceOf.call(accounts[1]), 0, "target account should still have 0 balance"); 40 | 41 | await multi.propose(accounts[1], 100000, web3.utils.toHex("test"), { from: accounts[1]}); 42 | assert.equal(await token.balanceOf.call(multi.address), 0, "contract account should have 0 balance"); 43 | assert.equal(await token.balanceOf.call(accounts[1]), 100000, "Account should have 100000 balance"); 44 | }); 45 | 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /test/nft.js: -------------------------------------------------------------------------------- 1 | //const { singletons, BN, expectEvent } = require('@openzeppelin/test-helpers'); 2 | 3 | const NFT = artifacts.require("MetisNFT"); 4 | 5 | const PREFIX = "Returned error: VM Exception while processing transaction: "; 6 | 7 | async function tryCatch(promise, message) { 8 | try { 9 | await promise; 10 | throw null; 11 | } 12 | catch (error) { 13 | assert(error, "Expected an error but did not get one"); 14 | assert(error.message.startsWith(PREFIX + message), "Expected an error starting with '" + PREFIX + message + "' but got '" + error.message + "' instead"); 15 | } 16 | }; 17 | 18 | catchRevert = async function(promise) {await tryCatch(promise, "revert")}; 19 | 20 | contract("MetisNFT Test", async accounts => { 21 | it("basic security", async() => { 22 | let token = await NFT.deployed(); 23 | await catchRevert(token.addMinter(accounts[2], {from: accounts[1]})); 24 | await catchRevert(token.awardItem(accounts[1],0, "a", "md5", {from: accounts[1]})); 25 | await catchRevert(token.awardItem(accounts[1],0, "a", "md5", {from: accounts[1]})); 26 | await catchRevert(token.mintWithTokenURI(accounts[1], "1", "a", {from: accounts[1]})); 27 | await catchRevert(token.burn("0", {from: accounts[1]})); 28 | await catchRevert(token.pause({from: accounts[1]})); 29 | await catchRevert(token.addMinter(accounts[1], {from: accounts[1]})); 30 | await catchRevert(token.setBaseURI("base", {from: accounts[1]})); 31 | 32 | }); 33 | it("mint", async() => { 34 | let token = await NFT.deployed(); 35 | await token.addMinter(accounts[2]); 36 | await token.setBaseURI("base/", {from: accounts[2]}); 37 | let result = await token.awardItem(accounts[1], 0, "uri", "md5", {from: accounts[2]}); 38 | let id = result.logs[0].args.tokenId; 39 | assert.equal(await token.ownerOf.call(id), accounts[1], "Account 1 should still have the token"); 40 | assert.equal(await token.quality(id), 0, "token should have quality 0"); 41 | assert.equal(await token.md5(id),"md5", "token md5 mismatch"); 42 | assert.equal(await token.tokenURI(id),"base/uri", "token uri mismatch"); 43 | }); 44 | 45 | }); 46 | -------------------------------------------------------------------------------- /test/vault.js: -------------------------------------------------------------------------------- 1 | //const { singletons, BN, expectEvent } = require('@openzeppelin/test-helpers'); 2 | 3 | const MIS = artifacts.require("MToken"); 4 | const MULTI = artifacts.require("MultiSigMinter"); 5 | const VAULT = artifacts.require("TokenVault"); 6 | 7 | const PREFIX = "Returned error: VM Exception while processing transaction: "; 8 | 9 | async function tryCatch(promise, message) { 10 | try { 11 | await promise; 12 | throw null; 13 | } 14 | catch (error) { 15 | assert(error, "Expected an error but did not get one"); 16 | assert(error.message.startsWith(PREFIX + message), "Expected an error starting with '" + PREFIX + message + "' but got '" + error.message + "' instead"); 17 | } 18 | }; 19 | 20 | catchRevert = async function(promise) {await tryCatch(promise, "revert")}; 21 | 22 | contract("Vault Test", async accounts => { 23 | it("mint", async() => { 24 | let token = await MIS.deployed(); 25 | let multi = await MULTI.deployed(); 26 | let vault = await VAULT.deployed(); 27 | let result = await multi.proposeMint(vault.address, 100000, { from: accounts[0]}); 28 | 29 | let pos = result.logs[0].args.proposalNo; 30 | await catchRevert(multi.signMint(0, {from: accounts[2]})); 31 | 32 | assert.equal(await token.balanceOf.call(vault.address), 0, "Account 1 should still have 0 balance"); 33 | await multi.signMint(pos, {from: accounts[0]}); 34 | assert.equal(await token.balanceOf.call(vault.address), 0, "Account 1 should still have 0 balance"); 35 | await multi.signMint(pos, {from: accounts[1]}); 36 | assert.equal(await token.balanceOf.call(vault.address), 100000, "Vault should have 100000 balance"); 37 | }); 38 | it("security", async() => { 39 | let vault = await VAULT.deployed(); 40 | await catchRevert(vault.addNew(accounts[2], 10, 0, {from: accounts[2]})); 41 | await catchRevert(vault.addNewPending(accounts[2], 10,{from: accounts[2]})); 42 | await catchRevert(vault.setDate(accounts[2], 0, 0, {from: accounts[2]})); 43 | }); 44 | it("addNew", async() => { 45 | let token = await MIS.deployed(); 46 | let vault = await VAULT.deployed(); 47 | 48 | assert.equal(await token.balanceOf.call(accounts[2]), 0, "Account 1 should still have 0 balance"); 49 | let date = (new Date()).getTime(); 50 | let timestamp = Math.floor(date / 1000); 51 | await vault.addNew(accounts[2], 1000, timestamp); 52 | assert.equal(await vault.checkArrangement(0, {from:accounts[2]}), "Amount:1000 Available on TS " + timestamp + " ARRANGED"); 53 | 54 | await vault.addNew(accounts[2], 1000, timestamp + 1); 55 | assert.equal(await vault.checkArrangement(0, {from:accounts[2]}), "Amount:1000 Available on TS " + timestamp + " ARRANGED"); 56 | assert.equal(await vault.checkArrangement(1, {from:accounts[2]}), "Amount:1000 Available on TS " + (timestamp + 1) + " ARRANGED"); 57 | 58 | await vault.addNew(accounts[2], 1000, timestamp + 10000); 59 | assert.equal(await vault.checkArrangement(2, {from:accounts[2]}), "Amount:1000 Available on TS " + (timestamp + 10000) + " ARRANGED"); 60 | 61 | await vault.addNew(accounts[3], 1000, timestamp); 62 | assert.equal(await vault.checkArrangement(0, {from:accounts[3]}), "Amount:1000 Available on TS " + timestamp + " ARRANGED"); 63 | await vault.addNewPending(accounts[3], 1000); 64 | assert.equal(await vault.checkArrangement(1, {from:accounts[3]}), "Amount:1000 Available on TS 0 LOCKED"); 65 | await vault.setDate(accounts[3], 1, timestamp + 10000); 66 | assert.equal(await vault.checkArrangement(1, {from:accounts[3]}), "Amount:1000 Available on TS " + (timestamp + 10000) + " ARRANGED"); 67 | 68 | }); 69 | 70 | it("claim", async() => { 71 | let token = await MIS.deployed(); 72 | let vault = await VAULT.deployed(); 73 | 74 | assert.equal(await token.balanceOf.call(accounts[2]), 0, "Account 2 should still have 0 balance"); 75 | assert.equal(await token.balanceOf.call(accounts[3]), 0, "Account 3 should still have 0 balance"); 76 | 77 | await vault.claim({from: accounts[4]}); 78 | assert.equal(await token.balanceOf.call(accounts[4]), 0, "Account 3 should still have 0 balance"); 79 | 80 | await vault.claim({from: accounts[2]}); 81 | assert.equal(await token.balanceOf.call(accounts[2]), 2000, "Accounts 2 should have 2000 balance"); 82 | 83 | await vault.claim({from: accounts[3]}); 84 | assert.equal(await token.balanceOf.call(accounts[3]), 1000, "Accounts 3 should have 1000 balance"); 85 | 86 | }); 87 | 88 | it("addNewPendings", async() => { 89 | let token = await MIS.deployed(); 90 | let vault = await VAULT.deployed(); 91 | 92 | assert.equal(await token.balanceOf.call(accounts[4]), 0, "Account 1 should still have 0 balance"); 93 | let date = (new Date()).getTime(); 94 | let timestamp = Math.floor(date / 1000); 95 | await vault.addNewPendings([accounts[4],accounts[4],accounts[5]], [1000,20,30]); 96 | 97 | assert.equal(await vault.checkArrangement(0, {from:accounts[4]}), "Amount:1000 Available on TS 0 LOCKED"); 98 | assert.equal(await vault.checkArrangement(1, {from:accounts[4]}), "Amount:20 Available on TS 0 LOCKED"); 99 | 100 | assert.equal(await vault.checkArrangement(0, {from:accounts[5]}), "Amount:30 Available on TS 0 LOCKED"); 101 | 102 | await vault.addNewPendings([accounts[4],accounts[4],accounts[5]], [1000,20,30]); 103 | 104 | assert.equal(await vault.checkArrangement(2, {from:accounts[4]}), "Amount:1000 Available on TS 0 LOCKED"); 105 | assert.equal(await vault.checkArrangement(3, {from:accounts[4]}), "Amount:20 Available on TS 0 LOCKED"); 106 | assert.equal(await vault.checkArrangement(1, {from:accounts[5]}), "Amount:30 Available on TS 0 LOCKED"); 107 | 108 | await vault.setDates([accounts[4], accounts[5], accounts[4]], [1, 1, 0], [timestamp, timestamp, timestamp]); 109 | assert.equal(await vault.checkArrangement(0, {from:accounts[4]}), "Amount:1000 Available on TS " + (timestamp) + " ARRANGED"); 110 | assert.equal(await vault.checkArrangement(1, {from:accounts[4]}), "Amount:20 Available on TS " + (timestamp) + " ARRANGED"); 111 | assert.equal(await vault.checkArrangement(2, {from:accounts[4]}), "Amount:1000 Available on TS 0 LOCKED"); 112 | assert.equal(await vault.checkArrangement(3, {from:accounts[4]}), "Amount:20 Available on TS 0 LOCKED"); 113 | 114 | assert.equal(await vault.checkArrangement(0, {from:accounts[5]}), "Amount:30 Available on TS 0 LOCKED"); 115 | 116 | assert.equal(await vault.checkArrangement(1, {from:accounts[5]}), "Amount:30 Available on TS " + (timestamp) + " ARRANGED"); 117 | 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /tokenlist/toptoken.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Metis Top Tokens", 3 | "logoURI": "https://raw.githubusercontent.com/Netswap/tokens/master/assets/0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000/logo.png", 4 | "keywords": [ 5 | "metis", 6 | "ethereum", 7 | "andromeda" 8 | ], 9 | "version": { 10 | "major": 7, 11 | "minor": 0, 12 | "patch": 0 13 | }, 14 | "tokens": [ 15 | { 16 | "chainId": 1088, 17 | "address": "0xea32a96608495e54156ae48931a7c20f0dcc1a21", 18 | "decimals": 6, 19 | "name": "USDC Token", 20 | "symbol": "m.USDC", 21 | "logoURI": "https://raw.githubusercontent.com/MetisProtocol/metis-bridge-resources/master/tokens/USDC/logo.png" 22 | }, 23 | { 24 | "chainId": 1088, 25 | "address": "0xbb06dca3ae6887fabf931640f67cab3e3a16f4dc", 26 | "decimals": 6, 27 | "name": "USDT Token", 28 | "symbol": "m.USDT", 29 | "logoURI": "https://raw.githubusercontent.com/MetisProtocol/metis-bridge-resources/master/tokens/USDT/logo.png" 30 | }, 31 | { 32 | "chainId": 1088, 33 | "address": "0x5ce34d9abe4bf239cbc08b89287c87f4cd6d80b7", 34 | "decimals": 18, 35 | "name": "WOW Token", 36 | "symbol": "m.WOW", 37 | "logoURI": "https://raw.githubusercontent.com/MetisProtocol/metis-bridge-resources/master/tokens/WOW/logo.png" 38 | }, 39 | { 40 | "chainId": 1088, 41 | "address": "0xf5f66d5daa89c090a7afa10e6c1553b2887a9a33", 42 | "decimals": 18, 43 | "name": "LINK Token", 44 | "symbol": "m.LINK", 45 | "logoURI": "https://raw.githubusercontent.com/MetisProtocol/metis-bridge-resources/master/tokens/LINK/logo.png" 46 | }, 47 | { 48 | "chainId": 1088, 49 | "address": "0x420000000000000000000000000000000000000a", 50 | "decimals": 18, 51 | "name": "Ether", 52 | "symbol": "WETH", 53 | "logoURI": "https://raw.githubusercontent.com/MetisProtocol/metis-bridge-resources/master/tokens/ETH/logo.png" 54 | }, 55 | { 56 | "chainId": 1088, 57 | "address": "0x68D97B7A961a5239B9F911DA8dEb57F6eF6e5e28", 58 | "decimals": 18, 59 | "name": "AAVE Token", 60 | "symbol": "AAVE", 61 | "logoURI": "https://raw.githubusercontent.com/MetisProtocol/metis-bridge-resources/master/tokens/AAVE/logo.png" 62 | }, 63 | { 64 | "chainId": 1088, 65 | "address": "0x87DD4a7Ad23B95cD9fF9C26B5cF325905CaF8663", 66 | "decimals": 18, 67 | "name": "Curve DAO Token", 68 | "symbol": "CRV", 69 | "logoURI": "https://raw.githubusercontent.com/MetisProtocol/metis-bridge-resources/master/tokens/CRV/logo.png" 70 | }, 71 | { 72 | "chainId": 1088, 73 | "address": "0x90fE084F877C65e1b577c7b2eA64B8D8dd1AB278", 74 | "decimals": 18, 75 | "name": "Netswap Token", 76 | "symbol": "NETT", 77 | "logoURI": "https://raw.githubusercontent.com/Netswap/tokens/master/assets/0x90fe084f877c65e1b577c7b2ea64b8d8dd1ab278/logo.png" 78 | }, 79 | { 80 | "chainId": 1088, 81 | "address": "0x445ea60f4ace3d8794369379e68fa0f76af06b02", 82 | "decimals": 18, 83 | "name": "Poly-Peg CAKE", 84 | "symbol": "CAKE", 85 | "logoURI": "https://raw.githubusercontent.com/Netswap/tokens/master/assets/0x445ea60f4ace3d8794369379e68fa0f76af06b02/logo.png" 86 | }, 87 | { 88 | "chainId": 1088, 89 | "address": "0x2692be44a6e38b698731fddf417d060f0d20a0cb", 90 | "decimals": 18, 91 | "name": "Poly-Peg BNB", 92 | "symbol": "BNB", 93 | "logoURI": "https://raw.githubusercontent.com/Netswap/tokens/master/assets/0x2692be44a6e38b698731fddf417d060f0d20a0cb/logo.png" 94 | }, 95 | { 96 | "chainId": 1088, 97 | "address": "0x12d84f1cfe870ca9c9df9785f8954341d7fbb249", 98 | "decimals": 18, 99 | "name": "Poly-Peg BUSD", 100 | "symbol": "BUSD", 101 | "logoURI": "https://raw.githubusercontent.com/Netswap/tokens/master/assets/0x12d84f1cfe870ca9c9df9785f8954341d7fbb249/logo.png" 102 | }, 103 | { 104 | "chainId": 1088, 105 | "address": "0xe253e0cea0cdd43d9628567d097052b33f98d611", 106 | "decimals": 18, 107 | "name": "Avalanche Token", 108 | "symbol": "rAVAX", 109 | "logoURI": "https://raw.githubusercontent.com/Netswap/tokens/master/assets/0xe253e0cea0cdd43d9628567d097052b33f98d611/logo.png" 110 | }, 111 | { 112 | "chainId": 1088, 113 | "address": "0x729de3792a5f810a2e7d7899da74de0d108e7e6b", 114 | "decimals": 18, 115 | "name": "Wrapped ONE", 116 | "symbol": "WONE", 117 | "logoURI": "https://raw.githubusercontent.com/Netswap/tokens/master/assets/0x729de3792a5f810a2e7d7899da74de0d108e7e6b/logo.png" 118 | }, 119 | { 120 | "chainId": 1088, 121 | "address": "0xa9109271abcf0c4106ab7366b4edb34405947eed", 122 | "decimals": 18, 123 | "name": "Fantom Token", 124 | "symbol": "rFTM", 125 | "logoURI": "https://raw.githubusercontent.com/Netswap/tokens/master/assets/0xa9109271abcf0c4106ab7366b4edb34405947eed/logo.png" 126 | }, 127 | { 128 | "chainId": 1088, 129 | "address": "0xfe282Af5f9eB59C30A3f78789EEfFA704188bdD4", 130 | "decimals": 18, 131 | "name": "Relay Token", 132 | "symbol": "RELAY", 133 | "logoURI": "https://raw.githubusercontent.com/Netswap/tokens/master/assets/0xfe282af5f9eb59c30a3f78789eeffa704188bdd4/logo.png" 134 | }, 135 | { 136 | "chainId": 1088, 137 | "address": "0x4b9d2923d875edf43980bf5ddddede3fb20fc742", 138 | "decimals": 18, 139 | "name": "Matic Token", 140 | "symbol": "MATIC", 141 | "logoURI": "https://raw.githubusercontent.com/Netswap/tokens/master/assets/0x4b9d2923d875edf43980bf5ddddede3fb20fc742/logo.png" 142 | }, 143 | { 144 | "chainId": 1088, 145 | "address": "0xE9Aa15a067b010a4078909baDE54F0C6aBcBB852", 146 | "decimals": 18, 147 | "name": "Metauce", 148 | "symbol": "MINES", 149 | "logoURI": "https://raw.githubusercontent.com/Netswap/tokens/master/assets/0xe9aa15a067b010a4078909bade54f0c6abcbb852/logo.png" 150 | }, 151 | { 152 | "chainId": 1088, 153 | "address": "0xa5B55ab1dAF0F8e1EFc0eB1931a957fd89B918f4", 154 | "decimals": 8, 155 | "name": "Wrapped BTC", 156 | "symbol": "WBTC", 157 | "logoURI": "https://raw.githubusercontent.com/Netswap/tokens/master/assets/0xa5b55ab1daf0f8e1efc0eb1931a957fd89b918f4/logo.png" 158 | }, 159 | { 160 | "chainId": 1088, 161 | "address": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000", 162 | "decimals": 18, 163 | "name": "Metis Token", 164 | "symbol": "METIS", 165 | "logoURI": "https://raw.githubusercontent.com/Netswap/tokens/master/assets/0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000/logo.png" 166 | } 167 | ], 168 | "timestamp": "2022-02-19T15:00:00+00:00" 169 | } 170 | -------------------------------------------------------------------------------- /truffle-config-mvm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * truffleframework.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | const HDWalletProvider = require('truffle-hdwallet-provider'); 22 | require('dotenv').config(); 23 | const infuraKey = process.env.INFURAKEY; 24 | const l2url = process.env.LAYER2; 25 | 26 | const fs = require('fs'); 27 | const mnemonic = fs.readFileSync(".secret").toString().trim(); 28 | const gasapi = "https://api.etherscan.io/api?module=gastracker&action=gasoracle&apikey=YourApiKeyToken"; 29 | 30 | 31 | module.exports = { 32 | /** 33 | * Networks define how you connect to your ethereum client and let you set the 34 | * defaults web3 uses to send transactions. If you don't specify one truffle 35 | * will spin up a development blockchain for you on port 9545 when you 36 | * run `develop` or `test`. You can ask a truffle command to use a specific 37 | * network from the command line, e.g 38 | * 39 | * $ truffle test --network 40 | */ 41 | contracts_build_directory: './build/contracts/mvm', 42 | 43 | networks: { 44 | // Useful for testing. The `development` name is special - truffle uses it by default 45 | // if it's defined here and no other network is specified at the command line. 46 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 47 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 48 | // options below to some value. 49 | // 50 | development: { 51 | host: "192.168.68.58", // Localhost (default: none) 52 | port: 7545, // Standard Ethereum port (default: none) 53 | network_id: "*", // Any network (default: none) 54 | networkCheckTimeout: 1000 55 | }, 56 | layer2: { 57 | host: l2url, // Localhost (default: none) 58 | port: 8545, // Standard Ethereum port (default: none) 59 | network_id: "*", // Any network (default: none) 60 | networkCheckTimeout: 1000, 61 | gasPrice: 0 62 | }, 63 | 64 | main: { 65 | provider: () => new HDWalletProvider(mnemonic, "https://mainnet.infura.io/v3/" + infuraKey), 66 | network_id: 1, // mainnet 67 | gasPrice: 115000, 68 | confirmations: 2, // # of confs to wait between deployments. (default: 0) 69 | timeoutBlocks: 1000, // # of blocks before a deployment times out (minimum/default: 50) 70 | networkCheckTimeout: 5000, 71 | skipDryRun: false // Skip dry run before migrations? (default: false for public nets ) 72 | }, 73 | // Another network with more advanced options... 74 | // advanced: { 75 | // port: 8777, // Custom port 76 | // network_id: 1342, // Custom network 77 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 78 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 79 | // from:
, // Account to send txs from (default: accounts[0]) 80 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 81 | // }, 82 | 83 | // Useful for deploying to a public network. 84 | // NB: It's important to wrap the provider as a function. 85 | ropsten: { 86 | provider: () => new HDWalletProvider(mnemonic, "https://ropsten.infura.io/v3/" + infuraKey), 87 | network_id: 3, // Ropsten's id 88 | //gas: 5500000, // Ropsten has a lower block limit than mainnet 89 | confirmations: 2, // # of confs to wait between deployments. (default: 0) 90 | timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 91 | skipDryRun: false // Skip dry run before migrations? (default: false for public nets ) 92 | }, 93 | 94 | // Useful for private networks 95 | // private: { 96 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 97 | // network_id: 2111, // This network is yours, in the cloud. 98 | // production: true // Treats this network as if it was a public net. (default: false) 99 | // } 100 | }, 101 | 102 | // Set default mocha options here, use special reporters etc. 103 | mocha: { 104 | // timeout: 100000 105 | }, 106 | 107 | // Configure your compilers 108 | compilers: { 109 | solc: { 110 | // Add path to the optimism solc fork 111 | version: "node_modules/@metis.io/solc", 112 | settings: { // See the solidity docs for advice about optimization and evmVersion 113 | optimizer: { 114 | enabled: true, 115 | runs: 1 116 | }, 117 | } 118 | } 119 | }, 120 | } 121 | -------------------------------------------------------------------------------- /truffle-config-normal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * truffleframework.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | const Web3 = require("web3"); 22 | const web3 = new Web3(); 23 | const HDWalletProvider = require('truffle-hdwallet-provider'); 24 | require('dotenv').config(); 25 | const infuraKey = process.env.INFURAKEY; 26 | 27 | const fs = require('fs'); 28 | const mnemonic = fs.readFileSync(".secret").toString().trim(); 29 | const gasapi = "https://api.etherscan.io/api?module=gastracker&action=gasoracle&apikey=YourApiKeyToken"; 30 | 31 | const LedgerWalletProvider = require('@umaprotocol/truffle-ledger-provider'); 32 | 33 | const ledgerOptions = { 34 | networkId: 1, // mainnet 35 | path: "44'/60'/0'/0/0", // ledger default derivation path 36 | askConfirm: true, 37 | accountsLength: 1, 38 | accountsOffset: 0 39 | }; 40 | 41 | 42 | module.exports = { 43 | /** 44 | * Networks define how you connect to your ethereum client and let you set the 45 | * defaults web3 uses to send transactions. If you don't specify one truffle 46 | * will spin up a development blockchain for you on port 9545 when you 47 | * run `develop` or `test`. You can ask a truffle command to use a specific 48 | * network from the command line, e.g 49 | * 50 | * $ truffle test --network 51 | */ 52 | 53 | networks: { 54 | // Useful for testing. The `development` name is special - truffle uses it by default 55 | // if it's defined here and no other network is specified at the command line. 56 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 57 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 58 | // options below to some value. 59 | // 60 | development: { 61 | host: "192.168.68.58", // Localhost (default: none) 62 | port: 7545, // Standard Ethereum port (default: none) 63 | network_id: "*", // Any network (default: none) 64 | networkCheckTimeout: 1000 65 | }, 66 | 67 | main: { 68 | provider: () => new LedgerWalletProvider(ledgerOptions, "https://mainnet.infura.io/v3/" + infuraKey), 69 | network_id: 1, // mainnet 70 | gasPrice: web3.utils.toWei('250', 'gwei'), 71 | confirmations: 2, // # of confs to wait between deployments. (default: 0) 72 | timeoutBlocks: 1000, // # of blocks before a deployment times out (minimum/default: 50) 73 | networkCheckTimeout: 5000, 74 | skipDryRun: false // Skip dry run before migrations? (default: false for public nets ) 75 | }, 76 | // Another network with more advanced options... 77 | // advanced: { 78 | // port: 8777, // Custom port 79 | // network_id: 1342, // Custom network 80 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 81 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 82 | // from:
, // Account to send txs from (default: accounts[0]) 83 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 84 | // }, 85 | 86 | // Useful for deploying to a public network. 87 | // NB: It's important to wrap the provider as a function. 88 | ropsten: { 89 | // provider: () => new LedgerWalletProvider(ledgerOptions, "https://ropsten.infura.io/v3/" + infuraKey), 90 | provider: () => new HDWalletProvider(mnemonic, "https://ropsten.infura.io/v3/" + infuraKey), 91 | network_id: 3, // Ropsten's id 92 | //gas: 5500000, // Ropsten has a lower block limit than mainnet 93 | confirmations: 2, // # of confs to wait between deployments. (default: 0) 94 | timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 95 | skipDryRun: false // Skip dry run before migrations? (default: false for public nets ) 96 | }, 97 | 98 | // Useful for private networks 99 | // private: { 100 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 101 | // network_id: 2111, // This network is yours, in the cloud. 102 | // production: true // Treats this network as if it was a public net. (default: false) 103 | // } 104 | }, 105 | 106 | // Set default mocha options here, use special reporters etc. 107 | mocha: { 108 | // timeout: 100000 109 | }, 110 | 111 | // Configure your compilers 112 | compilers: { 113 | solc: { 114 | version: "0.5.16", // Fetch exact version from solc-bin (default: truffle's version) 115 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 116 | settings: { // See the solidity docs for advice about optimization and evmVersion 117 | optimizer: { 118 | enabled: true, 119 | runs: 200 120 | }, 121 | // evmVersion: "byzantium" 122 | } 123 | } 124 | }, 125 | } 126 | -------------------------------------------------------------------------------- /truffle-config-ovm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * truffleframework.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | const Web3 = require("web3"); 22 | const web3 = new Web3(); 23 | const HDWalletProvider = require('truffle-hdwallet-provider'); 24 | const ProviderWrapper = require("@eth-optimism/ovm-truffle-provider-wrapper"); 25 | require('dotenv').config(); 26 | // Set this to the desired Execution Manager Address -- required for the transpiler 27 | process.env.EXECUTION_MANAGER_ADDRESS = process.env.EXECUTION_MANAGER_ADDRESS || "0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA"; 28 | const infuraKey = process.env.INFURAKEY; 29 | const web3url = process.env.WEB3URL; 30 | 31 | const fs = require('fs'); 32 | const mnemonic = fs.readFileSync(".secret").toString().trim(); 33 | 34 | module.exports = { 35 | /** 36 | * Networks define how you connect to your ethereum client and let you set the 37 | * defaults web3 uses to send transactions. If you don't specify one truffle 38 | * will spin up a development blockchain for you on port 9545 when you 39 | * run `develop` or `test`. You can ask a truffle command to use a specific 40 | * network from the command line, e.g 41 | * 42 | * $ truffle test --network 43 | */ 44 | 45 | networks: { 46 | test: { 47 | host: "127.0.0.1", 48 | port: 8545, 49 | network_id: "420", // match any network 50 | gasPrice: 0, 51 | }, 52 | live: { 53 | network_id: 108, 54 | provider: function () { 55 | return ProviderWrapper.wrapProvider(new HDWalletProvider(mnemonic, web3url, 0, 10)); 56 | }, 57 | gasPrice: 0, 58 | gas: 9000000, 59 | }, 60 | }, 61 | 62 | // Set default mocha options here, use special reporters etc. 63 | mocha: { 64 | // timeout: 100000 65 | }, 66 | 67 | // Configure your compilers 68 | compilers: { 69 | solc: { 70 | version: "./node_modules/@eth-optimism/solc-transpiler", 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * truffleframework.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | const Web3 = require("web3"); 22 | const web3 = new Web3(); 23 | const HDWalletProvider = require('truffle-hdwallet-provider'); 24 | require('dotenv').config(); 25 | const infuraKey = process.env.INFURAKEY; 26 | 27 | const fs = require('fs'); 28 | const mnemonic = fs.readFileSync(".secret").toString().trim(); 29 | const gasapi = "https://api.etherscan.io/api?module=gastracker&action=gasoracle&apikey=YourApiKeyToken"; 30 | 31 | const LedgerWalletProvider = require('@umaprotocol/truffle-ledger-provider'); 32 | 33 | const ledgerOptions = { 34 | networkId: 1, // mainnet 35 | path: "44'/60'/0'/0/0", // ledger default derivation path 36 | askConfirm: true, 37 | accountsLength: 1, 38 | accountsOffset: 0 39 | }; 40 | 41 | 42 | module.exports = { 43 | /** 44 | * Networks define how you connect to your ethereum client and let you set the 45 | * defaults web3 uses to send transactions. If you don't specify one truffle 46 | * will spin up a development blockchain for you on port 9545 when you 47 | * run `develop` or `test`. You can ask a truffle command to use a specific 48 | * network from the command line, e.g 49 | * 50 | * $ truffle test --network 51 | */ 52 | 53 | networks: { 54 | // Useful for testing. The `development` name is special - truffle uses it by default 55 | // if it's defined here and no other network is specified at the command line. 56 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 57 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 58 | // options below to some value. 59 | // 60 | development: { 61 | host: "192.168.68.58", // Localhost (default: none) 62 | port: 7545, // Standard Ethereum port (default: none) 63 | network_id: "*", // Any network (default: none) 64 | networkCheckTimeout: 1000 65 | }, 66 | 67 | main: { 68 | provider: () => new LedgerWalletProvider(ledgerOptions, "https://mainnet.infura.io/v3/" + infuraKey), 69 | network_id: 1, // mainnet 70 | gasPrice: web3.utils.toWei('250', 'gwei'), 71 | confirmations: 2, // # of confs to wait between deployments. (default: 0) 72 | timeoutBlocks: 1000, // # of blocks before a deployment times out (minimum/default: 50) 73 | networkCheckTimeout: 5000, 74 | skipDryRun: false // Skip dry run before migrations? (default: false for public nets ) 75 | }, 76 | // Another network with more advanced options... 77 | // advanced: { 78 | // port: 8777, // Custom port 79 | // network_id: 1342, // Custom network 80 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 81 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 82 | // from:
, // Account to send txs from (default: accounts[0]) 83 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 84 | // }, 85 | 86 | // Useful for deploying to a public network. 87 | // NB: It's important to wrap the provider as a function. 88 | ropsten: { 89 | // provider: () => new LedgerWalletProvider(ledgerOptions, "https://ropsten.infura.io/v3/" + infuraKey), 90 | provider: () => new HDWalletProvider(mnemonic, "https://ropsten.infura.io/v3/" + infuraKey), 91 | network_id: 3, // Ropsten's id 92 | //gas: 5500000, // Ropsten has a lower block limit than mainnet 93 | confirmations: 2, // # of confs to wait between deployments. (default: 0) 94 | timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 95 | skipDryRun: false // Skip dry run before migrations? (default: false for public nets ) 96 | }, 97 | 98 | // Useful for private networks 99 | // private: { 100 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 101 | // network_id: 2111, // This network is yours, in the cloud. 102 | // production: true // Treats this network as if it was a public net. (default: false) 103 | // } 104 | }, 105 | 106 | // Set default mocha options here, use special reporters etc. 107 | mocha: { 108 | // timeout: 100000 109 | }, 110 | 111 | // Configure your compilers 112 | compilers: { 113 | solc: { 114 | version: "0.5.16", // Fetch exact version from solc-bin (default: truffle's version) 115 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 116 | settings: { // See the solidity docs for advice about optimization and evmVersion 117 | optimizer: { 118 | enabled: true, 119 | runs: 200 120 | }, 121 | // evmVersion: "byzantium" 122 | } 123 | } 124 | }, 125 | } 126 | --------------------------------------------------------------------------------