├── .gitattributes ├── scripts ├── coverage.sh └── test.sh ├── migrations ├── 1_initial_migration.js ├── 2_deploy_misc.js ├── 3_deploy_nexusmutual.js └── 4_initialize_nexusmutual.js ├── .gitignore ├── test ├── utils │ ├── web3.js │ ├── encoder.js │ ├── latestTime.js │ ├── assertRevert.js │ ├── ethTools.js │ ├── expectEvent.js │ ├── getMCRPerThreshold.js │ ├── advanceToBlock.js │ ├── gvProposal.js │ ├── increaseTime.js │ └── getQuote.js ├── 02_Membership.test.js ├── 20_TokenModule.test.js ├── 05_Staking.test.js ├── 14_ProposalCategory.test.js ├── 19_NewTokenPriceTest.test.js ├── 13_MemberRoles.test.js ├── 04_Locking.test.js └── 11_MCR.test.js ├── contracts ├── mocks │ ├── ClaimsDataMock.sol │ ├── DSValueMock.sol │ ├── TokenDataMock.sol │ ├── FactoryMock.sol │ ├── PoolDataMock.sol │ ├── QuotationDataMock.sol │ ├── Pool1Mock.sol │ ├── TokenFunctionMock.sol │ ├── ExchangeMock.sol │ ├── MockMKR.sol │ └── MockDAI.sol ├── Migrations.sol ├── external │ ├── openzeppelin-solidity │ │ ├── token │ │ │ └── ERC20 │ │ │ │ ├── IERC20.sol │ │ │ │ └── ERC20.sol │ │ ├── math │ │ │ └── SafeMath.sol │ │ └── ownership │ │ │ └── Ownable.sol │ ├── proxy │ │ ├── Proxy.sol │ │ ├── UpgradeabilityProxy.sol │ │ └── OwnedUpgradeabilityProxy.sol │ ├── uniswap │ │ └── solidity-interface.sol │ ├── govblocks-protocol │ │ ├── Governed.sol │ │ └── interfaces │ │ │ ├── IMemberRoles.sol │ │ │ ├── IProposalCategory.sol │ │ │ └── IGovernance.sol │ └── ERC1132 │ │ └── IERC1132.sol ├── Iupgradable.sol ├── INXMMaster.sol ├── NXMToken.sol └── MCR.sol ├── nxdev.sh ├── nxdev.bat ├── .solhint.json ├── .solcover.js ├── .travis.yml ├── truffle-config.js ├── package.json └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity -------------------------------------------------------------------------------- /scripts/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SOLIDITY_COVERAGE=true scripts/test.sh 4 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require('Migrations'); 2 | 3 | module.exports = async function(deployer) { 4 | await deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build artifacts 2 | build 3 | 4 | # txt files 5 | *.txt 6 | 7 | # node modules 8 | node_modules 9 | 10 | # python script 11 | *.py 12 | 13 | # log files 14 | *.log 15 | 16 | # coverage 17 | coverageEnv 18 | coverage 19 | coverage.json 20 | -------------------------------------------------------------------------------- /test/utils/web3.js: -------------------------------------------------------------------------------- 1 | const pify = require('pify'); 2 | 3 | const ethAsync = pify(web3.eth); 4 | 5 | module.exports = { 6 | ethGetBalance: ethAsync.getBalance, 7 | ethSendTransaction: ethAsync.sendTransaction, 8 | ethGetBlock: ethAsync.getBlock 9 | }; 10 | -------------------------------------------------------------------------------- /test/utils/encoder.js: -------------------------------------------------------------------------------- 1 | var abi = require('ethereumjs-abi'); 2 | 3 | function encode(...args) { 4 | var encoded = abi.simpleEncode.apply(this, args); 5 | encoded = encoded.toString('hex'); 6 | return '0x' + encoded; 7 | } 8 | 9 | module.exports = { encode }; 10 | -------------------------------------------------------------------------------- /test/utils/latestTime.js: -------------------------------------------------------------------------------- 1 | const { ethGetBlock } = require('./web3'); 2 | 3 | // Returns the time of the last mined block in seconds 4 | async function latestTime() { 5 | const block = await ethGetBlock('latest'); 6 | return block.timestamp; 7 | } 8 | 9 | module.exports = { 10 | latestTime 11 | }; 12 | -------------------------------------------------------------------------------- /contracts/mocks/ClaimsDataMock.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity 0.5.7; 3 | 4 | import "../ClaimsData.sol"; 5 | 6 | 7 | contract ClaimsDataMock is ClaimsData { 8 | 9 | constructor() public ClaimsData() 10 | { 11 | maxVotingTime = 1800; 12 | minVotingTime = 1200; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /test/utils/assertRevert.js: -------------------------------------------------------------------------------- 1 | async function assertRevert(promise) { 2 | try { 3 | await promise; 4 | throw null; 5 | } catch (error) { 6 | assert(error, `Expected an error but did not get one`); 7 | assert( 8 | error.message.includes('revert'), 9 | `Expected an error containing "revert" but got "${error.message}" instead` 10 | ); 11 | } 12 | } 13 | 14 | module.exports = { 15 | assertRevert 16 | }; 17 | -------------------------------------------------------------------------------- /nxdev.sh: -------------------------------------------------------------------------------- 1 | @echo off 2 | title Nexus Dev Kitchen !!! 3 | echo Loading Resources . . . 4 | cd node_modules/.bin/ 5 | echo starting ganche-cli . . . 6 | ganache-cli --gasLimit 0xfffffffffff -i 5777 -p 8545 -m 'grocery obvious wire insane limit weather parade parrot patrol stock blast ivory' -a 30 -e 10000000 7 | ping 127.0.0.1 -n 5 > nul 8 | echo starting oraclize ethereum bridge . . . 9 | ethereum-bridge -H localhost:8545 -a 20 --dev 10 | -------------------------------------------------------------------------------- /nxdev.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | title Nexus Dev Kitchen !!! 3 | echo Loading Resources . . . 4 | cd node_modules/.bin/ 5 | echo starting ganche-cli . . . 6 | start cmd.exe /k "ganache-cli --gasLimit 0xfffffffffff -i 5777 -p 8545 -m 'grocery obvious wire insane limit weather parade parrot patrol stock blast ivory' -a 30 -e 10000000" 7 | ping 127.0.0.1 -n 5 > nul 8 | echo starting oraclize ethereum bridge . . . 9 | start cmd.exe /k "ethereum-bridge -H localhost:8545 -a 29 --dev" -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "default", 3 | "rules": { 4 | "indent": ["error", 4], 5 | "quotes": ["error", "double"], 6 | "max-line-length": ["error", 120], 7 | "code-complexity": false, 8 | "not-rely-on-time": false, 9 | "check-send-result": false, 10 | "function-max-lines": false, 11 | "max-states-count": false, 12 | "no-empty-blocks": false, 13 | "multiple-sends": false, 14 | "no-simple-event-func-name": false 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/utils/ethTools.js: -------------------------------------------------------------------------------- 1 | const Web3 = require('web3'); 2 | const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); // Hardcoded development port 3 | function ether(n) { 4 | return new web3.BigNumber(web3.toWei(n, 'ether')); 5 | } 6 | 7 | function toWei(value) { 8 | return web3.toWei(value, 'ether'); 9 | } 10 | 11 | function toHex(value) { 12 | return web3.toHex(value); 13 | } 14 | module.exports = { 15 | ether, 16 | toWei, 17 | toHex 18 | }; 19 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | port: 8555, 3 | norpc: true, 4 | deepSkip: true, 5 | skipFiles: [ 6 | 'imports', 7 | // 'Pool1.sol', 8 | // 'Pool2.sol', 9 | 'EventCaller.sol', 10 | // 'Governance.sol', 11 | // 'ProposalCategory.sol', 12 | 'dummyDaiFeed.sol', 13 | // 'MemberRoles.sol', 14 | 'mocks' 15 | ], 16 | forceParse: [ 17 | 'imports/ERC1132', 18 | 'imports/govblocks-protocol', 19 | // 'Governance.sol', 20 | // 'ProposalCategory.sol', 21 | 'mocks' 22 | ] 23 | }; 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: required 3 | language: node_js 4 | 5 | node_js: 6 | - "10" 7 | 8 | cache: 9 | directories: 10 | - node_modules 11 | 12 | script: 13 | - npm install 14 | 15 | jobs: 16 | fast_finish: true 17 | allow_failures: 18 | - env: SOLIDITY_COVERAGE=true 19 | include: 20 | - stage: tests 21 | name: "unit tests" 22 | script: npm run test 23 | - stage: tests 24 | name: "unit tests with coverage" 25 | script: travis_wait 90 npm run test 26 | env: SOLIDITY_COVERAGE=true 27 | 28 | -------------------------------------------------------------------------------- /test/utils/expectEvent.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | 3 | function inLogs(logs, eventName, eventArgs = {}) { 4 | const event = logs.find(e => e.event === eventName); 5 | should.exist(event); 6 | for (const [k, v] of Object.entries(eventArgs)) { 7 | should.exist(event.args[k]); 8 | event.args[k].should.equal(v); 9 | } 10 | return event; 11 | } 12 | 13 | async function inTransaction(tx, eventName, eventArgs = {}) { 14 | const { logs } = await tx; 15 | return inLogs(logs, eventName, eventArgs); 16 | } 17 | 18 | module.exports = { 19 | inLogs, 20 | inTransaction 21 | }; 22 | -------------------------------------------------------------------------------- /contracts/mocks/DSValueMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | 4 | contract DSValueMock { 5 | 6 | bytes32 public p; 7 | 8 | constructor() public { 9 | uint val = 120 * 10**18; 10 | p = bytes32(val); 11 | } 12 | 13 | function read() public view returns (bytes32) { 14 | return p; 15 | 16 | } 17 | 18 | function setRate(uint value) public { 19 | p = bytes32(value); 20 | } 21 | 22 | function peek() public pure returns (bytes32, bool) { 23 | return (0x000000000000000000000000000000000000000000000008696a94dfc55d0000, true); 24 | } 25 | } -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.0; 2 | 3 | 4 | contract Migrations { 5 | address public owner; 6 | uint public lastCompletedMigration; 7 | 8 | constructor() public { 9 | owner = msg.sender; 10 | } 11 | 12 | modifier restricted() { 13 | if (msg.sender == owner) _; 14 | } 15 | 16 | function setCompleted(uint completed) public restricted { 17 | lastCompletedMigration = completed; 18 | } 19 | 20 | function upgrade(address newAddress) public restricted { 21 | Migrations upgraded = Migrations(newAddress); 22 | upgraded.setCompleted(lastCompletedMigration); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/mocks/TokenDataMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | import "../TokenData.sol"; 4 | 5 | 6 | contract TokenDataMock is TokenData { 7 | 8 | constructor(address payable _walletAdd) public TokenData(_walletAdd) { 9 | walletAddress = _walletAdd; 10 | bookTime = 60; 11 | joiningFee = 2000000000000000; // 0.002 Ether 12 | lockTokenTimeAfterCoverExp = 35 days; 13 | scValidDays = 250; 14 | lockCADays = 7 days; 15 | lockMVDays = 2 days; 16 | stakerCommissionPer = 20; 17 | stakerMaxCommissionPer = 50; 18 | tokenExponent = 4; 19 | priceStep = 1000; 20 | 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | development: { 4 | host: '127.0.0.1', 5 | port: 8545, 6 | gas: 8000000, 7 | network_id: '5777' 8 | }, 9 | coverage: { 10 | host: '127.0.0.1', 11 | network_id: '5777', 12 | port: 8555, 13 | gas: 8000000, 14 | gasPrice: 0x01 15 | }, 16 | ganache: { 17 | host: '127.0.0.1', 18 | port: 8545, 19 | gas: 8000000, 20 | network_id: '5777' 21 | } 22 | }, 23 | compilers: { 24 | solc: { 25 | version: '0.5.7', 26 | settings: { 27 | optimizer: { 28 | enabled: true, 29 | runs: 200 30 | } 31 | } 32 | } 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /contracts/mocks/FactoryMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | 4 | contract FactoryMock { 5 | 6 | mapping (address => address) internal exchange; 7 | 8 | mapping (address => address) internal token; 9 | 10 | function getExchange(address _tokenAddress) public view returns (address) { 11 | 12 | return exchange[_tokenAddress]; 13 | } 14 | 15 | function getToken(address _exchangeAddress) public view returns (address) { 16 | return token[_exchangeAddress]; 17 | } 18 | 19 | function setFactory(address _tokenAddress, address _exchangeAddress) public { 20 | exchange[_tokenAddress] = _exchangeAddress; 21 | token[_exchangeAddress] = _tokenAddress; 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /test/utils/getMCRPerThreshold.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var BN = require('bn.js'); 3 | var ethABI = require('ethereumjs-abi'); 4 | var util = require('ethereumjs-util'); 5 | async function getValue(...args) { 6 | let vf = args[0]; 7 | let pd = args[1]; 8 | let mcr = args[2]; 9 | let vtp = await mcr.calVtpAndMCRtp(); 10 | let totalSa = await mcr.getAllSumAssurance(); 11 | let mincap = await pd.minCap(); 12 | let val = await mcr.getThresholdValues(vtp[0], vf, totalSa, mincap); 13 | // console.log(val); 14 | // console.log(vtp[0]," ",vf," ",totalSa," ",mincap); 15 | return parseInt((val[0] / 1 + val[1] / 1) / 2); 16 | } 17 | 18 | function bigNumberToBN(value) { 19 | return new BN(value.toString(), 10); 20 | } 21 | 22 | module.exports = { getValue }; 23 | -------------------------------------------------------------------------------- /test/utils/advanceToBlock.js: -------------------------------------------------------------------------------- 1 | function advanceBlock() { 2 | return new Promise((resolve, reject) => { 3 | web3.currentProvider.send( 4 | { 5 | jsonrpc: '2.0', 6 | method: 'evm_mine', 7 | id: Date.now() 8 | }, 9 | (err, res) => { 10 | return err ? reject(err) : resolve(res); 11 | } 12 | ); 13 | }); 14 | } 15 | 16 | // Advances the block number so that the last mined block is `number`. 17 | async function advanceToBlock(number) { 18 | if (web3.eth.blockNumber > number) { 19 | throw Error( 20 | `block number ${number} is in the past (current is ${ 21 | web3.eth.blockNumber 22 | })` 23 | ); 24 | } 25 | 26 | while (web3.eth.blockNumber < number) { 27 | await advanceBlock(); 28 | } 29 | } 30 | 31 | module.exports = { 32 | advanceBlock, 33 | advanceToBlock 34 | }; 35 | -------------------------------------------------------------------------------- /contracts/external/openzeppelin-solidity/token/ERC20/IERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | 4 | /** 5 | * @title ERC20 interface 6 | * @dev see https://github.com/ethereum/EIPs/issues/20 7 | */ 8 | interface IERC20 { 9 | function transfer(address to, uint256 value) external returns (bool); 10 | 11 | function approve(address spender, uint256 value) 12 | external returns (bool); 13 | 14 | function transferFrom(address from, address to, uint256 value) 15 | external returns (bool); 16 | 17 | function totalSupply() external view returns (uint256); 18 | 19 | function balanceOf(address who) external view returns (uint256); 20 | 21 | function allowance(address owner, address spender) 22 | external view returns (uint256); 23 | 24 | event Transfer( 25 | address indexed from, 26 | address indexed to, 27 | uint256 value 28 | ); 29 | 30 | event Approval( 31 | address indexed owner, 32 | address indexed spender, 33 | uint256 value 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /contracts/external/proxy/Proxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | 4 | /** 5 | * @title Proxy 6 | * @dev Gives the possibility to delegate any call to a foreign implementation. 7 | */ 8 | contract Proxy { 9 | /** 10 | * @dev Fallback function allowing to perform a delegatecall to the given implementation. 11 | * This function will return whatever the implementation call returns 12 | */ 13 | function () external payable { 14 | address _impl = implementation(); 15 | require(_impl != address(0)); 16 | 17 | assembly { 18 | let ptr := mload(0x40) 19 | calldatacopy(ptr, 0, calldatasize) 20 | let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0) 21 | let size := returndatasize 22 | returndatacopy(ptr, 0, size) 23 | 24 | switch result 25 | case 0 { revert(ptr, size) } 26 | default { return(ptr, size) } 27 | } 28 | } 29 | 30 | /** 31 | * @dev Tells the address of the implementation where every call will be delegated. 32 | * @return address of the implementation to which it will be delegated 33 | */ 34 | function implementation() public view returns (address); 35 | } -------------------------------------------------------------------------------- /contracts/mocks/PoolDataMock.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity 0.5.7; 3 | 4 | import "../MCR.sol"; 5 | import "../PoolData.sol"; 6 | 7 | 8 | contract PoolDataMock is PoolData { 9 | 10 | constructor(address _notariseAdd, address _daiFeedAdd, address _daiAdd) public 11 | PoolData(_notariseAdd, _daiFeedAdd, _daiAdd) { 12 | uint DECIMAL1E18 = 10 ** 18; 13 | notariseMCR = _notariseAdd; 14 | daiFeedAddress = _daiFeedAdd; 15 | c = 5203349; 16 | a = 1948; 17 | mcrTime = 24 hours; 18 | mcrFailTime = 6 hours; 19 | minCap = 7; 20 | shockParameter = 50; 21 | variationPercX100 = 100; //1% 22 | iaRatesTime = 24 hours; //24 hours in seconds 23 | uniswapDeadline = 20 minutes; 24 | liquidityTradeCallbackTime = 4 hours; 25 | ethVolumeLimit = 4; 26 | capacityLimit = 10; 27 | allCurrencyAssets["ETH"] = CurrencyAssets(address(0), 6 * DECIMAL1E18, 0); 28 | allCurrencyAssets["DAI"] = CurrencyAssets(_daiAdd, 7 * DECIMAL1E18, 0); 29 | allInvestmentAssets["ETH"] = InvestmentAssets(address(0), true, 500, 5000, 18); 30 | allInvestmentAssets["DAI"] = InvestmentAssets(_daiAdd, true, 500, 5000, 18); 31 | } 32 | 33 | function changeCurrencyAssetBaseMin(bytes4 curr, uint baseMin) external { 34 | allCurrencyAssets[curr].baseMin = baseMin; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/Iupgradable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | import "./INXMMaster.sol"; 4 | 5 | 6 | contract Iupgradable { 7 | 8 | INXMMaster public ms; 9 | address public nxMasterAddress; 10 | 11 | modifier onlyInternal { 12 | require(ms.isInternal(msg.sender)); 13 | _; 14 | } 15 | 16 | modifier isMemberAndcheckPause { 17 | require(ms.isPause() == false && ms.isMember(msg.sender) == true); 18 | _; 19 | } 20 | 21 | modifier onlyOwner { 22 | require(ms.isOwner(msg.sender)); 23 | _; 24 | } 25 | 26 | modifier checkPause { 27 | require(ms.isPause() == false); 28 | _; 29 | } 30 | 31 | modifier isMember { 32 | require(ms.isMember(msg.sender), "Not member"); 33 | _; 34 | } 35 | 36 | /** 37 | * @dev Iupgradable Interface to update dependent contract address 38 | */ 39 | function changeDependentContractAddress() public; 40 | 41 | /** 42 | * @dev change master address 43 | * @param _masterAddress is the new address 44 | */ 45 | function changeMasterAddress(address _masterAddress) public { 46 | if (address(ms) != address(0)) { 47 | require(address(ms) == msg.sender, "Not master"); 48 | } 49 | ms = INXMMaster(_masterAddress); 50 | nxMasterAddress = _masterAddress; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /migrations/2_deploy_misc.js: -------------------------------------------------------------------------------- 1 | const DAI = artifacts.require('MockDAI'); 2 | const MKR = artifacts.require('MockMKR'); 3 | const DSValue = artifacts.require('DSValueMock'); 4 | const FactoryMock = artifacts.require('FactoryMock'); 5 | const ExchangeMock = artifacts.require('ExchangeMock'); 6 | const ExchangeMKRMock = artifacts.require('ExchangeMock'); 7 | 8 | let dai; 9 | let factory; 10 | let exchange; 11 | let mkr; 12 | const EXCHANGE_TOKEN = '10000000000000000000000'; 13 | const EXCHANGE_ETHER = 100000000000000000000; 14 | 15 | module.exports = function(deployer, network, accounts) { 16 | deployer.then(async () => { 17 | dai = await deployer.deploy(DAI); 18 | mkr = await deployer.deploy(MKR); 19 | await deployer.deploy(DSValue); 20 | factory = await deployer.deploy(FactoryMock); 21 | exchange = await deployer.deploy( 22 | ExchangeMock, 23 | dai.address, 24 | factory.address 25 | ); 26 | exchangeMKR = await deployer.deploy( 27 | ExchangeMKRMock, 28 | mkr.address, 29 | factory.address 30 | ); 31 | await factory.setFactory(dai.address, exchange.address); 32 | await factory.setFactory(mkr.address, exchangeMKR.address); 33 | await dai.transfer(exchange.address, EXCHANGE_TOKEN); 34 | await mkr.transfer(exchangeMKR.address, EXCHANGE_TOKEN); 35 | await exchange.recieveEther({ value: EXCHANGE_ETHER }); 36 | await exchangeMKR.recieveEther({ value: EXCHANGE_ETHER }); 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /contracts/mocks/QuotationDataMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | import "../QuotationData.sol"; 4 | import "../PoolData.sol"; 5 | import "../external/proxy/OwnedUpgradeabilityProxy.sol"; 6 | 7 | 8 | contract QuotationDataMock is QuotationData { 9 | 10 | PoolData public pd; 11 | 12 | constructor (address _authQuoteAdd, address _kycAuthAdd) public QuotationData(_authQuoteAdd, _kycAuthAdd) { 13 | 14 | } 15 | 16 | function changeHoldedCoverDetails (uint index, uint[] memory newcoverDetails) public { 17 | allCoverHolded[index].coverDetails = newcoverDetails; 18 | } 19 | 20 | function changeHoldedCoverPeriod (uint index, uint16 newCoverPeriod) public { 21 | allCoverHolded[index].coverPeriod = newCoverPeriod; 22 | } 23 | 24 | function changeHoldedCoverCurrency (uint index, bytes4 newCurr) public { 25 | allCoverHolded[index].coverCurr = newCurr; 26 | } 27 | 28 | function changeCurrencyAssetAddress(bytes4 curr, address currAdd) public { 29 | pd = PoolData(ms.getLatestAddress("PD")); 30 | pd.changeCurrencyAssetAddress(curr, currAdd); 31 | } 32 | 33 | function changeInvestmentAssetAddress(bytes4 curr, address currAdd) public { 34 | pd = PoolData(ms.getLatestAddress("PD")); 35 | pd.changeInvestmentAssetAddressAndDecimal(curr, currAdd, 18); 36 | } 37 | 38 | function getImplementationAdd(bytes2 _contract) public view returns(address) { 39 | 40 | UpgradeabilityProxy up = UpgradeabilityProxy(ms.getLatestAddress(_contract)); 41 | return up.implementation(); 42 | } 43 | } -------------------------------------------------------------------------------- /contracts/external/uniswap/solidity-interface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | 4 | contract Factory { 5 | function getExchange(address token) public view returns (address); 6 | function getToken(address exchange) public view returns (address); 7 | } 8 | 9 | 10 | contract Exchange { 11 | function getEthToTokenInputPrice(uint256 ethSold) public view returns(uint256); 12 | 13 | function getTokenToEthInputPrice(uint256 tokensSold) public view returns(uint256); 14 | 15 | function ethToTokenSwapInput(uint256 minTokens, uint256 deadline) public payable returns (uint256); 16 | 17 | function ethToTokenTransferInput(uint256 minTokens, uint256 deadline, address recipient) 18 | public payable returns (uint256); 19 | 20 | function tokenToEthSwapInput(uint256 tokensSold, uint256 minEth, uint256 deadline) 21 | public payable returns (uint256); 22 | 23 | function tokenToEthTransferInput(uint256 tokensSold, uint256 minEth, uint256 deadline, address recipient) 24 | public payable returns (uint256); 25 | 26 | function tokenToTokenSwapInput( 27 | uint256 tokensSold, 28 | uint256 minTokensBought, 29 | uint256 minEthBought, 30 | uint256 deadline, 31 | address tokenAddress 32 | ) 33 | public returns (uint256); 34 | 35 | function tokenToTokenTransferInput( 36 | uint256 tokensSold, 37 | uint256 minTokensBought, 38 | uint256 minEthBought, 39 | uint256 deadline, 40 | address recipient, 41 | address tokenAddress 42 | ) 43 | public returns (uint256); 44 | } 45 | -------------------------------------------------------------------------------- /contracts/external/govblocks-protocol/Governed.sol: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 GovBlocks.io 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | This program is distributed in the hope that it will be useful, 7 | but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | GNU General Public License for more details. 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see http://www.gnu.org/licenses/ */ 12 | 13 | pragma solidity 0.5.7; 14 | 15 | 16 | contract IMaster { 17 | function getLatestAddress(bytes2 _module) public view returns(address); 18 | } 19 | 20 | 21 | contract Governed { 22 | 23 | address public masterAddress; // Name of the dApp, needs to be set by contracts inheriting this contract 24 | 25 | /// @dev modifier that allows only the authorized addresses to execute the function 26 | modifier onlyAuthorizedToGovern() { 27 | IMaster ms = IMaster(masterAddress); 28 | require(ms.getLatestAddress("GV") == msg.sender, "Not authorized"); 29 | _; 30 | } 31 | 32 | /// @dev checks if an address is authorized to govern 33 | function isAuthorizedToGovern(address _toCheck) public view returns(bool) { 34 | IMaster ms = IMaster(masterAddress); 35 | return (ms.getLatestAddress("GV") == _toCheck); 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nexusmutual", 3 | "version": "1.0.0", 4 | "description": "Smart Contract Covers", 5 | "files": [ 6 | "contracts", 7 | "test" 8 | ], 9 | "scripts": { 10 | "test": "scripts/test.sh", 11 | "pretty": "prettier --write --single-quote --tab-width 2 \"**/*.js\"", 12 | "precommit": "lint-staged" 13 | }, 14 | "lint-staged": { 15 | "**/*.{js,json}": [ 16 | "prettier --single-quote --write --tab-width 2", 17 | "git add" 18 | ] 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/somish/NexusMutual.git" 23 | }, 24 | "keywords": [ 25 | "solidity", 26 | "ethereum", 27 | "smart", 28 | "contracts", 29 | "insurance" 30 | ], 31 | "author": "nexusmutual.io", 32 | "license": "GPL-3.0", 33 | "bugs": { 34 | "url": "https://github.com/somish/NexusMutual/issues" 35 | }, 36 | "homepage": "https://github.com/somish/NexusMutual#readme", 37 | "dependencies": { 38 | "bitcore-lib": "^0.16.0", 39 | "eth-lightwallet": "^3.0.1", 40 | "web3": "^0.20.0" 41 | }, 42 | "devDependencies": { 43 | "chai": "^4.1.2", 44 | "chai-bignumber": "^2.0.2", 45 | "coveralls": "^3.0.2", 46 | "ethereum-bridge": "^0.6.1", 47 | "ethereumjs-abi": "^0.6.5", 48 | "ganache-cli": "^6.1.8", 49 | "husky": "^1.1.2", 50 | "lint-staged": "^7.2.2", 51 | "mocha": "^5.2.0", 52 | "prettier": "^1.14.2", 53 | "solc": "^0.5.7", 54 | "solhint": "^1.5.1", 55 | "solidity-coverage": "^0.5.11", 56 | "truffle": "^5.0.7", 57 | "truffle-hdwallet-provider": "0.0.5" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /contracts/INXMMaster.sol: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 NexusMutual.io 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program. If not, see http://www.gnu.org/licenses/ */ 15 | 16 | pragma solidity 0.5.7; 17 | 18 | 19 | contract INXMMaster { 20 | 21 | address public tokenAddress; 22 | 23 | address public owner; 24 | 25 | 26 | uint public pauseTime; 27 | 28 | function delegateCallBack(bytes32 myid) external; 29 | 30 | function masterInitialized() public view returns(bool); 31 | 32 | function isInternal(address _add) public view returns(bool); 33 | 34 | function isPause() public view returns(bool check); 35 | 36 | function isOwner(address _add) public view returns(bool); 37 | 38 | function isMember(address _add) public view returns(bool); 39 | 40 | function checkIsAuthToGoverned(address _add) public view returns(bool); 41 | 42 | function updatePauseTime(uint _time) public; 43 | 44 | function dAppLocker() public view returns(address _add); 45 | 46 | function dAppToken() public view returns(address _add); 47 | 48 | function getLatestAddress(bytes2 _contractName) public view returns(address payable contractAddress); 49 | } -------------------------------------------------------------------------------- /test/utils/gvProposal.js: -------------------------------------------------------------------------------- 1 | const increaseTime = require('./increaseTime.js').increaseTime; 2 | async function gvProposal(...args) { 3 | let catId = args[0]; 4 | let actionHash = args[1]; 5 | let mr = args[2]; 6 | let gv = args[3]; 7 | let seq = args[4]; 8 | let p = await gv.getProposalLength(); 9 | await gv.createProposal('proposal', 'proposal', 'proposal', 0); 10 | await gv.categorizeProposal(p, catId, 0); 11 | await gv.submitProposalWithSolution(p, 'proposal', actionHash); 12 | let members = await mr.members(seq); 13 | let iteration = 0; 14 | for (iteration = 0; iteration < members[1].length; iteration++) 15 | await gv.submitVote(p, 1, { 16 | from: members[1][iteration] 17 | }); 18 | // console.log(await gv.proposalDetails(p)); 19 | if (seq != 3) await gv.closeProposal(p); 20 | let proposal = await gv.proposal(p); 21 | assert.equal(proposal[2].toNumber(), 3); 22 | } 23 | 24 | async function gvProposalWithIncentive(...args) { 25 | let catId = args[0]; 26 | let actionHash = args[1]; 27 | let mr = args[2]; 28 | let gv = args[3]; 29 | let seq = args[4]; 30 | let incentive = args[5]; 31 | let p = await gv.getProposalLength(); 32 | await gv.createProposal('proposal', 'proposal', 'proposal', 0); 33 | await gv.categorizeProposal(p, catId, incentive); 34 | await gv.submitProposalWithSolution(p, 'proposal', actionHash); 35 | let members = await mr.members(seq); 36 | let iteration = 0; 37 | for (iteration = 0; iteration < members[1].length; iteration++) 38 | await gv.submitVote(p, 1, { 39 | from: members[1][iteration] 40 | }); 41 | // console.log(await gv.proposalDetails(p)); 42 | await increaseTime(604800); 43 | if (seq != 3) await gv.closeProposal(p); 44 | let proposal = await gv.proposal(p); 45 | assert.equal(proposal[2].toNumber(), 3); 46 | } 47 | 48 | module.exports = { gvProposalWithIncentive, gvProposal }; 49 | -------------------------------------------------------------------------------- /test/utils/increaseTime.js: -------------------------------------------------------------------------------- 1 | const { latestTime } = require('./latestTime'); 2 | 3 | // Increases ganache time by the passed duration in seconds 4 | function increaseTime(duration) { 5 | const id = Date.now(); 6 | 7 | return new Promise((resolve, reject) => { 8 | web3.currentProvider.send( 9 | { 10 | jsonrpc: '2.0', 11 | method: 'evm_increaseTime', 12 | params: [duration], 13 | id: id 14 | }, 15 | err1 => { 16 | if (err1) return reject(err1); 17 | 18 | web3.currentProvider.send( 19 | { 20 | jsonrpc: '2.0', 21 | method: 'evm_mine', 22 | id: id + 1 23 | }, 24 | (err2, res) => { 25 | return err2 ? reject(err2) : resolve(res); 26 | } 27 | ); 28 | } 29 | ); 30 | }); 31 | } 32 | 33 | /** 34 | * Beware that due to the need of calling two separate ganache methods and rpc calls overhead 35 | * it's hard to increase time precisely to a target point so design your test to tolerate 36 | * small fluctuations from time to time. 37 | * 38 | * @param target time in seconds 39 | */ 40 | async function increaseTimeTo(target) { 41 | const now = await latestTime(); 42 | 43 | if (target < now) 44 | throw Error( 45 | `Cannot increase current time(${now}) to a moment in the past(${target})` 46 | ); 47 | const diff = target - now; 48 | return increaseTime(diff); 49 | } 50 | 51 | const duration = { 52 | seconds: function(val) { 53 | return val; 54 | }, 55 | minutes: function(val) { 56 | return val * this.seconds(60); 57 | }, 58 | hours: function(val) { 59 | return val * this.minutes(60); 60 | }, 61 | days: function(val) { 62 | return val * this.hours(24); 63 | }, 64 | weeks: function(val) { 65 | return val * this.days(7); 66 | }, 67 | years: function(val) { 68 | return val * this.days(365); 69 | } 70 | }; 71 | 72 | module.exports = { 73 | increaseTime, 74 | increaseTimeTo, 75 | duration 76 | }; 77 | -------------------------------------------------------------------------------- /contracts/external/proxy/UpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | import "./Proxy.sol"; 4 | 5 | 6 | /** 7 | * @title UpgradeabilityProxy 8 | * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded 9 | */ 10 | contract UpgradeabilityProxy is Proxy { 11 | /** 12 | * @dev This event will be emitted every time the implementation gets upgraded 13 | * @param implementation representing the address of the upgraded implementation 14 | */ 15 | event Upgraded(address indexed implementation); 16 | 17 | // Storage position of the address of the current implementation 18 | bytes32 private constant IMPLEMENTATION_POSITION = keccak256("org.govblocks.proxy.implementation"); 19 | 20 | /** 21 | * @dev Constructor function 22 | */ 23 | constructor() public {} 24 | 25 | /** 26 | * @dev Tells the address of the current implementation 27 | * @return address of the current implementation 28 | */ 29 | function implementation() public view returns (address impl) { 30 | bytes32 position = IMPLEMENTATION_POSITION; 31 | assembly { 32 | impl := sload(position) 33 | } 34 | } 35 | 36 | /** 37 | * @dev Sets the address of the current implementation 38 | * @param _newImplementation address representing the new implementation to be set 39 | */ 40 | function _setImplementation(address _newImplementation) internal { 41 | bytes32 position = IMPLEMENTATION_POSITION; 42 | assembly { 43 | sstore(position, _newImplementation) 44 | } 45 | } 46 | 47 | /** 48 | * @dev Upgrades the implementation address 49 | * @param _newImplementation representing the address of the new implementation to be set 50 | */ 51 | function _upgradeTo(address _newImplementation) internal { 52 | address currentImplementation = implementation(); 53 | require(currentImplementation != _newImplementation); 54 | _setImplementation(_newImplementation); 55 | emit Upgraded(_newImplementation); 56 | } 57 | } -------------------------------------------------------------------------------- /contracts/external/openzeppelin-solidity/math/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | 4 | /** 5 | * @title SafeMath 6 | * @dev Math operations with safety checks that revert on error 7 | */ 8 | library SafeMath { 9 | 10 | /** 11 | * @dev Multiplies two numbers, reverts on overflow. 12 | */ 13 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 14 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 15 | // benefit is lost if 'b' is also tested. 16 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 17 | if (a == 0) { 18 | return 0; 19 | } 20 | 21 | uint256 c = a * b; 22 | require(c / a == b); 23 | 24 | return c; 25 | } 26 | 27 | /** 28 | * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. 29 | */ 30 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 31 | require(b > 0); // Solidity only automatically asserts when dividing by 0 32 | uint256 c = a / b; 33 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 34 | 35 | return c; 36 | } 37 | 38 | /** 39 | * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). 40 | */ 41 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 42 | require(b <= a); 43 | uint256 c = a - b; 44 | 45 | return c; 46 | } 47 | 48 | /** 49 | * @dev Adds two numbers, reverts on overflow. 50 | */ 51 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 52 | uint256 c = a + b; 53 | require(c >= a); 54 | 55 | return c; 56 | } 57 | 58 | /** 59 | * @dev Divides two numbers and returns the remainder (unsigned integer modulo), 60 | * reverts when dividing by zero. 61 | */ 62 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 63 | require(b != 0); 64 | return a % b; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit script as soon as a command fails. 4 | set -o errexit 5 | 6 | # Executes cleanup function at script exit. 7 | trap cleanup EXIT 8 | 9 | cleanup() { 10 | # Kill the ganache instance that we started (if we started one and if it's still running). 11 | if [ -n "$ganache_pid" ] && ps -p $ganache_pid > /dev/null; then 12 | kill -9 $ganache_pid 13 | fi 14 | } 15 | 16 | if [ "$SOLIDITY_COVERAGE" = true ]; then 17 | ganache_port=8555 18 | else 19 | ganache_port=8545 20 | fi 21 | 22 | ganache_running() { 23 | nc -z localhost "$ganache_port" 24 | } 25 | 26 | start_ganache() { 27 | if [ "$SOLIDITY_COVERAGE" = true ]; then 28 | node_modules/.bin/testrpc-sc --gasLimit 0xfffffffffff -p "$ganache_port" -i 5777 -m "grocery obvious wire insane limit weather parade parrot patrol stock blast ivory" -a 30 -e 10000000 > /dev/null & 29 | else 30 | node_modules/.bin/ganache-cli --gasLimit 8000000 -p "$ganache_port" -i 5777 -m "grocery obvious wire insane limit weather parade parrot patrol stock blast ivory" -a 30 -e 10000000 > /dev/null & 31 | fi 32 | 33 | ganache_pid=$! 34 | } 35 | 36 | if ganache_running; then 37 | echo "Using existing ganache instance" 38 | else 39 | echo "Starting our own ganache instance" 40 | start_ganache 41 | sleep 2 42 | fi 43 | if [ "$SOLIDITY_COVERAGE" = true ]; then 44 | curl -o node_modules/solidity-parser-sc/build/parser.js https://nexusmutual.io/js/parser.js 45 | curl -o node_modules/solidity-coverage/lib/app.js https://nexusmutual.io/js/app.js 46 | sleep 2 47 | node_modules/.bin/solidity-coverage 48 | if [ "$CONTINUOUS_INTEGRATION" = true ]; then 49 | cat coverage/lcov.info | node_modules/.bin/coveralls 50 | fi 51 | else 52 | if [ -d "node_modules/eth-lightwallet/node_modules/bitcore-lib" ]; then 53 | rm -r "node_modules/eth-lightwallet/node_modules/bitcore-lib" 54 | echo "Deleted eth bitcore-lib" 55 | fi 56 | if [ -d "node_modules/bitcore-mnemonic/node_modules/bitcore-lib" ]; then 57 | rm -r "node_modules/bitcore-mnemonic/node_modules/bitcore-lib" 58 | echo "Deleted mne bitcore-lib" 59 | fi 60 | echo "Now let's test truffle" 61 | node_modules/.bin/truffle test "$@" 62 | fi 63 | -------------------------------------------------------------------------------- /test/02_Membership.test.js: -------------------------------------------------------------------------------- 1 | const MemberRoles = artifacts.require('MemberRoles'); 2 | const TokenFunctions = artifacts.require('TokenFunctionMock'); 3 | const NXMaster = artifacts.require('NXMaster'); 4 | 5 | const { assertRevert } = require('./utils/assertRevert'); 6 | const { ether } = require('./utils/ethTools'); 7 | 8 | let tf; 9 | let mr; 10 | let nxms; 11 | const fee = ether(0.002); 12 | 13 | const BigNumber = web3.BigNumber; 14 | require('chai') 15 | .use(require('chai-bignumber')(BigNumber)) 16 | .should(); 17 | 18 | contract('NXMToken:Membership', function([owner, member1, member2]) { 19 | before(async function() { 20 | nxms = await NXMaster.deployed(); 21 | tf = await TokenFunctions.deployed(); 22 | mr = await MemberRoles.at(await nxms.getLatestAddress('0x4d52')); 23 | await mr.addMembersBeforeLaunch([], []); 24 | (await mr.launched()).should.be.equal(true); 25 | }); 26 | describe('Buy membership', function() { 27 | describe('if paid joining fee', function() { 28 | it('2.1 should be able to join as member', async function() { 29 | await mr.payJoiningFee(member1, { from: member1, value: fee }); 30 | await mr.kycVerdict(member1, true, { from: owner }); 31 | (await mr.checkRole(member1, 2)).should.equal(true); 32 | }); 33 | }); 34 | describe('if not paid joining fee', function() { 35 | it('2.2 reverts', async function() { 36 | await assertRevert( 37 | mr.payJoiningFee(member2, { from: member2, value: fee - 1e15 }) 38 | ); 39 | (await mr.checkRole(member2, 2)).should.equal(false); 40 | }); 41 | }); 42 | }); 43 | describe('Withdraw membership', function() { 44 | describe('If met Withdraw membership conditions', function() { 45 | it('2.3 should be able to withdraw membership', async function() { 46 | await mr.withdrawMembership({ from: member1 }); 47 | (await mr.checkRole(member1, 2)).should.equal(false); 48 | }); 49 | }); 50 | describe('Cannot withdrawn if already withdrawn', function() { 51 | it('2.4 reverts', async function() { 52 | await assertRevert(mr.withdrawMembership({ from: member1 })); 53 | }); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /migrations/3_deploy_nexusmutual.js: -------------------------------------------------------------------------------- 1 | const Claims = artifacts.require('Claims'); 2 | const ClaimsData = artifacts.require('ClaimsDataMock'); 3 | const ClaimsReward = artifacts.require('ClaimsReward'); 4 | const NXMaster = artifacts.require('NXMaster'); 5 | const MCR = artifacts.require('MCR'); 6 | const NXMToken = artifacts.require('NXMToken'); 7 | const TokenData = artifacts.require('TokenDataMock'); 8 | const TokenFunctions = artifacts.require('TokenFunctionMock'); 9 | const TokenController = artifacts.require('TokenController'); 10 | const Pool1 = artifacts.require('Pool1Mock'); 11 | const Pool2 = artifacts.require('Pool2'); 12 | const PoolData = artifacts.require('PoolDataMock'); 13 | const Quotation = artifacts.require('Quotation'); 14 | const QuotationDataMock = artifacts.require('QuotationDataMock'); 15 | const Governance = artifacts.require('Governance'); 16 | const ProposalCategory = artifacts.require('ProposalCategory'); 17 | const MemberRoles = artifacts.require('MemberRoles'); 18 | const FactoryMock = artifacts.require('FactoryMock'); 19 | const DSValue = artifacts.require('DSValueMock'); 20 | const DAI = artifacts.require('MockDAI'); 21 | const INITIAL_SUPPLY = '1500000000000000000000000'; 22 | const QE = '0x51042c4d8936a7764d18370a6a0762b860bb8e07'; 23 | 24 | module.exports = function(deployer, network, accounts) { 25 | deployer.then(async () => { 26 | let founderAddress = accounts[0]; 27 | let factory = await FactoryMock.deployed(); 28 | let dsv = await DSValue.deployed(); 29 | let cad = await DAI.deployed(); 30 | await deployer.deploy(Claims); 31 | await deployer.deploy(ClaimsData); 32 | await deployer.deploy(ClaimsReward); 33 | await deployer.deploy(Pool1); 34 | await deployer.deploy(Pool2, factory.address); 35 | await deployer.deploy(PoolData, founderAddress, dsv.address, cad.address); 36 | await deployer.deploy(MCR); 37 | const tc = await deployer.deploy(TokenController); 38 | const tk = await deployer.deploy(NXMToken, founderAddress, INITIAL_SUPPLY); 39 | await deployer.deploy(TokenData, founderAddress); 40 | await deployer.deploy(TokenFunctions); 41 | await deployer.deploy(Quotation); 42 | await deployer.deploy(QuotationDataMock, QE, founderAddress); 43 | await deployer.deploy(Governance); 44 | await deployer.deploy(ProposalCategory); 45 | await deployer.deploy(MemberRoles); 46 | await deployer.deploy(NXMaster, tk.address); 47 | }); 48 | }; 49 | -------------------------------------------------------------------------------- /contracts/external/openzeppelin-solidity/ownership/Ownable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | 4 | /** 5 | * @title Ownable 6 | * @dev The Ownable contract has an owner address, and provides basic authorization control 7 | * functions, this simplifies the implementation of "user permissions". 8 | */ 9 | contract Ownable { 10 | address private _owner; 11 | 12 | event OwnershipTransferred( 13 | address indexed previousOwner, 14 | address indexed newOwner 15 | ); 16 | 17 | /** 18 | * @dev The Ownable constructor sets the original `owner` of the contract to the sender 19 | * account. 20 | */ 21 | constructor() internal { 22 | _owner = msg.sender; 23 | emit OwnershipTransferred(address(0), _owner); 24 | } 25 | 26 | /** 27 | * @return the address of the owner. 28 | */ 29 | function owner() public view returns(address) { 30 | return _owner; 31 | } 32 | 33 | /** 34 | * @dev Throws if called by any account other than the owner. 35 | */ 36 | modifier onlyOwner() { 37 | require(isOwner()); 38 | _; 39 | } 40 | 41 | /** 42 | * @return true if `msg.sender` is the owner of the contract. 43 | */ 44 | function isOwner() public view returns(bool) { 45 | return msg.sender == _owner; 46 | } 47 | 48 | /** 49 | * @dev Allows the current owner to relinquish control of the contract. 50 | * @notice Renouncing to ownership will leave the contract without an owner. 51 | * It will not be possible to call the functions with the `onlyOwner` 52 | * modifier anymore. 53 | */ 54 | function renounceOwnership() public onlyOwner { 55 | emit OwnershipTransferred(_owner, address(0)); 56 | _owner = address(0); 57 | } 58 | 59 | /** 60 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 61 | * @param newOwner The address to transfer ownership to. 62 | */ 63 | function transferOwnership(address newOwner) public onlyOwner { 64 | _transferOwnership(newOwner); 65 | } 66 | 67 | /** 68 | * @dev Transfers control of the contract to a newOwner. 69 | * @param newOwner The address to transfer ownership to. 70 | */ 71 | function _transferOwnership(address newOwner) internal { 72 | require(newOwner != address(0)); 73 | emit OwnershipTransferred(_owner, newOwner); 74 | _owner = newOwner; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /contracts/external/proxy/OwnedUpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | import "./UpgradeabilityProxy.sol"; 4 | 5 | 6 | /** 7 | * @title OwnedUpgradeabilityProxy 8 | * @dev This contract combines an upgradeability proxy with basic authorization control functionalities 9 | */ 10 | contract OwnedUpgradeabilityProxy is UpgradeabilityProxy { 11 | /** 12 | * @dev Event to show ownership has been transferred 13 | * @param previousOwner representing the address of the previous owner 14 | * @param newOwner representing the address of the new owner 15 | */ 16 | event ProxyOwnershipTransferred(address previousOwner, address newOwner); 17 | 18 | // Storage position of the owner of the contract 19 | bytes32 private constant PROXY_OWNER_POSITION = keccak256("org.govblocks.proxy.owner"); 20 | 21 | /** 22 | * @dev the constructor sets the original owner of the contract to the sender account. 23 | */ 24 | constructor(address _implementation) public { 25 | _setUpgradeabilityOwner(msg.sender); 26 | _upgradeTo(_implementation); 27 | } 28 | 29 | /** 30 | * @dev Throws if called by any account other than the owner. 31 | */ 32 | modifier onlyProxyOwner() { 33 | require(msg.sender == proxyOwner()); 34 | _; 35 | } 36 | 37 | /** 38 | * @dev Tells the address of the owner 39 | * @return the address of the owner 40 | */ 41 | function proxyOwner() public view returns (address owner) { 42 | bytes32 position = PROXY_OWNER_POSITION; 43 | assembly { 44 | owner := sload(position) 45 | } 46 | } 47 | 48 | /** 49 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 50 | * @param _newOwner The address to transfer ownership to. 51 | */ 52 | function transferProxyOwnership(address _newOwner) public onlyProxyOwner { 53 | require(_newOwner != address(0)); 54 | _setUpgradeabilityOwner(_newOwner); 55 | emit ProxyOwnershipTransferred(proxyOwner(), _newOwner); 56 | } 57 | 58 | /** 59 | * @dev Allows the proxy owner to upgrade the current version of the proxy. 60 | * @param _implementation representing the address of the new implementation to be set. 61 | */ 62 | function upgradeTo(address _implementation) public onlyProxyOwner { 63 | _upgradeTo(_implementation); 64 | } 65 | 66 | /** 67 | * @dev Sets the address of the owner 68 | */ 69 | function _setUpgradeabilityOwner(address _newProxyOwner) internal { 70 | bytes32 position = PROXY_OWNER_POSITION; 71 | assembly { 72 | sstore(position, _newProxyOwner) 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /contracts/mocks/Pool1Mock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | import "../Pool1.sol"; 4 | import "../ClaimsData.sol"; 5 | 6 | 7 | contract Pool1Mock is Pool1 { 8 | 9 | 10 | function internalLiquiditySwap(bytes4 curr) external { 11 | p2.internalLiquiditySwap(curr); 12 | } 13 | 14 | function mint(address _to, uint _amount) external { 15 | tc.mint(_to, _amount); 16 | } 17 | 18 | function burnFrom(address _from, uint _amount) external { 19 | tc.burnFrom(_from, _amount); 20 | } 21 | 22 | function setpendingClaimStart(uint _start) external { 23 | ClaimsData cd = ClaimsData(ms.getLatestAddress("CD")); 24 | cd.setpendingClaimStart(_start); 25 | } 26 | 27 | function updateStakerCommissions(address _scAddress, uint _premiumNXM) external { 28 | TokenFunctions tf = TokenFunctions(ms.getLatestAddress("TF")); 29 | tf.updateStakerCommissions(_scAddress, _premiumNXM); 30 | } 31 | 32 | function burnStakerLockedToken(uint coverid, bytes4 curr, uint sumAssured) external { 33 | TokenFunctions tf = TokenFunctions(ms.getLatestAddress("TF")); 34 | tf.burnStakerLockedToken(coverid, curr, sumAssured); 35 | } 36 | 37 | function depositCN(uint coverId) public { 38 | TokenFunctions tf = TokenFunctions(ms.getLatestAddress("TF")); 39 | tf.depositCN(coverId); 40 | } 41 | 42 | function transferFundToOtherAdd(address payable _add, uint amt) public { 43 | 44 | _add.transfer(amt); 45 | 46 | } 47 | 48 | function upgradeInvestmentPool(address payable newPoolAddress) public { 49 | p2.upgradeInvestmentPool(newPoolAddress); 50 | } 51 | 52 | function transferCurrencyAssetToAddress(bytes4 _curr, address payable _address, uint _amount) 53 | public returns(bool succ) { 54 | if (_curr == "ETH") { 55 | if (address(this).balance < _amount) 56 | _amount = address(this).balance; 57 | _address.transfer(_amount); 58 | succ = true; 59 | } else { 60 | IERC20 erc20 = IERC20(pd.getCurrencyAssetAddress(_curr)); //solhint-disable-line 61 | if (erc20.balanceOf(address(this)) < _amount) 62 | _amount = erc20.balanceOf(address(this)); 63 | require(erc20.transfer(address(_address), _amount)); 64 | succ = true; 65 | 66 | } 67 | } 68 | 69 | function _oraclizeQuery ( 70 | uint paramCount, 71 | uint timestamp, 72 | string memory datasource, 73 | string memory arg, 74 | uint gasLimit 75 | ) 76 | internal 77 | 78 | returns (bytes32) 79 | { 80 | // To silence compiler warning :( 81 | return bytes32(keccak256( 82 | abi.encodePacked( 83 | paramCount, 84 | timestamp, 85 | datasource, 86 | arg, 87 | gasLimit, 88 | now 89 | ) 90 | )); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /contracts/external/govblocks-protocol/interfaces/IMemberRoles.sol: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 GovBlocks.io 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | This program is distributed in the hope that it will be useful, 7 | but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | GNU General Public License for more details. 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see http://www.gnu.org/licenses/ */ 12 | 13 | pragma solidity 0.5.7; 14 | 15 | 16 | contract IMemberRoles { 17 | 18 | event MemberRole(uint256 indexed roleId, bytes32 roleName, string roleDescription); 19 | 20 | /// @dev Adds new member role 21 | /// @param _roleName New role name 22 | /// @param _roleDescription New description hash 23 | /// @param _authorized Authorized member against every role id 24 | function addRole(bytes32 _roleName, string memory _roleDescription, address _authorized) public; 25 | 26 | /// @dev Assign or Delete a member from specific role. 27 | /// @param _memberAddress Address of Member 28 | /// @param _roleId RoleId to update 29 | /// @param _active active is set to be True if we want to assign this role to member, False otherwise! 30 | function updateRole(address _memberAddress, uint _roleId, bool _active) public; 31 | 32 | /// @dev Change Member Address who holds the authority to Add/Delete any member from specific role. 33 | /// @param _roleId roleId to update its Authorized Address 34 | /// @param _authorized New authorized address against role id 35 | function changeAuthorized(uint _roleId, address _authorized) public; 36 | 37 | /// @dev Return number of member roles 38 | function totalRoles() public view returns(uint256); 39 | 40 | /// @dev Gets the member addresses assigned by a specific role 41 | /// @param _memberRoleId Member role id 42 | /// @return roleId Role id 43 | /// @return allMemberAddress Member addresses of specified role id 44 | function members(uint _memberRoleId) public view returns(uint, address[] memory allMemberAddress); 45 | 46 | /// @dev Gets all members' length 47 | /// @param _memberRoleId Member role id 48 | /// @return memberRoleData[_memberRoleId].memberAddress.length Member length 49 | function numberOfMembers(uint _memberRoleId) public view returns(uint); 50 | 51 | /// @dev Return member address who holds the right to add/remove any member from specific role. 52 | function authorized(uint _memberRoleId) public view returns(address); 53 | 54 | /// @dev Get All role ids array that has been assigned to a member so far. 55 | function roles(address _memberAddress) public view returns(uint[] memory assignedRoles); 56 | 57 | /// @dev Returns true if the given role id is assigned to a member. 58 | /// @param _memberAddress Address of member 59 | /// @param _roleId Checks member's authenticity with the roleId. 60 | /// i.e. Returns true if this roleId is assigned to member 61 | function checkRole(address _memberAddress, uint _roleId) public view returns(bool); 62 | } -------------------------------------------------------------------------------- /contracts/mocks/TokenFunctionMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | import "./Pool1Mock.sol"; 4 | import "../ClaimsData.sol"; 5 | 6 | 7 | contract TokenFunctionMock is TokenFunctions { 8 | 9 | /** 10 | * @dev Burns tokens staked against a Smart Contract Cover. 11 | * Called when a claim submitted against this cover is accepted. 12 | */ 13 | function burnStakerLockedToken(address scAddress, uint burnNXMAmount) external { 14 | uint totalStaker = td.getStakedContractStakersLength(scAddress); 15 | address stakerAddress; 16 | uint stakerStakedNXM; 17 | uint toBurn = burnNXMAmount; 18 | for (uint i = td.stakedContractCurrentBurnIndex(scAddress); i < totalStaker; i++) { 19 | if (toBurn > 0) { 20 | stakerAddress = td.getStakedContractStakerByIndex(scAddress, i); 21 | uint stakerIndex = td.getStakedContractStakerIndex( 22 | scAddress, i); 23 | uint v; 24 | (v, stakerStakedNXM) = _unlockableBeforeBurningAndCanBurn(stakerAddress, scAddress, stakerIndex); 25 | td.pushUnlockableBeforeLastBurnTokens(stakerAddress, stakerIndex, v); 26 | // stakerStakedNXM = _getStakerStakedTokensOnSmartContract(stakerAddress, scAddress, i); 27 | if (stakerStakedNXM > 0) { 28 | if (stakerStakedNXM >= toBurn) { 29 | _burnStakerTokenLockedAgainstSmartContract( 30 | stakerAddress, scAddress, i, toBurn); 31 | if (i > 0) 32 | td.setStakedContractCurrentBurnIndex(scAddress, i); 33 | toBurn = 0; 34 | break; 35 | } else { 36 | _burnStakerTokenLockedAgainstSmartContract( 37 | stakerAddress, scAddress, i, stakerStakedNXM); 38 | toBurn = toBurn.sub(stakerStakedNXM); 39 | } 40 | } 41 | } else 42 | break; 43 | } 44 | if (toBurn > 0 && totalStaker > 0) 45 | td.setStakedContractCurrentBurnIndex(scAddress, totalStaker.sub(1)); 46 | } 47 | 48 | function mint(address _member, uint _amount) external { 49 | tc.mint(_member, _amount); 50 | } 51 | 52 | function burnFrom(address _of, uint amount) external { 53 | tc.burnFrom(_of, amount); 54 | } 55 | 56 | function reduceLock(address _of, bytes32 _reason, uint256 _time) external { 57 | tc.reduceLock(_of, _reason, _time); 58 | } 59 | 60 | function burnLockedTokens(address _of, bytes32 _reason, uint256 _amount) external { 61 | tc.burnLockedTokens(_of, _reason, _amount); 62 | } 63 | 64 | function releaseLockedTokens(address _of, bytes32 _reason, uint256 _amount) 65 | external 66 | 67 | { 68 | tc.releaseLockedTokens(_of, _reason, _amount); 69 | } 70 | 71 | function upgradeCapitalPool(address payable newPoolAddress) external { 72 | Pool1 p1 = Pool1(ms.getLatestAddress("P1")); 73 | p1.upgradeCapitalPool(newPoolAddress); 74 | } 75 | 76 | function setClaimSubmittedAtEPTrue(uint _index, bool _submit) external { 77 | ClaimsData cd = ClaimsData(ms.getLatestAddress("CD")); 78 | cd.setClaimSubmittedAtEPTrue(_index, _submit); 79 | } 80 | 81 | function transferCurrencyAsset( 82 | bytes4 _curr, 83 | address payable _address, 84 | uint _amount 85 | ) 86 | public 87 | returns(bool) 88 | { 89 | Pool1Mock p1 = Pool1Mock(ms.getLatestAddress("P1")); 90 | 91 | return p1.transferCurrencyAssetToAddress(_curr, _address, _amount); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /test/utils/getQuote.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var BN = require('bn.js'); 3 | var ethABI = require('ethereumjs-abi'); 4 | var util = require('ethereumjs-util'); 5 | const wallet = require('eth-lightwallet').keystore; 6 | var lightwallet = require('eth-lightwallet'); 7 | async function getQuoteValues(...args) { 8 | var order = { 9 | amount: args[0][0], 10 | curr: args[1], 11 | CP: args[2], 12 | smartCA: args[3], 13 | Price: args[0][1], 14 | price_nxm: args[0][2], 15 | expire: args[0][3], 16 | generationTime: args[0][4], 17 | quotationContract: args[4] 18 | }; 19 | var orderParts = [ 20 | { value: bigNumberToBN(order.amount), type: 'uint' }, 21 | { value: order.curr, type: 'bytes4' }, 22 | { value: bigNumberToBN(order.CP), type: 'uint16' }, 23 | { value: order.smartCA, type: 'address' }, 24 | { value: bigNumberToBN(order.Price), type: 'uint' }, 25 | { value: bigNumberToBN(order.price_nxm), type: 'uint' }, 26 | { value: bigNumberToBN(order.expire), type: 'uint' }, 27 | { value: bigNumberToBN(order.generationTime), type: 'uint' }, 28 | { value: order.quotationContract, type: 'address' } 29 | ]; 30 | 31 | var types = _.map(orderParts, function(o) { 32 | return o.type; 33 | }); 34 | var values = _.map(orderParts, function(o) { 35 | return o.value; 36 | }); 37 | var hashBuff = ethABI.soliditySHA3(types, values); 38 | var hashHex = util.bufferToHex(hashBuff); 39 | 40 | var ks = { 41 | salt: 'mDSSGi5eePbk3dBG4Ddk79f/Jwi5h3d0jI52F3M3yRg=', 42 | hdPathString: "m/44'/60'/0'/0", 43 | encSeed: { 44 | encStr: 45 | '1yzRGJIM7QTLqHzop5H96Txqpy/4P7DlgkPyPDzY9MsmmX6rT0M/4qNnNDX+wTY/NhZnFT84M6wZ8r8keBa/atNo81Xu84bNSRNk4b+W+9/69rcF3fNilP4GtxXE1X5WQhO7m6xeXDgGguQC9YdErDISAwvsSST8sVYhGkmmEtrp7GhE4xmeTA==', 46 | nonce: 'jsdTS0xT7ijtSljsgZabpktsZtNC633V' 47 | }, 48 | encHdRootPriv: { 49 | encStr: 50 | 'OXi2S5Fka6y4TG894bsagLcIPzfbwZlpq+ZTHjufbfaHccQmHnwEZDyjspTarf/OVc/nRI/qT1lOe68k+7bXSO8BTbnGxLorqYr9Qm+ImCaeexCRMYOdK9/Anm+2Aa2gLnjtlgBEf8dIEaWI8LoQhCKeJYSAFggXysoM31wYNQ==', 51 | nonce: 'XsKB+uXOmeSWzhN/XPQXTvru2Aa6pnob' 52 | }, 53 | version: 3, 54 | hdIndex: 1, 55 | encPrivKeys: { 56 | '51042c4d8936a7764d18370a6a0762b860bb8e07': { 57 | key: 'hGPlIoOYX9PKV0CyHHfC1EKYIibpeLKDkEUfWGWqBz25c9yVIk4TCZvMmkzgEMqD', 58 | nonce: 'cDHUhUEaqkJ6OwB4BPJXi5Vw47tbvFYo' 59 | } 60 | }, 61 | addresses: ['51042c4d8936a7764d18370a6a0762b860bb8e07'] 62 | }; 63 | ks = wallet.deserialize(JSON.stringify(ks)); 64 | var pwDerivedKey; 65 | pwDerivedKey = new Uint8Array([ 66 | 51, 67 | 95, 68 | 185, 69 | 86, 70 | 44, 71 | 101, 72 | 34, 73 | 239, 74 | 87, 75 | 233, 76 | 60, 77 | 63, 78 | 119, 79 | 227, 80 | 100, 81 | 242, 82 | 44, 83 | 242, 84 | 130, 85 | 145, 86 | 0, 87 | 32, 88 | 103, 89 | 29, 90 | 142, 91 | 236, 92 | 147, 93 | 33, 94 | 254, 95 | 230, 96 | 9, 97 | 225 98 | ]); 99 | 100 | const orderHashBuff = util.toBuffer(hashHex); 101 | const msgHashBuff = util.hashPersonalMessage(orderHashBuff); 102 | const sig = lightwallet.signing.signMsgHash( 103 | ks, 104 | pwDerivedKey, 105 | msgHashBuff, 106 | ks.addresses[0] 107 | ); 108 | var vrsData = []; 109 | vrsData.push(sig.v); 110 | vrsData.push('0x' + util.toUnsigned(util.fromSigned(sig.r)).toString('hex')); 111 | vrsData.push('0x' + util.toUnsigned(util.fromSigned(sig.s)).toString('hex')); 112 | return vrsData; 113 | } 114 | 115 | function bigNumberToBN(value) { 116 | return new BN(value.toString(), 10); 117 | } 118 | 119 | module.exports = { getQuoteValues }; 120 | -------------------------------------------------------------------------------- /migrations/4_initialize_nexusmutual.js: -------------------------------------------------------------------------------- 1 | const Claims = artifacts.require('Claims'); 2 | const ClaimsData = artifacts.require('ClaimsDataMock'); 3 | const ClaimsReward = artifacts.require('ClaimsReward'); 4 | const DAI = artifacts.require('MockDAI'); 5 | const DSValue = artifacts.require('DSValueMock'); 6 | const NXMaster = artifacts.require('NXMaster'); 7 | const MCR = artifacts.require('MCR'); 8 | const NXMToken = artifacts.require('NXMToken'); 9 | const TokenFunctions = artifacts.require('TokenFunctionMock'); 10 | const TokenController = artifacts.require('TokenController'); 11 | const TokenData = artifacts.require('TokenDataMock'); 12 | const Pool1 = artifacts.require('Pool1Mock'); 13 | const Pool2 = artifacts.require('Pool2'); 14 | const PoolData = artifacts.require('PoolDataMock'); 15 | const Quotation = artifacts.require('Quotation'); 16 | const QuotationDataMock = artifacts.require('QuotationDataMock'); 17 | const MemberRoles = artifacts.require('MemberRoles'); 18 | const Governance = artifacts.require('Governance'); 19 | const ProposalCategory = artifacts.require('ProposalCategory'); 20 | const FactoryMock = artifacts.require('FactoryMock'); 21 | 22 | const QE = '0x51042c4d8936a7764d18370a6a0762b860bb8e07'; 23 | const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; 24 | const POOL_ETHER = '3500000000000000000000'; 25 | const POOL_ASSET = '50000000000000000000'; 26 | 27 | module.exports = function(deployer, network, accounts) { 28 | deployer.then(async () => { 29 | const Owner = accounts[0]; 30 | const nxms = await NXMaster.deployed(); 31 | const tk = await NXMToken.deployed(); 32 | const td = await TokenData.deployed(); 33 | const tf = await TokenFunctions.deployed(); 34 | const tc = await TokenController.deployed(); 35 | const pl1 = await Pool1.deployed(); 36 | const pl2 = await Pool2.deployed(); 37 | const pd = await PoolData.deployed(); 38 | const qt = await Quotation.deployed(); 39 | const qd = await QuotationDataMock.deployed(); 40 | const cl = await Claims.deployed(); 41 | const cr = await ClaimsReward.deployed(); 42 | const cd = await ClaimsData.deployed(); 43 | const mcr = await MCR.deployed(); 44 | const dsv = await DSValue.deployed(); 45 | const gov = await Governance.deployed(); 46 | let propCat = await ProposalCategory.deployed(); 47 | const mr = await MemberRoles.deployed(); 48 | const factory = await FactoryMock.deployed(); 49 | // let gvAdd = await nxms.getLatestAddress("GV"); 50 | // let mrAdd = await nxms.getLatestAddress("MR"); 51 | // let pcAdd = await nxms.getLatestAddress("PC"); 52 | let addr = [ 53 | qd.address, 54 | td.address, 55 | cd.address, 56 | pd.address, 57 | qt.address, 58 | tf.address, 59 | tc.address, 60 | cl.address, 61 | cr.address, 62 | pl1.address, 63 | pl2.address, 64 | mcr.address, 65 | gov.address, 66 | propCat.address, 67 | mr.address 68 | ]; 69 | await nxms.addNewVersion(addr); 70 | let pcAddress = await nxms.getLatestAddress('0x5043'); 71 | pc = await ProposalCategory.at(pcAddress); 72 | await pc.proposalCategoryInitiate(); 73 | const dai = await DAI.deployed(); 74 | // await qd.changeCurrencyAssetAddress('0x444149', dai.address); 75 | // await qd.changeInvestmentAssetAddress('0x444149', dai.address); 76 | await pl1.sendEther({ from: Owner, value: POOL_ETHER }); 77 | await pl2.sendEther({ from: Owner, value: POOL_ETHER }); // 78 | await mcr.addMCRData( 79 | 13000, 80 | '100000000000000000000', 81 | '7000000000000000000000', 82 | ['0x455448', '0x444149'], 83 | [100, 15517], 84 | 20190103 85 | ); 86 | await pl2.saveIADetails( 87 | ['0x455448', '0x444149'], 88 | [100, 15517], 89 | 20190103, 90 | true 91 | ); //testing 92 | await dai.transfer(pl2.address, POOL_ASSET); 93 | let mrInstance = await MemberRoles.at( 94 | await nxms.getLatestAddress('0x4d52') 95 | ); 96 | await mrInstance.payJoiningFee(Owner, { 97 | from: Owner, 98 | value: '2000000000000000' 99 | }); 100 | await mrInstance.kycVerdict(Owner, true); 101 | await mrInstance.addInitialABMembers([Owner]); 102 | }); 103 | }; 104 | -------------------------------------------------------------------------------- /contracts/external/ERC1132/IERC1132.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | /** 4 | * @title ERC1132 interface 5 | * @dev see https://github.com/ethereum/EIPs/issues/1132 6 | */ 7 | 8 | contract IERC1132 { 9 | /** 10 | * @dev Reasons why a user's tokens have been locked 11 | */ 12 | mapping(address => bytes32[]) public lockReason; 13 | 14 | /** 15 | * @dev locked token structure 16 | */ 17 | struct LockToken { 18 | uint256 amount; 19 | uint256 validity; 20 | bool claimed; 21 | } 22 | 23 | /** 24 | * @dev Holds number & validity of tokens locked for a given reason for 25 | * a specified address 26 | */ 27 | mapping(address => mapping(bytes32 => LockToken)) public locked; 28 | 29 | /** 30 | * @dev Records data of all the tokens Locked 31 | */ 32 | event Locked( 33 | address indexed _of, 34 | bytes32 indexed _reason, 35 | uint256 _amount, 36 | uint256 _validity 37 | ); 38 | 39 | /** 40 | * @dev Records data of all the tokens unlocked 41 | */ 42 | event Unlocked( 43 | address indexed _of, 44 | bytes32 indexed _reason, 45 | uint256 _amount 46 | ); 47 | 48 | /** 49 | * @dev Locks a specified amount of tokens against an address, 50 | * for a specified reason and time 51 | * @param _reason The reason to lock tokens 52 | * @param _amount Number of tokens to be locked 53 | * @param _time Lock time in seconds 54 | */ 55 | function lock(bytes32 _reason, uint256 _amount, uint256 _time) 56 | public returns (bool); 57 | 58 | /** 59 | * @dev Returns tokens locked for a specified address for a 60 | * specified reason 61 | * 62 | * @param _of The address whose tokens are locked 63 | * @param _reason The reason to query the lock tokens for 64 | */ 65 | function tokensLocked(address _of, bytes32 _reason) 66 | public view returns (uint256 amount); 67 | 68 | /** 69 | * @dev Returns tokens locked for a specified address for a 70 | * specified reason at a specific time 71 | * 72 | * @param _of The address whose tokens are locked 73 | * @param _reason The reason to query the lock tokens for 74 | * @param _time The timestamp to query the lock tokens for 75 | */ 76 | function tokensLockedAtTime(address _of, bytes32 _reason, uint256 _time) 77 | public view returns (uint256 amount); 78 | 79 | /** 80 | * @dev Returns total tokens held by an address (locked + transferable) 81 | * @param _of The address to query the total balance of 82 | */ 83 | function totalBalanceOf(address _of) 84 | public view returns (uint256 amount); 85 | 86 | /** 87 | * @dev Extends lock for a specified reason and time 88 | * @param _reason The reason to lock tokens 89 | * @param _time Lock extension time in seconds 90 | */ 91 | function extendLock(bytes32 _reason, uint256 _time) 92 | public returns (bool); 93 | 94 | /** 95 | * @dev Increase number of tokens locked for a specified reason 96 | * @param _reason The reason to lock tokens 97 | * @param _amount Number of tokens to be increased 98 | */ 99 | function increaseLockAmount(bytes32 _reason, uint256 _amount) 100 | public returns (bool); 101 | 102 | /** 103 | * @dev Returns unlockable tokens for a specified address for a specified reason 104 | * @param _of The address to query the the unlockable token count of 105 | * @param _reason The reason to query the unlockable tokens for 106 | */ 107 | function tokensUnlockable(address _of, bytes32 _reason) 108 | public view returns (uint256 amount); 109 | 110 | /** 111 | * @dev Unlocks the unlockable tokens of a specified address 112 | * @param _of Address of user, claiming back unlockable tokens 113 | */ 114 | function unlock(address _of) 115 | public returns (uint256 unlockableTokens); 116 | 117 | /** 118 | * @dev Gets the unlockable tokens of a specified address 119 | * @param _of The address to query the the unlockable token count of 120 | */ 121 | function getUnlockableTokens(address _of) 122 | public view returns (uint256 unlockableTokens); 123 | 124 | } -------------------------------------------------------------------------------- /contracts/external/govblocks-protocol/interfaces/IProposalCategory.sol: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 GovBlocks.io 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | This program is distributed in the hope that it will be useful, 7 | but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | GNU General Public License for more details. 10 | You should have received a copy of the GNU General Public License 11 | along with this program. If not, see http://www.gnu.org/licenses/ */ 12 | 13 | pragma solidity 0.5.7; 14 | 15 | 16 | contract IProposalCategory { 17 | 18 | event Category( 19 | uint indexed categoryId, 20 | string categoryName, 21 | string actionHash 22 | ); 23 | 24 | /// @dev Adds new category 25 | /// @param _name Category name 26 | /// @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed. 27 | /// @param _allowedToCreateProposal Member roles allowed to create the proposal 28 | /// @param _majorityVotePerc Majority Vote threshold for Each voting layer 29 | /// @param _quorumPerc minimum threshold percentage required in voting to calculate result 30 | /// @param _closingTime Vote closing time for Each voting layer 31 | /// @param _actionHash hash of details containing the action that has to be performed after proposal is accepted 32 | /// @param _contractAddress address of contract to call after proposal is accepted 33 | /// @param _contractName name of contract to be called after proposal is accepted 34 | /// @param _incentives rewards to distributed after proposal is accepted 35 | function addCategory( 36 | string calldata _name, 37 | uint _memberRoleToVote, 38 | uint _majorityVotePerc, 39 | uint _quorumPerc, 40 | uint[] calldata _allowedToCreateProposal, 41 | uint _closingTime, 42 | string calldata _actionHash, 43 | address _contractAddress, 44 | bytes2 _contractName, 45 | uint[] calldata _incentives 46 | ) 47 | external; 48 | 49 | /// @dev gets category details 50 | function category(uint _categoryId) 51 | external 52 | view 53 | returns( 54 | uint categoryId, 55 | uint memberRoleToVote, 56 | uint majorityVotePerc, 57 | uint quorumPerc, 58 | uint[] memory allowedToCreateProposal, 59 | uint closingTime, 60 | uint minStake 61 | ); 62 | 63 | ///@dev gets category action details 64 | function categoryAction(uint _categoryId) 65 | external 66 | view 67 | returns( 68 | uint categoryId, 69 | address contractAddress, 70 | bytes2 contractName, 71 | uint defaultIncentive 72 | ); 73 | 74 | /// @dev Gets Total number of categories added till now 75 | function totalCategories() external view returns(uint numberOfCategories); 76 | 77 | /// @dev Updates category details 78 | /// @param _categoryId Category id that needs to be updated 79 | /// @param _name Category name 80 | /// @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed. 81 | /// @param _allowedToCreateProposal Member roles allowed to create the proposal 82 | /// @param _majorityVotePerc Majority Vote threshold for Each voting layer 83 | /// @param _quorumPerc minimum threshold percentage required in voting to calculate result 84 | /// @param _closingTime Vote closing time for Each voting layer 85 | /// @param _actionHash hash of details containing the action that has to be performed after proposal is accepted 86 | /// @param _contractAddress address of contract to call after proposal is accepted 87 | /// @param _contractName name of contract to be called after proposal is accepted 88 | /// @param _incentives rewards to distributed after proposal is accepted 89 | function updateCategory( 90 | uint _categoryId, 91 | string memory _name, 92 | uint _memberRoleToVote, 93 | uint _majorityVotePerc, 94 | uint _quorumPerc, 95 | uint[] memory _allowedToCreateProposal, 96 | uint _closingTime, 97 | string memory _actionHash, 98 | address _contractAddress, 99 | bytes2 _contractName, 100 | uint[] memory _incentives 101 | ) 102 | public; 103 | 104 | } -------------------------------------------------------------------------------- /test/20_TokenModule.test.js: -------------------------------------------------------------------------------- 1 | const NXMToken = artifacts.require('NXMToken'); 2 | const TokenController = artifacts.require('TokenController'); 3 | const Pool1 = artifacts.require('Pool1Mock'); 4 | const Pool2 = artifacts.require('Pool2'); 5 | const MemberRoles = artifacts.require('MemberRoles'); 6 | const NXMaster = artifacts.require('NXMaster'); 7 | const TokenData = artifacts.require('TokenDataMock'); 8 | const TokenFunctions = artifacts.require('TokenFunctionMock'); 9 | const DAI = artifacts.require('MockDAI'); 10 | const { ether, toHex, toWei } = require('./utils/ethTools'); 11 | const { assertRevert } = require('./utils/assertRevert'); 12 | const { increaseTimeTo } = require('./utils/increaseTime'); 13 | const { latestTime } = require('./utils/latestTime'); 14 | const expectEvent = require('./utils/expectEvent'); 15 | const { advanceBlock } = require('./utils/advanceToBlock'); 16 | 17 | const ETH = '0x455448'; 18 | const fee = ether(0.002); 19 | let ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; 20 | 21 | let dai; 22 | let tk; 23 | let tc; 24 | let p1; 25 | let p2; 26 | let mr; 27 | let nxms; 28 | let td; 29 | let tf; 30 | const BN = web3.utils.BN; 31 | 32 | const BigNumber = web3.BigNumber; 33 | require('chai') 34 | .use(require('chai-bignumber')(BigNumber)) 35 | .should(); 36 | 37 | contract('Token Module', function([owner, member1]) { 38 | const UNLIMITED_ALLOWANCE = 39 | '115792089237316195423570985008687907853269984665640564039457584007913129639935'; 40 | before(async function() { 41 | await advanceBlock(); 42 | tk = await NXMToken.deployed(); 43 | p1 = await Pool1.deployed(); 44 | p2 = await Pool2.deployed(); 45 | nxms = await NXMaster.deployed(); 46 | tf = await TokenFunctions.deployed(); 47 | mr = await MemberRoles.at(await nxms.getLatestAddress('0x4d52')); 48 | td = await TokenData.deployed(); 49 | tc = await TokenController.at(await nxms.getLatestAddress(toHex('TC'))); 50 | dai = await DAI.deployed(); 51 | await mr.addMembersBeforeLaunch([], []); 52 | (await mr.launched()).should.be.equal(true); 53 | await tf.upgradeCapitalPool(dai.address); 54 | await p1.sendEther({ from: owner, value: toWei(50) }); 55 | await p1.upgradeInvestmentPool(dai.address); 56 | await mr.payJoiningFee(member1, { from: member1, value: fee }); 57 | await mr.kycVerdict(member1, true); 58 | // await tk.approve(tc.address, UNLIMITED_ALLOWANCE, { from: member1 }); 59 | await tk.transfer(member1, toWei(30000), { from: owner }); 60 | // console.log(await tk.allowance(owner, tc.address)); 61 | await tk.approve(tc.address, UNLIMITED_ALLOWANCE, { from: owner }); 62 | }); 63 | describe('NXMToken: ', function() { 64 | it('20.1 onlyOperator "require" operator - else condition', async function() { 65 | await assertRevert(tk.mint(owner, 1)); // tc.mint is changed to tk.mint hence it needs to assertRevert 66 | }); 67 | 68 | it('20.2 approve function "require" - else ZERO_ADDRESS condition is checked', async function() { 69 | await assertRevert( 70 | tk.approve(ZERO_ADDRESS, UNLIMITED_ALLOWANCE, { from: member1 }) 71 | ); 72 | }); 73 | 74 | it('20.3 decreaseAllowance function is called, ZERO_ADDRESS is also checked', async function() { 75 | await tk.decreaseAllowance(tc.address, UNLIMITED_ALLOWANCE, { 76 | from: owner 77 | }); 78 | await assertRevert( 79 | tk.decreaseAllowance(ZERO_ADDRESS, UNLIMITED_ALLOWANCE, { 80 | from: owner 81 | }) 82 | ); 83 | }); 84 | 85 | it('20.4 increaseAllowance function is called, ZERO_ADDRESS is also checked', async function() { 86 | await assertRevert( 87 | tk.increaseAllowance(ZERO_ADDRESS, UNLIMITED_ALLOWANCE, { 88 | from: owner 89 | }) 90 | ); 91 | await tk.increaseAllowance(tc.address, UNLIMITED_ALLOWANCE, { 92 | from: owner 93 | }); 94 | }); 95 | 96 | it('20.5 transfer function "require" - else conditions are checked', async function() { 97 | // to check that transfer is not made to ZERO_ADDRESS 98 | await assertRevert( 99 | tk.transfer(ZERO_ADDRESS, toWei(30000), { from: owner }) 100 | ); 101 | 102 | // to check that owner is not locked for MV 103 | // await tc.lockForMemberVote(owner, 2); // lock the owner, so that it cannot transfer 104 | // await assertRevert(tk.transfer(member1, towei(30000), { from: owner })); 105 | }); 106 | 107 | it('20.6 _mint function "require" - else ZERO_ADDRESS condition is checked', async function() { 108 | await assertRevert(tf.mint(ZERO_ADDRESS, 1)); 109 | }); 110 | 111 | it('20.7 should not be able to burn more than user balance', async function() { 112 | await assertRevert( 113 | tf.burnFrom(member1, (await tk.balanceOf(member1)).toString()) 114 | ); 115 | }); 116 | 117 | it('20.8 should not be able to reduce lock if no locked tokens', async function() { 118 | await assertRevert( 119 | tf.reduceLock(member1, toHex('random'), await latestTime()) 120 | ); 121 | }); 122 | 123 | it('20.9 should not be able to burn if no locked tokens', async function() { 124 | await assertRevert( 125 | tf.burnLockedTokens(member1, toHex('random'), toWei(10)) 126 | ); 127 | }); 128 | 129 | it('20.10 should not be able to release tokens more than he have locked', async function() { 130 | await assertRevert( 131 | tf.releaseLockedTokens(member1, toHex('random'), toWei(10)) 132 | ); 133 | }); 134 | }); 135 | }); 136 | -------------------------------------------------------------------------------- /test/05_Staking.test.js: -------------------------------------------------------------------------------- 1 | const NXMToken = artifacts.require('NXMToken'); 2 | const TokenFunctions = artifacts.require('TokenFunctionMock'); 3 | const TokenController = artifacts.require('TokenController'); 4 | const TokenData = artifacts.require('TokenDataMock'); 5 | const Pool1 = artifacts.require('Pool1Mock'); 6 | const MemberRoles = artifacts.require('MemberRoles'); 7 | const NXMaster = artifacts.require('NXMaster'); 8 | const ClaimsReward = artifacts.require('ClaimsReward'); 9 | 10 | const { assertRevert } = require('./utils/assertRevert'); 11 | const { advanceBlock } = require('./utils/advanceToBlock'); 12 | const { ether, toHex, toWei } = require('./utils/ethTools'); 13 | const { increaseTimeTo, duration } = require('./utils/increaseTime'); 14 | const { latestTime } = require('./utils/latestTime'); 15 | 16 | const stakedContract = '0xd0a6e6c54dbc68db5db3a091b171a77407ff7ccf'; 17 | 18 | let tk; 19 | let tf; 20 | let tc; 21 | let td; 22 | let P1; 23 | let mr; 24 | let nxms; 25 | let cr; 26 | const BN = web3.utils.BN; 27 | 28 | const BigNumber = web3.BigNumber; 29 | require('chai') 30 | .use(require('chai-bignumber')(BigNumber)) 31 | .should(); 32 | 33 | contract('NXMToken:Staking', function([owner, member1, member2, notMember]) { 34 | const fee = ether(0.002); 35 | const stakeTokens = ether(5); 36 | const tokens = ether(200); 37 | const UNLIMITED_ALLOWANCE = new BN((2).toString()) 38 | .pow(new BN((256).toString())) 39 | .sub(new BN((1).toString())); 40 | before(async function() { 41 | await advanceBlock(); 42 | P1 = await Pool1.deployed(); 43 | tk = await NXMToken.deployed(); 44 | tf = await TokenFunctions.deployed(); 45 | td = await TokenData.deployed(); 46 | nxms = await NXMaster.deployed(); 47 | tc = await TokenController.at(await nxms.getLatestAddress(toHex('TC'))); 48 | mr = await MemberRoles.at(await nxms.getLatestAddress('0x4d52')); 49 | cr = await ClaimsReward.deployed(); 50 | await mr.addMembersBeforeLaunch([], []); 51 | (await mr.launched()).should.be.equal(true); 52 | await mr.payJoiningFee(member1, { from: member1, value: fee }); 53 | await mr.kycVerdict(member1, true); 54 | await tk.approve(tc.address, UNLIMITED_ALLOWANCE, { from: member1 }); 55 | await mr.payJoiningFee(member2, { from: member2, value: fee }); 56 | await mr.kycVerdict(member2, true); 57 | await tk.approve(tc.address, UNLIMITED_ALLOWANCE, { from: member2 }); 58 | await tk.transfer(member1, tokens); 59 | await tk.transfer(member2, tokens); 60 | }); 61 | 62 | describe('Stake Tokens', function() { 63 | describe('Staker is not member', function() { 64 | it('5.1 reverts', async function() { 65 | await assertRevert( 66 | tf.addStake(stakedContract, stakeTokens, { from: notMember }) 67 | ); 68 | }); 69 | }); 70 | describe('Staker is member', function() { 71 | describe('Staker does not have enough tokens', function() { 72 | it('5.2 reverts', async function() { 73 | await assertRevert( 74 | tf.addStake( 75 | stakedContract, 76 | new BN(stakeTokens.toString()).add( 77 | new BN(toWei(1000000).toString()) 78 | ), 79 | { 80 | from: member1 81 | } 82 | ) 83 | ); 84 | }); 85 | }); 86 | 87 | describe('Staker does have enough tokens', function() { 88 | let initialTokenBalance; 89 | let initialStakedTokens; 90 | it('5.3 should have zero staked tokens before', async function() { 91 | initialTokenBalance = await tk.balanceOf(member1); 92 | initialStakedTokens = await tf.getStakerAllLockedTokens.call(member1); 93 | initialStakedTokens.toString().should.be.equal((0).toString()); 94 | }); 95 | 96 | it('5.4 should be able to add stake on Smart Contracts', async function() { 97 | await tf.addStake(stakedContract, stakeTokens, { from: member1 }); 98 | const newStakedTokens = new BN(initialStakedTokens.toString()).add( 99 | new BN(stakeTokens.toString()) 100 | ); 101 | newStakedTokens 102 | .toString() 103 | .should.be.equal( 104 | (await tf.getStakerAllLockedTokens.call(member1)).toString() 105 | ); 106 | }); 107 | it('5.5 should decrease balance of member', async function() { 108 | const newTokenBalance = new BN(initialTokenBalance.toString()).sub( 109 | new BN(stakeTokens.toString()) 110 | ); 111 | newTokenBalance 112 | .toString() 113 | .should.be.equal((await tk.balanceOf(member1)).toString()); 114 | }); 115 | it('5.6 should return zero stake amt for non staker', async function() { 116 | initialStakedTokens = await tf.getStakerAllLockedTokens.call(member2); 117 | (await tf.getStakerAllLockedTokens.call(member2)) 118 | .toString() 119 | .should.be.equal(initialStakedTokens.toString()); 120 | }); 121 | describe('after 250 days', function() { 122 | before(async function() { 123 | await tf.addStake(member2, stakeTokens, { from: member2 }); 124 | let time = await latestTime(); 125 | time = time + (await duration.days(251)); 126 | await increaseTimeTo(time); 127 | await cr.claimAllPendingReward(20, { from: member2 }); 128 | }); 129 | it('5.7 staker should have zero total locked nxm tokens against smart contract', async function() { 130 | const lockedTokens = await tf.getStakerAllLockedTokens.call( 131 | member2 132 | ); 133 | lockedTokens.toString().should.be.equal((0).toString()); 134 | }); 135 | }); 136 | }); 137 | }); 138 | }); 139 | //contract block 140 | }); 141 | -------------------------------------------------------------------------------- /contracts/external/govblocks-protocol/interfaces/IGovernance.sol: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2017 GovBlocks.io 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program. If not, see http://www.gnu.org/licenses/ */ 15 | 16 | pragma solidity 0.5.7; 17 | 18 | 19 | contract IGovernance { 20 | 21 | event Proposal( 22 | address indexed proposalOwner, 23 | uint256 indexed proposalId, 24 | uint256 dateAdd, 25 | string proposalTitle, 26 | string proposalSD, 27 | string proposalDescHash 28 | ); 29 | 30 | event Solution( 31 | uint256 indexed proposalId, 32 | address indexed solutionOwner, 33 | uint256 indexed solutionId, 34 | string solutionDescHash, 35 | uint256 dateAdd 36 | ); 37 | 38 | event Vote( 39 | address indexed from, 40 | uint256 indexed proposalId, 41 | uint256 indexed voteId, 42 | uint256 dateAdd, 43 | uint256 solutionChosen 44 | ); 45 | 46 | event RewardClaimed( 47 | address indexed member, 48 | uint gbtReward 49 | ); 50 | 51 | /// @dev VoteCast event is called whenever a vote is cast that can potentially close the proposal. 52 | event VoteCast (uint256 proposalId); 53 | 54 | /// @dev ProposalAccepted event is called when a proposal is accepted so that a server can listen that can 55 | /// call any offchain actions 56 | event ProposalAccepted (uint256 proposalId); 57 | 58 | /// @dev CloseProposalOnTime event is called whenever a proposal is created or updated to close it on time. 59 | event CloseProposalOnTime ( 60 | uint256 indexed proposalId, 61 | uint256 time 62 | ); 63 | 64 | /// @dev ActionSuccess event is called whenever an onchain action is executed. 65 | event ActionSuccess ( 66 | uint256 proposalId 67 | ); 68 | 69 | /// @dev Creates a new proposal 70 | /// @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal 71 | /// @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective 72 | function createProposal( 73 | string calldata _proposalTitle, 74 | string calldata _proposalSD, 75 | string calldata _proposalDescHash, 76 | uint _categoryId 77 | ) 78 | external; 79 | 80 | /// @dev Edits the details of an existing proposal and creates new version 81 | /// @param _proposalId Proposal id that details needs to be updated 82 | /// @param _proposalDescHash Proposal description hash having long and short description of proposal. 83 | function updateProposal( 84 | uint _proposalId, 85 | string calldata _proposalTitle, 86 | string calldata _proposalSD, 87 | string calldata _proposalDescHash 88 | ) 89 | external; 90 | 91 | /// @dev Categorizes proposal to proceed further. Categories shows the proposal objective. 92 | function categorizeProposal( 93 | uint _proposalId, 94 | uint _categoryId, 95 | uint _incentives 96 | ) 97 | external; 98 | 99 | /// @dev Initiates add solution 100 | /// @param _solutionHash Solution hash having required data against adding solution 101 | function addSolution( 102 | uint _proposalId, 103 | string calldata _solutionHash, 104 | bytes calldata _action 105 | ) 106 | external; 107 | 108 | /// @dev Opens proposal for voting 109 | function openProposalForVoting(uint _proposalId) external; 110 | 111 | /// @dev Submit proposal with solution 112 | /// @param _proposalId Proposal id 113 | /// @param _solutionHash Solution hash contains parameters, values and description needed according to proposal 114 | function submitProposalWithSolution( 115 | uint _proposalId, 116 | string calldata _solutionHash, 117 | bytes calldata _action 118 | ) 119 | external; 120 | 121 | /// @dev Creates a new proposal with solution and votes for the solution 122 | /// @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal 123 | /// @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective 124 | /// @param _solutionHash Solution hash contains parameters, values and description needed according to proposal 125 | function createProposalwithSolution( 126 | string calldata _proposalTitle, 127 | string calldata _proposalSD, 128 | string calldata _proposalDescHash, 129 | uint _categoryId, 130 | string calldata _solutionHash, 131 | bytes calldata _action 132 | ) 133 | external; 134 | 135 | /// @dev Casts vote 136 | /// @param _proposalId Proposal id 137 | /// @param _solutionChosen solution chosen while voting. _solutionChosen[0] is the chosen solution 138 | function submitVote(uint _proposalId, uint _solutionChosen) external; 139 | 140 | function closeProposal(uint _proposalId) external; 141 | 142 | function claimReward(address _memberAddress, uint _maxRecords) external returns(uint pendingDAppReward); 143 | 144 | function proposal(uint _proposalId) 145 | external 146 | view 147 | returns( 148 | uint proposalId, 149 | uint category, 150 | uint status, 151 | uint finalVerdict, 152 | uint totalReward 153 | ); 154 | 155 | function canCloseProposal(uint _proposalId) public view returns(uint closeValue); 156 | 157 | function pauseProposal(uint _proposalId) public; 158 | 159 | function resumeProposal(uint _proposalId) public; 160 | 161 | function allowedToCatgorize() public view returns(uint roleId); 162 | 163 | } -------------------------------------------------------------------------------- /test/14_ProposalCategory.test.js: -------------------------------------------------------------------------------- 1 | const MemberRoles = artifacts.require('MemberRoles'); 2 | const Governance = artifacts.require('Governance'); 3 | const ProposalCategory = artifacts.require('ProposalCategory'); 4 | const NXMaster = artifacts.require('NXMaster'); 5 | const TokenFunctions = artifacts.require('TokenFunctionMock'); 6 | const assertRevert = require('./utils/assertRevert').assertRevert; 7 | const { toHex, toWei } = require('./utils/ethTools'); 8 | let pc; 9 | let gv; 10 | let tf; 11 | let mr; 12 | let nullAddress = '0x0000000000000000000000000000000000000000'; 13 | const encode = require('./utils/encoder.js').encode; 14 | 15 | contract('Proposal Category', function([owner, other]) { 16 | before(async function() { 17 | nxms = await NXMaster.deployed(); 18 | let address = await nxms.getLatestAddress(toHex('PC')); 19 | pc = await ProposalCategory.at(address); 20 | address = await nxms.getLatestAddress(toHex('GV')); 21 | gv = await Governance.at(address); 22 | tf = await TokenFunctions.deployed(); 23 | address = await nxms.getLatestAddress(toHex('MR')); 24 | mr = await MemberRoles.at(address); 25 | }); 26 | 27 | it('14.1 Should be initialized', async function() { 28 | await assertRevert(pc.proposalCategoryInitiate()); 29 | const g1 = await pc.totalCategories(); 30 | const g2 = await pc.category(1); 31 | assert.equal(g2[1].toNumber(), 1); 32 | const g5 = await pc.categoryAction(1); 33 | assert.equal(g5[2].toString(), '0x4d52'); 34 | const g6 = await pc.totalCategories(); 35 | assert.equal(g6.toNumber(), 33); 36 | }); 37 | 38 | it('14.2 should not allow unauthorized to change master address', async function() { 39 | await assertRevert(pc.changeMasterAddress(nxms.address, { from: other })); 40 | }); 41 | 42 | it('14.3 Should not add a proposal category if member roles are invalid', async function() { 43 | let c1 = await pc.totalCategories(); 44 | await assertRevert( 45 | pc.addCategory('Yo', 1, 1, 0, [1], 1, '', nullAddress, toHex('EX'), [ 46 | 0, 47 | 0, 48 | 0 49 | ]) 50 | ); 51 | //proposal to add category 52 | let actionHash = encode( 53 | 'addCategory(string,uint,uint,uint,uint[],uint,string,address,bytes2,uint[])', 54 | 'Description', 55 | 1, 56 | 1, 57 | 0, 58 | [5], 59 | 1, 60 | '', 61 | nullAddress, 62 | toHex('EX'), 63 | [0, 0, 0, 1] 64 | ); 65 | let p1 = await gv.getProposalLength(); 66 | await gv.createProposalwithSolution( 67 | 'Add new member', 68 | 'Add new member', 69 | 'Addnewmember', 70 | 3, 71 | 'Add new member', 72 | actionHash 73 | ); 74 | await gv.submitVote(p1.toNumber(), 1); 75 | await gv.closeProposal(p1.toNumber()); 76 | const c2 = await pc.totalCategories(); 77 | assert.equal(c2.toNumber(), c1.toNumber(), 'category added'); 78 | }); 79 | 80 | it('14.3 Should add a proposal category', async function() { 81 | let c1 = await pc.totalCategories(); 82 | await assertRevert( 83 | pc.addCategory('Yo', 1, 1, 0, [1], 1, '', nullAddress, toHex('EX'), [ 84 | 0, 85 | 0, 86 | 0 87 | ]) 88 | ); 89 | //proposal to add category 90 | let actionHash = encode( 91 | 'addCategory(string,uint,uint,uint,uint[],uint,string,address,bytes2,uint[])', 92 | 'Description', 93 | 1, 94 | 1, 95 | 0, 96 | [1], 97 | 1, 98 | '', 99 | nullAddress, 100 | toHex('EX'), 101 | [0, 0, 0, 1] 102 | ); 103 | let p1 = await gv.getProposalLength(); 104 | await gv.createProposalwithSolution( 105 | 'Add new member', 106 | 'Add new member', 107 | 'Addnewmember', 108 | 3, 109 | 'Add new member', 110 | actionHash 111 | ); 112 | await gv.submitVote(p1.toNumber(), 1); 113 | await gv.closeProposal(p1.toNumber()); 114 | }); 115 | 116 | it('14.4 Should update a proposal category', async function() { 117 | let c1 = await pc.totalCategories(); 118 | c1 = c1.toNumber() - 1; 119 | const cat1 = await pc.category(c1); 120 | await assertRevert( 121 | pc.updateCategory( 122 | c1, 123 | 'Yo', 124 | 1, 125 | 1, 126 | 0, 127 | [1], 128 | 1, 129 | '', 130 | nullAddress, 131 | toHex('EX'), 132 | [0, 0, 0] 133 | ) 134 | ); 135 | //proposal to update category 136 | let actionHash = encode( 137 | 'updateCategory(uint,string,uint,uint,uint,uint[],uint,string,address,bytes2,uint[])', 138 | c1, 139 | 'YoYo', 140 | 2, 141 | 1, 142 | 20, 143 | [1], 144 | 1, 145 | '', 146 | nullAddress, 147 | toHex('EX'), 148 | [0, 0, 0] 149 | ); 150 | let p1 = await gv.getProposalLength(); 151 | await gv.createProposalwithSolution( 152 | 'Add new member', 153 | 'Add new member', 154 | 'Addnewmember', 155 | 4, 156 | 'Add new member', 157 | actionHash 158 | ); 159 | await gv.submitVote(p1.toNumber(), 1); 160 | await gv.closeProposal(p1.toNumber()); 161 | let cat2 = await pc.category(c1); 162 | assert.notEqual(cat1[1], cat2[1], 'category not updated'); 163 | }); 164 | 165 | it('14.5 Should not update a proposal category if member roles are invalid', async function() { 166 | let c1 = await pc.totalCategories(); 167 | c1 = c1.toNumber() - 1; 168 | const cat1 = await pc.category(c1); 169 | await assertRevert( 170 | pc.updateCategory( 171 | c1, 172 | 'Yo', 173 | 1, 174 | 1, 175 | 0, 176 | [1], 177 | 1, 178 | '', 179 | nullAddress, 180 | toHex('EX'), 181 | [0, 0, 0] 182 | ) 183 | ); 184 | //proposal to update category 185 | let actionHash = encode( 186 | 'updateCategory(uint,string,uint,uint,uint,uint[],uint,string,address,bytes2,uint[])', 187 | c1, 188 | 'YoYo', 189 | 2, 190 | 1, 191 | 20, 192 | [7], 193 | 1, 194 | '', 195 | nullAddress, 196 | toHex('EX'), 197 | [0, 0, 0] 198 | ); 199 | let p1 = await gv.getProposalLength(); 200 | await gv.createProposalwithSolution( 201 | 'Add new member', 202 | 'Add new member', 203 | 'Addnewmember', 204 | 4, 205 | 'Add new member', 206 | actionHash 207 | ); 208 | await gv.submitVote(p1.toNumber(), 1); 209 | await gv.closeProposal(p1.toNumber()); 210 | let cat2 = await pc.category(c1); 211 | assert.notEqual(cat1[1], cat2[1], 'category not updated'); 212 | }); 213 | }); 214 | -------------------------------------------------------------------------------- /contracts/mocks/ExchangeMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | import "./FactoryMock.sol"; 4 | import "../external/openzeppelin-solidity/token/ERC20/ERC20.sol"; 5 | 6 | 7 | contract ExchangeMock { 8 | 9 | ERC20 internal token; 10 | FactoryMock internal factory; 11 | 12 | constructor (address tokenAddress, address factoryAddress) public { 13 | token = ERC20(tokenAddress); 14 | factory = FactoryMock(factoryAddress); 15 | } 16 | 17 | function recieveEther() public payable { 18 | 19 | } 20 | 21 | function removeEther(uint val) public { 22 | 23 | (msg.sender).transfer(val); 24 | } 25 | 26 | function sendEther() public payable { 27 | 28 | } 29 | 30 | function rateFactor() public view returns(uint256) { 31 | if (token.id() == 1) { 32 | return 10; 33 | } else 34 | return 5; 35 | } 36 | 37 | function getEthToTokenInputPrice(uint256 ethSold) public view returns(uint256) { 38 | // require(ethSold > 0); 39 | // uint256 tokenReserve = token.balanceOf(address(this)); 40 | // return getInputPrice(ethSold, address(this).balance, tokenReserve); 41 | return ethSold*rateFactor(); 42 | } 43 | 44 | function getTokenToEthInputPrice(uint256 tokensSold) public view returns(uint256) { 45 | // require(tokensSold > 0); 46 | // uint256 tokenReserve = token.balanceOf(address(this)); 47 | // uint256 ethBought = getInputPrice(tokensSold, tokenReserve, address(this).balance); 48 | // return (ethBought * 10**18); 49 | return (tokensSold/rateFactor()); 50 | } 51 | 52 | function ethToTokenSwapInput( 53 | uint256 minTokens, 54 | uint256 deadline 55 | ) 56 | public 57 | payable 58 | returns (uint256) 59 | { 60 | return ethToTokenInput(msg.value, minTokens, deadline, msg.sender, msg.sender); 61 | } 62 | 63 | function ethToTokenTransferInput( 64 | uint256 minTokens, 65 | uint256 deadline, 66 | address recipient 67 | ) 68 | public 69 | payable 70 | returns (uint256) 71 | { 72 | require(recipient != address(this) && recipient != address(0)); 73 | return ethToTokenInput(msg.value, minTokens, deadline, msg.sender, recipient); 74 | } 75 | 76 | function tokenToEthSwapInput( 77 | uint256 tokensSold, 78 | uint256 minEth, 79 | uint256 deadline 80 | ) 81 | public 82 | payable 83 | returns (uint256) 84 | { 85 | return tokenToEthInput(tokensSold, minEth, deadline, msg.sender, msg.sender); 86 | } 87 | 88 | function tokenToEthTransferInput( 89 | uint256 tokensSold, 90 | uint256 minEth, 91 | uint256 deadline, 92 | address payable recipient 93 | ) 94 | public 95 | payable 96 | returns (uint256) 97 | { 98 | require(recipient != address(this) && recipient != address(0)); 99 | return tokenToEthInput(tokensSold, minEth, deadline, msg.sender, recipient); 100 | } 101 | 102 | function tokenToTokenSwapInput( 103 | uint256 tokensSold, 104 | uint256 minTokensBought, 105 | uint256 minEthBought, 106 | uint256 deadline, 107 | address tokenAddress 108 | ) 109 | public 110 | returns (uint256) 111 | { 112 | 113 | address exchangeAddress = factory.getExchange(tokenAddress); 114 | return tokenToTokenInput( 115 | tokensSold, 116 | minTokensBought, 117 | minEthBought, 118 | deadline, 119 | msg.sender, 120 | msg.sender, 121 | exchangeAddress 122 | ); 123 | } 124 | 125 | function tokenToTokenTransferInput( 126 | uint256 tokensSold, 127 | uint256 minTokensBought, 128 | uint256 minEthBought, 129 | uint256 deadline, 130 | address recipient, 131 | address tokenAddress 132 | ) 133 | public 134 | returns (uint256) 135 | { 136 | address exchangeAddress = factory.getExchange(tokenAddress); 137 | return tokenToTokenInput( 138 | tokensSold, 139 | minTokensBought, 140 | minEthBought, 141 | deadline, 142 | msg.sender, 143 | recipient, 144 | exchangeAddress 145 | ); 146 | } 147 | 148 | function getInputPrice( 149 | uint256 inputAmount, 150 | uint256 inputReserve, 151 | uint256 outputReserve 152 | ) 153 | internal 154 | pure 155 | returns(uint256) 156 | { 157 | require(inputReserve > 0 && outputReserve > 0); 158 | uint256 inputAmountWithFee = inputAmount * 997; 159 | uint256 numerator = inputAmountWithFee * outputReserve; 160 | uint256 denominator = (inputReserve * 1000) + inputAmountWithFee; 161 | return (numerator / denominator); 162 | } 163 | 164 | function getOutputPrice( 165 | uint256 outputAmount, 166 | uint256 inputReserve, 167 | uint256 outputReserve 168 | ) 169 | internal 170 | pure 171 | returns(uint256) 172 | { 173 | require(inputReserve > 0 && outputReserve > 0); 174 | uint256 numerator = inputReserve * outputAmount * 1000; 175 | uint256 denominator = (outputReserve - outputAmount) * 997; 176 | return (numerator / denominator + 1); 177 | } 178 | 179 | function ethToTokenInput( 180 | uint256 ethSold, 181 | uint256 minTokens, 182 | uint256 deadline, 183 | address buyer, 184 | address recipient 185 | ) 186 | internal 187 | returns (uint256) 188 | { 189 | require(deadline >= block.timestamp && ethSold > 0 && minTokens > 0); 190 | // uint256 tokenReserve = token.balanceOf(address(this)); 191 | uint256 tokensBought = ethSold*rateFactor(); 192 | require(tokensBought >= minTokens); 193 | require(token.transfer(recipient, tokensBought)); 194 | buyer; 195 | return tokensBought; 196 | } 197 | 198 | function tokenToTokenInput( 199 | uint256 tokensSold, 200 | uint256 minTokensBought, 201 | uint256 minEthBought, 202 | uint256 deadline, 203 | address buyer, 204 | address recipient, 205 | address exchangeAddress 206 | ) 207 | internal 208 | returns (uint256) 209 | { 210 | 211 | require((deadline >= block.timestamp && tokensSold > 0) && (minTokensBought > 0 && minEthBought > 0)); 212 | require(exchangeAddress != address(this) && exchangeAddress != address(0)); 213 | // uint256 tokenReserve = token.balanceOf(address(this)); 214 | uint256 ethBought = tokensSold/rateFactor(); 215 | uint256 weiBought = (ethBought); 216 | require(weiBought >= minEthBought); 217 | require(token.transferFrom(buyer, address(this), tokensSold)); 218 | 219 | 220 | 221 | uint256 tokensBought = ExchangeMock(exchangeAddress).ethToTokenTransferInput.value( 222 | weiBought)(minTokensBought, deadline, recipient); 223 | // log.EthPurchase(buyer, tokensSold, weiBought); 224 | return tokensBought; 225 | } 226 | 227 | function tokenToEthInput( 228 | uint256 tokensSold, 229 | uint256 minEth, 230 | uint256 deadline, 231 | address buyer, 232 | address payable recipient 233 | ) 234 | internal 235 | returns (uint256) 236 | { 237 | require(deadline >= block.timestamp && tokensSold > 0 && minEth > 0); 238 | // uint256 tokenReserve = token.balanceOf(address(this)); 239 | uint256 ethBought = tokensSold/rateFactor(); 240 | uint256 weiBought = ethBought; 241 | require(weiBought >= minEth); 242 | recipient.transfer(weiBought); 243 | require(token.transferFrom(buyer, address(this), tokensSold)); 244 | // log.EthPurchase(buyer, tokens_sold, wei_bought) 245 | return weiBought; 246 | } 247 | } -------------------------------------------------------------------------------- /contracts/mocks/MockMKR.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | import "../external/openzeppelin-solidity/token/ERC20/IERC20.sol"; 4 | import "../external/openzeppelin-solidity/math/SafeMath.sol"; 5 | 6 | 7 | contract MockMKR is IERC20 { 8 | using SafeMath for uint256; 9 | 10 | string public name = "MKR"; 11 | string public symbol = "MKR"; 12 | uint public id = 2; 13 | uint8 public decimals = 18; 14 | uint256 private _totalSupply; 15 | 16 | mapping (address => uint256) private _balances; 17 | 18 | mapping (address => mapping (address => uint256)) private _allowed; 19 | 20 | constructor() public { 21 | _totalSupply = 999999 * (10**uint(decimals)); 22 | _balances[msg.sender] = _totalSupply; 23 | } 24 | 25 | /** 26 | * @dev Total number of tokens in existence 27 | */ 28 | function totalSupply() public view returns (uint256) { 29 | return _totalSupply; 30 | } 31 | 32 | /** 33 | * @dev Gets the balance of the specified address. 34 | * @param owner The address to query the balance of. 35 | * @return An uint256 representing the amount owned by the passed address. 36 | */ 37 | function balanceOf(address owner) public view returns (uint256) { 38 | return _balances[owner]; 39 | } 40 | 41 | /** 42 | * @dev Function to check the amount of tokens that an owner allowed to a spender. 43 | * @param owner address The address which owns the funds. 44 | * @param spender address The address which will spend the funds. 45 | * @return A uint256 specifying the amount of tokens still available for the spender. 46 | */ 47 | function allowance( 48 | address owner, 49 | address spender 50 | ) 51 | public 52 | view 53 | returns (uint256) 54 | { 55 | return _allowed[owner][spender]; 56 | } 57 | 58 | /** 59 | * @dev Transfer token for a specified address 60 | * @param to The address to transfer to. 61 | * @param value The amount to be transferred. 62 | */ 63 | function transfer(address to, uint256 value) public returns (bool) { 64 | _transfer(msg.sender, to, value); 65 | return true; 66 | } 67 | 68 | /** 69 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. 70 | * Beware that changing an allowance with this method brings the risk that someone may use both the old 71 | * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this 72 | * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: 73 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 74 | * @param spender The address which will spend the funds. 75 | * @param value The amount of tokens to be spent. 76 | */ 77 | function approve(address spender, uint256 value) public returns (bool) { 78 | require(spender != address(0)); 79 | 80 | _allowed[msg.sender][spender] = value; 81 | emit Approval(msg.sender, spender, value); 82 | return true; 83 | } 84 | 85 | /** 86 | * @dev Transfer tokens from one address to another 87 | * @param from address The address which you want to send tokens from 88 | * @param to address The address which you want to transfer to 89 | * @param value uint256 the amount of tokens to be transferred 90 | */ 91 | function transferFrom( 92 | address from, 93 | address to, 94 | uint256 value 95 | ) 96 | public 97 | returns (bool) 98 | { 99 | _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); 100 | _transfer(from, to, value); 101 | return true; 102 | } 103 | 104 | /** 105 | * @dev Increase the amount of tokens that an owner allowed to a spender. 106 | * approve should be called when allowed_[_spender] == 0. To increment 107 | * allowed value is better to use this function to avoid 2 calls (and wait until 108 | * the first transaction is mined) 109 | * From MonolithDAO Token.sol 110 | * @param spender The address which will spend the funds. 111 | * @param addedValue The amount of tokens to increase the allowance by. 112 | */ 113 | function increaseAllowance( 114 | address spender, 115 | uint256 addedValue 116 | ) 117 | public 118 | returns (bool) 119 | { 120 | require(spender != address(0)); 121 | 122 | _allowed[msg.sender][spender] = ( 123 | _allowed[msg.sender][spender].add(addedValue)); 124 | emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); 125 | return true; 126 | } 127 | 128 | /** 129 | * @dev Decrease the amount of tokens that an owner allowed to a spender. 130 | * approve should be called when allowed_[_spender] == 0. To decrement 131 | * allowed value is better to use this function to avoid 2 calls (and wait until 132 | * the first transaction is mined) 133 | * From MonolithDAO Token.sol 134 | * @param spender The address which will spend the funds. 135 | * @param subtractedValue The amount of tokens to decrease the allowance by. 136 | */ 137 | function decreaseAllowance( 138 | address spender, 139 | uint256 subtractedValue 140 | ) 141 | public 142 | returns (bool) 143 | { 144 | require(spender != address(0)); 145 | 146 | _allowed[msg.sender][spender] = ( 147 | _allowed[msg.sender][spender].sub(subtractedValue)); 148 | emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); 149 | return true; 150 | } 151 | 152 | /** 153 | * @dev Transfer token for a specified addresses 154 | * @param from The address to transfer from. 155 | * @param to The address to transfer to. 156 | * @param value The amount to be transferred. 157 | */ 158 | function _transfer(address from, address to, uint256 value) internal { 159 | require(to != address(0)); 160 | 161 | _balances[from] = _balances[from].sub(value); 162 | _balances[to] = _balances[to].add(value); 163 | emit Transfer(from, to, value); 164 | } 165 | 166 | /** 167 | * @dev Internal function that mints an amount of the token and assigns it to 168 | * an account. This encapsulates the modification of balances such that the 169 | * proper events are emitted. 170 | * @param account The account that will receive the created tokens. 171 | * @param value The amount that will be created. 172 | */ 173 | function _mint(address account, uint256 value) internal { 174 | require(account != address(0)); 175 | 176 | _totalSupply = _totalSupply.add(value); 177 | _balances[account] = _balances[account].add(value); 178 | emit Transfer(address(0), account, value); 179 | } 180 | 181 | /** 182 | * @dev Internal function that burns an amount of the token of a given 183 | * account. 184 | * @param account The account whose tokens will be burnt. 185 | * @param value The amount that will be burnt. 186 | */ 187 | function _burn(address account, uint256 value) internal { 188 | require(account != address(0)); 189 | 190 | _totalSupply = _totalSupply.sub(value); 191 | _balances[account] = _balances[account].sub(value); 192 | emit Transfer(account, address(0), value); 193 | } 194 | 195 | /** 196 | * @dev Internal function that burns an amount of the token of a given 197 | * account, deducting from the sender's allowance for said account. Uses the 198 | * internal burn function. 199 | * @param account The account whose tokens will be burnt. 200 | * @param value The amount that will be burnt. 201 | */ 202 | function _burnFrom(address account, uint256 value) internal { 203 | // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted, 204 | // this function needs to emit an event with the updated approval. 205 | _allowed[account][msg.sender] = _allowed[account][msg.sender].sub( 206 | value); 207 | _burn(account, value); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /contracts/mocks/MockDAI.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | import "../external/openzeppelin-solidity/token/ERC20/IERC20.sol"; 4 | import "../external/openzeppelin-solidity/math/SafeMath.sol"; 5 | 6 | 7 | contract MockDAI is IERC20 { 8 | using SafeMath for uint256; 9 | 10 | string public name = "DAI"; 11 | uint public id = 1; 12 | string public symbol = "DAI"; 13 | uint8 public decimals = 18; 14 | uint256 private _totalSupply; 15 | 16 | mapping (address => uint256) private _balances; 17 | 18 | mapping (address => mapping (address => uint256)) private _allowed; 19 | 20 | constructor() public { 21 | _totalSupply = 999999 * (10**uint(decimals)); 22 | _balances[msg.sender] = _totalSupply; 23 | } 24 | 25 | function sendEther() public payable { 26 | 27 | } 28 | 29 | /** 30 | * @dev Total number of tokens in existence 31 | */ 32 | function totalSupply() public view returns (uint256) { 33 | return _totalSupply; 34 | } 35 | 36 | /** 37 | * @dev Gets the balance of the specified address. 38 | * @param owner The address to query the balance of. 39 | * @return An uint256 representing the amount owned by the passed address. 40 | */ 41 | function balanceOf(address owner) public view returns (uint256) { 42 | return _balances[owner]; 43 | } 44 | 45 | /** 46 | * @dev Function to check the amount of tokens that an owner allowed to a spender. 47 | * @param owner address The address which owns the funds. 48 | * @param spender address The address which will spend the funds. 49 | * @return A uint256 specifying the amount of tokens still available for the spender. 50 | */ 51 | function allowance( 52 | address owner, 53 | address spender 54 | ) 55 | public 56 | view 57 | returns (uint256) 58 | { 59 | return _allowed[owner][spender]; 60 | } 61 | 62 | /** 63 | * @dev Transfer token for a specified address 64 | * @param to The address to transfer to. 65 | * @param value The amount to be transferred. 66 | */ 67 | function transfer(address to, uint256 value) public returns (bool) { 68 | _transfer(msg.sender, to, value); 69 | return true; 70 | } 71 | 72 | /** 73 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. 74 | * Beware that changing an allowance with this method brings the risk that someone may use both the old 75 | * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this 76 | * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: 77 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 78 | * @param spender The address which will spend the funds. 79 | * @param value The amount of tokens to be spent. 80 | */ 81 | function approve(address spender, uint256 value) public returns (bool) { 82 | require(spender != address(0)); 83 | 84 | _allowed[msg.sender][spender] = value; 85 | emit Approval(msg.sender, spender, value); 86 | return true; 87 | } 88 | 89 | /** 90 | * @dev Transfer tokens from one address to another 91 | * @param from address The address which you want to send tokens from 92 | * @param to address The address which you want to transfer to 93 | * @param value uint256 the amount of tokens to be transferred 94 | */ 95 | function transferFrom( 96 | address from, 97 | address to, 98 | uint256 value 99 | ) 100 | public 101 | returns (bool) 102 | { 103 | _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); 104 | _transfer(from, to, value); 105 | return true; 106 | } 107 | 108 | /** 109 | * @dev Increase the amount of tokens that an owner allowed to a spender. 110 | * approve should be called when allowed_[_spender] == 0. To increment 111 | * allowed value is better to use this function to avoid 2 calls (and wait until 112 | * the first transaction is mined) 113 | * From MonolithDAO Token.sol 114 | * @param spender The address which will spend the funds. 115 | * @param addedValue The amount of tokens to increase the allowance by. 116 | */ 117 | function increaseAllowance( 118 | address spender, 119 | uint256 addedValue 120 | ) 121 | public 122 | returns (bool) 123 | { 124 | require(spender != address(0)); 125 | 126 | _allowed[msg.sender][spender] = ( 127 | _allowed[msg.sender][spender].add(addedValue)); 128 | emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); 129 | return true; 130 | } 131 | 132 | /** 133 | * @dev Decrease the amount of tokens that an owner allowed to a spender. 134 | * approve should be called when allowed_[_spender] == 0. To decrement 135 | * allowed value is better to use this function to avoid 2 calls (and wait until 136 | * the first transaction is mined) 137 | * From MonolithDAO Token.sol 138 | * @param spender The address which will spend the funds. 139 | * @param subtractedValue The amount of tokens to decrease the allowance by. 140 | */ 141 | function decreaseAllowance( 142 | address spender, 143 | uint256 subtractedValue 144 | ) 145 | public 146 | returns (bool) 147 | { 148 | require(spender != address(0)); 149 | 150 | _allowed[msg.sender][spender] = ( 151 | _allowed[msg.sender][spender].sub(subtractedValue)); 152 | emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); 153 | return true; 154 | } 155 | 156 | /** 157 | * @dev Transfer token for a specified addresses 158 | * @param from The address to transfer from. 159 | * @param to The address to transfer to. 160 | * @param value The amount to be transferred. 161 | */ 162 | function _transfer(address from, address to, uint256 value) internal { 163 | require(to != address(0)); 164 | 165 | _balances[from] = _balances[from].sub(value); 166 | _balances[to] = _balances[to].add(value); 167 | emit Transfer(from, to, value); 168 | } 169 | 170 | /** 171 | * @dev Internal function that mints an amount of the token and assigns it to 172 | * an account. This encapsulates the modification of balances such that the 173 | * proper events are emitted. 174 | * @param account The account that will receive the created tokens. 175 | * @param value The amount that will be created. 176 | */ 177 | function _mint(address account, uint256 value) internal { 178 | require(account != address(0)); 179 | 180 | _totalSupply = _totalSupply.add(value); 181 | _balances[account] = _balances[account].add(value); 182 | emit Transfer(address(0), account, value); 183 | } 184 | 185 | /** 186 | * @dev Internal function that burns an amount of the token of a given 187 | * account. 188 | * @param account The account whose tokens will be burnt. 189 | * @param value The amount that will be burnt. 190 | */ 191 | function _burn(address account, uint256 value) internal { 192 | require(account != address(0)); 193 | 194 | _totalSupply = _totalSupply.sub(value); 195 | _balances[account] = _balances[account].sub(value); 196 | emit Transfer(account, address(0), value); 197 | } 198 | 199 | /** 200 | * @dev Internal function that burns an amount of the token of a given 201 | * account, deducting from the sender's allowance for said account. Uses the 202 | * internal burn function. 203 | * @param account The account whose tokens will be burnt. 204 | * @param value The amount that will be burnt. 205 | */ 206 | function _burnFrom(address account, uint256 value) internal { 207 | // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted, 208 | // this function needs to emit an event with the updated approval. 209 | _allowed[account][msg.sender] = _allowed[account][msg.sender].sub( 210 | value); 211 | _burn(account, value); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /contracts/external/openzeppelin-solidity/token/ERC20/ERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.7; 2 | 3 | import "./IERC20.sol"; 4 | import "../../math/SafeMath.sol"; 5 | 6 | 7 | /** 8 | * @title Standard ERC20 token 9 | * 10 | * @dev Implementation of the basic standard token. 11 | * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md 12 | * Originally based on code by FirstBlood: 13 | * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol 14 | */ 15 | contract ERC20 is IERC20 { 16 | using SafeMath for uint256; 17 | 18 | uint public id; 19 | 20 | mapping (address => uint256) private _balances; 21 | 22 | mapping (address => mapping (address => uint256)) private _allowed; 23 | 24 | uint256 private _totalSupply; 25 | 26 | /** 27 | * @dev Total number of tokens in existence 28 | */ 29 | function totalSupply() public view returns (uint256) { 30 | return _totalSupply; 31 | } 32 | 33 | /** 34 | * @dev Gets the balance of the specified address. 35 | * @param owner The address to query the balance of. 36 | * @return An uint256 representing the amount owned by the passed address. 37 | */ 38 | function balanceOf(address owner) public view returns (uint256) { 39 | return _balances[owner]; 40 | } 41 | 42 | /** 43 | * @dev Function to check the amount of tokens that an owner allowed to a spender. 44 | * @param owner address The address which owns the funds. 45 | * @param spender address The address which will spend the funds. 46 | * @return A uint256 specifying the amount of tokens still available for the spender. 47 | */ 48 | function allowance( 49 | address owner, 50 | address spender 51 | ) 52 | public 53 | view 54 | returns (uint256) 55 | { 56 | return _allowed[owner][spender]; 57 | } 58 | 59 | /** 60 | * @dev Transfer token for a specified address 61 | * @param to The address to transfer to. 62 | * @param value The amount to be transferred. 63 | */ 64 | function transfer(address to, uint256 value) public returns (bool) { 65 | _transfer(msg.sender, to, value); 66 | return true; 67 | } 68 | 69 | /** 70 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. 71 | * Beware that changing an allowance with this method brings the risk that someone may use both the old 72 | * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this 73 | * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: 74 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 75 | * @param spender The address which will spend the funds. 76 | * @param value The amount of tokens to be spent. 77 | */ 78 | function approve(address spender, uint256 value) public returns (bool) { 79 | require(spender != address(0)); 80 | 81 | _allowed[msg.sender][spender] = value; 82 | emit Approval(msg.sender, spender, value); 83 | return true; 84 | } 85 | 86 | /** 87 | * @dev Transfer tokens from one address to another 88 | * @param from address The address which you want to send tokens from 89 | * @param to address The address which you want to transfer to 90 | * @param value uint256 the amount of tokens to be transferred 91 | */ 92 | function transferFrom( 93 | address from, 94 | address to, 95 | uint256 value 96 | ) 97 | public 98 | returns (bool) 99 | { 100 | _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); 101 | _transfer(from, to, value); 102 | return true; 103 | } 104 | 105 | /** 106 | * @dev Increase the amount of tokens that an owner allowed to a spender. 107 | * approve should be called when allowed_[_spender] == 0. To increment 108 | * allowed value is better to use this function to avoid 2 calls (and wait until 109 | * the first transaction is mined) 110 | * From MonolithDAO Token.sol 111 | * @param spender The address which will spend the funds. 112 | * @param addedValue The amount of tokens to increase the allowance by. 113 | */ 114 | function increaseAllowance( 115 | address spender, 116 | uint256 addedValue 117 | ) 118 | public 119 | returns (bool) 120 | { 121 | require(spender != address(0)); 122 | 123 | _allowed[msg.sender][spender] = ( 124 | _allowed[msg.sender][spender].add(addedValue) 125 | ); 126 | emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); 127 | return true; 128 | } 129 | 130 | /** 131 | * @dev Decrease the amount of tokens that an owner allowed to a spender. 132 | * approve should be called when allowed_[_spender] == 0. To decrement 133 | * allowed value is better to use this function to avoid 2 calls (and wait until 134 | * the first transaction is mined) 135 | * From MonolithDAO Token.sol 136 | * @param spender The address which will spend the funds. 137 | * @param subtractedValue The amount of tokens to decrease the allowance by. 138 | */ 139 | function decreaseAllowance( 140 | address spender, 141 | uint256 subtractedValue 142 | ) 143 | public 144 | returns (bool) 145 | { 146 | require(spender != address(0)); 147 | 148 | _allowed[msg.sender][spender] = (_allowed[msg.sender][spender].sub(subtractedValue)); 149 | emit Approval(msg.sender, spender, _allowed[msg.sender][spender]); 150 | return true; 151 | } 152 | 153 | /** 154 | * @dev Transfer token for a specified addresses 155 | * @param from The address to transfer from. 156 | * @param to The address to transfer to. 157 | * @param value The amount to be transferred. 158 | */ 159 | function _transfer(address from, address to, uint256 value) internal { 160 | require(to != address(0)); 161 | 162 | _balances[from] = _balances[from].sub(value); 163 | _balances[to] = _balances[to].add(value); 164 | emit Transfer(from, to, value); 165 | } 166 | 167 | /** 168 | * @dev Internal function that mints an amount of the token and assigns it to 169 | * an account. This encapsulates the modification of balances such that the 170 | * proper events are emitted. 171 | * @param account The account that will receive the created tokens. 172 | * @param value The amount that will be created. 173 | */ 174 | function _mint(address account, uint256 value) internal { 175 | require(account != address(0)); 176 | 177 | _totalSupply = _totalSupply.add(value); 178 | _balances[account] = _balances[account].add(value); 179 | emit Transfer(address(0), account, value); 180 | } 181 | 182 | /** 183 | * @dev Internal function that burns an amount of the token of a given 184 | * account. 185 | * @param account The account whose tokens will be burnt. 186 | * @param value The amount that will be burnt. 187 | */ 188 | function _burn(address account, uint256 value) internal { 189 | require(account != address(0)); 190 | 191 | _totalSupply = _totalSupply.sub(value); 192 | _balances[account] = _balances[account].sub(value); 193 | emit Transfer(account, address(0), value); 194 | } 195 | 196 | /** 197 | * @dev Internal function that burns an amount of the token of a given 198 | * account, deducting from the sender's allowance for said account. Uses the 199 | * internal burn function. 200 | * @param account The account whose tokens will be burnt. 201 | * @param value The amount that will be burnt. 202 | */ 203 | function _burnFrom(address account, uint256 value) internal { 204 | // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted, 205 | // this function needs to emit an event with the updated approval. 206 | _allowed[account][msg.sender] = _allowed[account][msg.sender].sub( 207 | value); 208 | _burn(account, value); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /test/19_NewTokenPriceTest.test.js: -------------------------------------------------------------------------------- 1 | const MCR = artifacts.require('MCR'); 2 | const Pool1 = artifacts.require('Pool1Mock'); 3 | const Pool2 = artifacts.require('Pool2'); 4 | const PoolData = artifacts.require('PoolDataMock'); 5 | const DAI = artifacts.require('MockDAI'); 6 | const NXMToken = artifacts.require('NXMToken'); 7 | const MemberRoles = artifacts.require('MemberRoles'); 8 | const NXMaster = artifacts.require('NXMaster'); 9 | const TokenController = artifacts.require('TokenController'); 10 | const TokenFunctions = artifacts.require('TokenFunctionMock'); 11 | 12 | const { assertRevert } = require('./utils/assertRevert'); 13 | const { advanceBlock } = require('./utils/advanceToBlock'); 14 | const { ether, toHex, toWei } = require('./utils/ethTools'); 15 | const getValue = require('./utils/getMCRPerThreshold.js').getValue; 16 | 17 | const CA_ETH = '0x45544800'; 18 | const CA_DAI = '0x44414900'; 19 | const UNLIMITED_ALLOWANCE = toWei(4500); 20 | 21 | let mcr; 22 | let pd; 23 | let tk; 24 | let p1; 25 | let balance_DAI; 26 | let balance_ETH; 27 | let nxms; 28 | let mr; 29 | let cad; 30 | let p2; 31 | let tc; 32 | let tf; 33 | const BN = web3.utils.BN; 34 | 35 | const BigNumber = web3.BigNumber; 36 | require('chai') 37 | .use(require('chai-bignumber')(BigNumber)) 38 | .should(); 39 | 40 | contract('MCR', function([owner, notOwner]) { 41 | before(async function() { 42 | await advanceBlock(); 43 | mcr = await MCR.deployed(); 44 | tk = await NXMToken.deployed(); 45 | p2 = await Pool2.deployed(); 46 | p1 = await Pool1.deployed(); 47 | pd = await PoolData.deployed(); 48 | cad = await DAI.deployed(); 49 | nxms = await NXMaster.deployed(); 50 | tf = await TokenFunctions.deployed(); 51 | mr = await MemberRoles.at(await nxms.getLatestAddress('0x4d52')); 52 | tc = await TokenController.at(await nxms.getLatestAddress(toHex('TC'))); 53 | }); 54 | 55 | describe('Token Price Calculation', function() { 56 | let tp_eth; 57 | let tp_dai; 58 | 59 | before(async function() { 60 | await mr.addMembersBeforeLaunch([], []); 61 | (await mr.launched()).should.be.equal(true); 62 | await mr.payJoiningFee(notOwner, { 63 | from: notOwner, 64 | value: toWei(0.002) 65 | }); 66 | await p1.upgradeInvestmentPool(DAI.address); 67 | await tf.upgradeCapitalPool(DAI.address); 68 | await p1.sendEther({ from: owner, value: toWei(5500) }); 69 | await mr.kycVerdict(notOwner, true); 70 | await tk.approve(tc.address, UNLIMITED_ALLOWANCE, { from: owner }); 71 | await tk.approve(tc.address, UNLIMITED_ALLOWANCE, { from: notOwner }); 72 | await mcr.addMCRData( 73 | 9000, 74 | toWei(100), 75 | toWei(90), 76 | ['0x455448', '0x444149'], 77 | [100, 15517], 78 | 20190219 79 | ); 80 | await tf.transferCurrencyAsset(toHex('ETH'), owner, toWei(5500 - 90)); 81 | }); 82 | it('19.1 single tranche 0.1ETH', async function() { 83 | let dataaa = await pd.getTokenPriceDetails(toHex('ETH')); 84 | let x = await tk.balanceOf(notOwner); 85 | let expectedNXM = await p1.getToken(toWei(0.1)); 86 | await p1.buyToken({ from: notOwner, value: toWei(0.1) }); 87 | let y = await tk.balanceOf(notOwner); 88 | console.log('single tranche 0.1ETH ==> ', parseFloat(y - x) / toWei(1)); 89 | ((y - x) / toWei(1)) 90 | .toFixed(2) 91 | .toString() 92 | .should.be.equal((5.13).toString()); 93 | }); 94 | it('19.2 multiple tranches 100ETH', async function() { 95 | let x = await tk.balanceOf(notOwner); 96 | await p1.buyToken({ 97 | from: notOwner, 98 | value: toWei(100) 99 | }); 100 | let y = await tk.balanceOf(notOwner); 101 | console.log( 102 | 'multiple tranches 100ETH ==> ', 103 | parseFloat(y - x) / toWei(1) 104 | ); 105 | ((y - x) / toWei(1)) 106 | .toFixed(2) 107 | .toString() 108 | .should.be.equal((5114.54).toString()); 109 | }); 110 | }); 111 | 112 | describe('Token Price Calculation2', function() { 113 | let tp_eth; 114 | let tp_dai; 115 | 116 | before(async function() { 117 | await p1.upgradeInvestmentPool(DAI.address); 118 | await tf.upgradeCapitalPool(DAI.address); 119 | await p1.sendEther({ from: owner, value: toWei(10) }); 120 | await mcr.addMCRData( 121 | 1000, 122 | toWei(100), 123 | toWei(10), 124 | ['0x455448', '0x444149'], 125 | [100, 14800], 126 | 20190219 127 | ); 128 | }); 129 | it('19.3 single tranches 15 times Buy tokens', async function() { 130 | let x; 131 | let y; 132 | let cost = toWei(10); 133 | for (let i = 0; cost < toWei(180); i++) { 134 | cost = cost / 1 + (i / 1) * toWei(10); 135 | console.log( 136 | 'token rate 1ETH = ', 137 | toWei(1) / parseFloat(await mcr.calculateTokenPrice(toHex('ETH'))) 138 | ); 139 | x = await tk.balanceOf(notOwner); 140 | await p1.buyToken({ from: notOwner, value: cost }); 141 | y = await tk.balanceOf(notOwner); 142 | console.log( 143 | 'tranche ', 144 | cost / toWei(1), 145 | ' ETH ==> ', 146 | parseFloat(y - x) / toWei(1) 147 | ); 148 | } 149 | }); 150 | it('19.4 tranches Buy more tokens', async function() { 151 | await p1.upgradeInvestmentPool(DAI.address); 152 | await tf.upgradeCapitalPool(DAI.address); 153 | await p1.sendEther({ from: owner, value: toWei(607.7406473491) }); 154 | await mcr.addMCRData( 155 | 202, 156 | toWei(30000), 157 | toWei(607.7406473491), 158 | ['0x455448', '0x444149'], 159 | [100, 15517], 160 | 20190219 161 | ); 162 | let x; 163 | let y; 164 | let cost = toWei(15); 165 | console.log( 166 | 'token rate 1ETH = ', 167 | toWei(1) / parseFloat(await mcr.calculateTokenPrice(toHex('ETH'))) 168 | ); 169 | x = await tk.balanceOf(notOwner); 170 | await p1.buyToken({ from: notOwner, value: cost }); 171 | y = await tk.balanceOf(notOwner); 172 | console.log( 173 | 'tranche ', 174 | cost / toWei(1), 175 | ' ETH ==> ', 176 | parseFloat(y - x) / toWei(1) 177 | ); 178 | 179 | cost = toWei(35); 180 | console.log( 181 | 'token rate 1ETH = ', 182 | toWei(1) / parseFloat(await mcr.calculateTokenPrice(toHex('ETH'))) 183 | ); 184 | x = await tk.balanceOf(notOwner); 185 | await p1.buyToken({ from: notOwner, value: cost }); 186 | y = await tk.balanceOf(notOwner); 187 | console.log( 188 | 'tranche ', 189 | cost / toWei(1), 190 | ' ETH ==> ', 191 | parseFloat(y - x) / toWei(1) 192 | ); 193 | 194 | cost = toWei(600); 195 | console.log( 196 | 'token rate 1ETH = ', 197 | toWei(1) / parseFloat(await mcr.calculateTokenPrice(toHex('ETH'))) 198 | ); 199 | x = await tk.balanceOf(notOwner); 200 | await p1.buyToken({ from: notOwner, value: cost }); 201 | y = await tk.balanceOf(notOwner); 202 | console.log( 203 | 'tranche ', 204 | cost / toWei(1), 205 | ' ETH ==> ', 206 | parseFloat(y - x) / toWei(1) 207 | ); 208 | 209 | cost = toWei(5000); 210 | console.log( 211 | 'token rate 1ETH = ', 212 | toWei(1) / parseFloat(await mcr.calculateTokenPrice(toHex('ETH'))) 213 | ); 214 | }); 215 | it('19.5 Should revert while buying or 0 ETH', async function() { 216 | await assertRevert(p1.buyToken({ value: 0 })); 217 | }); 218 | }); 219 | 220 | describe('Token Selling', function() { 221 | it('19.6 Max sellable token will 0 if mcr percentage is less than 100', async function() { 222 | parseFloat(await mcr.getMaxSellTokens()).should.be.equal(0); 223 | }); 224 | it('19.7 sell more than 1000 NXMs', async function() { 225 | await p1.sendEther({ from: owner, value: toWei(11000) }); 226 | let poolBal = await mcr.calVtpAndMCRtp(); 227 | await mcr.addMCRData( 228 | 20000, 229 | toWei(100), 230 | poolBal[0], 231 | ['0x455448', '0x444149'], 232 | [100, 15517], 233 | 20190219 234 | ); 235 | await tf.transferCurrencyAsset(toHex('ETH'), owner, toWei(11000)); 236 | let initialBalNXM = await tk.balanceOf(owner); 237 | await p1.sellNXMTokens(toWei(1500)); 238 | let finalBalNXM = await tk.balanceOf(owner); 239 | 240 | (finalBalNXM / 1).should.be.equal(initialBalNXM / 1 - toWei(1500)); 241 | }); 242 | it('19.6 Max sellable token will 0 if pool balance is less than 1.5 times of basemin', async function() { 243 | await tf.upgradeCapitalPool(DAI.address); 244 | parseFloat(await mcr.getMaxSellTokens()).should.be.equal(0); 245 | }); 246 | }); 247 | }); 248 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://travis-ci.org/somish/NexusMutual?branch=master) 2 | 3 |
Nexus Mutual uses blockchain technology to bring the mutual ethos back to insurance by creating aligned incentives through smart contract code on the Ethereum blockchain.
5 |Nexus Mutual is built on the Ethereum blockchain and uses a modular system for grouping of Ethereum smart contracts, allowing logical components of the system to be upgraded without effecting the other components. Following are the key modules of Nexus Mutual.
7 |Token contracts maintain details of NXM Members and the NXM Tokens held by each of them. A member of the mutual can buy/sell tokens anytime. NXM tokens can be used to purchase a cover, submit a claim, underwrite smart contracts, assess a claim or transfer tokens to other addresses.
9 |17 |19 |Note: The smart contracts of this module had to be split in multiple smart contracts to cater to the Ethereum Gas limits. The above mentioned contracts need to be seen in conjunction
18 |
Quotation contracts contain all logic associated with creating and expiring covers. Smart contract cover is the first insurance product supported by the mutual. A member can generate a quotation offchain , and fund the same via NXM tokens / currency assets(currently ETH and DAI). This creates a cover on-chain. Quotation contracts interact with Token Contracts to lock NXM tokens against a cover which are then used at the time of claim submission.
29 |Claim contracts manages the entire claim lifecycle starting from submitting a claim against a cover note to taking part in claims assessment to closing a claim.
41 |Claims Reward Contract contains the methods for rewarding or punishing the Claim assessors/Members based on the vote cast and the final verdict. All rewards in Nexus Mutual, commission to stakers, rewards to Cliams assessors/members for claims assessment, participants in governance are given via this module.
55 |Pool contracts contain all logic associated with calling External oracles through Oraclize and processing the results retrieved from the same. The module also encompasses on-chain investment asset management using 0x-protocol.
68 |75 |77 |Note: The smart contracts of this module had to be split in multiple smart contracts to cater to the Ethereum Gas limits. The above mentioned contracts need to be seen in conjunction
76 |
MCR contracts contain functions for recording the Minimum Capital Requirement (MCR) of the system, each day, thus determining the NXM token price.
89 |Governance contracts contain the logic for creating, editing, categorizing and voting on proposals followed by action implementation, code upgradability. These governance contracts are generated in line with the GovBlocks Protocol.
100 |