From 302621ac8de1f2fbf1461b036004b4ef7a7327b8 Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 9 Jul 2020 10:51:41 +0300 Subject: [PATCH 1/7] Add Transition Scheme --- contracts/schemes/TransitionScheme.sol | 78 ++++++++++++ package-lock.json | 58 ++++++--- package.json | 2 +- test/transitionscheme.js | 157 +++++++++++++++++++++++++ 4 files changed, 280 insertions(+), 15 deletions(-) create mode 100644 contracts/schemes/TransitionScheme.sol create mode 100644 test/transitionscheme.js diff --git a/contracts/schemes/TransitionScheme.sol b/contracts/schemes/TransitionScheme.sol new file mode 100644 index 00000000..0a00663a --- /dev/null +++ b/contracts/schemes/TransitionScheme.sol @@ -0,0 +1,78 @@ +pragma solidity 0.5.17; + +import "../controller/Controller.sol"; + +/** + * @title A scheme for transitioning the DAO's assets to a new one. + */ + +contract TransitionScheme { + + Avatar public avatar; + address payable public newAvatar; + address[] public externalTokens; + address[] public assetAddresses; + bytes4[] public selectors; + + /** + * @dev initialize + * @param _avatar the avatar to migrate from + * @param _newAvatar the avatar to migrate to + * @param _externalTokens external tokens to allow transfer to the new avatar + * @param _assetAddresses the assets to transfer + * @param _selectors the functions to call to to transfer the assets + */ + function initialize( + Avatar _avatar, + address payable _newAvatar, + address[] calldata _externalTokens, + address[] calldata _assetAddresses, + bytes4[] calldata _selectors + ) external { + require(_assetAddresses.length == _selectors.length, "Arrays length mismatch"); + require(avatar == Avatar(0), "can be called only one time"); + require(_avatar != Avatar(0), "avatar cannot be zero"); + avatar = _avatar; + newAvatar = _newAvatar; + externalTokens = _externalTokens; + assetAddresses = _assetAddresses; + selectors = _selectors; + } + + /** + * @dev transferAssets function + * transfer the DAO assets to a new DAO + */ + function transferAssets() external { + for (uint256 i=0; i < assetAddresses.length; i++) { + Controller(avatar.owner()).genericCall(assetAddresses[i], abi.encodeWithSelector(selectors[i], newAvatar), avatar, 0); + } + } + + /** + * @dev sendEther function + * @param _amount the amount of ether to send to the new avatar + */ + function sendEther(uint256 _amount) external { + require( + Controller(avatar.owner()).sendEther(_amount, newAvatar, avatar), + "Sending ether should succeed" + ); + } + + /** + * @dev sendExternalToken function + * @param _amounts the amounts of tokens to send to the new avatar + */ + function sendExternalToken(uint256[] calldata _amounts) external { + require(externalTokens.length == _amounts.length, "Arrays length mismatch"); + for (uint256 i=0; i < externalTokens.length; i++) { + if (_amounts[i] > 0) { + require( + Controller(avatar.owner()).externalTokenTransfer(IERC20(externalTokens[i]), newAvatar, _amounts[i], avatar), + "Sending external token should succeed" + ); + } + } + } +} diff --git a/package-lock.json b/package-lock.json index ae83e662..2d81c9f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@daostack/arc", - "version": "0.0.1-rc.41", + "version": "0.0.1-rc.42", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -221,9 +221,9 @@ "integrity": "sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==" }, "acorn": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", - "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", "dev": true }, "acorn-globals": { @@ -4683,9 +4683,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "lodash.findindex": { @@ -4837,9 +4837,9 @@ } }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "mixin-deep": { "version": "1.3.2", @@ -4863,11 +4863,11 @@ } }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "requires": { - "minimist": "0.0.8" + "minimist": "^1.2.5" } }, "mocha": { @@ -4918,6 +4918,21 @@ "path-is-absolute": "^1.0.0" } }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -5404,6 +5419,21 @@ "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", "dev": true }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/package.json b/package.json index 1f9593c8..9213c62e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@daostack/arc", - "version": "0.0.1-rc.41", + "version": "0.0.1-rc.42", "description": "A platform for building DAOs", "files": [ "contracts/", diff --git a/test/transitionscheme.js b/test/transitionscheme.js new file mode 100644 index 00000000..53ec7ad6 --- /dev/null +++ b/test/transitionscheme.js @@ -0,0 +1,157 @@ +const helpers = require('./helpers'); +const DaoCreator = artifacts.require('./DaoCreator.sol'); +const ControllerCreator = artifacts.require('./ControllerCreator.sol'); +const DAOTracker = artifacts.require('./DAOTracker.sol'); +const constants = require('./constants'); +const ERC20Mock = artifacts.require('./test/ERC20Mock.sol'); +const Wallet = artifacts.require("./Wallet.sol"); +const TransitionScheme = artifacts.require('./TransitionScheme.sol'); + +let selector = web3.eth.abi.encodeFunctionSignature('transferOwnership(address)'); + +const setup = async function( + accounts, + testInitDifferentArrayLength=false +) { + var testSetup = new helpers.TestSetup(); + var controllerCreator = await ControllerCreator.new({ + gas: constants.ARC_GAS_LIMIT + }); + var daoTracker = await DAOTracker.new({ gas: constants.ARC_GAS_LIMIT }); + testSetup.daoCreator = await DaoCreator.new( + controllerCreator.address, + daoTracker.address, + { gas: constants.ARC_GAS_LIMIT } + ); + + testSetup.org = await helpers.setupOrganization( + testSetup.daoCreator, + accounts[0], + 1000, + 1000 + ); + testSetup.wallet = await Wallet.new(); + testSetup.wallet.transferOwnership(testSetup.org.avatar.address); + testSetup.standardToken = await ERC20Mock.new(testSetup.org.avatar.address, 100); + + testSetup.transitionScheme = await TransitionScheme.new(); + testSetup.selectors = [selector]; + if (testInitDifferentArrayLength) { + testSetup.selectors = [selector, selector]; + } + + await testSetup.transitionScheme.initialize( + testSetup.org.avatar.address, + helpers.SOME_ADDRESS, + [testSetup.standardToken.address], + [testSetup.wallet.address], + testSetup.selectors, + { gas: constants.ARC_GAS_LIMIT } + ); + + var permissions = '0x00000010'; + await testSetup.daoCreator.setSchemes( + testSetup.org.avatar.address, + [testSetup.transitionScheme.address], + [web3.utils.asciiToHex('0')], + [permissions], + 'metaData' + ); + + return testSetup; +}; + +contract('TransitionScheme', accounts => { + it('initialize', async () => { + let testSetup = await setup(accounts); + + assert.equal( + await testSetup.transitionScheme.avatar(), + testSetup.org.avatar.address + ); + assert.equal( + await testSetup.transitionScheme.newAvatar(), + helpers.SOME_ADDRESS + ); + assert.equal( + await testSetup.transitionScheme.externalTokens(0), + testSetup.standardToken.address + ); + assert.equal( + await testSetup.transitionScheme.assetAddresses(0), + testSetup.wallet.address + ); + assert.equal(await testSetup.transitionScheme.selectors(0), selector); + }); + + it('initialize assets and selector arrays must be same length', async () => { + try { + await setup(accounts, true); + assert(false, 'assets and selector arrays must be same length'); + } catch (error) { + helpers.assertVMException(error); + } + }); + + it('transfer assets', async () => { + let testSetup = await setup(accounts); + assert.equal(await testSetup.wallet.owner(), testSetup.org.avatar.address); + await testSetup.transitionScheme.transferAssets(); + assert.equal(await testSetup.wallet.owner(), helpers.SOME_ADDRESS); + }); + + it('transfer avatar ether', async () => { + let testSetup = await setup(accounts); + await web3.eth.sendTransaction({from:accounts[0],to: testSetup.org.avatar.address, value: web3.utils.toWei('1', 'ether')}); + assert.equal(await web3.eth.getBalance(testSetup.org.avatar.address),web3.utils.toWei('1', "ether")); + assert.equal(await web3.eth.getBalance(helpers.SOME_ADDRESS), 0); + await testSetup.transitionScheme.sendEther(web3.utils.toWei('1', 'ether')); + assert.equal(await web3.eth.getBalance(testSetup.org.avatar.address), 0); + assert.equal(await web3.eth.getBalance(helpers.SOME_ADDRESS),web3.utils.toWei('1', "ether")); + + await web3.eth.sendTransaction({from:accounts[0],to: testSetup.org.avatar.address, value: web3.utils.toWei('1', 'ether')}); + assert.equal(await web3.eth.getBalance(testSetup.org.avatar.address),web3.utils.toWei('1', "ether")); + assert.equal(await web3.eth.getBalance(helpers.SOME_ADDRESS),web3.utils.toWei('1', "ether")); + await testSetup.transitionScheme.sendEther(web3.utils.toWei('1', 'ether')); + assert.equal(await web3.eth.getBalance(testSetup.org.avatar.address), 0); + assert.equal(await web3.eth.getBalance(helpers.SOME_ADDRESS),web3.utils.toWei('2', "ether")); + }); + + it('transfer avatar external tokens', async () => { + let testSetup = await setup(accounts); + assert.equal(await testSetup.standardToken.balanceOf(testSetup.org.avatar.address), 100); + assert.equal(await testSetup.standardToken.balanceOf(helpers.SOME_ADDRESS), 0); + await testSetup.transitionScheme.sendExternalToken([100]); + assert.equal(await testSetup.standardToken.balanceOf(testSetup.org.avatar.address), 0); + assert.equal(await testSetup.standardToken.balanceOf(helpers.SOME_ADDRESS), 100); + }); + + it('external tokens and amounts arrays must be same length', async () => { + let testSetup = await setup(accounts); + assert.equal(await testSetup.standardToken.balanceOf(testSetup.org.avatar.address), 100); + assert.equal(await testSetup.standardToken.balanceOf(helpers.SOME_ADDRESS), 0); + try { + await testSetup.transitionScheme.sendExternalToken([100, 10]); + assert(false, 'external tokens and amounts arrays must be same length'); + } catch (error) { + helpers.assertVMException(error); + } + }); + + it('cannot initialize twice', async () => { + let testSetup = await setup(accounts); + try { + await testSetup.transitionScheme.initialize( + testSetup.org.avatar.address, + helpers.SOME_ADDRESS, + [testSetup.standardToken.address], + [testSetup.wallet.address], + testSetup.selectors, + { gas: constants.ARC_GAS_LIMIT } + ); + assert(false, 'cannot initialize twice'); + } catch (error) { + helpers.assertVMException(error); + } + }); +}); From 86584f313906dd98afbf07b9ac5a4904081be6b2 Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 9 Jul 2020 11:01:05 +0300 Subject: [PATCH 2/7] solhint --- contracts/schemes/TransitionScheme.sol | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/contracts/schemes/TransitionScheme.sol b/contracts/schemes/TransitionScheme.sol index 0a00663a..1372c17c 100644 --- a/contracts/schemes/TransitionScheme.sol +++ b/contracts/schemes/TransitionScheme.sol @@ -45,7 +45,12 @@ contract TransitionScheme { */ function transferAssets() external { for (uint256 i=0; i < assetAddresses.length; i++) { - Controller(avatar.owner()).genericCall(assetAddresses[i], abi.encodeWithSelector(selectors[i], newAvatar), avatar, 0); + Controller(avatar.owner()).genericCall( + assetAddresses[i], + abi.encodeWithSelector(selectors[i], newAvatar), + avatar, + 0 + ); } } @@ -69,7 +74,12 @@ contract TransitionScheme { for (uint256 i=0; i < externalTokens.length; i++) { if (_amounts[i] > 0) { require( - Controller(avatar.owner()).externalTokenTransfer(IERC20(externalTokens[i]), newAvatar, _amounts[i], avatar), + Controller(avatar.owner()).externalTokenTransfer( + IERC20(externalTokens[i]), + newAvatar, + _amounts[i], + avatar + ), "Sending external token should succeed" ); } From 38f488712a294b3630c6429ebb6481e2ed5f38e2 Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 9 Jul 2020 12:42:16 +0300 Subject: [PATCH 3/7] Add asset transferred event --- contracts/schemes/TransitionScheme.sol | 11 ++++++++++- test/transitionscheme.js | 12 +++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/contracts/schemes/TransitionScheme.sol b/contracts/schemes/TransitionScheme.sol index 1372c17c..5ca140af 100644 --- a/contracts/schemes/TransitionScheme.sol +++ b/contracts/schemes/TransitionScheme.sol @@ -8,6 +8,8 @@ import "../controller/Controller.sol"; contract TransitionScheme { + event OwnershipTransferred(address indexed _avatar, address indexed _newAvatar, address indexed _asset); + Avatar public avatar; address payable public newAvatar; address[] public externalTokens; @@ -45,12 +47,19 @@ contract TransitionScheme { */ function transferAssets() external { for (uint256 i=0; i < assetAddresses.length; i++) { - Controller(avatar.owner()).genericCall( + bytes memory genericCallReturnValue; + bool success; + Controller controller = Controller(avatar.owner()); + (success, genericCallReturnValue) = + controller.genericCall( assetAddresses[i], abi.encodeWithSelector(selectors[i], newAvatar), avatar, 0 ); + if (success) { + emit OwnershipTransferred(avatar, newAvatar, assetAddresses[i]) + } } } diff --git a/test/transitionscheme.js b/test/transitionscheme.js index 53ec7ad6..24a6d2ba 100644 --- a/test/transitionscheme.js +++ b/test/transitionscheme.js @@ -96,7 +96,17 @@ contract('TransitionScheme', accounts => { it('transfer assets', async () => { let testSetup = await setup(accounts); assert.equal(await testSetup.wallet.owner(), testSetup.org.avatar.address); - await testSetup.transitionScheme.transferAssets(); + let tx = await testSetup.transitionScheme.transferAssets(); + await testSetup.transitionScheme.getPastEvents('OwnershipTransferred', { + fromBlock: tx.blockNumber, + toBlock: 'latest' + }) + .then(function(events){ + assert.equal(events[0].event,"OwnershipTransferred"); + assert.equal(events[0].args._avatar, testSetup.org.avatar.avatar); + assert.equal(events[0].args._newAvatar, helpers.SOME_ADDRESS); + assert.equal(events[0].args._asset, testSetup.wallet.address); + }); assert.equal(await testSetup.wallet.owner(), helpers.SOME_ADDRESS); }); From 54a164e72b3b823c2695d6fbd6ec67c8fd1ee165 Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 9 Jul 2020 12:54:12 +0300 Subject: [PATCH 4/7] Fix --- contracts/schemes/TransitionScheme.sol | 4 ++-- test/transitionscheme.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/schemes/TransitionScheme.sol b/contracts/schemes/TransitionScheme.sol index 5ca140af..9d9e3124 100644 --- a/contracts/schemes/TransitionScheme.sol +++ b/contracts/schemes/TransitionScheme.sol @@ -8,7 +8,7 @@ import "../controller/Controller.sol"; contract TransitionScheme { - event OwnershipTransferred(address indexed _avatar, address indexed _newAvatar, address indexed _asset); + event OwnershipTransferred(Avatar indexed _avatar, address indexed _newAvatar, address indexed _asset); Avatar public avatar; address payable public newAvatar; @@ -58,7 +58,7 @@ contract TransitionScheme { 0 ); if (success) { - emit OwnershipTransferred(avatar, newAvatar, assetAddresses[i]) + emit OwnershipTransferred(avatar, newAvatar, assetAddresses[i]); } } } diff --git a/test/transitionscheme.js b/test/transitionscheme.js index 24a6d2ba..d3c331f5 100644 --- a/test/transitionscheme.js +++ b/test/transitionscheme.js @@ -103,7 +103,7 @@ contract('TransitionScheme', accounts => { }) .then(function(events){ assert.equal(events[0].event,"OwnershipTransferred"); - assert.equal(events[0].args._avatar, testSetup.org.avatar.avatar); + assert.equal(events[0].args._avatar, testSetup.org.avatar.address); assert.equal(events[0].args._newAvatar, helpers.SOME_ADDRESS); assert.equal(events[0].args._asset, testSetup.wallet.address); }); From 9031543e3631603fa7ba4f8c95c49b1e67ab0fc4 Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 9 Jul 2020 16:14:13 +0300 Subject: [PATCH 5/7] Add large test --- test/transitionscheme.js | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/test/transitionscheme.js b/test/transitionscheme.js index d3c331f5..ba2645eb 100644 --- a/test/transitionscheme.js +++ b/test/transitionscheme.js @@ -11,7 +11,8 @@ let selector = web3.eth.abi.encodeFunctionSignature('transferOwnership(address)' const setup = async function( accounts, - testInitDifferentArrayLength=false + testInitDifferentArrayLength=false, + testLimit=false ) { var testSetup = new helpers.TestSetup(); var controllerCreator = await ControllerCreator.new({ @@ -30,8 +31,11 @@ const setup = async function( 1000, 1000 ); + testSetup.wallet = await Wallet.new(); - testSetup.wallet.transferOwnership(testSetup.org.avatar.address); + await testSetup.wallet.transferOwnership(testSetup.org.avatar.address); + testSetup.assets = [testSetup.wallet.address]; + testSetup.standardToken = await ERC20Mock.new(testSetup.org.avatar.address, 100); testSetup.transitionScheme = await TransitionScheme.new(); @@ -40,11 +44,20 @@ const setup = async function( testSetup.selectors = [selector, selector]; } + if (testLimit) { + for (let i=0; i < 100; i++) { + let wallet = await Wallet.new(); + await wallet.transferOwnership(testSetup.org.avatar.address); + testSetup.assets.push(wallet.address); + testSetup.selectors.push(selector); + } + } + await testSetup.transitionScheme.initialize( testSetup.org.avatar.address, helpers.SOME_ADDRESS, [testSetup.standardToken.address], - [testSetup.wallet.address], + testSetup.assets, testSetup.selectors, { gas: constants.ARC_GAS_LIMIT } ); @@ -110,6 +123,24 @@ contract('TransitionScheme', accounts => { assert.equal(await testSetup.wallet.owner(), helpers.SOME_ADDRESS); }); + it('transfer many assets', async () => { + let testSetup = await setup(accounts, false, true); + assert.equal(await testSetup.wallet.owner(), testSetup.org.avatar.address); + let tx = await testSetup.transitionScheme.transferAssets(); + await testSetup.transitionScheme.getPastEvents('OwnershipTransferred', { + fromBlock: tx.blockNumber, + toBlock: 'latest' + }) + .then(function(events){ + assert.equal(events.length, 101); + assert.equal(events[0].event,"OwnershipTransferred"); + assert.equal(events[0].args._avatar, testSetup.org.avatar.address); + assert.equal(events[0].args._newAvatar, helpers.SOME_ADDRESS); + assert.equal(events[0].args._asset, testSetup.wallet.address); + }); + assert.equal(await testSetup.wallet.owner(), helpers.SOME_ADDRESS); + }); + it('transfer avatar ether', async () => { let testSetup = await setup(accounts); await web3.eth.sendTransaction({from:accounts[0],to: testSetup.org.avatar.address, value: web3.utils.toWei('1', 'ether')}); From 8fbf04dac5a2e89cb738721664080095378b76d7 Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 9 Jul 2020 18:18:58 +0300 Subject: [PATCH 6/7] Cap assets --- contracts/schemes/TransitionScheme.sol | 3 +++ test/transitionscheme.js | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/contracts/schemes/TransitionScheme.sol b/contracts/schemes/TransitionScheme.sol index 9d9e3124..d88e9742 100644 --- a/contracts/schemes/TransitionScheme.sol +++ b/contracts/schemes/TransitionScheme.sol @@ -8,6 +8,8 @@ import "../controller/Controller.sol"; contract TransitionScheme { + uint public constant ASSETS_CAP = 100; + event OwnershipTransferred(Avatar indexed _avatar, address indexed _newAvatar, address indexed _asset); Avatar public avatar; @@ -31,6 +33,7 @@ contract TransitionScheme { address[] calldata _assetAddresses, bytes4[] calldata _selectors ) external { + require(_assetAddresses.length <= ASSETS_CAP, "cannot transfer more than 100 assets"); require(_assetAddresses.length == _selectors.length, "Arrays length mismatch"); require(avatar == Avatar(0), "can be called only one time"); require(_avatar != Avatar(0), "avatar cannot be zero"); diff --git a/test/transitionscheme.js b/test/transitionscheme.js index ba2645eb..a86d1b7c 100644 --- a/test/transitionscheme.js +++ b/test/transitionscheme.js @@ -12,7 +12,8 @@ let selector = web3.eth.abi.encodeFunctionSignature('transferOwnership(address)' const setup = async function( accounts, testInitDifferentArrayLength=false, - testLimit=false + testLimit=false, + testOverLimit=false, ) { var testSetup = new helpers.TestSetup(); var controllerCreator = await ControllerCreator.new({ @@ -45,7 +46,7 @@ const setup = async function( } if (testLimit) { - for (let i=0; i < 100; i++) { + for (let i=0; i < (testOverLimit ? 100 : 99); i++) { let wallet = await Wallet.new(); await wallet.transferOwnership(testSetup.org.avatar.address); testSetup.assets.push(wallet.address); @@ -106,6 +107,15 @@ contract('TransitionScheme', accounts => { } }); + it('initialize with more than 100 assets should fail', async () => { + try { + await setup(accounts, false, true, true); + assert(false, 'initialize with more than 100 assets should fail'); + } catch (error) { + helpers.assertVMException(error); + } + }); + it('transfer assets', async () => { let testSetup = await setup(accounts); assert.equal(await testSetup.wallet.owner(), testSetup.org.avatar.address); @@ -132,7 +142,7 @@ contract('TransitionScheme', accounts => { toBlock: 'latest' }) .then(function(events){ - assert.equal(events.length, 101); + assert.equal(events.length, 100); assert.equal(events[0].event,"OwnershipTransferred"); assert.equal(events[0].args._avatar, testSetup.org.avatar.address); assert.equal(events[0].args._newAvatar, helpers.SOME_ADDRESS); From a273869e1a645dc2854979bbd1f988db039f3254 Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 9 Jul 2020 18:54:50 +0300 Subject: [PATCH 7/7] uint -> uint256 --- contracts/schemes/TransitionScheme.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/schemes/TransitionScheme.sol b/contracts/schemes/TransitionScheme.sol index d88e9742..fd28db21 100644 --- a/contracts/schemes/TransitionScheme.sol +++ b/contracts/schemes/TransitionScheme.sol @@ -8,7 +8,7 @@ import "../controller/Controller.sol"; contract TransitionScheme { - uint public constant ASSETS_CAP = 100; + uint256 public constant ASSETS_CAP = 100; event OwnershipTransferred(Avatar indexed _avatar, address indexed _newAvatar, address indexed _asset);