diff --git a/contracts/schemes/DxDaoSchemeConstraints.sol b/contracts/schemes/DxDaoSchemeConstraints.sol index a041ffc0..87dea875 100644 --- a/contracts/schemes/DxDaoSchemeConstraints.sol +++ b/contracts/schemes/DxDaoSchemeConstraints.sol @@ -3,10 +3,25 @@ pragma experimental ABIEncoderV2; import "./SchemeConstraints.sol"; - contract DxDaoSchemeConstraints is SchemeConstraints { using SafeMath for uint256; + event UpdatedContractsWhitelist( + address[] _contractsAddresses, + bool[] _contractsWhitelisted + ); + + event UpdatedPeriodLimitsTokens( + address[] _tokensAddresses, + uint256[] _tokensPeriodLimits + ); + + event UpdatedPeriodLimitWei( + uint256 _periodLimitWei + ); + + address public avatar; + address public genericSchemeMultiCall; uint256 public initialTimestamp; uint256 public periodSize; uint256 public periodLimitWei; @@ -18,26 +33,32 @@ contract DxDaoSchemeConstraints is SchemeConstraints { bytes4 private constant APPROVE_SIGNATURE = 0x095ea7b3;//approve(address,uint256) /* @dev initialize + * @param avatar the DAOs avatar address * @param _periodSize the time period to limit the tokens and eth spending * @param _periodLimitWei the limit of eth which can be sent per period * @param _periodLimitTokensAddresses tokens to limit * @param _periodLimitTokensAmounts the limit of token which can be sent per period * @param _contractsWhiteList the contracts the scheme is allowed to interact with + * @param _genericSchemeMultiCall genericSchemeMultiCall which allowed to call isAllowedToCall */ function initialize( + address _avatar, uint256 _periodSize, uint256 _periodLimitWei, address[] calldata _periodLimitTokensAddresses, uint256[] calldata _periodLimitTokensAmounts, - address[] calldata _contractsWhiteList + address[] calldata _contractsWhiteList, + address _genericSchemeMultiCall ) external { require(initialTimestamp == 0, "cannot initialize twice"); require(_periodSize > 0, "preriod size should be greater than 0"); require(_periodLimitTokensAddresses.length == _periodLimitTokensAmounts.length, "invalid length _periodLimitTokensAddresses"); + require(_genericSchemeMultiCall != address(0), "genericSchemeMultiCall cannot be zero"); periodSize = _periodSize; periodLimitWei = _periodLimitWei; + avatar = _avatar; // solhint-disable-next-line not-rely-on-time initialTimestamp = block.timestamp; for (uint i = 0; i < _contractsWhiteList.length; i++) { @@ -47,6 +68,55 @@ contract DxDaoSchemeConstraints is SchemeConstraints { periodLimitToken[_periodLimitTokensAddresses[i]] = _periodLimitTokensAmounts[i]; } contractsWhiteList = _contractsWhiteList; + genericSchemeMultiCall = _genericSchemeMultiCall; + } + + /* + * @dev updateContractWhitelist used to let the DAO update whitelisted contracts. + * @param _contractsAddresses - The contract that should be update + * @param _contractsWhitelisted – true adds a contract to the whitelist, false removes it. + */ + function updateContractsWhitelist( + address[] calldata _contractsAddresses, + bool[] calldata _contractsWhitelisted + ) + external { + require(msg.sender == avatar, "caller must be avatar"); + require(_contractsAddresses.length == _contractsWhitelisted.length, + "invalid length _periodLimitTokensAddresses"); + for (uint i = 0; i < _contractsAddresses.length; i++) { + contractsWhiteListMap[_contractsAddresses[i]] = _contractsWhitelisted[i]; + } + emit UpdatedContractsWhitelist(_contractsAddresses, _contractsWhitelisted); + } + + /* + * @dev updatePeriodLimitsTokens lets the dao update limits to token limits. + * @param _tokensAddresses - The token addresses to be updated + * @param _tokensPeriodLimits – The token amounts to be set as period limit. + */ + function updatePeriodLimitsTokens( + address[] calldata _tokensAddresses, + uint256[] calldata _tokensPeriodLimits + ) + external { + require(msg.sender == avatar, "caller must be avatar"); + require(_tokensAddresses.length == _tokensPeriodLimits.length, + "invalid length _tokensPeriodLimits"); + for (uint i = 0; i < _tokensAddresses.length; i++) { + periodLimitToken[_tokensAddresses[i]] = _tokensPeriodLimits[i]; + } + emit UpdatedPeriodLimitsTokens(_tokensAddresses, _tokensPeriodLimits); + } + + /* + * @dev updatePeriodLimitWei lets the dao update limits to ETH spending limit. + * @param _periodLimitWei - The new spending limit in WEI that should be set. + */ + function updatePeriodLimitWei(uint256 _periodLimitWei) external { + require(msg.sender == avatar, "caller must be avatar"); + periodLimitWei = _periodLimitWei; + emit UpdatedPeriodLimitWei(_periodLimitWei); } /* @@ -68,10 +138,11 @@ contract DxDaoSchemeConstraints is SchemeConstraints { external returns(bool) { - + require(msg.sender == genericSchemeMultiCall, "only genericSchemeMultiCall"); uint256 observervationIndex = observationIndex(); uint256 totalPeriodSpendingInWei; for (uint i = 0; i < _contractsToCall.length; i++) { + require(contractsWhiteListMap[_contractsToCall[i]], "contract not whitelisted"); // constraint eth transfer totalPeriodSpendingInWei = totalPeriodSpendingInWei.add(_values[i]); bytes memory callData = _callsData[i]; @@ -81,11 +152,14 @@ contract DxDaoSchemeConstraints is SchemeConstraints { callData[2] == APPROVE_SIGNATURE[2] && callData[3] == APPROVE_SIGNATURE[3]) { uint256 amount; + address spender; address contractToCall = _contractsToCall[i]; // solhint-disable-next-line no-inline-assembly assembly { - amount := mload(add(callData, 68)) + spender := mload(add(callData, 36)) + amount := mload(add(callData, 68)) } + require(contractsWhiteListMap[spender], "spender contract not whitelisted"); periodSpendingToken[observervationIndex][contractToCall] = periodSpendingToken[observervationIndex][contractToCall].add(amount); require( @@ -118,18 +192,13 @@ contract DxDaoSchemeConstraints is SchemeConstraints { returns(bool) { for (uint i = 0; i < _contractsToCall.length; i++) { - if (!contractsWhiteListMap[_contractsToCall[i]]) { + require(contractsWhiteListMap[_contractsToCall[i]], "contract not whitelisted"); + bytes memory callData = _callsData[i]; + if (callData[0] == APPROVE_SIGNATURE[0] && + callData[1] == APPROVE_SIGNATURE[1] && + callData[2] == APPROVE_SIGNATURE[2] && + callData[3] == APPROVE_SIGNATURE[3]) { address spender; - bytes memory callData = _callsData[i]; - require( - callData[0] == APPROVE_SIGNATURE[0] && - callData[1] == APPROVE_SIGNATURE[1] && - callData[2] == APPROVE_SIGNATURE[2] && - callData[3] == APPROVE_SIGNATURE[3], - "allow only approve call for none whitelistedContracts"); - //in solidity > 6 this can be replaced by: - //(spender,) = abi.descode(callData[4:], (address, uint)); - // see https://github.com/ethereum/solidity/issues/9439 // solhint-disable-next-line no-inline-assembly assembly { spender := mload(add(callData, 36)) diff --git a/test/genericschememulticall.js b/test/genericschememulticall.js index f389c698..8e48384a 100644 --- a/test/genericschememulticall.js +++ b/test/genericschememulticall.js @@ -63,7 +63,14 @@ const setup = async function (accounts, if (useSchemeConstraint) { testSetup.schemeConstraints = await DxDaoSchemeConstraints.new(); schemeConstraintsAddress = testSetup.schemeConstraints.address; - await testSetup.schemeConstraints.initialize(100000,100000,[tokenAddress],[1000],contractsWhiteList); + //use accounts[4] as the avatar. + await testSetup.schemeConstraints.initialize(accounts[4], + 100000, + 100000, + [tokenAddress], + [1000], + contractsWhiteList, + testSetup.genericSchemeMultiCall.address); } else { schemeConstraintsAddress = helpers.NULL_ADDRESS; } @@ -279,19 +286,19 @@ contract('GenericSchemeMultiCall', function(accounts) { }); it("schemeconstrains token value exceed limit", async function() { - var standardTokenMock = await ERC20Mock.new(accounts[0],1000); - var testSetup = await setup(accounts,[accounts[3]],0,true,standardTokenMock.address); - var encodedTokenApproval = await createCallToTokenApproval(standardTokenMock,accounts[3], 10001); - var tx = await testSetup.genericSchemeMultiCall.proposeCalls([standardTokenMock.address],[encodedTokenApproval],[0],helpers.NULL_HASH); - var proposalId = await helpers.getValueFromLogs(tx, '_proposalId'); - await testSetup.genericSchemeParams.votingMachine.genesisProtocol.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); - try { - await testSetup.genericSchemeMultiCall.execute(proposalId); - assert(false, "cannot approve token amount: periodSpendingTokensExceeded"); - } catch(error) { - helpers.assertVMException(error); - } - }); + var standardTokenMock = await ERC20Mock.new(accounts[0],1000); + var testSetup = await setup(accounts,[standardTokenMock.address,accounts[3]],0,true,standardTokenMock.address); + var encodedTokenApproval = await createCallToTokenApproval(standardTokenMock,accounts[3], 10001); + var tx = await testSetup.genericSchemeMultiCall.proposeCalls([standardTokenMock.address],[encodedTokenApproval],[0],helpers.NULL_HASH); + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId'); + await testSetup.genericSchemeParams.votingMachine.genesisProtocol.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + try { + await testSetup.genericSchemeMultiCall.execute(proposalId); + assert(false, "periodSpendingTokensExceeded"); + } catch(error) { + helpers.assertVMException(error); + } + }); it("execute proposeVote -negative decision - check action - with GenesisProtocol", async function() { var actionMock =await ActionMock.new(); @@ -366,7 +373,7 @@ contract('GenericSchemeMultiCall', function(accounts) { it("execute proposeVote with multiple calls with votingMachine -positive decision", async function() { var actionMock =await ActionMock.new(); var standardTokenMock = await ERC20Mock.new(accounts[0],1000); - var testSetup = await setup(accounts,[actionMock.address,accounts[3]],0,true,standardTokenMock.address); + var testSetup = await setup(accounts,[actionMock.address,accounts[3],standardTokenMock.address],0,true,standardTokenMock.address); var encodedTokenApproval = await createCallToTokenApproval(standardTokenMock,accounts[3], 1000); var callData1 = await createCallToActionMock(testSetup.org.avatar.address,actionMock); var tx = await testSetup.genericSchemeMultiCall.proposeCalls( @@ -395,29 +402,65 @@ contract('GenericSchemeMultiCall', function(accounts) { }); it("cannot init twice", async function() { - var actionMock =await ActionMock.new(); - var testSetup = await setup(accounts,[actionMock.address]); - try { - await testSetup.genericSchemeMultiCall.initialize( - testSetup.org.avatar.address, - accounts[0], - helpers.SOME_HASH, - testSetup.schemeConstraints.address - ); - assert(false, "cannot init twice"); - } catch(error) { - helpers.assertVMException(error); - } + var actionMock =await ActionMock.new(); + var testSetup = await setup(accounts,[actionMock.address]); + try { + await testSetup.genericSchemeMultiCall.initialize( + testSetup.org.avatar.address, + accounts[0], + helpers.SOME_HASH, + testSetup.schemeConstraints.address + ); + assert(false, "cannot init twice"); + } catch(error) { + helpers.assertVMException(error); + } }); + it("cannot init with invalid avatar address", async function() { + var genericSchemeMultiCallInitAvatar = await GenericSchemeMultiCall.new(); + try { + await genericSchemeMultiCallInitAvatar.initialize( + helpers.NULL_ADDRESS, + accounts[0], + helpers.SOME_HASH, + accounts[0] + ); + assert(false, "avatar cannot be zero"); + } catch(error) { + helpers.assertVMException(error); + } + }); + + it("can init with multiple contracts on whitelist", async function() { + var actionMock =await ActionMock.new(); + var standardTokenMock = await ERC20Mock.new(accounts[0],1000); + var testSetup = await setup(accounts,[actionMock.address],0,true,standardTokenMock.address); var dxDaoSchemeConstraints =await DxDaoSchemeConstraints.new(); + try { await dxDaoSchemeConstraints.initialize( + testSetup.org.avatar.address, 1, 0, [], [], - [accounts[0],accounts[1],accounts[2],accounts[3]] + [accounts[0],accounts[1],accounts[2],accounts[3]], + helpers.NULL_ADDRESS + ); + assert(false, "cannot init with zero genericSchemeMultiCall"); + } catch(error) { + helpers.assertVMException(error); + } + + await dxDaoSchemeConstraints.initialize( + testSetup.org.avatar.address, + 1, + 0, + [], + [], + [accounts[0],accounts[1],accounts[2],accounts[3]], + testSetup.genericSchemeMultiCall.address ); var contractsWhiteList = await dxDaoSchemeConstraints.getContractsWhiteList(); assert.equal(contractsWhiteList[0],accounts[0]); @@ -427,6 +470,88 @@ contract('GenericSchemeMultiCall', function(accounts) { }); + it("cannot initialize contraints with zero period", async function() { + var dxDaoSchemeConstraintsInit =await DxDaoSchemeConstraints.new(); + try { + await dxDaoSchemeConstraintsInit.initialize( + accounts[0], + 0, + 0, + [], + [], + [accounts[0]], + accounts[0] + + ); + assert(false, "preriod size should be greater than 0"); + } catch(error) { + helpers.assertVMException(error); + } + }); + + it("cannot initialize contraints with invalid array length", async function() { + var dxDaoSchemeConstraintsArray = await DxDaoSchemeConstraints.new(); + try { + await dxDaoSchemeConstraintsArray.initialize( + accounts[0], + 1, + 0, + [accounts[0]], + [100,100], + [accounts[0]], + accounts[0] + ); + assert(false, "invalid length _periodLimitTokensAddresses"); + } catch(error) { + helpers.assertVMException(error); + } + }); + + it("cannot initialize contraints twice", async function() { + var dxDaoSchemeConstraintsDouble=await DxDaoSchemeConstraints.new(); + await dxDaoSchemeConstraintsDouble.initialize( + accounts[0], + 3, + 0, + [], + [], + [accounts[0]], + accounts[0] + ); + try { + await dxDaoSchemeConstraintsDouble.initialize( + accounts[0], + 3, + 0, + [], + [], + [accounts[0]], + accounts[0] + ); + assert(false, "cannot initialize twice"); + } catch(error) { + helpers.assertVMException(error); + } + }); + + it("execute proposeVote with multiple calls with votingMachine without whitelisted token", async function() { + var actionMock =await ActionMock.new(); + var standardTokenMock = await ERC20Mock.new(accounts[0],1000); + var testSetup = await setup(accounts,[],0,true,standardTokenMock.address); + var encodedTokenApproval= await createCallToTokenApproval(standardTokenMock, accounts[3], 1000); + var callData1 = await createCallToActionMock(testSetup.org.avatar.address,actionMock); + try { + await testSetup.genericSchemeMultiCall.proposeCalls( + [actionMock.address], + [callData1,encodedTokenApproval], + [0,0], + helpers.NULL_HASH); + assert(false, "contract not whitelisted"); + } catch(error) { + helpers.assertVMException(error); + } + }); + it("execute proposeVote with multiple calls with votingMachine without whitelisted spender", async function() { var actionMock =await ActionMock.new(); var standardTokenMock = await ERC20Mock.new(accounts[0],1000); @@ -435,7 +560,7 @@ contract('GenericSchemeMultiCall', function(accounts) { var callData1 = await createCallToActionMock(testSetup.org.avatar.address,actionMock); try { await testSetup.genericSchemeMultiCall.proposeCalls( - [actionMock.address], + [actionMock.address,actionMock.address], [callData1,encodedTokenApproval], [0,0], helpers.NULL_HASH); @@ -445,7 +570,6 @@ contract('GenericSchemeMultiCall', function(accounts) { } }); - it("none exist schemeConstraints for proposeCall", async function() { var actionMock =await ActionMock.new(); var standardTokenMock = await ERC20Mock.new(accounts[0],1000); @@ -483,6 +607,251 @@ contract('GenericSchemeMultiCall', function(accounts) { } catch(error) { helpers.assertVMException(error); } + }); + it("can only update contraint whitelist & limits from avatar", async function() { + var actionMock =await ActionMock.new(); + var standardTokenMock = await ERC20Mock.new(accounts[0],1000); + var testSetup = await setup(accounts,[actionMock.address],0,true,standardTokenMock.address); + var dxDaoSchemeConstraints =await DxDaoSchemeConstraints.new(); + await dxDaoSchemeConstraints.initialize( + testSetup.org.avatar.address, + 1, + 0, + [], + [], + [accounts[0]], + testSetup.genericSchemeMultiCall.address + ); + try { + await dxDaoSchemeConstraints.updateContractsWhitelist([actionMock.address],[true]); + assert(false, "caller must be avatar"); + } catch(error) { + helpers.assertVMException(error); + } + + try { + await dxDaoSchemeConstraints.updatePeriodLimitsTokens([actionMock.address, standardTokenMock.address],[5000,6000]); + assert(false, "caller must be avatar"); + } catch(error) { + helpers.assertVMException(error); + } + + try { + await dxDaoSchemeConstraints.updatePeriodLimitWei(5000); + assert(false, "caller must be avatar"); + } catch(error) { + helpers.assertVMException(error); + } + + dxDaoSchemeConstraints =await DxDaoSchemeConstraints.new(); + await dxDaoSchemeConstraints.initialize( + accounts[3], + 1, + 0, + [], + [], + [accounts[0]], + testSetup.genericSchemeMultiCall.address + ); + await dxDaoSchemeConstraints.updatePeriodLimitWei(10000,{from:accounts[3]}); + await dxDaoSchemeConstraints.getPastEvents('UpdatedPeriodLimitWei', { + fromBlock: 0, + toBlock: 'latest' + }) + .then(function(events){ + assert.equal(events[0].event,"UpdatedPeriodLimitWei"); + assert.equal(events[0].args._periodLimitWei,10000); + }); + + await dxDaoSchemeConstraints.updatePeriodLimitsTokens([standardTokenMock.address],[10000],{from:accounts[3]}); + await dxDaoSchemeConstraints.getPastEvents('UpdatedPeriodLimitsTokens', { + fromBlock: 0, + toBlock: 'latest' + }) + .then(function(events){ + assert.equal(events[0].event,"UpdatedPeriodLimitsTokens"); + assert.equal(events[0].args._tokensAddresses[0],standardTokenMock.address); + assert.equal(events[0].args._tokensPeriodLimits[0],10000); + }); + + await dxDaoSchemeConstraints.updateContractsWhitelist([standardTokenMock.address],[true],{from:accounts[3]}); + await dxDaoSchemeConstraints.getPastEvents('UpdatedContractsWhitelist', { + fromBlock: 0, + toBlock: 'latest' + }) + .then(function(events){ + assert.equal(events[0].event,"UpdatedContractsWhitelist"); + assert.equal(events[0].args._contractsAddresses[0],standardTokenMock.address); + assert.equal(events[0].args._contractsWhitelisted[0],true); + }); + + try { + await dxDaoSchemeConstraints.updateContractsWhitelist([standardTokenMock.address],[true, false],{from:accounts[3]}); + assert(false, "invalid length _periodLimitTokensAddresses"); + } catch(error) { + helpers.assertVMException(error); + } + + try { + await dxDaoSchemeConstraints.updatePeriodLimitsTokens([standardTokenMock.address],[10000, 500],{from:accounts[3]}); + assert(false, "invalid length _tokensPeriodLimits"); + } catch(error) { + helpers.assertVMException(error); + } + + }); + + it("update contraints whitelist after proposal call and before execute and check spender", async function() { + var actionMock =await ActionMock.new(); + var standardTokenMock = await ERC20Mock.new(accounts[0],1000); + var testSetup = await setup(accounts,[actionMock.address,accounts[3],standardTokenMock.address],0,true,standardTokenMock.address); + var encodedTokenApproval= await createCallToTokenApproval(standardTokenMock, accounts[3], 1000); + var callData1 = await createCallToActionMock(testSetup.org.avatar.address,actionMock); + + var tx = await testSetup.genericSchemeMultiCall.proposeCalls([actionMock.address,standardTokenMock.address], + [callData1,encodedTokenApproval], + [0,0], + helpers.NULL_HASH); + + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId'); + + await testSetup.schemeConstraints.updateContractsWhitelist([accounts[3]],[false],{from:accounts[4]}); + + await testSetup.genericSchemeParams.votingMachine.genesisProtocol.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + + try { + await testSetup.genericSchemeMultiCall.execute(proposalId); + assert(false, "contract was blacklisted"); + } catch(error) { + helpers.assertVMException(error); + } + await testSetup.schemeConstraints.updateContractsWhitelist([accounts[3]],[true],{from:accounts[4]}); + await testSetup.genericSchemeMultiCall.execute(proposalId); + }); + + it("update contraints whitelist after proposal call and before execute", async function() { + var actionMock =await ActionMock.new(); + var standardTokenMock = await ERC20Mock.new(accounts[0],1000); + var testSetup = await setup(accounts,[actionMock.address,accounts[3],standardTokenMock.address],0,true,standardTokenMock.address); + var encodedTokenApproval= await createCallToTokenApproval(standardTokenMock, accounts[3], 1000); + var callData1 = await createCallToActionMock(testSetup.org.avatar.address,actionMock); + + var tx = await testSetup.genericSchemeMultiCall.proposeCalls([actionMock.address,standardTokenMock.address], + [callData1,encodedTokenApproval], + [0,0], + helpers.NULL_HASH); + + var proposalId = await helpers.getValueFromLogs(tx, '_proposalId'); + + await testSetup.schemeConstraints.updateContractsWhitelist([actionMock.address],[false],{from:accounts[4]}); + await testSetup.genericSchemeParams.votingMachine.genesisProtocol.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); + + try { + await testSetup.genericSchemeMultiCall.execute(proposalId); + assert(false, "contract was blacklisted"); + } catch(error) { + helpers.assertVMException(error); + } + await testSetup.schemeConstraints.updateContractsWhitelist([actionMock.address],[true],{from:accounts[4]}); + await testSetup.genericSchemeMultiCall.execute(proposalId); + }); + + + it("can only update contraint whitelist & limits from avatar with correct array length", async function() { + var standardTokenMock = await ERC20Mock.new(accounts[0],1000); + var dxDaoSchemeConstraints =await DxDaoSchemeConstraints.new(); + try { + await dxDaoSchemeConstraints.updateContractsWhitelist([standardTokenMock.address],[true, false],{from:accounts[3]}); + assert(false, "invalid length _periodLimitTokensAddresses"); + } catch(error) { + helpers.assertVMException(error); + } + + try { + await dxDaoSchemeConstraints.updatePeriodLimitsTokens([standardTokenMock.address],[10000, 500],{from:accounts[3]}); + assert(false, "invalid length _tokensPeriodLimits"); + } catch(error) { + helpers.assertVMException(error); + } + }); + + it("calculates the observationIndex correctly", async function() { + var actionMock =await ActionMock.new(); + var standardTokenMock = await ERC20Mock.new(accounts[0],1000); + var testSetup = await setup(accounts,[actionMock.address],0,true,standardTokenMock.address); + var dxDaoSchemeConstraints =await DxDaoSchemeConstraints.new(); + + // 10 seconds period + await dxDaoSchemeConstraints.initialize( + testSetup.org.avatar.address, + 10, + 0, + [], + [], + [accounts[0]], + testSetup.genericSchemeMultiCall.address + ); + assert.equal((await dxDaoSchemeConstraints.observationIndex()).toString(),0); + await helpers.increaseTime(10); + assert.equal((await dxDaoSchemeConstraints.observationIndex()).toString(),1); + await helpers.increaseTime(3600); // adding 1 hour + assert.equal((await dxDaoSchemeConstraints.observationIndex()).toString(),361); + await helpers.increaseTime(86400); // adding 1 day + assert.equal((await dxDaoSchemeConstraints.observationIndex()).toString(),9001); + await helpers.increaseTime(315360000);// adding 10 year + assert.equal((await dxDaoSchemeConstraints.observationIndex()).toString(),31545001); + var dxDaoSchemeConstraints2 =await DxDaoSchemeConstraints.new(); + + // 7 days period + await dxDaoSchemeConstraints2.initialize( + testSetup.org.avatar.address, + 604800, + 0, + [], + [], + [accounts[0]], + testSetup.genericSchemeMultiCall.address + ); + assert.equal((await dxDaoSchemeConstraints2.observationIndex()).toString(),0); + await helpers.increaseTime(50); + assert.equal((await dxDaoSchemeConstraints2.observationIndex()).toString(),0); + await helpers.increaseTime(604750); + assert.equal((await dxDaoSchemeConstraints2.observationIndex()).toString(),1); + await helpers.increaseTime(604800); // adding 7 days + assert.equal((await dxDaoSchemeConstraints2.observationIndex()).toString(),2); + await helpers.increaseTime(604800); // adding 7 days + assert.equal((await dxDaoSchemeConstraints2.observationIndex()).toString(),3); + await helpers.increaseTime(604800); // adding 7 days + assert.equal((await dxDaoSchemeConstraints2.observationIndex()).toString(),4); + await helpers.increaseTime(604800); // adding 7 days + assert.equal((await dxDaoSchemeConstraints2.observationIndex()).toString(),5); + await helpers.increaseTime(9072000); // adding 15 days + assert.equal((await dxDaoSchemeConstraints2.observationIndex()).toString(),20); + await helpers.increaseTime(6048000); // adding 10 days + assert.equal((await dxDaoSchemeConstraints2.observationIndex()).toString(),30); + await helpers.increaseTime(42336000); // adding 70 days + assert.equal((await dxDaoSchemeConstraints2.observationIndex()).toString(),100); + await helpers.increaseTime(604800000); // adding 1000 days + assert.equal((await dxDaoSchemeConstraints2.observationIndex()).toString(),1100); + }); + + it("only genericSchemeMultiCall allow to call isAllowedToCall", async function() { + var actionMock =await ActionMock.new(); + var testSetup = await setup(accounts,[actionMock.address]); + const encodeABI = await new web3.eth.Contract(actionMock.abi).methods.withoutReturnValue(testSetup.org.avatar.address).encodeABI(); + + try { + await testSetup.schemeConstraints.isAllowedToCall( + [actionMock.address], + [encodeABI], + [1], + accounts[4]); + assert(false, "only genericSchemeMultiCall allow to call isAllowedToCall"); + } catch(error) { + helpers.assertVMException(error); + } + }); + });