diff --git a/contracts/modules/Experimental/TransferManager/BlacklistTransferManager.sol b/contracts/modules/TransferManager/BTM/BlacklistTransferManager.sol similarity index 70% rename from contracts/modules/Experimental/TransferManager/BlacklistTransferManager.sol rename to contracts/modules/TransferManager/BTM/BlacklistTransferManager.sol index 124a5422b..36db17a2a 100644 --- a/contracts/modules/Experimental/TransferManager/BlacklistTransferManager.sol +++ b/contracts/modules/TransferManager/BTM/BlacklistTransferManager.sol @@ -1,37 +1,15 @@ pragma solidity ^0.5.0; -import "../../TransferManager/TransferManager.sol"; +import "../TransferManager.sol"; +import "./BlacklistTransferManagerStorage.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; /** * @title Transfer Manager module to automate blacklist and restrict transfers */ -contract BlacklistTransferManager is TransferManager { +contract BlacklistTransferManager is BlacklistTransferManagerStorage, TransferManager { using SafeMath for uint256; - struct BlacklistsDetails { - uint256 startTime; - uint256 endTime; - uint256 repeatPeriodTime; - } - - //hold the different blacklist details corresponds to its name - mapping(bytes32 => BlacklistsDetails) public blacklists; - - //hold the different name of blacklist corresponds to a investor - mapping(address => bytes32[]) investorToBlacklist; - - //get list of the addresses for a particular blacklist - mapping(bytes32 => address[]) blacklistToInvestor; - - //mapping use to store the indexes for different blacklist types for a investor - mapping(address => mapping(bytes32 => uint256)) investorToIndex; - - //mapping use to store the indexes for different investor for a blacklist type - mapping(bytes32 => mapping(address => uint256)) blacklistToIndex; - - bytes32[] allBlacklists; - // Emit when new blacklist type is added event AddBlacklistType( uint256 _startTime, @@ -72,9 +50,10 @@ contract BlacklistTransferManager is TransferManager { * @param _polyAddress Address of the polytoken */ constructor (address _securityToken, address _polyAddress) - public - Module(_securityToken, _polyAddress) + public + Module(_securityToken, _polyAddress) { + } /** @@ -92,8 +71,8 @@ contract BlacklistTransferManager is TransferManager { * if the current time is between the timeframe define for the * blacklist type associated with the _from address */ - function executeTransfer(address _from, address _to, uint256 _amount, bytes calldata _data) external returns(Result) { - (Result success, ) = verifyTransfer(_from, _to, _amount, _data); + function executeTransfer(address _from, address /*_to*/, uint256 /*_amount*/, bytes calldata /*_data*/) external returns(Result) { + (Result success, ) = _verifyTransfer(_from); return success; } @@ -110,25 +89,34 @@ contract BlacklistTransferManager is TransferManager { address /* _to */, uint256 /* _amount */, bytes memory/* _data */ - ) + ) public view returns(Result, bytes32) { + return _verifyTransfer(_from); + } + + function _verifyTransfer(address _from) internal view returns(Result, bytes32) { if (!paused) { if (investorToBlacklist[_from].length != 0) { for (uint256 i = 0; i < investorToBlacklist[_from].length; i++) { - uint256 endTimeTemp = blacklists[investorToBlacklist[_from][i]].endTime; - uint256 startTimeTemp = blacklists[investorToBlacklist[_from][i]].startTime; - uint256 repeatPeriodTimeTemp = blacklists[investorToBlacklist[_from][i]].repeatPeriodTime * 1 days; + bytes32 blacklistName = investorToBlacklist[_from][i]; + uint256 endTimeTemp = blacklists[blacklistName].endTime; + uint256 startTimeTemp = blacklists[blacklistName].startTime; + uint256 repeatPeriodTimeTemp = blacklists[blacklistName].repeatPeriodTime * 1 days; /*solium-disable-next-line security/no-block-members*/ if (now > startTimeTemp) { - // Find the repeating parameter that will be used to calculate the new startTime and endTime - // based on the new current time value - /*solium-disable-next-line security/no-block-members*/ - uint256 repeater = (now.sub(startTimeTemp)).div(repeatPeriodTimeTemp); - /*solium-disable-next-line security/no-block-members*/ - if (startTimeTemp.add(repeatPeriodTimeTemp.mul(repeater)) <= now && endTimeTemp.add(repeatPeriodTimeTemp.mul(repeater)) >= now) { + if (repeatPeriodTimeTemp > 0) { + // Find the repeating parameter that will be used to calculate the new startTime and endTime + // based on the new current time value + /*solium-disable-next-line security/no-block-members*/ + uint256 repeater = (now.sub(startTimeTemp)).div(repeatPeriodTimeTemp); + /*solium-disable-next-line security/no-block-members*/ + if (endTimeTemp.add(repeatPeriodTimeTemp.mul(repeater)) >= now) { + return (Result.INVALID, bytes32(uint256(address(this)) << 96)); + } + } else if(endTimeTemp >= now) { return (Result.INVALID, bytes32(uint256(address(this)) << 96)); } } @@ -144,12 +132,24 @@ contract BlacklistTransferManager is TransferManager { * @param _startTime Start date of the blacklist type * @param _endTime End date of the blacklist type * @param _blacklistName Name of the blacklist type - * @param _repeatPeriodTime Repeat period of the blacklist type + * @param _repeatPeriodTime Repeat period of the blacklist type in days */ function addBlacklistType(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime) public withPerm(ADMIN) { _addBlacklistType(_startTime, _endTime, _blacklistName, _repeatPeriodTime); } + function _addBlacklistType(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime) internal { + require(blacklists[_blacklistName].endTime == 0, "Blacklist type already exist"); + _addBlacklistTypeDetails(_startTime, _endTime, _blacklistName, _repeatPeriodTime); + allBlacklists.push(_blacklistName); + emit AddBlacklistType(_startTime, _endTime, _blacklistName, _repeatPeriodTime); + } + + function _addBlacklistTypeDetails(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime) internal { + _validParams(_startTime, _endTime, _blacklistName, _repeatPeriodTime); + blacklists[_blacklistName] = BlacklistsDetails(_startTime, _endTime, _repeatPeriodTime); + } + /** * @notice Used to add the multiple blacklist type * @param _startTimes Start date of the blacklist type @@ -158,29 +158,20 @@ contract BlacklistTransferManager is TransferManager { * @param _repeatPeriodTimes Repeat period of the blacklist type */ function addBlacklistTypeMulti( - uint256[] calldata _startTimes, - uint256[] calldata _endTimes, - bytes32[] calldata _blacklistNames, - uint256[] calldata _repeatPeriodTimes + uint256[] memory _startTimes, + uint256[] memory _endTimes, + bytes32[] memory _blacklistNames, + uint256[] memory _repeatPeriodTimes ) - external + public withPerm(ADMIN) { require (_startTimes.length == _endTimes.length && _endTimes.length == _blacklistNames.length && _blacklistNames.length == _repeatPeriodTimes.length, "Input array's length mismatch"); - for (uint256 i = 0; i < _startTimes.length; i++){ + for (uint256 i = 0; i < _startTimes.length; i++) { _addBlacklistType(_startTimes[i], _endTimes[i], _blacklistNames[i], _repeatPeriodTimes[i]); } } - /** - * @notice Internal function - */ - function _validParams(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime) internal view { - require(_blacklistName != bytes32(0), "Invalid blacklist name"); - require(_startTime >= now && _startTime < _endTime, "Invalid start or end date"); - require(_repeatPeriodTime.mul(1 days) >= _endTime.sub(_startTime) || _repeatPeriodTime == 0); - } - /** * @notice Used to modify the details of a given blacklist type * @param _startTime Start date of the blacklist type @@ -189,9 +180,12 @@ contract BlacklistTransferManager is TransferManager { * @param _repeatPeriodTime Repeat period of the blacklist type */ function modifyBlacklistType(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime) public withPerm(ADMIN) { + _modifyBlacklistType(_startTime, _endTime, _blacklistName, _repeatPeriodTime); + } + + function _modifyBlacklistType(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime) internal { require(blacklists[_blacklistName].endTime != 0, "Blacklist type doesn't exist"); - _validParams(_startTime, _endTime, _blacklistName, _repeatPeriodTime); - blacklists[_blacklistName] = BlacklistsDetails(_startTime, _endTime, _repeatPeriodTime); + _addBlacklistTypeDetails(_startTime, _endTime, _blacklistName, _repeatPeriodTime); emit ModifyBlacklistType(_startTime, _endTime, _blacklistName, _repeatPeriodTime); } @@ -203,17 +197,17 @@ contract BlacklistTransferManager is TransferManager { * @param _repeatPeriodTimes Repeat period of the blacklist type */ function modifyBlacklistTypeMulti( - uint256[] calldata _startTimes, - uint256[] calldata _endTimes, - bytes32[] calldata _blacklistNames, - uint256[] calldata _repeatPeriodTimes + uint256[] memory _startTimes, + uint256[] memory _endTimes, + bytes32[] memory _blacklistNames, + uint256[] memory _repeatPeriodTimes ) - external + public withPerm(ADMIN) { require (_startTimes.length == _endTimes.length && _endTimes.length == _blacklistNames.length && _blacklistNames.length == _repeatPeriodTimes.length, "Input array's length mismatch"); - for (uint256 i = 0; i < _startTimes.length; i++){ - modifyBlacklistType(_startTimes[i], _endTimes[i], _blacklistNames[i], _repeatPeriodTimes[i]); + for (uint256 i = 0; i < _startTimes.length; i++) { + _modifyBlacklistType(_startTimes[i], _endTimes[i], _blacklistNames[i], _repeatPeriodTimes[i]); } } @@ -222,18 +216,23 @@ contract BlacklistTransferManager is TransferManager { * @param _blacklistName Name of the blacklist type */ function deleteBlacklistType(bytes32 _blacklistName) public withPerm(ADMIN) { + _deleteBlacklistType(_blacklistName); + } + + function _deleteBlacklistType(bytes32 _blacklistName) internal { require(blacklists[_blacklistName].endTime != 0, "Blacklist type doesn’t exist"); require(blacklistToInvestor[_blacklistName].length == 0, "Investors are associated with the blacklist"); // delete blacklist type delete(blacklists[_blacklistName]); uint256 i = 0; - for (i = 0; i < allBlacklists.length; i++) { + uint256 blackListLength = allBlacklists.length; + for (i = 0; i < blackListLength; i++) { if (allBlacklists[i] == _blacklistName) { break; } } - if (i != allBlacklists.length -1) { - allBlacklists[i] = allBlacklists[allBlacklists.length -1]; + if (i != blackListLength - 1) { + allBlacklists[i] = allBlacklists[blackListLength -1]; } allBlacklists.length--; emit DeleteBlacklistType(_blacklistName); @@ -243,9 +242,9 @@ contract BlacklistTransferManager is TransferManager { * @notice Used to delete the multiple blacklist type * @param _blacklistNames Name of the blacklist type */ - function deleteBlacklistTypeMulti(bytes32[] calldata _blacklistNames) external withPerm(ADMIN) { - for(uint256 i = 0; i < _blacklistNames.length; i++){ - deleteBlacklistType(_blacklistNames[i]); + function deleteBlacklistTypeMulti(bytes32[] memory _blacklistNames) public withPerm(ADMIN) { + for(uint256 i = 0; i < _blacklistNames.length; i++) { + _deleteBlacklistType(_blacklistNames[i]); } } @@ -255,11 +254,13 @@ contract BlacklistTransferManager is TransferManager { * @param _blacklistName Name of the blacklist */ function addInvestorToBlacklist(address _investor, bytes32 _blacklistName) public withPerm(ADMIN) { + _addInvestorToBlacklist(_investor, _blacklistName); + } + + function _addInvestorToBlacklist(address _investor, bytes32 _blacklistName) internal { require(blacklists[_blacklistName].endTime != 0, "Blacklist type doesn't exist"); require(_investor != address(0), "Invalid investor address"); - uint256 index = investorToIndex[_investor][_blacklistName]; - if (index < investorToBlacklist[_investor].length) - require(investorToBlacklist[_investor][index] != _blacklistName, "Blacklist already added to investor"); + require(investorToIndex[_investor][_blacklistName] == 0, "Blacklist already added to investor"); uint256 investorIndex = investorToBlacklist[_investor].length; // Add blacklist index to the investor investorToIndex[_investor][_blacklistName] = investorIndex; @@ -276,9 +277,9 @@ contract BlacklistTransferManager is TransferManager { * @param _investors Address of the investor * @param _blacklistName Name of the blacklist */ - function addInvestorToBlacklistMulti(address[] calldata _investors, bytes32 _blacklistName) external withPerm(ADMIN){ - for(uint256 i = 0; i < _investors.length; i++){ - addInvestorToBlacklist(_investors[i], _blacklistName); + function addInvestorToBlacklistMulti(address[] memory _investors, bytes32 _blacklistName) public withPerm(ADMIN) { + for(uint256 i = 0; i < _investors.length; i++) { + _addInvestorToBlacklist(_investors[i], _blacklistName); } } @@ -287,10 +288,10 @@ contract BlacklistTransferManager is TransferManager { * @param _investors Address of the investor * @param _blacklistNames Name of the blacklist */ - function addMultiInvestorToBlacklistMulti(address[] calldata _investors, bytes32[] calldata _blacklistNames) external withPerm(ADMIN){ + function addMultiInvestorToBlacklistMulti(address[] memory _investors, bytes32[] memory _blacklistNames) public withPerm(ADMIN) { require (_investors.length == _blacklistNames.length, "Input array's length mismatch"); - for(uint256 i = 0; i < _investors.length; i++){ - addInvestorToBlacklist(_investors[i], _blacklistNames[i]); + for(uint256 i = 0; i < _investors.length; i++) { + _addInvestorToBlacklist(_investors[i], _blacklistNames[i]); } } @@ -302,32 +303,24 @@ contract BlacklistTransferManager is TransferManager { * @param _repeatPeriodTime Repeat period of the blacklist type * @param _investor Address of the investor */ - function addInvestorToNewBlacklist(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime, address _investor) external withPerm(ADMIN){ + function addInvestorToNewBlacklist( + uint256 _startTime, + uint256 _endTime, + bytes32 _blacklistName, + uint256 _repeatPeriodTime, + address _investor + ) public withPerm(ADMIN) { _addBlacklistType(_startTime, _endTime, _blacklistName, _repeatPeriodTime); - addInvestorToBlacklist(_investor, _blacklistName); + _addInvestorToBlacklist(_investor, _blacklistName); } /** - * @notice Used to delete the investor from all the associated blacklist types - * @param _investor Address of the investor - */ - function deleteInvestorFromAllBlacklist(address _investor) public withPerm(ADMIN) { - require(_investor != address(0), "Invalid investor address"); - require(investorToBlacklist[_investor].length != 0, "Investor is not associated to any blacklist type"); - uint256 index = investorToBlacklist[_investor].length - 1; - for (uint256 i = index; i >= 0 && i <= index; i--){ - deleteInvestorFromBlacklist(_investor, investorToBlacklist[_investor][i]); - } - } - - /** - * @notice Used to delete the multiple investor from all the associated blacklist types + * @notice Used to delete the investor from the blacklist * @param _investor Address of the investor + * @param _blacklistName Name of the blacklist */ - function deleteInvestorFromAllBlacklistMulti(address[] calldata _investor) external withPerm(ADMIN) { - for(uint256 i = 0; i < _investor.length; i++){ - deleteInvestorFromAllBlacklist(_investor[i]); - } + function deleteInvestorFromBlacklist(address _investor, bytes32 _blacklistName) public withPerm(ADMIN) { + _deleteInvestorFromBlacklist(_investor, _blacklistName); } /** @@ -335,7 +328,7 @@ contract BlacklistTransferManager is TransferManager { * @param _investor Address of the investor * @param _blacklistName Name of the blacklist */ - function deleteInvestorFromBlacklist(address _investor, bytes32 _blacklistName) public withPerm(ADMIN) { + function _deleteInvestorFromBlacklist(address _investor, bytes32 _blacklistName) internal { require(_investor != address(0), "Invalid investor address"); require(_blacklistName != bytes32(0),"Invalid blacklist name"); require(investorToBlacklist[_investor][investorToIndex[_investor][_blacklistName]] == _blacklistName, "Investor not associated to the blacklist"); @@ -362,24 +355,55 @@ contract BlacklistTransferManager is TransferManager { emit DeleteInvestorFromBlacklist(_investor, _blacklistName); } + /** + * @notice Used to delete the investor from all the associated blacklist types + * @param _investor Address of the investor + */ + function deleteInvestorFromAllBlacklist(address _investor) public withPerm(ADMIN) { + _deleteInvestorFromAllBlacklist(_investor); + } + + /** + * @notice Used to delete the investor from all the associated blacklist types + * @param _investor Address of the investor + */ + function _deleteInvestorFromAllBlacklist(address _investor) internal { + require(_investor != address(0), "Invalid investor address"); + uint256 index = investorToBlacklist[_investor].length - 1; + for (uint256 i = index; i >= 0 && i <= index; i--) { + _deleteInvestorFromBlacklist(_investor, investorToBlacklist[_investor][i]); + } + } + + /** + * @notice Used to delete the multiple investor from all the associated blacklist types + * @param _investor Address of the investor + */ + function deleteInvestorFromAllBlacklistMulti(address[] memory _investor) public withPerm(ADMIN) { + for(uint256 i = 0; i < _investor.length; i++) { + _deleteInvestorFromAllBlacklist(_investor[i]); + } + } + /** * @notice Used to delete the multiple investor from the blacklist * @param _investors address of the investor * @param _blacklistNames name of the blacklist */ - function deleteMultiInvestorsFromBlacklistMulti(address[] calldata _investors, bytes32[] calldata _blacklistNames) external withPerm(ADMIN) { + function deleteMultiInvestorsFromBlacklistMulti(address[] memory _investors, bytes32[] memory _blacklistNames) public withPerm(ADMIN) { require (_investors.length == _blacklistNames.length, "Input array's length mismatch"); - for(uint256 i = 0; i < _investors.length; i++){ - deleteInvestorFromBlacklist(_investors[i], _blacklistNames[i]); + for(uint256 i = 0; i < _investors.length; i++) { + _deleteInvestorFromBlacklist(_investors[i], _blacklistNames[i]); } } - function _addBlacklistType(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime) internal { - require(blacklists[_blacklistName].endTime == 0, "Blacklist type already exist"); - _validParams(_startTime, _endTime, _blacklistName, _repeatPeriodTime); - blacklists[_blacklistName] = BlacklistsDetails(_startTime, _endTime, _repeatPeriodTime); - allBlacklists.push(_blacklistName); - emit AddBlacklistType(_startTime, _endTime, _blacklistName, _repeatPeriodTime); + /** + * @notice Internal function + */ + function _validParams(uint256 _startTime, uint256 _endTime, bytes32 _blacklistName, uint256 _repeatPeriodTime) internal view { + require(_blacklistName != bytes32(0), "Invalid blacklist name"); + require(_startTime >= now && _startTime < _endTime, "Invalid start or end date"); + require(_repeatPeriodTime.mul(1 days) >= _endTime.sub(_startTime) || _repeatPeriodTime == 0); } /** @@ -412,9 +436,9 @@ contract BlacklistTransferManager is TransferManager { /** * @notice return the amount of tokens for a given user as per the partition */ - function getTokensByPartition(address /*_owner*/, bytes32 /*_partition*/) external view returns(uint256){ + function getTokensByPartition(address /*_owner*/, bytes32 /*_partition*/) external view returns(uint256) { return 0; - } + } /** * @notice Return the permissions flag that are associated with blacklist transfer manager diff --git a/contracts/modules/Experimental/TransferManager/BlacklistTransferManagerFactory.sol b/contracts/modules/TransferManager/BTM/BlacklistTransferManagerFactory.sol similarity index 60% rename from contracts/modules/Experimental/TransferManager/BlacklistTransferManagerFactory.sol rename to contracts/modules/TransferManager/BTM/BlacklistTransferManagerFactory.sol index 4aae8b4cc..44e227554 100644 --- a/contracts/modules/Experimental/TransferManager/BlacklistTransferManagerFactory.sol +++ b/contracts/modules/TransferManager/BTM/BlacklistTransferManagerFactory.sol @@ -1,30 +1,30 @@ pragma solidity ^0.5.0; -import "./BlacklistTransferManager.sol"; -import "../../ModuleFactory.sol"; -import "../../../libraries/Util.sol"; +import "../../UpgradableModuleFactory.sol"; +import "./BlacklistTransferManagerProxy.sol"; /** - * @title Factory for deploying BlacklistManager module + * @title Factory for deploying BlacklistTransferManager module */ -contract BlacklistTransferManagerFactory is ModuleFactory { +contract BlacklistTransferManagerFactory is UpgradableModuleFactory { /** * @notice Constructor * @param _setupCost Setup cost of the module * @param _usageCost Usage cost of the module + * @param _logicContract Contract address that contains the logic related to `description` * @param _polymathRegistry Address of the Polymath registry * @param _isCostInPoly true = cost in Poly, false = USD */ constructor( uint256 _setupCost, uint256 _usageCost, + address _logicContract, address _polymathRegistry, bool _isCostInPoly ) - public ModuleFactory(_setupCost, _usageCost, _polymathRegistry, _isCostInPoly) + public UpgradableModuleFactory("3.0.0", _setupCost, _usageCost, _logicContract, _polymathRegistry, _isCostInPoly) { - initialVersion = "3.0.0"; name = "BlacklistTransferManager"; title = "Blacklist Transfer Manager"; description = "Automate blacklist to restrict selling"; @@ -35,12 +35,13 @@ contract BlacklistTransferManagerFactory is ModuleFactory { compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(3), uint8(0), uint8(0)); } - /** - * @notice used to launch the Module with the help of factory + /** + * @notice Used to launch the Module with the help of factory + * @param _data Data used for the intialization of the module factory variables * @return address Contract address of the Module */ function deploy(bytes calldata _data) external returns(address) { - address blacklistTransferManager = address(new BlacklistTransferManager(msg.sender, IPolymathRegistry(polymathRegistry).getAddress("PolyToken"))); + address blacklistTransferManager = address(new BlacklistTransferManagerProxy(logicContracts[latestUpgrade].version, msg.sender, IPolymathRegistry(polymathRegistry).getAddress("PolyToken"), logicContracts[latestUpgrade].logicContract)); _initializeModule(blacklistTransferManager, _data); return blacklistTransferManager; } diff --git a/contracts/modules/TransferManager/BTM/BlacklistTransferManagerProxy.sol b/contracts/modules/TransferManager/BTM/BlacklistTransferManagerProxy.sol new file mode 100644 index 000000000..fc99fc4d5 --- /dev/null +++ b/contracts/modules/TransferManager/BTM/BlacklistTransferManagerProxy.sol @@ -0,0 +1,35 @@ +pragma solidity ^0.5.0; + +import "../../../proxy/OwnedUpgradeabilityProxy.sol"; +import "./BlacklistTransferManagerStorage.sol"; +import "../../../Pausable.sol"; +import "../../../storage/modules/ModuleStorage.sol"; + +/** + * @title CountTransferManager module Proxy + */ +contract BlacklistTransferManagerProxy is BlacklistTransferManagerStorage, ModuleStorage, Pausable, OwnedUpgradeabilityProxy { + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor ( + string memory _version, + address _securityToken, + address _polyAddress, + address _implementation + ) + public + ModuleStorage(_securityToken, _polyAddress) + { + require( + _implementation != address(0), + "Implementation address should not be 0x" + ); + _upgradeTo(_version, _implementation); + } + +} diff --git a/contracts/modules/TransferManager/BTM/BlacklistTransferManagerStorage.sol b/contracts/modules/TransferManager/BTM/BlacklistTransferManagerStorage.sol new file mode 100644 index 000000000..c8f460164 --- /dev/null +++ b/contracts/modules/TransferManager/BTM/BlacklistTransferManagerStorage.sol @@ -0,0 +1,31 @@ +pragma solidity ^0.5.0; + +/** + * @title Contract used to store layout for the CountTransferManager storage + */ +contract BlacklistTransferManagerStorage { + + struct BlacklistsDetails { + uint256 startTime; + uint256 endTime; + uint256 repeatPeriodTime; + } + + //hold the different blacklist details corresponds to its name + mapping(bytes32 => BlacklistsDetails) public blacklists; + + //hold the different name of blacklist corresponds to a investor + mapping(address => bytes32[]) investorToBlacklist; + + //get list of the addresses for a particular blacklist + mapping(bytes32 => address[]) blacklistToInvestor; + + //mapping use to store the indexes for different blacklist types for a investor + mapping(address => mapping(bytes32 => uint256)) investorToIndex; + + //mapping use to store the indexes for different investor for a blacklist type + mapping(bytes32 => mapping(address => uint256)) blacklistToIndex; + + bytes32[] allBlacklists; + +} diff --git a/contracts/modules/Experimental/TransferManager/LTM/LockUpTransferManager.sol b/contracts/modules/TransferManager/LTM/LockUpTransferManager.sol similarity index 93% rename from contracts/modules/Experimental/TransferManager/LTM/LockUpTransferManager.sol rename to contracts/modules/TransferManager/LTM/LockUpTransferManager.sol index a6eebd63c..57b24306d 100644 --- a/contracts/modules/Experimental/TransferManager/LTM/LockUpTransferManager.sol +++ b/contracts/modules/TransferManager/LTM/LockUpTransferManager.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.0; -import "../../../TransferManager/TransferManager.sol"; +import "../../TransferManager/TransferManager.sol"; import "./LockUpTransferManagerStorage.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; import "openzeppelin-solidity/contracts/math/Math.sol"; @@ -51,8 +51,8 @@ contract LockUpTransferManager is LockUpTransferManagerStorage, TransferManager * @param _from Address of the sender * @param _amount The amount of tokens to transfer */ - function executeTransfer(address _from, address _to, uint256 _amount, bytes calldata _data) external returns(Result) { - (Result success,) = verifyTransfer(_from, _to, _amount, _data); + function executeTransfer(address _from, address /*_to*/, uint256 _amount, bytes calldata /*_data*/) external returns(Result) { + (Result success,) = _verifyTransfer(_from, _amount); return success; } @@ -69,6 +69,21 @@ contract LockUpTransferManager is LockUpTransferManagerStorage, TransferManager public view returns(Result, bytes32) + { + return _verifyTransfer(_from, _amount); + } + + /** @notice Used to verify the transfer transaction and prevent locked up tokens from being transferred + * @param _from Address of the sender + * @param _amount The amount of tokens to transfer + */ + function _verifyTransfer( + address _from, + uint256 _amount + ) + internal + view + returns(Result, bytes32) { // only attempt to verify the transfer if the token is unpaused, this isn't a mint txn, and there exists a lockup for this user if (!paused && _from != address(0) && userToLockups[_from].length != 0) { @@ -115,13 +130,13 @@ contract LockUpTransferManager is LockUpTransferManagerStorage, TransferManager * @param _lockupNames Array of names of the lockup */ function addNewLockUpTypeMulti( - uint256[] calldata _lockupAmounts, - uint256[] calldata _startTimes, - uint256[] calldata _lockUpPeriodsSeconds, - uint256[] calldata _releaseFrequenciesSeconds, - bytes32[] calldata _lockupNames + uint256[] memory _lockupAmounts, + uint256[] memory _startTimes, + uint256[] memory _lockUpPeriodsSeconds, + uint256[] memory _releaseFrequenciesSeconds, + bytes32[] memory _lockupNames ) - external + public withPerm(ADMIN) { require( @@ -158,15 +173,15 @@ contract LockUpTransferManager is LockUpTransferManagerStorage, TransferManager } /** - * @notice Add the lockup to a user - * @param _userAddresses Address of the user - * @param _lockupNames Name of the lockup + * @notice Add lockups to users + * @param _userAddresses Array of addresses of the users + * @param _lockupNames Array of names of the lockups */ function addLockUpByNameMulti( - address[] calldata _userAddresses, - bytes32[] calldata _lockupNames + address[] memory _userAddresses, + bytes32[] memory _lockupNames ) - external + public withPerm(ADMIN) { _checkLengthOfArray(_userAddresses.length, _lockupNames.length); @@ -259,7 +274,7 @@ contract LockUpTransferManager is LockUpTransferManagerStorage, TransferManager * @notice Used to remove the multiple lockup type * @param _lockupNames Array of the lockup names. */ - function removeLockupTypeMulti(bytes32[] calldata _lockupNames) external withPerm(ADMIN) { + function removeLockupTypeMulti(bytes32[] memory _lockupNames) public withPerm(ADMIN) { for (uint256 i = 0; i < _lockupNames.length; i++) { _removeLockupType(_lockupNames[i]); } @@ -270,7 +285,7 @@ contract LockUpTransferManager is LockUpTransferManagerStorage, TransferManager * @param _userAddresses Array of addresses of the user whose tokens are locked up * @param _lockupNames Array of the names of the lockup that needs to be removed. */ - function removeLockUpFromUserMulti(address[] calldata _userAddresses, bytes32[] calldata _lockupNames) external withPerm(ADMIN) { + function removeLockUpFromUserMulti(address[] memory _userAddresses, bytes32[] memory _lockupNames) public withPerm(ADMIN) { _checkLengthOfArray(_userAddresses.length, _lockupNames.length); for (uint256 i = 0; i < _userAddresses.length; i++) { _removeLockUpFromUser(_userAddresses[i], _lockupNames[i]); @@ -374,7 +389,7 @@ contract LockUpTransferManager is LockUpTransferManagerStorage, TransferManager uint256[] memory releaseFrequencySeconds, uint256[] memory unlockedAmounts ) - { + { uint256 length = lockupArray.length; lockupAmounts = new uint256[](length); startTimes = new uint256[](length); @@ -471,7 +486,7 @@ contract LockUpTransferManager is LockUpTransferManagerStorage, TransferManager function _removeLockupType(bytes32 _lockupName) internal { _validLockUpCheck(_lockupName); - require(lockupToUsers[_lockupName].length == 0); + require(lockupToUsers[_lockupName].length == 0, "Users attached to lockup"); // delete lockup type delete(lockups[_lockupName]); uint256 i = 0; @@ -528,7 +543,8 @@ contract LockUpTransferManager is LockUpTransferManagerStorage, TransferManager _checkZeroAddress(_userAddress); _checkValidName(_lockupName); require( - userToLockups[_userAddress][userToLockupIndex[_userAddress][_lockupName]] == _lockupName + userToLockups[_userAddress][userToLockupIndex[_userAddress][_lockupName]] == _lockupName, + "User not in lockup" ); // delete the user from the lockup type @@ -583,7 +599,12 @@ contract LockUpTransferManager is LockUpTransferManagerStorage, TransferManager { _checkZeroAddress(_userAddress); _checkValidStartTime(lockups[_lockupName].startTime); - + if(userToLockups[_userAddress].length > 0) { + require( + userToLockups[_userAddress][userToLockupIndex[_userAddress][_lockupName]] != _lockupName, + "User already in lockup" + ); + } userToLockupIndex[_userAddress][_lockupName] = userToLockups[_userAddress].length; lockupToUserIndex[_lockupName][_userAddress] = lockupToUsers[_lockupName].length; userToLockups[_userAddress].push(_lockupName); diff --git a/contracts/modules/Experimental/TransferManager/LTM/LockUpTransferManagerFactory.sol b/contracts/modules/TransferManager/LTM/LockUpTransferManagerFactory.sol similarity index 97% rename from contracts/modules/Experimental/TransferManager/LTM/LockUpTransferManagerFactory.sol rename to contracts/modules/TransferManager/LTM/LockUpTransferManagerFactory.sol index ddeb53b4a..b0ce1b000 100644 --- a/contracts/modules/Experimental/TransferManager/LTM/LockUpTransferManagerFactory.sol +++ b/contracts/modules/TransferManager/LTM/LockUpTransferManagerFactory.sol @@ -1,7 +1,7 @@ pragma solidity ^0.5.0; import "./LockUpTransferManagerProxy.sol"; -import "../../../UpgradableModuleFactory.sol"; +import "../../UpgradableModuleFactory.sol"; import "./LockUpTransferManager.sol"; /** diff --git a/contracts/modules/Experimental/TransferManager/LTM/LockUpTransferManagerProxy.sol b/contracts/modules/TransferManager/LTM/LockUpTransferManagerProxy.sol similarity index 85% rename from contracts/modules/Experimental/TransferManager/LTM/LockUpTransferManagerProxy.sol rename to contracts/modules/TransferManager/LTM/LockUpTransferManagerProxy.sol index 7d3057970..cd198f478 100644 --- a/contracts/modules/Experimental/TransferManager/LTM/LockUpTransferManagerProxy.sol +++ b/contracts/modules/TransferManager/LTM/LockUpTransferManagerProxy.sol @@ -1,9 +1,9 @@ pragma solidity ^0.5.0; import "./LockUpTransferManagerStorage.sol"; -import "../../../../proxy/OwnedUpgradeabilityProxy.sol"; -import "../../../../Pausable.sol"; -import "../../../../storage/modules/ModuleStorage.sol"; +import "../../../proxy/OwnedUpgradeabilityProxy.sol"; +import "../../../Pausable.sol"; +import "../../../storage/modules/ModuleStorage.sol"; /** * @title CountTransferManager module Proxy diff --git a/contracts/modules/Experimental/TransferManager/LTM/LockUpTransferManagerStorage.sol b/contracts/modules/TransferManager/LTM/LockUpTransferManagerStorage.sol similarity index 100% rename from contracts/modules/Experimental/TransferManager/LTM/LockUpTransferManagerStorage.sol rename to contracts/modules/TransferManager/LTM/LockUpTransferManagerStorage.sol diff --git a/contracts/modules/TransferManager/VRTM/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VRTM/VolumeRestrictionTM.sol index 366742fcc..e65242c2a 100644 --- a/contracts/modules/TransferManager/VRTM/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VRTM/VolumeRestrictionTM.sol @@ -125,7 +125,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, TransferManager { isGlobal ); } - return success; + return success; } /** @@ -139,15 +139,15 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, TransferManager { address /*_to*/ , uint256 _amount, bytes memory /*_data*/ - ) + ) public view returns (Result, bytes32) { - + (Result success,,,,,,) = _verifyTransfer(_from, _amount); if (success == Result.INVALID) - return (success, bytes32(uint256(address(this)) << 96)); + return (success, bytes32(uint256(address(this)) << 96)); return (Result.NA, bytes32(0)); } @@ -160,11 +160,11 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, TransferManager { function _verifyTransfer( address _from, uint256 _amount - ) + ) internal view returns (Result, uint256, uint256, uint256, uint256, uint256, bool) - { + { // If `_from` is present in the exemptionList or it is `0x0` address then it will not follow the vol restriction if (!paused && _from != address(0) && exemptions.exemptIndex[_from] == 0) { // Checking the individual restriction if the `_from` comes in the individual category @@ -1187,7 +1187,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, TransferManager { */ function getTokensByPartition(address /*_owner*/, bytes32 /*_partition*/) external view returns(uint256){ return 0; - } + } /** * @notice Returns the permissions flag that are associated with Percentage transfer Manager diff --git a/contracts/modules/Experimental/Wallet/VestingEscrowWallet.sol b/contracts/modules/Wallet/VestingEscrowWallet.sol similarity index 97% rename from contracts/modules/Experimental/Wallet/VestingEscrowWallet.sol rename to contracts/modules/Wallet/VestingEscrowWallet.sol index dfce2aad5..565944646 100644 --- a/contracts/modules/Experimental/Wallet/VestingEscrowWallet.sol +++ b/contracts/modules/Wallet/VestingEscrowWallet.sol @@ -94,7 +94,7 @@ contract VestingEscrowWallet is VestingEscrowWalletStorage, Wallet { require(_numberOfTokens > 0, "Should be > 0"); require( ISecurityToken(securityToken).transferFrom(msg.sender, address(this), _numberOfTokens), - "Failed transferFrom due to insufficent Allowance provided" + "Failed transferFrom" ); unassignedTokens = unassignedTokens.add(_numberOfTokens); emit DepositTokens(_numberOfTokens, msg.sender); @@ -104,13 +104,12 @@ contract VestingEscrowWallet is VestingEscrowWalletStorage, Wallet { * @notice Sends unassigned tokens to the treasury wallet * @param _amount Amount of tokens that should be send to the treasury wallet */ - function sendToTreasury(uint256 _amount) external withPerm(OPERATOR) { + function sendToTreasury(uint256 _amount) public withPerm(OPERATOR) { require(_amount > 0, "Amount cannot be zero"); require(_amount <= unassignedTokens, "Amount is greater than unassigned tokens"); - uint256 amount = unassignedTokens; - unassignedTokens = 0; - require(ISecurityToken(securityToken).transfer(getTreasuryWallet(), amount), "Transfer failed"); - emit SendToTreasury(amount, msg.sender); + unassignedTokens = unassignedTokens - _amount; + require(ISecurityToken(securityToken).transfer(getTreasuryWallet(), _amount), "Transfer failed"); + emit SendToTreasury(_amount, msg.sender); } /** @@ -274,7 +273,7 @@ contract VestingEscrowWallet is VestingEscrowWalletStorage, Wallet { * @param _templateName Name of the template was used for schedule creation * @param _startTime Start time of the created vesting schedule */ - function modifySchedule(address _beneficiary, bytes32 _templateName, uint256 _startTime) public withPerm(ADMIN) { + function modifySchedule(address _beneficiary, bytes32 _templateName, uint256 _startTime) external withPerm(ADMIN) { _modifySchedule(_beneficiary, _templateName, _startTime); } @@ -433,7 +432,7 @@ contract VestingEscrowWallet is VestingEscrowWalletStorage, Wallet { * @param _fromIndex Start index of array of beneficiary's addresses * @param _toIndex End index of array of beneficiary's addresses */ - function pushAvailableTokensMulti(uint256 _fromIndex, uint256 _toIndex) external withPerm(OPERATOR) { + function pushAvailableTokensMulti(uint256 _fromIndex, uint256 _toIndex) public withPerm(OPERATOR) { require(_toIndex <= beneficiaries.length - 1, "Array out of bound"); for (uint256 i = _fromIndex; i <= _toIndex; i++) { if (schedules[beneficiaries[i]].length !=0) @@ -481,11 +480,11 @@ contract VestingEscrowWallet is VestingEscrowWalletStorage, Wallet { * @param _startTimes Array of the vesting start time */ function addScheduleFromTemplateMulti( - address[] calldata _beneficiaries, - bytes32[] calldata _templateNames, - uint256[] calldata _startTimes + address[] memory _beneficiaries, + bytes32[] memory _templateNames, + uint256[] memory _startTimes ) - external + public withPerm(ADMIN) { require(_beneficiaries.length == _templateNames.length && _beneficiaries.length == _startTimes.length, "Arrays sizes mismatch"); @@ -498,7 +497,7 @@ contract VestingEscrowWallet is VestingEscrowWalletStorage, Wallet { * @notice Used to bulk revoke vesting schedules for each of the beneficiaries * @param _beneficiaries Array of the beneficiary's addresses */ - function revokeSchedulesMulti(address[] calldata _beneficiaries) external withPerm(ADMIN) { + function revokeSchedulesMulti(address[] memory _beneficiaries) public withPerm(ADMIN) { for (uint256 i = 0; i < _beneficiaries.length; i++) { _revokeAllSchedules(_beneficiaries[i]); } diff --git a/contracts/modules/Experimental/Wallet/VestingEscrowWalletFactory.sol b/contracts/modules/Wallet/VestingEscrowWalletFactory.sol similarity index 93% rename from contracts/modules/Experimental/Wallet/VestingEscrowWalletFactory.sol rename to contracts/modules/Wallet/VestingEscrowWalletFactory.sol index d755e151a..c2bfb2ece 100644 --- a/contracts/modules/Experimental/Wallet/VestingEscrowWalletFactory.sol +++ b/contracts/modules/Wallet/VestingEscrowWalletFactory.sol @@ -1,9 +1,9 @@ pragma solidity ^0.5.0; import "./VestingEscrowWalletProxy.sol"; -import "../../../interfaces/IBoot.sol"; -import "../../UpgradableModuleFactory.sol"; -import "../../../libraries/Util.sol"; +import "../../interfaces/IBoot.sol"; +import "../UpgradableModuleFactory.sol"; +import "../../libraries/Util.sol"; /** * @title Factory for deploying VestingEscrowWallet module diff --git a/contracts/modules/Experimental/Wallet/VestingEscrowWalletProxy.sol b/contracts/modules/Wallet/VestingEscrowWalletProxy.sol similarity index 85% rename from contracts/modules/Experimental/Wallet/VestingEscrowWalletProxy.sol rename to contracts/modules/Wallet/VestingEscrowWalletProxy.sol index 500516fdb..b3073e5e2 100644 --- a/contracts/modules/Experimental/Wallet/VestingEscrowWalletProxy.sol +++ b/contracts/modules/Wallet/VestingEscrowWalletProxy.sol @@ -1,9 +1,9 @@ pragma solidity ^0.5.0; -import "../../../proxy/OwnedUpgradeabilityProxy.sol"; +import "../../proxy/OwnedUpgradeabilityProxy.sol"; import "./VestingEscrowWalletStorage.sol"; -import "../../../Pausable.sol"; -import "../../../storage/modules/ModuleStorage.sol"; +import "../../Pausable.sol"; +import "../../storage/modules/ModuleStorage.sol"; /** * @title Escrow wallet module for vesting functionality */ diff --git a/contracts/modules/Experimental/Wallet/VestingEscrowWalletStorage.sol b/contracts/modules/Wallet/VestingEscrowWalletStorage.sol similarity index 100% rename from contracts/modules/Experimental/Wallet/VestingEscrowWalletStorage.sol rename to contracts/modules/Wallet/VestingEscrowWalletStorage.sol diff --git a/contracts/modules/Experimental/Wallet/Wallet.sol b/contracts/modules/Wallet/Wallet.sol similarity index 84% rename from contracts/modules/Experimental/Wallet/Wallet.sol rename to contracts/modules/Wallet/Wallet.sol index 41177ce81..0e89bb8e0 100644 --- a/contracts/modules/Experimental/Wallet/Wallet.sol +++ b/contracts/modules/Wallet/Wallet.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.0; -import "../../Module.sol"; +import "../Module.sol"; /** * @title Interface to be implemented by all Wallet modules diff --git a/test/helpers/createInstances.js b/test/helpers/createInstances.js index fecfb2216..d249b2b4a 100644 --- a/test/helpers/createInstances.js +++ b/test/helpers/createInstances.js @@ -20,6 +20,7 @@ const ManualApprovalTransferManagerFactory = artifacts.require("./ManualApproval const TrackedRedemptionFactory = artifacts.require("./TrackedRedemptionFactory.sol"); const PercentageTransferManagerFactory = artifacts.require("./PercentageTransferManagerFactory.sol"); const PercentageTransferManager = artifacts.require("./PercentageTransferManager.sol"); +const BlacklistTransferManager = artifacts.require("./BlacklistTransferManager.sol"); const BlacklistTransferManagerFactory = artifacts.require("./BlacklistTransferManagerFactory.sol"); const ScheduledCheckpointFactory = artifacts.require('./ScheduledCheckpointFactory.sol'); const USDTieredSTOFactory = artifacts.require("./USDTieredSTOFactory.sol"); @@ -101,6 +102,7 @@ let I_STFactory; let I_USDTieredSTOLogic; let I_PolymathRegistry; let I_SecurityTokenRegistryProxy; +let I_BlacklistTransferManagerLogic; let I_BlacklistTransferManagerFactory; let I_VestingEscrowWalletLogic; let I_STRProxied; @@ -385,8 +387,8 @@ export async function deployPercentageTMAndVerified(accountPolymath, MRProxyInst } export async function deployBlacklistTMAndVerified(accountPolymath, MRProxyInstance, setupCost, feeInPoly = false) { - - I_BlacklistTransferManagerFactory = await BlacklistTransferManagerFactory.new(setupCost, new BN(0), I_PolymathRegistry.address, feeInPoly, { from: accountPolymath }); + I_BlacklistTransferManagerLogic = await BlacklistTransferManager.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: accountPolymath }); + I_BlacklistTransferManagerFactory = await BlacklistTransferManagerFactory.new(setupCost, new BN(0), I_BlacklistTransferManagerLogic.address, I_PolymathRegistry.address, feeInPoly, { from: accountPolymath }); assert.notEqual( I_BlacklistTransferManagerFactory.address.valueOf(), "0x0000000000000000000000000000000000000000", diff --git a/test/w_lockup_transfer_manager.js b/test/w_lockup_transfer_manager.js index a056f5fd4..41f3c546d 100644 --- a/test/w_lockup_transfer_manager.js +++ b/test/w_lockup_transfer_manager.js @@ -448,7 +448,7 @@ contract('LockUpTransferManager', accounts => { currentTime.add(new BN(duration.seconds(1))), new BN(duration.seconds(400000)), new BN(duration.seconds(100000)), - web3.utils.fromAscii("a_lockup"), + web3.utils.fromAscii("a_lockup2"), { from: token_owner } @@ -953,6 +953,9 @@ contract('LockUpTransferManager', accounts => { await I_LockUpTransferManager.addLockUpByName(account_investor2, web3.utils.fromAscii("l_lockup"), {from: token_owner}); + //Should not allow to add a user to a lockup multiple times + await catchRevert(I_LockUpTransferManager.addLockUpByName(account_investor2, web3.utils.fromAscii("l_lockup"), {from: token_owner})); + // try to delete the lockup but fail await catchRevert( diff --git a/test/z_blacklist_transfer_manager.js b/test/z_blacklist_transfer_manager.js index 053568628..357622538 100644 --- a/test/z_blacklist_transfer_manager.js +++ b/test/z_blacklist_transfer_manager.js @@ -964,6 +964,32 @@ contract('BlacklistTransferManager', accounts => { }); }); + describe("Test cases for blacklist with repeat period 0 (Never repeat)", async() => { + it("Should add a new blacklist with no repeat time", async() => { + let curTime = await latestTime(); + await I_BlacklistTransferManager.deleteInvestorFromAllBlacklist(account_investor3, { from: token_owner }); + await I_BlacklistTransferManager.addInvestorToNewBlacklist( + new BN(curTime).add(new BN(100)), + new BN(curTime).add(new BN(1000)), + web3.utils.fromAscii("anewbl"), + 0, + account_investor3, + { from: token_owner} + ); + await increaseTime(200); + await catchRevert(I_SecurityToken.transfer(account_investor4, web3.utils.toWei('1', 'ether'), { from: account_investor3 })); + }); + + it("Should allow transfer after blacklist end time", async() => { + await increaseTime(2000); + await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('1', 'ether'), { from: account_investor3 }); + assert.equal( + (await I_SecurityToken.balanceOf(account_investor4)).toString(), + web3.utils.toWei('4', 'ether') + ); + }); + }); + describe("Test cases for the factory", async() => { it("Should get the exact details of the factory", async() => { assert.equal(await I_BlacklistTransferManagerFactory.setupCost.call(),0); diff --git a/test/z_vesting_escrow_wallet.js b/test/z_vesting_escrow_wallet.js index ded4a5b5a..ff1fa088f 100644 --- a/test/z_vesting_escrow_wallet.js +++ b/test/z_vesting_escrow_wallet.js @@ -370,8 +370,21 @@ contract('VestingEscrowWallet', accounts => { ); }); - it("Should withdraw tokens to a treasury", async () => { - let numberOfTokens = 25000; + it("Should withdraw partial tokens to a treasury", async () => { + let numberOfTokens = 5000; + const tx = await I_VestingEscrowWallet.sendToTreasury(numberOfTokens, {from: wallet_operator}); + + assert.equal(tx.logs[0].args._numberOfTokens, numberOfTokens); + + let unassignedTokens = await I_VestingEscrowWallet.unassignedTokens.call(); + assert.equal(unassignedTokens, 20000); + + let balance = await I_SecurityToken.balanceOf.call(I_VestingEscrowWallet.address); + assert.equal(balance.toString(), 20000); + }); + + it("Should withdraw all tokens to a treasury", async () => { + let numberOfTokens = 20000; const tx = await I_VestingEscrowWallet.sendToTreasury(numberOfTokens, {from: wallet_operator}); assert.equal(tx.logs[0].args._numberOfTokens, numberOfTokens);