├── .eslintrc.yml ├── docs ├── CNAME ├── styles.css ├── docs │ ├── SafeMath │ │ └── index.html │ ├── ArrayUtils │ │ └── index.html │ ├── ReentrancyGuarded │ │ └── index.html │ ├── SaleKindInterface │ │ └── index.html │ └── MerkleProof │ │ └── index.html └── index.html ├── .soliumignore ├── scripts ├── test.sh ├── testrpc.sh ├── travis_coverage.sh ├── develop.sh ├── lint.sh ├── travis_prep.sh ├── compile.sh ├── doc.sh ├── coverage.sh ├── travis_test.sh ├── analyze.sh └── flatten.sh ├── doxity ├── .gitignore ├── css │ ├── favicon.png │ ├── style.scss │ ├── hljs-atom-one.css │ └── responsive.css ├── gatsby-node.js ├── config.toml ├── components │ ├── abi.js │ ├── methods.js │ ├── shortAddressLink.js │ ├── bytecode.js │ ├── source.js │ ├── contractDropdown.js │ ├── contract.js │ └── method.js ├── wrappers │ ├── md.jsx │ └── json.jsx ├── package.json ├── html.js └── pages │ ├── _template.jsx │ └── index.md ├── .doxityrc ├── .gitignore ├── .gitmodules ├── audits ├── v1 │ ├── solidified_audit_report.pdf │ └── AUDIT_SPEC.md ├── v2 │ ├── solidified_v2_audit_report.pdf │ ├── AUDIT_RESPONSE.md │ └── AUDIT_SPEC.md └── v2.1 │ └── AUDIT_SPEC.md ├── truffle-config.js ├── .travis.yml ├── .solcover.js ├── migrations ├── 1_initial_migration.js ├── 4_misc.js ├── config.js ├── 2_deploy_test_token_and_dao.js ├── 3_deploy_token_and_dao.js └── 5_deploy_registry_and_exchange.js ├── contracts ├── WyvernTokenTransferProxy.sol ├── WyvernDAOProxy.sol ├── registry │ ├── OwnableDelegateProxy.sol │ ├── TokenTransferProxy.sol │ ├── proxy │ │ ├── LICENSE │ │ ├── OwnedUpgradeabilityStorage.sol │ │ ├── Proxy.sol │ │ └── OwnedUpgradeabilityProxy.sol │ ├── AuthenticatedProxy.sol │ └── ProxyRegistry.sol ├── common │ ├── ReentrancyGuarded.sol │ ├── TokenRecipient.sol │ ├── TokenLocker.sol │ └── ArrayUtils.sol ├── Migrations.sol ├── TestStatic.sol ├── TestToken.sol ├── TestDAO.sol ├── WyvernAtomicizer.sol ├── WyvernExchange.sol ├── WyvernDAO.sol ├── WyvernProxyRegistry.sol ├── dao │ └── DelegateProxy.sol ├── token │ ├── DelayedReleaseToken.sol │ └── UTXORedeemableToken.sol ├── WyvernToken.sol └── exchange │ └── SaleKindInterface.sol ├── .soliumrc.json ├── test ├── test-token.js ├── wyvern-exchange-misc.js ├── aux.js ├── wyvern-dao.js ├── wyvern-exchange-static.js └── wyvern-dao-proxy.js ├── truffle.js ├── LICENSE ├── package.json ├── config.json └── README.md /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | extends: standard 2 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | docs.projectwyvern.com 2 | -------------------------------------------------------------------------------- /.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | contracts/exchange 3 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf build 4 | truffle test $1 5 | -------------------------------------------------------------------------------- /scripts/testrpc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | testrpc --networkId 50 --port 8545 4 | -------------------------------------------------------------------------------- /scripts/travis_coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | yarn coverage 4 | sleep 1 5 | exit 0 6 | -------------------------------------------------------------------------------- /doxity/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | public/ 3 | pages/docs/* 4 | .gatsby-context.js 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /scripts/develop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | node node_modules/@digix/doxity/lib/bin/doxity.js develop 4 | -------------------------------------------------------------------------------- /doxity/css/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProjectWyvern/wyvern-ethereum/HEAD/doxity/css/favicon.png -------------------------------------------------------------------------------- /scripts/lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ./node_modules/solium/bin/solium.js -d ./contracts --fix 4 | 5 | exit 0 6 | -------------------------------------------------------------------------------- /.doxityrc: -------------------------------------------------------------------------------- 1 | { 2 | "target": "doxity", 3 | "src": "contracts/*", 4 | "dir": "pages/docs", 5 | "out": "docs" 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .cache/ 3 | temp/ 4 | .gatsby-context.js 5 | encode.js 6 | proposal.js 7 | test.js 8 | -------------------------------------------------------------------------------- /scripts/travis_prep.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | truffle version 6 | yarn list 7 | yarn lint 8 | -------------------------------------------------------------------------------- /scripts/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf build/contracts 4 | yarn run -- truffle compile --offline 5 | rm -f yarn-error.log 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "utxo-merkle-proof"] 2 | path = utxo-merkle-proof 3 | url = https://github.com/ProjectWyvern/utxo-merkle-proof.git 4 | -------------------------------------------------------------------------------- /audits/v1/solidified_audit_report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProjectWyvern/wyvern-ethereum/HEAD/audits/v1/solidified_audit_report.pdf -------------------------------------------------------------------------------- /scripts/doc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | node node_modules/@digix/doxity/lib/bin/doxity.js build 4 | echo 'docs.projectwyvern.com' > docs/CNAME 5 | -------------------------------------------------------------------------------- /audits/v2/solidified_v2_audit_report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProjectWyvern/wyvern-ethereum/HEAD/audits/v2/solidified_v2_audit_report.pdf -------------------------------------------------------------------------------- /scripts/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf build 4 | ./node_modules/.bin/solidity-coverage 5 | cat coverage/lcov.info | ./node_modules/.bin/coveralls 6 | -------------------------------------------------------------------------------- /scripts/travis_test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | pkill -f testrpc 6 | sleep 2 7 | yarn testrpc & 8 | sleep 1 9 | yarn test 10 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // See 3 | // to customize your Truffle configuration! 4 | } 5 | -------------------------------------------------------------------------------- /doxity/gatsby-node.js: -------------------------------------------------------------------------------- 1 | exports.modifyWebpackConfig = (config) => { 2 | config.merge({ 3 | resolve: { 4 | alias: { 5 | 'semantic-ui-react': '@hitchcott/semantic-ui-react', 6 | }, 7 | }, 8 | }) 9 | return config 10 | } 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | group: beta 4 | language: node_js 5 | node_js: 6 | - "9" 7 | cache: 8 | yarn: true 9 | before_script: 10 | - scripts/travis_prep.sh 11 | script: 12 | - scripts/travis_coverage.sh 13 | - scripts/travis_test.sh 14 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | port: 8545, 3 | copyPackages: ['openzeppelin-solidity'], 4 | skipFiles: ['TestStatic.sol', 'common/ArrayUtils.sol', '../openzeppelin-solidity/contracts/math/SafeMath.sol'], 5 | compileCommand: '../node_modules/.bin/truffle compile', 6 | testCommand: '../node_modules/.bin/truffle test --network coverage' 7 | } 8 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | /* global artifacts:false */ 2 | 3 | const Migrations = artifacts.require('./Migrations.sol') 4 | const { setConfig } = require('./config.js') 5 | 6 | module.exports = (deployer, network) => { 7 | return deployer.deploy(Migrations).then(() => { 8 | setConfig('deployed.' + network + '.Migrations', Migrations.address) 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /contracts/WyvernTokenTransferProxy.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | << Project Wyvern Token Transfer Proxy >. 4 | 5 | */ 6 | 7 | pragma solidity 0.4.23; 8 | 9 | import "./registry/TokenTransferProxy.sol"; 10 | 11 | contract WyvernTokenTransferProxy is TokenTransferProxy { 12 | 13 | constructor (ProxyRegistry registryAddr) 14 | public 15 | { 16 | registry = registryAddr; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /doxity/config.toml: -------------------------------------------------------------------------------- 1 | linkPrefix = "" 2 | name = "wyvern-ethereum" 3 | license = "MIT" 4 | version = "1.0.0" 5 | description = "Wyvern Ethereum Contracts" 6 | homepage = "https://github.com/ProjectWyvern/wyvern-ethereum#readme" 7 | author = "Project Wyvern Developers" 8 | buildTime = 2018-03-04T03:35:33.490Z 9 | target = "doxity" 10 | src = "contracts/*" 11 | dir = "pages/docs" 12 | out = "docs" 13 | 14 | [interaction] -------------------------------------------------------------------------------- /contracts/WyvernDAOProxy.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | << Project Wyvern DAO Proxy >> 4 | 5 | */ 6 | 7 | pragma solidity 0.4.23; 8 | 9 | import "./dao/DelegateProxy.sol"; 10 | 11 | /** 12 | * @title WyvernDAOProxy 13 | * @author Project Wyvern Developers 14 | */ 15 | contract WyvernDAOProxy is DelegateProxy { 16 | 17 | constructor () 18 | public 19 | { 20 | owner = msg.sender; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /scripts/analyze.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | yarn flatten 4 | 5 | alias oyente="docker run -v $(pwd):/opt luongnguyen/oyente /oyente/oyente/oyente.py" 6 | alias myth="docker run -v $(pwd):/opt mythril myth" 7 | 8 | for contract in $(ls temp/); do 9 | echo "Analyzing $contract with Mythril..." 10 | myth -x /opt/temp/$contract 11 | echo "Analyzing $contract with Oyente..." 12 | oyente -s /opt/temp/$contract 13 | done 14 | -------------------------------------------------------------------------------- /.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:all", 3 | "plugins": [ 4 | "security" 5 | ], 6 | "rules": { 7 | "quotes": [ 8 | "error", 9 | "double" 10 | ], 11 | "indentation": [ 12 | "error", 13 | 4 14 | ], 15 | "arg-overflow": [ 16 | "off", 17 | 3 18 | ], 19 | "blank-lines": [ 20 | "off", 21 | 2 22 | ], 23 | "security/no-call-value": ["warning"], 24 | "security/no-block-members": ["warning"], 25 | "security/no-inline-assembly": ["warning"], 26 | "security/no-assign-params": ["warning"] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/test-token.js: -------------------------------------------------------------------------------- 1 | /* global artifacts:false, it:false, contract:false, assert:false */ 2 | 3 | const TestToken = artifacts.require('TestToken') 4 | 5 | const BigNumber = require('bignumber.js') 6 | 7 | contract('TestToken', (accounts) => { 8 | it('should set correct balance', () => { 9 | return TestToken 10 | .deployed() 11 | .then(tokenInstance => { 12 | return tokenInstance.balanceOf.call(accounts[0]) 13 | }) 14 | .then(amount => { 15 | assert.equal(amount.equals(new BigNumber(2 * Math.pow(10, 18 + 7))), true, 'Incorrect amount') 16 | }) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /doxity/components/abi.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | import hljs from 'highlight.js' 3 | 4 | export default class Abi extends Component { 5 | componentDidMount() { 6 | hljs.highlightBlock(this.refs.highlight) 7 | } 8 | render() { 9 | const { contract } = this.props 10 | return ( 11 |
12 |

ABI

13 |
14 |           {JSON.stringify(contract.abi, null, 2)}
15 |         
16 |
17 | ) 18 | } 19 | } 20 | 21 | Abi.propTypes = { 22 | contract: PropTypes.object, 23 | } 24 | -------------------------------------------------------------------------------- /contracts/registry/OwnableDelegateProxy.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | WyvernOwnableDelegateProxy 4 | 5 | */ 6 | 7 | pragma solidity 0.4.23; 8 | 9 | import "./ProxyRegistry.sol"; 10 | import "./AuthenticatedProxy.sol"; 11 | import "./proxy/OwnedUpgradeabilityProxy.sol"; 12 | 13 | contract OwnableDelegateProxy is OwnedUpgradeabilityProxy { 14 | 15 | constructor(address owner, address initialImplementation, bytes calldata) 16 | public 17 | { 18 | setUpgradeabilityOwner(owner); 19 | _upgradeTo(initialImplementation); 20 | require(initialImplementation.delegatecall(calldata)); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /doxity/wrappers/md.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import DocumentTitle from 'react-document-title' 3 | import { config } from 'config' 4 | 5 | export default class Md extends Component { 6 | render() { 7 | const post = this.props.route.page.data 8 | 9 | return ( 10 | 11 |
12 |

{post.title}

13 |
14 |
15 | 16 | ) 17 | } 18 | } 19 | 20 | Md.propTypes = { 21 | route: PropTypes.object, 22 | } 23 | -------------------------------------------------------------------------------- /doxity/components/methods.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | 3 | import sortBy from 'sort-by' 4 | 5 | import Method from './method' 6 | 7 | export default class Methods extends Component { 8 | render() { 9 | const { contract } = this.props 10 | return ( 11 |
12 | {contract.abiDocs.sort(sortBy('type', 'name')).map((method) => { 13 | return 14 | })} 15 |
16 | ) 17 | } 18 | } 19 | 20 | Methods.propTypes = { 21 | contract: PropTypes.object, 22 | } 23 | -------------------------------------------------------------------------------- /contracts/common/ReentrancyGuarded.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Simple contract extension to provide a contract-global reentrancy guard on functions. 4 | 5 | */ 6 | 7 | pragma solidity 0.4.23; 8 | 9 | /** 10 | * @title ReentrancyGuarded 11 | * @author Project Wyvern Developers 12 | */ 13 | contract ReentrancyGuarded { 14 | 15 | bool reentrancyLock = false; 16 | 17 | /* Prevent a contract function from being reentrant-called. */ 18 | modifier reentrancyGuard { 19 | if (reentrancyLock) { 20 | revert(); 21 | } 22 | reentrancyLock = true; 23 | _; 24 | reentrancyLock = false; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /doxity/components/shortAddressLink.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | import { config } from 'config' 3 | 4 | export default class ShortAddressLink extends Component { 5 | render() { 6 | const { interaction } = config 7 | if (!interaction) { return null } 8 | const { address } = this.props 9 | const urlPrefix = interaction.network === '2' ? 'testnet.' : '' 10 | const url = `https://${urlPrefix}etherscan.io/address/${address}` 11 | return ( 12 | {address} 13 | ) 14 | } 15 | } 16 | 17 | ShortAddressLink.propTypes = { 18 | address: PropTypes.string.isRequired, 19 | } 20 | -------------------------------------------------------------------------------- /migrations/4_misc.js: -------------------------------------------------------------------------------- 1 | /* global artifacts: false */ 2 | 3 | const WyvernDAOProxy = artifacts.require('./WyvernDAOProxy.sol') 4 | const WyvernAtomicizer = artifacts.require('./WyvernAtomicizer.sol') 5 | 6 | const { setConfig } = require('./config.js') 7 | 8 | module.exports = (deployer, network) => { 9 | if (network === 'main') return 10 | return deployer.deploy(WyvernDAOProxy) 11 | .then(() => { 12 | setConfig('deployed.' + network + '.WyvernDAOProxy', WyvernDAOProxy.address) 13 | return deployer.deploy(WyvernAtomicizer) 14 | .then(() => { 15 | setConfig('deployed.' + network + '.WyvernAtomicizer', WyvernAtomicizer.address) 16 | }) 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /audits/v2/AUDIT_RESPONSE.md: -------------------------------------------------------------------------------- 1 | In response to the issues found in the audit report: 2 | 3 | 1. Indeed, with the current EVM design, it is impossible to guarantee that a contract will never receive Ether. This was an oversight on our part. The balance assertion has been removed. 4 | 5 | 2. This is intentional. When paying fees in the protocol token, Exchange users have the option to pay no fees. Relayers, which host off-chain orderbooks, can require particular fees for submission to their orderbooks. 6 | 7 | 3. Relayers can choose to whitelist or blacklist tokens to prevent user confusion, so implementing token restrictions at the protocol layer is unnecessary. The recommendation of user caution is well-received. 8 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.23; 2 | 3 | contract Migrations { 4 | 5 | address public owner; 6 | uint public last_completed_migration; 7 | 8 | modifier restricted() { 9 | if (msg.sender == owner) { 10 | _; 11 | } 12 | } 13 | 14 | constructor () public { 15 | owner = msg.sender; 16 | } 17 | 18 | function setCompleted(uint completed) public restricted { 19 | last_completed_migration = completed; 20 | } 21 | 22 | function upgrade(address new_address) public restricted { 23 | Migrations upgraded = Migrations(new_address); 24 | upgraded.setCompleted(last_completed_migration); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /migrations/config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | const updateConfig = (func) => { 4 | const previous = JSON.parse(fs.readFileSync('config.json')) 5 | const updated = func(previous) 6 | fs.writeFileSync('config.json', JSON.stringify(updated, null, 2)) 7 | } 8 | 9 | const setConfig = (path, val) => { 10 | path = path.split('.').reverse() 11 | updateConfig(config => { 12 | var ref = config 13 | while (path.length > 1) { 14 | const key = path.pop() 15 | if (!ref[key]) ref[key] = {} 16 | ref = ref[key] 17 | } 18 | ref[path.pop()] = val 19 | return config 20 | }) 21 | } 22 | 23 | module.exports = { 24 | setConfig: setConfig, 25 | updateConfig: updateConfig 26 | } 27 | -------------------------------------------------------------------------------- /contracts/TestStatic.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | << Test Static Calls >> 4 | 5 | */ 6 | 7 | pragma solidity 0.4.23; 8 | 9 | /** 10 | * @title TestStatic 11 | * @author Project Wyvern Developers 12 | */ 13 | contract TestStatic { 14 | 15 | /** 16 | * @dev Initialize contract 17 | */ 18 | constructor () public { 19 | } 20 | 21 | function alwaysSucceed() 22 | public 23 | pure 24 | { 25 | require(true); 26 | } 27 | 28 | function alwaysFail() 29 | public 30 | pure 31 | { 32 | require(false); 33 | } 34 | 35 | function requireMinimumLength(bytes calldata) 36 | public 37 | pure 38 | { 39 | require(calldata.length > 2); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /contracts/TestToken.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | << Test Token (for use with the Test DAO) >> 4 | 5 | */ 6 | 7 | pragma solidity 0.4.23; 8 | 9 | import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; 10 | 11 | /** 12 | * @title TestToken 13 | * @author Project Wyvern Developers 14 | */ 15 | contract TestToken is StandardToken { 16 | 17 | uint constant public decimals = 18; 18 | string constant public name = "Test Token"; 19 | string constant public symbol = "TST"; 20 | 21 | uint constant public MINT_AMOUNT = 20000000 * (10 ** decimals); 22 | 23 | /** 24 | * @dev Initialize the test token 25 | */ 26 | constructor () public { 27 | balances[msg.sender] = MINT_AMOUNT; 28 | totalSupply_ = MINT_AMOUNT; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /doxity/wrappers/json.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import DocumentTitle from 'react-document-title' 3 | import { config } from 'config' 4 | import Contract from '../components/contract' 5 | 6 | export default class Json extends Component { 7 | render() { 8 | const contract = this.props.route.page.data 9 | const { web3 } = this.context 10 | // populate contract instance 11 | if (web3 && contract.address) { 12 | contract.instance = web3.eth.contract(contract.abi).at(contract.address) 13 | } 14 | return ( 15 | 16 | 17 | 18 | ) 19 | } 20 | } 21 | Json.contextTypes = { 22 | web3: PropTypes.object, 23 | } 24 | Json.propTypes = { 25 | route: PropTypes.object, 26 | } 27 | -------------------------------------------------------------------------------- /migrations/2_deploy_test_token_and_dao.js: -------------------------------------------------------------------------------- 1 | /* global artifacts: false */ 2 | 3 | const TestToken = artifacts.require('./TestToken.sol') 4 | const TestDAO = artifacts.require('./TestDAO.sol') 5 | const TestStatic = artifacts.require('./TestStatic.sol') 6 | 7 | const { setConfig } = require('./config.js') 8 | 9 | module.exports = (deployer, network) => { 10 | if (network === 'main') return 11 | deployer.deploy(TestToken) 12 | .then(() => { 13 | setConfig('deployed.' + network + '.TestToken', TestToken.address) 14 | return deployer.deploy(TestDAO, TestToken.address).then(() => { 15 | setConfig('deployed.' + network + '.TestDAO', TestDAO.address) 16 | return deployer.deploy(TestStatic).then(() => { 17 | setConfig('deployed.' + network + '.TestStatic', TestStatic.address) 18 | }) 19 | }) 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /doxity/components/bytecode.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | 3 | export default class Bytecode extends Component { 4 | render() { 5 | const { contract } = this.props 6 | return ( 7 |
8 | {contract.bin && 9 |
10 |

Hex

11 |
12 |               {contract.bin}
13 |             
14 |
15 | } 16 | {contract.opcodes && 17 |
18 |

Opcodes

19 |
20 |               {contract.opcodes}
21 |             
22 |
23 | } 24 |
25 | ) 26 | } 27 | } 28 | 29 | Bytecode.propTypes = { 30 | contract: PropTypes.object, 31 | } 32 | -------------------------------------------------------------------------------- /contracts/TestDAO.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | << Test DAO (since we need a DAO controlled by a token that we can send around in tests) >> 4 | 5 | */ 6 | 7 | pragma solidity 0.4.23; 8 | 9 | import "./dao/DelegatedShareholderAssociation.sol"; 10 | 11 | /** 12 | * @title TestDAO 13 | * @author Project Wyvern Developers 14 | */ 15 | contract TestDAO is DelegatedShareholderAssociation { 16 | 17 | string public constant name = "Test DAO"; 18 | 19 | uint public constant TOKEN_DECIMALS = 18; 20 | uint public constant REQUIRED_SHARES_TO_BE_BOARD_MEMBER = 2000 * (10 ** 18); // set to ~ 0.1% of supply 21 | 22 | constructor (ERC20 sharesAddress) public { 23 | sharesTokenAddress = sharesAddress; 24 | requiredSharesToBeBoardMember = REQUIRED_SHARES_TO_BE_BOARD_MEMBER; 25 | minimumQuorum = 1000 * (10 ** TOKEN_DECIMALS); 26 | debatingPeriodInMinutes = 0; 27 | tokenLocker = new TokenLocker(sharesAddress); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /doxity/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "doxity-gatsby-starter-project", 3 | "description": "Doxity Example Site", 4 | "version": "1.0.0", 5 | "author": "Chris Hitchcott ", 6 | "dependencies": { 7 | "@hitchcott/semantic-ui-react": "^0.61.1", 8 | "highlight.js": "^9.7.0", 9 | "highlightjs-solidity": "^1.0.5", 10 | "react-document-title": "^2.0.1", 11 | "react-markdown": "^2.4.2", 12 | "sort-by": "^1.1.1", 13 | "web3": "^0.17.0-beta" 14 | }, 15 | "devDependencies": { 16 | "eslint": "^3.8.1", 17 | "eslint-config-airbnb": "^12.0.0", 18 | "eslint-plugin-import": "^1.16.0", 19 | "eslint-plugin-jsx-a11y": "^2.2.3", 20 | "eslint-plugin-react": "^6.4.1", 21 | "gatsby": "0.12.18" 22 | }, 23 | "keywords": [ 24 | "gatsby" 25 | ], 26 | "license": "MIT", 27 | "scripts": { 28 | "develop": "./node_modules/.bin/gatsby develop", 29 | "build": "./node_modules/.bin/gatsby build --prefix-links" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/WyvernAtomicizer.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | << Project Wyvern Atomicizer >> 4 | 5 | Execute multiple transactions, in order, atomically (if any fails, all revert). 6 | This contract MUST be DELEGATECALLed. 7 | 8 | */ 9 | 10 | pragma solidity 0.4.23; 11 | 12 | /** 13 | * @title WyvernAtomicizer 14 | * @author Project Wyvern Developers 15 | */ 16 | library WyvernAtomicizer { 17 | 18 | function atomicize (address[] addrs, uint[] values, uint[] calldataLengths, bytes calldatas) 19 | public 20 | { 21 | require(addrs.length == values.length && addrs.length == calldataLengths.length); 22 | 23 | uint j = 0; 24 | for (uint i = 0; i < addrs.length; i++) { 25 | bytes memory calldata = new bytes(calldataLengths[i]); 26 | for (uint k = 0; k < calldataLengths[i]; k++) { 27 | calldata[k] = calldatas[j]; 28 | j++; 29 | } 30 | require(addrs[i].call.value(values[i])(calldata)); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /scripts/flatten.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf temp 4 | mkdir -p temp 5 | 6 | cd contracts 7 | find . -type f -name "*.sol" -exec sed -i 's/pragma solidity 0.4.23/pragma solidity ^0.4.23/g' {} + 8 | cd .. 9 | 10 | alias flatten="solidity_flattener --solc-paths openzeppelin-solidity=$(pwd)/node_modules/openzeppelin-solidity" 11 | 12 | flatten contracts/WyvernAtomicizer.sol --output temp/WyvernAtomicizer.sol 13 | flatten contracts/WyvernTokenTransferProxy.sol --output temp/WyvernTokenTransferProxy.sol 14 | flatten contracts/WyvernDAOProxy.sol --output temp/WyvernDAOProxy.sol 15 | flatten contracts/WyvernToken.sol --output temp/WyvernTokenFlattened.sol 16 | flatten contracts/WyvernDAO.sol --output temp/WyvernDAOFlattened.sol 17 | flatten contracts/WyvernExchange.sol --output temp/WyvernExchangeFlattened.sol 18 | flatten contracts/WyvernProxyRegistry.sol --output temp/WyvernProxyRegistryFlattened.sol 19 | 20 | cd contracts 21 | find . -type f -name "*.sol" -exec sed -i 's/pragma solidity ^0.4.23/pragma solidity 0.4.23/g' {} + 22 | cd .. 23 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | development: { 4 | host: 'localhost', 5 | port: 8545, 6 | network_id: '*', 7 | gas: 6700000 8 | }, 9 | kovan: { 10 | host: 'localhost', 11 | port: 8545, 12 | network_id: '*', 13 | gas: 6700000 14 | }, 15 | rinkeby: { 16 | host: 'localhost', 17 | from: '0xb483a98e72a583cc9b45b70cee07ac628d633d69', 18 | port: 8545, 19 | network_id: '*', 20 | gas: 6700000, 21 | gasPrice: 21000000000 22 | }, 23 | coverage: { 24 | host: 'localhost', 25 | network_id: '*', 26 | port: 8545, 27 | gas: 0xfffffffffff, 28 | gasPrice: 0x01 29 | }, 30 | main: { 31 | host: 'localhost', 32 | port: 8547, 33 | from: '0x0084a81668B9A978416aBEB88bC1572816cc7cAC', 34 | network_id: 1, 35 | gas: 6700000, 36 | gasPrice: 6110000000 37 | } 38 | }, 39 | solc: { 40 | optimizer: { 41 | enabled: true, 42 | runs: 200 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017-2018 Project Wyvern Developers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /doxity/components/source.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | 3 | import hljs from 'highlight.js' 4 | import hljsDefineSolidity from 'highlightjs-solidity' 5 | import '../css/hljs-atom-one.css' 6 | 7 | hljsDefineSolidity(hljs) 8 | 9 | export default class Source extends Component { 10 | constructor(props) { 11 | super(props) 12 | this.state = {} 13 | } 14 | componentDidMount() { 15 | hljs.highlightBlock(this.refs.highlight) 16 | } 17 | componentWillReceiveProps() { 18 | this.state = { renderHack: true } 19 | setTimeout(() => { 20 | this.setState({ renderHack: false }) 21 | hljs.highlightBlock(this.refs.highlight) 22 | }, 0) 23 | } 24 | render() { 25 | const { contract } = this.props 26 | return ( 27 |
28 | {!this.state.renderHack && 29 |
30 |             {contract.source}
31 |           
32 | } 33 |
34 | ) 35 | } 36 | } 37 | 38 | Source.propTypes = { 39 | contract: PropTypes.object, 40 | } 41 | -------------------------------------------------------------------------------- /contracts/registry/TokenTransferProxy.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Token transfer proxy. Uses the authentication table of a ProxyRegistry contract to grant ERC20 `transferFrom` access. 4 | This means that users only need to authorize the proxy contract once for all future protocol versions. 5 | 6 | */ 7 | 8 | pragma solidity 0.4.23; 9 | 10 | import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; 11 | 12 | import "./ProxyRegistry.sol"; 13 | 14 | contract TokenTransferProxy { 15 | 16 | /* Authentication registry. */ 17 | ProxyRegistry public registry; 18 | 19 | /** 20 | * Call ERC20 `transferFrom` 21 | * 22 | * @dev Authenticated contract only 23 | * @param token ERC20 token address 24 | * @param from From address 25 | * @param to To address 26 | * @param amount Transfer amount 27 | */ 28 | function transferFrom(address token, address from, address to, uint amount) 29 | public 30 | returns (bool) 31 | { 32 | require(registry.contracts(msg.sender)); 33 | return ERC20(token).transferFrom(from, to, amount); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /contracts/WyvernExchange.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | << Project Wyvern Exchange >> 4 | 5 | */ 6 | 7 | pragma solidity 0.4.23; 8 | 9 | import "./exchange/Exchange.sol"; 10 | 11 | /** 12 | * @title WyvernExchange 13 | * @author Project Wyvern Developers 14 | */ 15 | contract WyvernExchange is Exchange { 16 | 17 | string public constant name = "Project Wyvern Exchange"; 18 | 19 | string public constant version = "2.2"; 20 | 21 | string public constant codename = "Lambton Worm"; 22 | 23 | /** 24 | * @dev Initialize a WyvernExchange instance 25 | * @param registryAddress Address of the registry instance which this Exchange instance will use 26 | * @param tokenAddress Address of the token used for protocol fees 27 | */ 28 | constructor (ProxyRegistry registryAddress, TokenTransferProxy tokenTransferProxyAddress, ERC20 tokenAddress, address protocolFeeAddress) public { 29 | registry = registryAddress; 30 | tokenTransferProxy = tokenTransferProxyAddress; 31 | exchangeToken = tokenAddress; 32 | protocolFeeRecipient = protocolFeeAddress; 33 | owner = msg.sender; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /contracts/WyvernDAO.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | << Project Wyvern DAO >> 4 | 5 | */ 6 | 7 | pragma solidity 0.4.23; 8 | 9 | import "./dao/DelegatedShareholderAssociation.sol"; 10 | 11 | /** 12 | * @title WyvernDAO 13 | * @author Project Wyvern Developers 14 | */ 15 | contract WyvernDAO is DelegatedShareholderAssociation { 16 | 17 | string public constant name = "Project Wyvern DAO"; 18 | 19 | uint public constant TOKEN_DECIMALS = 18; 20 | uint public constant REQUIRED_SHARES_TO_BE_BOARD_MEMBER = 2000 * (10 ** TOKEN_DECIMALS); // set to ~ 0.1% of supply 21 | uint public constant MINIMUM_QUORUM = 200000 * (10 ** TOKEN_DECIMALS); // set to 10% of supply 22 | uint public constant DEBATE_PERIOD_MINUTES = 60 * 24 * 3; // set to 3 days 23 | 24 | constructor (ERC20 sharesAddress) public { 25 | sharesTokenAddress = sharesAddress; 26 | requiredSharesToBeBoardMember = REQUIRED_SHARES_TO_BE_BOARD_MEMBER; 27 | minimumQuorum = MINIMUM_QUORUM; 28 | debatingPeriodInMinutes = DEBATE_PERIOD_MINUTES; 29 | tokenLocker = new TokenLocker(sharesAddress); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /contracts/registry/proxy/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 zOS Global Limited. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /contracts/WyvernProxyRegistry.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | << Project Wyvern Proxy Registry >> 4 | 5 | */ 6 | 7 | pragma solidity 0.4.23; 8 | 9 | import "./registry/ProxyRegistry.sol"; 10 | import "./registry/AuthenticatedProxy.sol"; 11 | 12 | /** 13 | * @title WyvernProxyRegistry 14 | * @author Project Wyvern Developers 15 | */ 16 | contract WyvernProxyRegistry is ProxyRegistry { 17 | 18 | string public constant name = "Project Wyvern Proxy Registry"; 19 | 20 | /* Whether the initial auth address has been set. */ 21 | bool public initialAddressSet = false; 22 | 23 | constructor () 24 | public 25 | { 26 | delegateProxyImplementation = new AuthenticatedProxy(); 27 | } 28 | 29 | /** 30 | * Grant authentication to the initial Exchange protocol contract 31 | * 32 | * @dev No delay, can only be called once - after that the standard registry process with a delay must be used 33 | * @param authAddress Address of the contract to grant authentication 34 | */ 35 | function grantInitialAuthentication (address authAddress) 36 | onlyOwner 37 | public 38 | { 39 | require(!initialAddressSet); 40 | initialAddressSet = true; 41 | contracts[authAddress] = true; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /contracts/registry/proxy/OwnedUpgradeabilityStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.23; 2 | 3 | /** 4 | * @title OwnedUpgradeabilityStorage 5 | * @dev This contract keeps track of the upgradeability owner 6 | */ 7 | contract OwnedUpgradeabilityStorage { 8 | 9 | // Current implementation 10 | address internal _implementation; 11 | 12 | // Owner of the contract 13 | address private _upgradeabilityOwner; 14 | 15 | /** 16 | * @dev Tells the address of the owner 17 | * @return the address of the owner 18 | */ 19 | function upgradeabilityOwner() public view returns (address) { 20 | return _upgradeabilityOwner; 21 | } 22 | 23 | /** 24 | * @dev Sets the address of the owner 25 | */ 26 | function setUpgradeabilityOwner(address newUpgradeabilityOwner) internal { 27 | _upgradeabilityOwner = newUpgradeabilityOwner; 28 | } 29 | 30 | /** 31 | * @dev Tells the address of the current implementation 32 | * @return address of the current implementation 33 | */ 34 | function implementation() public view returns (address) { 35 | return _implementation; 36 | } 37 | 38 | /** 39 | * @dev Tells the proxy type (EIP 897) 40 | * @return Proxy type, 2 for forwarding proxy 41 | */ 42 | function proxyType() public pure returns (uint256 proxyTypeId) { 43 | return 2; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /contracts/common/TokenRecipient.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Token recipient. Modified very slightly from the example on http://ethereum.org/dao (just to index log parameters). 4 | 5 | */ 6 | 7 | pragma solidity 0.4.23; 8 | 9 | import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; 10 | 11 | /** 12 | * @title TokenRecipient 13 | * @author Project Wyvern Developers 14 | */ 15 | contract TokenRecipient { 16 | event ReceivedEther(address indexed sender, uint amount); 17 | event ReceivedTokens(address indexed from, uint256 value, address indexed token, bytes extraData); 18 | 19 | /** 20 | * @dev Receive tokens and generate a log event 21 | * @param from Address from which to transfer tokens 22 | * @param value Amount of tokens to transfer 23 | * @param token Address of token 24 | * @param extraData Additional data to log 25 | */ 26 | function receiveApproval(address from, uint256 value, address token, bytes extraData) public { 27 | ERC20 t = ERC20(token); 28 | require(t.transferFrom(from, this, value)); 29 | emit ReceivedTokens(from, value, token, extraData); 30 | } 31 | 32 | /** 33 | * @dev Receive Ether and generate a log event 34 | */ 35 | function () payable public { 36 | emit ReceivedEther(msg.sender, msg.value); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /migrations/3_deploy_token_and_dao.js: -------------------------------------------------------------------------------- 1 | /* global artifacts: false */ 2 | 3 | const MerkleProof = artifacts.require('./MerkleProof.sol') 4 | const WyvernToken = artifacts.require('./WyvernToken.sol') 5 | const WyvernDAO = artifacts.require('./WyvernDAO.sol') 6 | 7 | const { setConfig } = require('./config.js') 8 | const { utxoMerkleRoot, utxoAmount } = require('../test/aux.js') 9 | 10 | module.exports = (deployer, network) => { 11 | if (network === 'main' || network === 'rinkeby') return 12 | return deployer.deploy(MerkleProof).then(() => { 13 | setConfig('deployed.' + network + '.MerkleProof', MerkleProof.address) 14 | deployer.link(MerkleProof, WyvernToken) 15 | return deployer.deploy(WyvernToken, utxoMerkleRoot, utxoAmount) 16 | .then(() => { 17 | setConfig('deployed.' + network + '.WyvernToken', WyvernToken.address) 18 | return deployer.deploy(WyvernDAO, WyvernToken.address).then(() => { 19 | setConfig('deployed.' + network + '.WyvernDAO', WyvernDAO.address) 20 | }) 21 | }) 22 | }).then(() => { 23 | WyvernToken.deployed() 24 | .then(tokenInstance => { 25 | return WyvernDAO.deployed() 26 | .then(daoInstance => { 27 | return tokenInstance.releaseTokens.sendTransaction(daoInstance.address) 28 | }) 29 | }) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /contracts/registry/proxy/Proxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.23; 2 | 3 | /** 4 | * @title Proxy 5 | * @dev Gives the possibility to delegate any call to a foreign implementation. 6 | */ 7 | contract Proxy { 8 | 9 | /** 10 | * @dev Tells the address of the implementation where every call will be delegated. 11 | * @return address of the implementation to which it will be delegated 12 | */ 13 | function implementation() public view returns (address); 14 | 15 | /** 16 | * @dev Tells the type of proxy (EIP 897) 17 | * @return Type of proxy, 2 for upgradeable proxy 18 | */ 19 | function proxyType() public pure returns (uint256 proxyTypeId); 20 | 21 | /** 22 | * @dev Fallback function allowing to perform a delegatecall to the given implementation. 23 | * This function will return whatever the implementation call returns 24 | */ 25 | function () payable public { 26 | address _impl = implementation(); 27 | require(_impl != address(0)); 28 | 29 | assembly { 30 | let ptr := mload(0x40) 31 | calldatacopy(ptr, 0, calldatasize) 32 | let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0) 33 | let size := returndatasize 34 | returndatacopy(ptr, 0, size) 35 | 36 | switch result 37 | case 0 { revert(ptr, size) } 38 | default { return(ptr, size) } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /doxity/components/contractDropdown.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | import { prefixLink } from 'gatsby-helpers' 3 | import { Menu, Dropdown } from 'semantic-ui-react' 4 | import { Link } from 'react-router' 5 | 6 | export default class ContractDropdown extends Component { 7 | constructor(props) { 8 | super(props) 9 | this.state = { renderHack: true } 10 | } 11 | componentDidMount() { 12 | setTimeout(() => { 13 | this.setState({ renderHack: false }) 14 | }, 0) 15 | } 16 | render() { 17 | if (this.state.renderHack) { return null } 18 | const selected = (this.props.pages || []).find(page => page.path === this.props.location.pathname) 19 | return ( 20 | 21 | 22 | {this.props.pages.map(({ page }) => { 23 | return ( 24 | 31 | ) 32 | })} 33 | 34 | 35 | ) 36 | } 37 | } 38 | 39 | ContractDropdown.propTypes = { 40 | pages: PropTypes.array, 41 | location: PropTypes.object, 42 | } 43 | -------------------------------------------------------------------------------- /test/wyvern-exchange-misc.js: -------------------------------------------------------------------------------- 1 | /* global artifacts:false, it:false, contract:false, assert:false */ 2 | 3 | const WyvernExchange = artifacts.require('WyvernExchange') 4 | 5 | const arrays = [ 6 | '0x1234', 7 | '0x', 8 | '0x123412349873', 9 | '0x43237234', 10 | '0x21982342', 11 | '0x112341234987323412349873', 12 | '0x111234123498732341234987312341234987323412349873', 13 | '0x111234123498732341234987312341234987323412349873219823421234', 14 | '0x111234123498732341234987312341234987323412349873219823421234111234123498732341234987312341234987323412349873219823421234' 15 | ] 16 | 17 | contract('WyvernExchange', (accounts) => { 18 | arrays.map(arr => { 19 | it('should copy array ' + arr, () => { 20 | return WyvernExchange 21 | .deployed() 22 | .then(exchangeInstance => { 23 | return exchangeInstance.testCopy.call(arr).then(ret => { 24 | assert.equal(arr, ret, 'Copied array was unequal') 25 | }) 26 | }) 27 | }) 28 | }) 29 | 30 | accounts.map(acc => { 31 | it('should copy address ' + acc, () => { 32 | return WyvernExchange 33 | .deployed() 34 | .then(exchangeInstance => { 35 | return exchangeInstance.testCopyAddress.call(acc).then(ret => { 36 | assert.equal(ret, acc, 'Copied address was unequal') 37 | }) 38 | }) 39 | }) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /contracts/common/TokenLocker.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Contract to allow an owning contract to receive tokens (ERC20, not ERC223), transfer them at will, and do absolutely nothing else. 4 | 5 | Used to allow DAO shareholders to lock tokens for vote delegation but prevent the DAO from doing anything with the locked tokens. 6 | 7 | Much thanks to @adamkolar on Github - https://github.com/ProjectWyvern/wyvern-ethereum/issues/4 8 | 9 | */ 10 | 11 | pragma solidity 0.4.23; 12 | 13 | import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; 14 | 15 | /** 16 | * @title TokenLocker 17 | * @author Project Wyvern Developers 18 | */ 19 | contract TokenLocker { 20 | 21 | address public owner; 22 | 23 | ERC20 public token; 24 | 25 | /** 26 | * @dev Create a new TokenLocker contract 27 | * @param tokenAddr ERC20 token this contract will be used to lock 28 | */ 29 | constructor (ERC20 tokenAddr) public { 30 | owner = msg.sender; 31 | token = tokenAddr; 32 | } 33 | 34 | /** 35 | * @dev Call the ERC20 `transfer` function on the underlying token contract 36 | * @param dest Token destination 37 | * @param amount Amount of tokens to be transferred 38 | */ 39 | function transfer(address dest, uint amount) public returns (bool) { 40 | require(msg.sender == owner); 41 | return token.transfer(dest, amount); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /doxity/html.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable global-require, import/no-unresolved */ 2 | 3 | import React, { Component, PropTypes } from 'react' 4 | import DocumentTitle from 'react-document-title' 5 | import { prefixLink } from 'gatsby-helpers' 6 | 7 | import favicon from './css/favicon.png' 8 | 9 | const BUILD_TIME = new Date().getTime() 10 | 11 | export default class Html extends Component { 12 | render() { 13 | const title = DocumentTitle.rewind() 14 | 15 | let css 16 | if (process.env.NODE_ENV === 'production') { 17 | css = -------------------------------------------------------------------------------- /docs/docs/ArrayUtils/index.html: -------------------------------------------------------------------------------- 1 | 2 | ArrayUtils | wyvern-ethereum -------------------------------------------------------------------------------- /docs/docs/ReentrancyGuarded/index.html: -------------------------------------------------------------------------------- 1 | 2 | ReentrancyGuarded | wyvern-ethereum -------------------------------------------------------------------------------- /docs/docs/SaleKindInterface/index.html: -------------------------------------------------------------------------------- 1 | 2 | SaleKindInterface | wyvern-ethereum -------------------------------------------------------------------------------- /audits/v2.1/AUDIT_SPEC.md: -------------------------------------------------------------------------------- 1 | Wyvern Protocol v2.1 "Fafnir" Audit Specification 2 | ------------------------------------------------- 3 | 4 | ### Version 2.1 Note 5 | 6 | This is version 2.1 of the Wyvern Protocol, which comprises relatively minor changes from v1. If you audited the previous version, you may find it more efficient to simply view the contract code diff. This can easily be done by running `git diff 969a69f465b662abfde35815fa45c2b94736cf99 contracts` from the repository root. 7 | 8 | Versions 1 and 2 of the protocol are operating live on the Ethereum mainnet, and have already been used to trade [virtual items](https://exchange.projectwyvern.com/orders/0xa2c40276fbb97a87f464336cfbe97d00bcee1da0f491dabb5d935370c589aea8), [digital kittens](https://exchange.projectwyvern.com/orders/0x78fc4f8df1263000495c6dc952b87210cf6198fc254decc7640275e2de80719d), and even [a smart contract](https://exchange.projectwyvern.com/orders/0x43186f200cb8e687d9d2c15d538fb6742f32f9a454430447834d1319039ef214). Playing around with [the Wyvern Exchange](https://exchange.projectwyvern.com/) may help you understand the high-level goals of the protocol; however, please note that the present incarnation of the Exchange UI uses but a small subset of the functionality the protocol smart contracts incorporate. You can also read the [Contract Architecture](https://wiki.projectwyvern.com/contract-architecture) page on the Project Wyvern wiki. 9 | 10 | #### Summary of changes from v2 11 | 12 | - Heavy EVM optimization of masked bytecode replacement (mostly: performed word-wise rather than byte-wise) and byte array operations. See `contracts/common/ArrayUtils.sol`, mostly rewritten from v2. 13 | - Updated to solc v0.23 (emit before events, some minor syntax changes) 14 | - Updated `zeppelin-solidity` dependency 15 | - Manual (assembly) order hash calculation 16 | 17 | ### Full Audit Specification 18 | 19 | The Wyvern Protocol is an Ethereum framework for the exchange of nonfungible digital assets. Protocol users - human-operated Ethereum accounts or other Ethereum smart contracts - place orders expressing the intent to sell or buy a particular asset or any asset with certain characteristics. The protocol's job is to match buyer and seller intent on-chain such that the asset transfer and payment happen atomically. The protocol functions solely as a settlement layer - orderbook storage and matching algorithms are left to off-chain infrastructure. 20 | 21 | The protocol is representation-agnostic: it supports any asset that can be represented on the Ethereum chain (i.e., transferred in an Ethereum transaction or a sequence of transactions). Users will be able to buy and sell anything from CryptoKitties to ENS names to smart contracts themselves. The protocol "knows nothing" about asset representations - instead, buyer and seller intents are specified as functions over the space of Ethereum transactions, as follows: 22 | 23 | - Buy-side and sell-side orders each provide calldata (bytes) - for a sell-side order, the state transition for sale, for a buy-side order, the state transition to be bought. Along with the calldata, orders provide `replacementPattern`: a bytemask indicating which bytes of the calldata can be changed (e.g. NFT destination address). When a buy-side and sell-side order are matched, the desired calldatas are unified, masked with the bytemasks, and checked for agreement. This alone is enough to implement common simple state transitions, such as "transfer my CryptoKitty to any address" or "buy any of this kind of nonfungible token". 24 | - Orders of either side can optionally specify a static (no state modification) callback function, which receives configurable data along with the actual calldata as a parameter. This allows for arbitrary transaction validation functions. For example, a buy-sider order could express the intent to buy any CryptoKitty with a particular set of characteristics (checked in the static call), or a sell-side order could express the intent to sell any of three ENS names, but not two others. Use of the EVM's STATICCALL opcode, added in Ethereum Metropolis, allows the static calldata to be safely specified separately and thus this kind of matching to happen correctly - that is to say, wherever the two validation callbacks mapping Ethereum transactions to booleans intersect. 25 | 26 | The following contracts are within the scope of this audit (and together comprise complete functionality of the protocol): 27 | 28 | #### WyvernAtomicizer.sol 29 | 30 | Top-level atomicizer library. Provides a simple method to serialize multiple transactions at runtime (all-or-nothing; if one fails all are reverted). 31 | 32 | #### WyvernTokenTransferProxy.sol 33 | 34 | Top-level token transfer proxy contract, inherits from TokenTransferProxy.sol 35 | 36 | #### WyvernProxyRegistry.sol 37 | 38 | Top-level proxy registry contract, inherits from ProxyRegistry.sol. Facilitates a once-only immediate authentication of a contract to access user-created proxies. 39 | 40 | #### WyvernDAOProxy.sol 41 | 42 | Top-level delegate proxy contract, inherits from DelegateProxy. 43 | 44 | #### DelegateProxy.sol 45 | 46 | Simple, single-owner DELEGATECALL proxy contract. Designed to allow accounts / contracts which can only issue CALLs to make use of the Atomicizer library to serialize transactions. 47 | 48 | #### TokenTransferProxy.sol 49 | 50 | Simple proxy contract to authenticate ERC20 `transferFrom` calls. Uses the authentication table of a `ProxyRegistry` contract (so users will only need to call ERC20 `approve` once for all future protocol versions). 51 | 52 | #### AuthenticatedProxy.sol 53 | 54 | Proxy contract deployed by protocol users to hold assets on their behalf and transfer them when conditions for order matching are met. Facilitates arbitrary passthrough CALLs or DELEGATECALLs, performed either by the user who created the contract or by the Exchange contract, authenticated through the ProxyRegistry contract, when the conditions for order matching are met. 55 | 56 | #### ProxyRegistry.sol 57 | 58 | Proxy registry contract. Keeps a mapping of users to proxy contracts so the Exchange contract can look up the proxy contract for a particular order. Separate from the Exchange (a) to reduce Exchange attack surface and (b) to facilitate Exchange protocol upgrades, such as supporting a different kind of Dutch auction, without requiring that users transfer assets to new proxy contracts. The Registry will be controlled by the Wyvern DAO, which can authenticate new protocol versions after a mandatory delay period to prevent against possible economic attacks. 59 | 60 | #### WyvernExchange.sol 61 | 62 | Top-level exchange contract, inherits from Exchange.sol. No independent functionality. 63 | 64 | #### ArrayUtils.sol 65 | 66 | Utility library. Facilities masked byte array replacement and byte array equality comparision. Used by ExchangeCore. 67 | 68 | #### ReentrancyGuarded.sol 69 | 70 | Utility library. Function modifier to guard a function with a contract-global reentrancy prevention lock. Used by ExchangeCore. 71 | 72 | #### TokenRecipient.sol 73 | 74 | Utility library. Logs receipt of tokens and implements the default payable function. Used by AuthenticatedProxy. 75 | 76 | #### SaleKindInterface.sol 77 | 78 | Utility library. Facilitates validation of order parameters (fixed price / Dutch auction & timestamps) and calculates final order prices when orders are matched. 79 | 80 | #### Exchange.sol 81 | 82 | Public interface library, inherits from ExchangeCore. No independent state-modifying functionality. Exposes internal ExchangeCore functions with struct conversions (the internal functions use structs, external encoding for which the Solidity encoder does not yet support) and exposes a few convenience read-only methods. 83 | 84 | #### ExchangeCore.sol 85 | 86 | Core protocol contract. Facilitates order approval, order validation, order cancellation, and atomic order matching. The ExchangeCore contract holds no tokens or assets itself - ERC20 tokens are held by protocol users (who must call `approve`) and nonfungible assets are held by AuthenticatedProxy contracts (from which the users can withdraw the assets at any time). 87 | 88 | Deployed contracts on Rinkeby: 89 | 90 | [Wyvern Exchange](https://rinkeby.etherscan.io/address/0x838d2403a061b459e15b494e78acae2bb3333dda) 91 | 92 | [Wyvern Atomicizer](https://rinkeby.etherscan.io/address/0x6bec0bcf2834d34454942964b7b6f0537032546e) 93 | 94 | [Wyvern DAO Proxy](https://rinkeby.etherscan.io/address/0xb44cc6d88168852f77755982936a20b00d971415) 95 | 96 | [Wyvern Token Transfer Proxy](https://rinkeby.etherscan.io/address/0x03851346f00bc9166623197cce189e68d7f847dc) 97 | 98 | [Wyvern Proxy Registry](https://rinkeby.etherscan.io/address/0xb7297a9ae13e73e36069413f13d37c79b97db773) 99 | 100 | Note that the [wyvern-ethereum](https://github.com/projectwyvern/wyvern-ethereum) repository also contains contracts for the WYV token and the Wyvern DAO. Those contracts have already been deployed and are not within the scope of this audit (nor are they relevant to the functionality or correctness of the Wyvern Protocol). 101 | 102 | Individual function documentation can be found in the source code, written using Ethereum's NatSpec format. A rendered version of the documentation is also available at [docs.projectwyvern.com](https://docs.projectwyvern.com). 103 | 104 | For additional background on the Wyvern project and the motivation behind it, you may find [the whitepaper](https://github.com/ProjectWyvern/wyvern-protocol/raw/master/build/whitepaper.pdf) useful - however, the whitepaper's description of the protocol may not be entirely up-to-date; the smart contract versions submitted to the audit are canonical. 105 | -------------------------------------------------------------------------------- /audits/v2/AUDIT_SPEC.md: -------------------------------------------------------------------------------- 1 | Wyvern Protocol v2 "Bakunawa" Audit Specification 2 | ------------------------------------------------- 3 | 4 | ### Version 2 Note 5 | 6 | This is version 2 of the Wyvern Protocol, which comprises relatively minor changes from v1. If you audited the previous version, you may find it more efficient to simply view the contract code diff. This can easily be done by running `git diff b18dd6a9f4a8260a59eeb678914ade5ce92d6c5f contracts` from the repository root. 7 | 8 | Version 1 of the protocol is operating live on the Ethereum mainnet, and has already been used to trade [virtual items](https://exchange.projectwyvern.com/orders/0xa2c40276fbb97a87f464336cfbe97d00bcee1da0f491dabb5d935370c589aea8), [digital kittens](https://exchange.projectwyvern.com/orders/0x78fc4f8df1263000495c6dc952b87210cf6198fc254decc7640275e2de80719d), and even [a smart contract](https://exchange.projectwyvern.com/orders/0x43186f200cb8e687d9d2c15d538fb6742f32f9a454430447834d1319039ef214). Playing around with [the Wyvern Exchange](https://exchange.projectwyvern.com/) may help you understand the high-level goals of the protocol; however, please note that the present incarnation of the Exchange UI uses but a small subset of the functionality the protocol smart contracts incorporate. 9 | 10 | #### Summary of changes from v1 11 | 12 | - Addition of standard (unwrapped) Ether as a payment option for orders. Implemented by a sentinel `paymentToken` value in the order schema (zero-address). 13 | - The use of a proxy contract to authenticate ERC20 `transferFrom` calls, so Exchange users only need to call `approve` once for all future protocol versions. 14 | - The addition of split relayer/protocol fees as an alternative fee method to that of a protocol token. See [here](https://github.com/ProjectWyvern/WDPs/issues/6) for context (although as an auditor you are not required to evaluate Wyvern's strategy - only the smart contracts!). 15 | - The addition of a standard "Atomicizer" library to easily serialize multiple transactions, specified by bytecode at runtime, into a single transaction (e.g. a user selling multiple assets as a bundle). 16 | - The addition of a standard "DelegateProxy" contract to proxy an account or contract only able to execute CALLs (not DELEGATECALLs) and allow said account or contract to make use of the "Atomicizer" library. This will be used by the Wyvern DAO. 17 | - A few gas optimizations (mostly avoiding calculating the order hash when unnecessary). 18 | 19 | ### Full Audit Specification 20 | 21 | The Wyvern Protocol is an Ethereum framework for the exchange of nonfungible digital assets. Protocol users - human-operated Ethereum accounts or other Ethereum smart contracts - place orders expressing the intent to sell or buy a particular asset or any asset with certain characteristics. The protocol's job is to match buyer and seller intent on-chain such that the asset transfer and payment happen atomically. The protocol functions solely as a settlement layer - orderbook storage and matching algorithms are left to off-chain infrastructure. 22 | 23 | The protocol is representation-agnostic: it supports any asset that can be represented on the Ethereum chain (i.e., transferred in an Ethereum transaction or a sequence of transactions). Users will be able to buy and sell anything from CryptoKitties to ENS names to smart contracts themselves. The protocol "knows nothing" about asset representations - instead, buyer and seller intents are specified as functions over the space of Ethereum transactions, as follows: 24 | 25 | - Buy-side and sell-side orders each provide calldata (bytes) - for a sell-side order, the state transition for sale, for a buy-side order, the state transition to be bought. Along with the calldata, orders provide `replacementPattern`: a bytemask indicating which bytes of the calldata can be changed (e.g. NFT destination address). When a buy-side and sell-side order are matched, the desired calldatas are unified, masked with the bytemasks, and checked for agreement. This alone is enough to implement common simple state transitions, such as "transfer my CryptoKitty to any address" or "buy any of this kind of nonfungible token". 26 | - Orders of either side can optionally specify a static (no state modification) callback function, which receives configurable data along with the actual calldata as a parameter. This allows for arbitrary transaction validation functions. For example, a buy-sider order could express the intent to buy any CryptoKitty with a particular set of characteristics (checked in the static call), or a sell-side order could express the intent to sell any of three ENS names, but not two others. Use of the EVM's STATICCALL opcode, added in Ethereum Metropolis, allows the static calldata to be safely specified separately and thus this kind of matching to happen correctly - that is to say, wherever the two validation callbacks mapping Ethereum transactions to booleans intersect. 27 | 28 | The following contracts are within the scope of this audit (and together comprise complete functionality of the protocol): 29 | 30 | #### WyvernAtomicizer.sol 31 | 32 | Top-level atomicizer library. Provides a simple method to serialize multiple transactions at runtime (all-or-nothing; if one fails all are reverted). 33 | 34 | #### WyvernTokenTransferProxy.sol 35 | 36 | Top-level token transfer proxy contract, inherits from TokenTransferProxy.sol 37 | 38 | #### WyvernProxyRegistry.sol 39 | 40 | Top-level proxy registry contract, inherits from ProxyRegistry.sol. Facilitates a once-only immediate authentication of a contract to access user-created proxies. 41 | 42 | #### WyvernDAOProxy.sol 43 | 44 | Top-level delegate proxy contract, inherits from DelegateProxy. 45 | 46 | #### DelegateProxy.sol 47 | 48 | Simple, single-owner DELEGATECALL proxy contract. Designed to allow accounts / contracts which can only issue CALLs to make use of the Atomicizer library to serialize transactions. 49 | 50 | #### TokenTransferProxy.sol 51 | 52 | Simple proxy contract to authenticate ERC20 `transferFrom` calls. Uses the authentication table of a `ProxyRegistry` contract (so users will only need to call ERC20 `approve` once for all future protocol versions). 53 | 54 | #### AuthenticatedProxy.sol 55 | 56 | Proxy contract deployed by protocol users to hold assets on their behalf and transfer them when conditions for order matching are met. Facilitates arbitrary passthrough CALLs or DELEGATECALLs, performed either by the user who created the contract or by the Exchange contract, authenticated through the ProxyRegistry contract, when the conditions for order matching are met. 57 | 58 | #### ProxyRegistry.sol 59 | 60 | Proxy registry contract. Keeps a mapping of users to proxy contracts so the Exchange contract can look up the proxy contract for a particular order. Separate from the Exchange (a) to reduce Exchange attack surface and (b) to facilitate Exchange protocol upgrades, such as supporting a different kind of Dutch auction, without requiring that users transfer assets to new proxy contracts. The Registry will be controlled by the Wyvern DAO, which can authenticate new protocol versions after a mandatory delay period to prevent against possible economic attacks. 61 | 62 | #### WyvernExchange.sol 63 | 64 | Top-level exchange contract, inherits from Exchange.sol. No independent functionality. 65 | 66 | #### ArrayUtils.sol 67 | 68 | Utility library. Facilities masked byte array replacement and byte array equality comparision. Used by ExchangeCore. 69 | 70 | #### ReentrancyGuarded.sol 71 | 72 | Utility library. Function modifier to guard a function with a contract-global reentrancy prevention lock. Used by ExchangeCore. 73 | 74 | #### TokenRecipient.sol 75 | 76 | Utility library. Logs receipt of tokens and implements the default payable function. Used by AuthenticatedProxy. 77 | 78 | #### SaleKindInterface.sol 79 | 80 | Utility library. Facilitates validation of order parameters (fixed price / Dutch auction & timestamps) and calculates final order prices when orders are matched. 81 | 82 | #### Exchange.sol 83 | 84 | Public interface library, inherits from ExchangeCore. No independent state-modifying functionality. Exposes internal ExchangeCore functions with struct conversions (the internal functions use structs, external encoding for which the Solidity encoder does not yet support) and exposes a few convenience read-only methods. 85 | 86 | #### ExchangeCore.sol 87 | 88 | Core protocol contract. Facilitates order approval, order validation, order cancellation, and atomic order matching. The ExchangeCore contract holds no tokens or assets itself - ERC20 tokens are held by protocol users (who must call `approve`) and nonfungible assets are held by AuthenticatedProxy contracts (from which the users can withdraw the assets at any time). 89 | 90 | Deployed contracts on Rinkeby: 91 | 92 | [Wyvern Exchange](https://rinkeby.etherscan.io/address/0xdca1fbe9f9469613aa2101b5e797226a9b586297) 93 | 94 | [Wyvern Atomicizer](https://rinkeby.etherscan.io/address/0x90b0c4d26520be6a941954d565f90ecf2991d8a7) 95 | 96 | [Wyvern DAO Proxy](https://rinkeby.etherscan.io/address/0x32f51cefe7d1cac49334b7267da6ae7a127526da) 97 | 98 | [Wyvern Token Transfer Proxy](https://rinkeby.etherscan.io/address/0xb89f6ac677a7530d9d6649d299350be90a50ad1e) 99 | 100 | [Wyvern Proxy Registry](https://rinkeby.etherscan.io/address/0xeceaa7453a77bfe339b25d9d9e91009cde71c768) 101 | 102 | Note that the [wyvern-ethereum](https://github.com/projectwyvern/wyvern-ethereum) repository also contains contracts for the WYV token and the Wyvern DAO. Those contracts have already been deployed and are not within the scope of this audit (nor are they relevant to the functionality or correctness of the Wyvern Protocol). 103 | 104 | Individual function documentation can be found in the source code, written using Ethereum's NatSpec format. A rendered version of the documentation is also available at [docs.projectwyvern.com](https://docs.projectwyvern.com). 105 | 106 | For additional background on the Wyvern project and the motivation behind it, you may find [the whitepaper](https://github.com/ProjectWyvern/wyvern-protocol/raw/master/build/whitepaper.pdf) useful - however, the whitepaper's description of the protocol may not be entirely up-to-date; the smart contract versions submitted to the audit are canonical. 107 | -------------------------------------------------------------------------------- /docs/docs/MerkleProof/index.html: -------------------------------------------------------------------------------- 1 | 2 | MerkleProof | wyvern-ethereum -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | wyvern-ethereum

Project Wyvern Logo

3 |

Project Wyvern Ethereum Smart Contracts

4 |

https://badges.frapsoft.com/os/mit/mit.svg?v=102 Build Status Coverage Status

5 |

Synopsis

6 |

Autonomously governed decentralized digital asset exchange.

7 |

These are the Ethereum smart contracts for the Wyvern Protocol, the Wyvern ERC20 token (WYV), and the Wyvern DAO. For general information on the Wyvern project, please see the website.

8 |

Deployed Contracts

9 |

Please note: correct deployed contract addresses will always be in config.json. If you wish to import this repository directly, please use that file. The addresses in Truffle build output are not necessarily accurate.

10 |

Mainnet

11 |

Wyvern Exchange

12 |

Wyvern Proxy Registry

13 |

Wyvern Token

14 |

Wyvern DAO

15 |

Rinkeby Testnet

16 |

Wyvern Exchange

17 |

Wyvern Atomicizer

18 |

Wyvern DAO Proxy

19 |

Wyvern Token Transfer Proxy

20 |

Wyvern Proxy Registry

21 |

Wyvern Token

22 |

Wyvern DAO

23 |

Development Information

24 |

Setup

25 |

Node >= v6.9.1 and Yarn required.

26 |

Before any development, install the required NPM dependencies:

27 |
yarn
28 | 
29 |

Testing

30 |

Start Ethereum’s testrpc tool to provide a Web3 interface (leave this running):

31 |
yarn testrpc
32 | 
33 |

Compile the latest smart contracts:

34 |
yarn compile
35 | 
36 |

Run the testsuite against the simulated network:

37 |
yarn test
38 | 
39 |

Make sure to lint the Solidity files once you’re done:

40 |
yarn lint
41 | 
42 |

Generating Documentation

43 |

Install the dependencies:

44 |
cd doxity
45 | yarn
46 | cd ..
47 | 
48 |

Autogenerate documentation from Ethereum Natspec using Doxity:

49 |
yarn doc
50 | 
51 |

Final output will be written to docs, which will be automatically published on push to GitHub Pages at docs.projectwyvern.com.

52 |

Misc

53 |

Run automated smart contract analysis (requires Oyente and Mythril):

54 |
yarn analyze
55 | 
56 |

Flatten contract source (for e.g. Etherscan verification, requires solidity-flattener to be installed):

57 |
yarn flatten
58 | 
59 |

Contributing

60 |

Contributions welcome! Please use GitHub issues for suggestions/concerns - if you prefer to express your intentions in code, feel free to submit a pull request.

61 |
-------------------------------------------------------------------------------- /contracts/token/UTXORedeemableToken.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | UTXO redeemable token. 4 | 5 | This is a token extension to allow porting a Bitcoin or Bitcoin-fork sourced UTXO set to an ERC20 token through redemption of individual UTXOs in the token contract. 6 | 7 | Owners of UTXOs in a chosen final set (where "owner" is simply anyone who could have spent the UTXO) are allowed to redeem (mint) a number of tokens proportional to the satoshi amount of the UTXO. 8 | 9 | Notes 10 | 11 | - This method *does not* provision for special Bitcoin scripts (e.g. multisig addresses). 12 | - Pending transactions are public, so the UTXO redemption transaction must work *only* for an Ethereum address belonging to the same person who owns the UTXO. 13 | This is enforced by requiring that the redeeemer sign their Ethereum address with their Bitcoin (original-chain) private key. 14 | - We cannot simply store the UTXO set, as that would be far too expensive. Instead we compute a Merkle tree for the entire UTXO set at the chain state which is to be ported, 15 | store only the root of that Merkle tree, and require UTXO claimants prove that the UTXO they wish to claim is present in the tree. 16 | 17 | */ 18 | 19 | pragma solidity 0.4.23; 20 | 21 | import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; 22 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 23 | import "openzeppelin-solidity/contracts/MerkleProof.sol"; 24 | 25 | /** 26 | * @title UTXORedeemableToken 27 | * @author Project Wyvern Developers 28 | */ 29 | contract UTXORedeemableToken is StandardToken { 30 | 31 | /* Root hash of the UTXO Merkle tree, must be initialized by token constructor. */ 32 | bytes32 public rootUTXOMerkleTreeHash; 33 | 34 | /* Redeemed UTXOs. */ 35 | mapping(bytes32 => bool) redeemedUTXOs; 36 | 37 | /* Multiplier - tokens per Satoshi, must be initialized by token constructor. */ 38 | uint public multiplier; 39 | 40 | /* Total tokens redeemed so far. */ 41 | uint public totalRedeemed = 0; 42 | 43 | /* Maximum redeemable tokens, must be initialized by token constructor. */ 44 | uint public maximumRedeemable; 45 | 46 | /* Redemption event, containing all relevant data for later analysis if desired. */ 47 | event UTXORedeemed(bytes32 txid, uint8 outputIndex, uint satoshis, bytes32[] proof, bytes pubKey, uint8 v, bytes32 r, bytes32 s, address indexed redeemer, uint numberOfTokens); 48 | 49 | /** 50 | * @dev Extract a bytes32 subarray from an arbitrary length bytes array. 51 | * @param data Bytes array from which to extract the subarray 52 | * @param pos Starting position from which to copy 53 | * @return Extracted length 32 byte array 54 | */ 55 | function extract(bytes data, uint pos) private pure returns (bytes32 result) { 56 | for (uint i = 0; i < 32; i++) { 57 | result ^= (bytes32(0xff00000000000000000000000000000000000000000000000000000000000000) & data[i + pos]) >> (i * 8); 58 | } 59 | return result; 60 | } 61 | 62 | /** 63 | * @dev Validate that a provided ECSDA signature was signed by the specified address 64 | * @param hash Hash of signed data 65 | * @param v v parameter of ECDSA signature 66 | * @param r r parameter of ECDSA signature 67 | * @param s s parameter of ECDSA signature 68 | * @param expected Address claiming to have created this signature 69 | * @return Whether or not the signature was valid 70 | */ 71 | function validateSignature (bytes32 hash, uint8 v, bytes32 r, bytes32 s, address expected) public pure returns (bool) { 72 | return ecrecover(hash, v, r, s) == expected; 73 | } 74 | 75 | /** 76 | * @dev Validate that the hash of a provided address was signed by the ECDSA public key associated with the specified Ethereum address 77 | * @param addr Address signed 78 | * @param pubKey Uncompressed ECDSA public key claiming to have created this signature 79 | * @param v v parameter of ECDSA signature 80 | * @param r r parameter of ECDSA signature 81 | * @param s s parameter of ECDSA signature 82 | * @return Whether or not the signature was valid 83 | */ 84 | function ecdsaVerify (address addr, bytes pubKey, uint8 v, bytes32 r, bytes32 s) public pure returns (bool) { 85 | return validateSignature(sha256(addr), v, r, s, pubKeyToEthereumAddress(pubKey)); 86 | } 87 | 88 | /** 89 | * @dev Convert an uncompressed ECDSA public key into an Ethereum address 90 | * @param pubKey Uncompressed ECDSA public key to convert 91 | * @return Ethereum address generated from the ECDSA public key 92 | */ 93 | function pubKeyToEthereumAddress (bytes pubKey) public pure returns (address) { 94 | return address(uint(keccak256(pubKey)) & 0x000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); 95 | } 96 | 97 | /** 98 | * @dev Calculate the Bitcoin-style address associated with an ECDSA public key 99 | * @param pubKey ECDSA public key to convert 100 | * @param isCompressed Whether or not the Bitcoin address was generated from a compressed key 101 | * @return Raw Bitcoin address (no base58-check encoding) 102 | */ 103 | function pubKeyToBitcoinAddress(bytes pubKey, bool isCompressed) public pure returns (bytes20) { 104 | /* Helpful references: 105 | - https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses 106 | - https://github.com/cryptocoinjs/ecurve/blob/master/lib/point.js 107 | */ 108 | 109 | /* x coordinate - first 32 bytes of public key */ 110 | uint x = uint(extract(pubKey, 0)); 111 | /* y coordinate - second 32 bytes of public key */ 112 | uint y = uint(extract(pubKey, 32)); 113 | uint8 startingByte; 114 | if (isCompressed) { 115 | /* Hash the compressed public key format. */ 116 | startingByte = y % 2 == 0 ? 0x02 : 0x03; 117 | return ripemd160(sha256(startingByte, x)); 118 | } else { 119 | /* Hash the uncompressed public key format. */ 120 | startingByte = 0x04; 121 | return ripemd160(sha256(startingByte, x, y)); 122 | } 123 | } 124 | 125 | /** 126 | * @dev Verify a Merkle proof using the UTXO Merkle tree 127 | * @param proof Generated Merkle tree proof 128 | * @param merkleLeafHash Hash asserted to be present in the Merkle tree 129 | * @return Whether or not the proof is valid 130 | */ 131 | function verifyProof(bytes32[] proof, bytes32 merkleLeafHash) public view returns (bool) { 132 | return MerkleProof.verifyProof(proof, rootUTXOMerkleTreeHash, merkleLeafHash); 133 | } 134 | 135 | /** 136 | * @dev Convenience helper function to check if a UTXO can be redeemed 137 | * @param txid Transaction hash 138 | * @param originalAddress Raw Bitcoin address (no base58-check encoding) 139 | * @param outputIndex Output index of UTXO 140 | * @param satoshis Amount of UTXO in satoshis 141 | * @param proof Merkle tree proof 142 | * @return Whether or not the UTXO can be redeemed 143 | */ 144 | function canRedeemUTXO(bytes32 txid, bytes20 originalAddress, uint8 outputIndex, uint satoshis, bytes32[] proof) public view returns (bool) { 145 | /* Calculate the hash of the Merkle leaf associated with this UTXO. */ 146 | bytes32 merkleLeafHash = keccak256(txid, originalAddress, outputIndex, satoshis); 147 | 148 | /* Verify the proof. */ 149 | return canRedeemUTXOHash(merkleLeafHash, proof); 150 | } 151 | 152 | /** 153 | * @dev Verify that a UTXO with the specified Merkle leaf hash can be redeemed 154 | * @param merkleLeafHash Merkle tree hash of the UTXO to be checked 155 | * @param proof Merkle tree proof 156 | * @return Whether or not the UTXO with the specified hash can be redeemed 157 | */ 158 | function canRedeemUTXOHash(bytes32 merkleLeafHash, bytes32[] proof) public view returns (bool) { 159 | /* Check that the UTXO has not yet been redeemed and that it exists in the Merkle tree. */ 160 | return((redeemedUTXOs[merkleLeafHash] == false) && verifyProof(proof, merkleLeafHash)); 161 | } 162 | 163 | /** 164 | * @dev Redeem a UTXO, crediting a proportional amount of tokens (if valid) to the sending address 165 | * @param txid Transaction hash 166 | * @param outputIndex Output index of the UTXO 167 | * @param satoshis Amount of UTXO in satoshis 168 | * @param proof Merkle tree proof 169 | * @param pubKey Uncompressed ECDSA public key to which the UTXO was sent 170 | * @param isCompressed Whether the Bitcoin address was generated from a compressed public key 171 | * @param v v parameter of ECDSA signature 172 | * @param r r parameter of ECDSA signature 173 | * @param s s parameter of ECDSA signature 174 | * @return The number of tokens redeemed, if successful 175 | */ 176 | function redeemUTXO (bytes32 txid, uint8 outputIndex, uint satoshis, bytes32[] proof, bytes pubKey, bool isCompressed, uint8 v, bytes32 r, bytes32 s) public returns (uint tokensRedeemed) { 177 | 178 | /* Calculate original Bitcoin-style address associated with the provided public key. */ 179 | bytes20 originalAddress = pubKeyToBitcoinAddress(pubKey, isCompressed); 180 | 181 | /* Calculate the UTXO Merkle leaf hash. */ 182 | bytes32 merkleLeafHash = keccak256(txid, originalAddress, outputIndex, satoshis); 183 | 184 | /* Verify that the UTXO can be redeemed. */ 185 | require(canRedeemUTXOHash(merkleLeafHash, proof)); 186 | 187 | /* Claimant must sign the Ethereum address to which they wish to remit the redeemed tokens. */ 188 | require(ecdsaVerify(msg.sender, pubKey, v, r, s)); 189 | 190 | /* Mark the UTXO as redeemed. */ 191 | redeemedUTXOs[merkleLeafHash] = true; 192 | 193 | /* Calculate the redeemed tokens. */ 194 | tokensRedeemed = SafeMath.mul(satoshis, multiplier); 195 | 196 | /* Track total redeemed tokens. */ 197 | totalRedeemed = SafeMath.add(totalRedeemed, tokensRedeemed); 198 | 199 | /* Sanity check. */ 200 | require(totalRedeemed <= maximumRedeemable); 201 | 202 | /* Credit the redeemer. */ 203 | balances[msg.sender] = SafeMath.add(balances[msg.sender], tokensRedeemed); 204 | 205 | /* Mark the transfer event. */ 206 | emit Transfer(address(0), msg.sender, tokensRedeemed); 207 | 208 | /* Mark the UTXO redemption event. */ 209 | emit UTXORedeemed(txid, outputIndex, satoshis, proof, pubKey, v, r, s, msg.sender, tokensRedeemed); 210 | 211 | /* Return the number of tokens redeemed. */ 212 | return tokensRedeemed; 213 | 214 | } 215 | 216 | } 217 | --------------------------------------------------------------------------------