From a48ae403918196eb68d997fea074f4426ff7305d Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Tue, 18 Sep 2018 17:18:21 +0530 Subject: [PATCH 01/27] Initial Setup --- .../SingleTradeVolumeRestrictionManager.sol | 82 +++++++++++++++++++ ...leTradeVolumeRestrictionManagerFactory.sol | 76 +++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol create mode 100644 contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol new file mode 100644 index 000000000..fa05bbb94 --- /dev/null +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -0,0 +1,82 @@ +pragma solidity ^0.4.24; +import "./ITransferManager.sol"; + +contract SingleTradeVolumeRestrictionManager is ITransferManager { + using SafeMath for uint256; + bool public isPercentage; + uint256 public globalTransferLimit; + mapping(address=>bool) public exemptWallets; + mapping(address => bool) public specialTransferLimitWallets; + mapping(address => uint) public specialTransferLimits; + + constructor(address _securityToken, address _polyAddress) public + IModule(_securityToken, _polyAddress) + { + + } + + function verifyTransfer(address _from, address _to, uint256 _amount, bool /* _isTransfer */) public returns(Result) { + // TO IMPLEMENT + /* if(!paused) { + if(exemptWallets[_from]) return Result.NA; + if(specialTransferLimitWallets[_from]) { + if(isPercentage) { + _amount.div(securityToken.totalSupply()) + } + else { + + } + } + } */ + return Result.NA; + } + + function configure(bool _isPercentage, uint256 _globalTransferLimit) public onlyFactory { + isPercentage = _isPercentage; + changeGlobalLimit(_globalTransferLimit); + } + + function changeGlobalLimit(uint256 _newGlobalTransferLimit) public { + if(isPercentage) { + require(_newGlobalTransferLimit <= 100); + } + globalTransferLimit = _newGlobalTransferLimit; + } + + function addExemptWallet(address _walletAddress) public { + require(_walletAddress != address(0)); + exemptWallets[_walletAddress] = true; + } + + function removeExemptWallet(address _walletAddress) public { + require(_walletAddress != address(0)); + exemptWallets[_walletAddress] = false; + } + + function setTransferLimitForWallet(address _walletAddress, uint _transferLimit) public { + if(isPercentage) { + require(_transferLimit <= 100); + } + specialTransferLimitWallets[_walletAddress] = true; + specialTransferLimits[_walletAddress] = _transferLimit; + } + + function removeTransferLimitForWallet(address _walletAddress) public { + require(specialTransferLimitWallets[_walletAddress]); + specialTransferLimitWallets[_walletAddress] = false; + specialTransferLimits[_walletAddress] = 0; + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() public pure returns (bytes4) { + return bytes4(keccak256("configure(bool, uint256)")); + } + + // TO IMPLEMENT + function getPermissions() public view returns(bytes32[]) { + bytes32[] memory allPermissions = new bytes32[](1); + return allPermissions; + } +} diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol new file mode 100644 index 000000000..b9e3cb51e --- /dev/null +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol @@ -0,0 +1,76 @@ +pragma solidity ^0.4.24; + +import "../../interfaces/IModuleFactory.sol"; +import "./SingleTradeVolumeRestrictionManager.sol"; +contract SingleTradeVolumeRestrictionFactory is IModuleFactory { + + /** + * @notice Constructor + * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _subscriptionCost Subscription cost of the module + */ + constructor(address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public + IModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + { + + } + + /** + * @notice used to launch the Module with the help of factory + * @return address Contract address of the Module + */ + function deploy(bytes _data) external returns(address) { + if (setupCost > 0) + require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); + return address(new SingleTradeVolumeRestrictionManager(msg.sender, address(polyToken))); + } + + /** + * @notice Type of the Module factory + */ + function getType() public view returns(uint8) { + return 2; + } + + /** + * @notice Get the name of the Module + */ + function getName() public view returns(bytes32) { + return "SingleTradeVolumeRestriction"; + } + + /** + * @notice Get the description of the Module + */ + function getDescription() public view returns(string) { + return "Imposes volume restriction on a single trade"; + } + + /** + * @notice Get the title of the Module + */ + function getTitle() public view returns(string) { + return "Single Trade Volume Restriction"; + } + + /** + * @notice Get the Instructions that help to use the module + */ + function getInstructions() public view returns(string) { + return "Allows an issuer to impose volume restriction on a single trade"; + } + + /** + * @notice Get the tags related to the module factory + */ + function getTags() public view returns(bytes32[]) { + bytes32[] memory availableTags = new bytes32[](3); + availableTags[0] = "Single Trade"; + availableTags[1] = "Transfer"; + availableTags[2] = "Volume"; + return availableTags; + } + +} From 90c9c546d66a2d29431585ac8e36455595b866e0 Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Tue, 18 Sep 2018 17:33:40 +0530 Subject: [PATCH 02/27] deploy function --- .../SingleTradeVolumeRestrictionManagerFactory.sol | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol index b9e3cb51e..a5c4e9827 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol @@ -24,7 +24,11 @@ contract SingleTradeVolumeRestrictionFactory is IModuleFactory { function deploy(bytes _data) external returns(address) { if (setupCost > 0) require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); - return address(new SingleTradeVolumeRestrictionManager(msg.sender, address(polyToken))); + SingleTradeVolumeRestrictionManager singleTradeVolumeRestrictionManager = new SingleTradeVolumeRestrictionManager(msg.sender, address(polyToken)); + require(getSig(_data) == singleTradeVolumeRestrictionManager.getInitFunction(), "Provided data is not valid"); + require(address(singleTradeVolumeRestrictionManager).call(_data), "Un-successfull call"); + emit LogGenerateModuleFromFactory(address(singleTradeVolumeRestrictionManager), getName(), address(this), msg.sender, now); + return address(singleTradeVolumeRestrictionManager); } /** From 62f4d6ce8b8dab2e5d8e65deae1133e9bcb7167d Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Tue, 18 Sep 2018 20:06:00 +0530 Subject: [PATCH 03/27] Implement VerfyTransfer Function and also Permissions --- .../SingleTradeVolumeRestrictionManager.sol | 69 +++++++++++++------ 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index fa05bbb94..08614bcf5 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -1,14 +1,31 @@ pragma solidity ^0.4.24; import "./ITransferManager.sol"; +///////////////////// +// Module permissions +///////////////////// +// Owner ADMIN +// changeGlobalLimit X X +// addExemptWallet X X +// removeExemptWallet X X +// setTransferLimitForWallet X X +// removeTransferLimitForWallet X X + contract SingleTradeVolumeRestrictionManager is ITransferManager { using SafeMath for uint256; - bool public isPercentage; + + bool public globalTransferLimitInPercentage; + uint256 public globalTransferLimit; + mapping(address=>bool) public exemptWallets; + mapping(address => bool) public specialTransferLimitWallets; + mapping(address => uint) public specialTransferLimits; + bytes32 constant public ADMIN = "ADMIN"; + constructor(address _securityToken, address _polyAddress) public IModule(_securityToken, _polyAddress) { @@ -16,52 +33,59 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { } function verifyTransfer(address _from, address _to, uint256 _amount, bool /* _isTransfer */) public returns(Result) { - // TO IMPLEMENT - /* if(!paused) { - if(exemptWallets[_from]) return Result.NA; - if(specialTransferLimitWallets[_from]) { - if(isPercentage) { - _amount.div(securityToken.totalSupply()) - } - else { - - } + bool validTransfer; + if(paused) { + return Result.NA; + } + if(exemptWallets[_from]) return Result.NA; + if(specialTransferLimitWallets[_from]) { + if(globalTransferLimitInPercentage) { + validTransfer = (_amount.mul(100).div(securityToken.totalSupply())) <= specialTransferLimits[_from]; + } else { + validTransfer = (_amount <= specialTransferLimits[_from]; } - } */ - return Result.NA; + } else { + if(globalTransferLimitInPercentage) { + validTransfer = (_amount.mul(100).div(securityToken.totalSupply())) <= globalTransferLimit; + } else { + validTransfer = _amount <= globalTransferLimit; + } + } + if(validTransfer) return Result.NA; + return Result.INVALID; } - function configure(bool _isPercentage, uint256 _globalTransferLimit) public onlyFactory { - isPercentage = _isPercentage; + function configure(bool _globalTransferLimitInPercentage, uint256 _globalTransferLimit) public onlyFactory { + globalTransferLimitInPercentage = _globalTransferLimitInPercentage; changeGlobalLimit(_globalTransferLimit); } - function changeGlobalLimit(uint256 _newGlobalTransferLimit) public { - if(isPercentage) { + function changeGlobalLimit(uint256 _newGlobalTransferLimit) public withPerm(ADMIN) { + if(globalTransferLimitInPercentage) { require(_newGlobalTransferLimit <= 100); } globalTransferLimit = _newGlobalTransferLimit; } - function addExemptWallet(address _walletAddress) public { + function addExemptWallet(address _walletAddress) public withPerm(ADMIN) { require(_walletAddress != address(0)); exemptWallets[_walletAddress] = true; } - function removeExemptWallet(address _walletAddress) public { + function removeExemptWallet(address _walletAddress) public withPerm(ADMIN) { require(_walletAddress != address(0)); exemptWallets[_walletAddress] = false; } - function setTransferLimitForWallet(address _walletAddress, uint _transferLimit) public { - if(isPercentage) { + function setTransferLimitForWallet(address _walletAddress, uint _transferLimit) public withPerm(ADMIN) { + if(globalTransferLimitInPercentage) { require(_transferLimit <= 100); } specialTransferLimitWallets[_walletAddress] = true; specialTransferLimits[_walletAddress] = _transferLimit; } - function removeTransferLimitForWallet(address _walletAddress) public { + function removeTransferLimitForWallet(address _walletAddress) public withPerm(ADMIN) { require(specialTransferLimitWallets[_walletAddress]); specialTransferLimitWallets[_walletAddress] = false; specialTransferLimits[_walletAddress] = 0; @@ -77,6 +101,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { // TO IMPLEMENT function getPermissions() public view returns(bytes32[]) { bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = ADMIN; return allPermissions; } } From b1e882ac223de2387274c17d22d3e015fd681a87 Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Tue, 18 Sep 2018 20:22:09 +0530 Subject: [PATCH 04/27] Fix lookup errors --- .../SingleTradeVolumeRestrictionManager.sol | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index 08614bcf5..2b1f87f40 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -34,23 +34,27 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { function verifyTransfer(address _from, address _to, uint256 _amount, bool /* _isTransfer */) public returns(Result) { bool validTransfer; + if(paused) { return Result.NA; } + if(exemptWallets[_from]) return Result.NA; + if(specialTransferLimitWallets[_from]) { if(globalTransferLimitInPercentage) { - validTransfer = (_amount.mul(100).div(securityToken.totalSupply())) <= specialTransferLimits[_from]; + validTransfer = (_amount.mul(100).div(ISecurityToken(securityToken).totalSupply())) <= specialTransferLimits[_from]; } else { - validTransfer = (_amount <= specialTransferLimits[_from]; + validTransfer = _amount <= specialTransferLimits[_from]; } } else { if(globalTransferLimitInPercentage) { - validTransfer = (_amount.mul(100).div(securityToken.totalSupply())) <= globalTransferLimit; + validTransfer = (_amount.mul(100).div(ISecurityToken(securityToken).totalSupply())) <= globalTransferLimit; } else { validTransfer = _amount <= globalTransferLimit; } } + if(validTransfer) return Result.NA; return Result.INVALID; } From f6b9c7a031c048094e168b130fc0729aeb1cda7c Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Wed, 26 Sep 2018 01:52:41 +0530 Subject: [PATCH 05/27] Single Trade Volume Tests WIP --- .../SingleTradeVolumeRestrictionManager.sol | 140 ++++- ...leTradeVolumeRestrictionManagerFactory.sol | 7 +- test/u_single_trade_volume_restriction.js | 591 ++++++++++++++++++ 3 files changed, 702 insertions(+), 36 deletions(-) create mode 100644 test/u_single_trade_volume_restriction.js diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index 2b1f87f40..afcf6dd51 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -14,25 +14,41 @@ import "./ITransferManager.sol"; contract SingleTradeVolumeRestrictionManager is ITransferManager { using SafeMath for uint256; - bool public globalTransferLimitInPercentage; + bool public isTransferLimitInPercentage; uint256 public globalTransferLimit; + uint256 public globalTransferLimitInPercentage; + mapping(address=>bool) public exemptWallets; mapping(address => bool) public specialTransferLimitWallets; mapping(address => uint) public specialTransferLimits; + event LogExemptWalletAdded(address _wallet); + event LogExemptWalletRemoved(address _wallet); + event LogTransferLimitInTokensSet(address _wallet, uint256 _amount); + event LogTransferLimitInPercentageSet(address _wallet, uint _percentage); + event LogTransferLimitRemoved(address _wallet); + event LogGlobalTransferLimitInTokensSet(uint256 _amount); + event LogGlobalTransferLimitInPercentageSet(uint256 _percentage); + bytes32 constant public ADMIN = "ADMIN"; + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + */ constructor(address _securityToken, address _polyAddress) public IModule(_securityToken, _polyAddress) { } - function verifyTransfer(address _from, address _to, uint256 _amount, bool /* _isTransfer */) public returns(Result) { + /// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers + function verifyTransfer(address _from, address /* _to */, uint256 _amount, bool /* _isTransfer */) public returns(Result) { bool validTransfer; if(paused) { @@ -42,14 +58,14 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { if(exemptWallets[_from]) return Result.NA; if(specialTransferLimitWallets[_from]) { - if(globalTransferLimitInPercentage) { - validTransfer = (_amount.mul(100).div(ISecurityToken(securityToken).totalSupply())) <= specialTransferLimits[_from]; + if(isTransferLimitInPercentage) { + validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= specialTransferLimits[_from]; } else { validTransfer = _amount <= specialTransferLimits[_from]; } } else { - if(globalTransferLimitInPercentage) { - validTransfer = (_amount.mul(100).div(ISecurityToken(securityToken).totalSupply())) <= globalTransferLimit; + if(isTransferLimitInPercentage) { + validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= globalTransferLimitInPercentage; } else { validTransfer = _amount <= globalTransferLimit; } @@ -59,50 +75,108 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { return Result.INVALID; } - function configure(bool _globalTransferLimitInPercentage, uint256 _globalTransferLimit) public onlyFactory { - globalTransferLimitInPercentage = _globalTransferLimitInPercentage; - changeGlobalLimit(_globalTransferLimit); + /** + * @notice Used to intialize the variables of the contract + * @param _isTransferLimitInPercentage true if the transfer limit is in percentage else false + * @param _globalTransferLimitInPercentageOrToken transfer limit per single transaction. + */ + function configure(bool _isTransferLimitInPercentage, uint256 _globalTransferLimitInPercentageOrToken) public onlyFactory { + require(_globalTransferLimitInPercentageOrToken > 0, "global transfer limit has to greater than 0"); + isTransferLimitInPercentage = _isTransferLimitInPercentage; + if(isTransferLimitInPercentage) { + require(_globalTransferLimitInPercentageOrToken <= 100, "Global transfer limit has be less than 0"); + globalTransferLimitInPercentage = _globalTransferLimitInPercentageOrToken; + } else { + globalTransferLimit = _globalTransferLimitInPercentageOrToken; + } } - function changeGlobalLimit(uint256 _newGlobalTransferLimit) public withPerm(ADMIN) { - if(globalTransferLimitInPercentage) { - require(_newGlobalTransferLimit <= 100); - } + /** + * @notice Change the global transfer limit + * @param _newGlobalTransferLimit new transfer limit in tokens + */ + function changeGlobalLimitInTokens(uint256 _newGlobalTransferLimit) public withPerm(ADMIN) { + require(!isTransferLimitInPercentage, "Transfer limit not set in tokens"); globalTransferLimit = _newGlobalTransferLimit; + emit LogGlobalTransferLimitInTokensSet(_newGlobalTransferLimit); } - function addExemptWallet(address _walletAddress) public withPerm(ADMIN) { - require(_walletAddress != address(0)); - exemptWallets[_walletAddress] = true; + /** + * @notice Change the global transfer limit + * @param _newGlobalTransferLimitInPercentage new transfer limit in percentage. Multiple the percentage by 10^16. Eg 22% will be 22*10^16 + */ + function changeGlobalLimitInPercentage(uint256 _newGlobalTransferLimitInPercentage) public withPerm(ADMIN) { + require(isTransferLimitInPercentage, "Transfer limit not set in Percentage"); + require(_newGlobalTransferLimitInPercentage <= 100, "Transfer Limit has to be <= 100"); + globalTransferLimitInPercentage = _newGlobalTransferLimitInPercentage; + emit LogGlobalTransferLimitInPercentageSet(_newGlobalTransferLimitInPercentage); } - function removeExemptWallet(address _walletAddress) public withPerm(ADMIN) { - require(_walletAddress != address(0)); - exemptWallets[_walletAddress] = false; + /** + * @notice add an exempt wallet + * @param _wallet exempt wallet address + */ + function addExemptWallet(address _wallet) public withPerm(ADMIN) { + require(_wallet != address(0), "Wallet address cannot be a zero address"); + exemptWallets[_wallet] = true; + emit LogExemptWalletAdded(_wallet); } - function setTransferLimitForWallet(address _walletAddress, uint _transferLimit) public withPerm(ADMIN) { - if(globalTransferLimitInPercentage) { - require(_transferLimit <= 100); - } - specialTransferLimitWallets[_walletAddress] = true; - specialTransferLimits[_walletAddress] = _transferLimit; + /** + * @notice remove an exempt wallet + * @param _wallet exempt wallet address + */ + function removeExemptWallet(address _wallet) public withPerm(ADMIN) { + require(_wallet != address(0), "Wallet address cannot be a zero address"); + exemptWallets[_wallet] = false; + emit LogExemptWalletRemoved(_wallet); } - function removeTransferLimitForWallet(address _walletAddress) public withPerm(ADMIN) { - require(specialTransferLimitWallets[_walletAddress]); - specialTransferLimitWallets[_walletAddress] = false; - specialTransferLimits[_walletAddress] = 0; + /** + * @notice set transfer limit per wallet an exempt wallet + * @param _wallet wallet address + * @param _transferLimit transfer limit for the wallet in tokens + */ + function setTransferLimitForWallet(address _wallet, uint _transferLimit) public withPerm(ADMIN) { + require(!isTransferLimitInPercentage, "Transfer limit not in token amount"); + specialTransferLimitWallets[_wallet] = true; + specialTransferLimits[_wallet] = _transferLimit; + emit LogTransferLimitInTokensSet(_wallet, _transferLimit); + } + + /** + * @notice set transfer limit per wallet an exempt wallet + * @param _wallet wallet address + * @param _transferLimitInPercentage transfer limit for the wallet in percentage. Multiple the percentage by 10^16. Eg 22% will be 22*10^16 + */ + function setTransferLimitInPercentage(address _wallet, uint _transferLimitInPercentage) public withPerm(ADMIN) { + require(isTransferLimitInPercentage, "Transfer limit not in percentage"); + specialTransferLimitWallets[_wallet] = true; + specialTransferLimits[_wallet] = _transferLimitInPercentage; + emit LogTransferLimitInPercentageSet(_wallet, _transferLimitInPercentage); } /** - * @notice This function returns the signature of configure function - */ + * @notice removes transfer limit for a wallet + * @param _wallet wallet address + */ + function removeTransferLimitForWallet(address _wallet) public withPerm(ADMIN) { + require(specialTransferLimitWallets[_wallet], "Wallet Address does not have a transfer limit"); + specialTransferLimitWallets[_wallet] = false; + specialTransferLimits[_wallet] = 0; + emit LogTransferLimitRemoved(_wallet); + } + + /** + * @notice This function returns the signature of configure function + */ function getInitFunction() public pure returns (bytes4) { - return bytes4(keccak256("configure(bool, uint256)")); + return bytes4(keccak256("configure(bool,uint256)")); } - // TO IMPLEMENT + /** + * @notice Return the permissions flag that are associated with SingleTradeVolumeRestrictionManager + */ function getPermissions() public view returns(bytes32[]) { bytes32[] memory allPermissions = new bytes32[](1); allPermissions[0] = ADMIN; diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol index a5c4e9827..ef7db2334 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol @@ -2,7 +2,8 @@ pragma solidity ^0.4.24; import "../../interfaces/IModuleFactory.sol"; import "./SingleTradeVolumeRestrictionManager.sol"; -contract SingleTradeVolumeRestrictionFactory is IModuleFactory { +contract SingleTradeVolumeRestrictionManagerFactory is IModuleFactory { + /** * @notice Constructor @@ -56,14 +57,14 @@ contract SingleTradeVolumeRestrictionFactory is IModuleFactory { * @notice Get the title of the Module */ function getTitle() public view returns(string) { - return "Single Trade Volume Restriction"; + return "Single Trade Volume Restriction Manager"; } /** * @notice Get the Instructions that help to use the module */ function getInstructions() public view returns(string) { - return "Allows an issuer to impose volume restriction on a single trade"; + return "Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens"; } /** diff --git a/test/u_single_trade_volume_restriction.js b/test/u_single_trade_volume_restriction.js new file mode 100644 index 000000000..969d41650 --- /dev/null +++ b/test/u_single_trade_volume_restriction.js @@ -0,0 +1,591 @@ +import latestTime from './helpers/latestTime'; +import { duration, ensureException, promisifyLogWatch, latestBlock } from './helpers/utils'; +import takeSnapshot, { increaseTime, revertToSnapshot } from './helpers/time'; + +const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') +const ModuleRegistry = artifacts.require('./ModuleRegistry.sol'); +const SecurityToken = artifacts.require('./SecurityToken.sol'); +const SecurityTokenRegistry = artifacts.require('./SecurityTokenRegistry.sol'); +const TickerRegistry = artifacts.require('./TickerRegistry.sol'); +const STVersion = artifacts.require('./STVersionProxy001.sol'); +const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol'); +const GeneralTransferManagerFactory = artifacts.require('./GeneralTransferManagerFactory.sol'); +const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); +const SingleTradeVolumeRestrictionManagerFactory = artifacts.require('./SingleTradeVolumeRestrictionManagerFactory.sol'); +const SingleTradeVolumeRestrictionManager = artifacts.require('./SingleTradeVolumeRestrictionManager'); +const CountTransferManagerFactory = artifacts.require('./CountTransferManagerFactory.sol'); +const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); +const PolyTokenFaucet = artifacts.require('./PolyTokenFaucet.sol'); + +const Web3 = require('web3'); +const BigNumber = require('bignumber.js'); +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port + +contract('SingleTradeVolumeRestrictionManager', accounts => { + + + + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + let account_investor5; + + // investor Details + let fromTime = latestTime(); + let toTime = latestTime(); + let expiryTime = toTime + duration.days(15); + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let I_GeneralPermissionManagerFactory; + let I_GeneralTransferManagerFactory; + let I_GeneralPermissionManager; + let I_GeneralTransferManager; + let I_SingleTradeVolumeRestrictionManagerFactory; + let I_SingleTradeVolumeRestrictionManager; + let P_SingleTradeVolumeRestrictionManagerFactory; + let P_SingleTradeVolumeRestrictionManager; + let I_SingleTradeVolumeRestrictionPercentageManager; + let I_ModuleRegistry; + let I_TickerRegistry; + let I_SecurityTokenRegistry; + let I_STVersion; + let I_SecurityToken; + let I_PolyToken; + let I_PolymathRegistry; + + // SecurityToken Details + const swarmHash = "dagwrgwgvwergwrvwrg"; + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + // Initial fee for ticker registry and security token registry + const initRegFee = 250 * Math.pow(10, 18); + + before(async() => { + // Accounts setup + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + + account_investor1 = accounts[6]; + account_investor2 = accounts[7]; + account_investor3 = accounts[8]; + account_investor4 = accounts[9]; + account_investor5 = accounts[5]; + + // ----------- POLYMATH NETWORK Configuration ------------ + + // Step 0: Deploy the PolymathRegistry + I_PolymathRegistry = await PolymathRegistry.new({from: account_polymath}); + + // Step 1: Deploy the token Faucet and Mint tokens for token_owner + I_PolyToken = await PolyTokenFaucet.new(); + await I_PolyToken.getTokens((10000 * Math.pow(10, 18)), token_owner); + await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, {from: account_polymath}) + + // STEP 2: Deploy the ModuleRegistry + + I_ModuleRegistry = await ModuleRegistry.new(I_PolymathRegistry.address, {from:account_polymath}); + await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistry.address, {from: account_polymath}); + + assert.notEqual( + I_ModuleRegistry.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "ModuleRegistry contract was not deployed" + ); + + // STEP 2: Deploy the GeneralTransferManagerFactory + + I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); + + assert.notEqual( + I_GeneralTransferManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralTransferManagerFactory contract was not deployed" + ); + + + + // STEP 3: Deploy the GeneralPermissionManagerFactory + + I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); + + assert.notEqual( + I_GeneralPermissionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralDelegateManagerFactory contract was not deployed" + ); + + // STEP 4: Deploy the SingleTradeVolumeRestrictionManagerFactory + I_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from: account_polymath}); + + assert.notEqual( + I_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" + ); + + + P_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, web3.utils.toWei("500", "ether"), 0, 0, {from:account_polymath}); + assert.notEqual( + P_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" + ); + + // STEP 5: Register the Modules with the ModuleRegistry contract + + // (A) : Register the GeneralTransferManagerFactory + await I_ModuleRegistry.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_ModuleRegistry.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + + + + // (B) : Register the GeneralDelegateManagerFactory + await I_ModuleRegistry.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_ModuleRegistry.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + + // (C) : Register SingleTradeVolumeRestrictionManagerFactory + await I_ModuleRegistry.registerModule(I_SingleTradeVolumeRestrictionManagerFactory.address, { from: account_polymath }); + await I_ModuleRegistry.verifyModule(I_SingleTradeVolumeRestrictionManagerFactory.address, true, { from: account_polymath }); + + await I_ModuleRegistry.registerModule(P_SingleTradeVolumeRestrictionManagerFactory.address, { from: account_polymath }); + await I_ModuleRegistry.verifyModule(P_SingleTradeVolumeRestrictionManagerFactory.address, true, { from: account_polymath }); + + // Step 6: Deploy the TickerRegistry + I_TickerRegistry = await TickerRegistry.new(I_PolymathRegistry.address, initRegFee, { from: account_polymath }); + await I_PolymathRegistry.changeAddress("TickerRegistry", I_TickerRegistry.address, {from: account_polymath}); + + + assert.notEqual( + I_TickerRegistry.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "TickerRegistry contract was not deployed", + ); + + // Step 7: Deploy the STversionProxy contract + + I_STVersion = await STVersion.new(I_GeneralTransferManagerFactory.address); + + assert.notEqual( + I_STVersion.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "STVersion contract was not deployed", + ); + + // Step 8: Deploy the SecurityTokenRegistry + + I_SecurityTokenRegistry = await SecurityTokenRegistry.new( + I_PolymathRegistry.address, + I_STVersion.address, + initRegFee, + { + from: account_polymath + }); + await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistry.address, {from: account_polymath}); + + assert.notEqual( + I_SecurityTokenRegistry.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SecurityTokenRegistry contract was not deployed", + ); + + // Step 10: update the registries addresses from the PolymathRegistry contract + await I_SecurityTokenRegistry.updateFromRegistry({from: account_polymath}); + await I_ModuleRegistry.updateFromRegistry({from: account_polymath}); + await I_TickerRegistry.updateFromRegistry({from: account_polymath}); + + }) + + describe("Generate the SecurityToken", async () => { + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_TickerRegistry.address, initRegFee, { from: token_owner }); + let tx = await I_TickerRegistry.registerTicker(token_owner, symbol, contact, swarmHash, { from : token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._symbol, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_SecurityTokenRegistry.address, initRegFee, { from: token_owner }); + let _blockNo = latestBlock(); + let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner, gas: 60000000 }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + + const log = await promisifyLogWatch(I_SecurityToken.LogModuleAdded({from: _blockNo}), 1); + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._type.toNumber(), 2); + assert.equal(web3.utils.toUtf8(log.args._name), "GeneralTransferManager"); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = await I_SecurityToken.modules(2, 0); + I_GeneralTransferManager = GeneralTransferManager.at(moduleData[1]); + + assert.notEqual( + I_GeneralTransferManager.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralTransferManager contract was not deployed", + ); + + }); + }); +// + describe("Buy tokens using whitelist & manual approvals", async() => { + + it("Should Buy the tokens", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyWhitelist( + account_investor1, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer, + gas: 6000000 + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Jump time + await increaseTime(5000); + + // Mint some tokens + await I_SecurityToken.mint(account_investor1, web3.utils.toWei('4', 'ether'), { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), + web3.utils.toWei('4', 'ether') + ); + }); + + it("Should Buy some more tokens", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyWhitelist( + account_investor2, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer, + gas: 6000000 + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor2.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Mint some tokens + await I_SecurityToken.mint(account_investor2, web3.utils.toWei('1', 'ether'), { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor2)).toNumber(), + web3.utils.toWei('1', 'ether') + ); + }); +// + it("Should successfully attach the SingleTradeVolumeRestrictionManager with the security token", async () => { + let managerArgs = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'bool', + name: '_isTransferLimitInPercentage' + }, + { + type: 'uint256', + name: '_globalTransferLimitInPercentageOrToken' + } + ] + }, [true, 90]) + let errorThrown = false; + await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); + try { + const tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { from: token_owner }); + } catch(error) { + console.log(` tx -> failed because Token is not paid`.grey); + ensureException(error); + errorThrown = true; + } + assert.ok(errorThrown, message); + }); + + it("Should successfully attach the Paid SingleTradeVolumeRestrictionManager with the security token", async () => { + let errorThrown = false; + let managerArgs = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'bool', + name: '_isTransferLimitInPercentage' + }, + { + type: 'uint256', + name: '_globalTransferLimitInPercentageOrToken' + } + ] + }, [true, 7 * 10 ** 16]); + await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); + try { + const tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { from: token_owner }); + } catch(error) { + console.log(` tx -> failed because Token is not paid`.grey); + ensureException(error); + errorThrown = true; + } + assert.ok(errorThrown, message); + }); + + it("Should successfully attach the SingleTradeVolumeRestrictionManager with the security token", async () => { + let snapId = await takeSnapshot(); + let managerArgs = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'bool', + name: '_isTransferLimitInPercentage' + }, + { + type: 'uint256', + name: '_globalTransferLimitInPercentageOrToken' + } + ] + }, [true, 90]); + await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), {from: token_owner}); + let tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { from: token_owner }); + assert.equal(tx.logs[3].args._type.toNumber(), transferManagerKey, "SingleTradeVolumeRestrictionManager did not get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[3].args._name) + .replace(/\u0000/g, ''), + "SingleTradeVolumeRestriction", + "SingleTradeVolumeRestrictionManagerFactory module was not added" + ); + P_SingleTradeVolumeRestrictionManager = SingleTradeVolumeRestrictionManager.at(tx.logs[3].args._module); + await revertToSnapshot(snapId); + }); + + it("Should successfully attach the SingleTradeVolumeRestrictionManager with the security token", async () => { + let managerArgs = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'bool', + name: '_isTransferLimitInPercentage' + }, + { + type: 'uint256', + name: '_globalTransferLimitInPercentageOrToken' + } + ] + }, [false, 7 * 10 ** 16]) + const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { from: token_owner }); + assert.equal(tx.logs[2].args._type.toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name) + .replace(/\u0000/g, ''), + "SingleTradeVolumeRestriction", + "SingleTradeVolumeRestriction module was not added" + ); + I_SingleTradeVolumeRestrictionManager = SingleTradeVolumeRestrictionManager.at(tx.logs[2].args._module); + }); + + it("Should successfully attach the SingleTradeVolumeRestrictionManager (Percentage) with the security token", async () => { + let managerArgs = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'bool', + name: '_isTransferLimitInPercentage' + }, + { + type: 'uint256', + name: '_globalTransferLimitInPercentageOrToken' + } + ] + }, [true, 90]); + const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { from: token_owner }); + assert.equal(tx.logs[2].args._type.toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name) + .replace(/\u0000/g, ''), + "SingleTradeVolumeRestriction", + "SingleTradeVolumeRestriction module was not added" + ); + I_SingleTradeVolumeRestrictionPercentageManager = SingleTradeVolumeRestrictionManager.at(tx.logs[2].args._module); + }); + + it('should return get permissions', async () => { + let permissions = await I_SingleTradeVolumeRestrictionPercentageManager.getPermissions(); + assert.equal(permissions.length, 1, "Invalid Permissions"); + assert.equal( + web3.utils.toAscii(permissions[0]).replace(/\u0000/g, ''), + "ADMIN", + 'Wrong permissions' + ); + }); + + it('add exempt wallet', async() => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5]); + } catch(e) { + errorThrown = true; + } + assert.ok(errorThrown, "Non Admins cannot add exempt wallets"); + + + let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5], { from: token_owner }); + assert.equal(tx.logs[0].args._wallet, accounts[5], "Wrong wallet added as exempt"); + }); + + it('should remove an exempt wallet', async () => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5]); + } catch(e) { + errorThrown = true; + } + assert.ok(errorThrown, "Non Admins cannot add exempt wallets"); + + + let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5], { from: token_owner }); + assert.equal(tx.logs[0].args._wallet, accounts[5], "Wrong wallet removed from exempt"); + }); + + it('should set transfer limit for a wallet', async () => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(accounts[4], 100); + } catch(e) { + errorThrown = true; + } + assert.ok(errorThrown, "Non Admins cannot set transfer limits"); + + let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(accounts[4], 100, { from:token_owner }); + assert.equal(tx.logs[0].args._wallet, accounts[4]); + assert.equal(tx.logs[0].args._amount, 100); + + tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 50, { from: token_owner }); + assert.equal(tx.logs[0].args._wallet, accounts[4], "Wrong wallet added to transfer limits"); + assert.equal(tx.logs[0].args._percentage, 50, "Wrong percentage set"); + }); + + it('should remove transfer limit for wallet', async() => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitForWallet(accounts[4]); + } catch(e) { + errorThrown = true; + } + assert.ok(errorThrown, "Non Admins cannot set/remove transfer limits"); + let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitForWallet(accounts[4], {from: token_owner }); + assert.equal(tx.logs[0].args._wallet,accounts[4], "Wrong wallet removed"); + }); + + it("Should pause the tranfers at Manager level", async() => { + let tx = await I_SingleTradeVolumeRestrictionManager.pause({from: token_owner}); + }); + + it('should be able to set a global transfer limit', async () => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(100*10**18); + } catch(e) { + errorThrown = true; + } + assert.ok(errorThrown, true); + + + let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(10, {from: token_owner}); + assert.equal(tx.logs[0].args._amount, 10, "Global Limit not set"); + + errorThrown = false; + + try { + let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89); + } catch(e) { + errorThrown = true; + } + assert.ok(errorThrown, true, "Global limit can be set by non-admins"); + tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(40, {from:token_owner}); + assert.equal(tx.logs[0].args._percentage, 40, "Global Limit not set"); + + }); + + it('should be able to transfer tokens', async () => { + // await I_SingleTradeVolumeRestrictionManager.pause({from: token_owner }) + // await I_SingleTradeVolumeRestrictionPercentageManager.pause({from: token_owner }) + // await P_SingleTradeVolumeRestrictionManager.pause({from: token_owner }); + await I_GeneralTransferManager.modifyWhitelist( + account_investor3, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer, + gas: 6000000 + } + ); + + await I_GeneralTransferManager.modifyWhitelist( + account_investor4, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer, + gas: 6000000 + } + ); + }) + }); + + describe("SingleTradeVolumeRestrictionManager Factory test cases", async() => { + + it("Should get the exact details of the factory", async() => { + assert.equal(await I_SingleTradeVolumeRestrictionManagerFactory.setupCost.call(),0); + assert.equal(await I_SingleTradeVolumeRestrictionManagerFactory.getType.call(),2); + let name = web3.utils.toUtf8(await I_SingleTradeVolumeRestrictionManagerFactory.getName.call()); + assert.equal(name,"SingleTradeVolumeRestriction","Wrong Module added"); + let desc = await I_SingleTradeVolumeRestrictionManagerFactory.getDescription.call(); + assert.equal(desc,"Imposes volume restriction on a single trade","Wrong Module added"); + let title = await I_SingleTradeVolumeRestrictionManagerFactory.getTitle.call(); + assert.equal(title,"Single Trade Volume Restriction Manager","Wrong Module added"); + let inst = await I_SingleTradeVolumeRestrictionManagerFactory.getInstructions.call(); + assert.equal(inst,"Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens","Wrong Module added"); + }); + + it("Should get the tags of the factory", async() => { + let tags = await I_SingleTradeVolumeRestrictionManagerFactory.getTags.call(); + assert.equal(web3.utils.toUtf8(tags[0]), "Single Trade"); + assert.equal(web3.utils.toUtf8(tags[1]), "Transfer"); + assert.equal(web3.utils.toUtf8(tags[2]), "Volume"); + }); + + + }); +}); From c7f5012f6e0e0ff55160e0e1b518067a40d24663 Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Wed, 26 Sep 2018 21:31:38 +0530 Subject: [PATCH 06/27] Complete tests --- .../SingleTradeVolumeRestrictionManager.sol | 6 +- test/u_single_trade_volume_restriction.js | 142 +++++++++++++----- 2 files changed, 107 insertions(+), 41 deletions(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index afcf6dd51..ad33a7674 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -15,9 +15,9 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { using SafeMath for uint256; bool public isTransferLimitInPercentage; - uint256 public globalTransferLimit; + // should be multipled by 10^16. if the transfer percentage is 20%, then globalTransferLimitInPercentage should be 20*10^16 uint256 public globalTransferLimitInPercentage; mapping(address=>bool) public exemptWallets; @@ -50,7 +50,6 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { /// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers function verifyTransfer(address _from, address /* _to */, uint256 _amount, bool /* _isTransfer */) public returns(Result) { bool validTransfer; - if(paused) { return Result.NA; } @@ -70,7 +69,6 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { validTransfer = _amount <= globalTransferLimit; } } - if(validTransfer) return Result.NA; return Result.INVALID; } @@ -84,7 +82,6 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { require(_globalTransferLimitInPercentageOrToken > 0, "global transfer limit has to greater than 0"); isTransferLimitInPercentage = _isTransferLimitInPercentage; if(isTransferLimitInPercentage) { - require(_globalTransferLimitInPercentageOrToken <= 100, "Global transfer limit has be less than 0"); globalTransferLimitInPercentage = _globalTransferLimitInPercentageOrToken; } else { globalTransferLimit = _globalTransferLimitInPercentageOrToken; @@ -107,7 +104,6 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { */ function changeGlobalLimitInPercentage(uint256 _newGlobalTransferLimitInPercentage) public withPerm(ADMIN) { require(isTransferLimitInPercentage, "Transfer limit not set in Percentage"); - require(_newGlobalTransferLimitInPercentage <= 100, "Transfer Limit has to be <= 100"); globalTransferLimitInPercentage = _newGlobalTransferLimitInPercentage; emit LogGlobalTransferLimitInPercentageSet(_newGlobalTransferLimitInPercentage); } diff --git a/test/u_single_trade_volume_restriction.js b/test/u_single_trade_volume_restriction.js index 969d41650..7b8dc6c10 100644 --- a/test/u_single_trade_volume_restriction.js +++ b/test/u_single_trade_volume_restriction.js @@ -273,11 +273,11 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { await increaseTime(5000); // Mint some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei('4', 'ether'), { from: token_owner }); + await I_SecurityToken.mint(account_investor1, web3.utils.toWei('100', 'ether'), { from: token_owner }); assert.equal( (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), - web3.utils.toWei('4', 'ether') + web3.utils.toWei('100', 'ether') ); }); @@ -306,7 +306,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { ); }); // - it("Should successfully attach the SingleTradeVolumeRestrictionManager with the security token", async () => { + it("Fails to attach the SingleTradeVolumeRestrictionManager with the security token due to fees not paid", async () => { let managerArgs = web3.eth.abi.encodeFunctionCall({ name: 'configure', type: 'function', @@ -332,34 +332,9 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { assert.ok(errorThrown, message); }); - it("Should successfully attach the Paid SingleTradeVolumeRestrictionManager with the security token", async () => { - let errorThrown = false; - let managerArgs = web3.eth.abi.encodeFunctionCall({ - name: 'configure', - type: 'function', - inputs: [{ - type: 'bool', - name: '_isTransferLimitInPercentage' - }, - { - type: 'uint256', - name: '_globalTransferLimitInPercentageOrToken' - } - ] - }, [true, 7 * 10 ** 16]); - await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); - try { - const tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { from: token_owner }); - } catch(error) { - console.log(` tx -> failed because Token is not paid`.grey); - ensureException(error); - errorThrown = true; - } - assert.ok(errorThrown, message); - }); - it("Should successfully attach the SingleTradeVolumeRestrictionManager with the security token", async () => { - let snapId = await takeSnapshot(); + + it("Should successfully attach the Paid SingleTradeVolumeRestrictionManager with the security token", async () => { let managerArgs = web3.eth.abi.encodeFunctionCall({ name: 'configure', type: 'function', @@ -383,7 +358,6 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { "SingleTradeVolumeRestrictionManagerFactory module was not added" ); P_SingleTradeVolumeRestrictionManager = SingleTradeVolumeRestrictionManager.at(tx.logs[3].args._module); - await revertToSnapshot(snapId); }); it("Should successfully attach the SingleTradeVolumeRestrictionManager with the security token", async () => { @@ -527,17 +501,18 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89); } catch(e) { errorThrown = true; + ensureException(e); } assert.ok(errorThrown, true, "Global limit can be set by non-admins"); tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(40, {from:token_owner}); assert.equal(tx.logs[0].args._percentage, 40, "Global Limit not set"); - }); - it('should be able to transfer tokens', async () => { - // await I_SingleTradeVolumeRestrictionManager.pause({from: token_owner }) - // await I_SingleTradeVolumeRestrictionPercentageManager.pause({from: token_owner }) - // await P_SingleTradeVolumeRestrictionManager.pause({from: token_owner }); + it('should be able to transfer tokens SingleTradeVolumeRestriction', async () => { + await I_SingleTradeVolumeRestrictionManager.unpause({from: token_owner }) + await I_SingleTradeVolumeRestrictionPercentageManager.pause({from: token_owner }) + await P_SingleTradeVolumeRestrictionManager.pause({from: token_owner }); + await I_GeneralTransferManager.modifyWhitelist( account_investor3, latestTime(), @@ -561,7 +536,102 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { gas: 6000000 } ); + + await I_GeneralTransferManager.modifyWhitelist( + account_investor5, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer, + gas: 6000000 + } + ); + + //setting a max of 5 tokens + await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(web3.utils.toWei('5', 'ether'), {from: token_owner}) + let errorThrown = false; + try { + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('6', 'ether'), {from: account_investor1 }); + } catch(e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, true, "Transfer should have not happened"); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('4', 'ether'), {from: account_investor1}); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei('4', 'ether')); + + // exempt wallet + await I_SingleTradeVolumeRestrictionManager.addExemptWallet(account_investor1, { from: token_owner }); + await I_SecurityToken.transfer(account_investor5, web3.utils.toWei('7', 'ether'), {from: account_investor1}); + assert.equal((await I_SecurityToken.balanceOf(account_investor5)).toNumber(), web3.utils.toWei('7', 'ether')); + + //special limits wallet + await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(account_investor5, web3.utils.toWei('5', 'ether'), {from: token_owner}); + errorThrown = false; + try { + await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('7', 'ether'), {from: account_investor5}); + } catch(e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, true, "Transfer should have not happened"); + await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('4', 'ether'), {from: account_investor5 }) + assert.equal((await I_SecurityToken.balanceOf(account_investor4)).toNumber(), web3.utils.toWei('4', 'ether')) }) + + it('should be able to transfer tokens (percentage transfer limit)', async () => { + await I_SingleTradeVolumeRestrictionManager.pause({from: token_owner }); + let balance = (await I_SecurityToken.balanceOf(account_investor2)).toNumber(); + await I_SecurityToken.transfer(account_investor1, balance, {from: account_investor2 }); + + + balance = (await I_SecurityToken.balanceOf(account_investor3)).toNumber(); + + await I_SecurityToken.transfer(account_investor1, balance, {from: account_investor3 }); + + + balance = (await I_SecurityToken.balanceOf(account_investor4)).toNumber(); + await I_SecurityToken.transfer(account_investor1, balance, {from: account_investor4 }); + + balance = (await I_SecurityToken.balanceOf(account_investor5)).toNumber(); + await I_SecurityToken.transfer(account_investor1, balance, {from: account_investor5 }); + + await I_SingleTradeVolumeRestrictionPercentageManager.unpause({from: token_owner }); + // // + await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(49 * 10 ** 16, {from: token_owner}); + + let errorThrown = false; + try { + // more than the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('90', 'ether'), {from:account_investor1}); + } catch(e) { + ensureException(e); + errorThrown = true; + } + assert.ok(errorThrown, true, "Transfer above limit happened"); + + + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('20', 'ether'), {from: account_investor1}); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei('20', 'ether')) + + await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(account_investor1, 5 * 10 ** 16, {from : token_owner}); + errorThrown = false; + try { + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('35', 'ether'), {from: account_investor1}); + } catch(e) { + ensureException(e); + errorThrown = true; + } + assert.ok(errorThrown, true, "transfer happened above limit"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('1', 'ether'), {from: account_investor1}); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei('1', 'ether')); + }); + + + }); describe("SingleTradeVolumeRestrictionManager Factory test cases", async() => { From 939d0c0f19d6124fba523ba777accfd05c971e63 Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Wed, 26 Sep 2018 23:09:08 +0530 Subject: [PATCH 07/27] Update comments --- .../SingleTradeVolumeRestrictionManager.sol | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index ad33a7674..b2b0aaa20 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -1,15 +1,10 @@ pragma solidity ^0.4.24; import "./ITransferManager.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; -///////////////////// -// Module permissions -///////////////////// -// Owner ADMIN -// changeGlobalLimit X X -// addExemptWallet X X -// removeExemptWallet X X -// setTransferLimitForWallet X X -// removeTransferLimitForWallet X X +/** + * @title Transfer Manager for limiting volume of tokens in a single trade + */ contract SingleTradeVolumeRestrictionManager is ITransferManager { using SafeMath for uint256; From 4e1010f182d1fde3951cfc6d5ae310211a3d6547 Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Wed, 26 Sep 2018 23:35:36 +0530 Subject: [PATCH 08/27] change implementation according to development-1.5 branch --- .../SingleTradeVolumeRestrictionManager.sol | 2 +- ...leTradeVolumeRestrictionManagerFactory.sol | 37 +++++++++++++++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index b2b0aaa20..fdc601e0f 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -37,7 +37,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { * @param _polyAddress Address of the polytoken */ constructor(address _securityToken, address _polyAddress) public - IModule(_securityToken, _polyAddress) + Module(_securityToken, _polyAddress) { } diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol index ef7db2334..ecb9304f7 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol @@ -1,8 +1,12 @@ pragma solidity ^0.4.24; -import "../../interfaces/IModuleFactory.sol"; +import "./../ModuleFactory.sol"; import "./SingleTradeVolumeRestrictionManager.sol"; -contract SingleTradeVolumeRestrictionManagerFactory is IModuleFactory { +import "../../libraries/Util.sol"; +/** + * @title Factory for deploying SingleTradeVolumeRestrictionManager + */ +contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { /** @@ -13,9 +17,12 @@ contract SingleTradeVolumeRestrictionManagerFactory is IModuleFactory { * @param _subscriptionCost Subscription cost of the module */ constructor(address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - IModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) { - + version = "1.0.0"; + name = "SingleTradeVolumeRestriction"; + title = "Single Trade Volume Restriction Manager"; + description = "Imposes volume restriction on a single trade"; } /** @@ -26,7 +33,8 @@ contract SingleTradeVolumeRestrictionManagerFactory is IModuleFactory { if (setupCost > 0) require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); SingleTradeVolumeRestrictionManager singleTradeVolumeRestrictionManager = new SingleTradeVolumeRestrictionManager(msg.sender, address(polyToken)); - require(getSig(_data) == singleTradeVolumeRestrictionManager.getInitFunction(), "Provided data is not valid"); + + require(Util.getSig(_data) == singleTradeVolumeRestrictionManager.getInitFunction(), "Provided data is not valid"); require(address(singleTradeVolumeRestrictionManager).call(_data), "Un-successfull call"); emit LogGenerateModuleFromFactory(address(singleTradeVolumeRestrictionManager), getName(), address(this), msg.sender, now); return address(singleTradeVolumeRestrictionManager); @@ -43,21 +51,21 @@ contract SingleTradeVolumeRestrictionManagerFactory is IModuleFactory { * @notice Get the name of the Module */ function getName() public view returns(bytes32) { - return "SingleTradeVolumeRestriction"; + return name; } /** * @notice Get the description of the Module */ function getDescription() public view returns(string) { - return "Imposes volume restriction on a single trade"; + return description; } /** * @notice Get the title of the Module */ function getTitle() public view returns(string) { - return "Single Trade Volume Restriction Manager"; + return title; } /** @@ -67,6 +75,19 @@ contract SingleTradeVolumeRestrictionManagerFactory is IModuleFactory { return "Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens"; } + /** + * @notice Get the version of the Module + */ + function getVersion() external view returns(string) { + return version; + } + + /** + * @notice Get the setup cost of the module + */ + function getSetupCost() external view returns (uint256) { + return setupCost; + } /** * @notice Get the tags related to the module factory */ From c87b5c27c85697aff234992a991f5354bcf4d53d Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Thu, 27 Sep 2018 00:17:03 +0530 Subject: [PATCH 09/27] Tests refactored --- test/u_single_trade_volume_restriction.js | 234 +++++++++++----------- 1 file changed, 119 insertions(+), 115 deletions(-) diff --git a/test/u_single_trade_volume_restriction.js b/test/u_single_trade_volume_restriction.js index 7b8dc6c10..b9a8ccb52 100644 --- a/test/u_single_trade_volume_restriction.js +++ b/test/u_single_trade_volume_restriction.js @@ -1,13 +1,15 @@ import latestTime from './helpers/latestTime'; import { duration, ensureException, promisifyLogWatch, latestBlock } from './helpers/utils'; import takeSnapshot, { increaseTime, revertToSnapshot } from './helpers/time'; +import { encodeProxyCall } from './helpers/encodeCall'; const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') const ModuleRegistry = artifacts.require('./ModuleRegistry.sol'); const SecurityToken = artifacts.require('./SecurityToken.sol'); const SecurityTokenRegistry = artifacts.require('./SecurityTokenRegistry.sol'); -const TickerRegistry = artifacts.require('./TickerRegistry.sol'); -const STVersion = artifacts.require('./STVersionProxy001.sol'); +const SecurityTokenRegistryProxy = artifacts.require('./SecurityTokenRegistryProxy.sol'); +const FeatureRegistry = artifacts.require('./FeatureRegistry.sol'); +const STFactory = artifacts.require('./STFactory.sol'); const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol'); const GeneralTransferManagerFactory = artifacts.require('./GeneralTransferManagerFactory.sol'); const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); @@ -43,6 +45,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { let message = "Transaction Should Fail!"; // Contract Instance Declaration + let I_SecurityTokenRegistryProxy let I_GeneralPermissionManagerFactory; let I_GeneralTransferManagerFactory; let I_GeneralPermissionManager; @@ -53,9 +56,10 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { let P_SingleTradeVolumeRestrictionManager; let I_SingleTradeVolumeRestrictionPercentageManager; let I_ModuleRegistry; - let I_TickerRegistry; + let I_FeatureRegistry; let I_SecurityTokenRegistry; - let I_STVersion; + let I_STRProxied; + let I_STFactory; let I_SecurityToken; let I_PolyToken; let I_PolymathRegistry; @@ -74,157 +78,153 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { const stoKey = 3; // Initial fee for ticker registry and security token registry - const initRegFee = 250 * Math.pow(10, 18); + const initRegFee = web3.utils.toWei("250"); before(async() => { - // Accounts setup - account_polymath = accounts[0]; - account_issuer = accounts[1]; + // Accounts setup + account_polymath = accounts[0]; + account_issuer = accounts[1]; - token_owner = account_issuer; + token_owner = account_issuer; - account_investor1 = accounts[6]; - account_investor2 = accounts[7]; - account_investor3 = accounts[8]; - account_investor4 = accounts[9]; - account_investor5 = accounts[5]; + account_investor1 = accounts[6]; + account_investor2 = accounts[7]; + account_investor3 = accounts[8]; + account_investor4 = accounts[9]; + account_investor5 = accounts[5]; - // ----------- POLYMATH NETWORK Configuration ------------ + // ----------- POLYMATH NETWORK Configuration ------------ - // Step 0: Deploy the PolymathRegistry - I_PolymathRegistry = await PolymathRegistry.new({from: account_polymath}); + // Step 0: Deploy the PolymathRegistry + I_PolymathRegistry = await PolymathRegistry.new({from: account_polymath}); - // Step 1: Deploy the token Faucet and Mint tokens for token_owner - I_PolyToken = await PolyTokenFaucet.new(); - await I_PolyToken.getTokens((10000 * Math.pow(10, 18)), token_owner); - await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, {from: account_polymath}) + // Step 1: Deploy the token Faucet and Mint tokens for token_owner + I_PolyToken = await PolyTokenFaucet.new(); + await I_PolyToken.getTokens((10000 * Math.pow(10, 18)), token_owner); + await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, {from: account_polymath}) - // STEP 2: Deploy the ModuleRegistry - - I_ModuleRegistry = await ModuleRegistry.new(I_PolymathRegistry.address, {from:account_polymath}); - await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistry.address, {from: account_polymath}); - - assert.notEqual( - I_ModuleRegistry.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "ModuleRegistry contract was not deployed" - ); - - // STEP 2: Deploy the GeneralTransferManagerFactory - - I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); - - assert.notEqual( - I_GeneralTransferManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "GeneralTransferManagerFactory contract was not deployed" - ); + // STEP 2: Deploy the ModuleRegistry + I_ModuleRegistry = await ModuleRegistry.new(I_PolymathRegistry.address, {from:account_polymath}); + await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistry.address, {from: account_polymath}); + assert.notEqual( + I_ModuleRegistry.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "ModuleRegistry contract was not deployed" + ); - // STEP 3: Deploy the GeneralPermissionManagerFactory + // STEP 2: Deploy the GeneralTransferManagerFactory - I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); + I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); - assert.notEqual( - I_GeneralPermissionManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "GeneralDelegateManagerFactory contract was not deployed" - ); + assert.notEqual( + I_GeneralTransferManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralTransferManagerFactory contract was not deployed" + ); - // STEP 4: Deploy the SingleTradeVolumeRestrictionManagerFactory - I_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from: account_polymath}); + // STEP 3: Deploy the GeneralDelegateManagerFactoryFactory - assert.notEqual( - I_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" - ); + I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); + assert.notEqual( + I_GeneralPermissionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralDelegateManagerFactory contract was not deployed" + ); - P_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, web3.utils.toWei("500", "ether"), 0, 0, {from:account_polymath}); - assert.notEqual( - P_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" - ); - - // STEP 5: Register the Modules with the ModuleRegistry contract + // STEP 4: Deploy the SingleTradeVolumeRestrictionManagerFactory + I_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from: account_polymath}); - // (A) : Register the GeneralTransferManagerFactory - await I_ModuleRegistry.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_ModuleRegistry.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + assert.notEqual( + I_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" + ); + P_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, web3.utils.toWei("500", "ether"), 0, 0, {from:account_polymath}); + assert.notEqual( + P_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" + ); - // (B) : Register the GeneralDelegateManagerFactory - await I_ModuleRegistry.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_ModuleRegistry.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + // STEP 5: Register the Modules with the ModuleRegistry contract - // (C) : Register SingleTradeVolumeRestrictionManagerFactory - await I_ModuleRegistry.registerModule(I_SingleTradeVolumeRestrictionManagerFactory.address, { from: account_polymath }); - await I_ModuleRegistry.verifyModule(I_SingleTradeVolumeRestrictionManagerFactory.address, true, { from: account_polymath }); + // (A) : Register the GeneralTransferManagerFactory + await I_ModuleRegistry.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + await I_ModuleRegistry.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); - await I_ModuleRegistry.registerModule(P_SingleTradeVolumeRestrictionManagerFactory.address, { from: account_polymath }); - await I_ModuleRegistry.verifyModule(P_SingleTradeVolumeRestrictionManagerFactory.address, true, { from: account_polymath }); + // (B) : Register the GeneralDelegateManagerFactory + await I_ModuleRegistry.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); + await I_ModuleRegistry.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - // Step 6: Deploy the TickerRegistry - I_TickerRegistry = await TickerRegistry.new(I_PolymathRegistry.address, initRegFee, { from: account_polymath }); - await I_PolymathRegistry.changeAddress("TickerRegistry", I_TickerRegistry.address, {from: account_polymath}); + // (C) : Register SingleTradeVolumeRestrictionManagerFactory + await I_ModuleRegistry.registerModule(I_SingleTradeVolumeRestrictionManagerFactory.address, { from: account_polymath }); + await I_ModuleRegistry.verifyModule(I_SingleTradeVolumeRestrictionManagerFactory.address, true, { from: account_polymath }); + await I_ModuleRegistry.registerModule(P_SingleTradeVolumeRestrictionManagerFactory.address, { from: account_polymath }); + await I_ModuleRegistry.verifyModule(P_SingleTradeVolumeRestrictionManagerFactory.address, true, { from: account_polymath }); - assert.notEqual( - I_TickerRegistry.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "TickerRegistry contract was not deployed", - ); + // Step 7: Deploy the STFactory contract - // Step 7: Deploy the STversionProxy contract + I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address); - I_STVersion = await STVersion.new(I_GeneralTransferManagerFactory.address); + assert.notEqual( + I_STFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "STFactory contract was not deployed", + ); - assert.notEqual( - I_STVersion.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "STVersion contract was not deployed", - ); + // Step 9: Deploy the SecurityTokenRegistry - // Step 8: Deploy the SecurityTokenRegistry + I_SecurityTokenRegistry = await SecurityTokenRegistry.new({from: account_polymath }); - I_SecurityTokenRegistry = await SecurityTokenRegistry.new( - I_PolymathRegistry.address, - I_STVersion.address, - initRegFee, - { - from: account_polymath - }); - await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistry.address, {from: account_polymath}); - - assert.notEqual( - I_SecurityTokenRegistry.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "SecurityTokenRegistry contract was not deployed", - ); + assert.notEqual( + I_SecurityTokenRegistry.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SecurityTokenRegistry contract was not deployed", + ); + + // Step 10: update the registries addresses from the PolymathRegistry contract + I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({from: account_polymath}); + let bytesProxy = encodeProxyCall([I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); + await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, {from: account_polymath}); + I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); + // Step 10: Deploy the FeatureRegistry + + I_FeatureRegistry = await FeatureRegistry.new( + I_PolymathRegistry.address, + { + from: account_polymath + }); + await I_PolymathRegistry.changeAddress("FeatureRegistry", I_FeatureRegistry.address, {from: account_polymath}); - // Step 10: update the registries addresses from the PolymathRegistry contract - await I_SecurityTokenRegistry.updateFromRegistry({from: account_polymath}); - await I_ModuleRegistry.updateFromRegistry({from: account_polymath}); - await I_TickerRegistry.updateFromRegistry({from: account_polymath}); + assert.notEqual( + I_FeatureRegistry.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "FeatureRegistry contract was not deployed", + ); + // Step 11: update the registries addresses from the PolymathRegistry contract + await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_STRProxied.address, {from: account_polymath}); + await I_ModuleRegistry.updateFromRegistry({from: account_polymath}); }) describe("Generate the SecurityToken", async () => { it("Should register the ticker before the generation of the security token", async () => { - await I_PolyToken.approve(I_TickerRegistry.address, initRegFee, { from: token_owner }); - let tx = await I_TickerRegistry.registerTicker(token_owner, symbol, contact, swarmHash, { from : token_owner }); + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from : token_owner }); assert.equal(tx.logs[0].args._owner, token_owner); - assert.equal(tx.logs[0].args._symbol, symbol.toUpperCase()); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); }); it("Should generate the new security token with the same symbol as registered above", async () => { - await I_PolyToken.approve(I_SecurityTokenRegistry.address, initRegFee, { from: token_owner }); + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); let _blockNo = latestBlock(); - let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner, gas: 60000000 }); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner, gas: 60000000 }); // Verify the successful generation of the security token assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); @@ -235,12 +235,16 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { // Verify that GeneralTransferManager module get added successfully or not assert.equal(log.args._type.toNumber(), 2); - assert.equal(web3.utils.toUtf8(log.args._name), "GeneralTransferManager"); + assert.equal( + web3.utils.toAscii(log.args._name) + .replace(/\u0000/g, ''), + "GeneralTransferManager" + ); }); it("Should intialize the auto attached modules", async () => { let moduleData = await I_SecurityToken.modules(2, 0); - I_GeneralTransferManager = GeneralTransferManager.at(moduleData[1]); + I_GeneralTransferManager = GeneralTransferManager.at(moduleData); assert.notEqual( I_GeneralTransferManager.address.valueOf(), From 889db0e95a88e1f614bd02d414659b52f8758fc9 Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Thu, 27 Sep 2018 12:53:48 +0530 Subject: [PATCH 10/27] Added multi functions --- .../SingleTradeVolumeRestrictionManager.sol | 402 +++--- ...leTradeVolumeRestrictionManagerFactory.sol | 152 +-- test/u_single_trade_volume_restriction.js | 1123 ++++++++++------- 3 files changed, 957 insertions(+), 720 deletions(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index fdc601e0f..60fb1be0e 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -7,170 +7,240 @@ import "openzeppelin-solidity/contracts/math/SafeMath.sol"; */ contract SingleTradeVolumeRestrictionManager is ITransferManager { - using SafeMath for uint256; - - bool public isTransferLimitInPercentage; - uint256 public globalTransferLimit; - - // should be multipled by 10^16. if the transfer percentage is 20%, then globalTransferLimitInPercentage should be 20*10^16 - uint256 public globalTransferLimitInPercentage; - - mapping(address=>bool) public exemptWallets; - - mapping(address => bool) public specialTransferLimitWallets; - - mapping(address => uint) public specialTransferLimits; - - event LogExemptWalletAdded(address _wallet); - event LogExemptWalletRemoved(address _wallet); - event LogTransferLimitInTokensSet(address _wallet, uint256 _amount); - event LogTransferLimitInPercentageSet(address _wallet, uint _percentage); - event LogTransferLimitRemoved(address _wallet); - event LogGlobalTransferLimitInTokensSet(uint256 _amount); - event LogGlobalTransferLimitInPercentageSet(uint256 _percentage); - - bytes32 constant public ADMIN = "ADMIN"; - - /** - * @notice Constructor - * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken - */ - constructor(address _securityToken, address _polyAddress) public - Module(_securityToken, _polyAddress) - { - - } - - /// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers - function verifyTransfer(address _from, address /* _to */, uint256 _amount, bool /* _isTransfer */) public returns(Result) { - bool validTransfer; - if(paused) { - return Result.NA; - } - - if(exemptWallets[_from]) return Result.NA; - - if(specialTransferLimitWallets[_from]) { - if(isTransferLimitInPercentage) { - validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= specialTransferLimits[_from]; - } else { - validTransfer = _amount <= specialTransferLimits[_from]; - } - } else { - if(isTransferLimitInPercentage) { - validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= globalTransferLimitInPercentage; - } else { - validTransfer = _amount <= globalTransferLimit; - } - } - if(validTransfer) return Result.NA; - return Result.INVALID; - } - - /** - * @notice Used to intialize the variables of the contract - * @param _isTransferLimitInPercentage true if the transfer limit is in percentage else false - * @param _globalTransferLimitInPercentageOrToken transfer limit per single transaction. - */ - function configure(bool _isTransferLimitInPercentage, uint256 _globalTransferLimitInPercentageOrToken) public onlyFactory { - require(_globalTransferLimitInPercentageOrToken > 0, "global transfer limit has to greater than 0"); - isTransferLimitInPercentage = _isTransferLimitInPercentage; - if(isTransferLimitInPercentage) { - globalTransferLimitInPercentage = _globalTransferLimitInPercentageOrToken; - } else { - globalTransferLimit = _globalTransferLimitInPercentageOrToken; - } - } - - /** - * @notice Change the global transfer limit - * @param _newGlobalTransferLimit new transfer limit in tokens - */ - function changeGlobalLimitInTokens(uint256 _newGlobalTransferLimit) public withPerm(ADMIN) { - require(!isTransferLimitInPercentage, "Transfer limit not set in tokens"); - globalTransferLimit = _newGlobalTransferLimit; - emit LogGlobalTransferLimitInTokensSet(_newGlobalTransferLimit); - } - - /** - * @notice Change the global transfer limit - * @param _newGlobalTransferLimitInPercentage new transfer limit in percentage. Multiple the percentage by 10^16. Eg 22% will be 22*10^16 - */ - function changeGlobalLimitInPercentage(uint256 _newGlobalTransferLimitInPercentage) public withPerm(ADMIN) { - require(isTransferLimitInPercentage, "Transfer limit not set in Percentage"); - globalTransferLimitInPercentage = _newGlobalTransferLimitInPercentage; - emit LogGlobalTransferLimitInPercentageSet(_newGlobalTransferLimitInPercentage); - } - - /** - * @notice add an exempt wallet - * @param _wallet exempt wallet address - */ - function addExemptWallet(address _wallet) public withPerm(ADMIN) { - require(_wallet != address(0), "Wallet address cannot be a zero address"); - exemptWallets[_wallet] = true; - emit LogExemptWalletAdded(_wallet); - } - - /** - * @notice remove an exempt wallet - * @param _wallet exempt wallet address - */ - function removeExemptWallet(address _wallet) public withPerm(ADMIN) { - require(_wallet != address(0), "Wallet address cannot be a zero address"); - exemptWallets[_wallet] = false; - emit LogExemptWalletRemoved(_wallet); - } - - /** - * @notice set transfer limit per wallet an exempt wallet - * @param _wallet wallet address - * @param _transferLimit transfer limit for the wallet in tokens - */ - function setTransferLimitForWallet(address _wallet, uint _transferLimit) public withPerm(ADMIN) { - require(!isTransferLimitInPercentage, "Transfer limit not in token amount"); - specialTransferLimitWallets[_wallet] = true; - specialTransferLimits[_wallet] = _transferLimit; - emit LogTransferLimitInTokensSet(_wallet, _transferLimit); - } - - /** - * @notice set transfer limit per wallet an exempt wallet - * @param _wallet wallet address - * @param _transferLimitInPercentage transfer limit for the wallet in percentage. Multiple the percentage by 10^16. Eg 22% will be 22*10^16 - */ - function setTransferLimitInPercentage(address _wallet, uint _transferLimitInPercentage) public withPerm(ADMIN) { - require(isTransferLimitInPercentage, "Transfer limit not in percentage"); - specialTransferLimitWallets[_wallet] = true; - specialTransferLimits[_wallet] = _transferLimitInPercentage; - emit LogTransferLimitInPercentageSet(_wallet, _transferLimitInPercentage); - } - - /** - * @notice removes transfer limit for a wallet - * @param _wallet wallet address - */ - function removeTransferLimitForWallet(address _wallet) public withPerm(ADMIN) { - require(specialTransferLimitWallets[_wallet], "Wallet Address does not have a transfer limit"); - specialTransferLimitWallets[_wallet] = false; - specialTransferLimits[_wallet] = 0; - emit LogTransferLimitRemoved(_wallet); - } - - /** - * @notice This function returns the signature of configure function - */ - function getInitFunction() public pure returns (bytes4) { - return bytes4(keccak256("configure(bool,uint256)")); - } - - /** - * @notice Return the permissions flag that are associated with SingleTradeVolumeRestrictionManager - */ - function getPermissions() public view returns(bytes32[]) { - bytes32[] memory allPermissions = new bytes32[](1); - allPermissions[0] = ADMIN; - return allPermissions; - } + using SafeMath for uint256; + + bool public isTransferLimitInPercentage; + + uint256 public globalTransferLimit; + + // should be multipled by 10^16. if the transfer percentage is 20%, then globalTransferLimitInPercentage should be 20*10^16 + uint256 public globalTransferLimitInPercentage; + + mapping(address=>bool) public exemptWallets; + + mapping(address => bool) public specialTransferLimitWallets; + + mapping(address => uint) public specialTransferLimits; + + event ExemptWalletAdded(address _wallet); + event ExemptWalletRemoved(address _wallet); + event TransferLimitInTokensSet(address _wallet, uint256 _amount); + event TransferLimitInPercentageSet(address _wallet, uint _percentage); + event TransferLimitRemoved(address _wallet); + event GlobalTransferLimitInTokensSet(uint256 _amount, uint256 _oldAmount); + event GlobalTransferLimitInPercentageSet(uint256 _percentage, uint256 _oldPercentage); + + bytes32 constant public ADMIN = "ADMIN"; + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + */ + constructor(address _securityToken, address _polyAddress) public + Module(_securityToken, _polyAddress) + { + + } + + /// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers + function verifyTransfer(address _from, address /* _to */, uint256 _amount, bool /* _isTransfer */) public returns(Result) { + bool validTransfer; + + if(exemptWallets[_from] || paused) return Result.NA; + + if(specialTransferLimitWallets[_from]) { + if (isTransferLimitInPercentage) { + validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= specialTransferLimits[_from]; + } else { + validTransfer = _amount <= specialTransferLimits[_from]; + } + } else { + if (isTransferLimitInPercentage) { + validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= globalTransferLimitInPercentage; + } else { + validTransfer = _amount <= globalTransferLimit; + } + } + if(validTransfer) return Result.NA; + return Result.INVALID; + } + + /** + * @notice Used to intialize the variables of the contract + * @param _isTransferLimitInPercentage true if the transfer limit is in percentage else false + * @param _globalTransferLimitInPercentageOrToken transfer limit per single transaction. + */ + function configure(bool _isTransferLimitInPercentage, uint256 _globalTransferLimitInPercentageOrToken) public onlyFactory { + require(_globalTransferLimitInPercentageOrToken > 0, "global transfer limit has to greater than 0"); + isTransferLimitInPercentage = _isTransferLimitInPercentage; + if (isTransferLimitInPercentage) { + changeGlobalLimitInPercentage(_globalTransferLimitInPercentageOrToken); + } else { + changeGlobalLimitInTokens(_globalTransferLimitInPercentageOrToken); + } + } + + /** + * @notice Change the global transfer limit + * @param _newGlobalTransferLimit new transfer limit in tokens + * @dev This function can be used only when The manager is configured to use limits in tokens + */ + function changeGlobalLimitInTokens(uint256 _newGlobalTransferLimit) public withPerm(ADMIN) { + require(!isTransferLimitInPercentage, "Transfer limit not set in tokens"); + emit GlobalTransferLimitInTokensSet(_newGlobalTransferLimit, globalTransferLimit); + globalTransferLimit = _newGlobalTransferLimit; + + } + + /** + * @notice Change the global transfer limit + * @param _newGlobalTransferLimitInPercentage new transfer limit in percentage. + * Multiply the percentage by 10^16. Eg 22% will be 22*10^16 + * @dev This function can be used only when The manager is configured to use limits in percentage + */ + function changeGlobalLimitInPercentage(uint256 _newGlobalTransferLimitInPercentage) public withPerm(ADMIN) { + require(isTransferLimitInPercentage, "Transfer limit not set in Percentage"); + require(_newGlobalTransferLimitInPercentage <= 100 * 10 ** 16); + emit GlobalTransferLimitInPercentageSet(_newGlobalTransferLimitInPercentage, globalTransferLimitInPercentage); + globalTransferLimitInPercentage = _newGlobalTransferLimitInPercentage; + + } + + /** + * @notice add an exempt wallet + * @param _wallet exempt wallet address + */ + function addExemptWallet(address _wallet) public withPerm(ADMIN) { + require(_wallet != address(0), "Wallet address cannot be a zero address"); + exemptWallets[_wallet] = true; + emit ExemptWalletAdded(_wallet); + } + + /** + * @notice remove an exempt wallet + * @param _wallet exempt wallet address + */ + function removeExemptWallet(address _wallet) public withPerm(ADMIN) { + require(_wallet != address(0), "Wallet address cannot be a zero address"); + exemptWallets[_wallet] = false; + emit ExemptWalletRemoved(_wallet); + } + + /** + * @notice adds an array of exempt wallet + * @param _wallets array of exempt wallet addresses + */ + function addExemptWalletMulti(address[] _wallets) public withPerm(ADMIN) { + require(_wallets.length > 0, "Wallets cannot be empty"); + for (uint256 i = 0; i < _wallets.length; i++) { + addExemptWallet(_wallets[i]); + } + } + + /** + * @notice removes an array of exempt wallet + * @param _wallets array of exempt wallet addresses + */ + function removeExemptWalletMulti(address[] _wallets) public withPerm(ADMIN) { + require(_wallets.length > 0, "Wallets cannot be empty"); + for (uint256 i = 0; i < _wallets.length; i++) { + removeExemptWallet(_wallets[i]); + } + } + + /** + * @notice set transfer limit per wallet + * @param _wallet wallet address + * @param _transferLimit transfer limit for the wallet in tokens + * @dev the manager has to be configured to use limits in tokens + */ + function setTransferLimitForWallet(address _wallet, uint _transferLimit) public withPerm(ADMIN) { + require(!isTransferLimitInPercentage, "Transfer limit not in token amount"); + specialTransferLimitWallets[_wallet] = true; + specialTransferLimits[_wallet] = _transferLimit; + emit TransferLimitInTokensSet(_wallet, _transferLimit); + } + + /** + * @notice set transfer limit for a wallet + * @param _wallet wallet address + * @param _transferLimitInPercentage transfer limit for the wallet in percentage. + * Multiply the percentage by 10^16. Eg 22% will be 22*10^16 + * @dev The manager has to be configured to use percentages + */ + function setTransferLimitInPercentage(address _wallet, uint _transferLimitInPercentage) public withPerm(ADMIN) { + require(isTransferLimitInPercentage, "Transfer limit not in percentage"); + specialTransferLimitWallets[_wallet] = true; + specialTransferLimits[_wallet] = _transferLimitInPercentage; + emit TransferLimitInPercentageSet(_wallet, _transferLimitInPercentage); + } + + + /** + * @notice removes transfer limit for a wallet + * @param _wallet wallet address + */ + function removeTransferLimitForWallet(address _wallet) public withPerm(ADMIN) { + require(specialTransferLimitWallets[_wallet], "Wallet Address does not have a transfer limit"); + specialTransferLimitWallets[_wallet] = false; + specialTransferLimits[_wallet] = 0; + emit TransferLimitRemoved(_wallet); + } + + /** + * @notice sets transfer limits for an array of wallet + * @param _wallets array of wallet addresses + * @param _transferLimits array of transfer limits for each wallet in tokens + * @dev The manager has to be configured to use tokens as limit + */ + function setTransferLimitForWalletMulti(address[] _wallets, uint[] _transferLimits) public withPerm(ADMIN) { + require(_wallets.length > 0, "Wallets cannot be empty"); + require(_wallets.length == _transferLimits.length); + for (uint256 i=0; i < _wallets.length; i++ ) { + setTransferLimitForWallet(_wallets[i], _transferLimits[i]); + } + } + + /** + * @notice sets transfer limits for an array of wallet + * @param _wallets array of wallet addresses + * @param _transferLimitsInPercentage array of transfer limits for each wallet in percentages + * The percentage has to be multipled by 10 ** 16. Eg: 20% would be 20 * 10 ** 16 + * @dev The manager has to be configured to use percentage as limit + */ + function setTransferLimitInPercentageMulti(address[] _wallets, uint[] _transferLimitsInPercentage) public withPerm(ADMIN) { + require(_wallets.length > 0, "Wallets cannot be empty"); + require(_wallets.length == _transferLimitsInPercentage.length); + for (uint256 i=0; i < _wallets.length; i++) { + setTransferLimitInPercentage(_wallets[i], _transferLimitsInPercentage[i]); + } + } + + /** + * @notice removes transfer limits for an array of wallet + * @param _wallets array of wallet addresses + */ + function removeTransferLimitForWalletMulti(address[] _wallets) public withPerm(ADMIN) { + require(_wallets.length > 0, "Wallets cannot be empty"); + for (uint i = 0; i < _wallets.length; i++) { + removeTransferLimitForWallet(_wallets[i]); + } + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() public pure returns (bytes4) { + return bytes4(keccak256("configure(bool,uint256)")); + } + + /** + * @notice Return the permissions flag that are associated with SingleTradeVolumeRestrictionManager + */ + function getPermissions() public view returns(bytes32[]) { + bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = ADMIN; + return allPermissions; + } } diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol index ecb9304f7..a67aa3a31 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol @@ -9,94 +9,94 @@ import "../../libraries/Util.sol"; contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { - /** - * @notice Constructor - * @param _polyAddress Address of the polytoken - * @param _setupCost Setup cost of the module - * @param _usageCost Usage cost of the module - * @param _subscriptionCost Subscription cost of the module - */ - constructor(address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public - ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) - { - version = "1.0.0"; - name = "SingleTradeVolumeRestriction"; - title = "Single Trade Volume Restriction Manager"; - description = "Imposes volume restriction on a single trade"; - } + /** + * @notice Constructor + * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _subscriptionCost Subscription cost of the module + */ + constructor(address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public + ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + { + version = "1.0.0"; + name = "SingleTradeVolumeRestriction"; + title = "Single Trade Volume Restriction Manager"; + description = "Imposes volume restriction on a single trade"; + } - /** - * @notice used to launch the Module with the help of factory - * @return address Contract address of the Module - */ - function deploy(bytes _data) external returns(address) { - if (setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); - SingleTradeVolumeRestrictionManager singleTradeVolumeRestrictionManager = new SingleTradeVolumeRestrictionManager(msg.sender, address(polyToken)); + /** + * @notice used to launch the Module with the help of factory + * @return address Contract address of the Module + */ + function deploy(bytes _data) external returns(address) { + if (setupCost > 0) + require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); + SingleTradeVolumeRestrictionManager singleTradeVolumeRestrictionManager = new SingleTradeVolumeRestrictionManager(msg.sender, address(polyToken)); - require(Util.getSig(_data) == singleTradeVolumeRestrictionManager.getInitFunction(), "Provided data is not valid"); - require(address(singleTradeVolumeRestrictionManager).call(_data), "Un-successfull call"); - emit LogGenerateModuleFromFactory(address(singleTradeVolumeRestrictionManager), getName(), address(this), msg.sender, now); - return address(singleTradeVolumeRestrictionManager); - } + require(Util.getSig(_data) == singleTradeVolumeRestrictionManager.getInitFunction(), "Provided data is not valid"); + require(address(singleTradeVolumeRestrictionManager).call(_data), "Un-successfull call"); + emit LogGenerateModuleFromFactory(address(singleTradeVolumeRestrictionManager), getName(), address(this), msg.sender, now); + return address(singleTradeVolumeRestrictionManager); + } - /** - * @notice Type of the Module factory - */ - function getType() public view returns(uint8) { - return 2; - } + /** + * @notice Type of the Module factory + */ + function getType() public view returns(uint8) { + return 2; + } - /** - * @notice Get the name of the Module - */ - function getName() public view returns(bytes32) { - return name; - } + /** + * @notice Get the name of the Module + */ + function getName() public view returns(bytes32) { + return name; + } - /** - * @notice Get the description of the Module - */ - function getDescription() public view returns(string) { - return description; - } + /** + * @notice Get the description of the Module + */ + function getDescription() public view returns(string) { + return description; + } - /** - * @notice Get the title of the Module - */ - function getTitle() public view returns(string) { - return title; - } + /** + * @notice Get the title of the Module + */ + function getTitle() public view returns(string) { + return title; + } - /** - * @notice Get the Instructions that help to use the module - */ - function getInstructions() public view returns(string) { - return "Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens"; - } + /** + * @notice Get the Instructions that help to use the module + */ + function getInstructions() public view returns(string) { + return "Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens"; + } - /** + /** * @notice Get the version of the Module - */ - function getVersion() external view returns(string) { + */ + function getVersion() external view returns(string) { return version; - } + } - /** + /** * @notice Get the setup cost of the module */ - function getSetupCost() external view returns (uint256) { - return setupCost; - } - /** - * @notice Get the tags related to the module factory - */ - function getTags() public view returns(bytes32[]) { - bytes32[] memory availableTags = new bytes32[](3); - availableTags[0] = "Single Trade"; - availableTags[1] = "Transfer"; - availableTags[2] = "Volume"; - return availableTags; - } + function getSetupCost() external view returns (uint256) { + return setupCost; + } + /** + * @notice Get the tags related to the module factory + */ + function getTags() public view returns(bytes32[]) { + bytes32[] memory availableTags = new bytes32[](3); + availableTags[0] = "Single Trade"; + availableTags[1] = "Transfer"; + availableTags[2] = "Volume"; + return availableTags; + } } diff --git a/test/u_single_trade_volume_restriction.js b/test/u_single_trade_volume_restriction.js index b9a8ccb52..caf375b2c 100644 --- a/test/u_single_trade_volume_restriction.js +++ b/test/u_single_trade_volume_restriction.js @@ -1,7 +1,17 @@ import latestTime from './helpers/latestTime'; -import { duration, ensureException, promisifyLogWatch, latestBlock } from './helpers/utils'; -import takeSnapshot, { increaseTime, revertToSnapshot } from './helpers/time'; -import { encodeProxyCall } from './helpers/encodeCall'; +import { + duration, + ensureException, + promisifyLogWatch, + latestBlock +} from './helpers/utils'; +import takeSnapshot, { + increaseTime, + revertToSnapshot +} from './helpers/time'; +import { + encodeProxyCall +} from './helpers/encodeCall'; const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') const ModuleRegistry = artifacts.require('./ModuleRegistry.sol'); @@ -80,184 +90,238 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { // Initial fee for ticker registry and security token registry const initRegFee = web3.utils.toWei("250"); - before(async() => { - // Accounts setup - account_polymath = accounts[0]; - account_issuer = accounts[1]; + before(async () => { + // Accounts setup + account_polymath = accounts[0]; + account_issuer = accounts[1]; - token_owner = account_issuer; + token_owner = account_issuer; - account_investor1 = accounts[6]; - account_investor2 = accounts[7]; - account_investor3 = accounts[8]; - account_investor4 = accounts[9]; - account_investor5 = accounts[5]; + account_investor1 = accounts[6]; + account_investor2 = accounts[7]; + account_investor3 = accounts[8]; + account_investor4 = accounts[9]; + account_investor5 = accounts[5]; - // ----------- POLYMATH NETWORK Configuration ------------ + // ----------- POLYMATH NETWORK Configuration ------------ - // Step 0: Deploy the PolymathRegistry - I_PolymathRegistry = await PolymathRegistry.new({from: account_polymath}); - - // Step 1: Deploy the token Faucet and Mint tokens for token_owner - I_PolyToken = await PolyTokenFaucet.new(); - await I_PolyToken.getTokens((10000 * Math.pow(10, 18)), token_owner); - await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, {from: account_polymath}) + // Step 0: Deploy the PolymathRegistry + I_PolymathRegistry = await PolymathRegistry.new({ + from: account_polymath + }); - // STEP 2: Deploy the ModuleRegistry + // Step 1: Deploy the token Faucet and Mint tokens for token_owner + I_PolyToken = await PolyTokenFaucet.new(); + await I_PolyToken.getTokens((10000 * Math.pow(10, 18)), token_owner); + await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, { + from: account_polymath + }) - I_ModuleRegistry = await ModuleRegistry.new(I_PolymathRegistry.address, {from:account_polymath}); - await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistry.address, {from: account_polymath}); + // STEP 2: Deploy the ModuleRegistry - assert.notEqual( - I_ModuleRegistry.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "ModuleRegistry contract was not deployed" - ); + I_ModuleRegistry = await ModuleRegistry.new(I_PolymathRegistry.address, { + from: account_polymath + }); + await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistry.address, { + from: account_polymath + }); - // STEP 2: Deploy the GeneralTransferManagerFactory + assert.notEqual( + I_ModuleRegistry.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "ModuleRegistry contract was not deployed" + ); - I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); + // STEP 2: Deploy the GeneralTransferManagerFactory - assert.notEqual( - I_GeneralTransferManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "GeneralTransferManagerFactory contract was not deployed" - ); + I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, { + from: account_polymath + }); - // STEP 3: Deploy the GeneralDelegateManagerFactoryFactory + assert.notEqual( + I_GeneralTransferManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralTransferManagerFactory contract was not deployed" + ); - I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from:account_polymath}); + // STEP 3: Deploy the GeneralDelegateManagerFactoryFactory - assert.notEqual( - I_GeneralPermissionManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "GeneralDelegateManagerFactory contract was not deployed" - ); + I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(I_PolyToken.address, 0, 0, 0, { + from: account_polymath + }); - // STEP 4: Deploy the SingleTradeVolumeRestrictionManagerFactory - I_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, 0, 0, 0, {from: account_polymath}); + assert.notEqual( + I_GeneralPermissionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralDelegateManagerFactory contract was not deployed" + ); - assert.notEqual( - I_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" - ); + // STEP 4: Deploy the SingleTradeVolumeRestrictionManagerFactory + I_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, 0, 0, 0, { + from: account_polymath + }); + assert.notEqual( + I_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" + ); - P_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, web3.utils.toWei("500", "ether"), 0, 0, {from:account_polymath}); - assert.notEqual( - P_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" - ); - // STEP 5: Register the Modules with the ModuleRegistry contract + P_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, web3.utils.toWei("500", "ether"), 0, 0, { + from: account_polymath + }); + assert.notEqual( + P_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" + ); - // (A) : Register the GeneralTransferManagerFactory - await I_ModuleRegistry.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_ModuleRegistry.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); + // STEP 5: Register the Modules with the ModuleRegistry contract - // (B) : Register the GeneralDelegateManagerFactory - await I_ModuleRegistry.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_ModuleRegistry.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); + // (A) : Register the GeneralTransferManagerFactory + await I_ModuleRegistry.registerModule(I_GeneralTransferManagerFactory.address, { + from: account_polymath + }); + await I_ModuleRegistry.verifyModule(I_GeneralTransferManagerFactory.address, true, { + from: account_polymath + }); - // (C) : Register SingleTradeVolumeRestrictionManagerFactory - await I_ModuleRegistry.registerModule(I_SingleTradeVolumeRestrictionManagerFactory.address, { from: account_polymath }); - await I_ModuleRegistry.verifyModule(I_SingleTradeVolumeRestrictionManagerFactory.address, true, { from: account_polymath }); + // (B) : Register the GeneralDelegateManagerFactory + await I_ModuleRegistry.registerModule(I_GeneralPermissionManagerFactory.address, { + from: account_polymath + }); + await I_ModuleRegistry.verifyModule(I_GeneralPermissionManagerFactory.address, true, { + from: account_polymath + }); - await I_ModuleRegistry.registerModule(P_SingleTradeVolumeRestrictionManagerFactory.address, { from: account_polymath }); - await I_ModuleRegistry.verifyModule(P_SingleTradeVolumeRestrictionManagerFactory.address, true, { from: account_polymath }); + // (C) : Register SingleTradeVolumeRestrictionManagerFactory + await I_ModuleRegistry.registerModule(I_SingleTradeVolumeRestrictionManagerFactory.address, { + from: account_polymath + }); + await I_ModuleRegistry.verifyModule(I_SingleTradeVolumeRestrictionManagerFactory.address, true, { + from: account_polymath + }); - // Step 7: Deploy the STFactory contract + await I_ModuleRegistry.registerModule(P_SingleTradeVolumeRestrictionManagerFactory.address, { + from: account_polymath + }); + await I_ModuleRegistry.verifyModule(P_SingleTradeVolumeRestrictionManagerFactory.address, true, { + from: account_polymath + }); - I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address); + // Step 7: Deploy the STFactory contract - assert.notEqual( - I_STFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "STFactory contract was not deployed", - ); + I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address); - // Step 9: Deploy the SecurityTokenRegistry + assert.notEqual( + I_STFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "STFactory contract was not deployed", + ); - I_SecurityTokenRegistry = await SecurityTokenRegistry.new({from: account_polymath }); + // Step 9: Deploy the SecurityTokenRegistry - assert.notEqual( - I_SecurityTokenRegistry.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "SecurityTokenRegistry contract was not deployed", - ); + I_SecurityTokenRegistry = await SecurityTokenRegistry.new({ + from: account_polymath + }); - // Step 10: update the registries addresses from the PolymathRegistry contract - I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({from: account_polymath}); - let bytesProxy = encodeProxyCall([I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); - await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, {from: account_polymath}); - I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); - // Step 10: Deploy the FeatureRegistry + assert.notEqual( + I_SecurityTokenRegistry.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SecurityTokenRegistry contract was not deployed", + ); - I_FeatureRegistry = await FeatureRegistry.new( - I_PolymathRegistry.address, - { - from: account_polymath - }); - await I_PolymathRegistry.changeAddress("FeatureRegistry", I_FeatureRegistry.address, {from: account_polymath}); + // Step 10: update the registries addresses from the PolymathRegistry contract + I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({ + from: account_polymath + }); + let bytesProxy = encodeProxyCall([I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); + await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { + from: account_polymath + }); + I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); + // Step 10: Deploy the FeatureRegistry + + I_FeatureRegistry = await FeatureRegistry.new( + I_PolymathRegistry.address, { + from: account_polymath + }); + await I_PolymathRegistry.changeAddress("FeatureRegistry", I_FeatureRegistry.address, { + from: account_polymath + }); - assert.notEqual( - I_FeatureRegistry.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "FeatureRegistry contract was not deployed", - ); + assert.notEqual( + I_FeatureRegistry.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "FeatureRegistry contract was not deployed", + ); - // Step 11: update the registries addresses from the PolymathRegistry contract - await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_STRProxied.address, {from: account_polymath}); - await I_ModuleRegistry.updateFromRegistry({from: account_polymath}); + // Step 11: update the registries addresses from the PolymathRegistry contract + await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_STRProxied.address, { + from: account_polymath + }); + await I_ModuleRegistry.updateFromRegistry({ + from: account_polymath + }); }) describe("Generate the SecurityToken", async () => { - it("Should register the ticker before the generation of the security token", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from : token_owner }); - assert.equal(tx.logs[0].args._owner, token_owner); - assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); - }); - - it("Should generate the new security token with the same symbol as registered above", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); - let _blockNo = latestBlock(); - let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner, gas: 60000000 }); - - // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - - const log = await promisifyLogWatch(I_SecurityToken.LogModuleAdded({from: _blockNo}), 1); - - // Verify that GeneralTransferManager module get added successfully or not - assert.equal(log.args._type.toNumber(), 2); - assert.equal( - web3.utils.toAscii(log.args._name) - .replace(/\u0000/g, ''), - "GeneralTransferManager" - ); - }); - - it("Should intialize the auto attached modules", async () => { - let moduleData = await I_SecurityToken.modules(2, 0); - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); - - assert.notEqual( - I_GeneralTransferManager.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "GeneralTransferManager contract was not deployed", - ); - - }); + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { + from: token_owner + }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { + from: token_owner + }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { + from: token_owner + }); + let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { + from: token_owner, + gas: 60000000 + }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + + const log = await promisifyLogWatch(I_SecurityToken.LogModuleAdded({ + from: _blockNo + }), 1); + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._type.toNumber(), 2); + assert.equal( + web3.utils.toAscii(log.args._name) + .replace(/\u0000/g, ''), + "GeneralTransferManager" + ); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = await I_SecurityToken.modules(2, 0); + I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + + assert.notEqual( + I_GeneralTransferManager.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralTransferManager contract was not deployed", + ); + + }); }); -// - describe("Buy tokens using whitelist & manual approvals", async() => { + // + describe("Buy tokens using whitelist & manual approvals", async () => { - it("Should Buy the tokens", async() => { + it("Should Buy the tokens", async () => { // Add the Investor in to the whitelist let tx = await I_GeneralTransferManager.modifyWhitelist( @@ -265,8 +329,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { latestTime(), latestTime(), latestTime() + duration.days(10), - true, - { + true, { from: account_issuer, gas: 6000000 }); @@ -277,7 +340,9 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { await increaseTime(5000); // Mint some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei('100', 'ether'), { from: token_owner }); + await I_SecurityToken.mint(account_investor1, web3.utils.toWei('100', 'ether'), { + from: token_owner + }); assert.equal( (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), @@ -285,7 +350,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { ); }); - it("Should Buy some more tokens", async() => { + it("Should Buy some more tokens", async () => { // Add the Investor in to the whitelist let tx = await I_GeneralTransferManager.modifyWhitelist( @@ -293,8 +358,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { latestTime(), latestTime(), latestTime() + duration.days(10), - true, - { + true, { from: account_issuer, gas: 6000000 }); @@ -302,358 +366,461 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor2.toLowerCase(), "Failed in adding the investor in whitelist"); // Mint some tokens - await I_SecurityToken.mint(account_investor2, web3.utils.toWei('1', 'ether'), { from: token_owner }); + await I_SecurityToken.mint(account_investor2, web3.utils.toWei('1', 'ether'), { + from: token_owner + }); assert.equal( (await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei('1', 'ether') ); }); -// - it("Fails to attach the SingleTradeVolumeRestrictionManager with the security token due to fees not paid", async () => { - let managerArgs = web3.eth.abi.encodeFunctionCall({ - name: 'configure', - type: 'function', - inputs: [{ - type: 'bool', - name: '_isTransferLimitInPercentage' - }, - { - type: 'uint256', - name: '_globalTransferLimitInPercentageOrToken' + // + it("Fails to attach the SingleTradeVolumeRestrictionManager with the security token due to fees not paid", async () => { + let managerArgs = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'bool', + name: '_isTransferLimitInPercentage' + }, + { + type: 'uint256', + name: '_globalTransferLimitInPercentageOrToken' + } + ] + }, [true, 90]) + let errorThrown = false; + await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); + try { + const tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { + from: token_owner + }); + } catch (error) { + console.log(` tx -> failed because Token is not paid`.grey); + ensureException(error); + errorThrown = true; } - ] - }, [true, 90]) - let errorThrown = false; - await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); - try { - const tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { from: token_owner }); - } catch(error) { - console.log(` tx -> failed because Token is not paid`.grey); - ensureException(error); - errorThrown = true; - } - assert.ok(errorThrown, message); - }); - - - - it("Should successfully attach the Paid SingleTradeVolumeRestrictionManager with the security token", async () => { - let managerArgs = web3.eth.abi.encodeFunctionCall({ - name: 'configure', - type: 'function', - inputs: [{ - type: 'bool', - name: '_isTransferLimitInPercentage' - }, - { - type: 'uint256', - name: '_globalTransferLimitInPercentageOrToken' - } - ] - }, [true, 90]); - await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), {from: token_owner}); - let tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { from: token_owner }); - assert.equal(tx.logs[3].args._type.toNumber(), transferManagerKey, "SingleTradeVolumeRestrictionManager did not get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[3].args._name) - .replace(/\u0000/g, ''), - "SingleTradeVolumeRestriction", - "SingleTradeVolumeRestrictionManagerFactory module was not added" - ); - P_SingleTradeVolumeRestrictionManager = SingleTradeVolumeRestrictionManager.at(tx.logs[3].args._module); - }); - - it("Should successfully attach the SingleTradeVolumeRestrictionManager with the security token", async () => { - let managerArgs = web3.eth.abi.encodeFunctionCall({ - name: 'configure', - type: 'function', - inputs: [{ - type: 'bool', - name: '_isTransferLimitInPercentage' - }, - { - type: 'uint256', - name: '_globalTransferLimitInPercentageOrToken' + assert.ok(errorThrown, message); + }); + + + + it("Should successfully attach the Paid SingleTradeVolumeRestrictionManager with the security token", async () => { + let managerArgs = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'bool', + name: '_isTransferLimitInPercentage' + }, + { + type: 'uint256', + name: '_globalTransferLimitInPercentageOrToken' + } + ] + }, [true, 90]); + await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), { + from: token_owner + }); + let tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { + from: token_owner + }); + assert.equal(tx.logs[3].args._type.toNumber(), transferManagerKey, "SingleTradeVolumeRestrictionManager did not get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[3].args._name) + .replace(/\u0000/g, ''), + "SingleTradeVolumeRestriction", + "SingleTradeVolumeRestrictionManagerFactory module was not added" + ); + P_SingleTradeVolumeRestrictionManager = SingleTradeVolumeRestrictionManager.at(tx.logs[3].args._module); + }); + + it("Should successfully attach the SingleTradeVolumeRestrictionManager with the security token", async () => { + let managerArgs = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'bool', + name: '_isTransferLimitInPercentage' + }, + { + type: 'uint256', + name: '_globalTransferLimitInPercentageOrToken' + } + ] + }, [false, 7 * 10 ** 16]) + const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { + from: token_owner + }); + assert.equal(tx.logs[2].args._type.toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name) + .replace(/\u0000/g, ''), + "SingleTradeVolumeRestriction", + "SingleTradeVolumeRestriction module was not added" + ); + I_SingleTradeVolumeRestrictionManager = SingleTradeVolumeRestrictionManager.at(tx.logs[2].args._module); + }); + + it("Should successfully attach the SingleTradeVolumeRestrictionManager (Percentage) with the security token", async () => { + let managerArgs = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'bool', + name: '_isTransferLimitInPercentage' + }, + { + type: 'uint256', + name: '_globalTransferLimitInPercentageOrToken' + } + ] + }, [true, 90]); + const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { + from: token_owner + }); + assert.equal(tx.logs[2].args._type.toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name) + .replace(/\u0000/g, ''), + "SingleTradeVolumeRestriction", + "SingleTradeVolumeRestriction module was not added" + ); + I_SingleTradeVolumeRestrictionPercentageManager = SingleTradeVolumeRestrictionManager.at(tx.logs[2].args._module); + }); + + it('should return get permissions', async () => { + let permissions = await I_SingleTradeVolumeRestrictionPercentageManager.getPermissions(); + assert.equal(permissions.length, 1, "Invalid Permissions"); + assert.equal( + web3.utils.toAscii(permissions[0]).replace(/\u0000/g, ''), + "ADMIN", + 'Wrong permissions' + ); + }); + + it('add exempt wallet', async () => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5]); + } catch (e) { + errorThrown = true; } - ] - }, [false, 7 * 10 ** 16]) - const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { from: token_owner }); - assert.equal(tx.logs[2].args._type.toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[2].args._name) - .replace(/\u0000/g, ''), - "SingleTradeVolumeRestriction", - "SingleTradeVolumeRestriction module was not added" - ); - I_SingleTradeVolumeRestrictionManager = SingleTradeVolumeRestrictionManager.at(tx.logs[2].args._module); - }); - - it("Should successfully attach the SingleTradeVolumeRestrictionManager (Percentage) with the security token", async () => { - let managerArgs = web3.eth.abi.encodeFunctionCall({ - name: 'configure', - type: 'function', - inputs: [{ - type: 'bool', - name: '_isTransferLimitInPercentage' - }, - { - type: 'uint256', - name: '_globalTransferLimitInPercentageOrToken' + assert.ok(errorThrown, "Non Admins cannot add exempt wallets"); + + + let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5], { + from: token_owner + }); + assert.equal(tx.logs[0].args._wallet, accounts[5], "Wrong wallet added as exempt"); + }); + + it('should remove an exempt wallet', async () => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5]); + } catch (e) { + errorThrown = true; } - ] - }, [true, 90]); - const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { from: token_owner }); - assert.equal(tx.logs[2].args._type.toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[2].args._name) - .replace(/\u0000/g, ''), - "SingleTradeVolumeRestriction", - "SingleTradeVolumeRestriction module was not added" - ); - I_SingleTradeVolumeRestrictionPercentageManager = SingleTradeVolumeRestrictionManager.at(tx.logs[2].args._module); - }); - - it('should return get permissions', async () => { - let permissions = await I_SingleTradeVolumeRestrictionPercentageManager.getPermissions(); - assert.equal(permissions.length, 1, "Invalid Permissions"); - assert.equal( - web3.utils.toAscii(permissions[0]).replace(/\u0000/g, ''), - "ADMIN", - 'Wrong permissions' - ); - }); - - it('add exempt wallet', async() => { - let errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5]); - } catch(e) { - errorThrown = true; - } - assert.ok(errorThrown, "Non Admins cannot add exempt wallets"); - - - let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5], { from: token_owner }); - assert.equal(tx.logs[0].args._wallet, accounts[5], "Wrong wallet added as exempt"); - }); - - it('should remove an exempt wallet', async () => { - let errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5]); - } catch(e) { - errorThrown = true; - } - assert.ok(errorThrown, "Non Admins cannot add exempt wallets"); - - - let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5], { from: token_owner }); - assert.equal(tx.logs[0].args._wallet, accounts[5], "Wrong wallet removed from exempt"); - }); - - it('should set transfer limit for a wallet', async () => { - let errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(accounts[4], 100); - } catch(e) { - errorThrown = true; - } - assert.ok(errorThrown, "Non Admins cannot set transfer limits"); - - let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(accounts[4], 100, { from:token_owner }); - assert.equal(tx.logs[0].args._wallet, accounts[4]); - assert.equal(tx.logs[0].args._amount, 100); - - tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 50, { from: token_owner }); - assert.equal(tx.logs[0].args._wallet, accounts[4], "Wrong wallet added to transfer limits"); - assert.equal(tx.logs[0].args._percentage, 50, "Wrong percentage set"); - }); - - it('should remove transfer limit for wallet', async() => { - let errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitForWallet(accounts[4]); - } catch(e) { - errorThrown = true; - } - assert.ok(errorThrown, "Non Admins cannot set/remove transfer limits"); - let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitForWallet(accounts[4], {from: token_owner }); - assert.equal(tx.logs[0].args._wallet,accounts[4], "Wrong wallet removed"); - }); - - it("Should pause the tranfers at Manager level", async() => { - let tx = await I_SingleTradeVolumeRestrictionManager.pause({from: token_owner}); - }); - - it('should be able to set a global transfer limit', async () => { - let errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(100*10**18); - } catch(e) { - errorThrown = true; - } - assert.ok(errorThrown, true); - - - let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(10, {from: token_owner}); - assert.equal(tx.logs[0].args._amount, 10, "Global Limit not set"); - - errorThrown = false; - - try { - let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89); - } catch(e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, true, "Global limit can be set by non-admins"); - tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(40, {from:token_owner}); - assert.equal(tx.logs[0].args._percentage, 40, "Global Limit not set"); - }); - - it('should be able to transfer tokens SingleTradeVolumeRestriction', async () => { - await I_SingleTradeVolumeRestrictionManager.unpause({from: token_owner }) - await I_SingleTradeVolumeRestrictionPercentageManager.pause({from: token_owner }) - await P_SingleTradeVolumeRestrictionManager.pause({from: token_owner }); - - await I_GeneralTransferManager.modifyWhitelist( - account_investor3, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, - { - from: account_issuer, - gas: 6000000 - } - ); + assert.ok(errorThrown, "Non Admins cannot add exempt wallets"); - await I_GeneralTransferManager.modifyWhitelist( - account_investor4, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, - { - from: account_issuer, - gas: 6000000 - } - ); - await I_GeneralTransferManager.modifyWhitelist( - account_investor5, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, - { - from: account_issuer, - gas: 6000000 - } - ); + let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5], { + from: token_owner + }); + assert.equal(tx.logs[0].args._wallet, accounts[5], "Wrong wallet removed from exempt"); + }); - //setting a max of 5 tokens - await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(web3.utils.toWei('5', 'ether'), {from: token_owner}) - let errorThrown = false; - try { - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('6', 'ether'), {from: account_investor1 }); - } catch(e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, true, "Transfer should have not happened"); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('4', 'ether'), {from: account_investor1}); - assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei('4', 'ether')); - - // exempt wallet - await I_SingleTradeVolumeRestrictionManager.addExemptWallet(account_investor1, { from: token_owner }); - await I_SecurityToken.transfer(account_investor5, web3.utils.toWei('7', 'ether'), {from: account_investor1}); - assert.equal((await I_SecurityToken.balanceOf(account_investor5)).toNumber(), web3.utils.toWei('7', 'ether')); - - //special limits wallet - await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(account_investor5, web3.utils.toWei('5', 'ether'), {from: token_owner}); - errorThrown = false; - try { - await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('7', 'ether'), {from: account_investor5}); - } catch(e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, true, "Transfer should have not happened"); - await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('4', 'ether'), {from: account_investor5 }) - assert.equal((await I_SecurityToken.balanceOf(account_investor4)).toNumber(), web3.utils.toWei('4', 'ether')) - }) - - it('should be able to transfer tokens (percentage transfer limit)', async () => { - await I_SingleTradeVolumeRestrictionManager.pause({from: token_owner }); - let balance = (await I_SecurityToken.balanceOf(account_investor2)).toNumber(); - await I_SecurityToken.transfer(account_investor1, balance, {from: account_investor2 }); - - - balance = (await I_SecurityToken.balanceOf(account_investor3)).toNumber(); - - await I_SecurityToken.transfer(account_investor1, balance, {from: account_investor3 }); - - - balance = (await I_SecurityToken.balanceOf(account_investor4)).toNumber(); - await I_SecurityToken.transfer(account_investor1, balance, {from: account_investor4 }); - - balance = (await I_SecurityToken.balanceOf(account_investor5)).toNumber(); - await I_SecurityToken.transfer(account_investor1, balance, {from: account_investor5 }); - - await I_SingleTradeVolumeRestrictionPercentageManager.unpause({from: token_owner }); - // // - await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(49 * 10 ** 16, {from: token_owner}); - - let errorThrown = false; - try { - // more than the limit - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('90', 'ether'), {from:account_investor1}); - } catch(e) { - ensureException(e); - errorThrown = true; - } - assert.ok(errorThrown, true, "Transfer above limit happened"); - - - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('20', 'ether'), {from: account_investor1}); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei('20', 'ether')) - - await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(account_investor1, 5 * 10 ** 16, {from : token_owner}); - errorThrown = false; - try { - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('35', 'ether'), {from: account_investor1}); - } catch(e) { - ensureException(e); - errorThrown = true; - } - assert.ok(errorThrown, true, "transfer happened above limit"); - - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('1', 'ether'), {from: account_investor1}); - assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei('1', 'ether')); - }); + it('should set transfer limit for a wallet', async () => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(accounts[4], 100); + } catch (e) { + errorThrown = true; + } + assert.ok(errorThrown, "Non Admins cannot set transfer limits"); + + let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(accounts[4], 100, { + from: token_owner + }); + assert.equal(tx.logs[0].args._wallet, accounts[4]); + assert.equal(tx.logs[0].args._amount, 100); + + tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 50, { + from: token_owner + }); + assert.equal(tx.logs[0].args._wallet, accounts[4], "Wrong wallet added to transfer limits"); + assert.equal(tx.logs[0].args._percentage, 50, "Wrong percentage set"); + }); + + it('should remove transfer limit for wallet', async () => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitForWallet(accounts[4]); + } catch (e) { + errorThrown = true; + } + assert.ok(errorThrown, "Non Admins cannot set/remove transfer limits"); + let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitForWallet(accounts[4], { + from: token_owner + }); + assert.equal(tx.logs[0].args._wallet, accounts[4], "Wrong wallet removed"); + }); + + it("Should pause the tranfers at Manager level", async () => { + let tx = await I_SingleTradeVolumeRestrictionManager.pause({ + from: token_owner + }); + }); + + it('should be able to set a global transfer limit', async () => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(100 * 10 ** 18); + } catch (e) { + errorThrown = true; + } + assert.ok(errorThrown, true); + + + let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(10, { + from: token_owner + }); + assert.equal(tx.logs[0].args._amount, 10, "Global Limit not set"); + + errorThrown = false; + + try { + let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, true, "Global limit can be set by non-admins"); + tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(40, { + from: token_owner + }); + assert.equal(tx.logs[0].args._percentage, 40, "Global Limit not set"); + + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(101 * 10 ** 16, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, true, "Global limit cannot be set to more than 100"); + + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInPercentage(10, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, true, "Global limit in percentage cannot be set when limit is in tokens"); + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(10, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, true, "Global limit in tokens cannot be set when limit is in percentage"); + }); + + it('should be able to transfer tokens SingleTradeVolumeRestriction', async () => { + await I_SingleTradeVolumeRestrictionManager.unpause({ + from: token_owner + }) + await I_SingleTradeVolumeRestrictionPercentageManager.pause({ + from: token_owner + }) + await P_SingleTradeVolumeRestrictionManager.pause({ + from: token_owner + }); + + await I_GeneralTransferManager.modifyWhitelist( + account_investor3, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, { + from: account_issuer, + gas: 6000000 + } + ); + + await I_GeneralTransferManager.modifyWhitelist( + account_investor4, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, { + from: account_issuer, + gas: 6000000 + } + ); + + await I_GeneralTransferManager.modifyWhitelist( + account_investor5, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, { + from: account_issuer, + gas: 6000000 + } + ); + + //setting a max of 5 tokens + await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(web3.utils.toWei('5', 'ether'), { + from: token_owner + }) + let errorThrown = false; + try { + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('6', 'ether'), { + from: account_investor1 + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, true, "Transfer should have not happened"); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('4', 'ether'), { + from: account_investor1 + }); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei('4', 'ether')); + + // exempt wallet + await I_SingleTradeVolumeRestrictionManager.addExemptWallet(account_investor1, { + from: token_owner + }); + await I_SecurityToken.transfer(account_investor5, web3.utils.toWei('7', 'ether'), { + from: account_investor1 + }); + assert.equal((await I_SecurityToken.balanceOf(account_investor5)).toNumber(), web3.utils.toWei('7', 'ether')); + + //special limits wallet + await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(account_investor5, web3.utils.toWei('5', 'ether'), { + from: token_owner + }); + errorThrown = false; + try { + await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('7', 'ether'), { + from: account_investor5 + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, true, "Transfer should have not happened"); + await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('4', 'ether'), { + from: account_investor5 + }) + assert.equal((await I_SecurityToken.balanceOf(account_investor4)).toNumber(), web3.utils.toWei('4', 'ether')) + }) + + it('should be able to transfer tokens (percentage transfer limit)', async () => { + await I_SingleTradeVolumeRestrictionManager.pause({ + from: token_owner + }); + let balance = (await I_SecurityToken.balanceOf(account_investor2)).toNumber(); + await I_SecurityToken.transfer(account_investor1, balance, { + from: account_investor2 + }); + + + balance = (await I_SecurityToken.balanceOf(account_investor3)).toNumber(); + + await I_SecurityToken.transfer(account_investor1, balance, { + from: account_investor3 + }); + + + balance = (await I_SecurityToken.balanceOf(account_investor4)).toNumber(); + await I_SecurityToken.transfer(account_investor1, balance, { + from: account_investor4 + }); + + balance = (await I_SecurityToken.balanceOf(account_investor5)).toNumber(); + await I_SecurityToken.transfer(account_investor1, balance, { + from: account_investor5 + }); + + await I_SingleTradeVolumeRestrictionPercentageManager.unpause({ + from: token_owner + }); + // // + await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(49 * 10 ** 16, { + from: token_owner + }); + + let errorThrown = false; + try { + // more than the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('90', 'ether'), { + from: account_investor1 + }); + } catch (e) { + ensureException(e); + errorThrown = true; + } + assert.ok(errorThrown, true, "Transfer above limit happened"); + + + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('20', 'ether'), { + from: account_investor1 + }); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei('20', 'ether')) + + await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(account_investor1, 5 * 10 ** 16, { + from: token_owner + }); + errorThrown = false; + try { + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('35', 'ether'), { + from: account_investor1 + }); + } catch (e) { + ensureException(e); + errorThrown = true; + } + assert.ok(errorThrown, true, "transfer happened above limit"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('1', 'ether'), { + from: account_investor1 + }); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei('1', 'ether')); + }); }); - describe("SingleTradeVolumeRestrictionManager Factory test cases", async() => { + describe("SingleTradeVolumeRestrictionManager Factory test cases", async () => { - it("Should get the exact details of the factory", async() => { - assert.equal(await I_SingleTradeVolumeRestrictionManagerFactory.setupCost.call(),0); - assert.equal(await I_SingleTradeVolumeRestrictionManagerFactory.getType.call(),2); + it("Should get the exact details of the factory", async () => { + assert.equal(await I_SingleTradeVolumeRestrictionManagerFactory.setupCost.call(), 0); + assert.equal(await I_SingleTradeVolumeRestrictionManagerFactory.getType.call(), 2); let name = web3.utils.toUtf8(await I_SingleTradeVolumeRestrictionManagerFactory.getName.call()); - assert.equal(name,"SingleTradeVolumeRestriction","Wrong Module added"); + assert.equal(name, "SingleTradeVolumeRestriction", "Wrong Module added"); let desc = await I_SingleTradeVolumeRestrictionManagerFactory.getDescription.call(); - assert.equal(desc,"Imposes volume restriction on a single trade","Wrong Module added"); + assert.equal(desc, "Imposes volume restriction on a single trade", "Wrong Module added"); let title = await I_SingleTradeVolumeRestrictionManagerFactory.getTitle.call(); - assert.equal(title,"Single Trade Volume Restriction Manager","Wrong Module added"); + assert.equal(title, "Single Trade Volume Restriction Manager", "Wrong Module added"); let inst = await I_SingleTradeVolumeRestrictionManagerFactory.getInstructions.call(); - assert.equal(inst,"Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens","Wrong Module added"); + assert.equal(inst, "Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens", "Wrong Module added"); }); - it("Should get the tags of the factory", async() => { + it("Should get the tags of the factory", async () => { let tags = await I_SingleTradeVolumeRestrictionManagerFactory.getTags.call(); assert.equal(web3.utils.toUtf8(tags[0]), "Single Trade"); assert.equal(web3.utils.toUtf8(tags[1]), "Transfer"); From f399728ed87f098a0bc4c33c371a4532cb07aa5b Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Thu, 27 Sep 2018 13:22:13 +0530 Subject: [PATCH 11/27] added comments for singletradevolumeRestrictionFactory --- .../SingleTradeVolumeRestrictionManager.sol | 2 +- .../SingleTradeVolumeRestrictionManagerFactory.sol | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index 60fb1be0e..e3f4fc5c5 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -43,7 +43,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { } - /// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers + /// @notice Used to verify the transfer transaction according to the rule implemented in the transfer manager function verifyTransfer(address _from, address /* _to */, uint256 _amount, bool /* _isTransfer */) public returns(Result) { bool validTransfer; diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol index a67aa3a31..95640b3e9 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol @@ -42,6 +42,7 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { /** * @notice Type of the Module factory + * @return uint8 */ function getType() public view returns(uint8) { return 2; @@ -49,6 +50,7 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { /** * @notice Get the name of the Module + * @return bytes32 */ function getName() public view returns(bytes32) { return name; @@ -56,6 +58,7 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { /** * @notice Get the description of the Module + * @return string */ function getDescription() public view returns(string) { return description; @@ -63,6 +66,7 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { /** * @notice Get the title of the Module + * @return string */ function getTitle() public view returns(string) { return title; @@ -70,6 +74,7 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { /** * @notice Get the Instructions that help to use the module + * @return string */ function getInstructions() public view returns(string) { return "Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens"; @@ -77,6 +82,7 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { /** * @notice Get the version of the Module + * @return string */ function getVersion() external view returns(string) { return version; @@ -84,12 +90,14 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { /** * @notice Get the setup cost of the module + * return uint256 */ function getSetupCost() external view returns (uint256) { return setupCost; } /** * @notice Get the tags related to the module factory + * @return bytes32[] */ function getTags() public view returns(bytes32[]) { bytes32[] memory availableTags = new bytes32[](3); From 74fe6ed7631d2f7a0ce29be88969cb0a527314b8 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 27 Sep 2018 12:14:36 +0100 Subject: [PATCH 12/27] Add token info script --- scripts/tokenInfo.js | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 scripts/tokenInfo.js diff --git a/scripts/tokenInfo.js b/scripts/tokenInfo.js new file mode 100644 index 000000000..2df8c57c4 --- /dev/null +++ b/scripts/tokenInfo.js @@ -0,0 +1,68 @@ +const Web3 = require("web3"); +const fs = require("fs"); +const async = require("async"); +const path = require("path"); +const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/")); + +const securityTokenRegistryABI = JSON.parse(require('fs').readFileSync('../build/contracts/SecurityTokenRegistry.json').toString()).abi; +const securityTokenABI = JSON.parse(require('fs').readFileSync('../build/contracts/SecurityToken.json').toString()).abi; +const generalTransferManagerABI = JSON.parse(require('fs').readFileSync('../build/contracts/GeneralTransferManager.json').toString()).abi; +const securityTokenRegistryAddress = "0xEf58491224958d978fACF55D2120c55A24516B98"; +const securityTokenRegistry = new web3.eth.Contract(securityTokenRegistryABI, securityTokenRegistryAddress); + +async function getTokens() { + let strEvents = await web3.eth.getPastLogs({fromBlock:'0x5C5C18', address:securityTokenRegistry.address, topics: ["0x2510d802a0818e71139a7680a6388bcffcd3fa686e02a0f7319488c5bdb38fcb"]}); + for (let i = 0; i < strEvents.length; i++) { + let tokenAddress = '0x' + strEvents[i].topics[1].slice(26,66) + await getInfo(tokenAddress); + } +} + +async function getInfo(tokenAddress) { + let token = new web3.eth.Contract(securityTokenABI, tokenAddress); + console.log("Token - " + tokenAddress); + console.log("----------------------"); + console.log("Owner: " + await token.methods.owner().call()); + console.log("Name: " + await token.methods.name().call()); + console.log("Symbol: " + await token.methods.symbol().call()); + console.log("Total Supply: " + await token.methods.totalSupply().call()); + console.log("Frozen: " + await token.methods.freeze().call()); + console.log("Investors: " + await token.methods.investorCount().call()); + console.log("Latest Checkpoint: " + await token.methods.currentCheckpointId().call()); + console.log("Finished Issuer Minting: " + await token.methods.finishedIssuerMinting().call()); + console.log("Finished STO Minting: " + await token.methods.finishedSTOMinting().call()); + let gtmRes = await token.methods.modules(2, 0).call(); + let gtmEvents = await web3.eth.getPastLogs({fromBlock:'0x5C5C18', address:gtmRes.moduleAddress}); + console.log("Count of GeneralTransferManager Events: " + gtmEvents.length); + console.log("Modules Attached (TransferManager):"); + await getModules(2, token); + console.log("Modules Attached (PermissionManager):"); + await getModules(1, token); + console.log("Modules Attached (STO):"); + await getModules(3, token); + console.log("Modules Attached (Checkpoint):"); + await getModules(4, token); + console.log("") + console.log(); + console.log(); +} + +async function getModules(type, token) { + let index = 0; + while (true) { + try { + let res = await token.methods.modules(type, index).call(); + console.log(" Name: " + web3.utils.toAscii(res.name)); + console.log(" Address: " + res.moduleAddress); + } catch (err) { + // console.log(err); + if (index == 0) { + console.log(" None"); + } + return; + } + index = index + 1; + } +} + +getTokens(); From a233e46b9694f62a0952302a787c2b1ce068a275 Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Thu, 27 Sep 2018 19:49:20 +0530 Subject: [PATCH 13/27] Added more tests --- .../SingleTradeVolumeRestrictionManager.sol | 11 ++-- test/u_single_trade_volume_restriction.js | 55 ++++++++++++++++++- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index e3f4fc5c5..04d545d6d 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -18,8 +18,6 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { mapping(address=>bool) public exemptWallets; - mapping(address => bool) public specialTransferLimitWallets; - mapping(address => uint) public specialTransferLimits; event ExemptWalletAdded(address _wallet); @@ -49,7 +47,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { if(exemptWallets[_from] || paused) return Result.NA; - if(specialTransferLimitWallets[_from]) { + if(specialTransferLimits[_from] > 0) { if (isTransferLimitInPercentage) { validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= specialTransferLimits[_from]; } else { @@ -156,8 +154,8 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { * @dev the manager has to be configured to use limits in tokens */ function setTransferLimitForWallet(address _wallet, uint _transferLimit) public withPerm(ADMIN) { + require(_transferLimit > 0, "Transfer limit has to be greater than 0"); require(!isTransferLimitInPercentage, "Transfer limit not in token amount"); - specialTransferLimitWallets[_wallet] = true; specialTransferLimits[_wallet] = _transferLimit; emit TransferLimitInTokensSet(_wallet, _transferLimit); } @@ -171,7 +169,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { */ function setTransferLimitInPercentage(address _wallet, uint _transferLimitInPercentage) public withPerm(ADMIN) { require(isTransferLimitInPercentage, "Transfer limit not in percentage"); - specialTransferLimitWallets[_wallet] = true; + require(_transferLimitInPercentage > 0 && _transferLimitInPercentage <= 100 * 10 ** 16, "Transfer limit not in required range"); specialTransferLimits[_wallet] = _transferLimitInPercentage; emit TransferLimitInPercentageSet(_wallet, _transferLimitInPercentage); } @@ -182,8 +180,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { * @param _wallet wallet address */ function removeTransferLimitForWallet(address _wallet) public withPerm(ADMIN) { - require(specialTransferLimitWallets[_wallet], "Wallet Address does not have a transfer limit"); - specialTransferLimitWallets[_wallet] = false; + require(specialTransferLimits[_wallet] > 0 , "Wallet Address does not have a transfer limit"); specialTransferLimits[_wallet] = 0; emit TransferLimitRemoved(_wallet); } diff --git a/test/u_single_trade_volume_restriction.js b/test/u_single_trade_volume_restriction.js index caf375b2c..5c53e8ef7 100644 --- a/test/u_single_trade_volume_restriction.js +++ b/test/u_single_trade_volume_restriction.js @@ -419,7 +419,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { name: '_globalTransferLimitInPercentageOrToken' } ] - }, [true, 90]); + }, [false, 90]); await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), { from: token_owner }); @@ -636,6 +636,59 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { assert.ok(errorThrown, true, "Global limit in tokens cannot be set when limit is in percentage"); }); + it('should perform batch updates', async () => { + let wallets = [accounts[0], accounts[1], accounts[2]]; + let tokenLimits = [1, 2, 3]; + let percentageLimits = [5, 6, 7]; + + // add exempt wallet multi + let tx = await P_SingleTradeVolumeRestrictionManager.addExemptWalletMulti(wallets, { + from: token_owner + }); + let logs = tx.logs.filter(log => log.event === 'ExemptWalletAdded'); + assert.equal(logs.length, wallets.length, "Batch Exempt wallets not added"); + for (let i = 0; i < logs.length; i++) { + assert.equal(logs[i].args._wallet, wallets[i], "Wallet not added as exempt wallet"); + } + + // remove exempt wallet multi + tx = await P_SingleTradeVolumeRestrictionManager.removeExemptWalletMulti(wallets, { + from: token_owner + }) + logs = tx.logs.filter(log => log.event === 'ExemptWalletRemoved'); + assert.equal(logs.length, wallets.length, "Batch Exempt wallets not removed"); + + for (let i = 0; i < logs.length; i++) { + assert.equal(logs[i].args._wallet, wallets[i], "Wallet not added as exempt wallet"); + } + + tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitForWalletMulti(wallets, tokenLimits, { + from: token_owner + }); + logs = tx.logs.filter(log => log.event == 'TransferLimitInTokensSet'); + assert.equal(wallets.length, logs.length, "Transfer limit not set"); + for (let i = 0; i < wallets.length; i++) { + assert.equal(logs[i].args._wallet, wallets[i], "transfer limit not set for wallet"); + assert.equal(logs[i].args._amount.toNumber(), tokenLimits[i]); + } + + tx = await P_SingleTradeVolumeRestrictionManager.removeTransferLimitForWalletMulti(wallets, { from:token_owner }); + logs = tx.logs.filter(log => log.event === 'TransferLimitRemoved'); + assert.equal(logs.length, wallets.length, "Transfer limit not removed"); + for (let i = 0; i < wallets.length; i++) { + assert.equal(logs[i].args._wallet, wallets[i], "transfer limit not removed for wallet"); + } + + tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti(wallets, percentageLimits, {from:token_owner}); + logs = tx.logs.filter(log => log.event == 'TransferLimitInPercentageSet'); + assert.equal(logs.length, wallets.length, "transfer limits not set for wallets"); + + for (let i = 0; i< wallets.length; i++) { + assert.equal(logs[i].args._wallet, wallets[i], "Transfer limit not set for wallet"); + assert.equal(logs[i].args._percentage.toNumber(), percentageLimits[i]); + } + }) + it('should be able to transfer tokens SingleTradeVolumeRestriction', async () => { await I_SingleTradeVolumeRestrictionManager.unpause({ from: token_owner From 2f2b19d1ae4d5e4d945232e94cb83c1bb308c946 Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Thu, 27 Sep 2018 21:10:50 +0530 Subject: [PATCH 14/27] More tests --- .../SingleTradeVolumeRestrictionManager.sol | 26 +++- test/u_single_trade_volume_restriction.js | 135 ++++++++++++++++-- 2 files changed, 148 insertions(+), 13 deletions(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index 04d545d6d..0ada911a8 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -27,7 +27,8 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { event TransferLimitRemoved(address _wallet); event GlobalTransferLimitInTokensSet(uint256 _amount, uint256 _oldAmount); event GlobalTransferLimitInPercentageSet(uint256 _percentage, uint256 _oldPercentage); - + event TransferLimitChangedToTokens(); + event TransferLimitChangedtoPercentage(); bytes32 constant public ADMIN = "ADMIN"; /** @@ -79,6 +80,29 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { } } + /** + * @notice Changes the manager to use transfer limit as Percentages + * @param _newGlobalTransferLimitInPercentage uint256 new global Transfer Limit In Percentage. + * @dev specialTransferLimits set for wallets have to re-configured + */ + function changeTransferLimitToPercentage(uint256 _newGlobalTransferLimitInPercentage) public withPerm(ADMIN) { + require(!isTransferLimitInPercentage, "Transfer limit already in percentage"); + isTransferLimitInPercentage = true; + changeGlobalLimitInPercentage(_newGlobalTransferLimitInPercentage); + emit TransferLimitChangedtoPercentage(); + } + + /** + * @notice Changes the manager to use transfer limit as tokens + * @param _newGlobalTransferLimit uint256 new global Transfer Limit in tokens. + * @dev specialTransferLimits set for wallets have to re-configured + */ + function changeTransferLimitToTokens(uint _newGlobalTransferLimit) public withPerm(ADMIN) { + require(isTransferLimitInPercentage, "Transfer limit already in tokens"); + isTransferLimitInPercentage = false; + changeGlobalLimitInTokens(_newGlobalTransferLimit); + emit TransferLimitChangedToTokens(); + } /** * @notice Change the global transfer limit * @param _newGlobalTransferLimit new transfer limit in tokens diff --git a/test/u_single_trade_volume_restriction.js b/test/u_single_trade_volume_restriction.js index 5c53e8ef7..8db910dac 100644 --- a/test/u_single_trade_volume_restriction.js +++ b/test/u_single_trade_volume_restriction.js @@ -46,6 +46,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { let account_investor3; let account_investor4; let account_investor5; + let zero_address = '0x0000000000000000000000000000000000000000'; // investor Details let fromTime = latestTime(); @@ -509,6 +510,17 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { } assert.ok(errorThrown, "Non Admins cannot add exempt wallets"); + errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(zero_address, { + from: token_owner + }); + } catch (e) { + ensureException(e); + errorThrown = true; + } + + assert.ok(errorThrown, "Exempt wallet cannot be zero"); let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5], { from: token_owner @@ -525,6 +537,17 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { } assert.ok(errorThrown, "Non Admins cannot add exempt wallets"); + errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(zero_address, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + + assert.ok(errorThrown, "Zero address cannot be added to exempt wallet"); let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5], { from: token_owner @@ -581,7 +604,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { } catch (e) { errorThrown = true; } - assert.ok(errorThrown, true); + assert.ok(errorThrown); let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(10, { @@ -597,7 +620,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = true; ensureException(e); } - assert.ok(errorThrown, true, "Global limit can be set by non-admins"); + assert.ok(errorThrown, "Global limit can be set by non-admins"); tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(40, { from: token_owner }); @@ -612,7 +635,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = true; ensureException(e); } - assert.ok(errorThrown, true, "Global limit cannot be set to more than 100"); + assert.ok(errorThrown, "Global limit cannot be set to more than 100"); errorThrown = false; try { @@ -623,7 +646,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = true; ensureException(e); } - assert.ok(errorThrown, true, "Global limit in percentage cannot be set when limit is in tokens"); + assert.ok(errorThrown, "Global limit in percentage cannot be set when limit is in tokens"); errorThrown = false; try { await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(10, { @@ -633,7 +656,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = true; ensureException(e); } - assert.ok(errorThrown, true, "Global limit in tokens cannot be set when limit is in percentage"); + assert.ok(errorThrown, "Global limit in tokens cannot be set when limit is in percentage"); }); it('should perform batch updates', async () => { @@ -641,6 +664,17 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { let tokenLimits = [1, 2, 3]; let percentageLimits = [5, 6, 7]; + let errorThrown = false; + try { + await P_SingleTradeVolumeRestrictionManager.addExemptWalletMulti([], { + from: token_owner + }); + } catch(e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Exempt wallet multi cannot be empty wallet"); + // add exempt wallet multi let tx = await P_SingleTradeVolumeRestrictionManager.addExemptWalletMulti(wallets, { from: token_owner @@ -651,6 +685,17 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { assert.equal(logs[i].args._wallet, wallets[i], "Wallet not added as exempt wallet"); } + errorThrown = false; + try { + await P_SingleTradeVolumeRestrictionManager.removeExemptWalletMulti([], { + from: token_owner + }); + } catch(e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Exempt wallet multi cannot be empty wallet"); + // remove exempt wallet multi tx = await P_SingleTradeVolumeRestrictionManager.removeExemptWalletMulti(wallets, { from: token_owner @@ -662,6 +707,28 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { assert.equal(logs[i].args._wallet, wallets[i], "Wallet not added as exempt wallet"); } + errorThrown = false; + try { + tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitForWalletMulti([], tokenLimits, { + from: token_owner + }); + } catch(e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "wallets cannot be empty"); + + errorThrown = false; + try { + tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitForWalletMulti([], tokenLimits, { + from: token_owner + }); + } catch(e) { + errorThrown = true; + ensureException(e); + } + + tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitForWalletMulti(wallets, tokenLimits, { from: token_owner }); @@ -672,18 +739,22 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { assert.equal(logs[i].args._amount.toNumber(), tokenLimits[i]); } - tx = await P_SingleTradeVolumeRestrictionManager.removeTransferLimitForWalletMulti(wallets, { from:token_owner }); + tx = await P_SingleTradeVolumeRestrictionManager.removeTransferLimitForWalletMulti(wallets, { + from: token_owner + }); logs = tx.logs.filter(log => log.event === 'TransferLimitRemoved'); assert.equal(logs.length, wallets.length, "Transfer limit not removed"); for (let i = 0; i < wallets.length; i++) { assert.equal(logs[i].args._wallet, wallets[i], "transfer limit not removed for wallet"); } - tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti(wallets, percentageLimits, {from:token_owner}); + tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti(wallets, percentageLimits, { + from: token_owner + }); logs = tx.logs.filter(log => log.event == 'TransferLimitInPercentageSet'); assert.equal(logs.length, wallets.length, "transfer limits not set for wallets"); - for (let i = 0; i< wallets.length; i++) { + for (let i = 0; i < wallets.length; i++) { assert.equal(logs[i].args._wallet, wallets[i], "Transfer limit not set for wallet"); assert.equal(logs[i].args._percentage.toNumber(), percentageLimits[i]); } @@ -746,7 +817,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = true; ensureException(e); } - assert.ok(errorThrown, true, "Transfer should have not happened"); + assert.ok(errorThrown,"Transfer should have not happened"); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('4', 'ether'), { from: account_investor1 }); @@ -774,7 +845,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = true; ensureException(e); } - assert.ok(errorThrown, true, "Transfer should have not happened"); + assert.ok(errorThrown,"Transfer should have not happened"); await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('4', 'ether'), { from: account_investor5 }) @@ -826,7 +897,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { ensureException(e); errorThrown = true; } - assert.ok(errorThrown, true, "Transfer above limit happened"); + assert.ok(errorThrown, "Transfer above limit happened"); await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('20', 'ether'), { @@ -846,7 +917,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { ensureException(e); errorThrown = true; } - assert.ok(errorThrown, true, "transfer happened above limit"); + assert.ok(errorThrown, "transfer happened above limit"); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('1', 'ether'), { from: account_investor1 @@ -854,6 +925,46 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei('1', 'ether')); }); + it('should change transfer limits to tokens', async () => { + let errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.changeTransferLimitToPercentage(1, { + from: token_owner + }); + } catch (e) { + + ensureException(e); + errorThrown = true; + } + assert.equal(errorThrown, true, "Should not change to percentage again"); + + + let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeTransferLimitToTokens(1, { + from: token_owner + }); + assert.equal(await I_SingleTradeVolumeRestrictionPercentageManager.isTransferLimitInPercentage(), false, "Error Changing"); + assert.equal(tx.logs[0].args._amount.toNumber(), 1, "Transfer limit not changed"); + }) + + it('should change transfer limits to percentage', async () => { + let errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionManager.changeTransferLimitToTokens(1, { + from: token_owner + }); + } catch (e) { + ensureException(e); + errorThrown = true; + } + assert.equal(errorThrown, true, "Should not change to tokens again"); + + let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeTransferLimitToPercentage(1, { + from: token_owner + }); + assert.ok(await I_SingleTradeVolumeRestrictionPercentageManager.isTransferLimitInPercentage(), "Error Changing"); + assert.equal(tx.logs[0].args._percentage.toNumber(), 1, "Transfer limit not changed"); + }) + }); From 6738b0094854022f817c16e0165de85bdc928e2a Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Thu, 27 Sep 2018 22:48:22 +0530 Subject: [PATCH 15/27] Changes from the review and more tests --- .../SingleTradeVolumeRestrictionManager.sol | 3 +- test/u_single_trade_volume_restriction.js | 58 +++++++++++++++++-- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index 0ada911a8..3e2705065 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -110,6 +110,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { */ function changeGlobalLimitInTokens(uint256 _newGlobalTransferLimit) public withPerm(ADMIN) { require(!isTransferLimitInPercentage, "Transfer limit not set in tokens"); + require(_newGlobalTransferLimit > 0, "Transfer limit has to greater than zero"); emit GlobalTransferLimitInTokensSet(_newGlobalTransferLimit, globalTransferLimit); globalTransferLimit = _newGlobalTransferLimit; @@ -123,7 +124,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { */ function changeGlobalLimitInPercentage(uint256 _newGlobalTransferLimitInPercentage) public withPerm(ADMIN) { require(isTransferLimitInPercentage, "Transfer limit not set in Percentage"); - require(_newGlobalTransferLimitInPercentage <= 100 * 10 ** 16); + require(_newGlobalTransferLimitInPercentage > 0 && _newGlobalTransferLimitInPercentage <= 100 * 10 ** 16); emit GlobalTransferLimitInPercentageSet(_newGlobalTransferLimitInPercentage, globalTransferLimitInPercentage); globalTransferLimitInPercentage = _newGlobalTransferLimitInPercentage; diff --git a/test/u_single_trade_volume_restriction.js b/test/u_single_trade_volume_restriction.js index 8db910dac..e6f7692b3 100644 --- a/test/u_single_trade_volume_restriction.js +++ b/test/u_single_trade_volume_restriction.js @@ -604,8 +604,17 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { } catch (e) { errorThrown = true; } - assert.ok(errorThrown); + assert.ok(errorThrown, "only owner is allowed"); + errorThrown = false; + + try { + let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInPercentage(100 * 10 ** 18, {from: token_owner}); + } catch(e) { + ensureException(e); + errorThrown = true; + } + assert.ok(errorThrown, "Cannot change global limit in percentage when set to tokens"); let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(10, { from: token_owner @@ -621,6 +630,17 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { ensureException(e); } assert.ok(errorThrown, "Global limit can be set by non-admins"); + + errorThrown = false; + + try { + let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89, {from: token_owner}); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "cannot change global limit in tokens if transfer limit is set to percentage"); + tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(40, { from: token_owner }); @@ -720,14 +740,14 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = false; try { - tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitForWalletMulti([], tokenLimits, { + tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitForWalletMulti([accounts[0]], tokenLimits, { from: token_owner }); } catch(e) { errorThrown = true; ensureException(e); } - + assert.ok(errorThrown, "wallet array length dont match"); tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitForWalletMulti(wallets, tokenLimits, { from: token_owner @@ -738,7 +758,16 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { assert.equal(logs[i].args._wallet, wallets[i], "transfer limit not set for wallet"); assert.equal(logs[i].args._amount.toNumber(), tokenLimits[i]); } - + errorThrown = false + try { + await P_SingleTradeVolumeRestrictionManager.removeTransferLimitForWalletMulti([], { + from: token_owner + }); + } catch(e) { + ensureException(e); + errorThrown = true; + } + assert.ok(errorThrown, "Wallets cannot be empty"); tx = await P_SingleTradeVolumeRestrictionManager.removeTransferLimitForWalletMulti(wallets, { from: token_owner }); @@ -748,6 +777,27 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { assert.equal(logs[i].args._wallet, wallets[i], "transfer limit not removed for wallet"); } + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti([], percentageLimits, { + from: token_owner + }); + } catch(e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "wallets cannot be empty"); + + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti(wallets, [], { + from: token_owner + }); + } catch(e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "wallets and amounts dont match be empty"); tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti(wallets, percentageLimits, { from: token_owner }); From 8aa25605ef6e67af6e1a1fd8333e8a011ffb0d5b Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Thu, 27 Sep 2018 23:58:19 +0530 Subject: [PATCH 16/27] More tests --- .../SingleTradeVolumeRestrictionManager.sol | 1 - test/u_single_trade_volume_restriction.js | 68 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index 3e2705065..e02906ce9 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -71,7 +71,6 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { * @param _globalTransferLimitInPercentageOrToken transfer limit per single transaction. */ function configure(bool _isTransferLimitInPercentage, uint256 _globalTransferLimitInPercentageOrToken) public onlyFactory { - require(_globalTransferLimitInPercentageOrToken > 0, "global transfer limit has to greater than 0"); isTransferLimitInPercentage = _isTransferLimitInPercentage; if (isTransferLimitInPercentage) { changeGlobalLimitInPercentage(_globalTransferLimitInPercentageOrToken); diff --git a/test/u_single_trade_volume_restriction.js b/test/u_single_trade_volume_restriction.js index e6f7692b3..163941a72 100644 --- a/test/u_single_trade_volume_restriction.js +++ b/test/u_single_trade_volume_restriction.js @@ -564,12 +564,62 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { } assert.ok(errorThrown, "Non Admins cannot set transfer limits"); + errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(accounts[4], 0, { + from: token_owner + }); + } catch(e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Transfer limit cannot be set to 0") + errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInPercentage(accounts[4], 10, { + from: token_owner + }); + } catch(e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Transfer limit cannot be set in percentage") let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(accounts[4], 100, { from: token_owner }); assert.equal(tx.logs[0].args._wallet, accounts[4]); assert.equal(tx.logs[0].args._amount, 100); + errorThrown = false; + try { + tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 0, { + from: token_owner + }); + } catch(e) { + errorThrown = true; + ensureException(e); + } + errorThrown = false + try { + tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 101 * 10 ** 16, { + from: token_owner + }); + } catch(e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Transfer limit can not be set to more 0") + errorThrown = false; + try { + tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitForWallet(accounts[4], 1, { + from: token_owner + }); + } catch(e) { + errorThrown = true; + ensureException(e); + } + + assert.ok(errorThrown, "Transfer limit in tokens can not be set for a manager that has transfer limit set as percentage") tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 50, { from: token_owner }); @@ -585,6 +635,15 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = true; } assert.ok(errorThrown, "Non Admins cannot set/remove transfer limits"); + + errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitForWallet(accounts[0], {from: token_owner}); + } catch (e) { + errorThrown = true; + } + assert.ok(errorThrown, "Non Admins cannot set/remove transfer limits"); + let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitForWallet(accounts[4], { from: token_owner }); @@ -641,6 +700,15 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { } assert.ok(errorThrown, "cannot change global limit in tokens if transfer limit is set to percentage"); + errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(0, {from: token_owner}); + } catch(e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Cannot set global limit in tokens to 0"); + tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(40, { from: token_owner }); From 47c82fd15e74988dc129969eeeb7ea22fbde260f Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Fri, 28 Sep 2018 00:49:12 +0530 Subject: [PATCH 17/27] More tests --- test/u_single_trade_volume_restriction.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/u_single_trade_volume_restriction.js b/test/u_single_trade_volume_restriction.js index 163941a72..e0e93cdb9 100644 --- a/test/u_single_trade_volume_restriction.js +++ b/test/u_single_trade_volume_restriction.js @@ -675,6 +675,14 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { } assert.ok(errorThrown, "Cannot change global limit in percentage when set to tokens"); + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(0, {from:token_owner}); + } catch(e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Global limit cannot be set to 0"); let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(10, { from: token_owner }); From d823228ac1c70370766ada8cf84972da4cdb7a54 Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Fri, 28 Sep 2018 11:53:58 +0530 Subject: [PATCH 18/27] Changes after the merge --- ...leTradeVolumeRestrictionManagerFactory.sol | 2 +- test/u_single_trade_volume_restriction.js | 145 ++++++++++-------- 2 files changed, 81 insertions(+), 66 deletions(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol index 95640b3e9..f481c1840 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol @@ -36,7 +36,7 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { require(Util.getSig(_data) == singleTradeVolumeRestrictionManager.getInitFunction(), "Provided data is not valid"); require(address(singleTradeVolumeRestrictionManager).call(_data), "Un-successfull call"); - emit LogGenerateModuleFromFactory(address(singleTradeVolumeRestrictionManager), getName(), address(this), msg.sender, now); + emit GenerateModuleFromFactory(address(singleTradeVolumeRestrictionManager), getName(), address(this), msg.sender, setupCost, now); return address(singleTradeVolumeRestrictionManager); } diff --git a/test/u_single_trade_volume_restriction.js b/test/u_single_trade_volume_restriction.js index e0e93cdb9..03968e9b5 100644 --- a/test/u_single_trade_volume_restriction.js +++ b/test/u_single_trade_volume_restriction.js @@ -15,6 +15,7 @@ import { const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') const ModuleRegistry = artifacts.require('./ModuleRegistry.sol'); +const ModuleRegistryProxy = artifacts.require('./ModuleRegistryProxy.sol'); const SecurityToken = artifacts.require('./SecurityToken.sol'); const SecurityTokenRegistry = artifacts.require('./SecurityTokenRegistry.sol'); const SecurityTokenRegistryProxy = artifacts.require('./SecurityTokenRegistryProxy.sol'); @@ -67,6 +68,8 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { let P_SingleTradeVolumeRestrictionManager; let I_SingleTradeVolumeRestrictionPercentageManager; let I_ModuleRegistry; + let I_MRProxied; + let I_ModuleRegistryProxy; let I_FeatureRegistry; let I_SecurityTokenRegistry; let I_STRProxied; @@ -75,6 +78,9 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { let I_PolyToken; let I_PolymathRegistry; + const STRProxyParameters = ['address', 'address', 'uint256', 'uint256', 'address', 'address']; + const MRProxyParameters = ['address', 'address']; + // SecurityToken Details const swarmHash = "dagwrgwgvwergwrvwrg"; const name = "Team"; @@ -114,24 +120,27 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { // Step 1: Deploy the token Faucet and Mint tokens for token_owner I_PolyToken = await PolyTokenFaucet.new(); await I_PolyToken.getTokens((10000 * Math.pow(10, 18)), token_owner); - await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, { - from: account_polymath - }) + + I_FeatureRegistry = await FeatureRegistry.new( + I_PolymathRegistry.address, { + from: account_polymath + }); // STEP 2: Deploy the ModuleRegistry - I_ModuleRegistry = await ModuleRegistry.new(I_PolymathRegistry.address, { + I_ModuleRegistry = await ModuleRegistry.new({ + from: account_polymath + }); + + I_ModuleRegistryProxy = await ModuleRegistryProxy.new({ from: account_polymath }); - await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistry.address, { + let bytesMRProxy = encodeProxyCall(MRProxyParameters, [I_PolymathRegistry.address, account_polymath]); + await I_ModuleRegistryProxy.upgradeToAndCall("1.0.0", I_ModuleRegistry.address, bytesMRProxy, { from: account_polymath }); + I_MRProxied = await ModuleRegistry.at(I_ModuleRegistryProxy.address); - assert.notEqual( - I_ModuleRegistry.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "ModuleRegistry contract was not deployed" - ); // STEP 2: Deploy the GeneralTransferManagerFactory @@ -178,42 +187,42 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" ); - // STEP 5: Register the Modules with the ModuleRegistry contract - - // (A) : Register the GeneralTransferManagerFactory - await I_ModuleRegistry.registerModule(I_GeneralTransferManagerFactory.address, { + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { from: account_polymath }); - await I_ModuleRegistry.verifyModule(I_GeneralTransferManagerFactory.address, true, { + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); // (B) : Register the GeneralDelegateManagerFactory - await I_ModuleRegistry.registerModule(I_GeneralPermissionManagerFactory.address, { + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { from: account_polymath }); - await I_ModuleRegistry.verifyModule(I_GeneralPermissionManagerFactory.address, true, { + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { from: account_polymath }); - // (C) : Register SingleTradeVolumeRestrictionManagerFactory - await I_ModuleRegistry.registerModule(I_SingleTradeVolumeRestrictionManagerFactory.address, { + // (C) : Register the SingleTradeVolumeRestrictionManagerFactory + await I_MRProxied.registerModule(I_SingleTradeVolumeRestrictionManagerFactory.address, { from: account_polymath }); - await I_ModuleRegistry.verifyModule(I_SingleTradeVolumeRestrictionManagerFactory.address, true, { + await I_MRProxied.verifyModule(I_SingleTradeVolumeRestrictionManagerFactory.address, true, { from: account_polymath }); - await I_ModuleRegistry.registerModule(P_SingleTradeVolumeRestrictionManagerFactory.address, { + // (C) : Register the Paid SingleTradeVolumeRestrictionManagerFactory + await I_MRProxied.registerModule(P_SingleTradeVolumeRestrictionManagerFactory.address, { from: account_polymath }); - await I_ModuleRegistry.verifyModule(P_SingleTradeVolumeRestrictionManagerFactory.address, true, { + await I_MRProxied.verifyModule(P_SingleTradeVolumeRestrictionManagerFactory.address, true, { from: account_polymath }); - // Step 7: Deploy the STFactory contract + // Step 6: Deploy the STFactory contract - I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address); + I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, { + from: account_polymath + }); assert.notEqual( I_STFactory.address.valueOf(), @@ -221,7 +230,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { "STFactory contract was not deployed", ); - // Step 9: Deploy the SecurityTokenRegistry + // Step 7: Deploy the SecurityTokenRegistry contract I_SecurityTokenRegistry = await SecurityTokenRegistry.new({ from: account_polymath @@ -233,36 +242,30 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { "SecurityTokenRegistry contract was not deployed", ); - // Step 10: update the registries addresses from the PolymathRegistry contract + // Step 8: Deploy the proxy and attach the implementation contract to it. I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({ from: account_polymath }); - let bytesProxy = encodeProxyCall([I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); + let bytesProxy = encodeProxyCall(STRProxyParameters, [I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { from: account_polymath }); I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); - // Step 10: Deploy the FeatureRegistry - I_FeatureRegistry = await FeatureRegistry.new( - I_PolymathRegistry.address, { - from: account_polymath - }); + // Step 9: update the registries addresses from the PolymathRegistry contract + await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, { + from: account_polymath + }) + await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistryProxy.address, { + from: account_polymath + }); await I_PolymathRegistry.changeAddress("FeatureRegistry", I_FeatureRegistry.address, { from: account_polymath }); - - assert.notEqual( - I_FeatureRegistry.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "FeatureRegistry contract was not deployed", - ); - - // Step 11: update the registries addresses from the PolymathRegistry contract - await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_STRProxied.address, { + await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, { from: account_polymath }); - await I_ModuleRegistry.updateFromRegistry({ + await I_MRProxied.updateFromRegistry({ from: account_polymath }); }) @@ -294,7 +297,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - const log = await promisifyLogWatch(I_SecurityToken.LogModuleAdded({ + const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ from: _blockNo }), 1); @@ -569,7 +572,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(accounts[4], 0, { from: token_owner }); - } catch(e) { + } catch (e) { errorThrown = true; ensureException(e); } @@ -579,7 +582,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInPercentage(accounts[4], 10, { from: token_owner }); - } catch(e) { + } catch (e) { errorThrown = true; ensureException(e); } @@ -595,7 +598,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 0, { from: token_owner }); - } catch(e) { + } catch (e) { errorThrown = true; ensureException(e); } @@ -604,7 +607,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 101 * 10 ** 16, { from: token_owner }); - } catch(e) { + } catch (e) { errorThrown = true; ensureException(e); } @@ -614,7 +617,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitForWallet(accounts[4], 1, { from: token_owner }); - } catch(e) { + } catch (e) { errorThrown = true; ensureException(e); } @@ -638,7 +641,9 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = false; try { - let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitForWallet(accounts[0], {from: token_owner}); + let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitForWallet(accounts[0], { + from: token_owner + }); } catch (e) { errorThrown = true; } @@ -668,8 +673,10 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = false; try { - let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInPercentage(100 * 10 ** 18, {from: token_owner}); - } catch(e) { + let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInPercentage(100 * 10 ** 18, { + from: token_owner + }); + } catch (e) { ensureException(e); errorThrown = true; } @@ -677,8 +684,10 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = false; try { - await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(0, {from:token_owner}); - } catch(e) { + await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(0, { + from: token_owner + }); + } catch (e) { errorThrown = true; ensureException(e); } @@ -701,7 +710,9 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = false; try { - let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89, {from: token_owner}); + let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89, { + from: token_owner + }); } catch (e) { errorThrown = true; ensureException(e); @@ -710,8 +721,10 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = false; try { - let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(0, {from: token_owner}); - } catch(e) { + let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(0, { + from: token_owner + }); + } catch (e) { errorThrown = true; ensureException(e); } @@ -765,7 +778,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { await P_SingleTradeVolumeRestrictionManager.addExemptWalletMulti([], { from: token_owner }); - } catch(e) { + } catch (e) { errorThrown = true; ensureException(e); } @@ -786,7 +799,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { await P_SingleTradeVolumeRestrictionManager.removeExemptWalletMulti([], { from: token_owner }); - } catch(e) { + } catch (e) { errorThrown = true; ensureException(e); } @@ -808,7 +821,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitForWalletMulti([], tokenLimits, { from: token_owner }); - } catch(e) { + } catch (e) { errorThrown = true; ensureException(e); } @@ -819,7 +832,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitForWalletMulti([accounts[0]], tokenLimits, { from: token_owner }); - } catch(e) { + } catch (e) { errorThrown = true; ensureException(e); } @@ -839,7 +852,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { await P_SingleTradeVolumeRestrictionManager.removeTransferLimitForWalletMulti([], { from: token_owner }); - } catch(e) { + } catch (e) { ensureException(e); errorThrown = true; } @@ -858,7 +871,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti([], percentageLimits, { from: token_owner }); - } catch(e) { + } catch (e) { errorThrown = true; ensureException(e); } @@ -869,7 +882,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti(wallets, [], { from: token_owner }); - } catch(e) { + } catch (e) { errorThrown = true; ensureException(e); } @@ -943,7 +956,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = true; ensureException(e); } - assert.ok(errorThrown,"Transfer should have not happened"); + assert.ok(errorThrown, "Transfer should have not happened"); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('4', 'ether'), { from: account_investor1 }); @@ -971,7 +984,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = true; ensureException(e); } - assert.ok(errorThrown,"Transfer should have not happened"); + assert.ok(errorThrown, "Transfer should have not happened"); await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('4', 'ether'), { from: account_investor5 }) @@ -1108,6 +1121,8 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { assert.equal(title, "Single Trade Volume Restriction Manager", "Wrong Module added"); let inst = await I_SingleTradeVolumeRestrictionManagerFactory.getInstructions.call(); assert.equal(inst, "Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens", "Wrong Module added"); + let version = await I_SingleTradeVolumeRestrictionManagerFactory.getVersion.call(); + assert.equal(version, "1.0.0", "Version not correct"); }); it("Should get the tags of the factory", async () => { From 7a56664609aea946d02042726834b3254c5dd47e Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Mon, 1 Oct 2018 18:48:47 +0530 Subject: [PATCH 19/27] Changes from the review --- .../SingleTradeVolumeRestrictionManager.sol | 80 ++++++++++++------- test/u_single_trade_volume_restriction.js | 30 +++---- 2 files changed, 68 insertions(+), 42 deletions(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index e02906ce9..60ad07050 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -11,20 +11,23 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { bool public isTransferLimitInPercentage; - uint256 public globalTransferLimit; + uint256 public globalTransferLimitInTokens; // should be multipled by 10^16. if the transfer percentage is 20%, then globalTransferLimitInPercentage should be 20*10^16 uint256 public globalTransferLimitInPercentage; mapping(address=>bool) public exemptWallets; - mapping(address => uint) public specialTransferLimits; + mapping(address => uint) public specialTransferLimitsInTokens; + + mapping(address => uint) public specialTransferLimitsInPercentages; event ExemptWalletAdded(address _wallet); event ExemptWalletRemoved(address _wallet); event TransferLimitInTokensSet(address _wallet, uint256 _amount); event TransferLimitInPercentageSet(address _wallet, uint _percentage); - event TransferLimitRemoved(address _wallet); + event TransferLimitInPercentageRemoved(address _wallet); + event TransferLimitInTokensRemoved(address _wallet); event GlobalTransferLimitInTokensSet(uint256 _amount, uint256 _oldAmount); event GlobalTransferLimitInPercentageSet(uint256 _percentage, uint256 _oldPercentage); event TransferLimitChangedToTokens(); @@ -48,18 +51,18 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { if(exemptWallets[_from] || paused) return Result.NA; - if(specialTransferLimits[_from] > 0) { - if (isTransferLimitInPercentage) { - validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= specialTransferLimits[_from]; + if(isTransferLimitInPercentage) { + if(specialTransferLimitsInPercentages[_from] > 0) { + validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= specialTransferLimitsInPercentages[_from]; } else { - validTransfer = _amount <= specialTransferLimits[_from]; - } - } else { - if (isTransferLimitInPercentage) { validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= globalTransferLimitInPercentage; - } else { - validTransfer = _amount <= globalTransferLimit; } + } else { + if(specialTransferLimitsInTokens[_from] > 0) { + validTransfer = _amount <= specialTransferLimitsInTokens[_from]; + } else { + validTransfer = _amount <= globalTransferLimitInTokens; + } } if(validTransfer) return Result.NA; return Result.INVALID; @@ -104,14 +107,14 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { } /** * @notice Change the global transfer limit - * @param _newGlobalTransferLimit new transfer limit in tokens + * @param _newGlobalTransferLimitInTokens new transfer limit in tokens * @dev This function can be used only when The manager is configured to use limits in tokens */ - function changeGlobalLimitInTokens(uint256 _newGlobalTransferLimit) public withPerm(ADMIN) { + function changeGlobalLimitInTokens(uint256 _newGlobalTransferLimitInTokens) public withPerm(ADMIN) { require(!isTransferLimitInPercentage, "Transfer limit not set in tokens"); - require(_newGlobalTransferLimit > 0, "Transfer limit has to greater than zero"); - emit GlobalTransferLimitInTokensSet(_newGlobalTransferLimit, globalTransferLimit); - globalTransferLimit = _newGlobalTransferLimit; + require(_newGlobalTransferLimitInTokens > 0, "Transfer limit has to greater than zero"); + emit GlobalTransferLimitInTokensSet(_newGlobalTransferLimitInTokens, globalTransferLimitInTokens); + globalTransferLimitInTokens = _newGlobalTransferLimitInTokens; } @@ -177,10 +180,10 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { * @param _transferLimit transfer limit for the wallet in tokens * @dev the manager has to be configured to use limits in tokens */ - function setTransferLimitForWallet(address _wallet, uint _transferLimit) public withPerm(ADMIN) { + function setTransferLimitInTokens(address _wallet, uint _transferLimit) public withPerm(ADMIN) { require(_transferLimit > 0, "Transfer limit has to be greater than 0"); require(!isTransferLimitInPercentage, "Transfer limit not in token amount"); - specialTransferLimits[_wallet] = _transferLimit; + specialTransferLimitsInTokens[_wallet] = _transferLimit; emit TransferLimitInTokensSet(_wallet, _transferLimit); } @@ -194,7 +197,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { function setTransferLimitInPercentage(address _wallet, uint _transferLimitInPercentage) public withPerm(ADMIN) { require(isTransferLimitInPercentage, "Transfer limit not in percentage"); require(_transferLimitInPercentage > 0 && _transferLimitInPercentage <= 100 * 10 ** 16, "Transfer limit not in required range"); - specialTransferLimits[_wallet] = _transferLimitInPercentage; + specialTransferLimitsInPercentages[_wallet] = _transferLimitInPercentage; emit TransferLimitInPercentageSet(_wallet, _transferLimitInPercentage); } @@ -203,10 +206,20 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { * @notice removes transfer limit for a wallet * @param _wallet wallet address */ - function removeTransferLimitForWallet(address _wallet) public withPerm(ADMIN) { - require(specialTransferLimits[_wallet] > 0 , "Wallet Address does not have a transfer limit"); - specialTransferLimits[_wallet] = 0; - emit TransferLimitRemoved(_wallet); + function removeTransferLimitInPercentage(address _wallet) public withPerm(ADMIN) { + require(specialTransferLimitsInPercentages[_wallet] > 0 , "Wallet Address does not have a transfer limit"); + specialTransferLimitsInPercentages[_wallet] = 0; + emit TransferLimitInPercentageRemoved(_wallet); + } + + /** + * @notice removes transfer limit for a wallet + * @param _wallet wallet address + */ + function removeTransferLimitInTokens(address _wallet) public withPerm(ADMIN) { + require(specialTransferLimitsInTokens[_wallet] > 0 , "Wallet Address does not have a transfer limit"); + specialTransferLimitsInTokens[_wallet] = 0; + emit TransferLimitInTokensRemoved(_wallet); } /** @@ -215,11 +228,11 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { * @param _transferLimits array of transfer limits for each wallet in tokens * @dev The manager has to be configured to use tokens as limit */ - function setTransferLimitForWalletMulti(address[] _wallets, uint[] _transferLimits) public withPerm(ADMIN) { + function setTransferLimitInTokensMulti(address[] _wallets, uint[] _transferLimits) public withPerm(ADMIN) { require(_wallets.length > 0, "Wallets cannot be empty"); require(_wallets.length == _transferLimits.length); for (uint256 i=0; i < _wallets.length; i++ ) { - setTransferLimitForWallet(_wallets[i], _transferLimits[i]); + setTransferLimitInTokens(_wallets[i], _transferLimits[i]); } } @@ -242,10 +255,21 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { * @notice removes transfer limits for an array of wallet * @param _wallets array of wallet addresses */ - function removeTransferLimitForWalletMulti(address[] _wallets) public withPerm(ADMIN) { + function removeTransferLimitInTokensMulti(address[] _wallets) public withPerm(ADMIN) { + require(_wallets.length > 0, "Wallets cannot be empty"); + for (uint i = 0; i < _wallets.length; i++) { + removeTransferLimitInTokens(_wallets[i]); + } + } + + /** + * @notice removes transfer limits for an array of wallet + * @param _wallets array of wallet addresses + */ + function removeTransferLimitInPercentageMulti(address[] _wallets) public withPerm(ADMIN) { require(_wallets.length > 0, "Wallets cannot be empty"); for (uint i = 0; i < _wallets.length; i++) { - removeTransferLimitForWallet(_wallets[i]); + removeTransferLimitInPercentage(_wallets[i]); } } diff --git a/test/u_single_trade_volume_restriction.js b/test/u_single_trade_volume_restriction.js index 03968e9b5..625b3ef22 100644 --- a/test/u_single_trade_volume_restriction.js +++ b/test/u_single_trade_volume_restriction.js @@ -561,7 +561,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { it('should set transfer limit for a wallet', async () => { let errorThrown = false; try { - let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(accounts[4], 100); + let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 100); } catch (e) { errorThrown = true; } @@ -569,7 +569,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = false; try { - let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(accounts[4], 0, { + let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 0, { from: token_owner }); } catch (e) { @@ -587,7 +587,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { ensureException(e); } assert.ok(errorThrown, "Transfer limit cannot be set in percentage") - let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(accounts[4], 100, { + let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 100, { from: token_owner }); assert.equal(tx.logs[0].args._wallet, accounts[4]); @@ -614,7 +614,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { assert.ok(errorThrown, "Transfer limit can not be set to more 0") errorThrown = false; try { - tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitForWallet(accounts[4], 1, { + tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInTokens(accounts[4], 1, { from: token_owner }); } catch (e) { @@ -633,7 +633,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { it('should remove transfer limit for wallet', async () => { let errorThrown = false; try { - let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitForWallet(accounts[4]); + let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[4]); } catch (e) { errorThrown = true; } @@ -641,7 +641,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = false; try { - let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitForWallet(accounts[0], { + let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[0], { from: token_owner }); } catch (e) { @@ -649,7 +649,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { } assert.ok(errorThrown, "Non Admins cannot set/remove transfer limits"); - let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitForWallet(accounts[4], { + let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[4], { from: token_owner }); assert.equal(tx.logs[0].args._wallet, accounts[4], "Wrong wallet removed"); @@ -818,7 +818,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = false; try { - tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitForWalletMulti([], tokenLimits, { + tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti([], tokenLimits, { from: token_owner }); } catch (e) { @@ -829,7 +829,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = false; try { - tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitForWalletMulti([accounts[0]], tokenLimits, { + tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti([accounts[0]], tokenLimits, { from: token_owner }); } catch (e) { @@ -838,7 +838,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { } assert.ok(errorThrown, "wallet array length dont match"); - tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitForWalletMulti(wallets, tokenLimits, { + tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti(wallets, tokenLimits, { from: token_owner }); logs = tx.logs.filter(log => log.event == 'TransferLimitInTokensSet'); @@ -849,7 +849,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { } errorThrown = false try { - await P_SingleTradeVolumeRestrictionManager.removeTransferLimitForWalletMulti([], { + await P_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokensMulti([], { from: token_owner }); } catch (e) { @@ -857,10 +857,10 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { errorThrown = true; } assert.ok(errorThrown, "Wallets cannot be empty"); - tx = await P_SingleTradeVolumeRestrictionManager.removeTransferLimitForWalletMulti(wallets, { + tx = await P_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokensMulti(wallets, { from: token_owner }); - logs = tx.logs.filter(log => log.event === 'TransferLimitRemoved'); + logs = tx.logs.filter(log => log.event === 'TransferLimitInTokensRemoved'); assert.equal(logs.length, wallets.length, "Transfer limit not removed"); for (let i = 0; i < wallets.length; i++) { assert.equal(logs[i].args._wallet, wallets[i], "transfer limit not removed for wallet"); @@ -943,10 +943,12 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { } ); + //setting a max of 5 tokens await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(web3.utils.toWei('5', 'ether'), { from: token_owner }) + let errorThrown = false; try { await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('6', 'ether'), { @@ -972,7 +974,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { assert.equal((await I_SecurityToken.balanceOf(account_investor5)).toNumber(), web3.utils.toWei('7', 'ether')); //special limits wallet - await I_SingleTradeVolumeRestrictionManager.setTransferLimitForWallet(account_investor5, web3.utils.toWei('5', 'ether'), { + await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(account_investor5, web3.utils.toWei('5', 'ether'), { from: token_owner }); errorThrown = false; From 6b328dc971a4eac5596185c4c6df4de4dd5dab3d Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Mon, 1 Oct 2018 19:41:57 +0530 Subject: [PATCH 20/27] Add tests --- .../SingleTradeVolumeRestrictionManager.sol | 8 +++--- test/u_single_trade_volume_restriction.js | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index 60ad07050..fcb0ca7cc 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -203,7 +203,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { /** - * @notice removes transfer limit for a wallet + * @notice removes transfer limit set in percentage for a wallet * @param _wallet wallet address */ function removeTransferLimitInPercentage(address _wallet) public withPerm(ADMIN) { @@ -213,7 +213,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { } /** - * @notice removes transfer limit for a wallet + * @notice removes transfer limit set in tokens for a wallet * @param _wallet wallet address */ function removeTransferLimitInTokens(address _wallet) public withPerm(ADMIN) { @@ -252,7 +252,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { } /** - * @notice removes transfer limits for an array of wallet + * @notice removes transfer limits set in tokens for an array of wallet * @param _wallets array of wallet addresses */ function removeTransferLimitInTokensMulti(address[] _wallets) public withPerm(ADMIN) { @@ -263,7 +263,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { } /** - * @notice removes transfer limits for an array of wallet + * @notice removes transfer limits set in percentage for an array of wallet * @param _wallets array of wallet addresses */ function removeTransferLimitInPercentageMulti(address[] _wallets) public withPerm(ADMIN) { diff --git a/test/u_single_trade_volume_restriction.js b/test/u_single_trade_volume_restriction.js index 625b3ef22..d3e665142 100644 --- a/test/u_single_trade_volume_restriction.js +++ b/test/u_single_trade_volume_restriction.js @@ -897,6 +897,32 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { assert.equal(logs[i].args._wallet, wallets[i], "Transfer limit not set for wallet"); assert.equal(logs[i].args._percentage.toNumber(), percentageLimits[i]); } + + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentageMulti([], {from:token_owner}); + } catch(e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Wallets cannot be empty"); + + tx = await I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentageMulti(wallets, {from: token_owner}); + logs = tx.logs.filter(log => log.event == 'TransferLimitInPercentageRemoved'); + assert.equal(logs.length, wallets.length, "transfer limits not set for wallets"); + + for (let i = 0; i < wallets.length; i++) { + assert.equal(logs[i].args._wallet, wallets[i], "Transfer limit not set for wallet"); + } + + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentage(wallets[0], {from: token_owner}); + } catch(e) { + ensureException(e) + errorThrown = true; + } + assert.ok(errorThrown, "Wallet should not be removed"); }) it('should be able to transfer tokens SingleTradeVolumeRestriction', async () => { From 09015da650ab0494abde858a876c71c263b088dc Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Wed, 3 Oct 2018 13:05:19 +0530 Subject: [PATCH 21/27] Changes from the review --- .../SingleTradeVolumeRestrictionManager.sol | 2 + ...leTradeVolumeRestrictionManagerFactory.sol | 2 + migrations/2_deploy_contracts.js | 18 + test/u_single_trade_volume_restriction.js | 1165 ---------------- test/w_single_trade_volume_restriction.js | 1169 +++++++++++++++++ 5 files changed, 1191 insertions(+), 1165 deletions(-) delete mode 100644 test/u_single_trade_volume_restriction.js create mode 100644 test/w_single_trade_volume_restriction.js diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index fcb0ca7cc..153b62f4a 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -16,8 +16,10 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { // should be multipled by 10^16. if the transfer percentage is 20%, then globalTransferLimitInPercentage should be 20*10^16 uint256 public globalTransferLimitInPercentage; + //mapping to store the wallets that are exempted from the volume restriction mapping(address=>bool) public exemptWallets; + //addresses on this list have special transfer restrictions apart from global mapping(address => uint) public specialTransferLimitsInTokens; mapping(address => uint) public specialTransferLimitsInPercentages; diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol index f481c1840..6d1089f01 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol @@ -23,6 +23,8 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { name = "SingleTradeVolumeRestriction"; title = "Single Trade Volume Restriction Manager"; description = "Imposes volume restriction on a single trade"; + compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); } /** diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 97124b804..590654525 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -2,6 +2,7 @@ const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') const GeneralTransferManagerFactory = artifacts.require('./GeneralTransferManagerFactory.sol') const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol') const PercentageTransferManagerFactory = artifacts.require('./PercentageTransferManagerFactory.sol') +const SingleTradeVolumeRestrictionManagerFactory = artifacts.require('./SingleTradeVolumeRestrictionManagerFactory.sol') const USDTieredSTOProxyFactory = artifacts.require('./USDTieredSTOProxyFactory.sol'); const CountTransferManagerFactory = artifacts.require('./CountTransferManagerFactory.sol') const EtherDividendCheckpointFactory = artifacts.require('./EtherDividendCheckpointFactory.sol') @@ -165,6 +166,11 @@ const functionSignatureProxyMR = { // to manual approve the transfer that will overcome the other transfer restrictions) return deployer.deploy(ManualApprovalTransferManagerFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); }).then(() => { + // D) Deploy the SingleTradeVolumeRestrictionManagerFactory Contract (Factory used to generate the SingleTradeVolumeRestrictionManager contract used + // to restrict large volume transactions + return deployer.deploy(SingleTradeVolumeRestrictionManagerFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); + }) + .then(() => { // H) Deploy the STVersionProxy001 Contract which contains the logic of deployment of securityToken. return deployer.deploy(STFactory, GeneralTransferManagerFactory.address, {from: PolymathAccount}); }).then(() => { @@ -215,6 +221,10 @@ const functionSignatureProxyMR = { // E) Register the ERC20DividendCheckpointFactory in the ModuleRegistry to make the factory available at the protocol level. // So any securityToken can use that factory to generate the ERC20DividendCheckpoint contract. return moduleRegistry.registerModule(ERC20DividendCheckpointFactory.address, {from: PolymathAccount}); + }).then(() => { + // E) Register the SingleTradeVolumeRestrictionManagerFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the SingleTradeVolumeRestrictionManager contract. + return moduleRegistry.registerModule(SingleTradeVolumeRestrictionManagerFactory.address, {from: PolymathAccount}); }).then(() => { // F) Once the GeneralTransferManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. @@ -251,6 +261,12 @@ const functionSignatureProxyMR = { // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. return moduleRegistry.verifyModule(ManualApprovalTransferManagerFactory.address, true, {from: PolymathAccount}); }).then(() => { + // G) Once the SingleTradeVolumeRestrictionManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken + // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. + // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. + return moduleRegistry.verifyModule(SingleTradeVolumeRestrictionManagerFactory.address, true, {from: PolymathAccount}); + }) + .then(() => { // M) Deploy the CappedSTOFactory (Use to generate the CappedSTO contract which will used to collect the funds ). return deployer.deploy(CappedSTOFactory, PolyToken, cappedSTOSetupCost, 0, 0, {from: PolymathAccount}) }).then(() => { @@ -310,6 +326,8 @@ const functionSignatureProxyMR = { EtherDividendCheckpointFactory: ${EtherDividendCheckpointFactory.address} ERC20DividendCheckpointFactory: ${ERC20DividendCheckpointFactory.address} + SingleTradeVolumeRestrictionManagerFactory: + ${SingleTradeVolumeRestrictionManagerFactory.address} ----------------------------------------------------------------------------- `); console.log('\n'); diff --git a/test/u_single_trade_volume_restriction.js b/test/u_single_trade_volume_restriction.js deleted file mode 100644 index d3e665142..000000000 --- a/test/u_single_trade_volume_restriction.js +++ /dev/null @@ -1,1165 +0,0 @@ -import latestTime from './helpers/latestTime'; -import { - duration, - ensureException, - promisifyLogWatch, - latestBlock -} from './helpers/utils'; -import takeSnapshot, { - increaseTime, - revertToSnapshot -} from './helpers/time'; -import { - encodeProxyCall -} from './helpers/encodeCall'; - -const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') -const ModuleRegistry = artifacts.require('./ModuleRegistry.sol'); -const ModuleRegistryProxy = artifacts.require('./ModuleRegistryProxy.sol'); -const SecurityToken = artifacts.require('./SecurityToken.sol'); -const SecurityTokenRegistry = artifacts.require('./SecurityTokenRegistry.sol'); -const SecurityTokenRegistryProxy = artifacts.require('./SecurityTokenRegistryProxy.sol'); -const FeatureRegistry = artifacts.require('./FeatureRegistry.sol'); -const STFactory = artifacts.require('./STFactory.sol'); -const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol'); -const GeneralTransferManagerFactory = artifacts.require('./GeneralTransferManagerFactory.sol'); -const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); -const SingleTradeVolumeRestrictionManagerFactory = artifacts.require('./SingleTradeVolumeRestrictionManagerFactory.sol'); -const SingleTradeVolumeRestrictionManager = artifacts.require('./SingleTradeVolumeRestrictionManager'); -const CountTransferManagerFactory = artifacts.require('./CountTransferManagerFactory.sol'); -const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); -const PolyTokenFaucet = artifacts.require('./PolyTokenFaucet.sol'); - -const Web3 = require('web3'); -const BigNumber = require('bignumber.js'); -const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port - -contract('SingleTradeVolumeRestrictionManager', accounts => { - - - - // Accounts Variable declaration - let account_polymath; - let account_issuer; - let token_owner; - let account_investor1; - let account_investor2; - let account_investor3; - let account_investor4; - let account_investor5; - let zero_address = '0x0000000000000000000000000000000000000000'; - - // investor Details - let fromTime = latestTime(); - let toTime = latestTime(); - let expiryTime = toTime + duration.days(15); - - let message = "Transaction Should Fail!"; - - // Contract Instance Declaration - let I_SecurityTokenRegistryProxy - let I_GeneralPermissionManagerFactory; - let I_GeneralTransferManagerFactory; - let I_GeneralPermissionManager; - let I_GeneralTransferManager; - let I_SingleTradeVolumeRestrictionManagerFactory; - let I_SingleTradeVolumeRestrictionManager; - let P_SingleTradeVolumeRestrictionManagerFactory; - let P_SingleTradeVolumeRestrictionManager; - let I_SingleTradeVolumeRestrictionPercentageManager; - let I_ModuleRegistry; - let I_MRProxied; - let I_ModuleRegistryProxy; - let I_FeatureRegistry; - let I_SecurityTokenRegistry; - let I_STRProxied; - let I_STFactory; - let I_SecurityToken; - let I_PolyToken; - let I_PolymathRegistry; - - const STRProxyParameters = ['address', 'address', 'uint256', 'uint256', 'address', 'address']; - const MRProxyParameters = ['address', 'address']; - - // SecurityToken Details - const swarmHash = "dagwrgwgvwergwrvwrg"; - const name = "Team"; - const symbol = "sap"; - const tokenDetails = "This is equity type of issuance"; - const decimals = 18; - const contact = "team@polymath.network"; - - // Module key - const delegateManagerKey = 1; - const transferManagerKey = 2; - const stoKey = 3; - - // Initial fee for ticker registry and security token registry - const initRegFee = web3.utils.toWei("250"); - - before(async () => { - // Accounts setup - account_polymath = accounts[0]; - account_issuer = accounts[1]; - - token_owner = account_issuer; - - account_investor1 = accounts[6]; - account_investor2 = accounts[7]; - account_investor3 = accounts[8]; - account_investor4 = accounts[9]; - account_investor5 = accounts[5]; - - // ----------- POLYMATH NETWORK Configuration ------------ - - // Step 0: Deploy the PolymathRegistry - I_PolymathRegistry = await PolymathRegistry.new({ - from: account_polymath - }); - - // Step 1: Deploy the token Faucet and Mint tokens for token_owner - I_PolyToken = await PolyTokenFaucet.new(); - await I_PolyToken.getTokens((10000 * Math.pow(10, 18)), token_owner); - - I_FeatureRegistry = await FeatureRegistry.new( - I_PolymathRegistry.address, { - from: account_polymath - }); - - // STEP 2: Deploy the ModuleRegistry - - I_ModuleRegistry = await ModuleRegistry.new({ - from: account_polymath - }); - - I_ModuleRegistryProxy = await ModuleRegistryProxy.new({ - from: account_polymath - }); - let bytesMRProxy = encodeProxyCall(MRProxyParameters, [I_PolymathRegistry.address, account_polymath]); - await I_ModuleRegistryProxy.upgradeToAndCall("1.0.0", I_ModuleRegistry.address, bytesMRProxy, { - from: account_polymath - }); - I_MRProxied = await ModuleRegistry.at(I_ModuleRegistryProxy.address); - - - // STEP 2: Deploy the GeneralTransferManagerFactory - - I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, { - from: account_polymath - }); - - assert.notEqual( - I_GeneralTransferManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "GeneralTransferManagerFactory contract was not deployed" - ); - - // STEP 3: Deploy the GeneralDelegateManagerFactoryFactory - - I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(I_PolyToken.address, 0, 0, 0, { - from: account_polymath - }); - - assert.notEqual( - I_GeneralPermissionManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "GeneralDelegateManagerFactory contract was not deployed" - ); - - // STEP 4: Deploy the SingleTradeVolumeRestrictionManagerFactory - I_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, 0, 0, 0, { - from: account_polymath - }); - - assert.notEqual( - I_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" - ); - - - P_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, web3.utils.toWei("500", "ether"), 0, 0, { - from: account_polymath - }); - assert.notEqual( - P_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" - ); - - await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { - from: account_polymath - }); - await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { - from: account_polymath - }); - - // (B) : Register the GeneralDelegateManagerFactory - await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { - from: account_polymath - }); - await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { - from: account_polymath - }); - - // (C) : Register the SingleTradeVolumeRestrictionManagerFactory - await I_MRProxied.registerModule(I_SingleTradeVolumeRestrictionManagerFactory.address, { - from: account_polymath - }); - await I_MRProxied.verifyModule(I_SingleTradeVolumeRestrictionManagerFactory.address, true, { - from: account_polymath - }); - - // (C) : Register the Paid SingleTradeVolumeRestrictionManagerFactory - await I_MRProxied.registerModule(P_SingleTradeVolumeRestrictionManagerFactory.address, { - from: account_polymath - }); - await I_MRProxied.verifyModule(P_SingleTradeVolumeRestrictionManagerFactory.address, true, { - from: account_polymath - }); - - // Step 6: Deploy the STFactory contract - - I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, { - from: account_polymath - }); - - assert.notEqual( - I_STFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "STFactory contract was not deployed", - ); - - // Step 7: Deploy the SecurityTokenRegistry contract - - I_SecurityTokenRegistry = await SecurityTokenRegistry.new({ - from: account_polymath - }); - - assert.notEqual( - I_SecurityTokenRegistry.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "SecurityTokenRegistry contract was not deployed", - ); - - // Step 8: Deploy the proxy and attach the implementation contract to it. - I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({ - from: account_polymath - }); - let bytesProxy = encodeProxyCall(STRProxyParameters, [I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); - await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { - from: account_polymath - }); - I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); - - // Step 9: update the registries addresses from the PolymathRegistry contract - await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, { - from: account_polymath - }) - await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistryProxy.address, { - from: account_polymath - }); - await I_PolymathRegistry.changeAddress("FeatureRegistry", I_FeatureRegistry.address, { - from: account_polymath - }); - await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, { - from: account_polymath - }); - await I_MRProxied.updateFromRegistry({ - from: account_polymath - }); - }) - - describe("Generate the SecurityToken", async () => { - it("Should register the ticker before the generation of the security token", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { - from: token_owner - }); - let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { - from: token_owner - }); - assert.equal(tx.logs[0].args._owner, token_owner); - assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); - }); - - it("Should generate the new security token with the same symbol as registered above", async () => { - await I_PolyToken.approve(I_STRProxied.address, initRegFee, { - from: token_owner - }); - let _blockNo = latestBlock(); - let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { - from: token_owner, - gas: 60000000 - }); - - // Verify the successful generation of the security token - assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); - - I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); - - const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ - from: _blockNo - }), 1); - - // Verify that GeneralTransferManager module get added successfully or not - assert.equal(log.args._type.toNumber(), 2); - assert.equal( - web3.utils.toAscii(log.args._name) - .replace(/\u0000/g, ''), - "GeneralTransferManager" - ); - }); - - it("Should intialize the auto attached modules", async () => { - let moduleData = await I_SecurityToken.modules(2, 0); - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); - - assert.notEqual( - I_GeneralTransferManager.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "GeneralTransferManager contract was not deployed", - ); - - }); - }); - // - describe("Buy tokens using whitelist & manual approvals", async () => { - - it("Should Buy the tokens", async () => { - // Add the Investor in to the whitelist - - let tx = await I_GeneralTransferManager.modifyWhitelist( - account_investor1, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, { - from: account_issuer, - gas: 6000000 - }); - - assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in adding the investor in whitelist"); - - // Jump time - await increaseTime(5000); - - // Mint some tokens - await I_SecurityToken.mint(account_investor1, web3.utils.toWei('100', 'ether'), { - from: token_owner - }); - - assert.equal( - (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), - web3.utils.toWei('100', 'ether') - ); - }); - - it("Should Buy some more tokens", async () => { - // Add the Investor in to the whitelist - - let tx = await I_GeneralTransferManager.modifyWhitelist( - account_investor2, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, { - from: account_issuer, - gas: 6000000 - }); - - assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor2.toLowerCase(), "Failed in adding the investor in whitelist"); - - // Mint some tokens - await I_SecurityToken.mint(account_investor2, web3.utils.toWei('1', 'ether'), { - from: token_owner - }); - - assert.equal( - (await I_SecurityToken.balanceOf(account_investor2)).toNumber(), - web3.utils.toWei('1', 'ether') - ); - }); - // - it("Fails to attach the SingleTradeVolumeRestrictionManager with the security token due to fees not paid", async () => { - let managerArgs = web3.eth.abi.encodeFunctionCall({ - name: 'configure', - type: 'function', - inputs: [{ - type: 'bool', - name: '_isTransferLimitInPercentage' - }, - { - type: 'uint256', - name: '_globalTransferLimitInPercentageOrToken' - } - ] - }, [true, 90]) - let errorThrown = false; - await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); - try { - const tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { - from: token_owner - }); - } catch (error) { - console.log(` tx -> failed because Token is not paid`.grey); - ensureException(error); - errorThrown = true; - } - assert.ok(errorThrown, message); - }); - - - - it("Should successfully attach the Paid SingleTradeVolumeRestrictionManager with the security token", async () => { - let managerArgs = web3.eth.abi.encodeFunctionCall({ - name: 'configure', - type: 'function', - inputs: [{ - type: 'bool', - name: '_isTransferLimitInPercentage' - }, - { - type: 'uint256', - name: '_globalTransferLimitInPercentageOrToken' - } - ] - }, [false, 90]); - await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), { - from: token_owner - }); - let tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { - from: token_owner - }); - assert.equal(tx.logs[3].args._type.toNumber(), transferManagerKey, "SingleTradeVolumeRestrictionManager did not get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[3].args._name) - .replace(/\u0000/g, ''), - "SingleTradeVolumeRestriction", - "SingleTradeVolumeRestrictionManagerFactory module was not added" - ); - P_SingleTradeVolumeRestrictionManager = SingleTradeVolumeRestrictionManager.at(tx.logs[3].args._module); - }); - - it("Should successfully attach the SingleTradeVolumeRestrictionManager with the security token", async () => { - let managerArgs = web3.eth.abi.encodeFunctionCall({ - name: 'configure', - type: 'function', - inputs: [{ - type: 'bool', - name: '_isTransferLimitInPercentage' - }, - { - type: 'uint256', - name: '_globalTransferLimitInPercentageOrToken' - } - ] - }, [false, 7 * 10 ** 16]) - const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { - from: token_owner - }); - assert.equal(tx.logs[2].args._type.toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[2].args._name) - .replace(/\u0000/g, ''), - "SingleTradeVolumeRestriction", - "SingleTradeVolumeRestriction module was not added" - ); - I_SingleTradeVolumeRestrictionManager = SingleTradeVolumeRestrictionManager.at(tx.logs[2].args._module); - }); - - it("Should successfully attach the SingleTradeVolumeRestrictionManager (Percentage) with the security token", async () => { - let managerArgs = web3.eth.abi.encodeFunctionCall({ - name: 'configure', - type: 'function', - inputs: [{ - type: 'bool', - name: '_isTransferLimitInPercentage' - }, - { - type: 'uint256', - name: '_globalTransferLimitInPercentageOrToken' - } - ] - }, [true, 90]); - const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { - from: token_owner - }); - assert.equal(tx.logs[2].args._type.toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[2].args._name) - .replace(/\u0000/g, ''), - "SingleTradeVolumeRestriction", - "SingleTradeVolumeRestriction module was not added" - ); - I_SingleTradeVolumeRestrictionPercentageManager = SingleTradeVolumeRestrictionManager.at(tx.logs[2].args._module); - }); - - it('should return get permissions', async () => { - let permissions = await I_SingleTradeVolumeRestrictionPercentageManager.getPermissions(); - assert.equal(permissions.length, 1, "Invalid Permissions"); - assert.equal( - web3.utils.toAscii(permissions[0]).replace(/\u0000/g, ''), - "ADMIN", - 'Wrong permissions' - ); - }); - - it('add exempt wallet', async () => { - let errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5]); - } catch (e) { - errorThrown = true; - } - assert.ok(errorThrown, "Non Admins cannot add exempt wallets"); - - errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(zero_address, { - from: token_owner - }); - } catch (e) { - ensureException(e); - errorThrown = true; - } - - assert.ok(errorThrown, "Exempt wallet cannot be zero"); - - let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5], { - from: token_owner - }); - assert.equal(tx.logs[0].args._wallet, accounts[5], "Wrong wallet added as exempt"); - }); - - it('should remove an exempt wallet', async () => { - let errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5]); - } catch (e) { - errorThrown = true; - } - assert.ok(errorThrown, "Non Admins cannot add exempt wallets"); - - errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(zero_address, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - - assert.ok(errorThrown, "Zero address cannot be added to exempt wallet"); - - let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5], { - from: token_owner - }); - assert.equal(tx.logs[0].args._wallet, accounts[5], "Wrong wallet removed from exempt"); - }); - - it('should set transfer limit for a wallet', async () => { - let errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 100); - } catch (e) { - errorThrown = true; - } - assert.ok(errorThrown, "Non Admins cannot set transfer limits"); - - errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 0, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Transfer limit cannot be set to 0") - errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInPercentage(accounts[4], 10, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Transfer limit cannot be set in percentage") - let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 100, { - from: token_owner - }); - assert.equal(tx.logs[0].args._wallet, accounts[4]); - assert.equal(tx.logs[0].args._amount, 100); - - errorThrown = false; - try { - tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 0, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - errorThrown = false - try { - tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 101 * 10 ** 16, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Transfer limit can not be set to more 0") - errorThrown = false; - try { - tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInTokens(accounts[4], 1, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - - assert.ok(errorThrown, "Transfer limit in tokens can not be set for a manager that has transfer limit set as percentage") - tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 50, { - from: token_owner - }); - assert.equal(tx.logs[0].args._wallet, accounts[4], "Wrong wallet added to transfer limits"); - assert.equal(tx.logs[0].args._percentage, 50, "Wrong percentage set"); - }); - - it('should remove transfer limit for wallet', async () => { - let errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[4]); - } catch (e) { - errorThrown = true; - } - assert.ok(errorThrown, "Non Admins cannot set/remove transfer limits"); - - errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[0], { - from: token_owner - }); - } catch (e) { - errorThrown = true; - } - assert.ok(errorThrown, "Non Admins cannot set/remove transfer limits"); - - let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[4], { - from: token_owner - }); - assert.equal(tx.logs[0].args._wallet, accounts[4], "Wrong wallet removed"); - }); - - it("Should pause the tranfers at Manager level", async () => { - let tx = await I_SingleTradeVolumeRestrictionManager.pause({ - from: token_owner - }); - }); - - it('should be able to set a global transfer limit', async () => { - let errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(100 * 10 ** 18); - } catch (e) { - errorThrown = true; - } - assert.ok(errorThrown, "only owner is allowed"); - - errorThrown = false; - - try { - let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInPercentage(100 * 10 ** 18, { - from: token_owner - }); - } catch (e) { - ensureException(e); - errorThrown = true; - } - assert.ok(errorThrown, "Cannot change global limit in percentage when set to tokens"); - - errorThrown = false; - try { - await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(0, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Global limit cannot be set to 0"); - let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(10, { - from: token_owner - }); - assert.equal(tx.logs[0].args._amount, 10, "Global Limit not set"); - - errorThrown = false; - - try { - let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Global limit can be set by non-admins"); - - errorThrown = false; - - try { - let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "cannot change global limit in tokens if transfer limit is set to percentage"); - - errorThrown = false; - try { - let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(0, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Cannot set global limit in tokens to 0"); - - tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(40, { - from: token_owner - }); - assert.equal(tx.logs[0].args._percentage, 40, "Global Limit not set"); - - errorThrown = false; - try { - await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(101 * 10 ** 16, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Global limit cannot be set to more than 100"); - - errorThrown = false; - try { - await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInPercentage(10, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Global limit in percentage cannot be set when limit is in tokens"); - errorThrown = false; - try { - await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(10, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Global limit in tokens cannot be set when limit is in percentage"); - }); - - it('should perform batch updates', async () => { - let wallets = [accounts[0], accounts[1], accounts[2]]; - let tokenLimits = [1, 2, 3]; - let percentageLimits = [5, 6, 7]; - - let errorThrown = false; - try { - await P_SingleTradeVolumeRestrictionManager.addExemptWalletMulti([], { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Exempt wallet multi cannot be empty wallet"); - - // add exempt wallet multi - let tx = await P_SingleTradeVolumeRestrictionManager.addExemptWalletMulti(wallets, { - from: token_owner - }); - let logs = tx.logs.filter(log => log.event === 'ExemptWalletAdded'); - assert.equal(logs.length, wallets.length, "Batch Exempt wallets not added"); - for (let i = 0; i < logs.length; i++) { - assert.equal(logs[i].args._wallet, wallets[i], "Wallet not added as exempt wallet"); - } - - errorThrown = false; - try { - await P_SingleTradeVolumeRestrictionManager.removeExemptWalletMulti([], { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Exempt wallet multi cannot be empty wallet"); - - // remove exempt wallet multi - tx = await P_SingleTradeVolumeRestrictionManager.removeExemptWalletMulti(wallets, { - from: token_owner - }) - logs = tx.logs.filter(log => log.event === 'ExemptWalletRemoved'); - assert.equal(logs.length, wallets.length, "Batch Exempt wallets not removed"); - - for (let i = 0; i < logs.length; i++) { - assert.equal(logs[i].args._wallet, wallets[i], "Wallet not added as exempt wallet"); - } - - errorThrown = false; - try { - tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti([], tokenLimits, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "wallets cannot be empty"); - - errorThrown = false; - try { - tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti([accounts[0]], tokenLimits, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "wallet array length dont match"); - - tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti(wallets, tokenLimits, { - from: token_owner - }); - logs = tx.logs.filter(log => log.event == 'TransferLimitInTokensSet'); - assert.equal(wallets.length, logs.length, "Transfer limit not set"); - for (let i = 0; i < wallets.length; i++) { - assert.equal(logs[i].args._wallet, wallets[i], "transfer limit not set for wallet"); - assert.equal(logs[i].args._amount.toNumber(), tokenLimits[i]); - } - errorThrown = false - try { - await P_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokensMulti([], { - from: token_owner - }); - } catch (e) { - ensureException(e); - errorThrown = true; - } - assert.ok(errorThrown, "Wallets cannot be empty"); - tx = await P_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokensMulti(wallets, { - from: token_owner - }); - logs = tx.logs.filter(log => log.event === 'TransferLimitInTokensRemoved'); - assert.equal(logs.length, wallets.length, "Transfer limit not removed"); - for (let i = 0; i < wallets.length; i++) { - assert.equal(logs[i].args._wallet, wallets[i], "transfer limit not removed for wallet"); - } - - errorThrown = false; - try { - await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti([], percentageLimits, { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "wallets cannot be empty"); - - errorThrown = false; - try { - await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti(wallets, [], { - from: token_owner - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "wallets and amounts dont match be empty"); - tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti(wallets, percentageLimits, { - from: token_owner - }); - logs = tx.logs.filter(log => log.event == 'TransferLimitInPercentageSet'); - assert.equal(logs.length, wallets.length, "transfer limits not set for wallets"); - - for (let i = 0; i < wallets.length; i++) { - assert.equal(logs[i].args._wallet, wallets[i], "Transfer limit not set for wallet"); - assert.equal(logs[i].args._percentage.toNumber(), percentageLimits[i]); - } - - errorThrown = false; - try { - await I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentageMulti([], {from:token_owner}); - } catch(e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Wallets cannot be empty"); - - tx = await I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentageMulti(wallets, {from: token_owner}); - logs = tx.logs.filter(log => log.event == 'TransferLimitInPercentageRemoved'); - assert.equal(logs.length, wallets.length, "transfer limits not set for wallets"); - - for (let i = 0; i < wallets.length; i++) { - assert.equal(logs[i].args._wallet, wallets[i], "Transfer limit not set for wallet"); - } - - errorThrown = false; - try { - await I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentage(wallets[0], {from: token_owner}); - } catch(e) { - ensureException(e) - errorThrown = true; - } - assert.ok(errorThrown, "Wallet should not be removed"); - }) - - it('should be able to transfer tokens SingleTradeVolumeRestriction', async () => { - await I_SingleTradeVolumeRestrictionManager.unpause({ - from: token_owner - }) - await I_SingleTradeVolumeRestrictionPercentageManager.pause({ - from: token_owner - }) - await P_SingleTradeVolumeRestrictionManager.pause({ - from: token_owner - }); - - await I_GeneralTransferManager.modifyWhitelist( - account_investor3, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, { - from: account_issuer, - gas: 6000000 - } - ); - - await I_GeneralTransferManager.modifyWhitelist( - account_investor4, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, { - from: account_issuer, - gas: 6000000 - } - ); - - await I_GeneralTransferManager.modifyWhitelist( - account_investor5, - latestTime(), - latestTime(), - latestTime() + duration.days(10), - true, { - from: account_issuer, - gas: 6000000 - } - ); - - - //setting a max of 5 tokens - await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(web3.utils.toWei('5', 'ether'), { - from: token_owner - }) - - let errorThrown = false; - try { - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('6', 'ether'), { - from: account_investor1 - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Transfer should have not happened"); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('4', 'ether'), { - from: account_investor1 - }); - assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei('4', 'ether')); - - // exempt wallet - await I_SingleTradeVolumeRestrictionManager.addExemptWallet(account_investor1, { - from: token_owner - }); - await I_SecurityToken.transfer(account_investor5, web3.utils.toWei('7', 'ether'), { - from: account_investor1 - }); - assert.equal((await I_SecurityToken.balanceOf(account_investor5)).toNumber(), web3.utils.toWei('7', 'ether')); - - //special limits wallet - await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(account_investor5, web3.utils.toWei('5', 'ether'), { - from: token_owner - }); - errorThrown = false; - try { - await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('7', 'ether'), { - from: account_investor5 - }); - } catch (e) { - errorThrown = true; - ensureException(e); - } - assert.ok(errorThrown, "Transfer should have not happened"); - await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('4', 'ether'), { - from: account_investor5 - }) - assert.equal((await I_SecurityToken.balanceOf(account_investor4)).toNumber(), web3.utils.toWei('4', 'ether')) - }) - - it('should be able to transfer tokens (percentage transfer limit)', async () => { - await I_SingleTradeVolumeRestrictionManager.pause({ - from: token_owner - }); - let balance = (await I_SecurityToken.balanceOf(account_investor2)).toNumber(); - await I_SecurityToken.transfer(account_investor1, balance, { - from: account_investor2 - }); - - - balance = (await I_SecurityToken.balanceOf(account_investor3)).toNumber(); - - await I_SecurityToken.transfer(account_investor1, balance, { - from: account_investor3 - }); - - - balance = (await I_SecurityToken.balanceOf(account_investor4)).toNumber(); - await I_SecurityToken.transfer(account_investor1, balance, { - from: account_investor4 - }); - - balance = (await I_SecurityToken.balanceOf(account_investor5)).toNumber(); - await I_SecurityToken.transfer(account_investor1, balance, { - from: account_investor5 - }); - - await I_SingleTradeVolumeRestrictionPercentageManager.unpause({ - from: token_owner - }); - // // - await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(49 * 10 ** 16, { - from: token_owner - }); - - let errorThrown = false; - try { - // more than the limit - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('90', 'ether'), { - from: account_investor1 - }); - } catch (e) { - ensureException(e); - errorThrown = true; - } - assert.ok(errorThrown, "Transfer above limit happened"); - - - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('20', 'ether'), { - from: account_investor1 - }); - assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei('20', 'ether')) - - await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(account_investor1, 5 * 10 ** 16, { - from: token_owner - }); - errorThrown = false; - try { - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('35', 'ether'), { - from: account_investor1 - }); - } catch (e) { - ensureException(e); - errorThrown = true; - } - assert.ok(errorThrown, "transfer happened above limit"); - - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('1', 'ether'), { - from: account_investor1 - }); - assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei('1', 'ether')); - }); - - it('should change transfer limits to tokens', async () => { - let errorThrown = false; - try { - await I_SingleTradeVolumeRestrictionPercentageManager.changeTransferLimitToPercentage(1, { - from: token_owner - }); - } catch (e) { - - ensureException(e); - errorThrown = true; - } - assert.equal(errorThrown, true, "Should not change to percentage again"); - - - let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeTransferLimitToTokens(1, { - from: token_owner - }); - assert.equal(await I_SingleTradeVolumeRestrictionPercentageManager.isTransferLimitInPercentage(), false, "Error Changing"); - assert.equal(tx.logs[0].args._amount.toNumber(), 1, "Transfer limit not changed"); - }) - - it('should change transfer limits to percentage', async () => { - let errorThrown = false; - try { - await I_SingleTradeVolumeRestrictionManager.changeTransferLimitToTokens(1, { - from: token_owner - }); - } catch (e) { - ensureException(e); - errorThrown = true; - } - assert.equal(errorThrown, true, "Should not change to tokens again"); - - let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeTransferLimitToPercentage(1, { - from: token_owner - }); - assert.ok(await I_SingleTradeVolumeRestrictionPercentageManager.isTransferLimitInPercentage(), "Error Changing"); - assert.equal(tx.logs[0].args._percentage.toNumber(), 1, "Transfer limit not changed"); - }) - - - - }); - - describe("SingleTradeVolumeRestrictionManager Factory test cases", async () => { - - it("Should get the exact details of the factory", async () => { - assert.equal(await I_SingleTradeVolumeRestrictionManagerFactory.setupCost.call(), 0); - assert.equal(await I_SingleTradeVolumeRestrictionManagerFactory.getType.call(), 2); - let name = web3.utils.toUtf8(await I_SingleTradeVolumeRestrictionManagerFactory.getName.call()); - assert.equal(name, "SingleTradeVolumeRestriction", "Wrong Module added"); - let desc = await I_SingleTradeVolumeRestrictionManagerFactory.getDescription.call(); - assert.equal(desc, "Imposes volume restriction on a single trade", "Wrong Module added"); - let title = await I_SingleTradeVolumeRestrictionManagerFactory.getTitle.call(); - assert.equal(title, "Single Trade Volume Restriction Manager", "Wrong Module added"); - let inst = await I_SingleTradeVolumeRestrictionManagerFactory.getInstructions.call(); - assert.equal(inst, "Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens", "Wrong Module added"); - let version = await I_SingleTradeVolumeRestrictionManagerFactory.getVersion.call(); - assert.equal(version, "1.0.0", "Version not correct"); - }); - - it("Should get the tags of the factory", async () => { - let tags = await I_SingleTradeVolumeRestrictionManagerFactory.getTags.call(); - assert.equal(web3.utils.toUtf8(tags[0]), "Single Trade"); - assert.equal(web3.utils.toUtf8(tags[1]), "Transfer"); - assert.equal(web3.utils.toUtf8(tags[2]), "Volume"); - }); - - - }); -}); diff --git a/test/w_single_trade_volume_restriction.js b/test/w_single_trade_volume_restriction.js new file mode 100644 index 000000000..3b67222a3 --- /dev/null +++ b/test/w_single_trade_volume_restriction.js @@ -0,0 +1,1169 @@ +import latestTime from './helpers/latestTime'; +import { + duration, + ensureException, + promisifyLogWatch, + latestBlock +} from './helpers/utils'; +import takeSnapshot, { + increaseTime, + revertToSnapshot +} from './helpers/time'; +import { + encodeProxyCall +} from './helpers/encodeCall'; + +const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') +const ModuleRegistry = artifacts.require('./ModuleRegistry.sol'); +const ModuleRegistryProxy = artifacts.require('./ModuleRegistryProxy.sol'); +const SecurityToken = artifacts.require('./SecurityToken.sol'); +const SecurityTokenRegistry = artifacts.require('./SecurityTokenRegistry.sol'); +const SecurityTokenRegistryProxy = artifacts.require('./SecurityTokenRegistryProxy.sol'); +const FeatureRegistry = artifacts.require('./FeatureRegistry.sol'); +const STFactory = artifacts.require('./STFactory.sol'); +const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol'); +const GeneralTransferManagerFactory = artifacts.require('./GeneralTransferManagerFactory.sol'); +const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); +const SingleTradeVolumeRestrictionManagerFactory = artifacts.require('./SingleTradeVolumeRestrictionManagerFactory.sol'); +const SingleTradeVolumeRestrictionManager = artifacts.require('./SingleTradeVolumeRestrictionManager'); +const CountTransferManagerFactory = artifacts.require('./CountTransferManagerFactory.sol'); +const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); +const PolyTokenFaucet = artifacts.require('./PolyTokenFaucet.sol'); + +const Web3 = require('web3'); +const BigNumber = require('bignumber.js'); +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port + +contract('SingleTradeVolumeRestrictionManager', accounts => { + + + + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + let account_investor5; + let zero_address = '0x0000000000000000000000000000000000000000'; + + // investor Details + let fromTime = latestTime(); + let toTime = latestTime(); + let expiryTime = toTime + duration.days(15); + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let I_SecurityTokenRegistryProxy + let I_GeneralPermissionManagerFactory; + let I_GeneralTransferManagerFactory; + let I_GeneralPermissionManager; + let I_GeneralTransferManager; + let I_SingleTradeVolumeRestrictionManagerFactory; + let I_SingleTradeVolumeRestrictionManager; + let P_SingleTradeVolumeRestrictionManagerFactory; + let P_SingleTradeVolumeRestrictionManager; + let I_SingleTradeVolumeRestrictionPercentageManager; + let I_ModuleRegistry; + let I_MRProxied; + let I_ModuleRegistryProxy; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_STRProxied; + let I_STFactory; + let I_SecurityToken; + let I_PolyToken; + let I_PolymathRegistry; + + const STRProxyParameters = ['address', 'address', 'uint256', 'uint256', 'address', 'address']; + const MRProxyParameters = ['address', 'address']; + + // SecurityToken Details + const swarmHash = "dagwrgwgvwergwrvwrg"; + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + // Initial fee for ticker registry and security token registry + const initRegFee = web3.utils.toWei("250"); + + before(async () => { + // Accounts setup + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + + account_investor1 = accounts[6]; + account_investor2 = accounts[7]; + account_investor3 = accounts[8]; + account_investor4 = accounts[9]; + account_investor5 = accounts[5]; + + // ----------- POLYMATH NETWORK Configuration ------------ + + // Step 0: Deploy the PolymathRegistry + I_PolymathRegistry = await PolymathRegistry.new({ + from: account_polymath + }); + + // Step 1: Deploy the token Faucet and Mint tokens for token_owner + I_PolyToken = await PolyTokenFaucet.new(); + await I_PolyToken.getTokens((10000 * Math.pow(10, 18)), token_owner); + + I_FeatureRegistry = await FeatureRegistry.new( + I_PolymathRegistry.address, { + from: account_polymath + }); + + // STEP 2: Deploy the ModuleRegistry + + I_ModuleRegistry = await ModuleRegistry.new({ + from: account_polymath + }); + // + I_ModuleRegistryProxy = await ModuleRegistryProxy.new({ + from: account_polymath + }); + + let bytesMRProxy = encodeProxyCall(MRProxyParameters, [I_PolymathRegistry.address, account_polymath]); + await I_ModuleRegistryProxy.upgradeToAndCall("1.0.0", I_ModuleRegistry.address, bytesMRProxy, { + from: account_polymath + }); + I_MRProxied = await ModuleRegistry.at(I_ModuleRegistryProxy.address); + + + // + // + // // STEP 2: Deploy the GeneralTransferManagerFactory + // + I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(I_PolyToken.address, 0, 0, 0, { + from: account_polymath + }); + + assert.notEqual( + I_GeneralTransferManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralTransferManagerFactory contract was not deployed" + ); + + // STEP 3: Deploy the GeneralDelegateManagerFactoryFactory + + I_GeneralPermissionManagerFactory = await GeneralPermissionManagerFactory.new(I_PolyToken.address, 0, 0, 0, { + from: account_polymath + }); + + assert.notEqual( + I_GeneralPermissionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralDelegateManagerFactory contract was not deployed" + ); + + // STEP 4: Deploy the SingleTradeVolumeRestrictionManagerFactory + I_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, 0, 0, 0, { + from: account_polymath + }); + + assert.notEqual( + I_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" + ); + + + P_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(I_PolyToken.address, web3.utils.toWei("500", "ether"), 0, 0, { + from: account_polymath + }); + assert.notEqual( + P_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" + ); + // + // + // // Step 6: Deploy the STFactory contract + // + I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, { + from: account_polymath + }); + + assert.notEqual( + I_STFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "STFactory contract was not deployed", + ); + + // Step 7: Deploy the SecurityTokenRegistry contract + + I_SecurityTokenRegistry = await SecurityTokenRegistry.new({ + from: account_polymath + }); + + assert.notEqual( + I_SecurityTokenRegistry.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "SecurityTokenRegistry contract was not deployed", + ); + + // Step 8: Deploy the proxy and attach the implementation contract to it. + I_SecurityTokenRegistryProxy = await SecurityTokenRegistryProxy.new({ + from: account_polymath + }); + let bytesProxy = encodeProxyCall(STRProxyParameters, [I_PolymathRegistry.address, I_STFactory.address, initRegFee, initRegFee, I_PolyToken.address, account_polymath]); + await I_SecurityTokenRegistryProxy.upgradeToAndCall("1.0.0", I_SecurityTokenRegistry.address, bytesProxy, { + from: account_polymath + }); + I_STRProxied = await SecurityTokenRegistry.at(I_SecurityTokenRegistryProxy.address); + // + // // Step 9: update the registries addresses from the PolymathRegistry contract + await I_PolymathRegistry.changeAddress("PolyToken", I_PolyToken.address, { + from: account_polymath + }) + await I_PolymathRegistry.changeAddress("ModuleRegistry", I_ModuleRegistryProxy.address, { + from: account_polymath + }); + await I_PolymathRegistry.changeAddress("FeatureRegistry", I_FeatureRegistry.address, { + from: account_polymath + }); + await I_PolymathRegistry.changeAddress("SecurityTokenRegistry", I_SecurityTokenRegistryProxy.address, { + from: account_polymath + }); + await I_MRProxied.updateFromRegistry({ + from: account_polymath + }); + + await I_MRProxied.registerModule(I_GeneralTransferManagerFactory.address, { + from: account_polymath + }); + await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { + from: account_polymath + }); + + // (B) : Register the GeneralDelegateManagerFactory + await I_MRProxied.registerModule(I_GeneralPermissionManagerFactory.address, { + from: account_polymath + }); + await I_MRProxied.verifyModule(I_GeneralPermissionManagerFactory.address, true, { + from: account_polymath + }); + + // (C) : Register the SingleTradeVolumeRestrictionManagerFactory + await I_MRProxied.registerModule(I_SingleTradeVolumeRestrictionManagerFactory.address, { + from: account_polymath + }); + await I_MRProxied.verifyModule(I_SingleTradeVolumeRestrictionManagerFactory.address, true, { + from: account_polymath + }); + + // (C) : Register the Paid SingleTradeVolumeRestrictionManagerFactory + await I_MRProxied.registerModule(P_SingleTradeVolumeRestrictionManagerFactory.address, { + from: account_polymath + }); + await I_MRProxied.verifyModule(P_SingleTradeVolumeRestrictionManagerFactory.address, true, { + from: account_polymath + }); + }) + + describe("Generate the SecurityToken", async () => { + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { + from: token_owner + }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { + from: token_owner + }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { + from: token_owner + }); + let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { + from: token_owner + }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + + const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({ + from: _blockNo + }), 1); + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._type.toNumber(), 2); + assert.equal( + web3.utils.toAscii(log.args._name) + .replace(/\u0000/g, ''), + "GeneralTransferManager" + ); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = await I_SecurityToken.modules(2, 0); + I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + + assert.notEqual( + I_GeneralTransferManager.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "GeneralTransferManager contract was not deployed", + ); + + }); + }); + // + describe("Buy tokens using whitelist & manual approvals", async () => { + + it("Should Buy the tokens", async () => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyWhitelist( + account_investor1, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, { + from: account_issuer + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Jump time + await increaseTime(5000); + + // Mint some tokens + await I_SecurityToken.mint(account_investor1, web3.utils.toWei('100', 'ether'), { + from: token_owner + }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), + web3.utils.toWei('100', 'ether') + ); + }); + + it("Should Buy some more tokens", async () => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyWhitelist( + account_investor2, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, { + from: account_issuer + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor2.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Mint some tokens + await I_SecurityToken.mint(account_investor2, web3.utils.toWei('1', 'ether'), { + from: token_owner + }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor2)).toNumber(), + web3.utils.toWei('1', 'ether') + ); + }); + // + it("Fails to attach the SingleTradeVolumeRestrictionManager with the security token due to fees not paid", async () => { + let managerArgs = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'bool', + name: '_isTransferLimitInPercentage' + }, + { + type: 'uint256', + name: '_globalTransferLimitInPercentageOrToken' + } + ] + }, [true, 90]) + let errorThrown = false; + await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); + try { + const tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { + from: token_owner + }); + } catch (error) { + console.log(` tx -> failed because Token is not paid`.grey); + ensureException(error); + errorThrown = true; + } + assert.ok(errorThrown, message); + }); + + + + it("Should successfully attach the Paid SingleTradeVolumeRestrictionManager with the security token", async () => { + let managerArgs = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'bool', + name: '_isTransferLimitInPercentage' + }, + { + type: 'uint256', + name: '_globalTransferLimitInPercentageOrToken' + } + ] + }, [false, 90]); + await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), { + from: token_owner + }); + let tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { + from: token_owner + }); + assert.equal(tx.logs[3].args._type.toNumber(), transferManagerKey, "SingleTradeVolumeRestrictionManager did not get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[3].args._name) + .replace(/\u0000/g, ''), + "SingleTradeVolumeRestriction", + "SingleTradeVolumeRestrictionManagerFactory module was not added" + ); + P_SingleTradeVolumeRestrictionManager = SingleTradeVolumeRestrictionManager.at(tx.logs[3].args._module); + }); + + it("Should successfully attach the SingleTradeVolumeRestrictionManager with the security token", async () => { + let managerArgs = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'bool', + name: '_isTransferLimitInPercentage' + }, + { + type: 'uint256', + name: '_globalTransferLimitInPercentageOrToken' + } + ] + }, [false, 7 * 10 ** 16]) + const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { + from: token_owner + }); + assert.equal(tx.logs[2].args._type.toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name) + .replace(/\u0000/g, ''), + "SingleTradeVolumeRestriction", + "SingleTradeVolumeRestriction module was not added" + ); + I_SingleTradeVolumeRestrictionManager = SingleTradeVolumeRestrictionManager.at(tx.logs[2].args._module); + }); + + it("Should successfully attach the SingleTradeVolumeRestrictionManager (Percentage) with the security token", async () => { + let managerArgs = web3.eth.abi.encodeFunctionCall({ + name: 'configure', + type: 'function', + inputs: [{ + type: 'bool', + name: '_isTransferLimitInPercentage' + }, + { + type: 'uint256', + name: '_globalTransferLimitInPercentageOrToken' + } + ] + }, [true, 90]); + const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { + from: token_owner + }); + assert.equal(tx.logs[2].args._type.toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name) + .replace(/\u0000/g, ''), + "SingleTradeVolumeRestriction", + "SingleTradeVolumeRestriction module was not added" + ); + I_SingleTradeVolumeRestrictionPercentageManager = SingleTradeVolumeRestrictionManager.at(tx.logs[2].args._module); + }); + + it('should return get permissions', async () => { + let permissions = await I_SingleTradeVolumeRestrictionPercentageManager.getPermissions(); + assert.equal(permissions.length, 1, "Invalid Permissions"); + assert.equal( + web3.utils.toAscii(permissions[0]).replace(/\u0000/g, ''), + "ADMIN", + 'Wrong permissions' + ); + }); + + it('add exempt wallet', async () => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5]); + } catch (e) { + errorThrown = true; + } + assert.ok(errorThrown, "Non Admins cannot add exempt wallets"); + + errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(zero_address, { + from: token_owner + }); + } catch (e) { + ensureException(e); + errorThrown = true; + } + + assert.ok(errorThrown, "Exempt wallet cannot be zero"); + + let tx = await I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5], { + from: token_owner + }); + assert.equal(tx.logs[0].args._wallet, accounts[5], "Wrong wallet added as exempt"); + }); + + it('should remove an exempt wallet', async () => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5]); + } catch (e) { + errorThrown = true; + } + assert.ok(errorThrown, "Non Admins cannot add exempt wallets"); + + errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(zero_address, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + + assert.ok(errorThrown, "Zero address cannot be added to exempt wallet"); + + let tx = await I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5], { + from: token_owner + }); + assert.equal(tx.logs[0].args._wallet, accounts[5], "Wrong wallet removed from exempt"); + }); + + it('should set transfer limit for a wallet', async () => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 100); + } catch (e) { + errorThrown = true; + } + assert.ok(errorThrown, "Non Admins cannot set transfer limits"); + + errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 0, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Transfer limit cannot be set to 0") + errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInPercentage(accounts[4], 10, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Transfer limit cannot be set in percentage") + let tx = await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 100, { + from: token_owner + }); + assert.equal(tx.logs[0].args._wallet, accounts[4]); + assert.equal(tx.logs[0].args._amount, 100); + + errorThrown = false; + try { + tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 0, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + errorThrown = false + try { + tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 101 * 10 ** 16, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Transfer limit can not be set to more 0") + errorThrown = false; + try { + tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInTokens(accounts[4], 1, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + + assert.ok(errorThrown, "Transfer limit in tokens can not be set for a manager that has transfer limit set as percentage") + tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 50, { + from: token_owner + }); + assert.equal(tx.logs[0].args._wallet, accounts[4], "Wrong wallet added to transfer limits"); + assert.equal(tx.logs[0].args._percentage, 50, "Wrong percentage set"); + }); + + it('should remove transfer limit for wallet', async () => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[4]); + } catch (e) { + errorThrown = true; + } + assert.ok(errorThrown, "Non Admins cannot set/remove transfer limits"); + + errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[0], { + from: token_owner + }); + } catch (e) { + errorThrown = true; + } + assert.ok(errorThrown, "Non Admins cannot set/remove transfer limits"); + + let tx = await I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[4], { + from: token_owner + }); + assert.equal(tx.logs[0].args._wallet, accounts[4], "Wrong wallet removed"); + }); + + it("Should pause the tranfers at Manager level", async () => { + let tx = await I_SingleTradeVolumeRestrictionManager.pause({ + from: token_owner + }); + }); + + it('should be able to set a global transfer limit', async () => { + let errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(100 * 10 ** 18); + } catch (e) { + errorThrown = true; + } + assert.ok(errorThrown, "only owner is allowed"); + + errorThrown = false; + + try { + let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInPercentage(100 * 10 ** 18, { + from: token_owner + }); + } catch (e) { + ensureException(e); + errorThrown = true; + } + assert.ok(errorThrown, "Cannot change global limit in percentage when set to tokens"); + + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(0, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Global limit cannot be set to 0"); + let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(10, { + from: token_owner + }); + assert.equal(tx.logs[0].args._amount, 10, "Global Limit not set"); + + errorThrown = false; + + try { + let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Global limit can be set by non-admins"); + + errorThrown = false; + + try { + let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "cannot change global limit in tokens if transfer limit is set to percentage"); + + errorThrown = false; + try { + let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(0, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Cannot set global limit in tokens to 0"); + + tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(40, { + from: token_owner + }); + assert.equal(tx.logs[0].args._percentage, 40, "Global Limit not set"); + + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(101 * 10 ** 16, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Global limit cannot be set to more than 100"); + + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInPercentage(10, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Global limit in percentage cannot be set when limit is in tokens"); + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(10, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Global limit in tokens cannot be set when limit is in percentage"); + }); + + it('should perform batch updates', async () => { + let wallets = [accounts[0], accounts[1], accounts[2]]; + let tokenLimits = [1, 2, 3]; + let percentageLimits = [5, 6, 7]; + + let errorThrown = false; + try { + await P_SingleTradeVolumeRestrictionManager.addExemptWalletMulti([], { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Exempt wallet multi cannot be empty wallet"); + + // add exempt wallet multi + let tx = await P_SingleTradeVolumeRestrictionManager.addExemptWalletMulti(wallets, { + from: token_owner + }); + let logs = tx.logs.filter(log => log.event === 'ExemptWalletAdded'); + assert.equal(logs.length, wallets.length, "Batch Exempt wallets not added"); + for (let i = 0; i < logs.length; i++) { + assert.equal(logs[i].args._wallet, wallets[i], "Wallet not added as exempt wallet"); + } + + errorThrown = false; + try { + await P_SingleTradeVolumeRestrictionManager.removeExemptWalletMulti([], { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Exempt wallet multi cannot be empty wallet"); + + // remove exempt wallet multi + tx = await P_SingleTradeVolumeRestrictionManager.removeExemptWalletMulti(wallets, { + from: token_owner + }) + logs = tx.logs.filter(log => log.event === 'ExemptWalletRemoved'); + assert.equal(logs.length, wallets.length, "Batch Exempt wallets not removed"); + + for (let i = 0; i < logs.length; i++) { + assert.equal(logs[i].args._wallet, wallets[i], "Wallet not added as exempt wallet"); + } + + errorThrown = false; + try { + tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti([], tokenLimits, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "wallets cannot be empty"); + + errorThrown = false; + try { + tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti([accounts[0]], tokenLimits, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "wallet array length dont match"); + + tx = await P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti(wallets, tokenLimits, { + from: token_owner + }); + logs = tx.logs.filter(log => log.event == 'TransferLimitInTokensSet'); + assert.equal(wallets.length, logs.length, "Transfer limit not set"); + for (let i = 0; i < wallets.length; i++) { + assert.equal(logs[i].args._wallet, wallets[i], "transfer limit not set for wallet"); + assert.equal(logs[i].args._amount.toNumber(), tokenLimits[i]); + } + errorThrown = false + try { + await P_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokensMulti([], { + from: token_owner + }); + } catch (e) { + ensureException(e); + errorThrown = true; + } + assert.ok(errorThrown, "Wallets cannot be empty"); + tx = await P_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokensMulti(wallets, { + from: token_owner + }); + logs = tx.logs.filter(log => log.event === 'TransferLimitInTokensRemoved'); + assert.equal(logs.length, wallets.length, "Transfer limit not removed"); + for (let i = 0; i < wallets.length; i++) { + assert.equal(logs[i].args._wallet, wallets[i], "transfer limit not removed for wallet"); + } + + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti([], percentageLimits, { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "wallets cannot be empty"); + + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti(wallets, [], { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "wallets and amounts dont match be empty"); + tx = await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti(wallets, percentageLimits, { + from: token_owner + }); + logs = tx.logs.filter(log => log.event == 'TransferLimitInPercentageSet'); + assert.equal(logs.length, wallets.length, "transfer limits not set for wallets"); + + for (let i = 0; i < wallets.length; i++) { + assert.equal(logs[i].args._wallet, wallets[i], "Transfer limit not set for wallet"); + assert.equal(logs[i].args._percentage.toNumber(), percentageLimits[i]); + } + + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentageMulti([], { + from: token_owner + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Wallets cannot be empty"); + + tx = await I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentageMulti(wallets, { + from: token_owner + }); + logs = tx.logs.filter(log => log.event == 'TransferLimitInPercentageRemoved'); + assert.equal(logs.length, wallets.length, "transfer limits not set for wallets"); + + for (let i = 0; i < wallets.length; i++) { + assert.equal(logs[i].args._wallet, wallets[i], "Transfer limit not set for wallet"); + } + + errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentage(wallets[0], { + from: token_owner + }); + } catch (e) { + ensureException(e) + errorThrown = true; + } + assert.ok(errorThrown, "Wallet should not be removed"); + }) + + it('should be able to transfer tokens SingleTradeVolumeRestriction', async () => { + await I_SingleTradeVolumeRestrictionManager.unpause({ + from: token_owner + }) + await I_SingleTradeVolumeRestrictionPercentageManager.pause({ + from: token_owner + }) + await P_SingleTradeVolumeRestrictionManager.pause({ + from: token_owner + }); + + await I_GeneralTransferManager.modifyWhitelist( + account_investor3, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, { + from: account_issuer + } + ); + + await I_GeneralTransferManager.modifyWhitelist( + account_investor4, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, { + from: account_issuer + } + ); + + await I_GeneralTransferManager.modifyWhitelist( + account_investor5, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, { + from: account_issuer + } + ); + + + //setting a max of 5 tokens + await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(web3.utils.toWei('5', 'ether'), { + from: token_owner + }) + + let errorThrown = false; + try { + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('6', 'ether'), { + from: account_investor1 + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Transfer should have not happened"); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('4', 'ether'), { + from: account_investor1 + }); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei('4', 'ether')); + + // exempt wallet + await I_SingleTradeVolumeRestrictionManager.addExemptWallet(account_investor1, { + from: token_owner + }); + await I_SecurityToken.transfer(account_investor5, web3.utils.toWei('7', 'ether'), { + from: account_investor1 + }); + assert.equal((await I_SecurityToken.balanceOf(account_investor5)).toNumber(), web3.utils.toWei('7', 'ether')); + + //special limits wallet + await I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(account_investor5, web3.utils.toWei('5', 'ether'), { + from: token_owner + }); + errorThrown = false; + try { + await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('7', 'ether'), { + from: account_investor5 + }); + } catch (e) { + errorThrown = true; + ensureException(e); + } + assert.ok(errorThrown, "Transfer should have not happened"); + await I_SecurityToken.transfer(account_investor4, web3.utils.toWei('4', 'ether'), { + from: account_investor5 + }) + assert.equal((await I_SecurityToken.balanceOf(account_investor4)).toNumber(), web3.utils.toWei('4', 'ether')) + }) + + it('should be able to transfer tokens (percentage transfer limit)', async () => { + await I_SingleTradeVolumeRestrictionManager.pause({ + from: token_owner + }); + let balance = (await I_SecurityToken.balanceOf(account_investor2)).toNumber(); + await I_SecurityToken.transfer(account_investor1, balance, { + from: account_investor2 + }); + + + balance = (await I_SecurityToken.balanceOf(account_investor3)).toNumber(); + + await I_SecurityToken.transfer(account_investor1, balance, { + from: account_investor3 + }); + + + balance = (await I_SecurityToken.balanceOf(account_investor4)).toNumber(); + await I_SecurityToken.transfer(account_investor1, balance, { + from: account_investor4 + }); + + balance = (await I_SecurityToken.balanceOf(account_investor5)).toNumber(); + await I_SecurityToken.transfer(account_investor1, balance, { + from: account_investor5 + }); + + await I_SingleTradeVolumeRestrictionPercentageManager.unpause({ + from: token_owner + }); + // // + await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(49 * 10 ** 16, { + from: token_owner + }); + + let errorThrown = false; + try { + // more than the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('90', 'ether'), { + from: account_investor1 + }); + } catch (e) { + ensureException(e); + errorThrown = true; + } + assert.ok(errorThrown, "Transfer above limit happened"); + + + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('20', 'ether'), { + from: account_investor1 + }); + assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei('20', 'ether')) + + await I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(account_investor1, 5 * 10 ** 16, { + from: token_owner + }); + errorThrown = false; + try { + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('35', 'ether'), { + from: account_investor1 + }); + } catch (e) { + ensureException(e); + errorThrown = true; + } + assert.ok(errorThrown, "transfer happened above limit"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('1', 'ether'), { + from: account_investor1 + }); + assert.equal((await I_SecurityToken.balanceOf(account_investor3)).toNumber(), web3.utils.toWei('1', 'ether')); + }); + + it('should change transfer limits to tokens', async () => { + let errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionPercentageManager.changeTransferLimitToPercentage(1, { + from: token_owner + }); + } catch (e) { + + ensureException(e); + errorThrown = true; + } + assert.equal(errorThrown, true, "Should not change to percentage again"); + + + let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeTransferLimitToTokens(1, { + from: token_owner + }); + assert.equal(await I_SingleTradeVolumeRestrictionPercentageManager.isTransferLimitInPercentage(), false, "Error Changing"); + assert.equal(tx.logs[0].args._amount.toNumber(), 1, "Transfer limit not changed"); + }) + + it('should change transfer limits to percentage', async () => { + let errorThrown = false; + try { + await I_SingleTradeVolumeRestrictionManager.changeTransferLimitToTokens(1, { + from: token_owner + }); + } catch (e) { + ensureException(e); + errorThrown = true; + } + assert.equal(errorThrown, true, "Should not change to tokens again"); + + let tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeTransferLimitToPercentage(1, { + from: token_owner + }); + assert.ok(await I_SingleTradeVolumeRestrictionPercentageManager.isTransferLimitInPercentage(), "Error Changing"); + assert.equal(tx.logs[0].args._percentage.toNumber(), 1, "Transfer limit not changed"); + }) + + + + }); + + describe("SingleTradeVolumeRestrictionManager Factory test cases", async () => { + + it("Should get the exact details of the factory", async () => { + assert.equal(await I_SingleTradeVolumeRestrictionManagerFactory.setupCost.call(), 0); + assert.equal(await I_SingleTradeVolumeRestrictionManagerFactory.getType.call(), 2); + let name = web3.utils.toUtf8(await I_SingleTradeVolumeRestrictionManagerFactory.getName.call()); + assert.equal(name, "SingleTradeVolumeRestriction", "Wrong Module added"); + let desc = await I_SingleTradeVolumeRestrictionManagerFactory.getDescription.call(); + assert.equal(desc, "Imposes volume restriction on a single trade", "Wrong Module added"); + let title = await I_SingleTradeVolumeRestrictionManagerFactory.getTitle.call(); + assert.equal(title, "Single Trade Volume Restriction Manager", "Wrong Module added"); + let inst = await I_SingleTradeVolumeRestrictionManagerFactory.getInstructions.call(); + assert.equal(inst, "Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens", "Wrong Module added"); + let version = await I_SingleTradeVolumeRestrictionManagerFactory.getVersion.call(); + assert.equal(version, "1.0.0", "Version not correct"); + }); + + it("Should get the tags of the factory", async () => { + let tags = await I_SingleTradeVolumeRestrictionManagerFactory.getTags.call(); + assert.equal(web3.utils.toUtf8(tags[0]), "Single Trade"); + assert.equal(web3.utils.toUtf8(tags[1]), "Transfer"); + assert.equal(web3.utils.toUtf8(tags[2]), "Volume"); + }); + + + }); +}); From c6722fd9787b71f884a165366ecd31a1e696db30 Mon Sep 17 00:00:00 2001 From: Subramanian Venkatesan Date: Wed, 3 Oct 2018 13:43:11 +0530 Subject: [PATCH 22/27] Deleted tokeninfo.js --- scripts/tokenInfo.js | 68 -------------------------------------------- 1 file changed, 68 deletions(-) delete mode 100644 scripts/tokenInfo.js diff --git a/scripts/tokenInfo.js b/scripts/tokenInfo.js deleted file mode 100644 index 2df8c57c4..000000000 --- a/scripts/tokenInfo.js +++ /dev/null @@ -1,68 +0,0 @@ -const Web3 = require("web3"); -const fs = require("fs"); -const async = require("async"); -const path = require("path"); -const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/")); - -const securityTokenRegistryABI = JSON.parse(require('fs').readFileSync('../build/contracts/SecurityTokenRegistry.json').toString()).abi; -const securityTokenABI = JSON.parse(require('fs').readFileSync('../build/contracts/SecurityToken.json').toString()).abi; -const generalTransferManagerABI = JSON.parse(require('fs').readFileSync('../build/contracts/GeneralTransferManager.json').toString()).abi; -const securityTokenRegistryAddress = "0xEf58491224958d978fACF55D2120c55A24516B98"; -const securityTokenRegistry = new web3.eth.Contract(securityTokenRegistryABI, securityTokenRegistryAddress); - -async function getTokens() { - let strEvents = await web3.eth.getPastLogs({fromBlock:'0x5C5C18', address:securityTokenRegistry.address, topics: ["0x2510d802a0818e71139a7680a6388bcffcd3fa686e02a0f7319488c5bdb38fcb"]}); - for (let i = 0; i < strEvents.length; i++) { - let tokenAddress = '0x' + strEvents[i].topics[1].slice(26,66) - await getInfo(tokenAddress); - } -} - -async function getInfo(tokenAddress) { - let token = new web3.eth.Contract(securityTokenABI, tokenAddress); - console.log("Token - " + tokenAddress); - console.log("----------------------"); - console.log("Owner: " + await token.methods.owner().call()); - console.log("Name: " + await token.methods.name().call()); - console.log("Symbol: " + await token.methods.symbol().call()); - console.log("Total Supply: " + await token.methods.totalSupply().call()); - console.log("Frozen: " + await token.methods.freeze().call()); - console.log("Investors: " + await token.methods.investorCount().call()); - console.log("Latest Checkpoint: " + await token.methods.currentCheckpointId().call()); - console.log("Finished Issuer Minting: " + await token.methods.finishedIssuerMinting().call()); - console.log("Finished STO Minting: " + await token.methods.finishedSTOMinting().call()); - let gtmRes = await token.methods.modules(2, 0).call(); - let gtmEvents = await web3.eth.getPastLogs({fromBlock:'0x5C5C18', address:gtmRes.moduleAddress}); - console.log("Count of GeneralTransferManager Events: " + gtmEvents.length); - console.log("Modules Attached (TransferManager):"); - await getModules(2, token); - console.log("Modules Attached (PermissionManager):"); - await getModules(1, token); - console.log("Modules Attached (STO):"); - await getModules(3, token); - console.log("Modules Attached (Checkpoint):"); - await getModules(4, token); - console.log("") - console.log(); - console.log(); -} - -async function getModules(type, token) { - let index = 0; - while (true) { - try { - let res = await token.methods.modules(type, index).call(); - console.log(" Name: " + web3.utils.toAscii(res.name)); - console.log(" Address: " + res.moduleAddress); - } catch (err) { - // console.log(err); - if (index == 0) { - console.log(" None"); - } - return; - } - index = index + 1; - } -} - -getTokens(); From 9da57ab0606b9ad75cd5286b9c9e10fcdb437e22 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 4 Oct 2018 19:25:50 +0100 Subject: [PATCH 23/27] Update 2_deploy_contracts.js --- migrations/2_deploy_contracts.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 95cd4a451..0153563d5 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -2,7 +2,6 @@ const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') const GeneralTransferManagerFactory = artifacts.require('./GeneralTransferManagerFactory.sol') const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol') const PercentageTransferManagerFactory = artifacts.require('./PercentageTransferManagerFactory.sol') -const SingleTradeVolumeRestrictionManagerFactory = artifacts.require('./SingleTradeVolumeRestrictionManagerFactory.sol') const USDTieredSTOProxyFactory = artifacts.require('./USDTieredSTOProxyFactory.sol'); const CountTransferManagerFactory = artifacts.require('./CountTransferManagerFactory.sol') const EtherDividendCheckpointFactory = artifacts.require('./EtherDividendCheckpointFactory.sol') @@ -320,8 +319,6 @@ module.exports = function (deployer, network, accounts) { EtherDividendCheckpointFactory: ${EtherDividendCheckpointFactory.address} ERC20DividendCheckpointFactory: ${ERC20DividendCheckpointFactory.address} - SingleTradeVolumeRestrictionManagerFactory: - ${SingleTradeVolumeRestrictionManagerFactory.address} ----------------------------------------------------------------------------- `); console.log('\n'); From 77b34109f5145ce4175e9276d48d13f489473016 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 4 Oct 2018 20:17:16 +0100 Subject: [PATCH 24/27] Update SingleTradeVolumeRestrictionManager.sol --- .../TransferManager/SingleTradeVolumeRestrictionManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index 153b62f4a..5c80ef27e 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -48,7 +48,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { } /// @notice Used to verify the transfer transaction according to the rule implemented in the transfer manager - function verifyTransfer(address _from, address /* _to */, uint256 _amount, bool /* _isTransfer */) public returns(Result) { + function verifyTransfer(address _from, address /* _to */, uint256 _amount, bytes /* _data */, bool /* _isTransfer */) public returns(Result) { bool validTransfer; if(exemptWallets[_from] || paused) return Result.NA; From 786d7059891ab28b5fbed8d22dfe468ff3dab9ec Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 4 Oct 2018 21:20:01 +0100 Subject: [PATCH 25/27] Update 2_deploy_contracts.js --- migrations/2_deploy_contracts.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 0153563d5..7b1feeece 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -319,11 +319,9 @@ module.exports = function (deployer, network, accounts) { EtherDividendCheckpointFactory: ${EtherDividendCheckpointFactory.address} ERC20DividendCheckpointFactory: ${ERC20DividendCheckpointFactory.address} - ----------------------------------------------------------------------------- - `); - console.log('\n'); - // -------- END OF POLYMATH NETWORK Configuration -------// - }); - + ----------------------------------------------------------------------------- + `); + console.log('\n'); + // -------- END OF POLYMATH NETWORK Configuration -------// }); } From 149f56dad21c62f1fa71a60d7f205112e43ef44e Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 4 Oct 2018 22:25:00 +0100 Subject: [PATCH 26/27] Update w_single_trade_volume_restriction.js --- test/w_single_trade_volume_restriction.js | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/test/w_single_trade_volume_restriction.js b/test/w_single_trade_volume_restriction.js index 3b67222a3..fb2631d1c 100644 --- a/test/w_single_trade_volume_restriction.js +++ b/test/w_single_trade_volume_restriction.js @@ -305,7 +305,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { }), 1); // Verify that GeneralTransferManager module get added successfully or not - assert.equal(log.args._type.toNumber(), 2); + assert.equal(log.args._types[0].toNumber(), 2); assert.equal( web3.utils.toAscii(log.args._name) .replace(/\u0000/g, ''), @@ -314,15 +314,8 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { }); it("Should intialize the auto attached modules", async () => { - let moduleData = await I_SecurityToken.modules(2, 0); + let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; I_GeneralTransferManager = GeneralTransferManager.at(moduleData); - - assert.notEqual( - I_GeneralTransferManager.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "GeneralTransferManager contract was not deployed", - ); - }); }); // @@ -431,7 +424,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { let tx = await I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { from: token_owner }); - assert.equal(tx.logs[3].args._type.toNumber(), transferManagerKey, "SingleTradeVolumeRestrictionManager did not get deployed"); + assert.equal(tx.logs[3].args._types[0].toNumber(), transferManagerKey, "SingleTradeVolumeRestrictionManager did not get deployed"); assert.equal( web3.utils.toAscii(tx.logs[3].args._name) .replace(/\u0000/g, ''), @@ -458,7 +451,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { from: token_owner }); - assert.equal(tx.logs[2].args._type.toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); + assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name) .replace(/\u0000/g, ''), @@ -485,7 +478,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { from: token_owner }); - assert.equal(tx.logs[2].args._type.toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); + assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "PercentageTransferManager doesn't get deployed"); assert.equal( web3.utils.toAscii(tx.logs[2].args._name) .replace(/\u0000/g, ''), From 28c33153c8fc5a8b60bb4c12c66c783b11623b8e Mon Sep 17 00:00:00 2001 From: satyam Date: Fri, 5 Oct 2018 12:50:04 +0530 Subject: [PATCH 27/27] minor fixes --- .../SingleTradeVolumeRestrictionManager.sol | 13 +++++++------ ...gleTradeVolumeRestrictionManagerFactory.sol | 18 ++++++++++-------- test/w_single_trade_volume_restriction.js | 2 +- yarn.lock | 10 +++++++--- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol index 5c80ef27e..2cc962a29 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManager.sol @@ -9,6 +9,8 @@ import "openzeppelin-solidity/contracts/math/SafeMath.sol"; contract SingleTradeVolumeRestrictionManager is ITransferManager { using SafeMath for uint256; + bytes32 constant public ADMIN = "ADMIN"; + bool public isTransferLimitInPercentage; uint256 public globalTransferLimitInTokens; @@ -17,7 +19,7 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { uint256 public globalTransferLimitInPercentage; //mapping to store the wallets that are exempted from the volume restriction - mapping(address=>bool) public exemptWallets; + mapping(address => bool) public exemptWallets; //addresses on this list have special transfer restrictions apart from global mapping(address => uint) public specialTransferLimitsInTokens; @@ -34,7 +36,6 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { event GlobalTransferLimitInPercentageSet(uint256 _percentage, uint256 _oldPercentage); event TransferLimitChangedToTokens(); event TransferLimitChangedtoPercentage(); - bytes32 constant public ADMIN = "ADMIN"; /** * @notice Constructor @@ -51,22 +52,22 @@ contract SingleTradeVolumeRestrictionManager is ITransferManager { function verifyTransfer(address _from, address /* _to */, uint256 _amount, bytes /* _data */, bool /* _isTransfer */) public returns(Result) { bool validTransfer; - if(exemptWallets[_from] || paused) return Result.NA; + if (exemptWallets[_from] || paused) return Result.NA; - if(isTransferLimitInPercentage) { + if (isTransferLimitInPercentage) { if(specialTransferLimitsInPercentages[_from] > 0) { validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= specialTransferLimitsInPercentages[_from]; } else { validTransfer = (_amount.mul(10**18).div(ISecurityToken(securityToken).totalSupply())) <= globalTransferLimitInPercentage; } } else { - if(specialTransferLimitsInTokens[_from] > 0) { + if (specialTransferLimitsInTokens[_from] > 0) { validTransfer = _amount <= specialTransferLimitsInTokens[_from]; } else { validTransfer = _amount <= globalTransferLimitInTokens; } } - if(validTransfer) return Result.NA; + if (validTransfer) return Result.NA; return Result.INVALID; } diff --git a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol index 6d1089f01..4233d0788 100644 --- a/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol +++ b/contracts/modules/TransferManager/SingleTradeVolumeRestrictionManagerFactory.sol @@ -43,11 +43,13 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { } /** - * @notice Type of the Module factory - * @return uint8 + * @notice Get the types of the Module factory + * @return uint8[] */ - function getType() public view returns(uint8) { - return 2; + function getTypes() external view returns(uint8[]) { + uint8[] memory res = new uint8[](1); + res[0] = 2; + return res; } /** @@ -62,7 +64,7 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { * @notice Get the description of the Module * @return string */ - function getDescription() public view returns(string) { + function getDescription() external view returns(string) { return description; } @@ -70,7 +72,7 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { * @notice Get the title of the Module * @return string */ - function getTitle() public view returns(string) { + function getTitle() external view returns(string) { return title; } @@ -78,7 +80,7 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { * @notice Get the Instructions that help to use the module * @return string */ - function getInstructions() public view returns(string) { + function getInstructions() external view returns(string) { return "Allows an issuer to impose volume restriction on a single trade. Init function takes two parameters. First parameter is a bool indicating if restriction is in percentage. The second parameter is the value in percentage or amount of tokens"; } @@ -101,7 +103,7 @@ contract SingleTradeVolumeRestrictionManagerFactory is ModuleFactory { * @notice Get the tags related to the module factory * @return bytes32[] */ - function getTags() public view returns(bytes32[]) { + function getTags() external view returns(bytes32[]) { bytes32[] memory availableTags = new bytes32[](3); availableTags[0] = "Single Trade"; availableTags[1] = "Transfer"; diff --git a/test/w_single_trade_volume_restriction.js b/test/w_single_trade_volume_restriction.js index fb2631d1c..ef60a5639 100644 --- a/test/w_single_trade_volume_restriction.js +++ b/test/w_single_trade_volume_restriction.js @@ -1137,7 +1137,7 @@ contract('SingleTradeVolumeRestrictionManager', accounts => { it("Should get the exact details of the factory", async () => { assert.equal(await I_SingleTradeVolumeRestrictionManagerFactory.setupCost.call(), 0); - assert.equal(await I_SingleTradeVolumeRestrictionManagerFactory.getType.call(), 2); + assert.equal((await I_SingleTradeVolumeRestrictionManagerFactory.getTypes.call())[0], 2); let name = web3.utils.toUtf8(await I_SingleTradeVolumeRestrictionManagerFactory.getName.call()); assert.equal(name, "SingleTradeVolumeRestriction", "Wrong Module added"); let desc = await I_SingleTradeVolumeRestrictionManagerFactory.getDescription.call(); diff --git a/yarn.lock b/yarn.lock index 383b65c54..5dc2bd17c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3565,6 +3565,10 @@ growl@1.10.3: version "1.10.3" resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.3.tgz#1926ba90cf3edfe2adb4927f5880bc22c66c790f" +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + handlebars@^4.0.1: version "4.0.11" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" @@ -6638,9 +6642,9 @@ solc@^0.4.2: semver "^5.3.0" yargs "^4.7.1" -solidity-coverage@^0.5.5: - version "0.5.7" - resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.5.7.tgz#b7eb67678d446ab2702a26c2158ad71b4865a057" +solidity-coverage@^0.5.10: + version "0.5.11" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.5.11.tgz#1ee45f6d98b75a615aadb8f9aa7db4a2b32258e7" dependencies: death "^1.1.0" ethereumjs-testrpc-sc "6.1.6"