├── test ├── .gitkeep ├── test-tokens │ ├── truffle.js │ ├── migrations │ │ ├── 1_initial_migration.js │ │ ├── util │ │ │ └── migrationUtils.js │ │ ├── 3_deploy_OMG.js │ │ ├── 2_deploy_RDN.js │ │ ├── 5_deploy_GRID,CVC.js │ │ └── 4_deploy_DAI,GEN,KNC,MKR.js │ ├── contracts │ │ ├── TokenCVC.sol │ │ ├── TokenOMG.sol │ │ ├── TokenRDN.sol │ │ ├── TokenDAI.sol │ │ ├── TokenGEN.sol │ │ ├── TokenKNC.sol │ │ ├── TokenMKR.sol │ │ ├── TokenGRID.sol │ │ ├── Migrations.sol │ │ └── TestToken.sol │ ├── README.md │ ├── src │ │ ├── inject_network_info.js │ │ ├── extract_network_info.js │ │ └── conf │ │ │ └── network-restore.js │ ├── package.json │ └── networks.json ├── resources │ ├── add-token-pair │ │ ├── kovan │ │ │ ├── 01_RDN-WETH.js │ │ │ ├── WETH_GNO.js │ │ │ ├── WETH_RDN.js │ │ │ └── RDN_OMG.js │ │ ├── mainnet │ │ │ ├── 01_RDN-WETH.js │ │ │ ├── 02_OMG-WETH.js │ │ │ ├── 03_dxDao.js │ │ │ ├── WETH_GNO.js │ │ │ ├── WETH_OMG.js │ │ │ └── WETH_RDN.js │ │ └── rinkeby │ │ │ ├── WETH_CVC.js │ │ │ ├── WETH_GRID.js │ │ │ ├── WETH_OMG.js │ │ │ ├── WETH_RDN.js │ │ │ └── RDN_OMG.js │ └── approve-tokens │ │ ├── rdnAndOmg-mainnet.js │ │ └── rdnAndOmg-rinkeby.js ├── trufflescripts │ ├── snapshot.js │ ├── get_account_deposits.js │ ├── get_account_balances.js │ ├── revert.js │ ├── deposit.js │ ├── withdraw.js │ ├── utils │ │ └── index.js │ ├── give_tokens.js │ ├── topup_accounts.js │ ├── buy_order.js │ ├── sell_order.js │ ├── claim_funds.js │ ├── deposit_and_sell.js │ ├── add_token_pair.js │ └── increase_time.js ├── dutchExchange-depositWithdrawBadToken.js ├── dutchExchange-DepositAndSell.spec.js ├── dutchExchange-priceOracle.spec.js ├── dutchExchange-getCurrentAuctionPrice.spec.js ├── dutchExchange-TokenApproval.spec.js ├── dutchExchange-UpdateExchangeParams.spec.js └── dutchExchange-Proxy.spec.js ├── .nvmrc ├── .soliumignore ├── .eslintignore ├── .prettierignore ├── docs ├── StateDiagram.png ├── DutchX_Documentation.pdf ├── DutchX_1.0_Audit_Report.pdf └── DutchX_2.0_Audit_Report.pdf ├── .gitattributes ├── migrations ├── 1_initial_migration.js ├── 5_deploy_DX.js ├── 4_deploy_FRT.js ├── 3_DEV-deploy_price_feed.js ├── 6_setup_DX.js ├── 2_DEV_migrate_dependencies.js └── 7_set_DX_as_FRT_minter.js ├── contracts ├── Oracle │ ├── DSThing.sol │ ├── DSValue.sol │ ├── DSNote.sol │ ├── PriceFeed.sol │ ├── DSAuth.sol │ ├── PriceOracleInterface.sol │ ├── Medianizer.sol │ └── DSMath.sol ├── DutchExchangeProxy.sol ├── .solhint.json ├── ForTestingOnly │ ├── TokenGNO.sol │ ├── TokenOMG.sol │ ├── TokenRDN.sol │ ├── BadToken.sol │ ├── InternalTests.sol │ └── SubStandardToken.sol ├── Migrations.sol ├── TokenFRTProxy.sol ├── base │ ├── AuctioneerManaged.sol │ ├── TokenWhitelist.sol │ ├── DxUpgrade.sol │ ├── SafeTransfer.sol │ ├── EthOracle.sol │ └── DxMath.sol ├── DxDevDependencies.sol └── TokenFRT.sol ├── .eslintrc.js ├── .env.example ├── .env ├── src ├── inject_network_info.js ├── extract_network_info.js ├── conf │ └── network-restore.js ├── inject_network_info_deps_gno.js ├── inject_network_info_deps_utils.js ├── migrations-truffle-4 │ ├── 5_deploy_DX.js │ ├── 7_set_DX_as_FRT_minter.js │ ├── index.js │ ├── 2_migrate_dependencies.js │ ├── 4_deploy_FRT.js │ ├── EXAMPLE_migrate_all.js │ ├── 3_deploy_price_feed.js │ └── 6_setup_DX.js ├── migrations-truffle-5 │ ├── 5_deploy_DX.js │ ├── index.js │ ├── 7_set_DX_as_FRT_minter.js │ ├── 2_migrate_dependencies.js │ ├── 4_deploy_FRT.js │ ├── EXAMPLE_migrate_all.js │ ├── 3_deploy_price_feed.js │ └── 6_setup_DX.js └── truffle │ ├── get-abi-encoded-params.js │ ├── unlock-mgn.js │ ├── set-auctioneer.js │ ├── wrap-eth.js │ ├── set-weth-allowance.js │ ├── deposit-weth.js │ └── claim-unlocked-mgn.js ├── .npmignore ├── .soliumrc.json ├── travscripts ├── AfterScript.sh └── BranchScript.sh ├── .gitignore ├── scripts ├── extract_network_info.js ├── inject_network_info.js └── extractTopTokensEtherscanWeb.js ├── .solcover.js ├── .travis.yml ├── truffle.js ├── package.json ├── deploy-mainnet.txt ├── deploy-kovan.txt └── deploy-rinkeby.txt /test/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v8.1.0 2 | -------------------------------------------------------------------------------- /.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | docs 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build 2 | contracts 3 | .* 4 | node_modules 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .history 2 | docs 3 | scripts 4 | migrations 5 | travscripts -------------------------------------------------------------------------------- /docs/StateDiagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnosis/dx-contracts/HEAD/docs/StateDiagram.png -------------------------------------------------------------------------------- /docs/DutchX_Documentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnosis/dx-contracts/HEAD/docs/DutchX_Documentation.pdf -------------------------------------------------------------------------------- /test/test-tokens/truffle.js: -------------------------------------------------------------------------------- 1 | const truffleConfig = require('../../truffle') 2 | 3 | module.exports = truffleConfig -------------------------------------------------------------------------------- /docs/DutchX_1.0_Audit_Report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnosis/dx-contracts/HEAD/docs/DutchX_1.0_Audit_Report.pdf -------------------------------------------------------------------------------- /docs/DutchX_2.0_Audit_Report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gnosis/dx-contracts/HEAD/docs/DutchX_2.0_Audit_Report.pdf -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | *.pdf -diff 3 | *.sol linguist-language=Solidity 4 | #* text eol=lf -------------------------------------------------------------------------------- /test/resources/add-token-pair/kovan/01_RDN-WETH.js: -------------------------------------------------------------------------------- 1 | // This file can be either JSON, or a JS exporting an object 2 | 3 | module.exports = [ 4 | // WETH-RDN 5 | require('./WETH_RDN.js') 6 | ] -------------------------------------------------------------------------------- /test/resources/add-token-pair/mainnet/01_RDN-WETH.js: -------------------------------------------------------------------------------- 1 | // This file can be either JSON, or a JS exporting an object 2 | 3 | module.exports = [ 4 | // WETH-RDN 5 | require('./WETH_RDN.js') 6 | ] 7 | -------------------------------------------------------------------------------- /test/resources/add-token-pair/mainnet/02_OMG-WETH.js: -------------------------------------------------------------------------------- 1 | // This file can be either JSON, or a JS exporting an object 2 | 3 | module.exports = [ 4 | // WETH-OMG 5 | require('./WETH_OMG.js') 6 | ] 7 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | const Migrations = artifacts.require("Migrations"); 4 | 5 | module.exports = function(deployer) { 6 | deployer.deploy(Migrations); 7 | }; 8 | -------------------------------------------------------------------------------- /contracts/Oracle/DSThing.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "../Oracle/DSMath.sol"; 4 | import "../Oracle/DSAuth.sol"; 5 | import "../Oracle/DSNote.sol"; 6 | 7 | 8 | contract DSThing is DSAuth, DSNote, DSMath {} 9 | -------------------------------------------------------------------------------- /test/test-tokens/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | const Migrations = artifacts.require("Migrations"); 4 | 5 | module.exports = function(deployer) { 6 | deployer.deploy(Migrations); 7 | }; 8 | -------------------------------------------------------------------------------- /test/test-tokens/contracts/TokenCVC.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "./TestToken.sol"; 4 | 5 | contract TokenCVC is TestToken { 6 | constructor(uint amount) public TestToken("CVC", "Civic", 8, amount) {} 7 | } 8 | -------------------------------------------------------------------------------- /test/test-tokens/contracts/TokenOMG.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "./TestToken.sol"; 4 | 5 | contract TokenOMG is TestToken { 6 | constructor(uint amount) public TestToken("OMG", "OmiseGO", 18, amount) {} 7 | } 8 | -------------------------------------------------------------------------------- /test/test-tokens/contracts/TokenRDN.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "./TestToken.sol"; 4 | 5 | contract TokenRDN is TestToken { 6 | constructor(uint amount) public TestToken("RDN", "Raiden", 18, amount) {} 7 | } 8 | -------------------------------------------------------------------------------- /test/test-tokens/contracts/TokenDAI.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "./TestToken.sol"; 4 | 5 | contract TokenDAI is TestToken { 6 | constructor(uint amount) public TestToken("testDAI", "Test DAI", 18, amount) {} 7 | } 8 | -------------------------------------------------------------------------------- /test/test-tokens/contracts/TokenGEN.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "./TestToken.sol"; 4 | 5 | contract TokenGEN is TestToken { 6 | constructor(uint amount) public TestToken("testGEN", "Test GEN", 18, amount) {} 7 | } 8 | -------------------------------------------------------------------------------- /test/test-tokens/contracts/TokenKNC.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "./TestToken.sol"; 4 | 5 | contract TokenKNC is TestToken { 6 | constructor(uint amount) public TestToken("testKNC", "Test KNC", 18, amount) {} 7 | } 8 | -------------------------------------------------------------------------------- /test/test-tokens/contracts/TokenMKR.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "./TestToken.sol"; 4 | 5 | contract TokenMKR is TestToken { 6 | constructor(uint amount) public TestToken("testMKR", "Test MKR", 18, amount) {} 7 | } 8 | -------------------------------------------------------------------------------- /test/test-tokens/contracts/TokenGRID.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "./TestToken.sol"; 4 | 5 | contract TokenGRID is TestToken { 6 | constructor(uint amount) public TestToken("GRID", "GRID Token", 12, amount) {} 7 | } 8 | -------------------------------------------------------------------------------- /test/resources/add-token-pair/mainnet/03_dxDao.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | // WETH-RDN 3 | // require('./WETH_OMG.js') 4 | 5 | // WETH-RDN 6 | require('./WETH_RDN.js') 7 | 8 | // RDN-OMG 9 | // require('./RDN_OMG.js') 10 | ] 11 | -------------------------------------------------------------------------------- /contracts/DutchExchangeProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "@gnosis.pm/util-contracts/contracts/Proxy.sol"; 4 | 5 | 6 | contract DutchExchangeProxy is Proxy { 7 | constructor(address _masterCopy) public Proxy(_masterCopy) {} 8 | } 9 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'standard', 3 | plugins: [], 4 | rules: { 5 | 'strict': 0, 6 | 'arrow-parens': [2, 'as-needed'] 7 | }, 8 | env: { 9 | 'es6': true, 10 | 'node': true, 11 | 'mocha': true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/test-tokens/README.md: -------------------------------------------------------------------------------- 1 | # Rinkeby test tokens 2 | Just a small truffle project to deploy test tokens for testing in rinkeby. 3 | 4 | # Install dependencies 5 | ```bash 6 | yarn install 7 | ``` 8 | 9 | # Deploy in rinkeby 10 | ```bash 11 | yarn migrate --network rinkeby 12 | ``` 13 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Mnemonic or MNEMONIC 2 | #MNEMONIC='your mnemonic here' 3 | #PK='your-private-key' 4 | 5 | # Gas Price 6 | # GAS_PRICE_GWEI=4 7 | 8 | # Allow to use native solc docker image 9 | # - Requires to have the image: 10 | # docker pull ethereum/solc:0.4.25 11 | SOLC_USE_DOCKER=false -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # Mnemonic or MNEMONIC 2 | #MNEMONIC='your mnemonic here' 3 | #PK='your-private-key' 4 | 5 | # Gas Price 6 | # GAS_PRICE_GWEI=4 7 | 8 | # Allow to use native solc docker image 9 | # - Requires to have the image: 10 | # docker pull ethereum/solc:0.4.25 11 | # docker pull ethereum/solc:0.5.0 12 | SOLC_USE_DOCKER=true -------------------------------------------------------------------------------- /test/test-tokens/migrations/util/migrationUtils.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ web3 }) => { 2 | const BN = web3.utils.BN 3 | 4 | return { 5 | toWei (amount, decimals = 18) { 6 | const expoential = new BN(Math.pow(10, decimals).toString()) 7 | return (new BN(amount)).mul(expoential).toString() 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/inject_network_info.js: -------------------------------------------------------------------------------- 1 | const injectNetworks = require('@gnosis.pm/util-contracts/src/util/injectNetworks') 2 | const path = require('path') 3 | 4 | const DEFAULT_CONF_FILE = path.join(__dirname, './conf/network-restore') 5 | 6 | const confFile = process.env.CONF_FILE || DEFAULT_CONF_FILE 7 | injectNetworks(confFile) 8 | .catch(console.error) 9 | -------------------------------------------------------------------------------- /src/extract_network_info.js: -------------------------------------------------------------------------------- 1 | const extractNetworks = require('@gnosis.pm/util-contracts/src/util/extractNetworks') 2 | const path = require('path') 3 | 4 | const DEFAULT_CONF_FILE = path.join(__dirname, './conf/network-restore') 5 | 6 | const confFile = process.env.CONF_FILE || DEFAULT_CONF_FILE 7 | extractNetworks(confFile) 8 | .catch(console.error) 9 | -------------------------------------------------------------------------------- /test/test-tokens/src/inject_network_info.js: -------------------------------------------------------------------------------- 1 | const injectNetworks = require('@gnosis.pm/util-contracts/src/util/injectNetworks') 2 | const path = require('path') 3 | 4 | const DEFAULT_CONF_FILE = path.join(__dirname, './conf/network-restore') 5 | 6 | const confFile = process.env.CONF_FILE || DEFAULT_CONF_FILE 7 | injectNetworks(confFile) 8 | .catch(console.error) 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | .history 3 | 4 | migrations/4_deploy_internalTestContracts.js 5 | scripts/startAuctionTruffleScript.js 6 | scripts/listOfTOP150TokensByMarketCap.txt 7 | travscripts/* 8 | .gitattributes 9 | .solcover.js 10 | .travis.yml 11 | *.pyc 12 | yarn-error.log 13 | 14 | records.json 15 | stats.json 16 | .awcache 17 | .eslintcache 18 | .stylelintcache -------------------------------------------------------------------------------- /migrations/5_deploy_DX.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const deployDx = require('../src/migrations-truffle-5/5_deploy_DX') 5 | 6 | module.exports = function (deployer, network, accounts) { 7 | return deployDx({ 8 | artifacts, 9 | deployer, 10 | network, 11 | accounts, 12 | web3 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /test/test-tokens/src/extract_network_info.js: -------------------------------------------------------------------------------- 1 | const extractNetworks = require('@gnosis.pm/util-contracts/src/util/extractNetworks') 2 | const path = require('path') 3 | 4 | const DEFAULT_CONF_FILE = path.join(__dirname, './conf/network-restore') 5 | 6 | const confFile = process.env.CONF_FILE || DEFAULT_CONF_FILE 7 | extractNetworks(confFile) 8 | .catch(console.error) 9 | -------------------------------------------------------------------------------- /migrations/4_deploy_FRT.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const deployFrt = require('../src/migrations-truffle-5/4_deploy_FRT') 5 | 6 | module.exports = function (deployer, network, accounts) { 7 | return deployFrt({ 8 | artifacts, 9 | deployer, 10 | network, 11 | accounts, 12 | web3 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /src/conf/network-restore.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | const BASE_DIR = path.join(__dirname, '../..') 4 | const BUILD_DIR = path.join(BASE_DIR, 'build/contracts') 5 | const NETWORKS_FILE_PATH = path.join(BASE_DIR, 'networks.json') 6 | 7 | module.exports = { 8 | buildPath: BUILD_DIR, 9 | networkFilePath: NETWORKS_FILE_PATH, 10 | buildDirDependencies: [] 11 | } 12 | -------------------------------------------------------------------------------- /test/test-tokens/src/conf/network-restore.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | const BASE_DIR = path.join(__dirname, '../..') 4 | const BUILD_DIR = path.join(BASE_DIR, 'build/contracts') 5 | const NETWORKS_FILE_PATH = path.join(BASE_DIR, 'networks.json') 6 | 7 | module.exports = { 8 | buildPath: BUILD_DIR, 9 | networkFilePath: NETWORKS_FILE_PATH, 10 | buildDirDependencies: [] 11 | } 12 | -------------------------------------------------------------------------------- /.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 | "warning", 17 | 5 18 | ], 19 | "error-reason": 0, 20 | "security/no-assign-params": 0 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /travscripts/AfterScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #if [[ $TRAVIS_BRANCH =~ (feature/test-?(\/[a-zA-Z0-9/._-]*)?) ]]; then 4 | echo " ==> Detected a CONTRACT(S) branch" 5 | #jump back to root 6 | cd $TRAVIS_BUILD_DIR 7 | echo " ==> JUMPING LOCATIONS: NOW IN $TRAVIS_BUILD_DIR" 8 | #run solcover 9 | echo " ==> RUNNING solidity-coverage" && 10 | 11 | npm run coverage && cat ./coverage/lcov.info | coveralls 12 | #fi; 13 | -------------------------------------------------------------------------------- /src/inject_network_info_deps_gno.js: -------------------------------------------------------------------------------- 1 | const injectNetworksDeps = require('@gnosis.pm/util-contracts/src/util/injectNetworksDeps') 2 | const path = require('path') 3 | 4 | const NODE_MODULES_PATH = path.join(__dirname, '../node_modules') 5 | 6 | injectNetworksDeps({ 7 | buildPath: '@gnosis.pm/gno-token/build/contracts', 8 | packages: [ 9 | '@gnosis.pm/owl-token' 10 | ], 11 | nodeModulesPath: NODE_MODULES_PATH 12 | }).catch(console.error) 13 | -------------------------------------------------------------------------------- /contracts/.solhint.json: -------------------------------------------------------------------------------- 1 | diud{ 2 | "extends": "default", 3 | "rules": { 4 | "indent": ["error", 4], 5 | "quotes": ["error", "double"], 6 | "max-line-length": ["error", 129], 7 | "function-max-lines": ["error", 45], 8 | "max-states-count": ["error", 18] 9 | } 10 | } -------------------------------------------------------------------------------- /contracts/ForTestingOnly/TokenGNO.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "./SubStandardToken.sol"; 4 | 5 | contract BadGNO is SubStandardToken { 6 | string public constant symbol = "GNO"; 7 | string public constant name = "Gnosis"; 8 | uint8 public constant decimals = 18; 9 | 10 | constructor(uint amount) 11 | public 12 | { 13 | totalTokens = amount; 14 | balances[msg.sender] = amount; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/ForTestingOnly/TokenOMG.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "@gnosis.pm/util-contracts/contracts/GnosisStandardToken.sol"; 4 | 5 | 6 | contract TokenOMG is GnosisStandardToken { 7 | string public constant symbol = "OMG"; 8 | string public constant name = "OMG Test Token"; 9 | uint8 public constant decimals = 18; 10 | 11 | constructor(uint amount) public { 12 | balances[msg.sender] = amount; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /contracts/ForTestingOnly/TokenRDN.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "@gnosis.pm/util-contracts/contracts/GnosisStandardToken.sol"; 4 | 5 | 6 | contract TokenRDN is GnosisStandardToken { 7 | string public constant symbol = "RDN"; 8 | string public constant name = "Raiden network tokens"; 9 | uint8 public constant decimals = 18; 10 | 11 | constructor(uint amount) public { 12 | balances[msg.sender] = amount; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/test-tokens/migrations/3_deploy_OMG.js: -------------------------------------------------------------------------------- 1 | /* global web3, artifacts */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const TokenOMG = artifacts.require('TokenOMG') 5 | const INITIAL_FUNDING = 10e6 // 00M 6 | 7 | const { toWei } = require('./util/migrationUtils')({ 8 | web3 9 | }) 10 | 11 | module.exports = function (deployer) { 12 | console.log('Deploy OMG with initial funding of: ', INITIAL_FUNDING) 13 | deployer.deploy(TokenOMG, toWei(INITIAL_FUNDING).toString()) 14 | } 15 | -------------------------------------------------------------------------------- /test/test-tokens/migrations/2_deploy_RDN.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const TokenRDN = artifacts.require('TokenRDN') 5 | const INITIAL_FUNDING = 100e6 // 100M 6 | 7 | const { toWei } = require('./util/migrationUtils')({ 8 | web3 9 | }) 10 | 11 | module.exports = function (deployer) { 12 | console.log('Deploy RDN with initial funding of: ', INITIAL_FUNDING) 13 | deployer.deploy(TokenRDN, toWei(INITIAL_FUNDING).toString()) 14 | } 15 | -------------------------------------------------------------------------------- /src/inject_network_info_deps_utils.js: -------------------------------------------------------------------------------- 1 | const injectNetworksDeps = require('@gnosis.pm/util-contracts/src/util/injectNetworksDeps') 2 | const path = require('path') 3 | 4 | const NODE_MODULES_PATH = path.join(__dirname, '../node_modules') 5 | 6 | injectNetworksDeps({ 7 | buildPath: '@gnosis.pm/util-contracts/build/contracts', 8 | packages: [ 9 | '@gnosis.pm/gno-token', 10 | '@gnosis.pm/owl-token' 11 | ], 12 | nodeModulesPath: NODE_MODULES_PATH 13 | }).catch(console.error) 14 | -------------------------------------------------------------------------------- /migrations/3_DEV-deploy_price_feed.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | const deployPriceFeed = require('../src/migrations-truffle-5/3_deploy_price_feed') 4 | 5 | module.exports = function (deployer, network, accounts) { 6 | return deployPriceFeed({ 7 | artifacts, 8 | deployer, 9 | network, 10 | accounts, 11 | web3, 12 | ethUsdPrice: process.env.ETH_USD_PRICE, 13 | feedExpirePeriodDays: process.env.FEED_EXPIRE_PERIOD_DAYS 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /migrations/6_setup_DX.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | const setupDx = require('../src/migrations-truffle-5/6_setup_DX') 4 | 5 | module.exports = function (deployer, network, accounts) { 6 | return setupDx({ 7 | artifacts, 8 | deployer, 9 | network, 10 | accounts, 11 | web3, 12 | thresholdNewTokenPairUsd: process.env.THRESHOLD_NEW_TOKEN_PAIR_USD, 13 | thresholdAuctionStartUsd: process.env.THRESHOLD_AUCTION_START_USD 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /src/migrations-truffle-4/5_deploy_DX.js: -------------------------------------------------------------------------------- 1 | function migrate ({ 2 | artifacts, 3 | deployer, 4 | network, 5 | accounts 6 | }) { 7 | const DutchExchange = artifacts.require('DutchExchange') 8 | const DutchExchangeProxy = artifacts.require('DutchExchangeProxy') 9 | 10 | return deployer 11 | // Deploy DX and it's proxy 12 | .then(() => deployer.deploy(DutchExchange)) 13 | .then(() => deployer.deploy(DutchExchangeProxy, DutchExchange.address)) 14 | } 15 | 16 | module.exports = migrate 17 | -------------------------------------------------------------------------------- /src/migrations-truffle-4/7_set_DX_as_FRT_minter.js: -------------------------------------------------------------------------------- 1 | function migrate ({ 2 | artifacts, 3 | deployer 4 | }) { 5 | const TokenFRT = artifacts.require('TokenFRT') 6 | const TokenFRTProxy = artifacts.require('TokenFRTProxy') 7 | const DutchExchangeProxy = artifacts.require('DutchExchangeProxy') 8 | 9 | return deployer 10 | .then(() => TokenFRT.at(TokenFRTProxy.address)) 11 | .then(tokenFRT => tokenFRT.updateMinter(DutchExchangeProxy.address)) 12 | } 13 | 14 | module.exports = migrate 15 | -------------------------------------------------------------------------------- /test/trufflescripts/snapshot.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const { makeSnapshot } = require('./utils')(web3) 3 | 4 | /** 5 | * truffle exec test/trufflescripts/snapshot.js 6 | * Created snapshot of blockchain state and assigns a Block-ID 7 | * Block-ID can be reverted back to via revert.js 8 | */ 9 | 10 | module.exports = () => { 11 | const snapshot = makeSnapshot() 12 | console.log(` 13 | SNAPSHOT CREATED: # ${snapshot} 14 | BLOCK-NUMBER: ${web3.eth.blockNumber} 15 | `) 16 | } 17 | -------------------------------------------------------------------------------- /src/migrations-truffle-5/5_deploy_DX.js: -------------------------------------------------------------------------------- 1 | async function migrate ({ 2 | artifacts, 3 | deployer 4 | }) { 5 | const DutchExchange = artifacts.require('DutchExchange') 6 | const DutchExchangeProxy = artifacts.require('DutchExchangeProxy') 7 | 8 | console.log('Deploy DutchExchange contract') 9 | await deployer.deploy(DutchExchange) 10 | 11 | console.log('Deploy DutchExchangeProxy contract') 12 | await deployer.deploy(DutchExchangeProxy, DutchExchange.address) 13 | } 14 | 15 | module.exports = migrate 16 | -------------------------------------------------------------------------------- /migrations/2_DEV_migrate_dependencies.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | const migrateDependencies = require('../src/migrations-truffle-5/2_migrate_dependencies') 4 | 5 | module.exports = function (deployer, network, accounts) { 6 | return migrateDependencies({ 7 | artifacts, 8 | deployer, 9 | network, 10 | accounts, 11 | web3, 12 | ethUsdPrice: process.env.ETH_USD_PRICE, 13 | feedExpirePeriodDays: process.env.FEED_EXPIRE_PERIOD_DAYS 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /src/migrations-truffle-5/index.js: -------------------------------------------------------------------------------- 1 | const migrateDependencies = require('./2_migrate_dependencies') 2 | const deployPriceFeed = require('./3_deploy_price_feed') 3 | const deployFRT = require('./4_deploy_FRT') 4 | const deployDX = require('./5_deploy_DX') 5 | const setupDx = require('./6_setup_DX') 6 | const setDxAsFrtMintern = require('./7_set_DX_as_FRT_minter') 7 | 8 | module.exports = async params => { 9 | await migrateDependencies(params) 10 | await deployPriceFeed(params) 11 | await deployFRT(params) 12 | await deployDX(params) 13 | await setupDx(params) 14 | await setDxAsFrtMintern(params) 15 | } 16 | -------------------------------------------------------------------------------- /travscripts/BranchScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function run_tests { 4 | #jump back to root 5 | cd $TRAVIS_BUILD_DIR 6 | echo " ==> JUMPING LOCATIONS: NOW IN $TRAVIS_BUILD_DIR" 7 | 8 | # running contracts tests 9 | echo " ==> RUNNING test" 10 | npm test; 11 | } 12 | 13 | if [[ $TRAVIS_BRANCH = "master" || $TRAVIS_BRANCH = "develop" ]]; then 14 | echo " ==> Detected PRINCIPAL branch - compiling and testing contracts" 15 | run_tests 16 | else 17 | # echo " ==> No execution for branches other than MASTER or DEVELOP" 18 | echo " ==> Detected BRANCH branch - compiling and testing contracts" 19 | run_tests 20 | fi; 21 | -------------------------------------------------------------------------------- /migrations/7_set_DX_as_FRT_minter.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | const setDxAsFrtOwner = require('../src/migrations-truffle-5/7_set_DX_as_FRT_minter') 4 | 5 | module.exports = function (deployer, network, accounts) { 6 | return setDxAsFrtOwner({ 7 | artifacts, 8 | deployer, 9 | network, 10 | accounts, 11 | web3 12 | }) 13 | } 14 | // Last step of the migration: 15 | 16 | // At some later point we would change the ownerShip of the MagnoliaTokens in order to make funds secure. See audit report 17 | // .then(() => TokenFRT.deployed()) 18 | // .then(T => T.updateOwner(Proxy.address)) 19 | -------------------------------------------------------------------------------- /test/test-tokens/migrations/5_deploy_GRID,CVC.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const { toWei } = require('./util/migrationUtils')({ 5 | web3 6 | }) 7 | 8 | const INITIAL_FUNDING = 10e6 // 10M 9 | module.exports = function (deployer) { 10 | function _deploy (token, decimals) { 11 | console.log('Deploy %s with initial funding of: %s', token, INITIAL_FUNDING) 12 | 13 | return deployer 14 | .deploy(artifacts.require(`Token${token}`), toWei(INITIAL_FUNDING, decimals)) 15 | } 16 | 17 | deployer 18 | .then(() => _deploy('GRID', 12)) 19 | .then(() => _deploy('CVC', 8)) 20 | } 21 | -------------------------------------------------------------------------------- /test/test-tokens/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | constructor() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_STORE 3 | node_modules 4 | *~ 5 | *.pyc 6 | static 7 | .grunt 8 | _SpecRunner.html 9 | __benchmarks__ 10 | dist/ 11 | build/ 12 | coverage/ 13 | .module-cache 14 | *.gem 15 | docs/.bundle 16 | docs/code 17 | docs/_site 18 | docs/.sass-cache 19 | docs/js/* 20 | docs/downloads/*.zip 21 | docs/vendor/bundle 22 | examples/shared/*.js 23 | examples/**/bundle.js 24 | test/the-files-to-test.generated.js 25 | *.log* 26 | chrome-user-data 27 | *.sublime-project 28 | *.sublime-workspace 29 | .idea 30 | *.iml 31 | .vscode 32 | /semantic.json 33 | /semantic 34 | yarn-error.log 35 | 36 | records.json 37 | stats.json 38 | .awcache 39 | .eslintcache 40 | .stylelintcache 41 | .history 42 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | 4 | contract Migrations { 5 | address public owner; 6 | uint public last_completed_migration; 7 | 8 | modifier restricted() { 9 | if (msg.sender == owner) 10 | _; 11 | } 12 | 13 | constructor() public { 14 | owner = msg.sender; 15 | } 16 | 17 | function setCompleted(uint completed) public restricted { 18 | last_completed_migration = completed; 19 | } 20 | 21 | function upgrade(address new_address) public restricted { 22 | Migrations upgraded = Migrations(new_address); 23 | upgraded.setCompleted(last_completed_migration); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/migrations-truffle-4/index.js: -------------------------------------------------------------------------------- 1 | const migrateDependencies = require('./2_migrate_dependencies') 2 | const deployPriceFeed = require('./3_deploy_price_feed') 3 | const deployFRT = require('./4_deploy_FRT') 4 | const deployDX = require('./5_deploy_DX') 5 | const setupDx = require('./6_setup_DX') 6 | const setDxAsFrtMintern = require('./7_set_DX_as_FRT_minter') 7 | 8 | module.exports = params => { 9 | return params.deployer 10 | .then(() => migrateDependencies(params)) 11 | .then(() => deployPriceFeed(params)) 12 | .then(() => deployFRT(params)) 13 | .then(() => deployDX(params)) 14 | .then(() => setupDx(params)) 15 | .then(() => setDxAsFrtMintern(params)) 16 | } 17 | -------------------------------------------------------------------------------- /test/test-tokens/migrations/4_deploy_DAI,GEN,KNC,MKR.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const INITIAL_FUNDING = 10e6 // 10M 5 | const { toWei } = require('./util/migrationUtils')({ 6 | web3 7 | }) 8 | 9 | module.exports = function (deployer) { 10 | function _deploy (token) { 11 | console.log('Deploy %s with initial funding of: %s', token, INITIAL_FUNDING) 12 | 13 | return deployer 14 | .deploy(artifacts.require(`Token${token}`), toWei(INITIAL_FUNDING)) 15 | } 16 | 17 | deployer 18 | .then(() => _deploy('DAI')) 19 | .then(() => _deploy('GEN')) 20 | .then(() => _deploy('KNC')) 21 | .then(() => _deploy('MKR')) 22 | } 23 | -------------------------------------------------------------------------------- /test/resources/add-token-pair/kovan/WETH_GNO.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // WETH 3 | tokenA: { 4 | symbol: 'WETH', 5 | address: '0xd0a1e359811322d97991e03f863a0c30c2cf029c', 6 | // Check ETH oracle: 7 | // https://makerdao.com/feeds/#0x729d19f657bd0614b4985cf1d82531c67569197b 8 | // Price: 205 9 | // 1000$ = 1000/205 = 4.87 10 | funding: 5 11 | }, 12 | // GNO 13 | tokenB: { 14 | symbol: 'GNO', 15 | address: '0x6018bf616ec9db02f90c8c8529ddadc10a5c29dc', 16 | funding: 0 17 | }, 18 | // Price: 19 | // 1 ETH = 10 GNO 20 | // initial price = 859 RDN/WETH 21 | initialPrice: { 22 | numerator: 10, 23 | denominator: 1 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/Oracle/DSValue.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "../Oracle/DSThing.sol"; 4 | 5 | 6 | contract DSValue is DSThing { 7 | bool has; 8 | bytes32 val; 9 | function peek() public view returns (bytes32, bool) { 10 | return (val, has); 11 | } 12 | 13 | function read() public view returns (bytes32) { 14 | (bytes32 wut, bool _has) = peek(); 15 | assert(_has); 16 | return wut; 17 | } 18 | 19 | function poke(bytes32 wut) public payable note auth { 20 | val = wut; 21 | has = true; 22 | } 23 | 24 | function void() public payable note auth { 25 | // unset the value 26 | has = false; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/TokenFRTProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "@gnosis.pm/util-contracts/contracts/Proxy.sol"; 4 | import "@gnosis.pm/util-contracts/contracts/GnosisStandardToken.sol"; 5 | 6 | 7 | contract TokenFRTProxy is Proxy, GnosisStandardToken { 8 | /// @dev State variables remain for Blockchain exploring Proxied Token contracts 9 | address public owner; 10 | 11 | string public constant symbol = "MGN"; 12 | string public constant name = "Magnolia Token"; 13 | uint8 public constant decimals = 18; 14 | 15 | constructor(address proxied, address _owner) public Proxy(proxied) { 16 | require(_owner != address(0), "owner address cannot be 0"); 17 | owner = _owner; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/migrations-truffle-5/7_set_DX_as_FRT_minter.js: -------------------------------------------------------------------------------- 1 | async function migrate ({ 2 | artifacts 3 | }) { 4 | const TokenFRT = artifacts.require('TokenFRT') 5 | const TokenFRTProxy = artifacts.require('TokenFRTProxy') 6 | const DutchExchangeProxy = artifacts.require('DutchExchangeProxy') 7 | 8 | // Make sure TokenFRT and the proxy are deployed 9 | const dxProxy = await DutchExchangeProxy.deployed() 10 | const frtProxy = await TokenFRTProxy.deployed() 11 | const tokenFrt = await TokenFRT.at(frtProxy.address) 12 | 13 | console.log('Update minter in TokenFRT:') 14 | console.log(' - Set dutchX address: %s', dxProxy.address) 15 | await tokenFrt.updateMinter(dxProxy.address) 16 | } 17 | 18 | module.exports = migrate 19 | -------------------------------------------------------------------------------- /scripts/extract_network_info.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const _ = require('lodash') 4 | 5 | const dir = path.join('build', 'contracts') 6 | const dirFiles = fs.readdirSync(dir) 7 | const networkFile = process.env.NETWORKS_FILE || 'networks.json' 8 | 9 | Promise.all(dirFiles.filter(fname => fname.endsWith('.json')).map(fname => new Promise((resolve, reject) => { 10 | fs.readFile(path.join(dir, fname), (err, data) => { 11 | if (err) throw err 12 | resolve([fname.slice(0, -5), JSON.parse(data)['networks']]) 13 | }) 14 | }))).then(nameNetworkPairs => { 15 | fs.writeFileSync(networkFile, JSON.stringify(_.fromPairs(nameNetworkPairs.filter(([_name, nets]) => !_.isEmpty(nets))), null, 2)) 16 | }) 17 | -------------------------------------------------------------------------------- /test/resources/add-token-pair/rinkeby/WETH_CVC.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // WETH 3 | tokenA: { 4 | symbol: 'WETH', 5 | address: '0xc778417e063141139fce010982780140aa0cd5ab', 6 | funding: 5 7 | }, 8 | // CVC 9 | tokenB: { 10 | symbol: 'CVC', 11 | address: '0x67Fe0FDf579043de35d9CB2F10C81B63aa8aEef9', 12 | funding: 0 13 | }, 14 | // Price: 15 | // https://www.coingecko.com/en/coins/civic?utm_content=civic&utm_medium=search_coin&utm_source=coingecko 16 | // 1 ETH = 3176 CVC 17 | // 1 * 1e18 ETH in wei = 3176 * 1e8 CVC in wei 18 | // Price = (3176 * 1e8) / (1 * 1e18) 19 | // Price = 3176 / (1 * 1e10) 20 | 21 | initialPrice: { 22 | numerator: 3176, 23 | denominator: 1e10 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/resources/add-token-pair/rinkeby/WETH_GRID.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // WETH 3 | tokenA: { 4 | symbol: 'WETH', 5 | address: '0xc778417e063141139fce010982780140aa0cd5ab', 6 | funding: 5 7 | }, 8 | // CVC 9 | tokenB: { 10 | symbol: 'GRID', 11 | address: '0xB35E3E3E7A87C2B04DEc49a7b5DA7c1A23a09e64', 12 | funding: 0 13 | }, 14 | // Price: 15 | // https://www.coingecko.com/en/coins/grid?utm_content=grid&utm_medium=search_coin&utm_source=coingecko 16 | // 1 ETH = 1706 GRID 17 | // 1 * 1e18 ETH in wei = 1706 * 1e12 GRID in wei 18 | // Price = (1706 * 1e8) / (1 * 1e18) 19 | // Price = 1706 / (1 * 1e6) 20 | 21 | initialPrice: { 22 | numerator: 1706, 23 | denominator: 1e6 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/base/AuctioneerManaged.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | 4 | contract AuctioneerManaged { 5 | // auctioneer has the power to manage some variables 6 | address public auctioneer; 7 | 8 | function updateAuctioneer(address _auctioneer) public onlyAuctioneer { 9 | require(_auctioneer != address(0), "The auctioneer must be a valid address"); 10 | auctioneer = _auctioneer; 11 | } 12 | 13 | // > Modifiers 14 | modifier onlyAuctioneer() { 15 | // Only allows auctioneer to proceed 16 | // R1 17 | // require(msg.sender == auctioneer, "Only auctioneer can perform this operation"); 18 | require(msg.sender == auctioneer, "Only the auctioneer can nominate a new one"); 19 | _; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/resources/add-token-pair/rinkeby/WETH_OMG.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // WETH 3 | tokenA: { 4 | symbol: 'WETH', 5 | address: '0xc778417e063141139fce010982780140aa0cd5ab', 6 | // Check ETH oracle: 7 | // https://makerdao.com/feeds/#0x729d19f657bd0614b4985cf1d82531c67569197b 8 | // Price: 500 9 | // 10000$ = 10000/500 ETH = 20 10 | funding: 20 11 | }, 12 | // OMG 13 | tokenB: { 14 | symbol: 'OMG', 15 | address: '0x00df91984582e6e96288307e9c2f20b38c8fece9', 16 | funding: 0 17 | }, 18 | // Price: 19 | // https://www.coingecko.com/en/price_charts/omisego/eth 20 | // 1 ETH = 97,09644726041404 OMG 21 | // initial price = 98 OMG/WETH 22 | initialPrice: { 23 | numerator: 98, 24 | denominator: 1 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/Oracle/DSNote.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | 4 | contract DSNote { 5 | event LogNote( 6 | bytes4 indexed sig, 7 | address indexed guy, 8 | bytes32 indexed foo, 9 | bytes32 bar, 10 | uint wad, 11 | bytes fax 12 | ); 13 | 14 | modifier note { 15 | bytes32 foo; 16 | bytes32 bar; 17 | // solium-disable-next-line security/no-inline-assembly 18 | assembly { 19 | foo := calldataload(4) 20 | bar := calldataload(36) 21 | } 22 | 23 | emit LogNote( 24 | msg.sig, 25 | msg.sender, 26 | foo, 27 | bar, 28 | msg.value, 29 | msg.data 30 | ); 31 | 32 | _; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/resources/add-token-pair/rinkeby/WETH_RDN.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // WETH 3 | tokenA: { 4 | symbol: 'WETH', 5 | address: '0xc778417e063141139fce010982780140aa0cd5ab', 6 | // Check ETH oracle: 7 | // https://makerdao.com/feeds/#0x729d19f657bd0614b4985cf1d82531c67569197b 8 | // Price: 500 9 | // 10000$ = 10000/500 ETH = 20 10 | funding: 20 11 | }, 12 | // RDN 13 | tokenB: { 14 | symbol: 'RDN', 15 | address: '0x3615757011112560521536258c1e7325ae3b48ae', 16 | funding: 0 17 | }, 18 | // Price: 19 | // https://www.coingecko.com/en/price_charts/raiden-network/eth 20 | // 1 ETH = 511,3250313237714 RDN 21 | // initial price = 512 RDN/WETH 22 | initialPrice: { 23 | numerator: 512, 24 | denominator: 1 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/resources/add-token-pair/kovan/WETH_RDN.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // WETH 3 | tokenA: { 4 | symbol: 'WETH', 5 | address: '0xd0a1e359811322d97991e03f863a0c30c2cf029c', 6 | // Check ETH oracle: 7 | // https://makerdao.com/feeds/#0x729d19f657bd0614b4985cf1d82531c67569197b 8 | // Price: 256.493 9 | // 10000$ = 10000/256.493 ETH = 38.98741876 10 | funding: 70 11 | }, 12 | // RDN 13 | tokenB: { 14 | symbol: 'RDN', 15 | address: '0x1f7f270df126ba464228cc8d8203d2768429e085', 16 | funding: 0 17 | }, 18 | // Price: 19 | // https://www.coingecko.com/en/price_charts/raiden-network/eth 20 | // 1 ETH = 859 RDN 21 | // initial price = 859 RDN/WETH 22 | initialPrice: { 23 | numerator: 859, 24 | denominator: 1 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/resources/add-token-pair/mainnet/WETH_GNO.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // WETH 3 | tokenA: { 4 | symbol: 'WETH', 5 | address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', 6 | // Check ETH oracle 7 | // https://makerdao.com/feeds/#0x729d19f657bd0614b4985cf1d82531c67569197b 8 | // Price: 133.245 9 | // 10000$ = 10000/133.245 ETH = 75.0497204398 10 | funding: 75.5 11 | }, 12 | // GNO 13 | tokenB: { 14 | symbol: 'GNO', 15 | address: '0x6810e776880c02933d47db1b9fc05908e5386b96', 16 | funding: 72 17 | }, 18 | // Price: 19 | // https://www.coingecko.com/en/price_charts/gnosis/eth 20 | // 1 ETH = 9,553282765896997 GNO 21 | // initial price = 10 GNO 22 | initialPrice: { 23 | numerator: 10, 24 | denominator: 1 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/resources/add-token-pair/mainnet/WETH_OMG.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // WETH 3 | tokenA: { 4 | symbol: 'WETH', 5 | address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', 6 | // Check ETH oracle: 7 | // https://makerdao.com/feeds/#0x729d19f657bd0614b4985cf1d82531c67569197b 8 | // Price: 118.995 9 | // 10000$ = 10000/118.995 ETH = 84,0371444178 10 | funding: 88 11 | }, 12 | // OMG 13 | tokenB: { 14 | symbol: 'OMG', 15 | address: '0xd26114cd6EE289AccF82350c8d8487fedB8A0C07', 16 | funding: 0 17 | }, 18 | // Price: 19 | // https://www.coingecko.com/en/price_charts/omisego/eth 20 | // 1 ETH = 105,54353039478472 OMG 21 | // initial price = 65 OMG/WETH 22 | initialPrice: { 23 | numerator: 106, 24 | denominator: 1 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/resources/add-token-pair/mainnet/WETH_RDN.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // WETH 3 | tokenA: { 4 | symbol: 'WETH', 5 | address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', 6 | // Check ETH oracle 7 | // https://makerdao.com/feeds/#0x729d19f657bd0614b4985cf1d82531c67569197b 8 | // Price: 118.285 9 | // 10000$ = 10000/118.285 ETH = 84.5415733187 10 | funding: 86 11 | }, 12 | // RDN 13 | tokenB: { 14 | symbol: 'RDN', 15 | address: '0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6', 16 | funding: 0 17 | }, 18 | // Price: 19 | // https://www.coingecko.com/en/price_charts/raiden-network/eth 20 | // 1 ETH = 551,0627106609419 RDN 21 | // initial price = 552 RDN/WETH 22 | initialPrice: { 23 | numerator: 552, 24 | denominator: 1 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scripts/inject_network_info.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const _ = require('lodash') 4 | 5 | const dir = path.join('build', 'contracts') 6 | const networkFile = process.env.NETWORKS_FILE || 'networks.json' 7 | const contractNetworksMap = JSON.parse(fs.readFileSync(networkFile)) 8 | 9 | _.toPairs(contractNetworksMap) 10 | .map(([name, networks]) => [path.join(dir, name + '.json'), networks]) 11 | .filter(([file, _networks]) => { 12 | if (!fs.existsSync(file)) { throw new Error(`missing build artifact ${file}; make sure contracts are compiled`) } 13 | return true 14 | }) 15 | .forEach(([file, networks]) => { 16 | const artifactData = JSON.parse(fs.readFileSync(file)) 17 | _.merge(artifactData.networks, networks) 18 | fs.writeFileSync(file, JSON.stringify(artifactData, null, 2)) 19 | }) 20 | -------------------------------------------------------------------------------- /test/resources/approve-tokens/rdnAndOmg-mainnet.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | 'name': 'Wrapped Ether', 4 | 'symbol': 'WETH', 5 | 'approve': true, 6 | 'etherScanLink': 'https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', 7 | 'address': '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' 8 | }, 9 | { 10 | 'name': 'OmiseGO', 11 | 'symbol': 'OMG', 12 | 'approve': true, 13 | 'etherScanLink': 'https://etherscan.io/token/0xd26114cd6EE289AccF82350c8d8487fedB8A0C07', 14 | 'address': '0xd26114cd6EE289AccF82350c8d8487fedB8A0C07' 15 | }, 16 | { 17 | 'name': 'Raiden', 18 | 'symbol': 'RDN', 19 | 'approve': true, 20 | 'etherScanLink': 'https://etherscan.io/token/0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6', 21 | 'address': '0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6' 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /test/resources/approve-tokens/rdnAndOmg-rinkeby.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | 'name': 'Wrapped Ether', 4 | 'symbol': 'WETH', 5 | 'approve': true, 6 | 'etherScanLink': 'https://etherscan.io/token/0xc778417e063141139fce010982780140aa0cd5ab', 7 | 'address': '0xc778417e063141139fce010982780140aa0cd5ab' 8 | }, 9 | { 10 | 'name': 'OmiseGO', 11 | 'symbol': 'OMG', 12 | 'approve': true, 13 | 'etherScanLink': 'https://etherscan.io/token/0x00df91984582e6e96288307e9c2f20b38c8fece9', 14 | 'address': '0x00df91984582e6e96288307e9c2f20b38c8fece9' 15 | }, 16 | { 17 | 'name': 'Raiden', 18 | 'symbol': 'RDN', 19 | 'approve': true, 20 | 'etherScanLink': 'https://etherscan.io/token/0x3615757011112560521536258c1e7325ae3b48ae', 21 | 'address': '0x3615757011112560521536258c1e7325ae3b48ae' 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /test/resources/add-token-pair/rinkeby/RDN_OMG.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // RDN 3 | tokenA: { 4 | symbol: 'RDN', 5 | address: '0x3615757011112560521536258c1e7325ae3b48ae', 6 | // Check ETH oracle (i.e 500 USD/ETH) 7 | // 10000$ = -/500 ETH = 20 ETH 8 | funding: 40000 9 | }, 10 | // OMG 11 | tokenB: { 12 | symbol: 'OMG', 13 | address: '0x00df91984582e6e96288307e9c2f20b38c8fece9', 14 | funding: 0 15 | }, 16 | // Price: 17 | // https://www.coingecko.com/en/price_charts/omisego/eth 18 | // https://www.coingecko.com/en/price_charts/raiden-network/eth 19 | // 20 | // The price should be in OMG/RDN (since its the RDN-OMG auction) 21 | // - ETH-RDN Price = 530 RDN/WETH 22 | // - ETH-OMG Price = 101 OMG/WETH 23 | // - RDN-OMG Price = 101/630 = 0,1603174603 OMG/RDN 24 | initialPrice: { 25 | numerator: 101, 26 | denominator: 630 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/test-tokens/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rinkeby-test-tokens", 3 | "version": "1.0.0", 4 | "description": "Just a small truffle project to deploy test tokens for testing in rinkeby.", 5 | "main": "index.js", 6 | "scripts": { 7 | "preversion": "npm run restore", 8 | "restore": "rm -rf build && npm run compile && npm run networks-reset", 9 | "networks-reset": "mkdir -p build/contracts && truffle networks --clean && npm run networks-inject", 10 | "networks-extract": "node src/extract_network_info.js", 11 | "networks-inject": "node src/inject_network_info.js", 12 | "compile": "truffle compile", 13 | "networks": "truffle networks", 14 | "migrate": "truffle migrate" 15 | }, 16 | "license": "ISC", 17 | "dependencies": { 18 | "@gnosis.pm/util-contracts": "^2.0.0" 19 | }, 20 | "devDependencies": { 21 | "truffle": "^5.0.5", 22 | "truffle-contract": "^3.0.6", 23 | "truffle-hdwallet-provider": "1.0.4" 24 | } 25 | } -------------------------------------------------------------------------------- /contracts/DxDevDependencies.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | // NOTE: 4 | // This file porpouse is just to make sure truffle compiles all of depending 5 | // contracts when we are in development. 6 | // 7 | // For other environments, we just use the compiled contracts from the NPM 8 | // package 9 | 10 | // TODO: Use the same getter pattern also for dependencies 11 | import "@gnosis.pm/util-contracts/contracts/GnosisStandardToken.sol"; 12 | import "@gnosis.pm/util-contracts/contracts/EtherToken.sol"; 13 | import "@gnosis.pm/gno-token/contracts/TokenGNO.sol"; 14 | import "@gnosis.pm/owl-token/contracts/TokenOWLProxy.sol"; 15 | import "@gnosis.pm/owl-token/contracts/OWLAirdrop.sol"; 16 | 17 | // DX contracts 18 | import "./Oracle/Medianizer.sol"; 19 | import "./TokenFRT.sol"; 20 | import "./TokenFRTProxy.sol"; 21 | import "./DutchExchange.sol"; 22 | import "./DutchExchangeHelper.sol"; 23 | import "./DutchExchangeProxy.sol"; 24 | 25 | 26 | contract DxDevDependencies {} 27 | -------------------------------------------------------------------------------- /contracts/ForTestingOnly/BadToken.sol: -------------------------------------------------------------------------------- 1 | /// Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md 2 | pragma solidity ^0.5.2; 3 | 4 | 5 | /// @title Abstract token contract - Functions to be implemented by token contracts 6 | contract BadToken { 7 | 8 | /* 9 | * Events 10 | */ 11 | event Transfer(address indexed from, address indexed to, uint value); 12 | event Approval(address indexed owner, address indexed spender, uint value); 13 | 14 | /* 15 | * Public functions 16 | */ 17 | function transfer(address to, uint value) public; 18 | function transferFrom(address from, address to, uint value) public; 19 | function approve(address spender, uint value) public returns (bool); 20 | function balanceOf(address owner) public view returns (uint); 21 | function allowance(address owner, address spender) public view returns (uint); 22 | function totalSupply() public view returns (uint); 23 | } 24 | -------------------------------------------------------------------------------- /src/migrations-truffle-4/2_migrate_dependencies.js: -------------------------------------------------------------------------------- 1 | const deployUtils = require('@gnosis.pm/util-contracts/src/migrations-truffle-5') 2 | const deployGno = require('@gnosis.pm/gno-token/src/migrations-truffle-5') 3 | const deployOwl = require('@gnosis.pm/owl-token/src/migrations-truffle-5') 4 | 5 | function migrate ({ 6 | artifacts, 7 | deployer, 8 | network, 9 | accounts, 10 | initialTokenAmount, 11 | gnoLockPeriodInHours, 12 | web3 13 | }) { 14 | if (network === 'development') { 15 | const deployParams = { 16 | artifacts, 17 | deployer, 18 | network, 19 | accounts, 20 | initialTokenAmount, 21 | gnoLockPeriodInHours, 22 | web3 23 | } 24 | deployer 25 | .then(() => deployUtils(deployParams)) 26 | .then(() => deployGno(deployParams)) 27 | .then(() => deployOwl(deployParams)) 28 | } else { 29 | console.log('Not in development, so nothing to do. Current network is %s', network) 30 | } 31 | } 32 | 33 | module.exports = migrate 34 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | port: 8556, 3 | skipFiles: [ 4 | 'Migrations.sol', 5 | 'Tokens/EtherToken.sol', 6 | 'Tokens/GnosisStandardToken.sol', 7 | 'ForTestingOnly' 8 | ], 9 | testrpcOptions: '--port 8556 --account=0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d,50000000000000000000000 --account=0x6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1,50000000000000000000000 --account=0x6370fd033278c143179d81c5526140625662b8daa446c22ee2d73db3707e620c,50000000000000000000000 --account=0x646f1ce2fdad0e6deeeb5c7e8e5543bdde65e86029e2fd9fc169899c440a7913,50000000000000000000000 --account=0xadd53f9a7e588d003326d1cbf9e4a43c061aadd9bc938c843a79e7b4fd2ad743,50000000000000000000000 --account=0x395df67f0c2d2d9fe1ad08d1bc8b6627011959b79c53d7dd6a3536a33ab8a4fd,50000000000000000000000 --account=0xe485d098507f54e7733a205420dfddbe58db035fa577fc294ebd14db90767a52,50000000000000000000000', 10 | testCommand: 'truffle test test/*.spec.js -s', 11 | copyPackages: ['@gnosis.pm'], 12 | }; 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | #.travis.yml 2 | sudo: required 3 | dist: trusty 4 | language: node_js 5 | node_js: 6 | - '10' 7 | cache: npm 8 | before_install: 9 | - rm -rf node_modules 10 | - npm install -g ganache-cli@6.4.3 11 | install: 12 | - npm install 13 | before_script: 14 | - ganache-cli --defaultBalanceEther 500000000 > /dev/null & 15 | - sleep 5 16 | #replace solidity-parser for coverage report 17 | - curl https://raw.githubusercontent.com/maxsam4/solidity-parser/solidity-0.5/build/parser.js --output $TRAVIS_BUILD_DIR/node_modules/solidity-parser-sc/build/parser.js 18 | #script: 19 | #- cd $TRAVIS_BUILD_DIR/travscripts 20 | #- "chmod +x ./BranchScript.sh && ./BranchScript.sh" 21 | jobs: 22 | include: 23 | - stage: script 24 | script: 25 | - cd $TRAVIS_BUILD_DIR/travscripts 26 | - "chmod +x ./BranchScript.sh && ./BranchScript.sh" 27 | - stage: script 28 | script: 29 | - cd $TRAVIS_BUILD_DIR/travscripts 30 | - "chmod +x ./AfterScript.sh && ./AfterScript.sh" 31 | -------------------------------------------------------------------------------- /src/migrations-truffle-5/2_migrate_dependencies.js: -------------------------------------------------------------------------------- 1 | // TODO: Provide a index.js that migrate all in utils, GNO and OWL 2 | const deployUtils = require('@gnosis.pm/util-contracts/src/migrations-truffle-5') 3 | const deployGno = require('@gnosis.pm/gno-token/src/migrations-truffle-5') 4 | const deployOwl = require('@gnosis.pm/owl-token/src/migrations-truffle-5') 5 | 6 | async function migrate({ 7 | artifacts, 8 | deployer, 9 | network, 10 | accounts, 11 | web3, 12 | initialTokenAmount, 13 | gnoLockPeriodInHours 14 | }) { 15 | if (network === 'development') { 16 | const deployParams = { 17 | artifacts, 18 | deployer, 19 | network, 20 | accounts, 21 | initialTokenAmount, 22 | gnoLockPeriodInHours, 23 | web3 24 | } 25 | 26 | await deployUtils(deployParams) 27 | await deployGno(deployParams) 28 | await deployOwl(deployParams) 29 | } else { 30 | console.log('Not in development, so nothing to do. Current network is %s', network) 31 | } 32 | } 33 | 34 | module.exports = migrate 35 | -------------------------------------------------------------------------------- /test/resources/add-token-pair/kovan/RDN_OMG.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // RDN 3 | tokenA: { 4 | symbol: 'RDN', 5 | address: "0x3615757011112560521536258c1e7325ae3b48ae", 6 | // Check ETH oracle (i.e 730 USD/ETH) 7 | // 10$ = 10/730 ETH = 0.01369863014 ETH 8 | // Check price for WETH-RDN in the DX (i.e. 450 RDN/ETH) 9 | // i.e cli closing-price-official WETH-RDN 1 10 | // 0.0137 ETH = 0.0137 * 450 = 6.165 RDN 11 | funding: 6.165 12 | }, 13 | // OMG 14 | tokenB: { 15 | symbol: 'OMG', 16 | address: "0x00df91984582e6e96288307e9c2f20b38c8fece9", 17 | funding: 0 18 | }, 19 | // Price: 20 | // https://www.coingecko.com/en/price_charts/omisego/eth 21 | // https://www.coingecko.com/en/price_charts/raiden-network/eth 22 | // 23 | // The price should be in OMG/RDN (since its the RDN-OMG auction) 24 | // - ETH-RDN Price = 450 RDN/WETH 25 | // - ETH-OMG Price = 57 OMG/WETH 26 | // - RDN-OMG Price = 57/450 = 0,126666666 OMG/RDN 27 | initialPrice: { 28 | numerator: 57, 29 | denominator: 450 30 | } 31 | } -------------------------------------------------------------------------------- /src/truffle/get-abi-encoded-params.js: -------------------------------------------------------------------------------- 1 | /* global artifacts */ 2 | /* eslint no-undef: "error" */ 3 | 4 | var abi = require('ethereumjs-abi') 5 | 6 | async function getAbiEncodedParams () { 7 | const DutchExchange = artifacts.require('DutchExchange') 8 | const TokenFRT = artifacts.require('TokenFRT') 9 | 10 | const dxMasterAddress = DutchExchange.address 11 | const frt = await TokenFRT.deployed() 12 | const owner = await frt.owner.call() 13 | 14 | const dxProxyParams = _getAbiEncodedParams([ 'address' ], [ dxMasterAddress ]) 15 | const frtParams = _getAbiEncodedParams([ 'address' ], [ owner ]) 16 | 17 | console.log('Abi encoded params') 18 | console.log('------------------') 19 | console.log('\tDutchExchangeProxy params: %s', dxProxyParams) 20 | console.log('\tTokenFRT params: %s', frtParams) 21 | } 22 | 23 | function _getAbiEncodedParams (parameterTypes, parameterValues) { 24 | var encoded = abi.rawEncode(parameterTypes, parameterValues) 25 | 26 | return encoded.toString('hex') 27 | } 28 | 29 | module.exports = callback => { 30 | getAbiEncodedParams() 31 | .then(callback) 32 | .catch(callback) 33 | } 34 | -------------------------------------------------------------------------------- /src/migrations-truffle-5/4_deploy_FRT.js: -------------------------------------------------------------------------------- 1 | async function migrate ({ 2 | artifacts, 3 | deployer, 4 | network, 5 | accounts 6 | }) { 7 | const account = accounts[0] 8 | const TokenFRT = artifacts.require('TokenFRT') 9 | const TokenFRTProxy = artifacts.require('TokenFRTProxy') 10 | const { Math } = _getDependencies(artifacts, network, deployer) 11 | await Math.deployed() 12 | 13 | console.log('Link math lib to TokenFrt') 14 | await deployer.link(Math, [TokenFRT, TokenFRTProxy]) 15 | 16 | console.log('Deploying TokenFRT with owner: %s', account) 17 | await deployer.deploy(TokenFRT, account) 18 | await deployer.deploy(TokenFRTProxy, TokenFRT.address, account) 19 | } 20 | 21 | function _getDependencies (artifacts, network, deployer) { 22 | let Math 23 | if (network === 'development') { 24 | Math = artifacts.require('GnosisMath') 25 | } else { 26 | const contract = require('truffle-contract') 27 | Math = contract(require('@gnosis.pm/util-contracts/build/contracts/GnosisMath')) 28 | Math.setProvider(deployer.provider) 29 | } 30 | 31 | return { 32 | Math 33 | } 34 | } 35 | 36 | module.exports = migrate 37 | -------------------------------------------------------------------------------- /scripts/extractTopTokensEtherscanWeb.js: -------------------------------------------------------------------------------- 1 | // http://etherscan.io/tokens 2 | function toArray (items) { 3 | return [].slice.call(items) 4 | } 5 | 6 | var rowsHtml = document.querySelectorAll('#ContentPlaceHolder1_divresult tr') 7 | var rows = toArray(rowsHtml).slice(1) 8 | var tokens = rows 9 | .map(row => row.querySelector(':nth-child(3) h5 a')) 10 | .map(link => { 11 | var nameAndSymbolRegex = /([\w\s]+) \((\w+)\)/gi 12 | var addressRegex = /https?:\/\/etherscan.io\/token\/0x(\w+)/gi 13 | let nameAndSymbolMatch = nameAndSymbolRegex.exec(link.innerText) 14 | let addressMatch = addressRegex.exec(link.href) 15 | 16 | let name, symbol, address 17 | if (nameAndSymbolMatch) { 18 | name = nameAndSymbolMatch[1] 19 | symbol = nameAndSymbolMatch[2] 20 | } 21 | if (addressMatch) { 22 | address = '0x' + addressMatch[1] 23 | } 24 | 25 | if (nameAndSymbolMatch) { 26 | return { 27 | name, 28 | symbol, 29 | address, 30 | approve: true, 31 | etherScanLink: link.href 32 | } 33 | } 34 | }) 35 | 36 | console.log(tokens) 37 | console.log(JSON.stringify(tokens, null, 2)) 38 | -------------------------------------------------------------------------------- /contracts/ForTestingOnly/InternalTests.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "../DutchExchange.sol"; 4 | 5 | 6 | contract InternalTests is DutchExchange { 7 | constructor( 8 | TokenFRT _FRT, 9 | TokenOWL _OWL, 10 | address _owner, 11 | address _ETH, 12 | PriceOracleInterface _ETHUSDOracle, 13 | uint _thresholdNewTokenPair, 14 | uint _thresholdNewAuction 15 | ) public { 16 | setupDutchExchange( 17 | _FRT, 18 | _OWL, 19 | _owner, 20 | _ETH, 21 | _ETHUSDOracle, 22 | _thresholdNewTokenPair, 23 | _thresholdNewAuction 24 | ); 25 | } 26 | 27 | function settleFeePub(address primaryToken, address secondaryToken, uint auctionIndex, address user, uint amount) 28 | public 29 | returns (uint) 30 | { 31 | return super.settleFee(primaryToken, secondaryToken, auctionIndex, amount); 32 | } 33 | 34 | function getFeeRatioForJS(address user) public view returns (uint feeRatioNum, uint feeRatioDen) { 35 | (feeRatioNum, feeRatioDen) = super.getFeeRatio(user); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/migrations-truffle-4/4_deploy_FRT.js: -------------------------------------------------------------------------------- 1 | function migrate ({ 2 | artifacts, 3 | deployer, 4 | network, 5 | accounts 6 | }) { 7 | const TokenFRT = artifacts.require('TokenFRT') 8 | const TokenFRTProxy = artifacts.require('TokenFRTProxy') 9 | 10 | const { Math } = _getDependencies(artifacts, network, deployer) 11 | 12 | return deployer 13 | .then(() => Math.deployed()) 14 | .then(() => deployer.link(Math, [TokenFRT, TokenFRTProxy])) 15 | .then(() => deployer.deploy(TokenFRT)) 16 | // proxiedAddr, ownerAddr 17 | .then(() => { 18 | console.log('Deploying TokenFRTProxy with ACCOUNT ==> ', accounts[0]) 19 | return deployer.deploy(TokenFRTProxy, TokenFRT.address, accounts[0]) 20 | }) 21 | } 22 | 23 | function _getDependencies (artifacts, network, deployer) { 24 | let Math 25 | if (network === 'development') { 26 | Math = artifacts.require('GnosisMath') 27 | } else { 28 | const contract = require('truffle-contract') 29 | Math = contract(require('@gnosis.pm/util-contracts/build/contracts/GnosisMath')) 30 | Math.setProvider(deployer.provider) 31 | } 32 | 33 | return { 34 | Math 35 | } 36 | } 37 | 38 | module.exports = migrate 39 | -------------------------------------------------------------------------------- /test/trufflescripts/get_account_deposits.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const { getTokenDeposits } = require('./utils/contracts')(artifacts) 3 | 4 | const argv = require('minimist')(process.argv.slice(2), { string: 'a' }) 5 | 6 | /** 7 | * truffle exec test/trufflescripts/get_account_deposits.js 8 | * get ETH and GNO deposits for seller and buyer accounts 9 | * @flags: 10 | * -a
and for the given account 11 | */ 12 | 13 | module.exports = async () => { 14 | // web3 is available in the global context 15 | const [, seller, buyer] = web3.eth.accounts 16 | 17 | const getDepositsForAccounts = (...accounts) => Promise.all(accounts.map(acc => getTokenDeposits(acc))) 18 | 19 | 20 | const [sellerBal, buyerBal] = await getDepositsForAccounts(seller, buyer) 21 | 22 | 23 | console.log(`Seller:\t${sellerBal.ETH}\tETH,\t${sellerBal.GNO}\tGNO`) 24 | console.log(`Buyer:\t${buyerBal.ETH}\tETH,\t${buyerBal.GNO}\tGNO,`) 25 | 26 | if (argv.a) { 27 | const [{ ETH, GNO, FRT, OWL }] = await getDepositsForAccounts(argv.a) 28 | 29 | console.log(`\nAccount at ${argv.a} address`) 30 | console.log(`Deposit:\t${ETH}\tETH,\t${GNO}\tGNO,\t${FRT}\tFRT,\t${OWL}\tOWL`) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/migrations-truffle-5/EXAMPLE_migrate_all.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const deployUtils = require('@gnosis.pm/util-contracts/src/migrations-truffle-5') 5 | const deployGno = require('@gnosis.pm/gno-token/src/migrations-truffle-5') 6 | const deployOwl = require('@gnosis.pm/owl-token/src/migrations-truffle-5') 7 | 8 | const migrationsDx = require('@gnosis.pm/dx-contracts/src/migrations-truffle-5') 9 | 10 | module.exports = async (deployer, network, accounts) => { 11 | if (network === 'development') { 12 | const deployParams = { 13 | artifacts, 14 | deployer, 15 | network, 16 | accounts, 17 | web3, 18 | initialTokenAmount: process.env.GNO_TOKEN_AMOUNT, 19 | gnoLockPeriodInHours: process.env.GNO_LOCK_PERIOD_IN_HOURS, 20 | thresholdNewTokenPairUsd: process.env.GNO_LOCK_PERIOD_IN_HOURS, 21 | thresholdAuctionStartUsd: process.env.GNO_LOCK_PERIOD_IN_HOURS 22 | } 23 | 24 | await deployUtils(deployParams) 25 | await deployGno(deployParams) 26 | await deployOwl(deployParams) 27 | await migrationsDx(deployParams) 28 | } else { 29 | throw new Error('Migrations are just for development. Current network is %s', network) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/test-tokens/contracts/TestToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "@gnosis.pm/util-contracts/contracts/GnosisStandardToken.sol"; 4 | 5 | contract TestToken is GnosisStandardToken { 6 | address public minter; 7 | string public symbol; 8 | string public name; 9 | uint8 public decimals; 10 | 11 | modifier onlyMinter() { 12 | require(msg.sender == minter); 13 | _; 14 | } 15 | 16 | constructor(string memory _symbol, string memory _name, uint8 _decimals, uint amount) public { 17 | minter = msg.sender; 18 | symbol = _symbol; 19 | name = _name; 20 | decimals = _decimals; 21 | balances[minter] = amount; 22 | totalTokens = amount; 23 | 24 | emit SetMinter(minter); 25 | emit Mint(minter, amount); 26 | } 27 | 28 | function mint(address _address, uint amount) public onlyMinter { 29 | balances[_address] += amount; 30 | totalTokens += amount; 31 | emit Mint(_address, amount); 32 | } 33 | 34 | function changeMinter(address _minter) public onlyMinter { 35 | minter = _minter; 36 | emit SetMinter(minter); 37 | } 38 | 39 | event Mint(address indexed token, uint amount); 40 | 41 | event SetMinter(address indexed minter); 42 | } 43 | -------------------------------------------------------------------------------- /contracts/Oracle/PriceFeed.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | /// price-feed.sol 3 | 4 | // Copyright (C) 2017 DappHub, LLC 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"). 7 | // You may not use this file except in compliance with the License. 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND (express or implied). 12 | 13 | import "../Oracle/DSThing.sol"; 14 | 15 | 16 | contract PriceFeed is DSThing { 17 | uint128 val; 18 | uint32 public zzz; 19 | 20 | function peek() public view returns (bytes32, bool) { 21 | return (bytes32(uint256(val)), block.timestamp < zzz); 22 | } 23 | 24 | function read() public view returns (bytes32) { 25 | assert(block.timestamp < zzz); 26 | return bytes32(uint256(val)); 27 | } 28 | 29 | function post(uint128 val_, uint32 zzz_, address med_) public payable note auth { 30 | val = val_; 31 | zzz = zzz_; 32 | (bool success, ) = med_.call(abi.encodeWithSignature("poke()")); 33 | require(success, "The poke must succeed"); 34 | } 35 | 36 | function void() public payable note auth { 37 | zzz = 0; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /contracts/base/TokenWhitelist.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "./AuctioneerManaged.sol"; 4 | 5 | 6 | contract TokenWhitelist is AuctioneerManaged { 7 | // Mapping that stores the tokens, which are approved 8 | // Only tokens approved by auctioneer generate frtToken tokens 9 | // addressToken => boolApproved 10 | mapping(address => bool) public approvedTokens; 11 | 12 | event Approval(address indexed token, bool approved); 13 | 14 | /// @dev for quick overview of approved Tokens 15 | /// @param addressesToCheck are the ERC-20 token addresses to be checked whether they are approved 16 | function getApprovedAddressesOfList(address[] calldata addressesToCheck) external view returns (bool[] memory) { 17 | uint length = addressesToCheck.length; 18 | 19 | bool[] memory isApproved = new bool[](length); 20 | 21 | for (uint i = 0; i < length; i++) { 22 | isApproved[i] = approvedTokens[addressesToCheck[i]]; 23 | } 24 | 25 | return isApproved; 26 | } 27 | 28 | function updateApprovalOfToken(address[] memory token, bool approved) public onlyAuctioneer { 29 | for (uint i = 0; i < token.length; i++) { 30 | approvedTokens[token[i]] = approved; 31 | emit Approval(token[i], approved); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /contracts/base/DxUpgrade.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "./DxMath.sol"; 4 | import "./AuctioneerManaged.sol"; 5 | import "@gnosis.pm/util-contracts/contracts/Proxy.sol"; 6 | 7 | 8 | contract DxUpgrade is Proxied, AuctioneerManaged, DxMath { 9 | uint constant WAITING_PERIOD_CHANGE_MASTERCOPY = 30 days; 10 | 11 | address public newMasterCopy; 12 | // Time when new masterCopy is updatabale 13 | uint public masterCopyCountdown; 14 | 15 | event NewMasterCopyProposal(address newMasterCopy); 16 | 17 | function startMasterCopyCountdown(address _masterCopy) public onlyAuctioneer { 18 | require(_masterCopy != address(0), "The new master copy must be a valid address"); 19 | 20 | // Update masterCopyCountdown 21 | newMasterCopy = _masterCopy; 22 | masterCopyCountdown = add(block.timestamp, WAITING_PERIOD_CHANGE_MASTERCOPY); 23 | emit NewMasterCopyProposal(_masterCopy); 24 | } 25 | 26 | function updateMasterCopy() public { 27 | require(newMasterCopy != address(0), "The new master copy must be a valid address"); 28 | require(block.timestamp >= masterCopyCountdown, "The master contract cannot be updated in a waiting period"); 29 | 30 | // Update masterCopy 31 | masterCopy = newMasterCopy; 32 | newMasterCopy = address(0); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/migrations-truffle-4/EXAMPLE_migrate_all.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | 4 | // Note: Use "migrations-truffle-5" for truffle 5 5 | 6 | const deployUtils = require('@gnosis.pm/util-contracts/src/migrations-truffle-4') 7 | const deployGno = require('@gnosis.pm/gno-token/src/migrations-truffle-4') 8 | const deployOwl = require('@gnosis.pm/owl-token/src/migrations-truffle-4') 9 | const migrationsDx = require('@gnosis.pm/dx-contracts/src/migrations-truffle-4') 10 | 11 | module.exports = (deployer, network, accounts) => { 12 | if (network === 'development') { 13 | const deployParams = { 14 | artifacts, 15 | deployer, 16 | network, 17 | accounts, 18 | web3, 19 | initialTokenAmount: process.env.GNO_TOKEN_AMOUNT, 20 | gnoLockPeriodInHours: process.env.GNO_LOCK_PERIOD_IN_HOURS, 21 | thresholdNewTokenPairUsd: process.env.GNO_LOCK_PERIOD_IN_HOURS, 22 | thresholdAuctionStartUsd: process.env.GNO_LOCK_PERIOD_IN_HOURS 23 | } 24 | 25 | deployer 26 | .then(() => deployUtils(deployParams)) 27 | .then(() => deployGno(deployParams)) 28 | .then(() => deployOwl(deployParams)) 29 | .then(() => migrationsDx(deployParams)) 30 | } else { 31 | throw new Error('Migrations are just for development. Current network is %s', network) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts/base/SafeTransfer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@gnosis.pm/util-contracts/contracts/Token.sol"; 4 | 5 | interface BadToken { 6 | function transfer(address to, uint value) external; 7 | function transferFrom(address from, address to, uint value) external; 8 | } 9 | 10 | 11 | contract SafeTransfer { 12 | function safeTransfer(address token, address to, uint value, bool from) internal returns (bool result) { 13 | if (from) { 14 | BadToken(token).transferFrom(msg.sender, address(this), value); 15 | } else { 16 | BadToken(token).transfer(to, value); 17 | } 18 | 19 | // solium-disable-next-line security/no-inline-assembly 20 | assembly { 21 | switch returndatasize 22 | case 0 { 23 | // This is our BadToken 24 | result := not(0) // result is true 25 | } 26 | case 32 { 27 | // This is our GoodToken 28 | returndatacopy(0, 0, 32) 29 | result := mload(0) // result == returndata of external call 30 | } 31 | default { 32 | // This is not an ERC20 token 33 | result := 0 34 | } 35 | } 36 | return result; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/Oracle/DSAuth.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | 4 | contract DSAuthority { 5 | function canCall(address src, address dst, bytes4 sig) public view returns (bool); 6 | } 7 | 8 | 9 | contract DSAuthEvents { 10 | event LogSetAuthority(address indexed authority); 11 | event LogSetOwner(address indexed owner); 12 | } 13 | 14 | 15 | contract DSAuth is DSAuthEvents { 16 | DSAuthority public authority; 17 | address public owner; 18 | 19 | constructor() public { 20 | owner = msg.sender; 21 | emit LogSetOwner(msg.sender); 22 | } 23 | 24 | function setOwner(address owner_) public auth { 25 | owner = owner_; 26 | emit LogSetOwner(owner); 27 | } 28 | 29 | function setAuthority(DSAuthority authority_) public auth { 30 | authority = authority_; 31 | emit LogSetAuthority(address(authority)); 32 | } 33 | 34 | modifier auth { 35 | require(isAuthorized(msg.sender, msg.sig), "It must be an authorized call"); 36 | _; 37 | } 38 | 39 | function isAuthorized(address src, bytes4 sig) internal view returns (bool) { 40 | if (src == address(this)) { 41 | return true; 42 | } else if (src == owner) { 43 | return true; 44 | } else if (authority == DSAuthority(0)) { 45 | return false; 46 | } else { 47 | return authority.canCall(src, address(this), sig); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/trufflescripts/get_account_balances.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const { getTokenBalances } = require('./utils/contracts')(artifacts) 3 | 4 | const argv = require('minimist')(process.argv.slice(2), { string: 'a' }) 5 | 6 | /** 7 | * truffle exec test/trufflescripts/get_account_balances.js 8 | * get ETH and GNO balances for seller and buyer accounts 9 | * @flags: 10 | * -a
and for the given account 11 | */ 12 | 13 | module.exports = async () => { 14 | // web3 is available in the global context 15 | const [master, seller, buyer] = web3.eth.accounts 16 | 17 | const getBalancesForAccounts = (...accounts) => Promise.all(accounts.map(acc => getTokenBalances(acc))) 18 | 19 | 20 | const [masterBal, sellerBal, buyerBal] = await getBalancesForAccounts(master, seller, buyer) 21 | 22 | 23 | console.log(`Seller:\t${sellerBal.ETH}\tETH,\t${sellerBal.GNO}\tGNO,\t${sellerBal.FRT}\tFRT,\t${sellerBal.OWL}\tOWL`) 24 | console.log(`Buyer:\t${buyerBal.ETH}\tETH,\t${buyerBal.GNO}\tGNO,\t${buyerBal.FRT}\tFRT,\t${buyerBal.OWL}\tOWL`) 25 | console.log('________________________________________') 26 | console.log(`Master:\t${masterBal.ETH}\tETH,\t${masterBal.GNO}\tGNO,\t${masterBal.FRT}\tFRT,\t${masterBal.OWL}\tOWL`) 27 | 28 | if (argv.a) { 29 | const [{ ETH, GNO, FRT, OWL }] = await getBalancesForAccounts(argv.a) 30 | 31 | console.log(`\nAccount at ${argv.a} address`) 32 | console.log(`Balance:\t${ETH}\tETH,\t${GNO}\tGNO,\t${FRT}\tFRT,\t${OWL}\tOWL`) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/trufflescripts/revert.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const { revertSnapshot } = require('./utils')(web3) 3 | 4 | const argv = require('minimist')(process.argv.slice(2), { string: 'a' }) 5 | 6 | /** 7 | * truffle exec test/trufflescripts/claim_funds.js 8 | * to claim funds for the current auction for both seller and buyer, 9 | * from auction's sellerBalances and buyerBalances respectively 10 | * @flags: 11 | * --seller sellerBalance for seller only 12 | * --buyer buyerBalance for buyer only 13 | * -a seller|buyer|
for the given address 14 | * -i for auction with given index 15 | * --last for last auction 16 | */ 17 | 18 | module.exports = async () => { 19 | const snapshotID = argv.b || '0x01' 20 | 21 | const timeout = new Promise((resolve, reject) => setTimeout(() => reject(new Error('TIMED-OUT')), 1500)) 22 | const race = Promise.race([timeout, revertSnapshot(snapshotID)]) 23 | try { 24 | await race 25 | console.warn(` 26 | CAUTION: Reverting does NOT roll back time to snapshot time. You've been warned... 27 | `) 28 | console.log(` 29 | REVERTED TO SNAPSHOT-ID: # ${snapshotID} 30 | BLOCKNUMBER: ${web3.eth.blockNumber} 31 | `) 32 | } catch (e) { 33 | console.log(e) 34 | 35 | // Due to lock in rpc, kill w/Node 36 | process.on('exit', () => { 37 | console.log('KILLING SCRIPT') 38 | }) 39 | process.exit() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /contracts/base/EthOracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "../Oracle/PriceOracleInterface.sol"; 4 | import "./AuctioneerManaged.sol"; 5 | import "./DxMath.sol"; 6 | 7 | 8 | contract EthOracle is AuctioneerManaged, DxMath { 9 | uint constant WAITING_PERIOD_CHANGE_ORACLE = 30 days; 10 | 11 | // Price Oracle interface 12 | PriceOracleInterface public ethUSDOracle; 13 | // Price Oracle interface proposals during update process 14 | PriceOracleInterface public newProposalEthUSDOracle; 15 | 16 | uint public oracleInterfaceCountdown; 17 | 18 | event NewOracleProposal(PriceOracleInterface priceOracleInterface); 19 | 20 | function initiateEthUsdOracleUpdate(PriceOracleInterface _ethUSDOracle) public onlyAuctioneer { 21 | require(address(_ethUSDOracle) != address(0), "The oracle address must be valid"); 22 | newProposalEthUSDOracle = _ethUSDOracle; 23 | oracleInterfaceCountdown = add(block.timestamp, WAITING_PERIOD_CHANGE_ORACLE); 24 | emit NewOracleProposal(_ethUSDOracle); 25 | } 26 | 27 | function updateEthUSDOracle() public { 28 | require(address(newProposalEthUSDOracle) != address(0), "The new proposal must be a valid addres"); 29 | require( 30 | oracleInterfaceCountdown < block.timestamp, 31 | "It's not possible to update the oracle during the waiting period" 32 | ); 33 | ethUSDOracle = newProposalEthUSDOracle; 34 | newProposalEthUSDOracle = PriceOracleInterface(0); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/trufflescripts/deposit.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const { getTokenDeposits, depositToDX } = require('./utils/contracts')(artifacts) 3 | 4 | const argv = require('minimist')(process.argv.slice(2), { string: 'a' }) 5 | 6 | /** 7 | * truffle exec test/trufflescripts/deposit.js 8 | * to deposit funds to DutchExchange contracts 9 | * @flags: 10 | * --seller as the seller 11 | * --buyer as the buyer 12 | * -a
as the given address 13 | * --eth ETH tokens 14 | * --gno GNO tokens 15 | */ 16 | 17 | module.exports = async () => { 18 | if (!(argv.eth || argv.gno) || !(argv.seller || argv.buyer || argv.a)) { 19 | console.warn('No tokens or accounts specified') 20 | return 21 | } 22 | 23 | let account, accountName 24 | if (argv.a) account = accountName = argv.a 25 | else if (argv.seller) { 26 | [, account] = web3.eth.accounts 27 | accountName = 'Seller' 28 | } else { 29 | [, , account] = web3.eth.accounts 30 | accountName = 'Buyer' 31 | } 32 | 33 | console.log(`${accountName}`) 34 | 35 | let { ETH, GNO } = await getTokenDeposits(account) 36 | console.log(`Deposit was:\t${ETH}\tETH,\t${GNO}\tGNO`) 37 | 38 | const tokensToDeposit = { ETH: argv.eth, GNO: argv.gno, FRT: argv.frt, OWL: argv.owl } 39 | 40 | await depositToDX(account, tokensToDeposit); 41 | 42 | ({ ETH, GNO } = await getTokenDeposits(account)) 43 | console.log(`Deposit is:\t${ETH}\tETH,\t${GNO}\tGNO`) 44 | } 45 | -------------------------------------------------------------------------------- /test/trufflescripts/withdraw.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const { getTokenDeposits, withrawFromDX } = require('./utils/contracts')(artifacts) 3 | 4 | const argv = require('minimist')(process.argv.slice(2), { string: 'a' }) 5 | 6 | /** 7 | * truffle exec test/trufflescripts/withdraw.js 8 | * to withdraw funds from DutchExchange contract 9 | * @flags: 10 | * --seller as the seller 11 | * --buyer as the buyer 12 | * -a
as the given address 13 | * --eth ETH tokens 14 | * --gno GNO tokens 15 | */ 16 | 17 | module.exports = async () => { 18 | if (!(argv.eth || argv.gno) || !(argv.seller || argv.buyer || argv.a)) { 19 | console.warn('No tokens or accounts specified') 20 | return 21 | } 22 | 23 | let account, accountName 24 | if (argv.a) account = accountName = argv.a 25 | else if (argv.seller) { 26 | [, account] = web3.eth.accounts 27 | accountName = 'Seller' 28 | } else { 29 | [, , account] = web3.eth.accounts 30 | accountName = 'Buyer' 31 | } 32 | 33 | console.log(`${accountName}`) 34 | 35 | let { ETH, GNO } = await getTokenDeposits(account) 36 | console.log(`Deposit was:\t${ETH}\tETH,\t${GNO}\tGNO`) 37 | 38 | const tokensToWithdraw = { ETH: argv.eth, GNO: argv.gno, TUL: argv.tul, OWL: argv.owl } 39 | 40 | await withrawFromDX(account, tokensToWithdraw); 41 | 42 | ({ ETH, GNO } = await getTokenDeposits(account)) 43 | console.log(`Deposit is:\t${ETH}\tETH,\t${GNO}\tGNO`) 44 | } 45 | -------------------------------------------------------------------------------- /test/trufflescripts/utils/index.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | 3 | module.exports = (web3) => { 4 | const getTime = (blockNumber = 'latest') => web3.eth.getBlock(blockNumber).timestamp 5 | 6 | const mineCurrentBlock = () => web3.currentProvider.send({ 7 | jsonrpc: '2.0', 8 | method: 'evm_mine', 9 | params: [], 10 | id: 0, 11 | }) 12 | 13 | const increaseTimeBy = (seconds, dontMine) => { 14 | if (seconds < 0) { 15 | throw new Error('Can\'t decrease time in testrpc') 16 | } 17 | 18 | if (seconds === 0) return 19 | 20 | web3.currentProvider.send({ 21 | jsonrpc: '2.0', 22 | method: 'evm_increaseTime', 23 | params: [seconds], 24 | id: 0, 25 | }) 26 | 27 | if (!dontMine) { 28 | mineCurrentBlock() 29 | } 30 | } 31 | 32 | const setTime = (seconds, dontMine) => { 33 | const increaseBy = seconds - getTime() 34 | 35 | increaseTimeBy(increaseBy, dontMine) 36 | } 37 | 38 | const makeSnapshot = () => web3.currentProvider.send({ jsonrpc: '2.0', method: 'evm_snapshot' }).result 39 | 40 | const revertSnapshot = snapshotID => new Promise((resolve, reject) => { 41 | web3.currentProvider.sendAsync({ jsonrpc: '2.0', method: 'evm_revert', params: [snapshotID] }, (err) => { 42 | if (!err) { 43 | console.log('Revert Success') 44 | resolve(snapshotID) 45 | } else { 46 | reject(err) 47 | } 48 | }) 49 | }) 50 | 51 | return { 52 | getTime, 53 | increaseTimeBy, 54 | setTime, 55 | makeSnapshot, 56 | revertSnapshot, 57 | mineCurrentBlock, 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/trufflescripts/give_tokens.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const { getTokenBalances, giveTokens } = require('./utils/contracts')(artifacts) 3 | 4 | const argv = require('minimist')(process.argv.slice(2), { string: 'a' }) 5 | 6 | /** 7 | * truffle exec test/trufflescripts/give_tokens.js 8 | * give tokens from master 9 | * @flags: 10 | * --seller to seller 11 | * --buyer to buyer 12 | * -a
to the given address 13 | * --eth ETH tokens 14 | * --gno GNO tokens 15 | * --frt FRT tokens 16 | * --owl OWL tokens 17 | */ 18 | 19 | module.exports = async () => { 20 | if (!(argv.eth > 0 || argv.gno > 0 || argv.frt > 0 || argv.owl > 0) || !(argv.seller || argv.buyer || argv.a)) { 21 | console.warn('No tokens or accounts specified') 22 | return 23 | } 24 | 25 | // web3 is available in the global context 26 | const [master, seller, buyer] = web3.eth.accounts 27 | const account = argv.seller ? seller : argv.buyer ? buyer : argv.a 28 | const accountName = argv.seller ? 'Seller' : argv.buyer ? 'Buyer' : `Acc ${argv.a}` 29 | 30 | console.log(`${accountName}`) 31 | 32 | let { ETH, GNO, FRT, OWL } = await getTokenBalances(account) 33 | console.log(`Balance was:\t${ETH}\tETH,\t${GNO}\tGNO,\t${FRT}\tFRT,\t${OWL}\tOWL`) 34 | 35 | const tokensToGive = { ETH: argv.eth, GNO: argv.gno, FRT: argv.frt, OWL: argv.owl } 36 | 37 | giveTokens(account, tokensToGive, master); 38 | 39 | ({ ETH, GNO, FRT, OWL } = await getTokenBalances(account)) 40 | console.log(`Balance is:\t${ETH}\tETH,\t${GNO}\tGNO,\t${FRT}\tFRT,\t${OWL}\tOWL`) 41 | } 42 | -------------------------------------------------------------------------------- /test/trufflescripts/topup_accounts.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const { getTokenBalances, giveTokens } = require('./utils/contracts')(artifacts) 3 | 4 | const initBalances = { 5 | seller: { ETH: 1000, GNO: 100, FRT: 0, OWL: 100 }, 6 | buyer: { ETH: 100, GNO: 1000, FRT: 0, OWL: 100 }, 7 | } 8 | 9 | /** 10 | * truffle exec test/trufflescripts/topup_accounts.js 11 | * deposits ETH tokens in accounts names 12 | * and transfers other tokens from master account 13 | * so that seller and buyer balances are no less than initBalances fro respective tokens 14 | */ 15 | 16 | module.exports = async () => { 17 | // web3 is available in the global context 18 | const [master, seller, buyer] = web3.eth.accounts 19 | 20 | let sellerBal = await getTokenBalances(seller) 21 | let buyerBal = await getTokenBalances(buyer) 22 | 23 | const topupIfNeeded = (acc, currentBal, neededBal) => { 24 | const diffBal = Object.keys(neededBal).reduce((accum, key) => { 25 | const left = neededBal[key] - (currentBal[key] || 0) 26 | if (left > 0) accum[key] = left 27 | 28 | return accum 29 | }, {}) 30 | 31 | return giveTokens(acc, diffBal, master) 32 | } 33 | 34 | await topupIfNeeded(seller, sellerBal, initBalances.seller) 35 | await topupIfNeeded(buyer, buyerBal, initBalances.buyer) 36 | 37 | sellerBal = await getTokenBalances(seller) 38 | buyerBal = await getTokenBalances(buyer) 39 | 40 | console.log(`Seller:\t${sellerBal.ETH}\tETH,\t${sellerBal.GNO}\tGNO,\t${sellerBal.FRT}\tFRT,\t${sellerBal.OWL}\tOWL`) 41 | console.log(`Buyer:\t${buyerBal.ETH}\tETH,\t${buyerBal.GNO}\tGNO,\t${buyerBal.FRT}\tFRT,\t${buyerBal.OWL}\tOWL`) 42 | } 43 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | const truffleConfig = require('@gnosis.pm/util-contracts/src/util/truffleConfig') 2 | 3 | const DEFAULT_GAS_PRICE_GWEI = 5 4 | const DEFAULT_GAS_LIMIT = 6721975 5 | const DEFAULT_MNEMONIC = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat' 6 | 7 | // Load env vars 8 | require('dotenv').config() 9 | 10 | // Get the mnemonic 11 | const privateKey = process.env.PK 12 | let mnemonic = process.env.MNEMONIC 13 | if (!privateKey && !mnemonic) { 14 | mnemonic = DEFAULT_MNEMONIC 15 | } 16 | 17 | // Solc 18 | const compatibilityTruffle4 = false 19 | let solcUseDocker, solcVersion 20 | if (!compatibilityTruffle4) { 21 | // Use truffle 5 22 | solcUseDocker = process.env.SOLC_USE_DOCKER === 'true' || false 23 | solcVersion = '0.5.2' 24 | } 25 | 26 | // Gas price 27 | const gasPriceGWei = process.env.GAS_PRICE_GWEI || DEFAULT_GAS_PRICE_GWEI 28 | 29 | // Gas limit 30 | const gas = process.env.GAS_LIMIT || DEFAULT_GAS_LIMIT 31 | 32 | // Allow to add an aditional network (useful for docker-compose setups) 33 | // i.e. NETWORK='{ "name": "docker", "networkId": "99999", "url": "http://rpc:8545", "gas": "6700000", "gasPrice": "25000000000" }' 34 | let aditionalNetwork = process.env.NETWORK ? JSON.parse(process.env.NETWORK) : null 35 | 36 | module.exports = { 37 | ...truffleConfig({ 38 | mnemonic, 39 | privateKey, 40 | urlRinkeby: 'https://rinkeby.infura.io/v3/9408f47dedf04716a03ef994182cf150', 41 | urlKovan: 'https://kovan.infura.io/v3/9408f47dedf04716a03ef994182cf150', 42 | urlMainnet: 'https://mainnet.infura.io/v3/9408f47dedf04716a03ef994182cf150', 43 | gasPriceGWei, 44 | gas, 45 | aditionalNetwork, 46 | optimizedEnabled: true, 47 | solcUseDocker, 48 | solcVersion, 49 | compatibilityTruffle4 50 | }), 51 | // https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options 52 | mocha: { 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /contracts/base/DxMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | 4 | contract DxMath { 5 | // > Math fns 6 | function min(uint a, uint b) public pure returns (uint) { 7 | if (a < b) { 8 | return a; 9 | } else { 10 | return b; 11 | } 12 | } 13 | 14 | function atleastZero(int a) public pure returns (uint) { 15 | if (a < 0) { 16 | return 0; 17 | } else { 18 | return uint(a); 19 | } 20 | } 21 | 22 | /// @dev Returns whether an add operation causes an overflow 23 | /// @param a First addend 24 | /// @param b Second addend 25 | /// @return Did no overflow occur? 26 | function safeToAdd(uint a, uint b) public pure returns (bool) { 27 | return a + b >= a; 28 | } 29 | 30 | /// @dev Returns whether a subtraction operation causes an underflow 31 | /// @param a Minuend 32 | /// @param b Subtrahend 33 | /// @return Did no underflow occur? 34 | function safeToSub(uint a, uint b) public pure returns (bool) { 35 | return a >= b; 36 | } 37 | 38 | /// @dev Returns whether a multiply operation causes an overflow 39 | /// @param a First factor 40 | /// @param b Second factor 41 | /// @return Did no overflow occur? 42 | function safeToMul(uint a, uint b) public pure returns (bool) { 43 | return b == 0 || a * b / b == a; 44 | } 45 | 46 | /// @dev Returns sum if no overflow occurred 47 | /// @param a First addend 48 | /// @param b Second addend 49 | /// @return Sum 50 | function add(uint a, uint b) public pure returns (uint) { 51 | require(safeToAdd(a, b)); 52 | return a + b; 53 | } 54 | 55 | /// @dev Returns difference if no overflow occurred 56 | /// @param a Minuend 57 | /// @param b Subtrahend 58 | /// @return Difference 59 | function sub(uint a, uint b) public pure returns (uint) { 60 | require(safeToSub(a, b)); 61 | return a - b; 62 | } 63 | 64 | /// @dev Returns product if no overflow occurred 65 | /// @param a First factor 66 | /// @param b Second factor 67 | /// @return Product 68 | function mul(uint a, uint b) public pure returns (uint) { 69 | require(safeToMul(a, b)); 70 | return a * b; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /contracts/Oracle/PriceOracleInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | /* 4 | This contract is the interface between the MakerDAO priceFeed and our DX platform. 5 | */ 6 | 7 | import "../Oracle/PriceFeed.sol"; 8 | import "../Oracle/Medianizer.sol"; 9 | 10 | 11 | contract PriceOracleInterface { 12 | address public priceFeedSource; 13 | address public owner; 14 | bool public emergencyMode; 15 | 16 | // Modifiers 17 | modifier onlyOwner() { 18 | require(msg.sender == owner, "Only the owner can do the operation"); 19 | _; 20 | } 21 | 22 | /// @dev constructor of the contract 23 | /// @param _priceFeedSource address of price Feed Source -> should be maker feeds Medianizer contract 24 | constructor(address _owner, address _priceFeedSource) public { 25 | owner = _owner; 26 | priceFeedSource = _priceFeedSource; 27 | } 28 | 29 | /// @dev gives the owner the possibility to put the Interface into an emergencyMode, which will 30 | /// output always a price of 600 USD. This gives everyone time to set up a new pricefeed. 31 | function raiseEmergency(bool _emergencyMode) public onlyOwner { 32 | emergencyMode = _emergencyMode; 33 | } 34 | 35 | /// @dev updates the priceFeedSource 36 | /// @param _owner address of owner 37 | function updateCurator(address _owner) public onlyOwner { 38 | owner = _owner; 39 | } 40 | 41 | /// @dev returns the USDETH price 42 | function getUsdEthPricePeek() public view returns (bytes32 price, bool valid) { 43 | return Medianizer(priceFeedSource).peek(); 44 | } 45 | 46 | /// @dev returns the USDETH price, ie gets the USD price from Maker feed with 18 digits, but last 18 digits are cut off 47 | function getUSDETHPrice() public view returns (uint256) { 48 | // if the contract is in the emergencyMode, because there is an issue with the oracle, we will simply return a price of 600 USD 49 | if (emergencyMode) { 50 | return 600; 51 | } 52 | (bytes32 price, ) = Medianizer(priceFeedSource).peek(); 53 | 54 | // ensuring that there is no underflow or overflow possible, 55 | // even if the price is compromised 56 | uint priceUint = uint256(price)/(1 ether); 57 | if (priceUint == 0) { 58 | return 1; 59 | } 60 | if (priceUint > 1000000) { 61 | return 1000000; 62 | } 63 | return priceUint; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/migrations-truffle-4/3_deploy_price_feed.js: -------------------------------------------------------------------------------- 1 | const DEFAULT_ETH_USD_PRICE = process.env.ETH_USD_PRICE || 1100 // 500 USD/ETH 2 | const DEFAULT_FEED_EXPIRE_PERIOD_DAYS = process.env.FEED_EXPIRE_PERIOD_DAYS || 365 // 1 year 3 | 4 | function migrate ({ 5 | artifacts, 6 | deployer, 7 | network, 8 | accounts, 9 | web3, 10 | ethUsdPrice = DEFAULT_ETH_USD_PRICE, 11 | feedExpirePeriodDays = DEFAULT_FEED_EXPIRE_PERIOD_DAYS 12 | }) { 13 | const Medianizer = artifacts.require('Medianizer') 14 | const PriceOracleInterface = artifacts.require('PriceOracleInterface') 15 | const PriceFeed = artifacts.require('PriceFeed') 16 | 17 | const medianizerAddress = getMedianizerAddress(Medianizer) 18 | const account = accounts[0] 19 | if (!medianizerAddress) { 20 | console.log(`Deploying Maker Dao feed contracts, because they weren't published in network "${network}" yet`) 21 | // Deployment of PriceFeedInfrastructure 22 | return deployer 23 | .deploy(PriceFeed) 24 | .then(() => deployer.deploy(Medianizer)) 25 | .then(() => deployer.deploy(PriceOracleInterface, account, Medianizer.address)) 26 | .then(() => Medianizer.deployed()) 27 | .then(medianizer => medianizer.set(PriceFeed.address, { from: account })) 28 | .then(() => Promise.all([ 29 | PriceFeed.deployed(), 30 | getTime(web3) 31 | ])) 32 | .then(([priceFeed, now]) => priceFeed.post( 33 | ethUsdPrice * 1e18, 34 | now + feedExpirePeriodDays * 24 * 60 * 60, 35 | Medianizer.address, { 36 | from: account 37 | }) 38 | ) 39 | } else { 40 | console.log(`No need to deploy the Medianizer. Using ${medianizerAddress} as the Medianizer address`) 41 | console.log('Deploying PriceOracleInterface with owner: %s', account) 42 | return deployer 43 | .deploy(PriceOracleInterface, account, Medianizer.address) 44 | } 45 | } 46 | 47 | function getMedianizerAddress (Medianizer) { 48 | try { 49 | return Medianizer.address 50 | } catch (error) { 51 | // Medianizer.address throw an error if there's no config address 52 | // As a result, only development should be deploying this contracts 53 | return null 54 | } 55 | } 56 | 57 | function getTime (web3) { 58 | return new Promise((resolve, reject) => { 59 | web3.eth.getBlock('latest', (err, block) => { 60 | if (err) { 61 | return reject(err) 62 | } else { 63 | resolve(block.timestamp) 64 | } 65 | }) 66 | }) 67 | } 68 | 69 | module.exports = migrate 70 | -------------------------------------------------------------------------------- /test/dutchExchange-depositWithdrawBadToken.js: -------------------------------------------------------------------------------- 1 | /* global contract, assert, artifacts */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const { 5 | BN_ZERO, 6 | log: utilsLog, 7 | gasLogger 8 | } = require('./utils') 9 | 10 | const { getContracts } = require('./testFunctions') 11 | 12 | const BadGNO = artifacts.require('BadGNO') 13 | 14 | // Test VARS 15 | let dx, badGNO 16 | let contracts 17 | 18 | const separateLogs = () => utilsLog('\n ----------------------------------') 19 | const log = (...args) => utilsLog('\t', ...args) 20 | 21 | contract('DutchExchange - depoist/withdraw BadToken', accounts => { 22 | const [master, seller1] = accounts 23 | 24 | const startBal = { 25 | startingETH: 90.0.toWei(), 26 | startingGNO: 90.0.toWei(), 27 | ethUSDPrice: 1008.0.toWei(), 28 | sellingAmount: 50.0.toWei() 29 | } 30 | 31 | beforeEach(separateLogs) 32 | afterEach(gasLogger) 33 | 34 | before(async () => { 35 | // get contracts 36 | contracts = await getContracts({ resetCache: true }); 37 | // destructure contracts into upper state 38 | ({ 39 | DutchExchange: dx 40 | } = contracts) 41 | 42 | badGNO = await BadGNO.new(10000.0.toWei(), { from: master }) 43 | await Promise.all([ 44 | badGNO.transfer(seller1, startBal.startingGNO, { from: master }), 45 | badGNO.approve(dx.address, startBal.startingGNO, { from: seller1 }) 46 | ]) 47 | }) 48 | 49 | it('deposits bad ERC20 tokens', async () => { 50 | log('Depositing Bad GNO') 51 | const tx = await dx.deposit(badGNO.address, startBal.startingGNO, { from: seller1 }) 52 | log('tx: ', JSON.stringify(tx.logs, null, 2)) 53 | log('Succeeded Depositing Bad GNO') 54 | log('startBal.startingGNO: ', startBal.startingGNO) 55 | 56 | const deposited = await dx.balances(badGNO.address, seller1) 57 | log('deposited: ', deposited.toString()) 58 | 59 | assert(deposited.eq(startBal.startingGNO), 'deposited amount was exactly equal startingGNO') 60 | }) 61 | 62 | it('withdraws bad ERC20 tokens', async () => { 63 | log('Withdrawing Bad GNO') 64 | const tx = await dx.withdraw(badGNO.address, startBal.startingGNO, { from: seller1 }) 65 | log('tx: ', JSON.stringify(tx.logs, null, 2)) 66 | log('Succeeded Withdrawing Bad GNO') 67 | log('startBal.startingGNO: ', startBal.startingGNO) 68 | 69 | const deposited = await dx.balances(badGNO.address, seller1) 70 | log('deposited: ', deposited.toString()) 71 | 72 | assert(deposited.eq(BN_ZERO), 'deposited amount is exactly 0 after startingGNO was withdrawn') 73 | }) 74 | }) 75 | -------------------------------------------------------------------------------- /test/dutchExchange-DepositAndSell.spec.js: -------------------------------------------------------------------------------- 1 | /* global contract, assert */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const { 5 | BN, 6 | timestamp, 7 | gasLogger 8 | } = require('./utils') 9 | 10 | const { 11 | getContracts, 12 | getAuctionIndex 13 | } = require('./testFunctions') 14 | 15 | // Test VARS 16 | let eth 17 | let gno 18 | let dx 19 | 20 | let contracts 21 | 22 | contract('DutchExchange deposit and sell tests', accounts => { 23 | const testingAccs = accounts.slice(1, 5) 24 | 25 | const ETHBalance = 50.0.toWei() 26 | const initialToken1Funding = 10.0.toWei() 27 | 28 | afterEach(gasLogger) 29 | 30 | before(async () => { 31 | // get contracts 32 | contracts = await getContracts({ resetCache: true }); 33 | // destructure contracts into upper state 34 | ({ 35 | DutchExchange: dx, 36 | EtherToken: eth, 37 | TokenGNO: gno 38 | } = contracts) 39 | 40 | await Promise.all(testingAccs.map(acc => Promise.all([ 41 | eth.deposit({ from: acc, value: ETHBalance }), 42 | eth.approve(dx.address, ETHBalance, { from: acc }) 43 | ]))) 44 | 45 | await Promise.all(testingAccs.map(acc => dx.deposit(eth.address, ETHBalance.div(new BN('2')), { from: acc }))) 46 | }) 47 | 48 | it('Adds Token Pair', () => dx.addTokenPair(eth.address, gno.address, initialToken1Funding, 0, 2, 1, { from: accounts[1] })) 49 | 50 | it('DX Auction idx = 1 + Auction Start Time is > timestamp NOW [auction not started]', async () => { 51 | const [auctionIndex, auctionStart] = await Promise.all([ 52 | getAuctionIndex(), 53 | dx.getAuctionStart.call(eth.address, gno.address) 54 | ]) 55 | assert.equal(auctionIndex, 1, 'Auction index should be moved to 1') 56 | assert.isAbove(auctionStart.toNumber(), await timestamp(), 'auction time should be above now') 57 | }) 58 | 59 | it('DX balances cannot be more than amt deposited initially', () => Promise.all(testingAccs.map(async acc => { 60 | assert(await dx.balances.call(eth.address, acc) <= ETHBalance.div(new BN('2')), 'Balances cannot be more than ETHBalance / 2') 61 | }))) 62 | 63 | it('DX Sell Vol = 0 - nothing posted in sell order', async () => { 64 | assert.equal((await dx.sellVolumesCurrent.call(eth.address, gno.address)).toString(), (initialToken1Funding * 0.995).toString(), 'SellVolumesCurrent = 0') 65 | }) 66 | 67 | it('Deposits some ETH into DX and Posts Sell Order at same time', () => Promise.all(testingAccs.map(async acc => { 68 | await dx.depositAndSell(eth.address, gno.address, 4.0.toWei(), { from: acc }) 69 | const fee = 0.995 70 | 71 | assert.equal((await dx.sellVolumesCurrent.call(eth.address, gno.address)).toString(), (((4.0.toWei() * testingAccs.length) * fee) + (initialToken1Funding * 0.995)).toString(), 'SellVolumesCurrent = 4 * # of accts') 72 | }))) 73 | }) 74 | -------------------------------------------------------------------------------- /test/test-tokens/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "Migrations": { 3 | "4": { 4 | "events": {}, 5 | "links": {}, 6 | "address": "0x3fee2f347628b2aea0588c5c6f34e9e3e9d20604", 7 | "transactionHash": "0xcc49cb1456fbc7beaf1278952577ef84ec46c5efd5bf61c410e09444547faa28" 8 | } 9 | }, 10 | "TokenCVC": { 11 | "4": { 12 | "events": {}, 13 | "links": {}, 14 | "address": "0x67Fe0FDf579043de35d9CB2F10C81B63aa8aEef9", 15 | "transactionHash": "0x7a2e9f575b2f58f71a7ec311dbfa873e65fe05b65103a606acc86813f3049336" 16 | } 17 | }, 18 | "TokenDAI": { 19 | "4": { 20 | "events": {}, 21 | "links": {}, 22 | "address": "0x1638578de407719a486db086b36b53750db0199e", 23 | "transactionHash": "0xda8f1054ec6829789b2180a77be255b2ad976517b190862120ae28dc7979362e" 24 | } 25 | }, 26 | "TokenGEN": { 27 | "4": { 28 | "events": {}, 29 | "links": {}, 30 | "address": "0xA1F34744C80e7a9019a5Cd2Bf697F13DF00F9773", 31 | "transactionHash": "0x1619011f643a3a0ecd32d5d9fc348c72060710acbe264c91284d98f296475b58" 32 | } 33 | }, 34 | "TokenGRID": { 35 | "4": { 36 | "events": {}, 37 | "links": {}, 38 | "address": "0xB35E3E3E7A87C2B04DEc49a7b5DA7c1A23a09e64", 39 | "transactionHash": "0x0f7bc9c59a1f2dfe5d61ed600c2bc2473ffaad3a87e01732a9182a06c7f3755f" 40 | } 41 | }, 42 | "TokenKNC": { 43 | "4": { 44 | "events": {}, 45 | "links": {}, 46 | "address": "0xe99211284c54643f4363250b76d078ff0128c8de", 47 | "transactionHash": "0x3670f355266ffeb226b449ea8c05f5c90646b3f3fc45452a52a9a50a9f9f6fc5" 48 | } 49 | }, 50 | "TokenMKR": { 51 | "4": { 52 | "events": {}, 53 | "links": {}, 54 | "address": "0xe315cb6fa401092a7ecc92f05c62d05a974012f4", 55 | "transactionHash": "0x6e6ae370a6ffbb18fd62c26ad02a7b32a67f0a3ccf84fb90397d4c218de80839" 56 | } 57 | }, 58 | "TokenOMG": { 59 | "4": { 60 | "events": {}, 61 | "links": {}, 62 | "address": "0x06713c5fee26237c9de719f42299bccc3d8a2748", 63 | "transactionHash": "0xc8e7871e6e7c8dbfc49b3dd8d8a087cb8fc25f46173d6393401616e16c76d27c" 64 | }, 65 | "42": { 66 | "events": {}, 67 | "links": {}, 68 | "address": "0xde6efd396e18a950b45e24d6225505f48d0c627b", 69 | "transactionHash": "0x4d675f03b12a7c9cba3dc4b63008612f197d6b705e8853c586e9ced0d4844e89" 70 | } 71 | }, 72 | "TokenRDN": { 73 | "4": { 74 | "events": {}, 75 | "links": {}, 76 | "address": "0xb44fac8f17436b2364ff4860f33817c68c6ad6c1", 77 | "transactionHash": "0x44227c36a607b03a4f56f4951d32eab0e9491253037c8f056628e85c6d7fe3e6" 78 | }, 79 | "42": { 80 | "events": {}, 81 | "links": {}, 82 | "address": "0x1f7f270df126ba464228cc8d8203d2768429e085", 83 | "transactionHash": "0xdc8798e1fcc5994649384d3567982eb0df1e77ad5a4a5623ab60f9ee7c0b9b81" 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /contracts/Oracle/Medianizer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "./DSValue.sol"; 4 | 5 | 6 | contract Medianizer is DSValue { 7 | mapping(bytes12 => address) public values; 8 | mapping(address => bytes12) public indexes; 9 | bytes12 public next = bytes12(uint96(1)); 10 | uint96 public minimun = 0x1; 11 | 12 | function set(address wat) public auth { 13 | bytes12 nextId = bytes12(uint96(next) + 1); 14 | assert(nextId != 0x0); 15 | set(next, wat); 16 | next = nextId; 17 | } 18 | 19 | function set(bytes12 pos, address wat) public payable note auth { 20 | require(pos != 0x0, "pos cannot be 0x0"); 21 | require(wat == address(0) || indexes[wat] == 0, "wat is not defined or it has an index"); 22 | 23 | indexes[values[pos]] = bytes12(0); // Making sure to remove a possible existing address in that position 24 | 25 | if (wat != address(0)) { 26 | indexes[wat] = pos; 27 | } 28 | 29 | values[pos] = wat; 30 | } 31 | 32 | function setMin(uint96 min_) public payable note auth { 33 | require(min_ != 0x0, "min cannot be 0x0"); 34 | minimun = min_; 35 | } 36 | 37 | function setNext(bytes12 next_) public payable note auth { 38 | require(next_ != 0x0, "next cannot be 0x0"); 39 | next = next_; 40 | } 41 | 42 | function unset(bytes12 pos) public { 43 | set(pos, address(0)); 44 | } 45 | 46 | function unset(address wat) public { 47 | set(indexes[wat], address(0)); 48 | } 49 | 50 | function poke() public { 51 | poke(0); 52 | } 53 | 54 | function poke(bytes32) public payable note { 55 | (val, has) = compute(); 56 | } 57 | 58 | function compute() public view returns (bytes32, bool) { 59 | bytes32[] memory wuts = new bytes32[](uint96(next) - 1); 60 | uint96 ctr = 0; 61 | for (uint96 i = 1; i < uint96(next); i++) { 62 | if (values[bytes12(i)] != address(0)) { 63 | (bytes32 wut, bool wuz) = DSValue(values[bytes12(i)]).peek(); 64 | if (wuz) { 65 | if (ctr == 0 || wut >= wuts[ctr - 1]) { 66 | wuts[ctr] = wut; 67 | } else { 68 | uint96 j = 0; 69 | while (wut >= wuts[j]) { 70 | j++; 71 | } 72 | for (uint96 k = ctr; k > j; k--) { 73 | wuts[k] = wuts[k - 1]; 74 | } 75 | wuts[j] = wut; 76 | } 77 | ctr++; 78 | } 79 | } 80 | } 81 | 82 | if (ctr < minimun) 83 | return (val, false); 84 | 85 | bytes32 value; 86 | if (ctr % 2 == 0) { 87 | uint128 val1 = uint128(uint(wuts[(ctr / 2) - 1])); 88 | uint128 val2 = uint128(uint(wuts[ctr / 2])); 89 | value = bytes32(uint256(wdiv(hadd(val1, val2), 2 ether))); 90 | } else { 91 | value = wuts[(ctr - 1) / 2]; 92 | } 93 | 94 | return (value, true); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@gnosis.pm/dx-contracts", 3 | "version": "2.0.0", 4 | "description": "DutchX - Gnosis Dutch Auction Trading Platform. This npm package provides all smartcontracts used in the project", 5 | "scripts": { 6 | "rpc": "ganache-cli -d --mnemonic 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat' --defaultBalanceEther '500000000000000000000'", 7 | "preversion": "npm run restore", 8 | "test": "npm run solium && npx truffle test test/*.spec.js -s", 9 | "coverage": "npx solidity-coverage", 10 | "truffle": "npx truffle", 11 | "networks-extract": "node src/extract_network_info.js", 12 | "networks-inject": "node src/inject_network_info.js", 13 | "networks-reset": "mkdir -p build/contracts && npx truffle networks --clean && npm run networks-inject", 14 | "networks": "npx truffle networks", 15 | "compile-todo": "eslint --fix . && eslint . && npx truffle compile", 16 | "compile": "npx truffle compile", 17 | "restore": "rm -rf build && npm run compile && npm run networks-reset", 18 | "migrate": "npx truffle migrate", 19 | "lint": "eslint .", 20 | "solium": "solium --dir ./contracts/", 21 | "wrap-eth": "npx truffle exec src/truffle/wrap-eth.js", 22 | "set-weth-allowance": "npx truffle exec src/truffle/set-weth-allowance.js", 23 | "deposit-weth": "npx truffle exec src/truffle/deposit-weth.js", 24 | "add-token-pairs": "npx truffle exec src/truffle/add-token-pairs.js", 25 | "approve-tokens": "npx truffle exec src/truffle/approve-tokens.js", 26 | "unlock-mgn": "npx truffle exec src/truffle/unlock-mgn.js", 27 | "claim-unlocked-mgn": "npx truffle exec src/truffle/claim-unlocked-mgn.js", 28 | "set-auctioneer": "npx truffle exec src/truffle/set-auctioneer.js", 29 | "get-abi-encoded-params": "npx truffle exec src/truffle/get-abi-encoded-params.js", 30 | "prettier": "prettier --write --tab-width 4 --print-width 120 '**/*.sol'", 31 | "install": "cd $INIT_CWD && npm explore truffle -- npm install solc@0.5.2" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "https://github.com/gnosis/dx-contracts.git" 36 | }, 37 | "author": "Gnosis", 38 | "license": "LGPL-3.0", 39 | "dependencies": { 40 | "@gnosis.pm/gno-token": "^2.0.0", 41 | "@gnosis.pm/owl-token": "^2.0.0", 42 | "@gnosis.pm/util-contracts": "^2.0.0" 43 | }, 44 | "devDependencies": { 45 | "bignumber.js": "^5.0.0", 46 | "chai": "^4.2.0", 47 | "coveralls": "^3.0.3", 48 | "dotenv": "^8.0.0", 49 | "eslint": "^5.14.1", 50 | "eslint-config-standard": "^12.0.0", 51 | "eslint-plugin-import": "^2.14.0", 52 | "eslint-plugin-node": "^9.1.0", 53 | "eslint-plugin-promise": "^4.0.1", 54 | "eslint-plugin-standard": "^4.0.0", 55 | "ethereumjs-abi": "^0.6.6", 56 | "minimist": "^1.2.0", 57 | "openzeppelin-test-helpers": "^0.4.0", 58 | "prettier": "^1.16.4", 59 | "prettier-plugin-solidity-refactor": "^1.0.0-alpha.14", 60 | "solc": "0.5.2", 61 | "solidity-coverage": "^0.5.11", 62 | "solium": "^1.2.4", 63 | "truffle": "^5.0.5", 64 | "truffle-contract": "^4.0.17", 65 | "truffle-flattener": "^1.3.0", 66 | "truffle-hdwallet-provider": "1.0.9", 67 | "yargs": "^13.2.4" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/migrations-truffle-5/3_deploy_price_feed.js: -------------------------------------------------------------------------------- 1 | const DEFAULT_ETH_USD_PRICE = process.env.ETH_USD_PRICE || 1100 // 500 USD/ETH 2 | const DEFAULT_FEED_EXPIRE_PERIOD_DAYS = 3 | process.env.FEED_EXPIRE_PERIOD_DAYS || 365 // 1 year 4 | 5 | async function migrate ({ 6 | artifacts, 7 | deployer, 8 | network, 9 | accounts, 10 | web3, 11 | ethUsdPrice = DEFAULT_ETH_USD_PRICE, 12 | feedExpirePeriodDays = DEFAULT_FEED_EXPIRE_PERIOD_DAYS 13 | }) { 14 | const Medianizer = artifacts.require('Medianizer') 15 | const PriceOracleInterface = artifacts.require('PriceOracleInterface') 16 | const PriceFeed = artifacts.require('PriceFeed') 17 | 18 | const medianizerAddress = getMedianizerAddress(Medianizer) 19 | const account = accounts[0] 20 | if (!medianizerAddress) { 21 | console.log( 22 | `Deploying Maker Dao feed contracts, because they weren published in network "${network}" yet` 23 | ) 24 | // Deployment of PriceFeedInfrastructure 25 | 26 | console.log('Deploy PriceFeed') 27 | await deployer.deploy(PriceFeed) 28 | const priceFeed = await PriceFeed.deployed() 29 | 30 | console.log('Deploy Medianizer') 31 | await deployer.deploy(Medianizer) 32 | const medianizer = await Medianizer.deployed() 33 | 34 | console.log('Deploy PriceOracleInterface:') 35 | console.log(' - account: %s', account) 36 | console.log(' - medianizer address: %s', medianizer.address) 37 | await deployer.deploy(PriceOracleInterface, account, medianizer.address) 38 | 39 | console.log('Set price feed for medianizer:') 40 | console.log(' - price feed address: %s', PriceFeed.address) 41 | await medianizer.set(PriceFeed.address) 42 | 43 | const now = await getTime(web3) 44 | const expireTime = now + feedExpirePeriodDays * 24 * 60 * 60 45 | 46 | console.log('Post price for price feed:') 47 | console.log(' - ETH-USD: %s', ethUsdPrice) 48 | console.log(' - Expire time: %s', new Date(expireTime * 1000)) 49 | console.log(' - Medianizer address: %s', medianizer.address) 50 | 51 | const BN = web3.utils.BN 52 | const ethUsdPriceWei = web3.utils.toWei(new BN(ethUsdPrice)) 53 | await priceFeed.post(ethUsdPriceWei, expireTime, medianizer.address) 54 | } else { 55 | console.log( 56 | `No need to deploy the Medianizer. Using ${medianizerAddress} as the Medianizer address` 57 | ) 58 | 59 | console.log('Deploy PriceOracleInterface:') 60 | console.log(' - account: %s', account) 61 | console.log(' - medianizer address: %s', medianizerAddress) 62 | await deployer.deploy(PriceOracleInterface, account, medianizerAddress) 63 | } 64 | } 65 | 66 | function getMedianizerAddress (Medianizer) { 67 | try { 68 | return Medianizer.address 69 | } catch (error) { 70 | // Medianizer.address throw an error if there's no config address 71 | // As a result, only development should be deploying this contracts 72 | return null 73 | } 74 | } 75 | 76 | async function getTime (web3) { 77 | return new Promise((resolve, reject) => { 78 | web3.eth.getBlock('latest', (err, block) => { 79 | if (err) { 80 | return reject(err) 81 | } else { 82 | resolve(block.timestamp) 83 | } 84 | }) 85 | }) 86 | } 87 | 88 | module.exports = migrate 89 | -------------------------------------------------------------------------------- /contracts/ForTestingOnly/SubStandardToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | import "./BadToken.sol"; 3 | import "@gnosis.pm/util-contracts/contracts/Math.sol"; 4 | import "@gnosis.pm/util-contracts/contracts/Proxy.sol"; 5 | import {StandardTokenData} from "@gnosis.pm/util-contracts/contracts/GnosisStandardToken.sol"; 6 | 7 | 8 | /// @title Standard token contract with overflow protection 9 | contract SubStandardToken is BadToken, StandardTokenData { 10 | using GnosisMath for *; 11 | 12 | /* 13 | * Public functions 14 | */ 15 | /// @dev Transfers sender's tokens to a given address. Returns success 16 | /// @param to Address of token receiver 17 | /// @param value Number of tokens to transfer 18 | /// @return Was transfer successful? 19 | function transfer(address to, uint value) 20 | public 21 | { 22 | if ( !balances[msg.sender].safeToSub(value) || 23 | !balances[to].safeToAdd(value)) 24 | return; 25 | balances[msg.sender] -= value; 26 | balances[to] += value; 27 | emit Transfer(msg.sender, to, value); 28 | } 29 | 30 | /// @dev Allows allowed third party to transfer tokens from one address to another. Returns success 31 | /// @param from Address from where tokens are withdrawn 32 | /// @param to Address to where tokens are sent 33 | /// @param value Number of tokens to transfer 34 | /// @return Was transfer successful? 35 | function transferFrom(address from, address to, uint value) 36 | public 37 | { 38 | if ( !balances[from].safeToSub(value) || 39 | !allowances[from][msg.sender].safeToSub(value) || 40 | !balances[to].safeToAdd(value)) 41 | return; 42 | balances[from] -= value; 43 | allowances[from][msg.sender] -= value; 44 | balances[to] += value; 45 | emit Transfer(from, to, value); 46 | } 47 | 48 | /// @dev Sets approved amount of tokens for spender. Returns success 49 | /// @param spender Address of allowed account 50 | /// @param value Number of approved tokens 51 | /// @return Was approval successful? 52 | function approve(address spender, uint value) 53 | public 54 | returns (bool) 55 | { 56 | allowances[msg.sender][spender] = value; 57 | emit Approval(msg.sender, spender, value); 58 | return true; 59 | } 60 | 61 | /// @dev Returns number of allowed tokens for given address 62 | /// @param owner Address of token owner 63 | /// @param spender Address of token spender 64 | /// @return Remaining allowance for spender 65 | function allowance(address owner, address spender) 66 | public 67 | view 68 | returns (uint) 69 | { 70 | return allowances[owner][spender]; 71 | } 72 | 73 | /// @dev Returns number of tokens owned by given address 74 | /// @param owner Address of token owner 75 | /// @return Balance of owner 76 | function balanceOf(address owner) 77 | public 78 | view 79 | returns (uint) 80 | { 81 | return balances[owner]; 82 | } 83 | 84 | /// @dev Returns total supply of tokens 85 | /// @return Total supply 86 | function totalSupply() 87 | public 88 | view 89 | returns (uint) 90 | { 91 | return totalTokens; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /deploy-mainnet.txt: -------------------------------------------------------------------------------- 1 | yarn run v1.12.3 2 | $ npx truffle migrate --network mainnet 3 | Create HDWalletProvider 4 | Using gas limit: 6721.975 K 5 | Using gas price: 5 Gwei 6 | Optimizer enabled: true 7 | Sign transactions using: Private Key 8 | Using private key 9 | Truffle 4 10 | Using network 'mainnet'. 11 | 12 | Running migration: 1_initial_migration.js 13 | Deploying Migrations... 14 | [HDWalletProvider] Using nonce: 255 15 | ... 0x548baa2a1111fd30f432a09d0843a7fd6ac14e5b2751193d30623eee55cbe468 16 | Migrations: 0x135d8de3b5addb0232cd1f59002db7cf0b8b6e65 17 | Saving artifacts... 18 | Running migration: 2_DEV_migrate_dependencies.js 19 | Not in development, so nothing to do. Current network is mainnet 20 | Saving artifacts... 21 | Running migration: 3_DEV-deploy_price_feed.js 22 | No need to deploy the Medianizer. Using 0x729D19f657BD0614b4985Cf1D82531c67569197B as the Medianizer address 23 | Deploying PriceOracleInterface with owner: 0xb1f919db227048a1a45883138b9d12b9ef03df25 24 | Replacing PriceOracleInterface... 25 | [HDWalletProvider] Using nonce: 256 26 | ... 0x6b00382330116132a36bc755fd5f86a9e1ab1d3d00f5a61a62be5cfcfaf788b3 27 | PriceOracleInterface: 0xef6e5fc1a796db0a9a848eb1bb1156a9648f5ac6 28 | Saving artifacts... 29 | Running migration: 4_deploy_FRT.js 30 | Running step... 31 | Replacing TokenFRT... 32 | [HDWalletProvider] Using nonce: 257 33 | ... 0xdb55ddbb761be09782f59fa82d9a2a77f79d8dfda8e76a77c3040b6c3c5250f2 34 | TokenFRT: 0xbe4eecb9ebc040183a95f22a74a5763d442dfbb5 35 | Deploying TokenFRTProxy with ACCOUNT ==> 0xb1f919db227048a1a45883138b9d12b9ef03df25 36 | Replacing TokenFRTProxy... 37 | [HDWalletProvider] Using nonce: 258 38 | ... 0x274d24b7c8d129e3af23fba6c5a7b6dcf9b88566974a0708f772531dda63ccfe 39 | TokenFRTProxy: 0x80f222a749a2e18eb7f676d371f19ad7efeee3b7 40 | Saving artifacts... 41 | Running migration: 5_deploy_DX.js 42 | Running step... 43 | Replacing DutchExchange... 44 | [HDWalletProvider] Using nonce: 259 45 | ... 0xdfd7c80850e86ba0e8ecbe51267da31ed627bfeadec70e886069cf599de88c42 46 | DutchExchange: 0x2bae491b065032a76be1db9e9ecf5738afae203e 47 | Replacing DutchExchangeProxy... 48 | [HDWalletProvider] Using nonce: 260 49 | ... 0xb6061e32145f4b3a1b6864f0ee9d4fbdd13304afa44a6b98bdf03c05af01586f 50 | DutchExchangeProxy: 0xb9812e2fa995ec53b5b6df34d21f9304762c5497 51 | Saving artifacts... 52 | Running migration: 6_setup_DX.js 53 | Running step... 54 | Setup DX with: 55 | Owner: 0xb1f919db227048a1a45883138b9d12b9ef03df25 56 | OWL address: 0x1a5f9352af8af974bfc03399e3767df6370d82e4 57 | FRT address: 0x80f222a749a2e18eb7f676d371f19ad7efeee3b7 58 | WETH address: 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 59 | Price Oracle address: 0xef6e5fc1a796db0a9a848eb1bb1156a9648f5ac6 60 | Threshold for new token pair: 10000 61 | Threshold for auction to start: 1000 62 | [HDWalletProvider] Using nonce: 261 63 | ... 0xbe5c43f609568a4e4f4639206a520c0d60175f03a15bcb9fe245bc5da035f83a 64 | Deploy DutchExchangeHelper: 65 | Replacing DutchExchangeHelper... 66 | [HDWalletProvider] Using nonce: 262 67 | ... 0x2bf350595f8f1ebc9d8d44111900c30a6d7d46ea43162551bdcda765b2d7af5f 68 | DutchExchangeHelper: 0x64832950abccaa3d02ab8eb09aa058d789f5bb6a 69 | Saving artifacts... 70 | Running migration: 7_set_DX_as_FRT_minter.js 71 | Running step... 72 | [HDWalletProvider] Using nonce: 263 73 | ... 0x0fcf596cf41b2446d9cc918bdae3d322766396550ba5cbd2f34307af1fec7e2e 74 | Saving artifacts... 75 | Done in 627.40s. 76 | -------------------------------------------------------------------------------- /test/trufflescripts/buy_order.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const { 3 | deployed, 4 | getTokenDeposits, 5 | getAccountsStatsForTokenPairAuction, 6 | getExchangeStatsForTokenPair, 7 | postBuyOrder, 8 | } = require('./utils/contracts')(artifacts) 9 | const argv = require('minimist')(process.argv.slice(2), { string: 'a' }) 10 | 11 | /** 12 | * truffle exec test/trufflescripts/buy_order.js 13 | * to post a buy order to token pair auction as the buyer 14 | * @flags: 15 | * -n for a specific amount of buyToken 16 | * --pair token pair auction, eth,gno by default 17 | * --seller as the seller 18 | * -a
as the given account 19 | * --next to the next auction (lastAuctionIndex + 1) 20 | */ 21 | 22 | 23 | module.exports = async () => { 24 | const { eth, gno } = await deployed 25 | const availableTokens = { eth, gno } 26 | 27 | const [sell, buy] = argv.pair ? argv.pair.split(',') : ['eth', 'gno'] 28 | let sellToken = availableTokens[sell.toLowerCase()] || eth 29 | let buyToken = availableTokens[buy.toLowerCase()] || gno 30 | 31 | if (!sellToken || !buyToken) { 32 | console.warn(`Token ${!sellToken || !buyToken} is not available`) 33 | return 34 | } 35 | 36 | if (argv.n === undefined) { 37 | console.warn('No amount provided') 38 | return 39 | } 40 | 41 | sellToken = sellToken.address 42 | buyToken = buyToken.address 43 | 44 | const sellTokenName = sell ? sell.toUpperCase() : 'ETH' 45 | const buyTokenName = buy ? buy.toUpperCase() : 'GNO' 46 | 47 | let account 48 | if (argv.a) account = argv.a 49 | else if (argv.seller)[, account] = web3.eth.accounts 50 | else { 51 | [, , account] = web3.eth.accounts 52 | } 53 | 54 | let { [buyTokenName]: buyTokenDeposit = 0 } = await getTokenDeposits(account) 55 | 56 | if (buyTokenDeposit < argv.n) { 57 | console.log(`Account's deposit is ${argv.n - buyTokenDeposit} tokens short to submit this order`) 58 | return 59 | } 60 | 61 | const { latestAuctionIndex } = await getExchangeStatsForTokenPair({ sellToken, buyToken }) 62 | 63 | const index = argv.next ? latestAuctionIndex + 1 : latestAuctionIndex 64 | 65 | let [{ buyVolume }, { [account]: { buyerBalance } }] = await Promise.all([ 66 | getExchangeStatsForTokenPair({ sellToken, buyToken }), 67 | getAccountsStatsForTokenPairAuction({ sellToken, buyToken, index, accounts: [account] }), 68 | ]) 69 | 70 | console.log(`Auction ${sellTokenName} -> ${buyTokenName} index ${index} (${argv.next ? 'next' : 'current'}) 71 | was: 72 | buyVolume:\t${buyVolume} 73 | buyerBalance:\t${buyerBalance} in auction 74 | buyerDeposit:\t${buyTokenDeposit} ${buyTokenName} 75 | `) 76 | 77 | 78 | console.log(` 79 | Posting order for ${argv.n} ${buyTokenName} 80 | `) 81 | 82 | const tx = await postBuyOrder(account, { sellToken, buyToken, index, amount: argv.n }) 83 | if (!tx) return 84 | 85 | [ 86 | { [buyTokenName]: buyTokenDeposit = 0 }, 87 | { buyVolume }, 88 | { [account]: { buyerBalance } }, 89 | ] = await Promise.all([ 90 | getTokenDeposits(account), 91 | getExchangeStatsForTokenPair({ sellToken, buyToken }), 92 | getAccountsStatsForTokenPairAuction({ sellToken, buyToken, index, accounts: [account] }), 93 | ]) 94 | 95 | 96 | console.log(` now: 97 | buyVolume:\t${buyVolume} 98 | buyerBalance:\t${buyerBalance} in auction 99 | buyerDeposit:\t${buyTokenDeposit} ${buyTokenName} 100 | `) 101 | } 102 | -------------------------------------------------------------------------------- /src/migrations-truffle-5/6_setup_DX.js: -------------------------------------------------------------------------------- 1 | const DEFAULT_THRESHOLD_NEW_TOKEN_PAIR_USD = 10000 // 10K USD 2 | const DEFAULT_THRESHOLD_AUCTION_START_USD = 1000 // 1K USD 3 | 4 | async function migrate ({ 5 | artifacts, 6 | deployer, 7 | network, 8 | accounts, 9 | web3, 10 | thresholdNewTokenPairUsd = DEFAULT_THRESHOLD_NEW_TOKEN_PAIR_USD, 11 | thresholdAuctionStartUsd = DEFAULT_THRESHOLD_AUCTION_START_USD 12 | }) { 13 | const owner = accounts[0] 14 | const TokenFRT = artifacts.require('TokenFRT') 15 | const TokenFRTProxy = artifacts.require('TokenFRTProxy') 16 | const DutchExchange = artifacts.require('DutchExchange') 17 | const DutchExchangeProxy = artifacts.require('DutchExchangeProxy') 18 | const PriceOracleInterface = artifacts.require('PriceOracleInterface') 19 | const DutchExchangeHelper = artifacts.require('DutchExchangeHelper') 20 | 21 | const { 22 | EtherToken, 23 | TokenGNO, 24 | TokenOWLProxy 25 | } = _getDependencies(artifacts, network, deployer) 26 | 27 | // Ensure the folowing contracts are deployed: 28 | // - Tokens: GNO, OWL, WETH, MGN 29 | // - PriceOracleInterface 30 | // - DX contract and its proxy 31 | await TokenGNO.deployed() 32 | const tokenOWLProxy = await TokenOWLProxy.deployed() 33 | const etherToken = await EtherToken.deployed() 34 | 35 | const tokenFRT = await TokenFRT.at(TokenFRTProxy.address) 36 | 37 | const priceOracleInterface = await PriceOracleInterface.deployed() 38 | const dxProxy = await DutchExchangeProxy.deployed() 39 | await DutchExchange.deployed() 40 | const dx = await DutchExchange.at(dxProxy.address) 41 | 42 | const frtAddress = tokenFRT.address 43 | const owlAddress = tokenOWLProxy.address 44 | const wethAddress = etherToken.address 45 | const oracleAddress = priceOracleInterface.address 46 | 47 | console.log('Setup DX with:') 48 | console.log('\t Owner: %s', owner) 49 | console.log('\t OWL address: %s', owlAddress) 50 | console.log('\t FRT address: %s', frtAddress) 51 | console.log('\t WETH address: %s', wethAddress) 52 | console.log('\t Price Oracle address: %s', oracleAddress) 53 | console.log('\t Threshold for new token pair: %s', thresholdNewTokenPairUsd) 54 | console.log('\t Threshold for auction to start: %s', thresholdAuctionStartUsd) 55 | 56 | const BN = web3.utils.BN 57 | await dx.setupDutchExchange( 58 | frtAddress, 59 | owlAddress, 60 | owner, 61 | wethAddress, 62 | oracleAddress, 63 | web3.utils.toWei( 64 | new BN(thresholdNewTokenPairUsd) 65 | ), 66 | web3.utils.toWei( 67 | new BN(thresholdAuctionStartUsd) 68 | ) 69 | ) 70 | 71 | console.log('Deploy DutchExchangeHelper:') 72 | await deployer.deploy(DutchExchangeHelper, DutchExchangeProxy.address) 73 | } 74 | 75 | function _getDependencies (artifacts, network, deployer) { 76 | let EtherToken, TokenGNO, TokenOWLProxy 77 | if (network === 'development') { 78 | EtherToken = artifacts.require('EtherToken') 79 | TokenGNO = artifacts.require('TokenGNO') 80 | TokenOWLProxy = artifacts.require('TokenOWLProxy') 81 | } else { 82 | const contract = require('truffle-contract') 83 | EtherToken = contract(require('@gnosis.pm/util-contracts/build/contracts/EtherToken')) 84 | EtherToken.setProvider(deployer.provider) 85 | TokenGNO = contract(require('@gnosis.pm/gno-token/build/contracts/TokenGNO')) 86 | TokenGNO.setProvider(deployer.provider) 87 | TokenOWLProxy = contract(require('@gnosis.pm/owl-token/build/contracts/TokenOWLProxy')) 88 | TokenOWLProxy.setProvider(deployer.provider) 89 | } 90 | 91 | return { EtherToken, TokenGNO, TokenOWLProxy } 92 | } 93 | 94 | module.exports = migrate 95 | -------------------------------------------------------------------------------- /src/migrations-truffle-4/6_setup_DX.js: -------------------------------------------------------------------------------- 1 | const DEFAULT_THRESHOLD_NEW_TOKEN_PAIR_USD = 10000 // 10K USD 2 | const DEFAULT_THRESHOLD_AUCTION_START_USD = 1000 // 1K USD 3 | 4 | function migrate ({ 5 | artifacts, 6 | deployer, 7 | network, 8 | accounts, 9 | thresholdNewTokenPairUsd = DEFAULT_THRESHOLD_NEW_TOKEN_PAIR_USD, 10 | thresholdAuctionStartUsd = DEFAULT_THRESHOLD_AUCTION_START_USD 11 | }) { 12 | const TokenFRT = artifacts.require('TokenFRT') 13 | const TokenFRTProxy = artifacts.require('TokenFRTProxy') 14 | const DutchExchange = artifacts.require('DutchExchange') 15 | const DutchExchangeProxy = artifacts.require('DutchExchangeProxy') 16 | const PriceOracleInterface = artifacts.require('PriceOracleInterface') 17 | const DutchExchangeHelper = artifacts.require('DutchExchangeHelper') 18 | 19 | const { 20 | EtherToken, 21 | TokenGNO, 22 | TokenOWLProxy 23 | } = _getDependencies(artifacts, network, deployer) 24 | 25 | return deployer 26 | // Ensure the folowing contracts are deployed: 27 | // Tokens: GNO, OWL, WETH 28 | // PriceOracleInterface 29 | // DX contract and its proxy 30 | .then(() => Promise.all([ 31 | TokenFRT.deployed(), 32 | EtherToken.deployed(), 33 | TokenGNO.deployed(), 34 | TokenOWLProxy.deployed(), 35 | PriceOracleInterface.deployed(), 36 | DutchExchange.deployed(), 37 | DutchExchangeProxy.deployed() 38 | ])) 39 | .then(() => { 40 | const dx = DutchExchange.at(DutchExchangeProxy.address) 41 | const owner = accounts[0] 42 | const frt = TokenFRT.at(TokenFRTProxy.address) 43 | const frtAddress = frt.address 44 | const owlAddress = TokenOWLProxy.address 45 | const wethAddress = EtherToken.address 46 | const oracleAddress = PriceOracleInterface.address 47 | 48 | console.log('Setup DX with:') 49 | console.log('\t Owner: %s', owner) 50 | console.log('\t OWL address: %s', owlAddress) 51 | console.log('\t FRT address: %s', frtAddress) 52 | console.log('\t WETH address: %s', wethAddress) 53 | console.log('\t Price Oracle address: %s', oracleAddress) 54 | console.log('\t Threshold for new token pair: %s', thresholdNewTokenPairUsd) 55 | console.log('\t Threshold for auction to start: %s', thresholdAuctionStartUsd) 56 | 57 | return dx.setupDutchExchange( 58 | frtAddress, 59 | owlAddress, 60 | owner, 61 | wethAddress, 62 | oracleAddress, 63 | thresholdNewTokenPairUsd * 1e18, 64 | thresholdAuctionStartUsd * 1e18 65 | ) 66 | }) 67 | // Deploy the helper 68 | .then(() => { 69 | console.log('Deploy DutchExchangeHelper:') 70 | return deployer.deploy(DutchExchangeHelper, DutchExchangeProxy.address) 71 | }) 72 | } 73 | 74 | function _getDependencies (artifacts, network, deployer) { 75 | let EtherToken, TokenGNO, TokenOWLProxy 76 | if (network === 'development') { 77 | EtherToken = artifacts.require('EtherToken') 78 | TokenGNO = artifacts.require('TokenGNO') 79 | TokenOWLProxy = artifacts.require('TokenOWLProxy') 80 | } else { 81 | const contract = require('truffle-contract') 82 | EtherToken = contract(require('@gnosis.pm/util-contracts/build/contracts/EtherToken')) 83 | EtherToken.setProvider(deployer.provider) 84 | TokenGNO = contract(require('@gnosis.pm/gno-token/build/contracts/TokenGNO')) 85 | TokenGNO.setProvider(deployer.provider) 86 | TokenOWLProxy = contract(require('@gnosis.pm/owl-token/build/contracts/TokenOWLProxy')) 87 | TokenOWLProxy.setProvider(deployer.provider) 88 | } 89 | 90 | return { EtherToken, TokenGNO, TokenOWLProxy } 91 | } 92 | 93 | module.exports = migrate 94 | -------------------------------------------------------------------------------- /test/trufflescripts/sell_order.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const { 3 | deployed, 4 | getTokenDeposits, 5 | getAccountsStatsForTokenPairAuction, 6 | getExchangeStatsForTokenPair, 7 | postSellOrder, 8 | } = require('./utils/contracts')(artifacts) 9 | const argv = require('minimist')(process.argv.slice(2), { string: 'a' }) 10 | 11 | /** 12 | * truffle exec test/trufflescripts/sell_order.js 13 | * to post a sell order to token pair auction as the seller 14 | * @flags: 15 | * -n for a specific amount of sellToken 16 | * --pair token pair auction, eth,gno by default 17 | * --buyer as the buyer 18 | * -a
as the given account 19 | * --next to the next auction (lastAuctionIndex + 1) 20 | */ 21 | 22 | 23 | module.exports = async () => { 24 | const { eth, gno } = await deployed 25 | const availableTokens = { eth, gno } 26 | 27 | const [sell, buy] = argv.pair ? argv.pair.split(',') : ['eth', 'gno'] 28 | let sellToken = availableTokens[sell.toLowerCase()] || eth 29 | let buyToken = availableTokens[buy.toLowerCase()] || gno 30 | 31 | if (!sellToken || !buyToken) { 32 | console.warn(`Token ${!sellToken || !buyToken} is not available`) 33 | return 34 | } 35 | 36 | if (argv.n === undefined) { 37 | console.warn('No amount provided') 38 | return 39 | } 40 | 41 | sellToken = sellToken.address 42 | buyToken = buyToken.address 43 | 44 | const sellTokenName = sell ? sell.toUpperCase() : 'ETH' 45 | const buyTokenName = buy ? buy.toUpperCase() : 'GNO' 46 | 47 | let account 48 | if (argv.a) account = argv.a 49 | else if (argv.buyer)[, , account] = web3.eth.accounts 50 | else { 51 | [, account] = web3.eth.accounts 52 | } 53 | 54 | let { [sellTokenName]: sellTokenDeposit = 0 } = await getTokenDeposits(account) 55 | 56 | if (sellTokenDeposit < argv.n) { 57 | console.log(`Account's deposit is ${argv.n - sellTokenDeposit} tokens short to submit this order`) 58 | return 59 | } 60 | 61 | const { latestAuctionIndex } = await getExchangeStatsForTokenPair({ sellToken, buyToken }) 62 | 63 | const index = argv.next ? latestAuctionIndex + 1 : latestAuctionIndex 64 | 65 | let [{ sellVolumeCurrent, sellVolumeNext }, { [account]: { sellerBalance } }] = await Promise.all([ 66 | getExchangeStatsForTokenPair({ sellToken, buyToken }), 67 | getAccountsStatsForTokenPairAuction({ sellToken, buyToken, index, accounts: [account] }), 68 | ]) 69 | 70 | console.log(`Auction ${sellTokenName} -> ${buyTokenName} index ${index} (${argv.next ? 'next' : 'current'}) 71 | was: 72 | sellVolumeCurrent:\t${sellVolumeCurrent} 73 | sellVolumeNext:\t${sellVolumeNext} 74 | sellerBalance:\t${sellerBalance} in auction 75 | sellerDeposit:\t${sellTokenDeposit} ${sellTokenName} 76 | `) 77 | 78 | 79 | console.log(` 80 | Posting order for ${argv.n} ${sellTokenName} 81 | `) 82 | 83 | const tx = await postSellOrder(account, { sellToken, buyToken, index, amount: argv.n }) 84 | if (!tx) return 85 | 86 | [ 87 | { [sellTokenName]: sellTokenDeposit = 0 }, 88 | { sellVolumeCurrent, sellVolumeNext }, 89 | { [account]: { sellerBalance } }, 90 | ] = await Promise.all([ 91 | getTokenDeposits(account), 92 | getExchangeStatsForTokenPair({ sellToken, buyToken }), 93 | getAccountsStatsForTokenPairAuction({ sellToken, buyToken, index, accounts: [account] }), 94 | ]) 95 | 96 | 97 | console.log(` now: 98 | sellVolumeCurrent:\t${sellVolumeCurrent} 99 | sellVolumeNext:\t${sellVolumeNext} 100 | sellerBalance:\t${sellerBalance} in auction 101 | sellerDeposit:\t${sellTokenDeposit} ${sellTokenName} 102 | `) 103 | } 104 | -------------------------------------------------------------------------------- /test/trufflescripts/claim_funds.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const { 3 | deployed, 4 | getTokenDeposits, 5 | getAccountsStatsForTokenPairAuction, 6 | getExchangeStatsForTokenPair, 7 | claimBuyerFunds, 8 | claimSellerFunds, 9 | getUnclaimedBuyerFunds, 10 | getUnclaimedSellerFunds, 11 | } = require('./utils/contracts')(artifacts) 12 | const argv = require('minimist')(process.argv.slice(2), { string: 'a' }) 13 | 14 | /** 15 | * truffle exec test/trufflescripts/claim_funds.js 16 | * to claim funds for the current auction for both seller and buyer, 17 | * from auction's sellerBalances and buyerBalances respectively 18 | * @flags: 19 | * --seller sellerBalance for seller only 20 | * --buyer buyerBalance for buyer only 21 | * -a seller|buyer|master|
for the given address 22 | * -i for auction with given index 23 | * --last for last auction 24 | */ 25 | 26 | module.exports = async () => { 27 | const { eth: sellToken, gno: buyToken } = await deployed 28 | const sellTokenName = 'ETH' 29 | const buyTokenName = 'GNO' 30 | 31 | let index = argv.i !== undefined ? argv.i 32 | : (await getExchangeStatsForTokenPair({ sellToken, buyToken })).latestAuctionIndex 33 | if (argv.i === undefined && argv.last) index -= 1 34 | // eslint-disable-next-line 35 | let [master, seller, buyer] = web3.eth.accounts 36 | 37 | if (argv.a === 'seller') buyer = seller 38 | else if (argv.a === 'buyer') seller = buyer 39 | else if (argv.a === 'master') seller = buyer = master 40 | else if (argv.a) seller = buyer = argv.a 41 | 42 | const getStats = async (account, role) => { 43 | const [ 44 | { [account]: { sellerBalance, buyerBalance, claimedAmount } }, 45 | { [sellTokenName]: sellTokenDeposit = 0, [buyTokenName]: buyTokenDeposit = 0 }, 46 | [unclaimedAmount] = [], 47 | ] = await Promise.all([ 48 | getAccountsStatsForTokenPairAuction({ sellToken, buyToken, index, accounts: [account] }), 49 | getTokenDeposits(account), 50 | (role === 'seller' ? getUnclaimedSellerFunds : getUnclaimedBuyerFunds)({ sellToken, buyToken, user: account, index }), 51 | ]) 52 | 53 | return { sellerBalance, buyerBalance, claimedAmount, sellTokenDeposit, buyTokenDeposit, unclaimedAmount } 54 | } 55 | 56 | const printSeller = async () => { 57 | let { sellerBalance, claimedAmount, unclaimedAmount, sellTokenDeposit } = await getStats(seller, 'seller') 58 | 59 | console.log(` 60 | Seller\tbalance\tunclaimed \tclaimed\tdeposit 61 | was:\t${sellerBalance}\t${unclaimedAmount}\t\t${claimedAmount}\t${sellTokenDeposit}`) 62 | 63 | await claimSellerFunds({ sellToken, buyToken, user: seller, index }); 64 | 65 | ({ sellerBalance, claimedAmount, unclaimedAmount, sellTokenDeposit } = await getStats(seller, 'seller')) 66 | 67 | console.log(` is:\t\t${sellerBalance}\t${unclaimedAmount}\t\t${claimedAmount}\t${sellTokenDeposit}`) 68 | } 69 | 70 | const printBuyer = async () => { 71 | let { buyerBalance, claimedAmount, unclaimedAmount, buyTokenDeposit } = await getStats(buyer, 'buyer') 72 | 73 | console.log(` 74 | Buyer\tbalance\tunclaimed \tclaimed\tdeposit 75 | was:\t${buyerBalance}\t${unclaimedAmount}\t\t${claimedAmount}\t${buyTokenDeposit} 76 | `) 77 | 78 | await claimBuyerFunds({ sellToken, buyToken, user: buyer, index }); 79 | 80 | ({ buyerBalance, claimedAmount, unclaimedAmount, buyTokenDeposit } = await getStats(buyer, 'buyer')) 81 | 82 | console.log(` is:\t\t${buyerBalance}\t${unclaimedAmount}\t\t${claimedAmount}\t${buyTokenDeposit}`) 83 | } 84 | 85 | console.log(`in auction ${index}`) 86 | 87 | if (argv.seller) { 88 | await printSeller() 89 | } else if (argv.buyer) { 90 | await printBuyer() 91 | } else { 92 | await printSeller() 93 | await printBuyer() 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /deploy-kovan.txt: -------------------------------------------------------------------------------- 1 | yarn run v1.12.3 2 | $ npx truffle migrate --network kovan --reset 3 | Create HDWalletProvider 4 | Using gas limit: 6721.975 K 5 | Using gas price: 5 Gwei 6 | Optimizer enabled: true 7 | Sign transactions using: Private Key 8 | Using private key 9 | Truffle 4 10 | Compiling ./contracts/ForTestingOnly/BadToken.sol... 11 | Compiling ./contracts/ForTestingOnly/SubStandardToken.sol... 12 | Compiling ./contracts/ForTestingOnly/TokenGNO.sol... 13 | Compiling @gnosis.pm/util-contracts/contracts/GnosisStandardToken.sol... 14 | Compiling @gnosis.pm/util-contracts/contracts/Math.sol... 15 | Compiling @gnosis.pm/util-contracts/contracts/Proxy.sol... 16 | Compiling @gnosis.pm/util-contracts/contracts/Token.sol... 17 | Writing artifacts to ./build/contracts 18 | 19 | Using network 'kovan'. 20 | 21 | Running migration: 1_initial_migration.js 22 | Deploying Migrations... 23 | [HDWalletProvider] Using nonce: 356 24 | ... 0xbffe78bd757c5fe67f277681fb0849669c1a3998a9c19495f442a836f8ede07c 25 | Migrations: 0x4275e9a28b2e9a38e09f6ad34462840e1fdbc618 26 | Saving artifacts... 27 | Running migration: 2_DEV_migrate_dependencies.js 28 | Not in development, so nothing to do. Current network is kovan 29 | Saving artifacts... 30 | Running migration: 3_DEV-deploy_price_feed.js 31 | No need to deploy the Medianizer. Using 0xa944bd4b25c9f186a846fd5668941aa3d3b8425f as the Medianizer address 32 | Deploying PriceOracleInterface with owner: 0xf85d1a0e1b71e72013db51139f285c6d5356b712 33 | Replacing PriceOracleInterface... 34 | [HDWalletProvider] Using nonce: 357 35 | ... 0x997ee57d544c0d1ba45d554a8eadc97e2b93c290464fb3c0d9c3111e01dd921c 36 | PriceOracleInterface: 0xbf72ca4c2e7c0edf1ca82ff6c9f6e9204d1e9580 37 | Saving artifacts... 38 | Running migration: 4_deploy_FRT.js 39 | Running step... 40 | Replacing TokenFRT... 41 | [HDWalletProvider] Using nonce: 358 42 | ... 0xbbc458f59a207f07f03d5d67403242f509f3f77b1aa8728e7e9e0f2d167d4bcd 43 | TokenFRT: 0xb4d40b3dba88e53cdbd9361717f5d86899ede1b3 44 | Deploying TokenFRTProxy with ACCOUNT ==> 0xf85d1a0e1b71e72013db51139f285c6d5356b712 45 | Deploying TokenFRTProxy... 46 | [HDWalletProvider] Using nonce: 359 47 | ... 0xc5cdd5f19ff58da080b9b509dbe2caeb3c20045ebe600edecfaa7534b914f1da 48 | TokenFRTProxy: 0x2b3a76ed4edb76e8fcd261fd978e78efb313d5a2 49 | Saving artifacts... 50 | Running migration: 5_deploy_DX.js 51 | Running step... 52 | Replacing DutchExchange... 53 | [HDWalletProvider] Using nonce: 360 54 | ... 0x0a69add3414410147d96bec629774e634fe74a73de0455a96ca7aa960039e5dc 55 | DutchExchange: 0xab4860ccc54f27a1e2c7a8bed64e2980142461b2 56 | Replacing DutchExchangeProxy... 57 | [HDWalletProvider] Using nonce: 361 58 | ... 0x64d16ed028081a741ae0de0e75714878a5d54474a4d263eb0e71a64861deadb9 59 | DutchExchangeProxy: 0x775ea749a82a87f12199019e5166980f305f4c8f 60 | Saving artifacts... 61 | Running migration: 6_setup_DX.js 62 | Running step... 63 | Setup DX with: 64 | Owner: 0xf85d1a0e1b71e72013db51139f285c6d5356b712 65 | OWL address: 0xb6f77a34ff81dd13fa68b5774d74541a61047fe8 66 | FRT address: 0x2b3a76ed4edb76e8fcd261fd978e78efb313d5a2 67 | WETH address: 0xd0a1e359811322d97991e03f863a0c30c2cf029c 68 | Price Oracle address: 0xbf72ca4c2e7c0edf1ca82ff6c9f6e9204d1e9580 69 | Threshold for new token pair: 10000 70 | Threshold for auction to start: 1000 71 | [HDWalletProvider] Using nonce: 362 72 | ... 0x58abbad7a56677f34a010f8ae53473c664294598be9d665f50ce7d7fd30426c8 73 | Deploy DutchExchangeHelper: 74 | Deploying DutchExchangeHelper... 75 | [HDWalletProvider] Using nonce: 363 76 | ... 0xf9dcb3eb85bbb4283b1c9f29cf3634e021d3b20151af5363f9542e74f78001b6 77 | DutchExchangeHelper: 0xa71d54360d4adf8d52460fe068611dd608b0a8ef 78 | Saving artifacts... 79 | Running migration: 7_set_DX_as_FRT_minter.js 80 | Running step... 81 | [HDWalletProvider] Using nonce: 364 82 | ... 0x523d5a266a2eab273a6ef517e4c39b7c183b9254dac68762a5223823d5e4bcb9 83 | Saving artifacts... 84 | Done in 116.57s. 85 | -------------------------------------------------------------------------------- /src/truffle/unlock-mgn.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const assert = require('assert') 5 | 6 | const GAS = 5e5 // 500K 7 | const DEFAULT_GAS_PRICE_GWEI = 5 // 5 GWei 8 | 9 | // Usage example: 10 | // yarn MNEMONIC="secret mnemonic" yarn unlock-mgn --network rinkeby --dry-run 11 | // yarn MNEMONIC="secret mnemonic" yarn unlock-mgn --network rinkeby 12 | var argv = require('yargs') 13 | .usage('Usage: yarn unlock-mgn [--gas-price num] [--network name] [--dry-run]') 14 | .option('gasPrice', { 15 | type: 'integer', 16 | default: process.env.GAS_PRICE_GWEI || DEFAULT_GAS_PRICE_GWEI, 17 | describe: 'Gas price for adding each token pair' 18 | }) 19 | .option('network', { 20 | type: 'string', 21 | default: 'development', 22 | describe: 'One of the ethereum networks defined in truffle config' 23 | }) 24 | .option('dryRun', { 25 | type: 'boolean', 26 | default: false, 27 | describe: 'Dry run. Do not add the token pair, do just the validations.' 28 | }) 29 | .help('h') 30 | .strict() 31 | .argv 32 | 33 | async function setAuctioneer () { 34 | if (!argv._[0]) { 35 | argv.showHelp() 36 | } else { 37 | const { gasPrice, network, dryRun, auctioneer: newAuctioneer } = argv 38 | console.log('\n ************** Unlock MGN **************\n') 39 | console.log(`Data: 40 | Dry run: ${dryRun ? 'Yes' : 'No'} 41 | Network: ${network} 42 | Gas: ${GAS} 43 | Gas Price: ${gasPrice} GWei`) 44 | 45 | // Load the DX info 46 | const { mgn, dx, account } = await loadContractsInfo() 47 | const lockedAmount = await mgn.lockedTokenBalances(account) 48 | console.log(`\ 49 | User account: ${account} 50 | DutchX address: ${dx.address} 51 | MGN address: ${mgn.address} 52 | 53 | currently locked MGN: ${lockedAmount.div(1e18)} 54 | `) 55 | 56 | if (!lockedAmount.isZero()) { 57 | if (dryRun) { 58 | // Dry run 59 | console.log('The dry run execution passed all validations') 60 | await mgn.unlockTokens.call({ 61 | from: account 62 | }) 63 | console.log('Dry run success!') 64 | } else { 65 | // Real add token pair execution 66 | console.log('Changing auctioneer to: ' + newAuctioneer) 67 | const txResult = await mgn.unlockTokens({ 68 | from: account, 69 | gas: GAS, 70 | gasPrice: gasPrice * 1e9 71 | }) 72 | console.log(`Success! ${lockedAmount.div(1e18)} has been unlocked. 73 | They will be withdrawable in 24h. 74 | Transaction: ${txResult.tx}`) 75 | } 76 | } else { 77 | console.log(`The user doesn't have any locked MGN`) 78 | } 79 | 80 | 81 | 82 | console.log('\n ************** Unlock MGN **************\n') 83 | } 84 | } 85 | 86 | async function loadContractsInfo () { 87 | const DutchExchangeProxy = artifacts.require('DutchExchangeProxy') 88 | const DutchExchange = artifacts.require('DutchExchange') 89 | const TokenFRTProxy = artifacts.require('TokenFRTProxy') 90 | const TokenFRT = artifacts.require('TokenFRT') 91 | 92 | // Get contract examples 93 | const dxProxy = await DutchExchangeProxy.deployed() 94 | const dx = DutchExchange.at(dxProxy.address) 95 | const mgnProxy = await TokenFRTProxy.deployed() 96 | const mgn = TokenFRT.at(mgnProxy.address) 97 | 98 | // get Accounts 99 | const accounts = await new Promise((resolve, reject) => { 100 | web3.eth.getAccounts((error, result) => { 101 | if (error) { 102 | reject(error) 103 | } else { 104 | resolve(result) 105 | } 106 | }) 107 | }) 108 | 109 | return { 110 | mgn, 111 | dx, 112 | account: accounts[0] 113 | } 114 | } 115 | 116 | module.exports = callback => { 117 | setAuctioneer() 118 | .then(callback) 119 | .catch(callback) 120 | } 121 | -------------------------------------------------------------------------------- /deploy-rinkeby.txt: -------------------------------------------------------------------------------- 1 | yarn run v1.12.3 2 | $ npx truffle migrate --network rinkeby --reset 3 | Create HDWalletProvider 4 | Using gas limit: 6721.975 K 5 | Using gas price: 5 Gwei 6 | Optimizer enabled: true 7 | Sign transactions using: Private Key 8 | Using private key 9 | Truffle 4 10 | Compiling ./contracts/ForTestingOnly/BadToken.sol... 11 | Compiling ./contracts/ForTestingOnly/SubStandardToken.sol... 12 | Compiling ./contracts/ForTestingOnly/TokenGNO.sol... 13 | Compiling @gnosis.pm/util-contracts/contracts/GnosisStandardToken.sol... 14 | Compiling @gnosis.pm/util-contracts/contracts/Math.sol... 15 | Compiling @gnosis.pm/util-contracts/contracts/Proxy.sol... 16 | Compiling @gnosis.pm/util-contracts/contracts/Token.sol... 17 | Writing artifacts to ./build/contracts 18 | 19 | Using network 'rinkeby'. 20 | 21 | Running migration: 1_initial_migration.js 22 | Deploying Migrations... 23 | [HDWalletProvider] Using nonce: 3307 24 | ... 0x431f79e8e8c590554c6e440504df963b3d225507844f92f106afdca314414bf5 25 | Migrations: 0x22fd67707762be0109e64b708c7774900241e40e 26 | Saving artifacts... 27 | Running migration: 2_DEV_migrate_dependencies.js 28 | Not in development, so nothing to do. Current network is rinkeby 29 | Saving artifacts... 30 | Running migration: 3_DEV-deploy_price_feed.js 31 | No need to deploy the Medianizer. Using 0xd6fe8f66520a245626cb4035501903e44fd1ad44 as the Medianizer address 32 | Deploying PriceOracleInterface with owner: 0xf85d1a0e1b71e72013db51139f285c6d5356b712 33 | Replacing PriceOracleInterface... 34 | [HDWalletProvider] Using nonce: 3308 35 | ... 0x6bbf16fb6c46c05d0c7e1c919f797c6e3da995cc07fd9558c2df3eb76f460b99 36 | PriceOracleInterface: 0xbee04d92b297d79889b3bca0c33ed76e02de62b4 37 | Saving artifacts... 38 | Running migration: 4_deploy_FRT.js 39 | Running step... 40 | Replacing TokenFRT... 41 | [HDWalletProvider] Using nonce: 3309 42 | ... 0x35adc72bfb2495307027f2acb6ece4243b119589bf58e6a5a1265579b08198cb 43 | TokenFRT: 0x84fb65d27ffa1c5ed2671e680438a988f295a4f4 44 | Deploying TokenFRTProxy with ACCOUNT ==> 0xf85d1a0e1b71e72013db51139f285c6d5356b712 45 | Replacing TokenFRTProxy... 46 | [HDWalletProvider] Using nonce: 3310 47 | ... 0x85d818256c3d65ba8be5297702cc03b965b9bfdecab84ddfeb569a40e44fcadb 48 | TokenFRTProxy: 0x4ed5e1ec6bdbecf5967fe257f60e05237db9d583 49 | Saving artifacts... 50 | Running migration: 5_deploy_DX.js 51 | Running step... 52 | Replacing DutchExchange... 53 | [HDWalletProvider] Using nonce: 3311 54 | ... 0xbbd3b20e3b310233f76998e95e2ba79c8ba6794ce3228d9910ce351b30f6baa6 55 | DutchExchange: 0x7b7dc59adbe59ca4d0eb32042fd5259cf5329de1 56 | Replacing DutchExchangeProxy... 57 | [HDWalletProvider] Using nonce: 3312 58 | ... 0xfaef0f2ded54fd853f7c90d4e6a15da03d011fad1ecac5f37c3ad63165f9d308 59 | DutchExchangeProxy: 0xaaeb2035ff394fdb2c879190f95e7676f1a9444b 60 | Saving artifacts... 61 | Running migration: 6_setup_DX.js 62 | Running step... 63 | Setup DX with: 64 | Owner: 0xf85d1a0e1b71e72013db51139f285c6d5356b712 65 | OWL address: 0xa7d1c04faf998f9161fc9f800a99a809b84cfc9d 66 | FRT address: 0x4ed5e1ec6bdbecf5967fe257f60e05237db9d583 67 | WETH address: 0xc778417e063141139fce010982780140aa0cd5ab 68 | Price Oracle address: 0xbee04d92b297d79889b3bca0c33ed76e02de62b4 69 | Threshold for new token pair: 10000 70 | Threshold for auction to start: 1000 71 | [HDWalletProvider] Using nonce: 3313 72 | ... 0xa455a25114934c002056d121624028daf842c45cd1987488e121b3a6759af9a1 73 | Deploy DutchExchangeHelper: 74 | Replacing DutchExchangeHelper... 75 | [HDWalletProvider] Using nonce: 3314 76 | ... 0xe8f089304327bc6685fb768c2c9b95c1d914aaa4c98ce77b5612e7ecc5137ddd 77 | DutchExchangeHelper: 0x97f73cde38699065ba00fb5eeb34c02dcda667cd 78 | Saving artifacts... 79 | Running migration: 7_set_DX_as_FRT_minter.js 80 | Running step... 81 | [HDWalletProvider] Using nonce: 3315 82 | ... 0x725bba133d3123378e5ce16a4b1ea9ae3b7aaf44a80708791fec76541001c2ed 83 | Saving artifacts... 84 | Done in 152.13s. 85 | -------------------------------------------------------------------------------- /test/trufflescripts/deposit_and_sell.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const { 3 | deployed, 4 | getTokenDeposits, 5 | getAccountsStatsForTokenPairAuction, 6 | getExchangeStatsForTokenPair, 7 | approveForDX, 8 | depositAndSell, 9 | } = require('./utils/contracts')(artifacts) 10 | const { getTime } = require('./utils')(web3) 11 | const argv = require('minimist')(process.argv.slice(2), { string: 'a' }) 12 | 13 | /** 14 | * truffle exec test/trufflescripts/deposit_and_sell.js 15 | * to deposit and post a sell order to token pair auction as the seller 16 | * it always posts to the current (if not started) or next auction 17 | * @flags: 18 | * -n for a specific amount of sellToken 19 | * --pair token pair auction, eth,gno by default 20 | * --buyer as the buyer 21 | * -a
as the given account 22 | */ 23 | 24 | 25 | module.exports = async () => { 26 | const { eth, gno } = await deployed 27 | const availableTokens = { eth, gno } 28 | 29 | const [sell, buy] = argv.pair ? argv.pair.split(',') : ['eth', 'gno'] 30 | let sellToken = availableTokens[sell.toLowerCase()] || eth 31 | let buyToken = availableTokens[buy.toLowerCase()] || gno 32 | 33 | if (!sellToken || !buyToken) { 34 | console.warn(`Token ${!sellToken || !buyToken} is not available`) 35 | return 36 | } 37 | 38 | if (argv.n === undefined) { 39 | console.warn('No amount provided') 40 | return 41 | } 42 | 43 | sellToken = sellToken.address 44 | buyToken = buyToken.address 45 | 46 | const sellTokenName = sell ? sell.toUpperCase() : 'ETH' 47 | const buyTokenName = buy ? buy.toUpperCase() : 'GNO' 48 | 49 | let account 50 | if (argv.a) account = argv.a 51 | else if (argv.buyer)[, , account] = web3.eth.accounts 52 | else { 53 | [, account] = web3.eth.accounts 54 | } 55 | 56 | let { [sellTokenName]: sellTokenDeposit = 0 } = await getTokenDeposits(account) 57 | 58 | const { latestAuctionIndex, auctionStart } = await getExchangeStatsForTokenPair({ sellToken, buyToken }) 59 | const postingToNextAuction = !(auctionStart === 1 || auctionStart > await getTime()) 60 | 61 | const index = postingToNextAuction ? latestAuctionIndex + 1 : latestAuctionIndex 62 | 63 | let [{ sellVolumeCurrent, sellVolumeNext }, { [account]: { sellerBalance } }] = await Promise.all([ 64 | getExchangeStatsForTokenPair({ sellToken, buyToken }), 65 | getAccountsStatsForTokenPairAuction({ sellToken, buyToken, index, accounts: [account] }), 66 | ]) 67 | 68 | console.log(`Auction ${sellTokenName} -> ${buyTokenName} index ${index} (${postingToNextAuction ? 'next' : 'current'}) 69 | was: 70 | sellVolumeCurrent:\t${sellVolumeCurrent} 71 | sellVolumeNext:\t${sellVolumeNext} 72 | sellerBalance:\t${sellerBalance} in auction 73 | sellerDeposit:\t${sellTokenDeposit} ${sellTokenName} 74 | `) 75 | 76 | console.log(` 77 | Approving transfer of ${argv.n} ${sellTokenName} to DX 78 | `) 79 | 80 | const apprTX = await approveForDX(account, { [sellTokenName]: argv.n }) 81 | if (!apprTX) return 82 | 83 | console.log(` 84 | Posting deposit and sell order for ${argv.n} ${sellTokenName} 85 | `) 86 | 87 | const tx = await depositAndSell(account, { sellToken, buyToken, amount: argv.n }) 88 | if (!tx) return 89 | 90 | [ 91 | { [sellTokenName]: sellTokenDeposit = 0 }, 92 | { sellVolumeCurrent, sellVolumeNext }, 93 | { [account]: { sellerBalance } }, 94 | ] = await Promise.all([ 95 | getTokenDeposits(account), 96 | getExchangeStatsForTokenPair({ sellToken, buyToken }), 97 | getAccountsStatsForTokenPairAuction({ sellToken, buyToken, index, accounts: [account] }), 98 | ]) 99 | 100 | console.log(` now: 101 | sellVolumeCurrent:\t${sellVolumeCurrent} 102 | sellVolumeNext:\t${sellVolumeNext} 103 | sellerBalance:\t${sellerBalance} in auction 104 | sellerDeposit:\t${sellTokenDeposit} ${sellTokenName} 105 | `) 106 | } 107 | -------------------------------------------------------------------------------- /src/truffle/set-auctioneer.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const assert = require('assert') 5 | 6 | const GAS = 5e5 // 500K 7 | const DEFAULT_GAS_PRICE_GWEI = 5 // 5 GWei 8 | 9 | // Usage example: 10 | // yarn set-auctioneer --auctioneer 0x1 --dry-run 11 | // yarn set-auctioneer --auctioneer 0x1 12 | var argv = require('yargs') 13 | .usage('Usage: yarn set-auctioner [--auctioneer newAddress] [--gas-price num] [--network name] [--dry-run]') 14 | .option('auctioneer', { 15 | type: 'string', 16 | describe: 'New auctioneer' 17 | }) 18 | .option('gasPrice', { 19 | type: 'integer', 20 | default: process.env.GAS_PRICE_GWEI || DEFAULT_GAS_PRICE_GWEI, 21 | describe: 'Gas price for adding each token pair' 22 | }) 23 | .option('network', { 24 | type: 'string', 25 | default: 'development', 26 | describe: 'One of the ethereum networks defined in truffle config' 27 | }) 28 | .option('dryRun', { 29 | type: 'boolean', 30 | default: false, 31 | describe: 'Dry run. Do not add the token pair, do just the validations.' 32 | }) 33 | .help('h') 34 | .strict() 35 | .argv 36 | 37 | async function setAuctioneer () { 38 | if (!argv._[0]) { 39 | argv.showHelp() 40 | } else { 41 | const { gasPrice, network, dryRun, auctioneer: newAuctioneer } = argv 42 | console.log('\n ************** Set auctioneer **************\n') 43 | console.log(`Data: 44 | Dry run: ${dryRun ? 'Yes' : 'No'} 45 | Network: ${network} 46 | Gas: ${GAS} 47 | Gas Price: ${gasPrice} GWei`) 48 | 49 | // Load the DX info 50 | const { auctioneer, dx, account } = await loadContractsInfo() 51 | console.log(`\ 52 | User account: ${account} 53 | DutchX Auctioneer: ${auctioneer} 54 | DutchX address: ${dx.address} 55 | 56 | Set auctioneer to: ${newAuctioneer} 57 | `) 58 | assert(newAuctioneer, 'auctioneer is a required param') 59 | 60 | if (auctioneer !== newAuctioneer) { 61 | assert.equal(account, auctioneer, 'Only the auctioneer can update the auctioneer. Check the account you are using') 62 | 63 | if (dryRun) { 64 | // Dry run 65 | console.log('The dry run execution passed all validations') 66 | await dx.updateAuctioneer.call(newAuctioneer, { 67 | from: account 68 | }) 69 | console.log('Dry run success!') 70 | } else { 71 | // Real add token pair execution 72 | console.log('Changing auctioneer to: ' + newAuctioneer) 73 | const addTokenResult = await dx.updateAuctioneer(newAuctioneer, { 74 | from: account, 75 | gas: GAS, 76 | gasPrice: gasPrice * 1e9 77 | }) 78 | console.log('Success! The token pair was added. Transaction: ' + addTokenResult.tx) 79 | } 80 | } else { 81 | console.log(`The auctioneer is already ${newAuctioneer}. So, there nothing to do`) 82 | } 83 | 84 | 85 | 86 | console.log('\n ************** Set auctioneer **************\n') 87 | } 88 | } 89 | 90 | async function loadContractsInfo () { 91 | const DutchExchangeProxy = artifacts.require('DutchExchangeProxy') 92 | const DutchExchange = artifacts.require('DutchExchange') 93 | 94 | // Get contract examples 95 | const dxProxy = await DutchExchangeProxy.deployed() 96 | const dx = DutchExchange.at(dxProxy.address) 97 | 98 | // Get some data from dx 99 | const [ 100 | auctioneer, 101 | accounts 102 | ] = await Promise.all([ 103 | // Get the auctioneer 104 | dx.auctioneer.call(), 105 | 106 | // get Accounts 107 | new Promise((resolve, reject) => { 108 | web3.eth.getAccounts((error, result) => { 109 | if (error) { 110 | reject(error) 111 | } else { 112 | resolve(result) 113 | } 114 | }) 115 | }) 116 | ]) 117 | 118 | return { 119 | auctioneer, 120 | dx, 121 | account: accounts[0] 122 | } 123 | } 124 | 125 | module.exports = callback => { 126 | setAuctioneer() 127 | .then(callback) 128 | .catch(callback) 129 | } 130 | -------------------------------------------------------------------------------- /src/truffle/wrap-eth.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const assert = require('assert') 5 | const contract = require('truffle-contract') 6 | 7 | const GAS = 5e5 // 500K 8 | 9 | // Usage example: 10 | // PK=PRIVATE_KEY yarn wrap-eth --amount 40 --dry-run 11 | var argv = require('yargs') 12 | .usage('Usage: yarn wrap-eth [--amount wethAmount] [--network name] [--dry-run]') 13 | .option('amount', { 14 | type: 'integer', 15 | describe: 'Amount of ETH to wrap into WETH', 16 | demandOption: true 17 | }) 18 | .option('network', { 19 | type: 'string', 20 | default: 'development', 21 | describe: 'One of the ethereum networks defined in truffle config' 22 | }) 23 | .option('dryRun', { 24 | type: 'boolean', 25 | default: false, 26 | describe: 'Dry run. Do not add the token pair, do just the validations.' 27 | }) 28 | .help('h') 29 | .strict() 30 | .argv 31 | 32 | async function wrapEth () { 33 | if (!argv._[0]) { 34 | argv.showHelp() 35 | } else { 36 | const { network, amount, dryRun } = argv 37 | console.log('\n ************** Wrap ETH **************\n') 38 | console.log(`Data: 39 | Dry run: ${dryRun ? 'Yes' : 'No'} 40 | Network: ${network} 41 | Gas: ${GAS} 42 | `) 43 | 44 | // Load the DX info 45 | const { weth, dx, account, etherBalance } = await loadContractsInfo() 46 | const wethBalance = await weth.balanceOf(account) 47 | const wethBalanceInDx = await dx.balances(weth.address, account) 48 | 49 | console.log(`\ 50 | Addresses: 51 | DutchX address: ${dx.address} 52 | WETH address: ${weth.address} 53 | Account: ${account} 54 | Balances: 55 | Balance of Ether: ${etherBalance / 1e18} 56 | Balance of WETH: ${wethBalance / 1e18} 57 | Balance of WETH in DutchX: ${wethBalanceInDx / 1e18} 58 | Amount to wrap: ${amount} 59 | `) 60 | assert(amount > 0, 'amount must be grater than 0') 61 | assert(amount * 1e18 <= etherBalance, "You don't have enough Ether balance") 62 | 63 | if (dryRun) { 64 | // Dry run 65 | console.log('The dry run execution passed all validations') 66 | await weth.deposit.call({ 67 | from: account, 68 | value: amount * 1e18 69 | }) 70 | console.log('Dry run success!') 71 | } else { 72 | // Real wrap WETH 73 | console.log('Wrapping %s ETH into WETH', amount) 74 | const wrapResult = await weth.deposit({ 75 | from: account, 76 | value: amount * 1e18 77 | }) 78 | console.log('Success! Wrapped %s. Transaction: %s', amount, wrapResult.tx) 79 | } 80 | 81 | console.log('\n ************** Wrap ETH **************\n') 82 | } 83 | } 84 | 85 | async function loadContractsInfo () { 86 | const DutchExchangeProxy = artifacts.require('DutchExchangeProxy') 87 | const DutchExchange = artifacts.require('DutchExchange') 88 | 89 | const EtherToken = contract(require('@gnosis.pm/util-contracts/build/contracts/EtherToken')) 90 | EtherToken.setProvider(web3.currentProvider) 91 | 92 | // Get contract examples 93 | const dxProxy = await DutchExchangeProxy.deployed() 94 | const dx = DutchExchange.at(dxProxy.address) 95 | const weth = await EtherToken.deployed() 96 | 97 | // get Accounts 98 | const accounts = await new Promise((resolve, reject) => { 99 | web3.eth.getAccounts((error, result) => { 100 | if (error) { 101 | reject(error) 102 | } else { 103 | resolve(result) 104 | } 105 | }) 106 | }) 107 | 108 | const account = accounts[0] 109 | const etherBalance = await new Promise((resolve, reject) => { 110 | web3.eth.getBalance(account, (error, result) => { 111 | if (error) { 112 | reject(error) 113 | } else { 114 | resolve(result) 115 | } 116 | }) 117 | }) 118 | 119 | return { 120 | weth, 121 | dx, 122 | etherBalance, 123 | account 124 | } 125 | } 126 | 127 | module.exports = callback => { 128 | wrapEth() 129 | .then(callback) 130 | .catch(callback) 131 | } 132 | -------------------------------------------------------------------------------- /src/truffle/set-weth-allowance.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const assert = require('assert') 5 | const contract = require('truffle-contract') 6 | 7 | const GAS = 5e5 // 500K 8 | 9 | // Usage example: 10 | // PK=PRIVATE_KEY yarn set-weth-allowance --amount 40 --dry-run 11 | var argv = require('yargs') 12 | .usage('Usage: yarn set-weth-allowance [--amount wethAmount] [--network name] [--dry-run]') 13 | .option('amount', { 14 | type: 'integer', 15 | describe: 'Amount of WETH to set as the new allowance', 16 | demandOption: true 17 | }) 18 | .option('network', { 19 | type: 'string', 20 | default: 'development', 21 | describe: 'One of the ethereum networks defined in truffle config' 22 | }) 23 | .option('dryRun', { 24 | type: 'boolean', 25 | default: false, 26 | describe: 'Dry run. Do not add the token pair, do just the validations.' 27 | }) 28 | .help('h') 29 | .strict() 30 | .argv 31 | 32 | async function setWethAllowance () { 33 | if (!argv._[0]) { 34 | argv.showHelp() 35 | } else { 36 | const { network, amount, dryRun } = argv 37 | console.log('\n ************** Set WETH Allowance **************\n') 38 | console.log(`Data: 39 | Dry run: ${dryRun ? 'Yes' : 'No'} 40 | Network: ${network} 41 | Gas: ${GAS} 42 | `) 43 | 44 | // Load the DX info 45 | const { weth, dx, account, etherBalance } = await loadContractsInfo() 46 | const wethBalance = await weth.balanceOf(account) 47 | const oldAllowance = await weth.allowance(account, dx.address) 48 | const wethBalanceInDx = await dx.balances(weth.address, account) 49 | 50 | console.log(`\ 51 | Addresses: 52 | DutchX address: ${dx.address} 53 | WETH address: ${weth.address} 54 | Account: ${account} 55 | Balances: 56 | Balance of Ether: ${etherBalance / 1e18} 57 | Balance of WETH: ${wethBalance / 1e18} 58 | Balance of WETH in DutchX: ${wethBalanceInDx / 1e18} 59 | Old Allowance: ${oldAllowance / 1e18} 60 | New Allowance: ${amount} 61 | `) 62 | 63 | if (dryRun) { 64 | // Dry run 65 | console.log('The dry run execution passed all validations') 66 | await weth.approve.call(dx.address, amount * 1e18, { 67 | from: account 68 | }) 69 | console.log('Dry run success!') 70 | } else { 71 | // Real wrap WETH 72 | console.log('Setting the allowance to %s for WETH', amount) 73 | const setAllowanceResult = await weth.approve(dx.address, amount * 1e18, { 74 | from: account 75 | }) 76 | console.log('Success! The allowance is now %s. Transaction: %s', amount, setAllowanceResult.tx) 77 | } 78 | 79 | console.log('\n ************** Set WETH Allowance **************\n') 80 | } 81 | } 82 | 83 | async function loadContractsInfo () { 84 | const DutchExchangeProxy = artifacts.require('DutchExchangeProxy') 85 | const DutchExchange = artifacts.require('DutchExchange') 86 | 87 | const EtherToken = contract(require('@gnosis.pm/util-contracts/build/contracts/EtherToken')) 88 | EtherToken.setProvider(web3.currentProvider) 89 | 90 | // Get contract examples 91 | const dxProxy = await DutchExchangeProxy.deployed() 92 | const dx = DutchExchange.at(dxProxy.address) 93 | const weth = await EtherToken.deployed() 94 | 95 | // get Accounts 96 | const accounts = await new Promise((resolve, reject) => { 97 | web3.eth.getAccounts((error, result) => { 98 | if (error) { 99 | reject(error) 100 | } else { 101 | resolve(result) 102 | } 103 | }) 104 | }) 105 | 106 | const account = accounts[0] 107 | const etherBalance = await new Promise((resolve, reject) => { 108 | web3.eth.getBalance(account, (error, result) => { 109 | if (error) { 110 | reject(error) 111 | } else { 112 | resolve(result) 113 | } 114 | }) 115 | }) 116 | 117 | return { 118 | weth, 119 | dx, 120 | etherBalance, 121 | account 122 | } 123 | } 124 | 125 | module.exports = callback => { 126 | setWethAllowance() 127 | .then(callback) 128 | .catch(callback) 129 | } 130 | -------------------------------------------------------------------------------- /test/trufflescripts/add_token_pair.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0, no-multi-spaces:0, prefer-destructuring:1 */ 2 | 3 | const TokenETH = artifacts.require('EtherToken') 4 | const TokenGNO = artifacts.require('TokenGNO') 5 | const PriceOracle = artifacts.require('PriceFeed') 6 | const Medianizer = artifacts.require('Medianizer') 7 | const DutchExchange = artifacts.require('DutchExchange') 8 | const Proxy = artifacts.require('DutchExchangeProxy') 9 | 10 | const argv = require('minimist')(process.argv.slice(2), { string: 'a' }) 11 | 12 | /** 13 | * truffle exec test/trufflescripts/add_token_pair.js 14 | * adds a new TokenPair as master account by default 15 | * @flags: 16 | * --seller as the seller 17 | * --buyer as the buyer 18 | * -a
as the given address 19 | * --t1 starting T1(ETH) tokens 20 | * --t2 starting T2(GNO) tokens 21 | * --pair token pair auction, eth,gno by default 22 | */ 23 | 24 | module.exports = async () => { 25 | const { accounts } = web3.eth 26 | const [master, seller, buyer] = accounts 27 | 28 | let account, accountName 29 | if (argv.a) account = accountName = argv.a 30 | else if (argv.seller) { 31 | account = seller 32 | accountName = 'Seller' 33 | } else if (argv.buyer) { 34 | account = buyer 35 | accountName = 'Buyer' 36 | } else { 37 | account = master 38 | accountName = 'Master' 39 | } 40 | 41 | const dx = await DutchExchange.at(Proxy.address) 42 | const eth = await TokenETH.deployed() 43 | const gno = await TokenGNO.deployed() 44 | const rdn = await TokenGNO.new(web3.toWei(10000, 'ether'), { from: master }) 45 | const omg = await TokenGNO.new(web3.toWei(10000, 'ether'), { from: master }) 46 | const oracle = await PriceOracle.deployed() 47 | const medianizer = await Medianizer.deployed() 48 | 49 | const availableTokens = { 50 | eth, 51 | gno, 52 | rdn, 53 | omg, 54 | } 55 | 56 | const [sell, buy] = argv.pair ? argv.pair.split(',') : ['eth', 'gno'] 57 | 58 | const sellToken = availableTokens[sell.toLowerCase()] 59 | const buyToken = availableTokens[buy.toLowerCase()] 60 | 61 | const startingETH = argv.t1 || web3.toWei(10, 'ether') 62 | const startingGNO = argv.t2 || web3.toWei(10, 'ether') 63 | const ethUSDPrice = web3.toWei(5000, 'ether') 64 | 65 | await Promise.all(accounts.map((acct) => { 66 | const otherToken = sell === 'eth' ? buyToken : sellToken 67 | return Promise.all([ 68 | eth.deposit({ from: acct, value: startingETH }), 69 | eth.approve(dx.address, startingETH, { from: acct }), 70 | otherToken.transfer(acct, startingGNO, { from: master }), 71 | otherToken.approve(dx.address, startingGNO, { from: acct }), 72 | ]) 73 | })) 74 | // Deposit depends on ABOVE finishing first... so run here 75 | await Promise.all(accounts.map(acct => Promise.all([ 76 | dx.deposit(sellToken.address, startingETH, { from: acct }), 77 | dx.deposit(buyToken.address, startingGNO, { from: acct }), 78 | ]))) 79 | 80 | await oracle.post(ethUSDPrice, 1516168838 * 2, medianizer.address, { from: master }) 81 | 82 | console.log('Threshold new token pair == ', (await dx.thresholdNewTokenPair.call()).toNumber() / (10 ** 18)) 83 | 84 | console.log('Account', accountName) 85 | console.log('Sell Token = ', sell, '|| BAL == ', (await dx.balances.call(sellToken.address, account)).toNumber() / (10 ** 18)) 86 | console.log('Buy Token = ', buy, '|| BAL == ', (await dx.balances.call(buyToken.address, account)).toNumber() / (10 ** 18)) 87 | 88 | console.log('FundingUSD == ', startingETH * ethUSDPrice) 89 | console.log('Auction Index == ', (await dx.getAuctionIndex.call(sellToken.address, buyToken.address)).toNumber()) 90 | 91 | const funds = sell === 'eth' ? 92 | [web3.toWei(10, 'ether'), 0, 2, 1] : 93 | [0, web3.toWei(10, 'ether'), 1, 2] 94 | 95 | return dx.addTokenPair( 96 | sellToken.address, // -----> SellToken Address 97 | buyToken.address, // -----> BuyToken Address 98 | ...funds, // -----> sellFund, buyFund, closingPriceNum, closingPriceDen 99 | { from: account }, 100 | ) 101 | } 102 | -------------------------------------------------------------------------------- /test/trufflescripts/increase_time.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | const { deployed, getExchangeStatsForTokenPair, getAuctionStatsForTokenPair } = require('./utils/contracts')(artifacts) 3 | const { getTime, increaseTimeBy, setTime } = require('./utils')(web3) 4 | 5 | const argv = require('minimist')(process.argv.slice(2)) 6 | 7 | const getTimeStr = (timestamp) => { 8 | const date = new Date(Math.abs(timestamp)) 9 | const hh = date.getUTCHours() 10 | const mm = date.getUTCMinutes() 11 | const ss = date.getUTCSeconds() 12 | 13 | return `${hh ? `${hh} hour(s) ` : ''}${mm ? `${mm} minute(s) ` : ''}${ss ? `${ss} second(s) ` : ''}` 14 | } 15 | 16 | const getSeconds = ({ h = 0, m = 0, s = 0 }) => (h * 60 * 60) + (m * 60) + s 17 | 18 | const getNumDenStr = ([num, den]) => `${num}/${den} = ${(num / den).toFixed(8)}` 19 | 20 | /** 21 | * truffle exec test/trufflescripts/increase_timer.js 22 | * increases auction time 23 | * @flags: 24 | * --start first, sets time to auction start 25 | * --clear or auction end, 26 | * then increases time by 27 | * -h given hours 28 | * -m given minutes 29 | * -s given seconds 30 | */ 31 | 32 | module.exports = async () => { 33 | const { eth, gno } = await deployed 34 | 35 | const printAuctionTimes = async () => { 36 | const now = getTime() 37 | const { 38 | auctionStart, 39 | latestAuctionIndex, 40 | sellTokenOraclePrice, 41 | buyTokenOraclePrice, 42 | } = await getExchangeStatsForTokenPair({ sellToken: eth, buyToken: gno }) 43 | 44 | // const timeUntilStart = auctionStart - now 45 | // const timeStr = getTimeStr(timeUntilStart * 1000) 46 | 47 | 48 | console.log(` 49 | Auction index ${latestAuctionIndex} 50 | ______________________________________ 51 | now:\t\t\t${new Date(now * 1000).toTimeString()} 52 | auctionStart:\t\t\t${new Date(auctionStart * 1000).toTimeString()} 53 | `) 54 | 55 | if (auctionStart === 0) { 56 | console.log('auction has never run before') 57 | } else { 58 | const timeUntilStart = auctionStart - now 59 | const timeStr = getTimeStr(timeUntilStart * 1000) 60 | 61 | if (timeUntilStart > 0) { 62 | console.log(`next auction starts in\t\t${timeStr}`) 63 | } else if (timeUntilStart < 0) { 64 | console.log(`auction started\t\t${timeStr}ago`) 65 | } else { 66 | console.log('auction just started') 67 | } 68 | } 69 | 70 | const { 71 | price, 72 | sellVolume, 73 | buyVolume, 74 | } = await getAuctionStatsForTokenPair({ sellToken: eth, buyToken: gno, index: latestAuctionIndex }) 75 | 76 | let timeWhenAuctionClears 77 | 78 | if (price && sellTokenOraclePrice && buyTokenOraclePrice) { 79 | const [num, den] = price 80 | 81 | const amountToClearAuction = Math.floor((sellVolume * num) / den) - buyVolume 82 | console.log(`\n currentPrice: 1 ETH = ${getNumDenStr(price)} GNO`) 83 | 84 | if (amountToClearAuction > 0) console.log(` to clear auction buy\t${amountToClearAuction} GNO`) 85 | 86 | timeWhenAuctionClears = 86400 + auctionStart 87 | 88 | if (auctionStart === 1 || auctionStart > now) { 89 | console.log(' auction haven\t started yet') 90 | } else if (now < timeWhenAuctionClears) { 91 | const timeUntilAuctionClears = getTimeStr((now - timeWhenAuctionClears) * 1000) 92 | console.log(` will clear with time in ${timeUntilAuctionClears}`) 93 | } 94 | } 95 | 96 | return { auctionStart, timeWhenAuctionClears } 97 | } 98 | 99 | const { auctionStart, timeWhenAuctionClears } = await printAuctionTimes() 100 | 101 | const incTimeBy = getSeconds(argv) 102 | 103 | console.log(`Setting time to ${argv.start ? 'AUCTION_START' : argv.clear ? 'AUCTION_END' : ''} ${incTimeBy ? `+ ${getTimeStr(incTimeBy * 1000)}` : ''}`) 104 | 105 | if (argv.start) { 106 | setTime(auctionStart, incTimeBy) 107 | } else if (argv.clear && timeWhenAuctionClears !== undefined && timeWhenAuctionClears !== Infinity) { 108 | setTime(timeWhenAuctionClears, incTimeBy) 109 | } 110 | 111 | if (incTimeBy) { 112 | increaseTimeBy(incTimeBy) 113 | } 114 | 115 | console.log('==========================') 116 | 117 | await printAuctionTimes() 118 | } 119 | -------------------------------------------------------------------------------- /src/truffle/deposit-weth.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const assert = require('assert') 5 | const contract = require('truffle-contract') 6 | 7 | const GAS = 5e5 // 500K 8 | 9 | // Usage example: 10 | // PK=PRIVATE_KEY yarn deposit-weth --amount 40 --dry-run 11 | var argv = require('yargs') 12 | .usage('Usage: yarn deposit-weth [--amount wethAmount] [--network name] [--dry-run]') 13 | .option('amount', { 14 | type: 'integer', 15 | describe: 'Amount of WETH to set as the new allowance', 16 | demandOption: true 17 | }) 18 | .option('network', { 19 | type: 'string', 20 | default: 'development', 21 | describe: 'One of the ethereum networks defined in truffle config' 22 | }) 23 | .option('dryRun', { 24 | type: 'boolean', 25 | default: false, 26 | describe: 'Dry run. Do not add the token pair, do just the validations.' 27 | }) 28 | .help('h') 29 | .strict() 30 | .argv 31 | 32 | async function depositWeth () { 33 | if (!argv._[0]) { 34 | argv.showHelp() 35 | } else { 36 | const { network, amount, dryRun } = argv 37 | console.log('\n ************** Deposit WETH **************\n') 38 | console.log(`Data: 39 | Dry run: ${dryRun ? 'Yes' : 'No'} 40 | Network: ${network} 41 | Gas: ${GAS} 42 | `) 43 | 44 | // Load the DX info 45 | const { weth, dx, account, etherBalance } = await loadContractsInfo() 46 | const wethBalance = await weth.balanceOf(account) 47 | const allowance = await weth.allowance(account, dx.address) 48 | const wethBalanceInDx = await dx.balances(weth.address, account) 49 | 50 | console.log(`\ 51 | Addresses: 52 | DutchX address: ${dx.address} 53 | WETH address: ${weth.address} 54 | Account: ${account} 55 | Balances: 56 | Balance of Ether: ${etherBalance / 1e18} 57 | Balance of WETH: ${wethBalance / 1e18} 58 | Balance of WETH in DutchX: ${wethBalanceInDx / 1e18} 59 | WETH allowance for DutchX: ${allowance / 1e18} 60 | Amount: ${amount} 61 | `) 62 | 63 | assert(amount > 0, 'amount must be grater than 0') 64 | assert(amount * 1e18 <= wethBalance, "You don't have enough WETH") 65 | assert(amount * 1e18 <= allowance, "You don't have allowance") 66 | 67 | if (dryRun) { 68 | // Dry run 69 | console.log('The dry run execution passed all validations') 70 | await dx.deposit.call(weth.address, amount * 1e18, { 71 | from: account 72 | }) 73 | console.log('Dry run success!') 74 | } else { 75 | // Real wrap WETH 76 | console.log('Setting the allowance to %s for WETH', amount) 77 | const setAllowanceResult = await dx.deposit(weth.address, amount * 1e18, { 78 | from: account 79 | }) 80 | console.log('Success! The allowance is now %s. Transaction: %s', amount, setAllowanceResult.tx) 81 | } 82 | 83 | console.log('\n ************** Deposit WETH **************\n') 84 | } 85 | } 86 | 87 | async function loadContractsInfo () { 88 | const DutchExchangeProxy = artifacts.require('DutchExchangeProxy') 89 | const DutchExchange = artifacts.require('DutchExchange') 90 | 91 | const EtherToken = contract(require('@gnosis.pm/util-contracts/build/contracts/EtherToken')) 92 | EtherToken.setProvider(web3.currentProvider) 93 | 94 | // Get contract examples 95 | const dxProxy = await DutchExchangeProxy.deployed() 96 | const dx = DutchExchange.at(dxProxy.address) 97 | const weth = await EtherToken.deployed() 98 | 99 | // get Accounts 100 | const accounts = await new Promise((resolve, reject) => { 101 | web3.eth.getAccounts((error, result) => { 102 | if (error) { 103 | reject(error) 104 | } else { 105 | resolve(result) 106 | } 107 | }) 108 | }) 109 | 110 | const account = accounts[0] 111 | const etherBalance = await new Promise((resolve, reject) => { 112 | web3.eth.getBalance(account, (error, result) => { 113 | if (error) { 114 | reject(error) 115 | } else { 116 | resolve(result) 117 | } 118 | }) 119 | }) 120 | 121 | return { 122 | weth, 123 | dx, 124 | etherBalance, 125 | account 126 | } 127 | } 128 | 129 | module.exports = callback => { 130 | depositWeth() 131 | .then(callback) 132 | .catch(callback) 133 | } 134 | -------------------------------------------------------------------------------- /src/truffle/claim-unlocked-mgn.js: -------------------------------------------------------------------------------- 1 | /* global artifacts, web3 */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const assert = require('assert') 5 | 6 | const GAS = 5e5 // 500K 7 | const DEFAULT_GAS_PRICE_GWEI = 5 // 5 GWei 8 | 9 | // Usage example: 10 | // yarn MNEMONIC="secret mnemonic" yarn claim-unlocked-mgn --network rinkeby --dry-run 11 | // yarn MNEMONIC="secret mnemonic" yarn claim-unlocked-mgn --network rinkeby 12 | var argv = require('yargs') 13 | .usage('Usage: yarn claim-unlocked-mgn [--gas-price num] [--network name] [--dry-run]') 14 | .option('gasPrice', { 15 | type: 'integer', 16 | default: process.env.GAS_PRICE_GWEI || DEFAULT_GAS_PRICE_GWEI, 17 | describe: 'Gas price for adding each token pair' 18 | }) 19 | .option('network', { 20 | type: 'string', 21 | default: 'development', 22 | describe: 'One of the ethereum networks defined in truffle config' 23 | }) 24 | .option('dryRun', { 25 | type: 'boolean', 26 | default: false, 27 | describe: 'Dry run. Do not add the token pair, do just the validations.' 28 | }) 29 | .help('h') 30 | .strict() 31 | .argv 32 | 33 | async function setAuctioneer () { 34 | if (!argv._[0]) { 35 | argv.showHelp() 36 | } else { 37 | const { gasPrice, network, dryRun, auctioneer: newAuctioneer } = argv 38 | console.log('\n ************** Claim unlocked MGN **************\n') 39 | console.log(`Data: 40 | Dry run: ${dryRun ? 'Yes' : 'No'} 41 | Network: ${network} 42 | Gas: ${GAS} 43 | Gas Price: ${gasPrice} GWei`) 44 | 45 | // Load the DX info 46 | const { mgn, dx, account } = await loadContractsInfo() 47 | const [ amountUnlocked, withdrawalTimeSeconds ] = await mgn.unlockedTokens(account) 48 | const withdrawalTime = new Date(withdrawalTimeSeconds.toNumber() * 1000) 49 | const withdrawalTimeFmt = withdrawalTime.toLocaleDateString() + ' ' + 50 | withdrawalTime.getHours() + ':' + withdrawalTime.getMinutes() 51 | 52 | console.log(`\ 53 | User account: ${account} 54 | DutchX address: ${dx.address} 55 | MGN address: ${mgn.address} 56 | 57 | Currently unlocked MGN: ${amountUnlocked.div(1e18)} 58 | Withdraw time for unlocked MGN: ${withdrawalTimeFmt} 59 | `) 60 | 61 | const now = new Date() 62 | if (amountUnlocked.isZero()) { 63 | console.log(`The user doesn't have any unlocked MGN`) 64 | } else if (withdrawalTime > now) { 65 | console.log(`The user has unlockded MGN, but is not claimable yet`) 66 | } else { 67 | // Ready to claim 68 | if (dryRun) { 69 | // Dry run 70 | console.log('The dry run execution passed all validations') 71 | await mgn.withdrawUnlockedTokens.call({ 72 | from: account 73 | }) 74 | console.log('Dry run success!') 75 | } else { 76 | // Real add token pair execution 77 | console.log('Changing auctioneer to: ' + newAuctioneer) 78 | const txResult = await mgn.withdrawUnlockedTokens({ 79 | from: account, 80 | gas: GAS, 81 | gasPrice: gasPrice * 1e9 82 | }) 83 | console.log(`Success! ${amountUnlocked.div(1e18)} has been unlocked. Transaction: ${txResult.tx}`) 84 | } 85 | } 86 | console.log('\n ************** Claim unlocked MGN **************\n') 87 | } 88 | } 89 | 90 | async function loadContractsInfo () { 91 | const DutchExchangeProxy = artifacts.require('DutchExchangeProxy') 92 | const DutchExchange = artifacts.require('DutchExchange') 93 | const TokenFRTProxy = artifacts.require('TokenFRTProxy') 94 | const TokenFRT = artifacts.require('TokenFRT') 95 | 96 | // Get contract examples 97 | const dxProxy = await DutchExchangeProxy.deployed() 98 | const dx = DutchExchange.at(dxProxy.address) 99 | const mgnProxy = await TokenFRTProxy.deployed() 100 | const mgn = TokenFRT.at(mgnProxy.address) 101 | 102 | // get Accounts 103 | const accounts = await new Promise((resolve, reject) => { 104 | web3.eth.getAccounts((error, result) => { 105 | if (error) { 106 | reject(error) 107 | } else { 108 | resolve(result) 109 | } 110 | }) 111 | }) 112 | 113 | return { 114 | mgn, 115 | dx, 116 | account: accounts[0] 117 | } 118 | } 119 | 120 | module.exports = callback => { 121 | setAuctioneer() 122 | .then(callback) 123 | .catch(callback) 124 | } 125 | -------------------------------------------------------------------------------- /test/dutchExchange-priceOracle.spec.js: -------------------------------------------------------------------------------- 1 | /* global contract, assert, artifacts */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const { 5 | assertRejects, 6 | gasLogger, 7 | enableContractFlag, 8 | toEth 9 | } = require('./utils') 10 | 11 | const { 12 | getContracts, 13 | setupTest, 14 | wait 15 | } = require('./testFunctions') 16 | 17 | const Medianizer = artifacts.require('Medianizer') 18 | const PriceFeed = artifacts.require('PriceFeed') 19 | const PriceOracleInterface = artifacts.require('PriceOracleInterface') 20 | 21 | // Test VARS 22 | let oracle 23 | let priceFeed 24 | let dx 25 | let medzr2 26 | let contracts 27 | let newPriceOracleInterface 28 | 29 | const setupContracts = async () => { 30 | contracts = await getContracts({ resetCache: true }); 31 | // destructure contracts into upper state 32 | ({ 33 | PriceOracleInterface: oracle, 34 | PriceFeed: priceFeed, 35 | DutchExchange: dx 36 | } = contracts) 37 | } 38 | 39 | const c1 = () => contract('DX PriceOracleInterface Flow', accounts => { 40 | const [owner, notOwner, newCurator] = accounts 41 | // Accounts to fund for faster setupTest 42 | const setupAccounts = [owner, notOwner, newCurator] 43 | 44 | const startBal = { 45 | startingETH: 1000.0.toWei(), 46 | startingGNO: 1000.0.toWei(), 47 | ethUSDPrice: 1100.0.toWei(), // 400 ETH @ $6000/ETH = $2,400,000 USD 48 | sellingAmount: 100.0.toWei() // Same as web3.toWei(50, 'ether') - $60,000USD 49 | } 50 | 51 | afterEach(gasLogger) 52 | 53 | it('SETUP: fund accounts, fund DX', async () => { 54 | // get contracts 55 | await setupContracts() 56 | contracts.medzr2 = await Medianizer.new() 57 | contracts.priceFeed2 = await PriceFeed.new(); 58 | ({ medzr2 } = contracts) 59 | 60 | // set up accounts and tokens[contracts] 61 | await setupTest(setupAccounts, contracts, startBal) 62 | }) 63 | 64 | it('raiseEmergency: throws when NON-OWNER tries to call it', async () => 65 | assertRejects(oracle.raiseEmergency(true, { from: notOwner })) 66 | ) 67 | 68 | it('raiseEmergency: switches into emergency mode', async () => { 69 | await oracle.raiseEmergency(true, { from: owner }) 70 | 71 | let ethUSDPrice = (await oracle.getUSDETHPrice.call()).toNumber() 72 | assert.equal(ethUSDPrice, 600, 'Oracle ethUSDPrice should report emergency price') 73 | await oracle.raiseEmergency(false, { from: owner }) 74 | 75 | ethUSDPrice = (await oracle.getUSDETHPrice.call()).toNumber() 76 | assert.equal(ethUSDPrice, 1100, 'Oracle ethUSDPrice should on longer report emergency price') 77 | }) 78 | 79 | it('getUSDETHPrice: calls this correctly', async () => { 80 | const ethUSDPrice = (await oracle.getUSDETHPrice.call()).toNumber() 81 | assert.equal(ethUSDPrice, 1100, 'Oracle ethUSDPrice is not the set price ethUSDPrice: 1100..toWei(),') 82 | }) 83 | 84 | it('getUSDETHPrice: price is correctly restricted if actual price is 0', async () => { 85 | newPriceOracleInterface = await PriceOracleInterface.new(owner, medzr2.address) 86 | await dx.initiateEthUsdOracleUpdate(newPriceOracleInterface.address, { from: owner }) 87 | await assertRejects(dx.updateEthUSDOracle({ from: owner })) 88 | await wait(60 * 60 * 24 * 30 + 5) 89 | await dx.updateEthUSDOracle({ from: owner }) 90 | const ethUSDPrice = (await newPriceOracleInterface.getUSDETHPrice.call()).toNumber() 91 | assert.equal(ethUSDPrice, 1, 'Oracle ethUSDPrice is not set and should return 1') 92 | }) 93 | it('getUSDETHPrice: set price should work correctly', async () => { 94 | const ethUSDPrice = 1500.0.toWei() 95 | const medzr = await Medianizer.at(medzr2.address) 96 | await medzr.set(PriceFeed.address, { from: owner }) 97 | await priceFeed.post(ethUSDPrice, 1516168838 * 2, medzr2.address, { from: owner }) 98 | const getNewETHUSDPrice = await newPriceOracleInterface.getUSDETHPrice.call() 99 | 100 | assert.equal(toEth(ethUSDPrice).toString(), getNewETHUSDPrice.toString(), 'Should be same') 101 | }) 102 | 103 | it('updateCurator: throws when NON-OWNER tries to change curator', 104 | async () => assertRejects(oracle.updateCurator(medzr2, { from: notOwner })) 105 | ) 106 | 107 | it('updateCurator: switches OWNER to new OWNER', async () => { 108 | const oldOwner = await oracle.owner.call() 109 | await oracle.updateCurator(newCurator, { from: owner }) 110 | const newOwner = await oracle.owner.call() 111 | 112 | assert.notEqual(oldOwner, newOwner, 'Old Owner should NOT == New Owner') 113 | assert.equal(newCurator, newOwner, 'New Curator passed in is indeed newOwner') 114 | }) 115 | }) 116 | 117 | enableContractFlag(c1) 118 | -------------------------------------------------------------------------------- /test/dutchExchange-getCurrentAuctionPrice.spec.js: -------------------------------------------------------------------------------- 1 | /* global contract, assert */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const { 5 | BN, 6 | eventWatcher, 7 | log, 8 | timestamp 9 | } = require('./utils') 10 | 11 | const { 12 | setupTest, 13 | getContracts, 14 | getAuctionIndex, 15 | waitUntilPriceIsXPercentOfPreviousPrice, 16 | setAndCheckAuctionStarted, 17 | postBuyOrder 18 | } = require('./testFunctions') 19 | 20 | // Test VARS 21 | let eth 22 | let gno 23 | let dx 24 | 25 | let contracts 26 | 27 | const setupContracts = async () => { 28 | contracts = await getContracts({ resetCache: true }); 29 | // destructure contracts into upper state 30 | ({ 31 | DutchExchange: dx, 32 | EtherToken: eth, 33 | TokenGNO: gno 34 | } = contracts) 35 | } 36 | const startBal = { 37 | startingETH: 90.0.toWei(), 38 | startingGNO: 90.0.toWei(), 39 | ethUSDPrice: 1008.0.toWei(), 40 | sellingAmount: 50.0.toWei() // Same as web3.toWei(50, 'ether') 41 | } 42 | 43 | contract('DutchExchange - getCurrentAuctionPrice', accounts => { 44 | const [master, seller1, , buyer1, buyer2] = accounts 45 | // Accounts to fund for faster setupTest 46 | const setupAccounts = [master, seller1, buyer1, buyer2] 47 | 48 | before(async () => { 49 | // get contracts 50 | await setupContracts() 51 | 52 | // set up accounts and tokens[contracts] 53 | await setupTest(setupAccounts, contracts, startBal) 54 | 55 | // add tokenPair ETH GNO 56 | await dx.addTokenPair( 57 | eth.address, 58 | gno.address, 59 | 10.0.toWei(), 60 | 0, 61 | 2, 62 | 1, 63 | { from: seller1 } 64 | ) 65 | 66 | eventWatcher(dx, 'Log', {}) 67 | }) 68 | 69 | after(eventWatcher.stopWatching) 70 | 71 | it('1. check that getCurrentAuctionPrice returns the right value according to time for a normal running auction', async () => { 72 | const auctionIndex = await getAuctionIndex() 73 | await setAndCheckAuctionStarted(eth, gno) 74 | const auctionStart = (await dx.getAuctionStart.call(eth.address, gno.address)).toNumber() 75 | await waitUntilPriceIsXPercentOfPreviousPrice(eth, gno, 1.5) 76 | 77 | const { num, den } = await dx.getCurrentAuctionPrice.call(eth.address, gno.address, auctionIndex) 78 | const currenttime = await timestamp() 79 | const { num: numPrevious, den: denPrevious } = await dx.getPriceInPastAuction.call(eth.address, gno.address, auctionIndex - 1) 80 | const timeElapsed = currenttime - auctionStart 81 | log('numPrevious', numPrevious) 82 | log('timeE', timeElapsed) 83 | assert.equal(num.toString(), (new BN((86400 - timeElapsed).toString())).mul(numPrevious).toString()) 84 | assert.equal(den.toString(), (new BN((timeElapsed + 43200).toString())).mul(denPrevious).toString()) 85 | }) 86 | 87 | it('2. check that getCurrentAuctionPrice returns the right value (closing Price ) for a theoretical closed auction', async () => { 88 | const auctionIndex = await getAuctionIndex() 89 | 90 | await postBuyOrder(eth, gno, auctionIndex, 5.0.toWei(), buyer1) 91 | await postBuyOrder(eth, gno, auctionIndex, 5.0.toWei(), buyer2) 92 | // closing theoretical 93 | await waitUntilPriceIsXPercentOfPreviousPrice(eth, gno, 0.4) 94 | 95 | // check prices: - actually reduantant with tests postBuyOrder 96 | const closingPriceNum = await dx.buyVolumes.call(eth.address, gno.address) 97 | const closingPriceDen = await dx.sellVolumesCurrent.call(eth.address, gno.address) 98 | const { num, den } = await dx.getCurrentAuctionPrice.call(eth.address, gno.address, auctionIndex) 99 | assert.equal(closingPriceNum.toString(), num.toString()) 100 | assert.equal(closingPriceDen.toString(), den.toString()) 101 | }) 102 | 103 | it('3. check that getCurrentAuctionPrice returns the (0,0) for future auctions', async () => { 104 | const auctionIndex = await getAuctionIndex() 105 | const { num, den } = await dx.getCurrentAuctionPrice.call(eth.address, gno.address, auctionIndex + 1) 106 | assert.equal(0, num) 107 | assert.equal(0, den) 108 | }) 109 | 110 | it('4. check that getCurrentAuctionPrice returns the right value (closing Price ) for a closed auction', async () => { 111 | const auctionIndex = await getAuctionIndex() 112 | 113 | // clearning the auction 114 | await postBuyOrder(eth, gno, auctionIndex, 5.0.toWei(), buyer2) 115 | const { num: closingPriceNum, den: closingPriceDen } = await dx.closingPrices.call(eth.address, gno.address, auctionIndex) 116 | const { num, den } = await dx.getCurrentAuctionPrice.call(eth.address, gno.address, auctionIndex) 117 | assert.equal(closingPriceNum.toString(), num.toString()) 118 | assert.equal(closingPriceDen.toString(), den.toString()) 119 | }) 120 | }) 121 | -------------------------------------------------------------------------------- /contracts/Oracle/DSMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | 4 | contract DSMath { 5 | /* 6 | standard uint256 functions 7 | */ 8 | 9 | function add(uint256 x, uint256 y) internal pure returns (uint256 z) { 10 | assert((z = x + y) >= x); 11 | } 12 | 13 | function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { 14 | assert((z = x - y) <= x); 15 | } 16 | 17 | function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { 18 | assert((z = x * y) >= x); 19 | } 20 | 21 | function div(uint256 x, uint256 y) internal pure returns (uint256 z) { 22 | z = x / y; 23 | } 24 | 25 | function min(uint256 x, uint256 y) internal pure returns (uint256 z) { 26 | return x <= y ? x : y; 27 | } 28 | 29 | function max(uint256 x, uint256 y) internal pure returns (uint256 z) { 30 | return x >= y ? x : y; 31 | } 32 | 33 | /* 34 | uint128 functions (h is for half) 35 | */ 36 | 37 | function hadd(uint128 x, uint128 y) internal pure returns (uint128 z) { 38 | assert((z = x + y) >= x); 39 | } 40 | 41 | function hsub(uint128 x, uint128 y) internal pure returns (uint128 z) { 42 | assert((z = x - y) <= x); 43 | } 44 | 45 | function hmul(uint128 x, uint128 y) internal pure returns (uint128 z) { 46 | assert((z = x * y) >= x); 47 | } 48 | 49 | function hdiv(uint128 x, uint128 y) internal pure returns (uint128 z) { 50 | z = x / y; 51 | } 52 | 53 | function hmin(uint128 x, uint128 y) internal pure returns (uint128 z) { 54 | return x <= y ? x : y; 55 | } 56 | 57 | function hmax(uint128 x, uint128 y) internal pure returns (uint128 z) { 58 | return x >= y ? x : y; 59 | } 60 | 61 | /* 62 | int256 functions 63 | */ 64 | 65 | function imin(int256 x, int256 y) internal pure returns (int256 z) { 66 | return x <= y ? x : y; 67 | } 68 | 69 | function imax(int256 x, int256 y) internal pure returns (int256 z) { 70 | return x >= y ? x : y; 71 | } 72 | 73 | /* 74 | WAD math 75 | */ 76 | 77 | uint128 constant WAD = 10 ** 18; 78 | 79 | function wadd(uint128 x, uint128 y) internal pure returns (uint128) { 80 | return hadd(x, y); 81 | } 82 | 83 | function wsub(uint128 x, uint128 y) internal pure returns (uint128) { 84 | return hsub(x, y); 85 | } 86 | 87 | function wmul(uint128 x, uint128 y) internal pure returns (uint128 z) { 88 | z = cast((uint256(x) * y + WAD / 2) / WAD); 89 | } 90 | 91 | function wdiv(uint128 x, uint128 y) internal pure returns (uint128 z) { 92 | z = cast((uint256(x) * WAD + y / 2) / y); 93 | } 94 | 95 | function wmin(uint128 x, uint128 y) internal pure returns (uint128) { 96 | return hmin(x, y); 97 | } 98 | 99 | function wmax(uint128 x, uint128 y) internal pure returns (uint128) { 100 | return hmax(x, y); 101 | } 102 | 103 | /* 104 | RAY math 105 | */ 106 | 107 | uint128 constant RAY = 10 ** 27; 108 | 109 | function radd(uint128 x, uint128 y) internal pure returns (uint128) { 110 | return hadd(x, y); 111 | } 112 | 113 | function rsub(uint128 x, uint128 y) internal pure returns (uint128) { 114 | return hsub(x, y); 115 | } 116 | 117 | function rmul(uint128 x, uint128 y) internal pure returns (uint128 z) { 118 | z = cast((uint256(x) * y + RAY / 2) / RAY); 119 | } 120 | 121 | function rdiv(uint128 x, uint128 y) internal pure returns (uint128 z) { 122 | z = cast((uint256(x) * RAY + y / 2) / y); 123 | } 124 | 125 | function rpow(uint128 x, uint64 n) internal pure returns (uint128 z) { 126 | // This famous algorithm is called "exponentiation by squaring" 127 | // and calculates x^n with x as fixed-point and n as regular unsigned. 128 | // 129 | // It's O(log n), instead of O(n) for naive repeated multiplication. 130 | // 131 | // These facts are why it works: 132 | // 133 | // If n is even, then x^n = (x^2)^(n/2). 134 | // If n is odd, then x^n = x * x^(n-1), 135 | // and applying the equation for even x gives 136 | // x^n = x * (x^2)^((n-1) / 2). 137 | // 138 | // Also, EVM division is flooring and 139 | // floor[(n-1) / 2] = floor[n / 2]. 140 | 141 | z = n % 2 != 0 ? x : RAY; 142 | 143 | for (n /= 2; n != 0; n /= 2) { 144 | x = rmul(x, x); 145 | 146 | if (n % 2 != 0) { 147 | z = rmul(z, x); 148 | } 149 | } 150 | } 151 | 152 | function rmin(uint128 x, uint128 y) internal pure returns (uint128) { 153 | return hmin(x, y); 154 | } 155 | 156 | function rmax(uint128 x, uint128 y) internal pure returns (uint128) { 157 | return hmax(x, y); 158 | } 159 | 160 | function cast(uint256 x) internal pure returns (uint128 z) { 161 | assert((z = uint128(x)) == x); 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /test/dutchExchange-TokenApproval.spec.js: -------------------------------------------------------------------------------- 1 | /* global contract, assert */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const { 5 | logger, 6 | assertRejects, 7 | gasLogger 8 | } = require('./utils') 9 | 10 | const { getContracts } = require('./testFunctions') 11 | 12 | // Test VARS 13 | let eth 14 | let gno 15 | let dx 16 | 17 | let contracts 18 | 19 | contract('DutchExchange updating token approval', accounts => { 20 | const [master, seller1] = accounts 21 | let testingTokens 22 | 23 | afterEach(gasLogger) 24 | before(async () => { 25 | // get contracts 26 | contracts = await getContracts({ resetCache: true }); 27 | // destructure contracts into upper state 28 | ({ 29 | DutchExchange: dx, 30 | EtherToken: eth, 31 | TokenGNO: gno 32 | } = contracts) 33 | 34 | testingTokens = [eth, gno] 35 | }) 36 | 37 | const getTokenApproval = token => { 38 | const addr = token.address || token 39 | 40 | return dx.approvedTokens.call(addr) 41 | } 42 | 43 | const getAndPrintApproval = async (token, symbol) => { 44 | const approved = await getTokenApproval(token) 45 | logger(`Token ${symbol} at ${token.address} is ${approved ? '' : 'NOT'} APPROVED`) 46 | 47 | return approved 48 | } 49 | 50 | const assertIsOwner = async acc => { 51 | const owner = await dx.auctioneer.call() 52 | assert.strictEqual(owner, acc, 'account should be DutchExchange contract owner') 53 | } 54 | 55 | const assertIsNotOwner = async acc => { 56 | const owner = await dx.auctioneer.call() 57 | assert.notStrictEqual(owner, acc, 'account should not be DutchExchange contract owner') 58 | } 59 | 60 | it('intially tokens aren\'t approved', () => Promise.all(testingTokens.map(async token => { 61 | const symbol = await token.symbol.call() 62 | const approved = await getAndPrintApproval(token, symbol) 63 | 64 | assert.isFalse(approved, `${symbol} token shouldn't be approved yet`) 65 | }))) 66 | 67 | it('not owner can\'t set token approval', () => Promise.all(testingTokens.map(async token => { 68 | const symbol = await token.symbol.call() 69 | const approved1 = await getAndPrintApproval(token, symbol) 70 | assert.isFalse(approved1, `${symbol} token is not approved`) 71 | 72 | await assertIsNotOwner(seller1) 73 | 74 | logger(`Not owner tries to change ${symbol} approval to ${!approved1}`) 75 | 76 | await assertRejects(dx.updateApprovalOfToken([token.address], !approved1, { from: seller1 }), `not owner can't set ${symbol} token approval`) 77 | 78 | const approved2 = await getAndPrintApproval(token, symbol) 79 | 80 | assert.strictEqual(approved1, approved2, ` ${symbol} token should not change approval`) 81 | assert.isFalse(approved2, `${symbol} token shouldn't be approved yet`) 82 | }))) 83 | 84 | it('owner can set token approval', () => Promise.all(testingTokens.map(async token => { 85 | const symbol = await token.symbol.call() 86 | const approved1 = await getAndPrintApproval(token, symbol) 87 | assert.isFalse(approved1, `${symbol} token is not approved`) 88 | 89 | await assertIsOwner(master) 90 | 91 | logger(`Owner changes ${symbol} approval to ${!approved1}`) 92 | 93 | await dx.updateApprovalOfToken([token.address], !approved1, { from: master }) 94 | 95 | const approved2 = await getAndPrintApproval(token, symbol) 96 | 97 | assert.strictEqual(!approved1, approved2, ` ${symbol} token should change approval`) 98 | assert.isTrue(approved2, `${symbol} token should be approved`) 99 | }))) 100 | 101 | it('not owner can\'t remove token approval', () => Promise.all(testingTokens.map(async token => { 102 | const symbol = await token.symbol.call() 103 | const approved1 = await getAndPrintApproval(token, symbol) 104 | assert.isTrue(approved1, `${symbol} token is approved`) 105 | 106 | await assertIsNotOwner(seller1) 107 | 108 | logger(`Not owner tries to change ${symbol} approval to ${!approved1}`) 109 | 110 | await assertRejects(dx.updateApprovalOfToken([token.address], !approved1, { from: seller1 }), `not owner can't remove ${symbol} token approval`) 111 | 112 | const approved2 = await getAndPrintApproval(token, symbol) 113 | 114 | assert.strictEqual(approved1, approved2, ` ${symbol} token should not change approval`) 115 | assert.isTrue(approved2, `${symbol} token should still be approved`) 116 | }))) 117 | 118 | it('owner can remove token approval', () => Promise.all(testingTokens.map(async token => { 119 | const symbol = await token.symbol.call() 120 | const approved1 = await getAndPrintApproval(token, symbol) 121 | assert.isTrue(approved1, `${symbol} token is approved`) 122 | 123 | await assertIsOwner(master) 124 | 125 | logger(`Owner changes ${symbol} approval to ${!approved1}`) 126 | 127 | await dx.updateApprovalOfToken([token.address], !approved1, { from: master }) 128 | 129 | const approved2 = await getAndPrintApproval(token, symbol) 130 | 131 | assert.strictEqual(!approved1, approved2, ` ${symbol} token should change approval`) 132 | assert.isFalse(approved2, `${symbol} token should be unapproved`) 133 | }))) 134 | }) 135 | -------------------------------------------------------------------------------- /test/dutchExchange-UpdateExchangeParams.spec.js: -------------------------------------------------------------------------------- 1 | /* global contract, assert, artifacts */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const { 5 | logger, 6 | assertRejects, 7 | gasLogger 8 | } = require('./utils') 9 | 10 | const { 11 | getContracts, 12 | wait 13 | } = require('./testFunctions') 14 | 15 | const PriceOracleInterface = artifacts.require('PriceOracleInterface') 16 | 17 | // Test VARS 18 | let newPO 19 | let dx 20 | let medianizer 21 | let params2 22 | let contracts 23 | 24 | contract('DutchExchange updating exchange params', accounts => { 25 | const [master, seller1] = accounts 26 | 27 | afterEach(gasLogger) 28 | 29 | before(async () => { 30 | // get contractsU 31 | contracts = await getContracts({ resetCache: true }); 32 | // destructure contracts into upper state 33 | ({ 34 | DutchExchange: dx, 35 | Medianizer: medianizer 36 | } = contracts) 37 | 38 | // a new deployed PriceOracleInterface to replace the old with 39 | contracts.newPO = await PriceOracleInterface.new(master, medianizer.address); 40 | ({ newPO } = contracts) 41 | 42 | params2 = { 43 | auctioneer: seller1, 44 | ethUSDOracle: newPO.address, 45 | thresholdNewTokenPair: '5000', 46 | thresholdNewAuction: '500' 47 | } 48 | }) 49 | 50 | const getExchangeParams = async () => { 51 | const [auctioneer, ethUSDOracle, thresholdNewTokenPair, thresholdNewAuction] = await Promise.all([ 52 | dx.auctioneer.call(), 53 | dx.ethUSDOracle.call(), 54 | dx.thresholdNewTokenPair.call(), 55 | dx.thresholdNewAuction.call() 56 | ]) 57 | 58 | return { 59 | auctioneer, 60 | ethUSDOracle, 61 | thresholdNewTokenPair: thresholdNewTokenPair.toString(), 62 | thresholdNewAuction: thresholdNewAuction.toString() 63 | } 64 | } 65 | 66 | const getAndPrintExchangeParams = async () => { 67 | const params = await getExchangeParams() 68 | const { 69 | auctioneer, 70 | ethUSDOracle, 71 | thresholdNewTokenPair, 72 | thresholdNewAuction 73 | } = params 74 | 75 | logger(`DutchExchange parameters: 76 | auctioneer: ${auctioneer}, 77 | ethUSDOracle: ${ethUSDOracle}, 78 | thresholdNewTokenPair: ${thresholdNewTokenPair}, 79 | thresholdNewAuction: ${thresholdNewAuction} 80 | `) 81 | 82 | return params 83 | } 84 | 85 | const assertIsAuctioneer = async acc => { 86 | const auctioneer = await dx.auctioneer.call() 87 | assert.strictEqual(auctioneer, acc, 'account should be DutchExchange contract auctioneer') 88 | } 89 | 90 | const assertIsNotAuctioneer = async acc => { 91 | const auctioneer = await dx.auctioneer.call() 92 | assert.notStrictEqual(auctioneer, acc, 'account should not be DutchExchange contract auctioneer') 93 | } 94 | 95 | it('not auctioneer can\'t change params', async () => { 96 | const params1 = await getAndPrintExchangeParams() 97 | 98 | await assertIsNotAuctioneer(seller1) 99 | assert.notDeepEqual(params1, params2, 'parameters must be different') 100 | 101 | logger(`Not auctioneer tries to change params to ${JSON.stringify(params2, null, 5)}`) 102 | await assertRejects(dx.updateAuctioneer(params2.auctioneer, { from: seller1 }), 'not auctioneer can\'t change params') 103 | await assertRejects(dx.initiateEthUsdOracleUpdate({ from: seller1 }), 'not auctioneer can\'t change params') 104 | await assertRejects(dx.updateEthUSDOracle(params2.ethUSDOracle, { from: seller1 }), 'not auctioneer can\'t change params') 105 | await assertRejects(dx.updateThresholdNewTokenPair(params2.thresholdNewTokenPair, { from: seller1 }), 'not auctioneer can\'t change params') 106 | await assertRejects(dx.updateThresholdNewAuction(params2.thresholdNewAuction, { from: seller1 }), 'not auctioneer can\'t change params') 107 | 108 | assert.deepEqual(params1, await getAndPrintExchangeParams(), 'exchange params should stay the same') 109 | }) 110 | it('price oracle can not be changed immediately params', async () => { 111 | const params1 = await getAndPrintExchangeParams() 112 | 113 | await assertIsAuctioneer(master) 114 | logger(`initiating oracle address update to ${params2.ethUSDOracle}`) 115 | await dx.initiateEthUsdOracleUpdate(params2.ethUSDOracle, { from: master }) 116 | await assertRejects(dx.updateEthUSDOracle({ from: master }), 'to early to change oracle interface') 117 | }) 118 | it('auctioneer can change params', async () => { 119 | const params1 = await getAndPrintExchangeParams() 120 | 121 | await assertIsAuctioneer(master) 122 | 123 | assert.notDeepEqual(params1, params2, 'parameters must be different') 124 | await wait(60 * 60 * 24 * 30 + 5) 125 | logger(`auctioneer changes params to ${JSON.stringify(params2, null, 5)}`) 126 | await dx.updateEthUSDOracle({ from: master }) 127 | await dx.updateThresholdNewTokenPair(params2.thresholdNewTokenPair, { from: master }) 128 | await dx.updateThresholdNewAuction(params2.thresholdNewAuction, { from: master }) 129 | await dx.updateAuctioneer(params2.auctioneer, { from: master }) 130 | 131 | assert.deepEqual(params2, await getAndPrintExchangeParams(), 'exchange params should be changed') 132 | }) 133 | }) 134 | -------------------------------------------------------------------------------- /contracts/TokenFRT.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | import "@gnosis.pm/util-contracts/contracts/Proxy.sol"; 4 | import "@gnosis.pm/util-contracts/contracts/GnosisStandardToken.sol"; 5 | 6 | 7 | /// @title Standard token contract with overflow protection 8 | contract TokenFRT is Proxied, GnosisStandardToken { 9 | address public owner; 10 | 11 | string public constant symbol = "MGN"; 12 | string public constant name = "Magnolia Token"; 13 | uint8 public constant decimals = 18; 14 | 15 | struct UnlockedToken { 16 | uint amountUnlocked; 17 | uint withdrawalTime; 18 | } 19 | 20 | /* 21 | * Storage 22 | */ 23 | address public minter; 24 | 25 | // user => UnlockedToken 26 | mapping(address => UnlockedToken) public unlockedTokens; 27 | 28 | // user => amount 29 | mapping(address => uint) public lockedTokenBalances; 30 | 31 | /* 32 | * Public functions 33 | */ 34 | 35 | // @dev allows to set the minter of Magnolia tokens once. 36 | // @param _minter the minter of the Magnolia tokens, should be the DX-proxy 37 | function updateMinter(address _minter) public { 38 | require(msg.sender == owner, "Only the minter can set a new one"); 39 | require(_minter != address(0), "The new minter must be a valid address"); 40 | 41 | minter = _minter; 42 | } 43 | 44 | // @dev the intention is to set the owner as the DX-proxy, once it is deployed 45 | // Then only an update of the DX-proxy contract after a 30 days delay could change the minter again. 46 | function updateOwner(address _owner) public { 47 | require(msg.sender == owner, "Only the owner can update the owner"); 48 | require(_owner != address(0), "The new owner must be a valid address"); 49 | owner = _owner; 50 | } 51 | 52 | function mintTokens(address user, uint amount) public { 53 | require(msg.sender == minter, "Only the minter can mint tokens"); 54 | 55 | lockedTokenBalances[user] = add(lockedTokenBalances[user], amount); 56 | totalTokens = add(totalTokens, amount); 57 | } 58 | 59 | /// @dev Lock Token 60 | function lockTokens(uint amount) public returns (uint totalAmountLocked) { 61 | // Adjust amount by balance 62 | uint actualAmount = min(amount, balances[msg.sender]); 63 | 64 | // Update state variables 65 | balances[msg.sender] = sub(balances[msg.sender], actualAmount); 66 | lockedTokenBalances[msg.sender] = add(lockedTokenBalances[msg.sender], actualAmount); 67 | 68 | // Get return variable 69 | totalAmountLocked = lockedTokenBalances[msg.sender]; 70 | } 71 | 72 | function unlockTokens() public returns (uint totalAmountUnlocked, uint withdrawalTime) { 73 | // Adjust amount by locked balances 74 | uint amount = lockedTokenBalances[msg.sender]; 75 | 76 | if (amount > 0) { 77 | // Update state variables 78 | lockedTokenBalances[msg.sender] = sub(lockedTokenBalances[msg.sender], amount); 79 | unlockedTokens[msg.sender].amountUnlocked = add(unlockedTokens[msg.sender].amountUnlocked, amount); 80 | unlockedTokens[msg.sender].withdrawalTime = now + 24 hours; 81 | } 82 | 83 | // Get return variables 84 | totalAmountUnlocked = unlockedTokens[msg.sender].amountUnlocked; 85 | withdrawalTime = unlockedTokens[msg.sender].withdrawalTime; 86 | } 87 | 88 | function withdrawUnlockedTokens() public { 89 | require(unlockedTokens[msg.sender].withdrawalTime < now, "The tokens cannot be withdrawn yet"); 90 | balances[msg.sender] = add(balances[msg.sender], unlockedTokens[msg.sender].amountUnlocked); 91 | unlockedTokens[msg.sender].amountUnlocked = 0; 92 | } 93 | 94 | function min(uint a, uint b) public pure returns (uint) { 95 | if (a < b) { 96 | return a; 97 | } else { 98 | return b; 99 | } 100 | } 101 | 102 | /// @dev Returns whether an add operation causes an overflow 103 | /// @param a First addend 104 | /// @param b Second addend 105 | /// @return Did no overflow occur? 106 | function safeToAdd(uint a, uint b) public pure returns (bool) { 107 | return a + b >= a; 108 | } 109 | 110 | /// @dev Returns whether a subtraction operation causes an underflow 111 | /// @param a Minuend 112 | /// @param b Subtrahend 113 | /// @return Did no underflow occur? 114 | function safeToSub(uint a, uint b) public pure returns (bool) { 115 | return a >= b; 116 | } 117 | 118 | /// @dev Returns sum if no overflow occurred 119 | /// @param a First addend 120 | /// @param b Second addend 121 | /// @return Sum 122 | function add(uint a, uint b) public pure returns (uint) { 123 | require(safeToAdd(a, b), "It must be a safe adition"); 124 | return a + b; 125 | } 126 | 127 | /// @dev Returns difference if no overflow occurred 128 | /// @param a Minuend 129 | /// @param b Subtrahend 130 | /// @return Difference 131 | function sub(uint a, uint b) public pure returns (uint) { 132 | require(safeToSub(a, b), "It must be a safe substraction"); 133 | return a - b; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /test/dutchExchange-Proxy.spec.js: -------------------------------------------------------------------------------- 1 | /* global contract, assert, artifacts */ 2 | /* eslint no-undef: "error" */ 3 | 4 | const { 5 | log: utilsLog, 6 | assertRejects, 7 | gasLogger 8 | } = require('./utils') 9 | 10 | const { getContracts, wait } = require('./testFunctions') 11 | 12 | const InternalTests = artifacts.require('InternalTests') 13 | 14 | // Test VARS 15 | let dx, dxNew, ethToken 16 | let pr 17 | 18 | let contracts 19 | 20 | const separateLogs = () => utilsLog('\n ----------------------------------') 21 | const log = (...args) => utilsLog('\t', ...args) 22 | 23 | contract('DutchExchange - Proxy', accounts => { 24 | const [master, seller1] = accounts 25 | 26 | beforeEach(separateLogs) 27 | afterEach(gasLogger) 28 | 29 | before(async () => { 30 | // get contractsU 31 | contracts = await getContracts({ resetCache: true }); 32 | // destructure contracts into upper state 33 | ({ 34 | DutchExchange: dx, 35 | EtherToken: ethToken, 36 | // dxNew has new code as it is an InternalTests contract 37 | DutchExchangeProxy: pr 38 | } = contracts) 39 | const initParams = await getExchangeParams(dx) 40 | dxNew = await InternalTests.new(...initParams) 41 | }) 42 | 43 | const getExchangeParams = async (dxContr = dx) => { 44 | const [frtToken, 45 | owlToken, 46 | auctioneer, 47 | eth, 48 | ethUSDOracle, 49 | thresholdNewTokenPair, 50 | thresholdNewAuction] = await Promise.all([ 51 | dxContr.frtToken.call(), 52 | dxContr.owlToken.call(), 53 | dxContr.auctioneer.call(), 54 | dxContr.ethToken.call(), 55 | dxContr.ethUSDOracle.call(), 56 | dxContr.thresholdNewTokenPair.call(), 57 | dxContr.thresholdNewAuction.call() 58 | ]) 59 | 60 | return [ 61 | frtToken, 62 | owlToken, 63 | auctioneer, 64 | eth, 65 | ethUSDOracle, 66 | thresholdNewTokenPair, 67 | thresholdNewAuction 68 | ] 69 | } 70 | 71 | const assertIsAuctioneer = async acc => { 72 | const auctioneer = await dx.auctioneer.call() 73 | assert.strictEqual(auctioneer, acc, 'account should be DutchExchange contract auctioneer') 74 | } 75 | 76 | const assertIsNotAuctioneer = async acc => { 77 | const auctioneer = await dx.auctioneer.call() 78 | assert.notStrictEqual(auctioneer, acc, 'account should not be DutchExchange contract auctioneer') 79 | } 80 | 81 | it('DutchExchange is initialized and params are set', async () => { 82 | const ethTokenAddress = await dx.ethToken.call() 83 | assert.strictEqual(ethTokenAddress, ethToken.address, 'DutchExchange should be initialized') 84 | 85 | const params = await getExchangeParams() 86 | assert.isTrue(Object.values(params).every(param => !!+param), 'No zero-initialized parameters') 87 | }) 88 | 89 | it('masterCopy can\'t be updated before masterCopyCountdown was started', async () => { 90 | await assertIsAuctioneer(master) 91 | log('calling dx.updateMasterCopy() as auctioneer') 92 | await assertRejects(dx.updateMasterCopy({ from: master }), 'should reject as startMasterCopyCountdown wasn\'t yet called') 93 | log('tx was rejected') 94 | }) 95 | 96 | it('not auctioneer can\'t call startMasterCopyCountdown', async () => { 97 | await assertIsNotAuctioneer(seller1) 98 | log('calling dx.startMasterCopyCountdown() as not auctioneer') 99 | await assertRejects(dx.startMasterCopyCountdown(dxNew.address, { from: seller1 }), 'should reject as caller isn\'t the auctioneer') 100 | log('tx was rejected') 101 | }) 102 | 103 | it('can\'t call startMasterCopyCountdown with zero dx address', async () => { 104 | await assertIsAuctioneer(master) 105 | log('calling dx.startMasterCopyCountdown() with dx address == 0') 106 | await assertRejects(dx.startMasterCopyCountdown(0, { from: master }), 'should reject as caller isn\'t the auctioneer') 107 | log('tx was rejected') 108 | }) 109 | 110 | it('auctioneer can call startMasterCopyCountdown', async () => { 111 | await assertIsAuctioneer(master) 112 | log('calling dx.startMasterCopyCountdown() as auctioneer with valid dx address') 113 | await dx.startMasterCopyCountdown(dxNew.address, { from: master }) 114 | }) 115 | 116 | it('auctioneer can\'t update masterCopy before time limit', async () => { 117 | await assertIsAuctioneer(master) 118 | log('calling dx.updateMasterCopy() as auctioneer before time limit') 119 | await assertRejects(dx.updateMasterCopy({ from: master }), 'should reject as time hasn\t passed') 120 | log('tx was rejected') 121 | }) 122 | 123 | it('any user can update masterCopy after time limit', async () => { 124 | await wait(60 * 60 * 24 * 30) 125 | await assertIsNotAuctioneer(seller1) 126 | const params1 = await getExchangeParams() 127 | 128 | assert.notEqual(await dx.getMasterCopy(), dxNew.address, 'address should not yet be the same') 129 | log(`DutchExchange contract is at the ${dx.address} address`) 130 | 131 | log('calling dx.updateMasterCopy() as not auctioneer after time limit') 132 | await dx.updateMasterCopy({ from: seller1 }) 133 | 134 | // using a new interface as masterCopy is an InternalTests now 135 | const ndx = await InternalTests.at(pr.address) 136 | const params2 = await getExchangeParams(ndx) 137 | assert.deepEqual(params1, params2, 'exchange params should stay the same') 138 | 139 | assert.strictEqual(await ndx.getMasterCopy.call(), dxNew.address, 'masterCopy address should have changed') 140 | log(`DutchExchange contract is now at the ${dxNew.address} address`) 141 | }) 142 | }) 143 | --------------------------------------------------------------------------------