From 29ed76d79a5f6a09ab99e484de53c77eb39cf113 Mon Sep 17 00:00:00 2001 From: satyam Date: Fri, 16 Nov 2018 11:55:30 +0530 Subject: [PATCH 01/56] intialise --- .../libraries/BokkyPooBahsDateTimeLibrary.sol | 339 ++++++++++++++++++ .../TransferManager/VolumeRestrictionTM.sol | 7 + .../VolumeRestrictionTMFactory.sol | 68 ++++ 3 files changed, 414 insertions(+) create mode 100644 contracts/libraries/BokkyPooBahsDateTimeLibrary.sol create mode 100644 contracts/modules/TransferManager/VolumeRestrictionTM.sol create mode 100644 contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol diff --git a/contracts/libraries/BokkyPooBahsDateTimeLibrary.sol b/contracts/libraries/BokkyPooBahsDateTimeLibrary.sol new file mode 100644 index 000000000..2823f9993 --- /dev/null +++ b/contracts/libraries/BokkyPooBahsDateTimeLibrary.sol @@ -0,0 +1,339 @@ +pragma solidity ^0.4.24; + +// ---------------------------------------------------------------------------- +// BokkyPooBah's DateTime Library v1.00 +// +// A gas-efficient Solidity date and time library +// +// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary +// +// Tested date range 1970/01/01 to 2345/12/31 +// +// Conventions: +// Unit | Range | Notes +// :-------- |:-------------:|:----- +// timestamp | >= 0 | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC +// year | 1970 ... 2345 | +// month | 1 ... 12 | +// day | 1 ... 31 | +// hour | 0 ... 23 | +// minute | 0 ... 59 | +// second | 0 ... 59 | +// dayOfWeek | 1 ... 7 | 1 = Monday, ..., 7 = Sunday +// +// +// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018. +// +// GNU Lesser General Public License 3.0 +// https://www.gnu.org/licenses/lgpl-3.0.en.html +// ---------------------------------------------------------------------------- + +library BokkyPooBahsDateTimeLibrary { + + uint constant SECONDS_PER_DAY = 24 * 60 * 60; + uint constant SECONDS_PER_HOUR = 60 * 60; + uint constant SECONDS_PER_MINUTE = 60; + int constant OFFSET19700101 = 2440588; + + uint constant DOW_MON = 1; + uint constant DOW_TUE = 2; + uint constant DOW_WED = 3; + uint constant DOW_THU = 4; + uint constant DOW_FRI = 5; + uint constant DOW_SAT = 6; + uint constant DOW_SUN = 7; + + // ------------------------------------------------------------------------ + // Calculate the number of days from 1970/01/01 to year/month/day using + // the date conversion algorithm from + // http://aa.usno.navy.mil/faq/docs/JD_Formula.php + // and subtracting the offset 2440588 so that 1970/01/01 is day 0 + // + // days = day + // - 32075 + // + 1461 * (year + 4800 + (month - 14) / 12) / 4 + // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12 + // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4 + // - offset + // ------------------------------------------------------------------------ + function _daysFromDate(uint year, uint month, uint day) internal pure returns (uint _days) { + require(year >= 1970); + int _year = int(year); + int _month = int(month); + int _day = int(day); + + int __days = _day + - 32075 + + 1461 * (_year + 4800 + (_month - 14) / 12) / 4 + + 367 * (_month - 2 - (_month - 14) / 12 * 12) / 12 + - 3 * ((_year + 4900 + (_month - 14) / 12) / 100) / 4 + - OFFSET19700101; + + _days = uint(__days); + } + + // ------------------------------------------------------------------------ + // Calculate year/month/day from the number of days since 1970/01/01 using + // the date conversion algorithm from + // http://aa.usno.navy.mil/faq/docs/JD_Formula.php + // and adding the offset 2440588 so that 1970/01/01 is day 0 + // + // int L = days + 68569 + offset + // int N = 4 * L / 146097 + // L = L - (146097 * N + 3) / 4 + // year = 4000 * (L + 1) / 1461001 + // L = L - 1461 * year / 4 + 31 + // month = 80 * L / 2447 + // dd = L - 2447 * month / 80 + // L = month / 11 + // month = month + 2 - 12 * L + // year = 100 * (N - 49) + year + L + // ------------------------------------------------------------------------ + function _daysToDate(uint _days) internal pure returns (uint year, uint month, uint day) { + int __days = int(_days); + + int L = __days + 68569 + OFFSET19700101; + int N = 4 * L / 146097; + L = L - (146097 * N + 3) / 4; + int _year = 4000 * (L + 1) / 1461001; + L = L - 1461 * _year / 4 + 31; + int _month = 80 * L / 2447; + int _day = L - 2447 * _month / 80; + L = _month / 11; + _month = _month + 2 - 12 * L; + _year = 100 * (N - 49) + _year + L; + + year = uint(_year); + month = uint(_month); + day = uint(_day); + } + + function timestampFromDate(uint year, uint month, uint day) internal pure returns (uint timestamp) { + timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY; + } + function timestampFromDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (uint timestamp) { + timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + hour * SECONDS_PER_HOUR + minute * SECONDS_PER_MINUTE + second; + } + function timestampToDate(uint timestamp) internal pure returns (uint year, uint month, uint day) { + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function timestampToDateTime(uint timestamp) internal pure returns (uint year, uint month, uint day, uint hour, uint minute, uint second) { + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + uint secs = timestamp % SECONDS_PER_DAY; + hour = secs / SECONDS_PER_HOUR; + secs = secs % SECONDS_PER_HOUR; + minute = secs / SECONDS_PER_MINUTE; + second = secs % SECONDS_PER_MINUTE; + } + + function isValidDate(uint year, uint month, uint day) internal pure returns (bool valid) { + if (year >= 1970 && month > 0 && month <= 12) { + uint daysInMonth = _getDaysInMonth(year, month); + if (day > 0 && day <= daysInMonth) { + valid = true; + } + } + } + function isValidDateTime(uint year, uint month, uint day, uint hour, uint minute, uint second) internal pure returns (bool valid) { + if (isValidDate(year, month, day)) { + if (hour < 24 && minute < 60 && second < 60) { + valid = true; + } + } + } + function isLeapYear(uint timestamp) internal pure returns (bool leapYear) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + leapYear = _isLeapYear(year); + } + function _isLeapYear(uint year) internal pure returns (bool leapYear) { + leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); + } + function isWeekDay(uint timestamp) internal pure returns (bool weekDay) { + weekDay = getDayOfWeek(timestamp) <= DOW_FRI; + } + function isWeekEnd(uint timestamp) internal pure returns (bool weekEnd) { + weekEnd = getDayOfWeek(timestamp) >= DOW_SAT; + } + function getDaysInMonth(uint timestamp) internal pure returns (uint daysInMonth) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + daysInMonth = _getDaysInMonth(year, month); + } + function _getDaysInMonth(uint year, uint month) internal pure returns (uint daysInMonth) { + if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) { + daysInMonth = 31; + } else if (month != 2) { + daysInMonth = 30; + } else { + daysInMonth = _isLeapYear(year) ? 29 : 28; + } + } + // 1 = Monday, 7 = Sunday + function getDayOfWeek(uint timestamp) internal pure returns (uint dayOfWeek) { + uint _days = timestamp / SECONDS_PER_DAY; + dayOfWeek = (_days + 3) % 7 + 1; + } + + function getYear(uint timestamp) internal pure returns (uint year) { + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function getMonth(uint timestamp) internal pure returns (uint month) { + uint year; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function getDay(uint timestamp) internal pure returns (uint day) { + uint year; + uint month; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + } + function getHour(uint timestamp) internal pure returns (uint hour) { + uint secs = timestamp % SECONDS_PER_DAY; + hour = secs / SECONDS_PER_HOUR; + } + function getMinute(uint timestamp) internal pure returns (uint minute) { + uint secs = timestamp % SECONDS_PER_HOUR; + minute = secs / SECONDS_PER_MINUTE; + } + function getSecond(uint timestamp) internal pure returns (uint second) { + second = timestamp % SECONDS_PER_MINUTE; + } + + function addYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + year += _years; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp >= timestamp); + } + function addMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + month += _months; + year += (month - 1) / 12; + month = (month - 1) % 12 + 1; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp >= timestamp); + } + function addDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp + _days * SECONDS_PER_DAY; + require(newTimestamp >= timestamp); + } + function addHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp + _hours * SECONDS_PER_HOUR; + require(newTimestamp >= timestamp); + } + function addMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE; + require(newTimestamp >= timestamp); + } + function addSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp + _seconds; + require(newTimestamp >= timestamp); + } + + function subYears(uint timestamp, uint _years) internal pure returns (uint newTimestamp) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + year -= _years; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp <= timestamp); + } + function subMonths(uint timestamp, uint _months) internal pure returns (uint newTimestamp) { + uint year; + uint month; + uint day; + (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY); + uint yearMonth = year * 12 + (month - 1) - _months; + year = yearMonth / 12; + month = yearMonth % 12 + 1; + uint daysInMonth = _getDaysInMonth(year, month); + if (day > daysInMonth) { + day = daysInMonth; + } + newTimestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY + timestamp % SECONDS_PER_DAY; + require(newTimestamp <= timestamp); + } + function subDays(uint timestamp, uint _days) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _days * SECONDS_PER_DAY; + require(newTimestamp <= timestamp); + } + function subHours(uint timestamp, uint _hours) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _hours * SECONDS_PER_HOUR; + require(newTimestamp <= timestamp); + } + function subMinutes(uint timestamp, uint _minutes) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE; + require(newTimestamp <= timestamp); + } + function subSeconds(uint timestamp, uint _seconds) internal pure returns (uint newTimestamp) { + newTimestamp = timestamp - _seconds; + require(newTimestamp <= timestamp); + } + + function diffYears(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _years) { + require(fromTimestamp <= toTimestamp); + uint fromYear; + uint fromMonth; + uint fromDay; + uint toYear; + uint toMonth; + uint toDay; + (fromYear, fromMonth, fromDay) = _daysToDate(fromTimestamp / SECONDS_PER_DAY); + (toYear, toMonth, toDay) = _daysToDate(toTimestamp / SECONDS_PER_DAY); + _years = toYear - fromYear; + } + function diffMonths(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _months) { + require(fromTimestamp <= toTimestamp); + uint fromYear; + uint fromMonth; + uint fromDay; + uint toYear; + uint toMonth; + uint toDay; + (fromYear, fromMonth, fromDay) = _daysToDate(fromTimestamp / SECONDS_PER_DAY); + (toYear, toMonth, toDay) = _daysToDate(toTimestamp / SECONDS_PER_DAY); + _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth; + } + function diffDays(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _days) { + require(fromTimestamp <= toTimestamp); + _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY; + } + function diffHours(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _hours) { + require(fromTimestamp <= toTimestamp); + _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR; + } + function diffMinutes(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _minutes) { + require(fromTimestamp <= toTimestamp); + _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE; + } + function diffSeconds(uint fromTimestamp, uint toTimestamp) internal pure returns (uint _seconds) { + require(fromTimestamp <= toTimestamp); + _seconds = toTimestamp - fromTimestamp; + } +} \ No newline at end of file diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol new file mode 100644 index 000000000..c1c088d29 --- /dev/null +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.4.24; + +import "./ITransferManager.sol"; + +contract VolumeRestrictionTM is ITransferManager { + +} diff --git a/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol b/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol new file mode 100644 index 000000000..b1b799c26 --- /dev/null +++ b/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol @@ -0,0 +1,68 @@ +pragma solidity ^0.4.24; + +import "./VolumeRestrictionTM.sol"; +import "../ModuleFactory.sol"; + +/** + * @title Factory for deploying VolumeRestrictionTM module + */ +contract VolumeRestrictionTMFactory is ModuleFactory { + /** + * @notice Constructor + * @param _polyAddress Address of the polytoken + */ + constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public + ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + { + version = "1.0.0"; + name = "VolumeRestrictionTM"; + title = "Volume Restriction Transfer Manager"; + description = "Manage transfers based on the volume of tokens that transact"; + compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + } + + + /** + * @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), "Sufficent Allowance is not provided"); + address VolumeRestrictionTransferManager = new VolumeRestrictionTM(msg.sender, address(polyToken)); + /*solium-disable-next-line security/no-block-members*/ + emit GenerateModuleFromFactory(address(VolumeRestrictionTransferManager), getName(), address(this), msg.sender, setupCost, now); + return address(VolumeRestrictionTransferManager); + } + + + /** + * @notice Type of the Module factory + */ + function getTypes() external view returns(uint8[]) { + uint8[] memory res = new uint8[](1); + res[0] = 2; + return res; + } + + /** + * @notice Returns the instructions associated with the module + */ + function getInstructions() external view returns(string) { + /*solium-disable-next-line max-len*/ + return ""; /// TODO - need to add the Instruction for module + } + + /** + * @notice Get the tags related to the module factory + */ + function getTags() public view returns(bytes32[]) { + bytes32[] memory availableTags = new bytes32[](2); + availableTags[0] = "Maximum Volume"; + availableTags[1] = "Transfer Restriction"; + availableTags[2] = "Daily Restriction"; + return availableTags; + } + +} \ No newline at end of file From 8adb72d6890dfe3f8f57b9b2f30021c29a6095a4 Mon Sep 17 00:00:00 2001 From: satyam Date: Fri, 16 Nov 2018 18:29:52 +0530 Subject: [PATCH 02/56] function declaration --- .../TransferManager/VolumeRestrictionTM.sol | 136 ++++++++++++++++++ .../VolumeRestrictionTMFactory.sol | 2 +- .../TransferManager/VolumeRestrictionTM.sol | 7 - 3 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol rename contracts/modules/{ => Experimental}/TransferManager/VolumeRestrictionTMFactory.sol (98%) delete mode 100644 contracts/modules/TransferManager/VolumeRestrictionTM.sol diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol new file mode 100644 index 000000000..a4096b0a0 --- /dev/null +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol @@ -0,0 +1,136 @@ +pragma solidity ^0.4.24; + +import "../../TransferManager/ITransferManager.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../../../libraries/BokkyPooBahsDateTimeLibrary.sol"; + +contract VolumeRestrictionTM is ITransferManager { + + using SafeMath for uint256; + + enum RestrictionType { Global, Individual } + enum TokenType { Fixed, Variable} + + struct VolumeRestriction { + uint256 allowedTokens; + uint256 startTime; + uint256 rollingPeriodInDays; + uint256 endTime; + } + + VolumeRestriction fixedGlobalRestriction; + VolumeRestriction variableGlobalRestriction; + + mapping(address => VolumeRestriction[]) internal fixedIndividualRestriction; + mapping(address => VolumeRestriciton[]) internal variableIndividualRestriction; + mapping(address => mapping(uint256 => )) + + mapping(address => bool) public exemptList; + + event ExemptWalletListChanged(address _wallet, bool _change); + + function changeExemptWalletList(address _wallet, bool _change) public withPerm(ADMIN) { + + } + + function addFixedIndividualRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime + ) + public + withPerm(ADMIN) + { + + } + + function addVariableIndividualRestriction( + address _holder, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime + ) + public + withPerm(ADMIN) + { + + } + + function addFixedGlobalRestriction( + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime + ) + public + withPerm(ADMIN) + { + + } + + function addVariableGlobalRestriciton( + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime + ) + public + withPerm(ADMIN) + { + + } + + function modifyFixedIndividualRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime + ) + public + withPerm(ADMIN) + { + + } + + function modifyVariableIndividualRestriction( + address _holder, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime + ) + public + withPerm(ADMIN) + { + + } + + function modifyFixedGlobalRestriction( + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime + ) + public + withPerm(ADMIN) + { + + } + + function modifyVariableGlobalRestriciton( + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime + ) + public + withPerm(ADMIN) + { + + } + +} diff --git a/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol similarity index 98% rename from contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol rename to contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol index b1b799c26..0c1821b17 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.24; import "./VolumeRestrictionTM.sol"; -import "../ModuleFactory.sol"; +import "../../ModuleFactory.sol"; /** * @title Factory for deploying VolumeRestrictionTM module diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol deleted file mode 100644 index c1c088d29..000000000 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ /dev/null @@ -1,7 +0,0 @@ -pragma solidity ^0.4.24; - -import "./ITransferManager.sol"; - -contract VolumeRestrictionTM is ITransferManager { - -} From 055d2acdade9c3fbc6c452ad33b9d5e1378cc973 Mon Sep 17 00:00:00 2001 From: satyam Date: Tue, 20 Nov 2018 16:03:17 +0530 Subject: [PATCH 03/56] initial draft of VRTM --- .../TransferManager/VolumeRestrictionTM.sol | 546 ++++++++++++++++-- .../VolumeRestrictionTMFactory.sol | 2 +- 2 files changed, 498 insertions(+), 50 deletions(-) diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol index a4096b0a0..6fafbf62c 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol @@ -8,129 +8,577 @@ contract VolumeRestrictionTM is ITransferManager { using SafeMath for uint256; - enum RestrictionType { Global, Individual } - enum TokenType { Fixed, Variable} + // permission definition + bytes32 public constant ADMIN = "ADMIN"; + + enum RestrictionType { Fixed, Variable } struct VolumeRestriction { uint256 allowedTokens; + uint256 allowedPercentageOfTokens; uint256 startTime; uint256 rollingPeriodInDays; uint256 endTime; + RestrictionType typeOfRestriction; + } + + struct BucketDetails { + uint256[] timestamps; + uint256 sumOfLastPeriod; + uint256 daysCovered; + uint256 latestTimestampIndex; } - VolumeRestriction fixedGlobalRestriction; - VolumeRestriction variableGlobalRestriction; + uint256 globalSumOfLastPeriod; - mapping(address => VolumeRestriction[]) internal fixedIndividualRestriction; - mapping(address => VolumeRestriciton[]) internal variableIndividualRestriction; - mapping(address => mapping(uint256 => )) + VolumeRestriction globalRestriction; + mapping(address => VolumeRestriction) internal individualRestriction; + // Storing _from => day's timestamp => total amount transact in a day + mapping(address => mapping(uint256 => uint256)) internal bucket; + // Storing the information that used to validate the transaction + mapping(address => BucketDetails) internal bucketToUser; + // List of wallets that are exempted from all the restrictions applied by the this contract mapping(address => bool) public exemptList; + mapping(address => BucketDetails) internal globalBucketToUser; - event ExemptWalletListChanged(address _wallet, bool _change); + event ChangedExemptWalletList(address indexed _wallet, bool _change); + + event AddNewIndividualRestriction( + address indexed _holder, + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); - function changeExemptWalletList(address _wallet, bool _change) public withPerm(ADMIN) { + event ModifyIndividualRestriction( + address indexed _holder, + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); - } + event AddNewGlobalRestriction( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); - function addFixedIndividualRestriction( - address _holder, + event ModifyGlobalRestriction( uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, - uint256 _endTime - ) - public - withPerm(ADMIN) + uint256 _endTime, + uint256 _typeOfRestriction + ); + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + */ + constructor (address _securityToken, address _polyAddress) + public + Module(_securityToken, _polyAddress) { } - function addVariableIndividualRestriction( - address _holder, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime + /** + * @notice Used to verify the transfer/transferFrom transaction and prevent tranaction + * whose volume of tokens will voilate the maximum volume transfer restriction + * @param _from Address of the sender + * @param _to Address of the reciever + * @param _amount The amount of tokens to transfer + * @param _isTransfer Whether or not this is an actual transfer or just a test to see if the tokens would be transferrable + */ + function verifyTransfer(address _from, address _to, uint256 _amount, bytes /*_data*/, bool _isTransfer) public returns (Result) { + if (!paused && _from != address(0) && !exemptList[_from]) { + require(msg.sender == securityToken || !_isTransfer); + return _checkRestriction(_from, _amount, _isTransfer); + } else { + return Result.NA; + } + } + + function _checkRestriction(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { + if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { + BucketDetails memory _bucketDetails = bucketToUser[_from]; + if (_bucketDetails.timestamps[0] == 0) { + return _individualBucketCheck(individualRestriction[_from].startTime, now, _from,individualRestriction[_from], _amount); + } else { + return _individualBucketCheck(_bucketDetails.timestamps[_bucketDetails.latestTimestampIndex], now, _from, individualRestriction[_from], _amount); + } + } else if (now <= globalRestriction.endTime && now >= globalRestriction.startTime) { + // TODO: Add the global bucket check + // Algorithm should be loop to all(who doesn't have the individual restriction) the individualBucketCheck + // and get the amount that was transacted till now. + // Calculate the sum of that amount + new txn amount and compare with allowed percentage or fixed amount + // amount of tokens + _globalBucketCheck(); + } else { + return Result.NA; + } + } + + function _globalBucketCheck() internal { + // TODO: Definition pending + } + + function _individualBucketCheck( + uint256 _fromTimestamp, + uint256 _toTimestamp, + address _from, + VolumeRestriction storage _restriction, + uint256 _amount ) - public - withPerm(ADMIN) + internal + returns (Result) + { + uint256 _diffDays = BokkyPooBahsDateTimeLibrary.diffDays(_fromTimestamp, _toTimestamp); + uint256 i = 0; + BucketDetails storage _bucketDetails = bucketToUser[_from]; + uint256 counter = _bucketDetails.latestTimestampIndex; + uint256 tempTimeStamp = 0; + for (i = 1; i <= _diffDays; i++) { + // calculating the timestamp that will used as an index of the next bucket + // i.e buckets period will be look like this T1 to T2-1, T2 to T3-1 .... + // where T1,T2,T3 are timestamps having 24 hrs difference + tempTimeStamp = _fromTimestamp.add((i - 1) * 1 days); + // Creating the round array + if (counter > _restriction.rollingPeriodInDays -1) { + counter = 0; + } + // This condition is to check whether the first rolling period is covered or not + // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting + // the earlier value at that index + if (_bucketDetails.daysCovered <= _restriction.rollingPeriodInDays) { + _bucketDetails.daysCovered++; + } else { + // temporarily storing the preious value of timestamp at the "counter" index + uint256 _previousTimestamp = _bucketDetails.timestamps[counter]; + // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod + _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.sub(bucket[_from][_previousTimestamp]); + } + // Adding the last amount that is transacted on the tempTimeStamp + _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); + // Storing the passed timestamp in the array + _bucketDetails.timestamps[counter] = tempTimeStamp; + // Assigning the sum of transacted amount on the passed day + bucket[_from][tempTimeStamp] = 0; + counter ++; + } + bool valid; + if (_restriction.typeOfRestriction == RestrictionType.Variable) { + uint256 _allowedAmount = (_restriction.allowedTokens.mul(ISecurityToken(securityToken).totalSupply()))/ 10 ** 18; + valid = _checkValidAmountToTransact(_bucketDetails.sumOfLastPeriod, _amount, _allowedAmount); + } else { + valid = _checkValidAmountToTransact(_bucketDetails.sumOfLastPeriod, _amount, _restriction.allowedTokens); + } + // Storing the index of the latest timestamp from the array of timestamp that is being processed + _bucketDetails.latestTimestampIndex = counter; + if (!valid) { + return Result.INVALID; + } else { + _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod + _amount; + bucket[_from][_bucketDetails.timestamps[counter]] = bucket[_from][_bucketDetails.timestamps[counter]].add(_amount); + return Result.NA; + } + + } + + function _checkValidAmountToTransact( + uint256 _sumOfLastPeriod, + uint256 _amountToTransact, + uint256 _allowedTokens + ) + internal + pure + returns (bool) { + if (_allowedTokens >= _sumOfLastPeriod.add(_amountToTransact)) { + return true; + } else { + return false; + } + } + /** + * @notice Add/Remove wallet address from the exempt list + * @param _wallet Ethereum wallet/contract address that need to be exempted + * @param _change Boolean value used to add (i.e true) or remove (i.e false) from the list + */ + function changeExemptWalletList(address _wallet, bool _change) public withPerm(ADMIN) { + require(_wallet != address(0), "0x0 address not allowed"); + exemptList[_wallet] = _change; + emit ChangedExemptWalletList(_wallet, _change); } - function addFixedGlobalRestriction( + /** + * @notice Use to add the new individual restriction for a given token holder + * @param _holder Address of the token holder, whom restriction will be implied + * @param _allowedTokens Amount of tokens allowed to be trade for a given address. + * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function addIndividualRestriction( + address _holder, uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, - uint256 _endTime + uint256 _endTime, + uint256 _restrictionType ) public withPerm(ADMIN) { + _addIndividualRestriction( + _holder, + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + /** + * @notice Use to add the new individual restriction for multiple token holders + * @param _holders Array of address of the token holders, whom restriction will be implied + * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. + * @param _allowedPercentageOfTokens Array of percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTimes Array of unix timestamps at which restrictions get into effect + * @param _rollingPeriodInDays Array of rolling period in days (Minimum value should be 1 day) + * @param _endTimes Array of unix timestamps at which restriction effects will gets end. + * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function addIndividualRestrictionMulti( + address[] _holders, + uint256[] _allowedTokens, + uint256[] _allowedPercentageOfTokens, + uint256[] _startTimes, + uint256[] _rollingPeriodInDays, + uint256[] _endTimes, + uint256[] _restrictionTypes + ) + public + withPerm(ADMIN) + { + _checkLengthOfArray(_allowedTokens, _allowedPercentageOfTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); + require(_holders.length == _allowedTokens.length, "Array length mismatch"); + for (uint256 i = 0; i < _holders.length; i++) { + _addIndividualRestriction( + _holders[i], + _allowedTokens[i], + _allowedPercentageOfTokens[i], + _startTimes[i], + _rollingPeriodInDays[i], + _endTimes[i], + _restrictionTypes[i] + ); + } } - function addVariableGlobalRestriciton( + /** + * @notice Use to add the new global restriction for all token holder + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function addGlobalRestriction( + uint256 _allowedTokens, uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, - uint256 _endTime - ) + uint256 _endTime, + uint256 _restrictionType + ) public withPerm(ADMIN) - { - + { + require( + globalRestriction.endTime == 0 || globalRestriction.endTime > now, + "Restriction already present" + ); + _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + globalRestriction = VolumeRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + emit AddNewGlobalRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); } - function modifyFixedIndividualRestriction( + /** + * @notice Use to modify the existing individual restriction for a given token holder + * @param _holder Address of the token holder, whom restriction will be implied + * @param _allowedTokens Amount of tokens allowed to be trade for a given address. + * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function modifyIndividualRestriction( address _holder, uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, - uint256 _endTime + uint256 _endTime, + uint256 _restrictionType ) public withPerm(ADMIN) { - + _modifyIndividualRestriction( + _holder, + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); } - function modifyVariableIndividualRestriction( - address _holder, + /** + * @notice Use to modify the existing individual restriction for multiple token holders + * @param _holders Array of address of the token holders, whom restriction will be implied + * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. + * @param _allowedPercentageOfTokens Array of percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTimes Array of unix timestamps at which restrictions get into effect + * @param _rollingPeriodInDays Array of rolling period in days (Minimum value should be 1 day) + * @param _endTimes Array of unix timestamps at which restriction effects will gets end. + * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function modifyIndividualRestrictionMulti( + address[] _holders, + uint256[] _allowedTokens, + uint256[] _allowedPercentageOfTokens, + uint256[] _startTimes, + uint256[] _rollingPeriodInDays, + uint256[] _endTimes, + uint256[] _restrictionTypes + ) + public + withPerm(ADMIN) + { + _checkLengthOfArray(_allowedTokens, _allowedPercentageOfTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); + require(_holders.length == _allowedTokens.length, "Array length mismatch"); + for (uint256 i = 0; i < _holders.length; i++) { + _modifyIndividualRestriction( + _holders[i], + _allowedTokens[i], + _allowedPercentageOfTokens[i], + _startTimes[i], + _rollingPeriodInDays[i], + _endTimes[i], + _restrictionTypes[i] + ); + } + } + + /** + * @notice Use to modify the global restriction for all token holder + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function modifyGlobalRestriction( + uint256 _allowedTokens, uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, - uint256 _endTime - ) + uint256 _endTime, + uint256 _restrictionType + ) public withPerm(ADMIN) - { - + { + require(globalRestriction.startTime > now, "Not allowed to modify"); + _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + globalRestriction = VolumeRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + emit ModifyGlobalRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); } - function modifyFixedGlobalRestriction( + function _modifyIndividualRestriction( + address _holder, uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, - uint256 _endTime + uint256 _endTime, + uint256 _restrictionType ) - public - withPerm(ADMIN) - { + internal + { + _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + require(individualRestriction[_holder].startTime > now, "Not allowed to modify"); + individualRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + bucketToUser[_holder] = BucketDetails(new uint256[](_rollingPeriodInDays), 0, 0, 0); + emit ModifyIndividualRestriction( + _holder, + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); } - function modifyVariableGlobalRestriciton( + function _addIndividualRestriction( + address _holder, + uint256 _allowedTokens, uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, - uint256 _endTime + uint256 _endTime, + uint256 _restrictionType + ) + internal + { + require( + individualRestriction[_holder].endTime == 0 || individualRestriction[_holder].endTime > now, + "Restriction already present" + ); + require(_holder != address(0) && !exemptList[_holder], "Invalid address"); + _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + + individualRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + bucketToUser[_holder] = BucketDetails(new uint256[](_rollingPeriodInDays), 0, 0, 0); + emit AddNewIndividualRestriction( + _holder, + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + + function _checkInputParams( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodDays, + uint256 _endTime, + uint256 _restrictionType ) - public - withPerm(ADMIN) + internal + pure { + require(_restrictionType == 0 || _restrictionType == 1, "Invalid restriction type"); + if (_restrictionType == uint256(RestrictionType.Fixed)) { + require(_allowedTokens > 0, "Invalid value of allowed tokens"); + } else { + require( + _allowedPercentageOfTokens > 0 && _allowedPercentageOfTokens <= 100 * 10 ** 16, + "Percentage of tokens not within (0,100]" + ); + } + require(_startTime > 0 && _endTime > _startTime); + require(_rollingPeriodDays >= 1); + require(BokkyPooBahsDateTimeLibrary.diffDays(_startTime, _endTime) >= _rollingPeriodDays); + } + + function _checkLengthOfArray( + uint256[] _allowedTokens, + uint256[] _allowedPercentageOfTokens, + uint256[] _startTimes, + uint256[] _rollingPeriodInDays, + uint256[] _endTimes, + uint256[] _restrictionTypes + ) + internal + pure + { + require( + _allowedTokens.length == _allowedPercentageOfTokens.length && + _allowedPercentageOfTokens.length == _startTimes.length && + _startTimes.length == _rollingPeriodInDays.length && + _rollingPeriodInDays.length == _endTimes.length && + _endTimes.length == _restrictionTypes.length, + "Array length mismatch" + ); + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() public view returns(bytes4) { + return bytes4(0); + } + /** + * @notice Returns the permissions flag that are associated with Percentage transfer Manager + */ + function getPermissions() public view returns(bytes32[]) { + bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = ADMIN; + return allPermissions; } } diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol index 0c1821b17..5065b42d7 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol @@ -17,7 +17,7 @@ contract VolumeRestrictionTMFactory is ModuleFactory { version = "1.0.0"; name = "VolumeRestrictionTM"; title = "Volume Restriction Transfer Manager"; - description = "Manage transfers based on the volume of tokens that transact"; + description = "Manage transfers based on the volume of tokens that needs to be transact"; compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); } From 6aee7991a782dbd4a0b303e8906e1eca59637e41 Mon Sep 17 00:00:00 2001 From: satyam Date: Tue, 20 Nov 2018 17:08:12 +0530 Subject: [PATCH 04/56] remove BokkyPooBahsDateTimeLibrary from coverage --- .solcover.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.solcover.js b/.solcover.js index 23ac4d3ef..3a6f05092 100644 --- a/.solcover.js +++ b/.solcover.js @@ -4,6 +4,6 @@ module.exports = { copyPackages: ['openzeppelin-solidity'], testCommand: 'node ../node_modules/.bin/truffle test `find test/*.js ! -name a_poly_oracle.js -and ! -name s_v130_to_v140_upgrade.js` --network coverage', deepSkip: true, - skipFiles: ['external', 'flat', 'helpers', 'mocks', 'oracles', 'libraries/KindMath.sol', 'storage', 'modules/Experimental'], + skipFiles: ['external', 'flat', 'helpers', 'mocks', 'oracles', 'libraries/KindMath.sol', 'libraries/BokkyPooBahsDateTimeLibrary.sol', 'storage', 'modules/Experimental'], forceParse: ['mocks', 'oracles', 'modules/Experimental'] }; From f805ad6da04e312a31c93755f83997e821c50182 Mon Sep 17 00:00:00 2001 From: satyam Date: Tue, 20 Nov 2018 17:48:55 +0530 Subject: [PATCH 05/56] test file addition --- test/helpers/createInstances.js | 18 +++- test/z_volume_restriction_tm.js | 163 ++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 test/z_volume_restriction_tm.js diff --git a/test/helpers/createInstances.js b/test/helpers/createInstances.js index 2021d350e..2303e7eed 100644 --- a/test/helpers/createInstances.js +++ b/test/helpers/createInstances.js @@ -30,6 +30,7 @@ const PolyTokenFaucet = artifacts.require("./PolyTokenFaucet.sol"); const DummySTOFactory = artifacts.require("./DummySTOFactory.sol"); const MockBurnFactory = artifacts.require("./MockBurnFactory.sol"); const MockWrongTypeFactory = artifacts.require("./MockWrongTypeFactory.sol"); +const VolumeRestrictionTMFactory = artifacts.require("./VolumeRestrictionTMFactory.sol"); const Web3 = require("web3"); const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port @@ -48,6 +49,7 @@ let I_PercentageTransferManagerFactory; let I_EtherDividendCheckpointFactory; let I_CountTransferManagerFactory; let I_ERC20DividendCheckpointFactory; +let I_VolumeRestrictionTMFactory; let I_GeneralPermissionManagerFactory; let I_GeneralTransferManagerFactory; let I_GeneralTransferManager; @@ -196,7 +198,7 @@ export async function deployGTMAndVerifyed(accountPolymath, MRProxyInstance, pol I_GeneralTransferManagerFactory = await GeneralTransferManagerFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); assert.notEqual( - I_GeneralPermissionManagerFactory.address.valueOf(), + I_GeneralTransferManagerFactory.address.valueOf(), "0x0000000000000000000000000000000000000000", "GeneralPermissionManagerFactory contract was not deployed" ); @@ -206,6 +208,20 @@ export async function deployGTMAndVerifyed(accountPolymath, MRProxyInstance, pol return new Array(I_GeneralTransferManagerFactory); } +export async function deployVRTMAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { + I_VolumeRestrictionTMFactory = await VolumeRestrictionTMFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); + + assert.notEqual( + I_VolumeRestrictionTMFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "VolumeRestrictionTMFactory contract was not deployed" + ); + + // (B) : Register the GeneralDelegateManagerFactory + await registerAndVerifyByMR(I_VolumeRestrictionTMFactory.address, accountPolymath, MRProxyInstance); + return new Array(I_VolumeRestrictionTMFactory); +} + export async function deployCountTMAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { I_CountTransferManagerFactory = await CountTransferManagerFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); diff --git a/test/z_volume_restriction_tm.js b/test/z_volume_restriction_tm.js new file mode 100644 index 000000000..b6748c360 --- /dev/null +++ b/test/z_volume_restriction_tm.js @@ -0,0 +1,163 @@ +import latestTime from './helpers/latestTime'; +import {signData} from './helpers/signData'; +import { pk } from './helpers/testprivateKey'; +import { duration, promisifyLogWatch, latestBlock } from './helpers/utils'; +import { takeSnapshot, increaseTime, revertToSnapshot } from './helpers/time'; +import { catchRevert } from "./helpers/exceptions"; +import { setUpPolymathNetwork, deployVRTMAndVerifyed } from "./helpers/createInstances"; + +const SecurityToken = artifacts.require('./SecurityToken.sol'); +const GeneralTransferManager = artifacts.require('./GeneralTransferManager.sol'); +const VolumeRestrictionTM = artifacts.require('./VolumeRestrictionTM.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('VolumeRestrictionTransferManager', accounts => { + + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let token_owner_pk; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + let account_delegate; + let account_delegate2; + let account_delegate3; + // investor Details + let fromTime = latestTime(); + let toTime = latestTime(); + let expiryTime = toTime + duration.days(15); + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let I_VolumeRestrictionTMFactory; + let P_VolumeRestrictionTMFactory; + let I_SecurityTokenRegistryProxy; + let P_VolumeRestrictionTM; + let I_GeneralTransferManagerFactory; + let I_VolumeRestrictionTM; + let I_GeneralTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_DummySTOFactory; + let I_STFactory; + let I_SecurityToken; + let I_MRProxied; + let I_STRProxied; + let I_PolyToken; + let I_PolymathRegistry; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + const delegateDetails = "Hello I am legit delegate"; + + // 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; + token_owner_pk = pk.account_1; + + account_investor1 = accounts[8]; + account_investor2 = accounts[9]; + account_delegate = accounts[7]; + account_delegate2 = accounts[6]; + account_delegate3 = accounts[5]; + + // Step 1: Deploy the genral PM ecosystem + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied + ] = instances; + + // STEP 5: Deploy the VolumeRestrictionTMFactory + [I_VolumeRestrictionTMFactory] = await deployVRTMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + // STEP 6: Deploy the VolumeRestrictionTMFactory + [P_VolumeRestrictionTMFactory] = await deployVRTMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500")); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistryProxy ${I_ModuleRegistryProxy.address} + ModuleRegistry: ${I_ModuleRegistry.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + VolumeRestrictionTMFactory: ${I_VolumeRestrictionTMFactory.address} + ----------------------------------------------------------------------------- + `); + }); + + 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._types[0].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.getModulesByType(2))[0]; + I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + }); + }); + + describe("Attach the VRTM", async() => { + it("Deploy the VRTM and attach with the ST", async()=> { + + }) + }) +}); \ No newline at end of file From c3de389f6b3034b7493e7883063e3b741403a8cb Mon Sep 17 00:00:00 2001 From: satyam Date: Wed, 21 Nov 2018 17:18:10 +0530 Subject: [PATCH 06/56] allow read using verifyTransfer() --- .../TransferManager/VolumeRestrictionTM.sol | 85 +++++++++++++------ test/z_volume_restriction_tm.js | 79 ++++++++++++++++- 2 files changed, 135 insertions(+), 29 deletions(-) diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol index 6fafbf62c..138f1a34a 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol @@ -33,7 +33,7 @@ contract VolumeRestrictionTM is ITransferManager { VolumeRestriction globalRestriction; - mapping(address => VolumeRestriction) internal individualRestriction; + mapping(address => VolumeRestriction) public individualRestriction; // Storing _from => day's timestamp => total amount transact in a day mapping(address => mapping(uint256 => uint256)) internal bucket; // Storing the information that used to validate the transaction @@ -114,10 +114,27 @@ contract VolumeRestrictionTM is ITransferManager { function _checkRestriction(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { BucketDetails memory _bucketDetails = bucketToUser[_from]; + uint256 _diffDays; if (_bucketDetails.timestamps[0] == 0) { - return _individualBucketCheck(individualRestriction[_from].startTime, now, _from,individualRestriction[_from], _amount); + _diffDays = BokkyPooBahsDateTimeLibrary.diffDays(individualRestriction[_from].startTime, now); + return _individualBucketCheck( + individualRestriction[_from].startTime, + _diffDays, + _from, + individualRestriction[_from], + _amount, + _isTransfer + ); } else { - return _individualBucketCheck(_bucketDetails.timestamps[_bucketDetails.latestTimestampIndex], now, _from, individualRestriction[_from], _amount); + _diffDays = BokkyPooBahsDateTimeLibrary.diffDays(_bucketDetails.timestamps[_bucketDetails.latestTimestampIndex], now); + return _individualBucketCheck( + _bucketDetails.timestamps[_bucketDetails.latestTimestampIndex], + _diffDays, + _from, + individualRestriction[_from], + _amount, + _isTransfer + ); } } else if (now <= globalRestriction.endTime && now >= globalRestriction.startTime) { // TODO: Add the global bucket check @@ -136,28 +153,31 @@ contract VolumeRestrictionTM is ITransferManager { } function _individualBucketCheck( - uint256 _fromTimestamp, - uint256 _toTimestamp, + uint256 _fromTime, + uint256 _diffDays, address _from, - VolumeRestriction storage _restriction, - uint256 _amount + VolumeRestriction _restriction, + uint256 _amount, + bool _isTransfer ) internal returns (Result) { - uint256 _diffDays = BokkyPooBahsDateTimeLibrary.diffDays(_fromTimestamp, _toTimestamp); + BucketDetails memory _bucketDetails = bucketToUser[_from]; + uint256[] memory passedTimestamps = new uint256[](_diffDays); + uint256[] memory counters = new uint256[](_diffDays); + // using the existing memory variable instead of creating new one (avoiding stack too deep error) + // uint256 counter = _bucketDetails.latestTimestampIndex; uint256 i = 0; - BucketDetails storage _bucketDetails = bucketToUser[_from]; - uint256 counter = _bucketDetails.latestTimestampIndex; - uint256 tempTimeStamp = 0; + bool valid; for (i = 1; i <= _diffDays; i++) { // calculating the timestamp that will used as an index of the next bucket // i.e buckets period will be look like this T1 to T2-1, T2 to T3-1 .... // where T1,T2,T3 are timestamps having 24 hrs difference - tempTimeStamp = _fromTimestamp.add((i - 1) * 1 days); + _fromTime = _fromTime.add(1 days); // Creating the round array - if (counter > _restriction.rollingPeriodInDays -1) { - counter = 0; + if (_bucketDetails.latestTimestampIndex > _restriction.rollingPeriodInDays -1) { + _bucketDetails.latestTimestampIndex = 0; } // This condition is to check whether the first rolling period is covered or not // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting @@ -165,33 +185,42 @@ contract VolumeRestrictionTM is ITransferManager { if (_bucketDetails.daysCovered <= _restriction.rollingPeriodInDays) { _bucketDetails.daysCovered++; } else { - // temporarily storing the preious value of timestamp at the "counter" index - uint256 _previousTimestamp = _bucketDetails.timestamps[counter]; + // temporarily storing the previous value of timestamp at the "_bucketDetails.latestTimestampIndex" index + uint256 _previousTimestamp = _bucketDetails.timestamps[_bucketDetails.latestTimestampIndex]; // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.sub(bucket[_from][_previousTimestamp]); } - // Adding the last amount that is transacted on the tempTimeStamp + // Adding the last amount that is transacted on the _fromTime _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); // Storing the passed timestamp in the array - _bucketDetails.timestamps[counter] = tempTimeStamp; - // Assigning the sum of transacted amount on the passed day - bucket[_from][tempTimeStamp] = 0; - counter ++; + _bucketDetails.timestamps[_bucketDetails.latestTimestampIndex] = _fromTime; + // Storing all those timestamps whose total transacted value is 0 + passedTimestamps[i] = _fromTime; + counters[i] = _bucketDetails.latestTimestampIndex; + _bucketDetails.latestTimestampIndex ++; } - bool valid; if (_restriction.typeOfRestriction == RestrictionType.Variable) { uint256 _allowedAmount = (_restriction.allowedTokens.mul(ISecurityToken(securityToken).totalSupply()))/ 10 ** 18; valid = _checkValidAmountToTransact(_bucketDetails.sumOfLastPeriod, _amount, _allowedAmount); } else { valid = _checkValidAmountToTransact(_bucketDetails.sumOfLastPeriod, _amount, _restriction.allowedTokens); } - // Storing the index of the latest timestamp from the array of timestamp that is being processed - _bucketDetails.latestTimestampIndex = counter; if (!valid) { return Result.INVALID; } else { - _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod + _amount; - bucket[_from][_bucketDetails.timestamps[counter]] = bucket[_from][_bucketDetails.timestamps[counter]].add(_amount); + if (_isTransfer) { + for (i = 0; i < passedTimestamps.length; i++) { + // Assigning the sum of transacted amount on the passed day + bucket[_from][passedTimestamps[i]] = 0; + // To save gas by not assigning a memory timestamp array to storage timestamp array. + bucketToUser[_from].timestamps[counters[i]] = _bucketDetails.timestamps[counters[i]]; + } + bucketToUser[_from].sumOfLastPeriod = _bucketDetails.sumOfLastPeriod + _amount; + bucketToUser[_from].daysCovered = _bucketDetails.daysCovered; + // Storing the index of the latest timestamp from the array of timestamp that is being processed + bucketToUser[_from].latestTimestampIndex = _bucketDetails.latestTimestampIndex; + bucket[_from][_bucketDetails.timestamps[_bucketDetails.latestTimestampIndex]] = bucket[_from][_bucketDetails.timestamps[_bucketDetails.latestTimestampIndex]].add(_amount); + } return Result.NA; } @@ -540,7 +569,7 @@ contract VolumeRestrictionTM is ITransferManager { ); } require(_startTime > 0 && _endTime > _startTime); - require(_rollingPeriodDays >= 1); + require(_rollingPeriodDays >= 1 && _rollingPeriodDays <= 365); require(BokkyPooBahsDateTimeLibrary.diffDays(_startTime, _endTime) >= _rollingPeriodDays); } @@ -563,7 +592,7 @@ contract VolumeRestrictionTM is ITransferManager { _endTimes.length == _restrictionTypes.length, "Array length mismatch" ); - } + } /** * @notice This function returns the signature of configure function diff --git a/test/z_volume_restriction_tm.js b/test/z_volume_restriction_tm.js index b6748c360..14a55cd62 100644 --- a/test/z_volume_restriction_tm.js +++ b/test/z_volume_restriction_tm.js @@ -81,6 +81,7 @@ contract('VolumeRestrictionTransferManager', accounts => { account_investor1 = accounts[8]; account_investor2 = accounts[9]; + account_investor3 = accounts[4]; account_delegate = accounts[7]; account_delegate2 = accounts[6]; account_delegate3 = accounts[5]; @@ -157,7 +158,83 @@ contract('VolumeRestrictionTransferManager', accounts => { describe("Attach the VRTM", async() => { it("Deploy the VRTM and attach with the ST", async()=> { + let tx = await I_SecurityToken.addModule(I_VolumeRestrictionTMFactory.address, 0, 0, 0, {from: token_owner }); + assert.equal(tx.logs[2].args._moduleFactory, I_VolumeRestrictionTMFactory.address); + assert.equal( + web3.utils.toUtf8(tx.logs[2].args._name), + "VolumeRestrictionTM", + "VolumeRestrictionTMFactory doesn not added"); + I_VolumeRestrictionTM = VolumeRestrictionTM.at(tx.logs[2].args._module); + }); + + it("Transfer some tokens to different account", async() => { + // Add tokens in to the whitelist + await I_GeneralTransferManager.modifyWhitelistMulti( + [account_investor1, account_investor2, account_investor3], + [latestTime(), latestTime(), latestTime()], + [latestTime(), latestTime(), latestTime()], + [latestTime() + duration.days(30), latestTime() + duration.days(30), latestTime() + duration.days(30)], + [true, true, true], + { + from: token_owner + } + ); + + // Mint some tokens and transferred to whitelisted addresses + await I_SecurityToken.mint(account_investor1, web3.utils.toWei("40", "ether"), {from: token_owner}); + await I_SecurityToken.mint(account_investor2, web3.utils.toWei("30", "ether"), {from: token_owner}); + + // Check the balance of the investors + let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); + let bal2 = await I_SecurityToken.balanceOf.call(account_investor2); + // Verifying the balances + assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 40); + assert.equal(web3.utils.fromWei((bal2.toNumber()).toString()), 30); + + }); + + it("Should transfer the tokens freely without any restriction", async() => { + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('5', 'ether'), { from: account_investor1 }); + let bal1 = await I_SecurityToken.balanceOf.call(account_investor3); + // Verifying the balances + assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 5); + }); + }) + + describe("Test for the addIndividualRestriction", async() => { + + it("Should add the restriction succesfully", async() => { + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei("5"), + 0, + latestTime() + duration.seconds(2), + 3, + latestTime() + duration.days(10), + 0, + { + from: token_owner + } + ); + + assert.equal(tx.logs[0].args._holder, account_investor1); + assert.equal(tx.logs[0].args._typeOfRestriction, 0); + console.log(await I_VolumeRestrictionTM.individualRestriction.call(account_investor1)); + }); - }) + // it("Should not successfully transact the tokens -- failed because volume is above the limit", async() => { + // await catchRevert( + // I_SecurityToken.transfer(account_investor3, web3.utils.toWei("6"), { from: account_investor1}) + // ); + // }); + + // it("Should successfully transact the tokens after 1 and half days", async() => { + // await increaseTime(duration.days(1.5)); + // await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor1}); + // // Check the balance of the investors + // let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); + // // Verifying the balances + // assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 34); + // }); }) }); \ No newline at end of file From eddd5a53b732118e366849c317582e08eda1fa27 Mon Sep 17 00:00:00 2001 From: satyam Date: Thu, 22 Nov 2018 18:59:45 +0530 Subject: [PATCH 07/56] refactoring bucket function --- .../TransferManager/VolumeRestrictionTM.sol | 140 +++++++----- test/z_volume_restriction_tm.js | 204 ++++++++++++++++-- 2 files changed, 269 insertions(+), 75 deletions(-) diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol index 138f1a34a..e9f2c9d9c 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol @@ -98,11 +98,10 @@ contract VolumeRestrictionTM is ITransferManager { * @notice Used to verify the transfer/transferFrom transaction and prevent tranaction * whose volume of tokens will voilate the maximum volume transfer restriction * @param _from Address of the sender - * @param _to Address of the reciever * @param _amount The amount of tokens to transfer * @param _isTransfer Whether or not this is an actual transfer or just a test to see if the tokens would be transferrable */ - function verifyTransfer(address _from, address _to, uint256 _amount, bytes /*_data*/, bool _isTransfer) public returns (Result) { + function verifyTransfer(address _from, address /*_to */, uint256 _amount, bytes /*_data*/, bool _isTransfer) public returns (Result) { if (!paused && _from != address(0) && !exemptList[_from]) { require(msg.sender == securityToken || !_isTransfer); return _checkRestriction(_from, _amount, _isTransfer); @@ -169,73 +168,89 @@ contract VolumeRestrictionTM is ITransferManager { // using the existing memory variable instead of creating new one (avoiding stack too deep error) // uint256 counter = _bucketDetails.latestTimestampIndex; uint256 i = 0; - bool valid; - for (i = 1; i <= _diffDays; i++) { - // calculating the timestamp that will used as an index of the next bucket - // i.e buckets period will be look like this T1 to T2-1, T2 to T3-1 .... - // where T1,T2,T3 are timestamps having 24 hrs difference - _fromTime = _fromTime.add(1 days); - // Creating the round array - if (_bucketDetails.latestTimestampIndex > _restriction.rollingPeriodInDays -1) { - _bucketDetails.latestTimestampIndex = 0; - } - // This condition is to check whether the first rolling period is covered or not - // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting - // the earlier value at that index - if (_bucketDetails.daysCovered <= _restriction.rollingPeriodInDays) { - _bucketDetails.daysCovered++; - } else { - // temporarily storing the previous value of timestamp at the "_bucketDetails.latestTimestampIndex" index - uint256 _previousTimestamp = _bucketDetails.timestamps[_bucketDetails.latestTimestampIndex]; - // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod - _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.sub(bucket[_from][_previousTimestamp]); + if (_diffDays != 0) { + for (i = 0; i < _diffDays; i++) { + // calculating the timestamp that will used as an index of the next bucket + // i.e buckets period will be look like this T1 to T2-1, T2 to T3-1 .... + // where T1,T2,T3 are timestamps having 24 hrs difference + _fromTime = _fromTime.add(1 days); + // Creating the round array + if (_bucketDetails.latestTimestampIndex == _restriction.rollingPeriodInDays -1) { + _bucketDetails.latestTimestampIndex = 0; + } + // This condition is to check whether the first rolling period is covered or not + // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting + // the earlier value at that index + if (_bucketDetails.daysCovered < _restriction.rollingPeriodInDays -1) { + _bucketDetails.daysCovered++; + } else { + // temporarily storing the previous value of timestamp at the "_bucketDetails.latestTimestampIndex" index + uint256 _previousTimestamp = _bucketDetails.timestamps[_bucketDetails.latestTimestampIndex]; + // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod + _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.sub(bucket[_from][_previousTimestamp]); + } + // Adding the last amount that is transacted on the _fromTime + //_bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); + // increasing the value of latest timestamp Index + _bucketDetails.latestTimestampIndex++; + // Storing the passed timestamp in the array + _bucketDetails.timestamps[_bucketDetails.latestTimestampIndex] = _fromTime; + // Storing all those timestamps whose total transacted value is 0 + passedTimestamps[i] = _fromTime; + counters[i] = _bucketDetails.latestTimestampIndex; } - // Adding the last amount that is transacted on the _fromTime - _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); - // Storing the passed timestamp in the array - _bucketDetails.timestamps[_bucketDetails.latestTimestampIndex] = _fromTime; - // Storing all those timestamps whose total transacted value is 0 - passedTimestamps[i] = _fromTime; - counters[i] = _bucketDetails.latestTimestampIndex; - _bucketDetails.latestTimestampIndex ++; - } - if (_restriction.typeOfRestriction == RestrictionType.Variable) { - uint256 _allowedAmount = (_restriction.allowedTokens.mul(ISecurityToken(securityToken).totalSupply()))/ 10 ** 18; - valid = _checkValidAmountToTransact(_bucketDetails.sumOfLastPeriod, _amount, _allowedAmount); - } else { - valid = _checkValidAmountToTransact(_bucketDetails.sumOfLastPeriod, _amount, _restriction.allowedTokens); - } - if (!valid) { - return Result.INVALID; - } else { + } + if (_checkValidAmountToTransact(_bucketDetails.sumOfLastPeriod, _amount, _restriction)) { if (_isTransfer) { - for (i = 0; i < passedTimestamps.length; i++) { - // Assigning the sum of transacted amount on the passed day - bucket[_from][passedTimestamps[i]] = 0; - // To save gas by not assigning a memory timestamp array to storage timestamp array. - bucketToUser[_from].timestamps[counters[i]] = _bucketDetails.timestamps[counters[i]]; - } - bucketToUser[_from].sumOfLastPeriod = _bucketDetails.sumOfLastPeriod + _amount; - bucketToUser[_from].daysCovered = _bucketDetails.daysCovered; - // Storing the index of the latest timestamp from the array of timestamp that is being processed - bucketToUser[_from].latestTimestampIndex = _bucketDetails.latestTimestampIndex; - bucket[_from][_bucketDetails.timestamps[_bucketDetails.latestTimestampIndex]] = bucket[_from][_bucketDetails.timestamps[_bucketDetails.latestTimestampIndex]].add(_amount); + _updateStorage(passedTimestamps, counters, _from, _fromTime, _diffDays, _amount, _bucketDetails); } return Result.NA; - } + } + + return Result.INVALID; } + event LogA(uint256 _sum, uint256 _amount, uint256 _allowedAmount); + function _updateStorage(uint256[] passedTimestamps, uint256[] counters, address _from, uint256 _fromTime, uint256 _diffDays, uint256 _amount, BucketDetails _bucketDetails) internal { + if (_diffDays != 0) { + for (uint256 i = 0; i < passedTimestamps.length; i++) { + // Assigning the sum of transacted amount on the passed day + bucket[_from][passedTimestamps[i]] = 0; + // To save gas by not assigning a memory timestamp array to storage timestamp array. + bucketToUser[_from].timestamps[counters[i]] = _bucketDetails.timestamps[counters[i]]; + } + bucketToUser[_from].daysCovered = _bucketDetails.daysCovered; + // Storing the index of the latest timestamp from the array of timestamp that is being processed + bucketToUser[_from].latestTimestampIndex = _bucketDetails.latestTimestampIndex; + } + // This condition is the works only when the transaction performed just after the startTime (_diffDays == 0) + if (_bucketDetails.timestamps[0] == 0) { + bucketToUser[_from].timestamps[_bucketDetails.latestTimestampIndex] = _fromTime; + } + bucketToUser[_from].sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(_amount); + // Re-using the local variable to avoid stack too deep error + _fromTime = bucketToUser[_from].timestamps[_bucketDetails.latestTimestampIndex]; + bucket[_from][_fromTime] = bucket[_from][_fromTime].add(_amount); + } function _checkValidAmountToTransact( uint256 _sumOfLastPeriod, uint256 _amountToTransact, - uint256 _allowedTokens + VolumeRestriction _restriction ) internal - pure + view returns (bool) - { - if (_allowedTokens >= _sumOfLastPeriod.add(_amountToTransact)) { + { + uint256 _allowedAmount = 0; + if (_restriction.typeOfRestriction == RestrictionType.Variable) { + _allowedAmount = (_restriction.allowedTokens.mul(ISecurityToken(securityToken).totalSupply()))/ 10 ** 18; + } else { + _allowedAmount = _restriction.allowedTokens; + } + emit LogA(_sumOfLastPeriod, _amountToTransact, _allowedAmount); + // Validation on the amount to transact + if (_allowedAmount >= _sumOfLastPeriod.add(_amountToTransact)) { return true; } else { return false; @@ -594,6 +609,19 @@ contract VolumeRestrictionTM is ITransferManager { ); } + function getBucketDetailsToUser(address _user) external view returns(uint256[], uint256, uint256, uint256) { + return( + bucketToUser[_user].timestamps, + bucketToUser[_user].sumOfLastPeriod, + bucketToUser[_user].daysCovered, + bucketToUser[_user].latestTimestampIndex + ); + } + + function getTotalTradeByuser(address _user, uint256 _at) external view returns(uint256) { + return bucket[_user][_at]; + } + /** * @notice This function returns the signature of configure function */ diff --git a/test/z_volume_restriction_tm.js b/test/z_volume_restriction_tm.js index 14a55cd62..83f6add28 100644 --- a/test/z_volume_restriction_tm.js +++ b/test/z_volume_restriction_tm.js @@ -136,7 +136,7 @@ contract('VolumeRestrictionTransferManager', accounts => { 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 }); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, true, { 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"); @@ -197,7 +197,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('5', 'ether'), { from: account_investor1 }); let bal1 = await I_SecurityToken.balanceOf.call(account_investor3); // Verifying the balances - assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 5); + assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 5); }); }) @@ -206,7 +206,7 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should add the restriction succesfully", async() => { let tx = await I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, - web3.utils.toWei("5"), + web3.utils.toWei("12"), 0, latestTime() + duration.seconds(2), 3, @@ -219,22 +219,188 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.equal(tx.logs[0].args._holder, account_investor1); assert.equal(tx.logs[0].args._typeOfRestriction, 0); - console.log(await I_VolumeRestrictionTM.individualRestriction.call(account_investor1)); }); - // it("Should not successfully transact the tokens -- failed because volume is above the limit", async() => { - // await catchRevert( - // I_SecurityToken.transfer(account_investor3, web3.utils.toWei("6"), { from: account_investor1}) - // ); - // }); - - // it("Should successfully transact the tokens after 1 and half days", async() => { - // await increaseTime(duration.days(1.5)); - // await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor1}); - // // Check the balance of the investors - // let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); - // // Verifying the balances - // assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 34); - // }); - }) + it("Should not successfully transact the tokens -- failed because volume is above the limit", async() => { + await increaseTime(duration.seconds(10)); + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei("13"), { from: account_investor1}) + ); + }); + + it("Should succesfully transact the tokens just after the starttime", async() => { + // Check the transfer will be valid or not by calling the verifyTransfer() directly by using _isTransfer = false + let result = await I_VolumeRestrictionTM.verifyTransfer.call(account_investor1, account_investor3, web3.utils.toWei('.3'), "0x0", false); + console.log(result); + assert.equal(result.toNumber(), 1); + // Perform the transaction + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('.3'), {from: account_investor1}); + // Check the balance of the investors + let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); + // Verifying the balances + assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 34.7); + + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + console.log('\n'); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])} + `); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Days Covered : ${data[2].toNumber()} + Last Timestamp Index : ${data[3].toNumber()} + `); + }) + + it("Should successfully transact the tokens after 1 and half days", async() => { + await increaseTime(duration.days(1.5)); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor1}); + // Check the balance of the investors + let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); + // Verifying the balances + assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 33.7); + + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])} + `); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Days Covered : ${data[2].toNumber()} + Last Timestamp Index : ${data[3].toNumber()} + `); + }); + + it("Should successfully transact more tokens on the same day (Fuzz test)", async() => { + // Check the balance of the investors + let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); + let totalAmountTransacted = 0; + for (let i = 0; i < 10; i++) { + let amount = Math.random(); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor1}); + console.log(`${i}: Restricted investor 1 able to transact ${amount} tokens to investor 3`); + totalAmountTransacted += amount; + } + + // Check the balance of the investors + let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); + // Verifying the balances + assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); + + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])} + `); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Days Covered : ${data[2].toNumber()} + Last Timestamp Index : ${data[3].toNumber()} + `); + }); + + it("Should successfully transfer the tokens after half days-- should increase the day covered by 1", async() => { + await increaseTime(duration.days(.5)); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2"), {from: account_investor1}); + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])} + `); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Days Covered : ${data[2].toNumber()} + Last Timestamp Index : ${data[3].toNumber()} + `); + assert.equal(data[2].toNumber(), 2); + }); + + it("Should successfully transfer the tokens in the last day of rolling period", async() => { + // Check the balance of the investors + let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); + let totalAmountTransacted = 0; + for (let i = 0; i < 3; i++) { + let amount = Math.random(); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor1}); + console.log(`${i}: Restricted investor 1 able to transact ${amount} tokens to investor 3`); + totalAmountTransacted += amount; + } + // Check the balance of the investors + let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); + // Verifying the balances + assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); + + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])} + `); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Days Covered : ${data[2].toNumber()} + Last Timestamp Index : ${data[3].toNumber()} + `); + }); + + it("Should fail to transact the tokens more than the allowed tokens in a rolling period", async() => { + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + let minimumAmount = new BigNumber(12).times(new BigNumber(10).pow(18)).minus(data[1]); + let testAmount = minimumAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))) + await catchRevert( + I_SecurityToken.transfer(account_investor3, testAmount, {from: account_investor1}) + ); + }); + + it("Should fail to buy tokens in the new rolling period --failed because amount is more than last two timestamps", async() => { + await increaseTime(duration.days(2)); + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei("10"), {from: account_investor1}) + ); + }); + + it("Should transfer the tokens in a new rolling period", async() => { + let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + for (let i = 0; i < oldData[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${oldData[0][i].toNumber()} + Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])} + `); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((oldData[1].toNumber()).toString())} + Days Covered : ${oldData[2].toNumber()} + Last Timestamp Index : ${oldData[3].toNumber()} + `); + + let tx = await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(".3"), {from: account_investor1}); + console.log('\n'); + console.log(tx.logs); + let newData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + for (let i = 0; i < newData[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${newData[0][i].toNumber()} + Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])} + `); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((newData[1].toNumber()).toString())} + Days Covered : ${newData[2].toNumber()} + Last Timestamp Index : ${newData[3].toNumber()} + `); + assert.notEqual(oldData[0][0].toNumber(), newData[0][0].toNumber()); + assert.notEqual(oldData[0][1].toNumber(), newData[0][1].toNumber()); + }); + }); }); \ No newline at end of file From 3455d948bc77345c9b2df03a8505412ca2457b61 Mon Sep 17 00:00:00 2001 From: satyam Date: Sat, 24 Nov 2018 03:10:59 +0530 Subject: [PATCH 08/56] global + individual check added --- .../TransferManager/VolumeRestrictionTM.sol | 339 ++++++++++-------- test/z_volume_restriction_tm.js | 96 +++-- 2 files changed, 249 insertions(+), 186 deletions(-) diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol index e9f2c9d9c..b1e80875c 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol @@ -13,6 +13,8 @@ contract VolumeRestrictionTM is ITransferManager { enum RestrictionType { Fixed, Variable } + enum Scope { Global, Individual } + struct VolumeRestriction { uint256 allowedTokens; uint256 allowedPercentageOfTokens; @@ -25,22 +27,19 @@ contract VolumeRestrictionTM is ITransferManager { struct BucketDetails { uint256[] timestamps; uint256 sumOfLastPeriod; - uint256 daysCovered; - uint256 latestTimestampIndex; } - uint256 globalSumOfLastPeriod; - VolumeRestriction globalRestriction; mapping(address => VolumeRestriction) public individualRestriction; - // Storing _from => day's timestamp => total amount transact in a day + // Storing _from => day's timestamp => total amount transact in a day --individual mapping(address => mapping(uint256 => uint256)) internal bucket; // Storing the information that used to validate the transaction mapping(address => BucketDetails) internal bucketToUser; // List of wallets that are exempted from all the restrictions applied by the this contract mapping(address => bool) public exemptList; - mapping(address => BucketDetails) internal globalBucketToUser; + + address[] globalUsers; event ChangedExemptWalletList(address indexed _wallet, bool _change); @@ -110,153 +109,6 @@ contract VolumeRestrictionTM is ITransferManager { } } - function _checkRestriction(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { - if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { - BucketDetails memory _bucketDetails = bucketToUser[_from]; - uint256 _diffDays; - if (_bucketDetails.timestamps[0] == 0) { - _diffDays = BokkyPooBahsDateTimeLibrary.diffDays(individualRestriction[_from].startTime, now); - return _individualBucketCheck( - individualRestriction[_from].startTime, - _diffDays, - _from, - individualRestriction[_from], - _amount, - _isTransfer - ); - } else { - _diffDays = BokkyPooBahsDateTimeLibrary.diffDays(_bucketDetails.timestamps[_bucketDetails.latestTimestampIndex], now); - return _individualBucketCheck( - _bucketDetails.timestamps[_bucketDetails.latestTimestampIndex], - _diffDays, - _from, - individualRestriction[_from], - _amount, - _isTransfer - ); - } - } else if (now <= globalRestriction.endTime && now >= globalRestriction.startTime) { - // TODO: Add the global bucket check - // Algorithm should be loop to all(who doesn't have the individual restriction) the individualBucketCheck - // and get the amount that was transacted till now. - // Calculate the sum of that amount + new txn amount and compare with allowed percentage or fixed amount - // amount of tokens - _globalBucketCheck(); - } else { - return Result.NA; - } - } - - function _globalBucketCheck() internal { - // TODO: Definition pending - } - - function _individualBucketCheck( - uint256 _fromTime, - uint256 _diffDays, - address _from, - VolumeRestriction _restriction, - uint256 _amount, - bool _isTransfer - ) - internal - returns (Result) - { - BucketDetails memory _bucketDetails = bucketToUser[_from]; - uint256[] memory passedTimestamps = new uint256[](_diffDays); - uint256[] memory counters = new uint256[](_diffDays); - // using the existing memory variable instead of creating new one (avoiding stack too deep error) - // uint256 counter = _bucketDetails.latestTimestampIndex; - uint256 i = 0; - if (_diffDays != 0) { - for (i = 0; i < _diffDays; i++) { - // calculating the timestamp that will used as an index of the next bucket - // i.e buckets period will be look like this T1 to T2-1, T2 to T3-1 .... - // where T1,T2,T3 are timestamps having 24 hrs difference - _fromTime = _fromTime.add(1 days); - // Creating the round array - if (_bucketDetails.latestTimestampIndex == _restriction.rollingPeriodInDays -1) { - _bucketDetails.latestTimestampIndex = 0; - } - // This condition is to check whether the first rolling period is covered or not - // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting - // the earlier value at that index - if (_bucketDetails.daysCovered < _restriction.rollingPeriodInDays -1) { - _bucketDetails.daysCovered++; - } else { - // temporarily storing the previous value of timestamp at the "_bucketDetails.latestTimestampIndex" index - uint256 _previousTimestamp = _bucketDetails.timestamps[_bucketDetails.latestTimestampIndex]; - // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod - _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.sub(bucket[_from][_previousTimestamp]); - } - // Adding the last amount that is transacted on the _fromTime - //_bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); - // increasing the value of latest timestamp Index - _bucketDetails.latestTimestampIndex++; - // Storing the passed timestamp in the array - _bucketDetails.timestamps[_bucketDetails.latestTimestampIndex] = _fromTime; - // Storing all those timestamps whose total transacted value is 0 - passedTimestamps[i] = _fromTime; - counters[i] = _bucketDetails.latestTimestampIndex; - } - } - if (_checkValidAmountToTransact(_bucketDetails.sumOfLastPeriod, _amount, _restriction)) { - if (_isTransfer) { - _updateStorage(passedTimestamps, counters, _from, _fromTime, _diffDays, _amount, _bucketDetails); - } - return Result.NA; - } - - return Result.INVALID; - - } - event LogA(uint256 _sum, uint256 _amount, uint256 _allowedAmount); - function _updateStorage(uint256[] passedTimestamps, uint256[] counters, address _from, uint256 _fromTime, uint256 _diffDays, uint256 _amount, BucketDetails _bucketDetails) internal { - if (_diffDays != 0) { - for (uint256 i = 0; i < passedTimestamps.length; i++) { - // Assigning the sum of transacted amount on the passed day - bucket[_from][passedTimestamps[i]] = 0; - // To save gas by not assigning a memory timestamp array to storage timestamp array. - bucketToUser[_from].timestamps[counters[i]] = _bucketDetails.timestamps[counters[i]]; - } - bucketToUser[_from].daysCovered = _bucketDetails.daysCovered; - // Storing the index of the latest timestamp from the array of timestamp that is being processed - bucketToUser[_from].latestTimestampIndex = _bucketDetails.latestTimestampIndex; - } - // This condition is the works only when the transaction performed just after the startTime (_diffDays == 0) - if (_bucketDetails.timestamps[0] == 0) { - bucketToUser[_from].timestamps[_bucketDetails.latestTimestampIndex] = _fromTime; - } - bucketToUser[_from].sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(_amount); - // Re-using the local variable to avoid stack too deep error - _fromTime = bucketToUser[_from].timestamps[_bucketDetails.latestTimestampIndex]; - bucket[_from][_fromTime] = bucket[_from][_fromTime].add(_amount); - } - - function _checkValidAmountToTransact( - uint256 _sumOfLastPeriod, - uint256 _amountToTransact, - VolumeRestriction _restriction - ) - internal - view - returns (bool) - { - uint256 _allowedAmount = 0; - if (_restriction.typeOfRestriction == RestrictionType.Variable) { - _allowedAmount = (_restriction.allowedTokens.mul(ISecurityToken(securityToken).totalSupply()))/ 10 ** 18; - } else { - _allowedAmount = _restriction.allowedTokens; - } - emit LogA(_sumOfLastPeriod, _amountToTransact, _allowedAmount); - // Validation on the amount to transact - if (_allowedAmount >= _sumOfLastPeriod.add(_amountToTransact)) { - return true; - } else { - return false; - } - } - /** * @notice Add/Remove wallet address from the exempt list * @param _wallet Ethereum wallet/contract address that need to be exempted @@ -491,6 +343,179 @@ contract VolumeRestrictionTM is ITransferManager { ); } + function _checkRestriction(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { + uint256 _fromTimestamp; + BucketDetails memory _bucketDetails = bucketToUser[_from]; + if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { + if (_bucketDetails.timestamps.length == 0) { + _fromTimestamp = individualRestriction[_from].startTime; + } else { + _fromTimestamp = _bucketDetails.timestamps[_bucketDetails.timestamps.length - 1]; + } + return _bucketCheck( + _fromTimestamp, + BokkyPooBahsDateTimeLibrary.diffDays(_fromTimestamp, now), + _from, + _amount, + _isTransfer, + Scope.Individual, + individualRestriction[_from] + ); + } else if (now <= globalRestriction.endTime && now >= globalRestriction.startTime) { + // Algorithm should be loop to all(who doesn't have the individual restriction) the individualBucketCheck + // and get the amount that was transacted till now. + // Calculate the sum of that amount + new txn amount and compare with allowed percentage or fixed amount + // amount of tokens + if (_bucketDetails.timestamps.length == 0) { + _fromTimestamp = globalRestriction.startTime; + } else { + _fromTimestamp = _bucketDetails.timestamps[_bucketDetails.timestamps.length - 1]; + } + return _bucketCheck( + _fromTimestamp, + BokkyPooBahsDateTimeLibrary.diffDays(_fromTimestamp, now), + _from, + _amount, + _isTransfer, + Scope.Global, + globalRestriction + ); + } else { + return Result.NA; + } + } + + function _bucketCheck( + uint256 _fromTime, + uint256 _diffDays, + address _from, + uint256 _amount, + bool _isTransfer, + Scope _scope, + VolumeRestriction _restriction + ) + internal + returns (Result) + { + BucketDetails memory _bucketDetails = bucketToUser[_from]; + uint256[] memory passedTimestamps = new uint256[](_diffDays); + // using the existing memory variable instead of creating new one (avoiding stack too deep error) + uint256 counter = _bucketDetails.timestamps.length; + uint256 i = 0; + if (_diffDays != 0) { + for (i = 0; i < _diffDays; i++) { + // calculating the timestamp that will used as an index of the next bucket + // i.e buckets period will be look like this T1 to T2-1, T2 to T3-1 .... + // where T1,T2,T3 are timestamps having 24 hrs difference + _fromTime = _fromTime.add(1 days); + + // This condition is to check whether the first rolling period is covered or not + // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting + // the earlier value at that index + if (counter >= _restriction.rollingPeriodInDays) { + // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod + _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. + sub(bucket[_from][_bucketDetails.timestamps[counter.sub(_restriction.rollingPeriodInDays)]]); + } + + // Adding the last amount that is transacted on the _fromTime + //_bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); + // Storing all those timestamps whose total transacted value is 0 + passedTimestamps[i] = _fromTime; + counter++; + } + } + if (_scope == Scope.Global) { + i = _getTotalSumOfLastPeriod(_bucketDetails.sumOfLastPeriod, _from); + } else { + i = _bucketDetails.sumOfLastPeriod; + } + if (_checkValidAmountToTransact(i, _amount, _restriction)) { + if (_isTransfer) { + _updateStorage( + passedTimestamps, + _from, + _fromTime, + _amount, + _diffDays, + _scope, + _bucketDetails.sumOfLastPeriod + ); + } + return Result.NA; + } + return Result.INVALID; + } + + function _getTotalSumOfLastPeriod(uint256 _sumOfuser, address _from) internal view returns(uint256 globalSumOfLastPeriod) { + for (uint256 i = 0; i < globalUsers.length; i++) { + if (globalUsers[i] == _from) { + globalSumOfLastPeriod.add(_sumOfuser); + } else { + uint256 _latestTimestampIndex = bucketToUser[globalUsers[i]].timestamps.length - 1; + uint256 _latestTimestamp = bucketToUser[globalUsers[i]].timestamps[_latestTimestampIndex]; + if (BokkyPooBahsDateTimeLibrary.diffDays(_latestTimestamp, now) < globalRestriction.rollingPeriodInDays) + globalSumOfLastPeriod.add(bucketToUser[globalUsers[i]].sumOfLastPeriod); + } + } + } + + function _checkValidAmountToTransact( + uint256 _sumOfLastPeriod, + uint256 _amountToTransact, + VolumeRestriction _restriction + ) + internal + view + returns (bool) + { + uint256 _allowedAmount = 0; + if (_restriction.typeOfRestriction == RestrictionType.Variable) { + _allowedAmount = (_restriction.allowedPercentageOfTokens.mul(ISecurityToken(securityToken).totalSupply()))/ 10 ** 18; + } else { + _allowedAmount = _restriction.allowedTokens; + } + // Validation on the amount to transact + if (_allowedAmount >= _sumOfLastPeriod.add(_amountToTransact)) { + return true; + } else { + return false; + } + } + + function _updateStorage( + uint256[] passedTimestamps, + address _from, + uint256 _fromTime, + uint256 _amount, + uint256 _diffDays, + Scope _scope, + uint256 _sumOfLastPeriod + ) + internal + { + if (bucketToUser[_from].timestamps.length == 0 && _scope == Scope.Global) { + globalUsers.push(_from); + } + if (_diffDays != 0) { + for (uint256 i = 0; i < passedTimestamps.length; i++) { + // Assigning the sum of transacted amount on the passed day + // bucket[_from][passedTimestamps[i]] = 0; + // To save gas by not assigning a memory timestamp array to storage timestamp array. + bucketToUser[_from].timestamps.push(passedTimestamps[i]); + } + } + // This condition is the works only when the transaction performed just after the startTime (_diffDays == 0) + if (bucketToUser[_from].timestamps.length == 0) { + bucketToUser[_from].timestamps.push(_fromTime); + } + bucketToUser[_from].sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + + // Re-using the local variable to avoid stack too deep error + _fromTime = bucketToUser[_from].timestamps[bucketToUser[_from].timestamps.length -1]; + bucket[_from][_fromTime] = bucket[_from][_fromTime].add(_amount); + } + function _modifyIndividualRestriction( address _holder, uint256 _allowedTokens, @@ -513,7 +538,6 @@ contract VolumeRestrictionTM is ITransferManager { _endTime, RestrictionType(_restrictionType) ); - bucketToUser[_holder] = BucketDetails(new uint256[](_rollingPeriodInDays), 0, 0, 0); emit ModifyIndividualRestriction( _holder, _allowedTokens, @@ -551,7 +575,6 @@ contract VolumeRestrictionTM is ITransferManager { _endTime, RestrictionType(_restrictionType) ); - bucketToUser[_holder] = BucketDetails(new uint256[](_rollingPeriodInDays), 0, 0, 0); emit AddNewIndividualRestriction( _holder, _allowedTokens, @@ -609,12 +632,10 @@ contract VolumeRestrictionTM is ITransferManager { ); } - function getBucketDetailsToUser(address _user) external view returns(uint256[], uint256, uint256, uint256) { + function getBucketDetailsToUser(address _user) external view returns(uint256[], uint256) { return( bucketToUser[_user].timestamps, - bucketToUser[_user].sumOfLastPeriod, - bucketToUser[_user].daysCovered, - bucketToUser[_user].latestTimestampIndex + bucketToUser[_user].sumOfLastPeriod ); } diff --git a/test/z_volume_restriction_tm.js b/test/z_volume_restriction_tm.js index 83f6add28..138068b8f 100644 --- a/test/z_volume_restriction_tm.js +++ b/test/z_volume_restriction_tm.js @@ -68,6 +68,8 @@ contract('VolumeRestrictionTransferManager', accounts => { const transferManagerKey = 2; const stoKey = 3; + let tempAmount = new BigNumber(0); + // Initial fee for ticker registry and security token registry const initRegFee = web3.utils.toWei("250"); @@ -245,13 +247,13 @@ contract('VolumeRestrictionTransferManager', accounts => { for (let i = 0; i < data[0].length; i++) { console.log(` Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} `); } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Days Covered : ${data[2].toNumber()} - Last Timestamp Index : ${data[3].toNumber()} + Last Timestamp Index : ${data[0].length -1} `); }) @@ -267,13 +269,13 @@ contract('VolumeRestrictionTransferManager', accounts => { for (let i = 0; i < data[0].length; i++) { console.log(` Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} `); } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Days Covered : ${data[2].toNumber()} - Last Timestamp Index : ${data[3].toNumber()} + Last Timestamp Index : ${data[0].length -1} `); }); @@ -297,13 +299,13 @@ contract('VolumeRestrictionTransferManager', accounts => { for (let i = 0; i < data[0].length; i++) { console.log(` Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} `); } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Days Covered : ${data[2].toNumber()} - Last Timestamp Index : ${data[3].toNumber()} + Last Timestamp Index : ${data[0].length - 1} `); }); @@ -314,15 +316,15 @@ contract('VolumeRestrictionTransferManager', accounts => { for (let i = 0; i < data[0].length; i++) { console.log(` Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} `); } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Days Covered : ${data[2].toNumber()} - Last Timestamp Index : ${data[3].toNumber()} + Last Timestamp Index : ${data[0].length - 1} `); - assert.equal(data[2].toNumber(), 2); + assert.equal(data[0].length - 1, 2); }); it("Should successfully transfer the tokens in the last day of rolling period", async() => { @@ -344,13 +346,13 @@ contract('VolumeRestrictionTransferManager', accounts => { for (let i = 0; i < data[0].length; i++) { console.log(` Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} `); } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Days Covered : ${data[2].toNumber()} - Last Timestamp Index : ${data[3].toNumber()} + Last Timestamp Index : ${data[0].length - 1} `); }); @@ -372,35 +374,75 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should transfer the tokens in a new rolling period", async() => { let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + let firstDayAmount; + tempAmount = new BigNumber(0); for (let i = 0; i < oldData[0].length; i++) { console.log(` Timestamps array index ${i}: ${oldData[0][i].toNumber()} - Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])) + .dividedBy(new BigNumber(10).pow(18))} `); + if (i != 2) { + firstDayAmount = await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][0]); + tempAmount = tempAmount.plus(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])); + } } + console.log(` SumOfLastPeriod : ${web3.utils.fromWei((oldData[1].toNumber()).toString())} - Days Covered : ${oldData[2].toNumber()} - Last Timestamp Index : ${oldData[3].toNumber()} + Last Timestamp Index : ${oldData[0].length -1} `); + + let currentDayAmount = firstDayAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))); + let tx = await I_SecurityToken.transfer(account_investor3, currentDayAmount, {from: account_investor1}); + tempAmount = tempAmount.minus(currentDayAmount); - let tx = await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(".3"), {from: account_investor1}); console.log('\n'); - console.log(tx.logs); let newData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - for (let i = 0; i < newData[0].length; i++) { + for (let i = 2; i < newData[0].length; i++) { console.log(` Timestamps array index ${i}: ${newData[0][i].toNumber()} - Total Trade till now: ${await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) + .dividedBy(new BigNumber(10).pow(18))} `); } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((newData[1].toNumber()).toString())} - Days Covered : ${newData[2].toNumber()} - Last Timestamp Index : ${newData[3].toNumber()} + Last Timestamp Index : ${newData[0].length -1} + `); + assert.notEqual(oldData[0][0].toNumber(), newData[0][3].toNumber()); + assert.notEqual(oldData[0][1].toNumber(), newData[0][4].toNumber()); + }); + + it("Should transfer the more tokens on the same day", async() => { + // Check the balance of the investors + let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); + console.log(tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber()); + await I_SecurityToken.transfer(account_investor3, tempAmount, {from: account_investor1}); + + // Check the balance of the investors + let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); + // Verifying the balances + assert.closeTo( + (balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), + tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber(), + 0.01 + ); + + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + for (let i = 2; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length -1} `); - assert.notEqual(oldData[0][0].toNumber(), newData[0][0].toNumber()); - assert.notEqual(oldData[0][1].toNumber(), newData[0][1].toNumber()); }); + + }); }); \ No newline at end of file From 9f1970d4feb328ccb850074cc428daf0c7d85b9b Mon Sep 17 00:00:00 2001 From: satyam Date: Sun, 25 Nov 2018 16:55:11 +0530 Subject: [PATCH 09/56] finalised the logic of sc --- .../TransferManager/VolumeRestrictionTM.sol | 464 +++++++++++--- test/z_volume_restriction_tm.js | 566 +++++++++++++++++- 2 files changed, 921 insertions(+), 109 deletions(-) diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol index b1e80875c..60d244758 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol @@ -13,8 +13,6 @@ contract VolumeRestrictionTM is ITransferManager { enum RestrictionType { Fixed, Variable } - enum Scope { Global, Individual } - struct VolumeRestriction { uint256 allowedTokens; uint256 allowedPercentageOfTokens; @@ -26,11 +24,16 @@ contract VolumeRestrictionTM is ITransferManager { struct BucketDetails { uint256[] timestamps; - uint256 sumOfLastPeriod; + uint256 sumOfLastPeriod; // It is the sum of transacted amount within the last rollingPeriodDays } - VolumeRestriction globalRestriction; - + // Global restriction that applies to all token holders + VolumeRestriction public globalRestriction; + // Daily global restriction that applies to all token holders (Total ST traded daily is restricted) + VolumeRestriction public dailyGlobalRestriction; + // Variable stores the data matrix for the globa restrictions + BucketDetails internal globalBucketDetails; + // Restriction stored corresponds to a particular token holder mapping(address => VolumeRestriction) public individualRestriction; // Storing _from => day's timestamp => total amount transact in a day --individual mapping(address => mapping(uint256 => uint256)) internal bucket; @@ -38,11 +41,12 @@ contract VolumeRestrictionTM is ITransferManager { mapping(address => BucketDetails) internal bucketToUser; // List of wallets that are exempted from all the restrictions applied by the this contract mapping(address => bool) public exemptList; + // Store the amount of tokens get transacted in a day + mapping(uint256 => uint256) public globalBucket; - address[] globalUsers; - + // Emit when the token holder is added/removed from the exemption list event ChangedExemptWalletList(address indexed _wallet, bool _change); - + // Emit when the new individual restriction is added corresponds to new token holders event AddNewIndividualRestriction( address indexed _holder, uint256 _allowedTokens, @@ -52,7 +56,7 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _endTime, uint256 _typeOfRestriction ); - + // Emit when the individual restriction is modified for a given address event ModifyIndividualRestriction( address indexed _holder, uint256 _allowedTokens, @@ -62,8 +66,8 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _endTime, uint256 _typeOfRestriction ); - - event AddNewGlobalRestriction( + // Emit when the new global restriction is added + event AddGlobalRestriction( uint256 _allowedTokens, uint256 _allowedPercentageOfTokens, uint256 _startTime, @@ -71,7 +75,16 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _endTime, uint256 _typeOfRestriction ); - + // Emit when the new daily (global) restriction is added + event AddDailyGlobalRestriction( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when global restriction get modified event ModifyGlobalRestriction( uint256 _allowedTokens, uint256 _allowedPercentageOfTokens, @@ -80,6 +93,21 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _endTime, uint256 _typeOfRestriction ); + // Emit when daily global restriction get modified + event ModifyDailyGlobalRestriction( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when the individual restriction gets removed + event IndividualRestrictionRemoved(address _user); + // Emit when the global restriction gets removed + event GlobalRestrictionRemoved(); + // Emit when the daily global restriction gets removed + event DailyGlobalRestrictionRemoved(); /** * @notice Constructor @@ -101,12 +129,19 @@ contract VolumeRestrictionTM is ITransferManager { * @param _isTransfer Whether or not this is an actual transfer or just a test to see if the tokens would be transferrable */ function verifyTransfer(address _from, address /*_to */, uint256 _amount, bytes /*_data*/, bool _isTransfer) public returns (Result) { + // If `_from` is present in the exemptionList or it is `0x0` address then it will not follow the vol restriction if (!paused && _from != address(0) && !exemptList[_from]) { - require(msg.sender == securityToken || !_isTransfer); - return _checkRestriction(_from, _amount, _isTransfer); - } else { - return Result.NA; + // Function must only be called by the associated security token if _isTransfer == true + require(msg.sender == securityToken || !_isTransfer, "Sender is not the owner"); + // Checking the individual restriction if the `_from` comes in the individual category + if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { + return _individualRestrictionCheck(_from, _amount, _isTransfer); + // If the `_from` doesn't fall under the individual category. It will processed with in the global category automatically + } else if (globalRestriction.endTime >= now && globalRestriction.startTime <= now) { + return _globalRestrictionCheck(_from, _amount, _isTransfer); + } } + return Result.NA; } /** @@ -207,7 +242,7 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _endTime, uint256 _restrictionType ) - public + external withPerm(ADMIN) { require( @@ -223,7 +258,7 @@ contract VolumeRestrictionTM is ITransferManager { _endTime, RestrictionType(_restrictionType) ); - emit AddNewGlobalRestriction( + emit AddGlobalRestriction( _allowedTokens, _allowedPercentageOfTokens, _startTime, @@ -233,6 +268,85 @@ contract VolumeRestrictionTM is ITransferManager { ); } + /** + * @notice Use to add the new global daily restriction for all token holder + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function addDailyGlobalRestriction( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + require( + dailyGlobalRestriction.endTime == 0 || dailyGlobalRestriction.endTime > now, + "Restriction already present" + ); + _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, 1, _endTime, _restrictionType); + dailyGlobalRestriction = VolumeRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + 1, + _endTime, + RestrictionType(_restrictionType) + ); + emit AddDailyGlobalRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + 1, + _endTime, + _restrictionType + ); + } + + /** + * @notice use to remove the individual restriction for a given address + * @param _user Address of the user + */ + function removeIndividualRestriction(address _user) external withPerm(ADMIN) { + _removeIndividualRestriction(_user); + } + + /** + * @notice use to remove the individual restriction for a given address + * @param _users Array of address of the user + */ + function removeIndividualRestrictionMulti(address[] _users) external withPerm(ADMIN) { + for (uint256 i = 0; i < _users.length; i++) { + _removeIndividualRestriction(_users[i]); + } + } + + /** + * @notice Use to remove the global restriction + */ + function removeGlobalRestriction() external withPerm(ADMIN) { + require(globalRestriction.endTime != 0, "Not present"); + globalRestriction = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); + globalBucketDetails.timestamps.length = 0; + globalBucketDetails.sumOfLastPeriod = 0; + emit GlobalRestrictionRemoved(); + } + + /** + * @notice Use to remove the daily global restriction + */ + function removeDailyGlobalRestriction() external withPerm(ADMIN) { + require(dailyGlobalRestriction.endTime != 0, "Not present"); + dailyGlobalRestriction = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); + emit DailyGlobalRestrictionRemoved(); + } + /** * @notice Use to modify the existing individual restriction for a given token holder * @param _holder Address of the token holder, whom restriction will be implied @@ -320,7 +434,7 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _endTime, uint256 _restrictionType ) - public + external withPerm(ADMIN) { require(globalRestriction.startTime > now, "Not allowed to modify"); @@ -343,63 +457,139 @@ contract VolumeRestrictionTM is ITransferManager { ); } - function _checkRestriction(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { - uint256 _fromTimestamp; - BucketDetails memory _bucketDetails = bucketToUser[_from]; - if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { - if (_bucketDetails.timestamps.length == 0) { - _fromTimestamp = individualRestriction[_from].startTime; - } else { - _fromTimestamp = _bucketDetails.timestamps[_bucketDetails.timestamps.length - 1]; - } - return _bucketCheck( - _fromTimestamp, - BokkyPooBahsDateTimeLibrary.diffDays(_fromTimestamp, now), - _from, - _amount, - _isTransfer, - Scope.Individual, - individualRestriction[_from] - ); - } else if (now <= globalRestriction.endTime && now >= globalRestriction.startTime) { - // Algorithm should be loop to all(who doesn't have the individual restriction) the individualBucketCheck - // and get the amount that was transacted till now. - // Calculate the sum of that amount + new txn amount and compare with allowed percentage or fixed amount - // amount of tokens - if (_bucketDetails.timestamps.length == 0) { - _fromTimestamp = globalRestriction.startTime; - } else { - _fromTimestamp = _bucketDetails.timestamps[_bucketDetails.timestamps.length - 1]; - } - return _bucketCheck( - _fromTimestamp, - BokkyPooBahsDateTimeLibrary.diffDays(_fromTimestamp, now), - _from, - _amount, - _isTransfer, - Scope.Global, - globalRestriction - ); + /** + * @notice Use to modify the daily global restriction for all token holder + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function modifyDailyGlobalRestriction( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + require(dailyGlobalRestriction.startTime > now, "Not allowed to modify"); + _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + dailyGlobalRestriction = VolumeRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + emit ModifyDailyGlobalRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + + /** + * @notice Internal function have a logic to validate the txn amount with global restriction + */ + function _globalRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { + uint256[] memory timestamps = globalBucketDetails.timestamps; + uint256 fromTimestamp; + uint256 sumOfLastPeriod = 0; + if (timestamps.length == 0) { + // It will execute when the txn is performed first time after the addition of global restriction + fromTimestamp = globalRestriction.startTime; } else { + // picking up the preivous timestamp + fromTimestamp = timestamps[timestamps.length -1]; + } + // Calculating the difference of days + uint256 diffDays = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now); + uint256[] memory passedTimestamps = new uint256[](diffDays); + // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod + // re-using the local variables to avoid the stack too deep error. + (sumOfLastPeriod, fromTimestamp, passedTimestamps) = _bucketCheck( + fromTimestamp, + diffDays, + _from, + globalBucketDetails, + true, + globalRestriction + ); + // Total amout that is transacted uptill now for `fromTimestamp` day + uint256 txSumOfDay = globalBucket[fromTimestamp]; + // Global transaction check + if (_globalTxCheck(sumOfLastPeriod, txSumOfDay, _amount)) { + // allow modification in storage when `_isTransfer` equals true + if(_isTransfer) { + // update the global storage + _updateGlobalStorage( + passedTimestamps, + fromTimestamp, + _amount, + diffDays, + sumOfLastPeriod + ); + } return Result.NA; } + return Result.INVALID; } + /** + * @notice Internal function to update the state variables related to global restriction + */ + function _updateGlobalStorage( + uint256[] passedTimestamps, + uint256 _fromTime, + uint256 _amount, + uint256 _diffDays, + uint256 _sumOfLastPeriod + ) + internal + { + if (_diffDays != 0) { + for (uint256 i = 0; i < passedTimestamps.length; i++) { + // Add the timestamp that is already passed + globalBucketDetails.timestamps.push(passedTimestamps[i]); + } + } + // This condition is the works only when the transaction performed just after the startTime (_diffDays == 0) + if (globalBucketDetails.timestamps.length == 0) { + globalBucketDetails.timestamps.push(_fromTime); + } + if(_amount != 0) { + // updating the sumOfLastPeriod + globalBucketDetails.sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + + // Re-using the local variable to avoid stack too deep error + _fromTime = globalBucketDetails.timestamps[globalBucketDetails.timestamps.length -1]; + globalBucket[_fromTime] = globalBucket[_fromTime].add(_amount); + } + } + + /// Internal function for the bucket check function _bucketCheck( uint256 _fromTime, uint256 _diffDays, address _from, - uint256 _amount, - bool _isTransfer, - Scope _scope, + BucketDetails _bucketDetails, + bool _isGlobal, VolumeRestriction _restriction ) internal - returns (Result) + view + returns (uint256, uint256, uint256[]) { - BucketDetails memory _bucketDetails = bucketToUser[_from]; uint256[] memory passedTimestamps = new uint256[](_diffDays); - // using the existing memory variable instead of creating new one (avoiding stack too deep error) uint256 counter = _bucketDetails.timestamps.length; uint256 i = 0; if (_diffDays != 0) { @@ -413,51 +603,97 @@ contract VolumeRestrictionTM is ITransferManager { // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting // the earlier value at that index if (counter >= _restriction.rollingPeriodInDays) { - // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod - _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. - sub(bucket[_from][_bucketDetails.timestamps[counter.sub(_restriction.rollingPeriodInDays)]]); + if (_isGlobal) { + // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod + _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. + sub(globalBucket[_bucketDetails.timestamps[counter.sub(_restriction.rollingPeriodInDays)]]); + } else { + // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod + _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. + sub(bucket[_from][_bucketDetails.timestamps[counter.sub(_restriction.rollingPeriodInDays)]]); + } + } - // Adding the last amount that is transacted on the _fromTime + // Adding the last amount that is transacted on the `_fromTime` not actually doing it but left written to understand + // the alogrithm //_bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); // Storing all those timestamps whose total transacted value is 0 passedTimestamps[i] = _fromTime; counter++; } } - if (_scope == Scope.Global) { - i = _getTotalSumOfLastPeriod(_bucketDetails.sumOfLastPeriod, _from); + return (_bucketDetails.sumOfLastPeriod, _fromTime, passedTimestamps); + } + + /** + * @notice Internal function used to validate the transaction for a given address + * If it validates then it also update the storage corressponds to the individual restriction + */ + function _individualRestrictionCheck( + address _from, + uint256 _amount, + bool _isTransfer + ) + internal + returns(Result) + { + uint256 fromTimestamp; + uint256 sumOfLastPeriod = 0; + if (bucketToUser[_from].timestamps.length == 0) { + // It will execute when the txn is performed first time after the addition of individual restriction + fromTimestamp = individualRestriction[_from].startTime; } else { - i = _bucketDetails.sumOfLastPeriod; + // Picking up the last timestamp + fromTimestamp = bucketToUser[_from].timestamps[bucketToUser[_from].timestamps.length -1]; } - if (_checkValidAmountToTransact(i, _amount, _restriction)) { + // Calculating the difference of days + uint256 diffDays = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now); + uint256[] memory passedTimestamps = new uint256[](diffDays); + // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod + // re-using the local variables to avoid the stack too deep error. + (sumOfLastPeriod, fromTimestamp, passedTimestamps) = _bucketCheck( + fromTimestamp, + diffDays, + _from, + bucketToUser[_from], + false, + individualRestriction[_from] + ); + if (_checkValidAmountToTransact(sumOfLastPeriod, _amount, individualRestriction[_from])) { if (_isTransfer) { - _updateStorage( + _updateIndividualStorage( passedTimestamps, _from, - _fromTime, + fromTimestamp, _amount, - _diffDays, - _scope, - _bucketDetails.sumOfLastPeriod + diffDays, + sumOfLastPeriod ); } return Result.NA; } - return Result.INVALID; + return Result.INVALID; } - function _getTotalSumOfLastPeriod(uint256 _sumOfuser, address _from) internal view returns(uint256 globalSumOfLastPeriod) { - for (uint256 i = 0; i < globalUsers.length; i++) { - if (globalUsers[i] == _from) { - globalSumOfLastPeriod.add(_sumOfuser); - } else { - uint256 _latestTimestampIndex = bucketToUser[globalUsers[i]].timestamps.length - 1; - uint256 _latestTimestamp = bucketToUser[globalUsers[i]].timestamps[_latestTimestampIndex]; - if (BokkyPooBahsDateTimeLibrary.diffDays(_latestTimestamp, now) < globalRestriction.rollingPeriodInDays) - globalSumOfLastPeriod.add(bucketToUser[globalUsers[i]].sumOfLastPeriod); - } + + function _globalTxCheck( + uint256 _sumOfLastPeriod, + uint256 _dailyAmount, + uint256 _amount + ) + internal + view + returns(bool) + { + // Checking whether the global day restriction is added or not if yes then calculate + // the total amount get traded on a particular day (~ _fromTime) + if (!_checkValidAmountToTransact(_dailyAmount, _amount, dailyGlobalRestriction) && + now <= dailyGlobalRestriction.endTime && now >= dailyGlobalRestriction.startTime + ) { + return false; } + return _checkValidAmountToTransact(_sumOfLastPeriod, _amount, globalRestriction); } function _checkValidAmountToTransact( @@ -483,25 +719,19 @@ contract VolumeRestrictionTM is ITransferManager { } } - function _updateStorage( + function _updateIndividualStorage( uint256[] passedTimestamps, address _from, uint256 _fromTime, uint256 _amount, uint256 _diffDays, - Scope _scope, uint256 _sumOfLastPeriod ) internal { - if (bucketToUser[_from].timestamps.length == 0 && _scope == Scope.Global) { - globalUsers.push(_from); - } if (_diffDays != 0) { for (uint256 i = 0; i < passedTimestamps.length; i++) { - // Assigning the sum of transacted amount on the passed day - // bucket[_from][passedTimestamps[i]] = 0; - // To save gas by not assigning a memory timestamp array to storage timestamp array. + // Add the timestamp that is already passed bucketToUser[_from].timestamps.push(passedTimestamps[i]); } } @@ -509,11 +739,23 @@ contract VolumeRestrictionTM is ITransferManager { if (bucketToUser[_from].timestamps.length == 0) { bucketToUser[_from].timestamps.push(_fromTime); } - bucketToUser[_from].sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + if(_amount != 0) { + bucketToUser[_from].sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + + // Re-using the local variable to avoid stack too deep error + _fromTime = bucketToUser[_from].timestamps[bucketToUser[_from].timestamps.length -1]; + bucket[_from][_fromTime] = bucket[_from][_fromTime].add(_amount); + } + + } - // Re-using the local variable to avoid stack too deep error - _fromTime = bucketToUser[_from].timestamps[bucketToUser[_from].timestamps.length -1]; - bucket[_from][_fromTime] = bucket[_from][_fromTime].add(_amount); + function _removeIndividualRestriction(address _user) internal { + require(_user != address(0), "Invalid address"); + require(individualRestriction[_user].endTime != 0, "Not present"); + individualRestriction[_user] = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); + bucketToUser[_user].timestamps.length = 0; + bucketToUser[_user].sumOfLastPeriod = 0; + emit IndividualRestrictionRemoved(_user); } function _modifyIndividualRestriction( @@ -607,6 +849,7 @@ contract VolumeRestrictionTM is ITransferManager { ); } require(_startTime > 0 && _endTime > _startTime); + // Maximum limit for the rollingPeriod is 365 days require(_rollingPeriodDays >= 1 && _rollingPeriodDays <= 365); require(BokkyPooBahsDateTimeLibrary.diffDays(_startTime, _endTime) >= _rollingPeriodDays); } @@ -632,6 +875,12 @@ contract VolumeRestrictionTM is ITransferManager { ); } + /** + * @notice Use to get the bucket details for a given address + * @param _user Address of the token holder for whom the bucket details has queried + * @return uint256 Array of the timestamps + * @return uint256 sumOfLastPeriod + */ function getBucketDetailsToUser(address _user) external view returns(uint256[], uint256) { return( bucketToUser[_user].timestamps, @@ -639,10 +888,29 @@ contract VolumeRestrictionTM is ITransferManager { ); } + /** + * @notice Use to get the volume of token that being traded at a particular day (`_at` + 24 hours) for a given user + * @param _user Address of the token holder + * @param _at Timestamp + */ function getTotalTradeByuser(address _user, uint256 _at) external view returns(uint256) { return bucket[_user][_at]; } + /** + * @notice Use to get the global bucket details + * @return uint256 Array of timestamps + * @return uint256 sumOfLastPeriod + * @return uint256 Total amount traded on the latest timestamp + */ + function getGlobalBucketDetails() external view returns(uint256[], uint256, uint256) { + return( + globalBucketDetails.timestamps, + globalBucketDetails.sumOfLastPeriod, + globalBucket[globalBucketDetails.timestamps[globalBucketDetails.timestamps.length - 1]] + ); + } + /** * @notice This function returns the signature of configure function */ diff --git a/test/z_volume_restriction_tm.js b/test/z_volume_restriction_tm.js index 138068b8f..298fc18c8 100644 --- a/test/z_volume_restriction_tm.js +++ b/test/z_volume_restriction_tm.js @@ -69,6 +69,9 @@ contract('VolumeRestrictionTransferManager', accounts => { const stoKey = 3; let tempAmount = new BigNumber(0); + let tempArray = new Array(); + let tempArrayVariable = new Array(); + let tempArrayGlobal = new Array(); // Initial fee for ticker registry and security token registry const initRegFee = web3.utils.toWei("250"); @@ -84,6 +87,7 @@ contract('VolumeRestrictionTransferManager', accounts => { account_investor1 = accounts[8]; account_investor2 = accounts[9]; account_investor3 = accounts[4]; + account_investor4 = accounts[3]; account_delegate = accounts[7]; account_delegate2 = accounts[6]; account_delegate3 = accounts[5]; @@ -250,38 +254,52 @@ contract('VolumeRestrictionTransferManager', accounts => { Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) .dividedBy(new BigNumber(10).pow(18))} `); + assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), 0.3); + assert.equal(data[0][i].toNumber(), (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber()) } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} Last Timestamp Index : ${data[0].length -1} `); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 0.3); + assert.equal(data[0].length -1, 0); + tempArray.push(0.3); }) it("Should successfully transact the tokens after 1 and half days", async() => { await increaseTime(duration.days(1.5)); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor1}); // Check the balance of the investors let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); // Verifying the balances assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 33.7); - + tempArray.push(1); let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + for (let i = 0; i < data[0].length; i++) { console.log(` Timestamps array index ${i}: ${data[0][i].toNumber()} Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} + .dividedBy(new BigNumber(10).pow(18)).toNumber()} `); + assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i]); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} Last Timestamp Index : ${data[0].length -1} `); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 1.3); + assert.equal(data[0].length -1, 1); }); it("Should successfully transact more tokens on the same day (Fuzz test)", async() => { // Check the balance of the investors let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); let totalAmountTransacted = 0; for (let i = 0; i < 10; i++) { let amount = Math.random(); @@ -294,44 +312,63 @@ contract('VolumeRestrictionTransferManager', accounts => { let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); // Verifying the balances assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); - + tempArray[1] = tempArray[1] + totalAmountTransacted; let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + for (let i = 0; i < data[0].length; i++) { console.log(` Timestamps array index ${i}: ${data[0][i].toNumber()} Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) .dividedBy(new BigNumber(10).pow(18))} `); + assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} Last Timestamp Index : ${data[0].length - 1} `); + assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), 1.3 + totalAmountTransacted, 0.000001); + assert.equal(data[0].length -1, 1); }); it("Should successfully transfer the tokens after half days-- should increase the day covered by 1", async() => { + let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); await increaseTime(duration.days(.5)); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2"), {from: account_investor1}); let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + tempArray.push(2); + for (let i = 0; i < data[0].length; i++) { console.log(` Timestamps array index ${i}: ${data[0][i].toNumber()} Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) .dividedBy(new BigNumber(10).pow(18))} `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) + assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); } + console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} Last Timestamp Index : ${data[0].length - 1} `); assert.equal(data[0].length - 1, 2); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), + (oldData[1].plus(new BigNumber(2).times(new BigNumber(10).pow(18)))) + .dividedBy(new BigNumber(10).pow(18)).toNumber()); }); it("Should successfully transfer the tokens in the last day of rolling period", async() => { // Check the balance of the investors let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); let totalAmountTransacted = 0; - for (let i = 0; i < 3; i++) { + for (let i = 0; i < 2; i++) { let amount = Math.random(); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor1}); console.log(`${i}: Restricted investor 1 able to transact ${amount} tokens to investor 3`); @@ -341,7 +378,7 @@ contract('VolumeRestrictionTransferManager', accounts => { let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); // Verifying the balances assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); - + tempArray[2] = tempArray[2] + totalAmountTransacted; let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); for (let i = 0; i < data[0].length; i++) { console.log(` @@ -349,11 +386,18 @@ contract('VolumeRestrictionTransferManager', accounts => { Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) .dividedBy(new BigNumber(10).pow(18))} `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) + assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); } + console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} Last Timestamp Index : ${data[0].length - 1} `); + assert.equal(data[0].length - 1, 2); + assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), + oldData[1].dividedBy(new BigNumber(10).pow(18)).toNumber() + totalAmountTransacted, 0.00001); }); it("Should fail to transact the tokens more than the allowed tokens in a rolling period", async() => { @@ -374,6 +418,7 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should transfer the tokens in a new rolling period", async() => { let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); let firstDayAmount; tempAmount = new BigNumber(0); for (let i = 0; i < oldData[0].length; i++) { @@ -396,7 +441,8 @@ contract('VolumeRestrictionTransferManager', accounts => { let currentDayAmount = firstDayAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))); let tx = await I_SecurityToken.transfer(account_investor3, currentDayAmount, {from: account_investor1}); tempAmount = tempAmount.minus(currentDayAmount); - + tempArray.push(0); + tempArray.push(currentDayAmount.dividedBy(new BigNumber(10).pow(18)).toNumber()); console.log('\n'); let newData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); for (let i = 2; i < newData[0].length; i++) { @@ -405,19 +451,20 @@ contract('VolumeRestrictionTransferManager', accounts => { Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) .dividedBy(new BigNumber(10).pow(18))} `); + assert.equal(newData[0][i].toNumber(), startTime + duration.days(i)) + assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.00001); } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((newData[1].toNumber()).toString())} Last Timestamp Index : ${newData[0].length -1} `); - assert.notEqual(oldData[0][0].toNumber(), newData[0][3].toNumber()); - assert.notEqual(oldData[0][1].toNumber(), newData[0][4].toNumber()); }); it("Should transfer the more tokens on the same day", async() => { // Check the balance of the investors let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); - console.log(tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber()); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); await I_SecurityToken.transfer(account_investor3, tempAmount, {from: account_investor1}); // Check the balance of the investors @@ -428,7 +475,7 @@ contract('VolumeRestrictionTransferManager', accounts => { tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber(), 0.01 ); - + tempArray[tempArray.length -1] = tempArray[tempArray.length -1] + tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber(); let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); for (let i = 2; i < data[0].length; i++) { console.log(` @@ -436,13 +483,510 @@ contract('VolumeRestrictionTransferManager', accounts => { Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) .dividedBy(new BigNumber(10).pow(18))} `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) + assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.00001); } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} Last Timestamp Index : ${data[0].length -1} `); + let sumOflastperiod = 0; + for (let i = tempArray.length - 1; i >= tempArray.length - 3; i--) { + sumOflastperiod += tempArray[i]; + } + assert.equal(data[0].length - 1, 4); + assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), sumOflastperiod, 0.00001); + }); + }); + + describe("Test case for the variable individual restriction", async() => { + it("Should add the restriction succesfully", async() => { + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor2, + 0, + new BigNumber(20).times(new BigNumber(10).pow(16)), + latestTime() + duration.seconds(2), + 3, + latestTime() + duration.days(10), + 1, + { + from: token_owner + } + ); + + assert.equal(tx.logs[0].args._holder, account_investor2); + assert.equal(tx.logs[0].args._typeOfRestriction, 1); }); - + it("Should not successfully transact the tokens -- failed because volume is above the limit", async() => { + await increaseTime(duration.seconds(10)); + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei("15"), { from: account_investor2}) + ); + }); + + it("Should succesfully transact the tokens just after the starttime", async() => { + // Check the transfer will be valid or not by calling the verifyTransfer() directly by using _isTransfer = false + let result = await I_VolumeRestrictionTM.verifyTransfer.call(account_investor2, account_investor3, web3.utils.toWei('.3'), "0x0", false); + console.log(result); + assert.equal(result.toNumber(), 1); + // Perform the transaction + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('.3'), {from: account_investor2}); + // Check the balance of the investors + let bal1 = await I_SecurityToken.balanceOf.call(account_investor2); + // Verifying the balances + assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 29.7); + + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor2); + console.log('\n'); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), 0.3); + assert.equal(data[0][i].toNumber(), (await I_VolumeRestrictionTM.individualRestriction.call(account_investor2))[2].toNumber()) + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length -1} + `); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 0.3); + assert.equal(data[0].length -1, 0); + tempArrayVariable.push(0.3); + }); + + it("Should successfully transact the tokens after 1 and half days", async() => { + await increaseTime(duration.days(1.5)); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor2))[2].toNumber(); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor2}); + // Check the balance of the investors + let bal1 = await I_SecurityToken.balanceOf.call(account_investor2); + // Verifying the balances + assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 28.7); + tempArrayVariable.push(1); + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor2); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayVariable[i]); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length -1} + `); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 1.3); + assert.equal(data[0].length -1, 1); + }); + + it("Should successfully transact more tokens on the same day (Fuzz test)", async() => { + // Check the balance of the investors + let balBefore = await I_SecurityToken.balanceOf.call(account_investor2); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor2))[2].toNumber(); + let totalAmountTransacted = 0; + for (let i = 0; i < 10; i++) { + let amount = Math.random(); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor2}); + console.log(`${i}: Restricted investor 2 able to transact ${amount} tokens to investor 3`); + totalAmountTransacted += amount; + } + + // Check the balance of the investors + let balAfter = await I_SecurityToken.balanceOf.call(account_investor2); + tempArrayVariable[1] = tempArrayVariable[1] + totalAmountTransacted; + // Verifying the balances + assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); + + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor2); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayVariable[i], 0.000001); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + `); + assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), 1.3 + totalAmountTransacted, 0.000001); + assert.equal(data[0].length -1, 1); + }); + + it("Should successfully transfer the tokens after half days-- should increase the day covered by 1", async() => { + let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor2); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor2))[2].toNumber(); + await increaseTime(duration.days(.5)); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2"), {from: account_investor2}); + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor2); + tempArrayVariable.push(2); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) + assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayVariable[i], 0.000001); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + `); + assert.equal(data[0].length - 1, 2); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), + (oldData[1].plus(new BigNumber(2).times(new BigNumber(10).pow(18)))) + .dividedBy(new BigNumber(10).pow(18)).toNumber()); + }); + + it("Should fail to transfer the tokens on third day -- because it is more than the rolling period allowed", async() => { + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor2); + let res = await I_VolumeRestrictionTM.individualRestriction.call(account_investor2); + let allowedAmount = (res[1].mul(await I_SecurityToken.totalSupply.call())).dividedBy(new BigNumber(10).pow(18)); + let remainingAmount = allowedAmount.minus(data[1]); + + await catchRevert( + I_SecurityToken.transfer( + account_investor3, + remainingAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))), + { + from: account_investor2 + } + ) + ); + tempAmount = remainingAmount; + }); + + it("Should successfully transfer tokens more than the allowed as totalsupply get increased", async() => { + await I_SecurityToken.mint(account_investor2, web3.utils.toWei("10"), { from: token_owner }); + + await I_SecurityToken.transfer( + account_investor3, + tempAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))), + { + from: account_investor2 + } + ); + }); }); + + describe("Add the test cases for global restriction", async() => { + + it("Should successfully add the global restriction", async() => { + await I_VolumeRestrictionTM.addGlobalRestriction( + web3.utils.toWei("20"), + 0, + latestTime() + duration.seconds(2), + 3, + latestTime() + duration.days(10), + 0, + { + from: token_owner + } + ); + + let data = await I_VolumeRestrictionTM.globalRestriction.call(); + assert.equal(data[0], web3.utils.toWei("20")); + assert.equal(data[3], 3); + }); + + it("Should successfully add the 2 more address in the whitelist", async() => { + await I_GeneralTransferManager.modifyWhitelistMulti( + [account_investor4, account_delegate3], + [latestTime(), latestTime()], + [latestTime(), latestTime()], + [latestTime() + duration.days(30), latestTime() + duration.days(30)], + [true, true], + { + from: token_owner + } + ); + + // Mint some tokens and transferred to whitelisted addresses + await I_SecurityToken.mint(account_investor4, web3.utils.toWei("40", "ether"), {from: token_owner}); + await I_SecurityToken.mint(account_delegate3, web3.utils.toWei("30", "ether"), {from: token_owner}); + + // Check the balance of the investors + let bal1 = await I_SecurityToken.balanceOf.call(account_investor4); + let bal2 = await I_SecurityToken.balanceOf.call(account_delegate3); + // Verifying the balances + assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 40); + assert.equal(web3.utils.fromWei((bal2.toNumber()).toString()), 30); + }); + + it("Should successfully transfer the tokens with in the global range", async() => { + let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor4}); + let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); + let latestTimestamp = 0; + tempArrayGlobal.push(1); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); + assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + `); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 1); + assert.equal(data[0].length - 1, 0); + + console.log(`Transfer the tokens from the another investor comes under the global category`); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("4"), {from: account_delegate3}); + tempArrayGlobal[tempArrayGlobal.length - 1] += 4; + data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); + + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); + assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); + latestTimestamp = data[0][i]; + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} + `); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 5); + assert.equal(data[0].length - 1, 0); + assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 5); + }); + + it("Should transfer the tokens on the another day", async() => { + await increaseTime(duration.days(1.2)); + let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei("5"), { from: account_investor3}); + tempArrayGlobal.push(5); + let latestTimestamp = 0; + let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); + assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); + latestTimestamp = data[0][i]; + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} + `); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 10); + assert.equal(data[0].length - 1, 1); + assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 5); + }); + + it("Should transfer tokens on the third day of rolling period", async() => { + await increaseTime(duration.days(1)); + let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("6"), {from: account_investor4}); + let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); + tempArrayGlobal.push(6); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); + assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} + `); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 16); + assert.equal(data[0].length - 1, 2); + assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 6); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("4"), {from: account_delegate3}); + data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); + tempArrayGlobal[tempArrayGlobal.length -1] += 4; + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); + assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} + `); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 20); + assert.equal(data[0].length - 1, 2); + assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 10); + }) + + it("Should transfer of tokens get failed - limit of global token txn get reached", async() => { + let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); + let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); + let latestTimestamp = 0; + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + latestTimestamp = data[0][i]; + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} + `); + + // Already 20 tokens transferred + await catchRevert( + I_SecurityToken.transfer(account_delegate3, web3.utils.toWei("5"), {from: account_investor3}) + ); + }); + + it("Should allow to transact on the next rolling period", async() => { + await increaseTime(duration.days(1.7)); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor4}); + let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); + let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); + let latestTimestamp = 0; + tempArrayGlobal.push(1); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); + assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); + latestTimestamp = data[0][i]; + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} + `); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 16); + assert.equal(data[0].length - 1, 3); + assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 1); + }); + + it("Should add the daily restriction successfully", async() => { + await I_VolumeRestrictionTM.addDailyGlobalRestriction( + web3.utils.toWei("5"), + 0, + latestTime() + duration.seconds(2), + latestTime() + duration.days(3), + 0, + { + from: token_owner + } + ); + let data = await I_VolumeRestrictionTM.dailyGlobalRestriction.call(); + assert.equal(data[0].dividedBy(new BigNumber(10).pow(18)).toNumber(), 5); + assert.equal(data[1].toNumber(), 0); + assert.equal(data[5].toNumber(), 0); + }); + + it("Should failed to transact tokens -- failed because amount of tx is more than the daily limit",async() => { + let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); + await increaseTime(duration.days(2)); + // Failed because amount of tokens is greater than the daily limit + await catchRevert( + I_SecurityToken.transfer(account_delegate3, web3.utils.toWei("5.1"), {from: account_investor4}) + ); + }); + + it("Should transfer the tokens with in the daily limit", async() => { + let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); + await I_SecurityToken.transfer(account_delegate3, web3.utils.toWei("4.9"), {from: account_investor4}); + tempArrayGlobal.push(0); + tempArrayGlobal.push(4.9); + let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); + assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} + `); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 5.9); + assert.equal(data[0].length - 1, 5); + assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 4.9); + }); + + it("Should transact the tokens more than the daily limit because daily limit restriction is ended", async() => { + let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); + await increaseTime(duration.days(1)); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("10"), {from: account_delegate3}); + tempArrayGlobal.push(10); + let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); + assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} + `); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 14.9); + assert.equal(data[0].length - 1, 6); + assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 10); + }); + }) + + describe("Test for the exemptlist", async() => { + + it("Should add the token holder in the exemption list", async() => { + + }) + }) }); \ No newline at end of file From 61262019aef2dd8e6edc8750564d542904ab574e Mon Sep 17 00:00:00 2001 From: satyam Date: Sun, 25 Nov 2018 17:30:00 +0530 Subject: [PATCH 10/56] minor fixes --- .../TransferManager/VolumeRestrictionTM.sol | 4 ++-- .../TransferManager/VolumeRestrictionTMFactory.sol | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol index 60d244758..b05842b75 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol @@ -174,7 +174,7 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _endTime, uint256 _restrictionType ) - public + external withPerm(ADMIN) { _addIndividualRestriction( @@ -366,7 +366,7 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _endTime, uint256 _restrictionType ) - public + external withPerm(ADMIN) { _modifyIndividualRestriction( diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol index 5065b42d7..e1636fbe0 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol @@ -30,10 +30,10 @@ contract VolumeRestrictionTMFactory is ModuleFactory { function deploy(bytes /* _data */) external returns(address) { if (setupCost > 0) require(polyToken.transferFrom(msg.sender, owner, setupCost), "Sufficent Allowance is not provided"); - address VolumeRestrictionTransferManager = new VolumeRestrictionTM(msg.sender, address(polyToken)); + address volumeRestrictionTransferManager = new VolumeRestrictionTM(msg.sender, address(polyToken)); /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(VolumeRestrictionTransferManager), getName(), address(this), msg.sender, setupCost, now); - return address(VolumeRestrictionTransferManager); + emit GenerateModuleFromFactory(volumeRestrictionTransferManager, getName(), address(this), msg.sender, setupCost, now); + return volumeRestrictionTransferManager; } @@ -51,17 +51,18 @@ contract VolumeRestrictionTMFactory is ModuleFactory { */ function getInstructions() external view returns(string) { /*solium-disable-next-line max-len*/ - return ""; /// TODO - need to add the Instruction for module + return "Module used to restrict the volume of tokens traded by the token holders"; } /** * @notice Get the tags related to the module factory */ function getTags() public view returns(bytes32[]) { - bytes32[] memory availableTags = new bytes32[](2); + bytes32[] memory availableTags = new bytes32[](3); availableTags[0] = "Maximum Volume"; availableTags[1] = "Transfer Restriction"; availableTags[2] = "Daily Restriction"; + availableTags[3] = "Individual Restriction"; return availableTags; } From b069122054a94ba46eff367ceff789ef36464def Mon Sep 17 00:00:00 2001 From: satyam Date: Sun, 25 Nov 2018 17:41:10 +0530 Subject: [PATCH 11/56] reduce the error strings to lower the sixe of the contract --- .../TransferManager/VolumeRestrictionTM.sol | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol index b05842b75..7fee7d66c 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol @@ -132,7 +132,7 @@ contract VolumeRestrictionTM is ITransferManager { // If `_from` is present in the exemptionList or it is `0x0` address then it will not follow the vol restriction if (!paused && _from != address(0) && !exemptList[_from]) { // Function must only be called by the associated security token if _isTransfer == true - require(msg.sender == securityToken || !_isTransfer, "Sender is not the owner"); + require(msg.sender == securityToken || !_isTransfer); // Checking the individual restriction if the `_from` comes in the individual category if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { return _individualRestrictionCheck(_from, _amount, _isTransfer); @@ -150,7 +150,7 @@ contract VolumeRestrictionTM is ITransferManager { * @param _change Boolean value used to add (i.e true) or remove (i.e false) from the list */ function changeExemptWalletList(address _wallet, bool _change) public withPerm(ADMIN) { - require(_wallet != address(0), "0x0 address not allowed"); + require(_wallet != address(0), "Invalid address"); exemptList[_wallet] = _change; emit ChangedExemptWalletList(_wallet, _change); } @@ -211,7 +211,7 @@ contract VolumeRestrictionTM is ITransferManager { withPerm(ADMIN) { _checkLengthOfArray(_allowedTokens, _allowedPercentageOfTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); - require(_holders.length == _allowedTokens.length, "Array length mismatch"); + require(_holders.length == _allowedTokens.length, "Length mismatch"); for (uint256 i = 0; i < _holders.length; i++) { _addIndividualRestriction( _holders[i], @@ -247,7 +247,7 @@ contract VolumeRestrictionTM is ITransferManager { { require( globalRestriction.endTime == 0 || globalRestriction.endTime > now, - "Restriction already present" + "Not allowed" ); _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); globalRestriction = VolumeRestriction( @@ -288,7 +288,7 @@ contract VolumeRestrictionTM is ITransferManager { { require( dailyGlobalRestriction.endTime == 0 || dailyGlobalRestriction.endTime > now, - "Restriction already present" + "Not Allowed" ); _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, 1, _endTime, _restrictionType); dailyGlobalRestriction = VolumeRestriction( @@ -403,7 +403,7 @@ contract VolumeRestrictionTM is ITransferManager { withPerm(ADMIN) { _checkLengthOfArray(_allowedTokens, _allowedPercentageOfTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); - require(_holders.length == _allowedTokens.length, "Array length mismatch"); + require(_holders.length == _allowedTokens.length, "Length mismatch"); for (uint256 i = 0; i < _holders.length; i++) { _modifyIndividualRestriction( _holders[i], @@ -437,7 +437,7 @@ contract VolumeRestrictionTM is ITransferManager { external withPerm(ADMIN) { - require(globalRestriction.startTime > now, "Not allowed to modify"); + require(globalRestriction.startTime > now, "Not allowed"); _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); globalRestriction = VolumeRestriction( _allowedTokens, @@ -477,7 +477,7 @@ contract VolumeRestrictionTM is ITransferManager { external withPerm(ADMIN) { - require(dailyGlobalRestriction.startTime > now, "Not allowed to modify"); + require(dailyGlobalRestriction.startTime > now, "Not allowed"); _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); dailyGlobalRestriction = VolumeRestriction( _allowedTokens, From f44d05372852c7355e0a7dd2f46d9c3cadc96d40 Mon Sep 17 00:00:00 2001 From: satyam Date: Mon, 26 Nov 2018 12:53:27 +0530 Subject: [PATCH 12/56] minor fixes --- .../TransferManager/VolumeRestrictionTM.sol | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol index 7fee7d66c..e61743445 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol @@ -246,10 +246,13 @@ contract VolumeRestrictionTM is ITransferManager { withPerm(ADMIN) { require( - globalRestriction.endTime == 0 || globalRestriction.endTime > now, + globalRestriction.endTime < now, "Not allowed" ); _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + if (globalRestriction.endTime != 0) { + removeGlobalRestriction(); + } globalRestriction = VolumeRestriction( _allowedTokens, _allowedPercentageOfTokens, @@ -287,7 +290,7 @@ contract VolumeRestrictionTM is ITransferManager { withPerm(ADMIN) { require( - dailyGlobalRestriction.endTime == 0 || dailyGlobalRestriction.endTime > now, + dailyGlobalRestriction.endTime < now, "Not Allowed" ); _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, 1, _endTime, _restrictionType); @@ -330,7 +333,7 @@ contract VolumeRestrictionTM is ITransferManager { /** * @notice Use to remove the global restriction */ - function removeGlobalRestriction() external withPerm(ADMIN) { + function removeGlobalRestriction() public withPerm(ADMIN) { require(globalRestriction.endTime != 0, "Not present"); globalRestriction = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); globalBucketDetails.timestamps.length = 0; @@ -437,7 +440,7 @@ contract VolumeRestrictionTM is ITransferManager { external withPerm(ADMIN) { - require(globalRestriction.startTime > now, "Not allowed"); + require(globalRestriction.startTime < now, "Not allowed"); _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); globalRestriction = VolumeRestriction( _allowedTokens, @@ -477,7 +480,7 @@ contract VolumeRestrictionTM is ITransferManager { external withPerm(ADMIN) { - require(dailyGlobalRestriction.startTime > now, "Not allowed"); + require(dailyGlobalRestriction.startTime < now, "Not allowed"); _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); dailyGlobalRestriction = VolumeRestriction( _allowedTokens, @@ -770,8 +773,8 @@ contract VolumeRestrictionTM is ITransferManager { internal { _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); - require(individualRestriction[_holder].startTime > now, "Not allowed to modify"); - + require(individualRestriction[_holder].startTime < now, "Not allowed to modify"); + individualRestriction[_holder] = VolumeRestriction( _allowedTokens, _allowedPercentageOfTokens, @@ -803,12 +806,15 @@ contract VolumeRestrictionTM is ITransferManager { internal { require( - individualRestriction[_holder].endTime == 0 || individualRestriction[_holder].endTime > now, + individualRestriction[_holder].endTime < now, "Restriction already present" ); require(_holder != address(0) && !exemptList[_holder], "Invalid address"); _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + if (individualRestriction[_holder].endTime != 0) { + _removeIndividualRestriction(_holder); + } individualRestriction[_holder] = VolumeRestriction( _allowedTokens, _allowedPercentageOfTokens, @@ -837,7 +843,7 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _restrictionType ) internal - pure + view { require(_restrictionType == 0 || _restrictionType == 1, "Invalid restriction type"); if (_restrictionType == uint256(RestrictionType.Fixed)) { @@ -848,7 +854,7 @@ contract VolumeRestrictionTM is ITransferManager { "Percentage of tokens not within (0,100]" ); } - require(_startTime > 0 && _endTime > _startTime); + require(_startTime >= now && _endTime > _startTime); // Maximum limit for the rollingPeriod is 365 days require(_rollingPeriodDays >= 1 && _rollingPeriodDays <= 365); require(BokkyPooBahsDateTimeLibrary.diffDays(_startTime, _endTime) >= _rollingPeriodDays); From 96bf749123ecf16e3abc5799ee19da856c3861b1 Mon Sep 17 00:00:00 2001 From: satyam Date: Mon, 26 Nov 2018 13:50:15 +0530 Subject: [PATCH 13/56] add more test cases --- test/z_volume_restriction_tm.js | 395 +++++++++++++++++++++++++++++++- 1 file changed, 391 insertions(+), 4 deletions(-) diff --git a/test/z_volume_restriction_tm.js b/test/z_volume_restriction_tm.js index 298fc18c8..f59e06486 100644 --- a/test/z_volume_restriction_tm.js +++ b/test/z_volume_restriction_tm.js @@ -209,6 +209,176 @@ contract('VolumeRestrictionTransferManager', accounts => { describe("Test for the addIndividualRestriction", async() => { + it("Should add the restriction -- failed because of bad owner", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei("12"), + 0, + latestTime() + duration.seconds(2), + 3, + latestTime() + duration.days(10), + 0, + { + from: account_polymath + } + ) + ); + }) + + it("Should add the restriction -- failed because of bad parameters i.e invalid restriction type", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei("12"), + 0, + latestTime() + duration.seconds(2), + 3, + latestTime() + duration.days(10), + 3, + { + from: token_owner + } + ) + ); + }) + + it("Should add the restriction -- failed because of bad parameters i.e Invalid value of allowed tokens", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + 0, + 0, + latestTime() + duration.seconds(2), + 3, + latestTime() + duration.days(10), + 0, + { + from: token_owner + } + ) + ); + }) + + it("Should add the restriction -- failed because of bad parameters i.e Percentage of tokens not within (0,100]", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + 0, + 0, + latestTime() + duration.seconds(2), + 3, + latestTime() + duration.days(10), + 1, + { + from: token_owner + } + ) + ); + }) + + it("Should add the restriction -- failed because of bad parameters i.e Percentage of tokens not within (0,100]", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + 0, + web3.utils.toWei("10"), + latestTime() + duration.seconds(2), + 3, + latestTime() + duration.days(10), + 1, + { + from: token_owner + } + ) + ); + }) + + it("Should add the restriction -- failed because of bad parameters i.e invalid dates", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei("10"), + 0, + latestTime() - duration.seconds(5), + 3, + latestTime() + duration.days(10), + 0, + { + from: token_owner + } + ) + ); + }) + + it("Should add the restriction -- failed because of bad parameters i.e invalid dates", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei("10"), + 0, + latestTime() + duration.days(2), + 3, + latestTime() + duration.days(1), + 0, + { + from: token_owner + } + ) + ); + }); + + it("Should add the restriction -- failed because of bad parameters i.e invalid rolling period", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei("10"), + 0, + latestTime() + duration.days(2), + 0, + latestTime() + duration.days(10), + 0, + { + from: token_owner + } + ) + ); + }); + + it("Should add the restriction -- failed because of bad parameters i.e invalid rolling period", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei("10"), + 0, + latestTime() + duration.days(2), + 366, + latestTime() + duration.days(10), + 0, + { + from: token_owner + } + ) + ); + }); + + it("Should add the restriction -- failed because of bad parameters i.e invalid rolling period", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei("10"), + 0, + latestTime() + duration.days(2), + 3, + latestTime() + duration.days(3), + 0, + { + from: token_owner + } + ) + ); + }); + it("Should add the restriction succesfully", async() => { let tx = await I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, @@ -216,7 +386,7 @@ contract('VolumeRestrictionTransferManager', accounts => { 0, latestTime() + duration.seconds(2), 3, - latestTime() + duration.days(10), + latestTime() + duration.days(5), 0, { from: token_owner @@ -227,6 +397,211 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.equal(tx.logs[0].args._typeOfRestriction, 0); }); + it("Should add the restriction for multiple investor -- failed because of bad owner", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], + [0,0,0], + [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], + [3,4,5], + [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], + [0,0,0], + { + from: account_polymath + } + ) + ) + }); + + it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3], + [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], + [0,0,0], + [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], + [3,4,5], + [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], + [0,0,0], + { + from: token_owner + } + ) + ) + }); + + it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [web3.utils.toWei("12"), web3.utils.toWei("10")], + [0,0,0], + [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], + [3,4,5], + [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], + [0,0,0], + { + from: account_polymath + } + ) + ) + }); + + it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], + [0,0], + [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], + [3,4,5], + [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], + [0,0,0], + { + from: token_owner + } + ) + ) + }); + + it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], + [0,0,0], + [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], + [3,4,5], + [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], + [0,0,0], + { + from: token_owner + } + ) + ) + }); + + it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], + [0,0,0], + [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], + [3], + [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], + [0,0,0], + { + from: token_owner + } + ) + ) + }); + + it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], + [0,0,0], + [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], + [3, 4, 5], + [latestTime() + duration.days(5)], + [0,0,0], + { + from: token_owner + } + ) + ) + }); + + it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], + [0,0,0], + [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], + [3, 4, 5], + [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], + [], + { + from: token_owner + } + ) + ) + }); + + it("Should add the restriction for multiple investor successfully", async() => { + await I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor2, account_delegate3, account_investor4], + [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], + [0,0,0], + [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], + [3, 4, 5], + [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], + [0,0,0], + { + from: token_owner + } + ); + assert.equal((await I_VolumeRestrictionTM.individualRestriction.call(account_investor2))[3].toNumber(), 3); + assert.equal((await I_VolumeRestrictionTM.individualRestriction.call(account_delegate3))[3].toNumber(), 4); + assert.equal((await I_VolumeRestrictionTM.individualRestriction.call(account_investor4))[3].toNumber(), 5); + }); + + it("Should remove the restriction multi -- failed because of address is 0", async() => { + await catchRevert( + I_VolumeRestrictionTM.removeIndividualRestrictionMulti( + [0, account_delegate3, account_investor4], + { + from: token_owner + } + ) + ); + }); + + it("Should successfully remove the restriction", async() => { + await I_VolumeRestrictionTM.removeIndividualRestriction(account_investor2, {from: token_owner}); + assert.equal((await I_VolumeRestrictionTM.individualRestriction.call(account_investor2))[3].toNumber(), 0); + }); + + it("Should remove the restriction -- failed because restriction not present anymore", async() => { + await catchRevert( + I_VolumeRestrictionTM.removeIndividualRestriction(account_investor2, {from: token_owner}) + ); + }); + + it("Should remove the restriction multi", async() => { + await I_VolumeRestrictionTM.removeIndividualRestrictionMulti( + [account_delegate3, account_investor4], + { + from: token_owner + } + ) + }); + + it("Should add the restriction succesfully after the expiry of previous one", async() => { + await increaseTime(duration.days(5.1)); + + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei("12"), + 0, + latestTime() + duration.seconds(2), + 3, + latestTime() + duration.days(10), + 0, + { + from: token_owner + } + ); + + assert.equal(tx.logs[1].args._holder, account_investor1); + assert.equal(tx.logs[1].args._typeOfRestriction, 0); + }); + it("Should not successfully transact the tokens -- failed because volume is above the limit", async() => { await increaseTime(duration.seconds(10)); await catchRevert( @@ -985,8 +1360,20 @@ contract('VolumeRestrictionTransferManager', accounts => { describe("Test for the exemptlist", async() => { + it("Should add the token holder in the exemption list -- failed because of bad owner", async() => { + await catchRevert( + I_VolumeRestrictionTM.changeExemptWalletList(account_investor4, true, {from: account_polymath}) + ); + }); + it("Should add the token holder in the exemption list", async() => { - - }) - }) + await I_VolumeRestrictionTM.changeExemptWalletList(account_investor4, true, {from: token_owner}); + let beforeBal = await I_SecurityToken.balanceOf.call(account_investor4); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("3"), {from: account_investor4}); + let afterBal = await I_SecurityToken.balanceOf.call(account_investor4); + let diff = beforeBal.minus(afterBal); + assert.equal(web3.utils.fromWei((diff.toNumber()).toString()), 3); + }); + }); + }); \ No newline at end of file From 8e44d1b8c6eed3088556b188c3ace1dfed545501 Mon Sep 17 00:00:00 2001 From: satyam Date: Mon, 26 Nov 2018 14:34:48 +0530 Subject: [PATCH 14/56] remove the reason strings --- .../TransferManager/VolumeRestrictionTM.sol | 14 +++++++------- .../TransferManager/VolumeRestrictionTMFactory.sol | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol index e61743445..9e16d60ef 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol @@ -334,7 +334,7 @@ contract VolumeRestrictionTM is ITransferManager { * @notice Use to remove the global restriction */ function removeGlobalRestriction() public withPerm(ADMIN) { - require(globalRestriction.endTime != 0, "Not present"); + require(globalRestriction.endTime != 0); globalRestriction = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); globalBucketDetails.timestamps.length = 0; globalBucketDetails.sumOfLastPeriod = 0; @@ -345,7 +345,7 @@ contract VolumeRestrictionTM is ITransferManager { * @notice Use to remove the daily global restriction */ function removeDailyGlobalRestriction() external withPerm(ADMIN) { - require(dailyGlobalRestriction.endTime != 0, "Not present"); + require(dailyGlobalRestriction.endTime != 0); dailyGlobalRestriction = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); emit DailyGlobalRestrictionRemoved(); } @@ -773,7 +773,7 @@ contract VolumeRestrictionTM is ITransferManager { internal { _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); - require(individualRestriction[_holder].startTime < now, "Not allowed to modify"); + require(individualRestriction[_holder].startTime < now, "Not allowed"); individualRestriction[_holder] = VolumeRestriction( _allowedTokens, @@ -807,7 +807,7 @@ contract VolumeRestrictionTM is ITransferManager { { require( individualRestriction[_holder].endTime < now, - "Restriction already present" + "Already present" ); require(_holder != address(0) && !exemptList[_holder], "Invalid address"); _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); @@ -845,13 +845,13 @@ contract VolumeRestrictionTM is ITransferManager { internal view { - require(_restrictionType == 0 || _restrictionType == 1, "Invalid restriction type"); + require(_restrictionType == 0 || _restrictionType == 1, "Invalid type"); if (_restrictionType == uint256(RestrictionType.Fixed)) { - require(_allowedTokens > 0, "Invalid value of allowed tokens"); + require(_allowedTokens > 0, "Invalid value"); } else { require( _allowedPercentageOfTokens > 0 && _allowedPercentageOfTokens <= 100 * 10 ** 16, - "Percentage of tokens not within (0,100]" + "Percentage is not within (0,100]" ); } require(_startTime >= now && _endTime > _startTime); diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol index e1636fbe0..4609ea2ff 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol @@ -29,7 +29,7 @@ contract VolumeRestrictionTMFactory is ModuleFactory { */ function deploy(bytes /* _data */) external returns(address) { if (setupCost > 0) - require(polyToken.transferFrom(msg.sender, owner, setupCost), "Sufficent Allowance is not provided"); + require(polyToken.transferFrom(msg.sender, owner, setupCost), "Insufficent Allowance"); address volumeRestrictionTransferManager = new VolumeRestrictionTM(msg.sender, address(polyToken)); /*solium-disable-next-line security/no-block-members*/ emit GenerateModuleFromFactory(volumeRestrictionTransferManager, getName(), address(this), msg.sender, setupCost, now); From 56d0a573f1dfa6964173c2fbcadb1931a1782ef7 Mon Sep 17 00:00:00 2001 From: satyam Date: Tue, 27 Nov 2018 17:16:43 +0530 Subject: [PATCH 15/56] minor fixes in the contract --- .../TransferManager/VolumeRestrictionTM.sol | 14 ++-- test/z_volume_restriction_tm.js | 68 +++++++++++++++++++ 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol index 9e16d60ef..2431b2045 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol @@ -440,7 +440,7 @@ contract VolumeRestrictionTM is ITransferManager { external withPerm(ADMIN) { - require(globalRestriction.startTime < now, "Not allowed"); + require(globalRestriction.startTime > now, "Not allowed"); _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); globalRestriction = VolumeRestriction( _allowedTokens, @@ -465,7 +465,6 @@ contract VolumeRestrictionTM is ITransferManager { * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. * @param _startTime Unix timestamp at which restriction get into effect - * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) * @param _endTime Unix timestamp at which restriction effects will gets end. * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) */ @@ -473,20 +472,19 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _allowedTokens, uint256 _allowedPercentageOfTokens, uint256 _startTime, - uint256 _rollingPeriodInDays, uint256 _endTime, uint256 _restrictionType ) external withPerm(ADMIN) { - require(dailyGlobalRestriction.startTime < now, "Not allowed"); - _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + require(dailyGlobalRestriction.startTime > now, "Not allowed"); + _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, 1, _endTime, _restrictionType); dailyGlobalRestriction = VolumeRestriction( _allowedTokens, _allowedPercentageOfTokens, _startTime, - _rollingPeriodInDays, + 1, _endTime, RestrictionType(_restrictionType) ); @@ -494,7 +492,7 @@ contract VolumeRestrictionTM is ITransferManager { _allowedTokens, _allowedPercentageOfTokens, _startTime, - _rollingPeriodInDays, + 1, _endTime, _restrictionType ); @@ -773,7 +771,7 @@ contract VolumeRestrictionTM is ITransferManager { internal { _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); - require(individualRestriction[_holder].startTime < now, "Not allowed"); + require(individualRestriction[_holder].startTime > now, "Not allowed"); individualRestriction[_holder] = VolumeRestriction( _allowedTokens, diff --git a/test/z_volume_restriction_tm.js b/test/z_volume_restriction_tm.js index f59e06486..7c4762fd7 100644 --- a/test/z_volume_restriction_tm.js +++ b/test/z_volume_restriction_tm.js @@ -1375,5 +1375,73 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.equal(web3.utils.fromWei((diff.toNumber()).toString()), 3); }); }); + + describe("Test for modify functions", async() => { + + it("Should not able to modify the already started restrictions --global", async() =>{ + await catchRevert( + I_VolumeRestrictionTM.modifyGlobalRestriction( + web3.utils.toWei("50"), + 0, + latestTime() + duration.seconds(50), + 10, + latestTime() + duration.days(20), + 0, + { + from: token_owner + } + ) + ); + }); + + it("Should not able to modify the already started restrictions -- daily global", async() =>{ + await catchRevert( + I_VolumeRestrictionTM.modifyDailyGlobalRestriction( + web3.utils.toWei("50"), + 0, + latestTime() + duration.seconds(50), + latestTime() + duration.days(20), + 0, + { + from: token_owner + } + ) + ); + }); + + it("Should not able to modify the already started restrictions -- Individual", async() =>{ + await catchRevert( + I_VolumeRestrictionTM.modifyIndividualRestriction( + account_investor2, + web3.utils.toWei("50"), + 0, + latestTime() + duration.seconds(50), + 10, + latestTime() + duration.days(20), + 0, + { + from: token_owner + } + ) + ); + }); + + it("Should not able to modify the already started transaction -- multi Individuals", async() => { + await catchRevert( + I_VolumeRestrictionTM.modifyIndividualRestrictionMulti( + [account_investor2, account_investor1], + [web3.utils.toWei("50"), web3.utils.toWei("50")], + [0, 0], + [latestTime() + duration.seconds(50), latestTime() + duration.seconds(50)], + [10, 20], + [latestTime() + duration.days(20), latestTime() + duration.days(50)], + [0, 0], + { + from: token_owner + } + ) + ); + }); + }); }); \ No newline at end of file From 2d96fc2e7ed2ffdeac776a4d85d21591e28a6e59 Mon Sep 17 00:00:00 2001 From: satyam Date: Tue, 27 Nov 2018 18:00:26 +0530 Subject: [PATCH 16/56] merge dev-2.1.0 and add new test case --- test/z_volume_restriction_tm.js | 65 ++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/test/z_volume_restriction_tm.js b/test/z_volume_restriction_tm.js index 7c4762fd7..96c96776d 100644 --- a/test/z_volume_restriction_tm.js +++ b/test/z_volume_restriction_tm.js @@ -180,7 +180,7 @@ contract('VolumeRestrictionTransferManager', accounts => { [latestTime(), latestTime(), latestTime()], [latestTime(), latestTime(), latestTime()], [latestTime() + duration.days(30), latestTime() + duration.days(30), latestTime() + duration.days(30)], - [true, true, true], + [0, 0, 0], { from: token_owner } @@ -1083,7 +1083,7 @@ contract('VolumeRestrictionTransferManager', accounts => { [latestTime(), latestTime()], [latestTime(), latestTime()], [latestTime() + duration.days(30), latestTime() + duration.days(30)], - [true, true], + [0, 0], { from: token_owner } @@ -1442,6 +1442,67 @@ contract('VolumeRestrictionTransferManager', accounts => { ) ); }); + + it("Should add the individual restriction for multiple investor", async() => { + await I_VolumeRestrictionTM.addIndividualRestrictionMulti( + [account_investor3, account_delegate3], + [web3.utils.toWei("15"), 0], + [0, new BigNumber(12.78).times(new BigNumber(10).pow(16))], + [latestTime() + duration.days(1), latestTime() + duration.days(2)], + [15, 20], + [latestTime() + duration.days(40), latestTime() + duration.days(60)], + [0,1], + { + from: token_owner + } + ); + + let indi1 = await I_VolumeRestrictionTM.individualRestriction.call(account_investor3); + let indi2 = await I_VolumeRestrictionTM.individualRestriction.call(account_delegate3); + + assert.equal(indi1[0].dividedBy(new BigNumber(10).pow(18)), 15); + assert.equal(indi2[0].dividedBy(new BigNumber(10).pow(18)), 0); + + assert.equal(indi1[1].dividedBy(new BigNumber(10).pow(18)), 0); + assert.equal(indi2[1].dividedBy(new BigNumber(10).pow(16)), 12.78); + + assert.equal(indi1[3].toNumber(), 15); + assert.equal(indi2[3].toNumber(), 20); + + assert.equal(indi1[5].toNumber(), 0); + assert.equal(indi2[5].toNumber(), 1); + }); + + it("Should modify the details before the starttime passed", async() => { + await I_VolumeRestrictionTM.modifyIndividualRestrictionMulti( + [account_investor3, account_delegate3], + [0, web3.utils.toWei("15")], + [new BigNumber(12.78).times(new BigNumber(10).pow(16)), 0], + [latestTime() + duration.days(1), latestTime() + duration.days(2)], + [20, 15], + [latestTime() + duration.days(40), latestTime() + duration.days(60)], + [1,0], + { + from: token_owner + } + ); + + let indi1 = await I_VolumeRestrictionTM.individualRestriction.call(account_investor3); + let indi2 = await I_VolumeRestrictionTM.individualRestriction.call(account_delegate3); + + assert.equal(indi2[0].dividedBy(new BigNumber(10).pow(18)), 15); + assert.equal(indi1[0].dividedBy(new BigNumber(10).pow(18)), 0); + + assert.equal(indi2[1].dividedBy(new BigNumber(10).pow(18)), 0); + assert.equal(indi1[1].dividedBy(new BigNumber(10).pow(16)), 12.78); + + assert.equal(indi2[3].toNumber(), 15); + assert.equal(indi1[3].toNumber(), 20); + + assert.equal(indi2[5].toNumber(), 0); + assert.equal(indi1[5].toNumber(), 1); + }); + }); }); \ No newline at end of file From 9802d8abd0aca4c3c980ef3f46c12a17ed659fbf Mon Sep 17 00:00:00 2001 From: satyam Date: Tue, 27 Nov 2018 19:43:21 +0530 Subject: [PATCH 17/56] test cases fixed --- test/z_volume_restriction_tm.js | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/test/z_volume_restriction_tm.js b/test/z_volume_restriction_tm.js index 96c96776d..e302b9926 100644 --- a/test/z_volume_restriction_tm.js +++ b/test/z_volume_restriction_tm.js @@ -677,7 +677,7 @@ contract('VolumeRestrictionTransferManager', accounts => { let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); let totalAmountTransacted = 0; for (let i = 0; i < 10; i++) { - let amount = Math.random(); + let amount = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor1}); console.log(`${i}: Restricted investor 1 able to transact ${amount} tokens to investor 3`); totalAmountTransacted += amount; @@ -742,13 +742,10 @@ contract('VolumeRestrictionTransferManager', accounts => { let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - let totalAmountTransacted = 0; - for (let i = 0; i < 2; i++) { - let amount = Math.random(); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor1}); - console.log(`${i}: Restricted investor 1 able to transact ${amount} tokens to investor 3`); - totalAmountTransacted += amount; - } + + let totalAmountTransacted = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(totalAmountTransacted.toString()), {from: account_investor1}); + // Check the balance of the investors let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); // Verifying the balances @@ -772,7 +769,7 @@ contract('VolumeRestrictionTransferManager', accounts => { `); assert.equal(data[0].length - 1, 2); assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), - oldData[1].dividedBy(new BigNumber(10).pow(18)).toNumber() + totalAmountTransacted, 0.00001); + oldData[1].dividedBy(new BigNumber(10).pow(18)).toNumber() + totalAmountTransacted, 0.001); }); it("Should fail to transact the tokens more than the allowed tokens in a rolling period", async() => { @@ -828,7 +825,7 @@ contract('VolumeRestrictionTransferManager', accounts => { `); assert.equal(newData[0][i].toNumber(), startTime + duration.days(i)) assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.00001); + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.001); } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((newData[1].toNumber()).toString())} @@ -860,7 +857,7 @@ contract('VolumeRestrictionTransferManager', accounts => { `); assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.00001); + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.001); } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} @@ -871,7 +868,7 @@ contract('VolumeRestrictionTransferManager', accounts => { sumOflastperiod += tempArray[i]; } assert.equal(data[0].length - 1, 4); - assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), sumOflastperiod, 0.00001); + assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), sumOflastperiod, 0.001); }); }); @@ -968,7 +965,7 @@ contract('VolumeRestrictionTransferManager', accounts => { let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor2))[2].toNumber(); let totalAmountTransacted = 0; for (let i = 0; i < 10; i++) { - let amount = Math.random(); + let amount = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor2}); console.log(`${i}: Restricted investor 2 able to transact ${amount} tokens to investor 3`); totalAmountTransacted += amount; @@ -1125,7 +1122,7 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.equal(data[0].length - 1, 0); console.log(`Transfer the tokens from the another investor comes under the global category`); - + await increaseTime(duration.minutes(10)); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("4"), {from: account_delegate3}); tempArrayGlobal[tempArrayGlobal.length - 1] += 4; data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); From ed9406809116ba72d5264b60897cb269f076b7f9 Mon Sep 17 00:00:00 2001 From: satyam Date: Tue, 27 Nov 2018 19:50:30 +0530 Subject: [PATCH 18/56] minor test fix because of gtm --- test/z_volume_restriction_tm.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/z_volume_restriction_tm.js b/test/z_volume_restriction_tm.js index e302b9926..48645f5f4 100644 --- a/test/z_volume_restriction_tm.js +++ b/test/z_volume_restriction_tm.js @@ -180,7 +180,7 @@ contract('VolumeRestrictionTransferManager', accounts => { [latestTime(), latestTime(), latestTime()], [latestTime(), latestTime(), latestTime()], [latestTime() + duration.days(30), latestTime() + duration.days(30), latestTime() + duration.days(30)], - [0, 0, 0], + [true, true, true], { from: token_owner } @@ -1080,7 +1080,7 @@ contract('VolumeRestrictionTransferManager', accounts => { [latestTime(), latestTime()], [latestTime(), latestTime()], [latestTime() + duration.days(30), latestTime() + duration.days(30)], - [0, 0], + [true, true], { from: token_owner } From c0878e84165e97bcdfa9e72eb0d48cf59319652a Mon Sep 17 00:00:00 2001 From: satyam Date: Tue, 27 Nov 2018 22:32:32 +0530 Subject: [PATCH 19/56] update the permission doc --- docs/permissions_list.md | 58 +++++++++++----------------------------- 1 file changed, 16 insertions(+), 42 deletions(-) diff --git a/docs/permissions_list.md b/docs/permissions_list.md index b49c95a0e..dba79396d 100644 --- a/docs/permissions_list.md +++ b/docs/permissions_list.md @@ -143,7 +143,7 @@ allocateTokensMulti() - TransferManager + TransferManager CountTransferManager changeHolderCount() withPerm(ADMIN) @@ -205,71 +205,45 @@ changeHolderPercentage() - LockupVolumeRestrictionTM - addLockup() - withPerm(ADMIN) + VolumeRestrictionTM + changeExemptWalletList() + withPerm(ADMIN) - addLockUpMulti() + addIndividualRestriction() - removeLockUp() + addIndividualRestrictionMulti() - modifyLockUp() + addGlobalRestriction() - SingleTradeVolumeRestrictionTM - setAllowPrimaryIssuance() - withPerm(ADMIN) - - - changeTransferLimitToPercentage() - - - changeTransferLimitToTokens() - - - changeGlobalLimitInTokens() - - - changeGlobalLimitInPercentage() - - - addExemptWallet() - - - removeExemptWallet() - - - addExemptWalletMulti() - - - removeExemptWalletMulti() + addDailyGlobalRestriction() - setTransferLimitInTokens() + removeIndividualRestriction() - setTransferLimitInPercentage() + removeIndividualRestrictionMulti() - removeTransferLimitInPercentage() + removeGlobalRestriction() - removeTransferLimitInTokens() + removeDailyGlobalRestriction() - setTransferLimitInTokensMulti() + modifyIndividualRestriction() - setTransferLimitInPercentageMulti() + modifyIndividualRestrictionMulti() - removeTransferLimitInTokensMulti() + modifyGlobalRestriction() - removeTransferLimitInPercentageMulti + modifyDailyGlobalRestriction() From 0afae263ff0b7099d29ce4b47c180517301246b1 Mon Sep 17 00:00:00 2001 From: satyam Date: Wed, 28 Nov 2018 11:44:00 +0530 Subject: [PATCH 20/56] VRTM moved from experimental to usable TM list --- CHANGELOG.md | 9 ++++++++- .../TransferManager/VolumeRestrictionTM.sol | 4 ++-- .../VolumeRestrictionTMFactory.sol | 2 +- migrations/2_deploy_contracts.js | 15 +++++++++++++++ 4 files changed, 26 insertions(+), 4 deletions(-) rename contracts/modules/{Experimental => }/TransferManager/VolumeRestrictionTM.sol (99%) rename contracts/modules/{Experimental => }/TransferManager/VolumeRestrictionTMFactory.sol (98%) diff --git a/CHANGELOG.md b/CHANGELOG.md index db3d8dcde..aa7d6c60c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. [__2.1.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __13-09-18__ -# USDTieredSTO 2.0.1 +## USDTieredSTO 2.0.1 * Added `buyTokensView` and `getTokensMintedByTier` to USDTSTO. * Added `getSTODetails` to USDTSTO. * Added an Array of Tiers that will hold data about every tier in USDTSTO. @@ -13,9 +13,16 @@ All notable changes to this project will be documented in this file. * Added `getTokensSoldByTier` to return sold (not minted during finalisation) tokens in each tier to USDTSTO. * Removed individual mappings for tier data removed in UDSTSTO. +## Experimental modules +* Remove the `SingleTradeVolumeRestrictionTMFactory.sol` and its corresponding module `SingleTradeVolumeRestrictionTM.sol`. + ##Changed * `getAllModulesAndPermsFromTypes()` does not take securityToken address as a parameter anymore. +## Added +* Add new module called `VolumeRestrictionTM.sol` under the TransferManager modules list. It will be used to restrict the token +volume traded in a given rolling period. + # v1.5.0 [__1.5.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __15-08-18__ diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol similarity index 99% rename from contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol rename to contracts/modules/TransferManager/VolumeRestrictionTM.sol index 2431b2045..1ccb525be 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -1,8 +1,8 @@ pragma solidity ^0.4.24; -import "../../TransferManager/ITransferManager.sol"; +import "./ITransferManager.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; -import "../../../libraries/BokkyPooBahsDateTimeLibrary.sol"; +import "../../libraries/BokkyPooBahsDateTimeLibrary.sol"; contract VolumeRestrictionTM is ITransferManager { diff --git a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol b/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol similarity index 98% rename from contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol rename to contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol index 4609ea2ff..03d6e89fc 100644 --- a/contracts/modules/Experimental/TransferManager/VolumeRestrictionTMFactory.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.24; import "./VolumeRestrictionTM.sol"; -import "../../ModuleFactory.sol"; +import "../ModuleFactory.sol"; /** * @title Factory for deploying VolumeRestrictionTM module diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 694fdf03e..b28be4c92 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -13,6 +13,7 @@ const CappedSTOFactory = artifacts.require('./CappedSTOFactory.sol') const USDTieredSTOFactory = artifacts.require('./USDTieredSTOFactory.sol') const SecurityTokenRegistry = artifacts.require('./SecurityTokenRegistry.sol') const SecurityTokenRegistryProxy = artifacts.require('./SecurityTokenRegistryProxy.sol') +const VolumeRestrictionTMFactory = artifacts.require('./VolumeRestrictionTMFactory.sol') const FeatureRegistry = artifacts.require('./FeatureRegistry.sol') const STFactory = artifacts.require('./tokens/STFactory.sol') const DevPolyToken = artifacts.require('./helpers/PolyTokenFaucet.sol') @@ -169,6 +170,10 @@ module.exports = function (deployer, network, accounts) { // D) Deploy the ERC20DividendCheckpointFactory Contract (Factory used to generate the ERC20DividendCheckpoint contract use // to provide the functionality of the dividend in terms of ERC20 token) return deployer.deploy(ERC20DividendCheckpointFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); + }).then(() => { + // D) Deploy the VolumeRestrictionTMFactory Contract (Factory used to generate the VolumeRestrictionTM contract use + // to provide the functionality of restricting the token volume) + return deployer.deploy(VolumeRestrictionTMFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); }).then(() => { // D) Deploy the ManualApprovalTransferManagerFactory Contract (Factory used to generate the ManualApprovalTransferManager contract use // to manual approve the transfer that will overcome the other transfer restrictions) @@ -216,6 +221,10 @@ module.exports = function (deployer, network, accounts) { // E) Register the GeneralPermissionManagerFactory in the ModuleRegistry to make the factory available at the protocol level. // So any securityToken can use that factory to generate the GeneralPermissionManager contract. return moduleRegistry.registerModule(EtherDividendCheckpointFactory.address, {from: PolymathAccount}); + }).then(() => { + // D) Register the VolumeRestrictionTMFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the VolumeRestrictionTM contract. + return moduleRegistry.registerModule(VolumeRestrictionTMFactory.address, {from: PolymathAccount}); }).then(() => { // D) Register the ManualApprovalTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. // So any securityToken can use that factory to generate the ManualApprovalTransferManager contract. @@ -254,6 +263,11 @@ module.exports = function (deployer, network, accounts) { // 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(ERC20DividendCheckpointFactory.address, true, {from: PolymathAccount}); + }).then(() => { + // G) Once the VolumeRestrictionTMFactory 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(VolumeRestrictionTMFactory.address, true, {from: PolymathAccount}); }).then(() => { // G) Once the ManualApprovalTransferManagerFactory 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. @@ -315,6 +329,7 @@ module.exports = function (deployer, network, accounts) { ManualApprovalTransferManagerFactory: ${ManualApprovalTransferManagerFactory.address} EtherDividendCheckpointFactory: ${EtherDividendCheckpointFactory.address} ERC20DividendCheckpointFactory: ${ERC20DividendCheckpointFactory.address} + VolumeRestrictionTMFactory: ${VolumeRestrictionTMFactory.address} --------------------------------------------------------------------------------- `); console.log('\n'); From e29027b4b58aa46ed19257cd50652f1a6bcf11c4 Mon Sep 17 00:00:00 2001 From: satyam Date: Wed, 28 Nov 2018 11:44:28 +0530 Subject: [PATCH 21/56] remove the STVRTM --- .../SingleTradeVolumeRestrictionTM.sol | 331 -------- .../SingleTradeVolumeRestrictionTMFactory.sol | 79 -- test/helpers/createInstances.js | 14 - ...ckpoints.js => x_scheduled_checkpoints.js} | 0 test/x_single_trade_volume_restriction.js | 726 ------------------ ...ction_tm.js => y_volume_restriction_tm.js} | 0 6 files changed, 1150 deletions(-) delete mode 100644 contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTM.sol delete mode 100644 contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol rename test/{y_scheduled_checkpoints.js => x_scheduled_checkpoints.js} (100%) delete mode 100644 test/x_single_trade_volume_restriction.js rename test/{z_volume_restriction_tm.js => y_volume_restriction_tm.js} (100%) diff --git a/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTM.sol deleted file mode 100644 index b92272167..000000000 --- a/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTM.sol +++ /dev/null @@ -1,331 +0,0 @@ -pragma solidity ^0.4.24; - -import "./../../TransferManager/ITransferManager.sol"; -import "openzeppelin-solidity/contracts/math/SafeMath.sol"; - -/** - * @title Transfer Manager for limiting volume of tokens in a single trade - */ - -contract SingleTradeVolumeRestrictionTM is ITransferManager { - using SafeMath for uint256; - - bytes32 constant public ADMIN = "ADMIN"; - - bool public isTransferLimitInPercentage; - - 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; - - // Ignore transactions which are part of the primary issuance - bool public allowPrimaryIssuance = true; - - //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; - - event ExemptWalletAdded(address _wallet); - event ExemptWalletRemoved(address _wallet); - event TransferLimitInTokensSet(address _wallet, uint256 _amount); - event TransferLimitInPercentageSet(address _wallet, uint _percentage); - event TransferLimitInPercentageRemoved(address _wallet); - event TransferLimitInTokensRemoved(address _wallet); - event GlobalTransferLimitInTokensSet(uint256 _amount, uint256 _oldAmount); - event GlobalTransferLimitInPercentageSet(uint256 _percentage, uint256 _oldPercentage); - event TransferLimitChangedToTokens(); - event TransferLimitChangedtoPercentage(); - event SetAllowPrimaryIssuance(bool _allowPrimaryIssuance, uint256 _timestamp); - - /** - * @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 and prevent an account from sending more tokens than allowed in a single transfer - * @param _from Address of the sender - * @param _amount The amount of tokens to transfer - */ - 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 (_from == address(0) && allowPrimaryIssuance) { - return Result.NA; - } - - 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) { - validTransfer = _amount <= specialTransferLimitsInTokens[_from]; - } else { - validTransfer = _amount <= globalTransferLimitInTokens; - } - } - 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, - bool _allowPrimaryIssuance - ) public onlyFactory { - isTransferLimitInPercentage = _isTransferLimitInPercentage; - if (isTransferLimitInPercentage) { - changeGlobalLimitInPercentage(_globalTransferLimitInPercentageOrToken); - } else { - changeGlobalLimitInTokens(_globalTransferLimitInPercentageOrToken); - } - allowPrimaryIssuance = _allowPrimaryIssuance; - } - - /** - * @notice Sets whether or not to consider primary issuance transfers - * @param _allowPrimaryIssuance whether to allow all primary issuance transfers - */ - function setAllowPrimaryIssuance(bool _allowPrimaryIssuance) public withPerm(ADMIN) { - require(_allowPrimaryIssuance != allowPrimaryIssuance, "Must change setting"); - allowPrimaryIssuance = _allowPrimaryIssuance; - /*solium-disable-next-line security/no-block-members*/ - emit SetAllowPrimaryIssuance(_allowPrimaryIssuance, now); - } - - /** - * @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 Changes the global transfer limit - * @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 _newGlobalTransferLimitInTokens) public withPerm(ADMIN) { - require(!isTransferLimitInPercentage, "Transfer limit not set in tokens"); - require(_newGlobalTransferLimitInTokens > 0, "Transfer limit has to greater than zero"); - emit GlobalTransferLimitInTokensSet(_newGlobalTransferLimitInTokens, globalTransferLimitInTokens); - globalTransferLimitInTokens = _newGlobalTransferLimitInTokens; - - } - - /** - * @notice Changes 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 > 0 && _newGlobalTransferLimitInPercentage <= 100 * 10 ** 16, "Limit not within [0,100]"); - emit GlobalTransferLimitInPercentageSet(_newGlobalTransferLimitInPercentage, globalTransferLimitInPercentage); - globalTransferLimitInPercentage = _newGlobalTransferLimitInPercentage; - - } - - /** - * @notice Adds 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 Removes 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 Sets 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 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"); - specialTransferLimitsInTokens[_wallet] = _transferLimit; - emit TransferLimitInTokensSet(_wallet, _transferLimit); - } - - /** - * @notice Sets 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"); - require(_transferLimitInPercentage > 0 && _transferLimitInPercentage <= 100 * 10 ** 16, "Transfer limit not in required range"); - specialTransferLimitsInPercentages[_wallet] = _transferLimitInPercentage; - emit TransferLimitInPercentageSet(_wallet, _transferLimitInPercentage); - } - - - /** - * @notice Removes transfer limit set in percentage for a wallet - * @param _wallet wallet address - */ - 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 set in tokens 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); - } - - /** - * @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 setTransferLimitInTokensMulti(address[] _wallets, uint[] _transferLimits) public withPerm(ADMIN) { - require(_wallets.length > 0, "Wallets cannot be empty"); - require(_wallets.length == _transferLimits.length, "Wallets don't match to transfer limits"); - for (uint256 i = 0; i < _wallets.length; i++ ) { - setTransferLimitInTokens(_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, "Wallets don't match to percentage limits"); - for (uint256 i = 0; i < _wallets.length; i++) { - setTransferLimitInPercentage(_wallets[i], _transferLimitsInPercentage[i]); - } - } - - /** - * @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) { - require(_wallets.length > 0, "Wallets cannot be empty"); - for (uint i = 0; i < _wallets.length; i++) { - removeTransferLimitInTokens(_wallets[i]); - } - } - - /** - * @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) { - require(_wallets.length > 0, "Wallets cannot be empty"); - for (uint i = 0; i < _wallets.length; i++) { - removeTransferLimitInPercentage(_wallets[i]); - } - } - - /** - * @notice This function returns the signature of configure function - */ - function getInitFunction() public pure returns (bytes4) { - return bytes4(keccak256("configure(bool,uint256,bool)")); - } - - /** - * @notice Returns 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/Experimental/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol b/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol deleted file mode 100644 index e6d8ed2be..000000000 --- a/contracts/modules/Experimental/TransferManager/SingleTradeVolumeRestrictionTMFactory.sol +++ /dev/null @@ -1,79 +0,0 @@ -pragma solidity ^0.4.24; - -import "./../../ModuleFactory.sol"; -import "./SingleTradeVolumeRestrictionTM.sol"; -import "../../../libraries/Util.sol"; - -/** - * @title Factory for deploying SingleTradeVolumeRestrictionManager - */ -contract SingleTradeVolumeRestrictionTMFactory 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 = "SingleTradeVolumeRestrictionTM"; - 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)); - } - - /** - * @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"); - SingleTradeVolumeRestrictionTM singleTradeVolumeRestrictionManager = new SingleTradeVolumeRestrictionTM(msg.sender, address(polyToken)); - - require(Util.getSig(_data) == singleTradeVolumeRestrictionManager.getInitFunction(), "Provided data is not valid"); - /*solium-disable-next-line security/no-low-level-calls*/ - require(address(singleTradeVolumeRestrictionManager).call(_data), "Unsuccessful call"); - /*solium-disable-next-line security/no-block-members*/ - emit GenerateModuleFromFactory(address(singleTradeVolumeRestrictionManager), getName(), address(this), msg.sender, setupCost, now); - return address(singleTradeVolumeRestrictionManager); - } - - /** - * @notice Get the types of the Module factory - * @return uint8[] - */ - function getTypes() external view returns(uint8[]) { - uint8[] memory res = new uint8[](1); - res[0] = 2; - return res; - } - - /** - * @notice Get the Instructions that help to use the module - * @return string - */ - function getInstructions() external view returns(string) { - /*solium-disable-next-line max-len*/ - 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 tags related to the module factory - * @return bytes32[] - */ - function getTags() external 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/helpers/createInstances.js b/test/helpers/createInstances.js index 2303e7eed..6c130ab9f 100644 --- a/test/helpers/createInstances.js +++ b/test/helpers/createInstances.js @@ -11,7 +11,6 @@ const SecurityTokenRegistryMock = artifacts.require("./SecurityTokenRegistryMock const ERC20DividendCheckpointFactory = artifacts.require("./ERC20DividendCheckpointFactory.sol"); const EtherDividendCheckpointFactory = artifacts.require("./EtherDividendCheckpointFactory.sol"); const ManualApprovalTransferManagerFactory = artifacts.require("./ManualApprovalTransferManagerFactory.sol"); -const SingleTradeVolumeRestrictionManagerFactory = artifacts.require('./SingleTradeVolumeRestrictionTMFactory.sol'); const TrackedRedemptionFactory = artifacts.require("./TrackedRedemptionFactory.sol"); const PercentageTransferManagerFactory = artifacts.require("./PercentageTransferManagerFactory.sol"); const ScheduledCheckpointFactory = artifacts.require('./ScheduledCheckpointFactory.sol'); @@ -42,7 +41,6 @@ let I_TrackedRedemptionFactory; let I_ScheduledCheckpointFactory; let I_MockBurnFactory; let I_MockWrongTypeBurnFactory; -let I_SingleTradeVolumeRestrictionManagerFactory; let I_ManualApprovalTransferManagerFactory; let I_VolumeRestrictionTransferManagerFactory; let I_PercentageTransferManagerFactory; @@ -271,18 +269,6 @@ export async function deployLockupVolumeRTMAndVerified(accountPolymath, MRProxyI return new Array(I_VolumeRestrictionTransferManagerFactory); } -export async function deploySingleTradeVolumeRMAndVerified(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); - assert.notEqual( - I_SingleTradeVolumeRestrictionManagerFactory.address.valueOf(), - "0x0000000000000000000000000000000000000000", - "SingleTradeVolumeRestrictionManagerFactory contract was not deployed" - ); - - await registerAndVerifyByMR(I_SingleTradeVolumeRestrictionManagerFactory.address, accountPolymath, MRProxyInstance); - return new Array(I_SingleTradeVolumeRestrictionManagerFactory); -} - export async function deployScheduleCheckpointAndVerified(accountPolymath, MRProxyInstance, polyToken, setupCost) { I_ScheduledCheckpointFactory = await ScheduledCheckpointFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); assert.notEqual( diff --git a/test/y_scheduled_checkpoints.js b/test/x_scheduled_checkpoints.js similarity index 100% rename from test/y_scheduled_checkpoints.js rename to test/x_scheduled_checkpoints.js diff --git a/test/x_single_trade_volume_restriction.js b/test/x_single_trade_volume_restriction.js deleted file mode 100644 index 15c01e68c..000000000 --- a/test/x_single_trade_volume_restriction.js +++ /dev/null @@ -1,726 +0,0 @@ -import latestTime from './helpers/latestTime'; -import { duration, promisifyLogWatch, latestBlock } from './helpers/utils'; -import { takeSnapshot, increaseTime, revertToSnapshot } from './helpers/time'; -import { encodeModuleCall } from './helpers/encodeCall'; -import {deploySingleTradeVolumeRMAndVerified, setUpPolymathNetwork } from "./helpers/createInstances"; -import { catchRevert } from "./helpers/exceptions"; - -const SecurityToken = artifacts.require('./SecurityToken.sol'); -const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol'); -const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); -const SingleTradeVolumeRestrictionManager = artifacts.require('./SingleTradeVolumeRestrictionTM'); -const CountTransferManagerFactory = artifacts.require('./CountTransferManagerFactory.sol'); -const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); - -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_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; - - // SecurityToken Details - const name = "Team"; - const symbol = "sap"; - const tokenDetails = "This is equity type of issuance"; - const decimals = 18; - const contact = "team@polymath.network"; - const STVRParameters = ["bool", "uint256", "bool"]; - - // 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]; - - let instances = await setUpPolymathNetwork(account_polymath, token_owner); - - [ - I_PolymathRegistry, - I_PolyToken, - I_FeatureRegistry, - I_ModuleRegistry, - I_ModuleRegistryProxy, - I_MRProxied, - I_GeneralTransferManagerFactory, - I_STFactory, - I_SecurityTokenRegistry, - I_SecurityTokenRegistryProxy, - I_STRProxied - ] = instances; - - // STEP 4: Deploy the SingleTradeVolumeRestrictionManagerFactory - [I_SingleTradeVolumeRestrictionManagerFactory] = await deploySingleTradeVolumeRMAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, 0); - [P_SingleTradeVolumeRestrictionManagerFactory] = await deploySingleTradeVolumeRMAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500")); - - }); - - 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._types[0].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.getModulesByType(2))[0]; - I_GeneralTransferManager = GeneralTransferManager.at(moduleData); - }); - }); - // - 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 = encodeModuleCall(STVRParameters, [true, 90, false]); - - await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); - await catchRevert( - I_SecurityToken.addModule(P_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, web3.utils.toWei("500", "ether"), 0, { from: token_owner}) - ); - }); - - it("Should successfully attach the Paid SingleTradeVolumeRestrictionManager with the security token", async () => { - let managerArgs = encodeModuleCall(STVRParameters, [false, 90, false]); - 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._types[0].toNumber(), transferManagerKey, "SingleTradeVolumeRestrictionManager did not get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[3].args._name) - .replace(/\u0000/g, ''), - "SingleTradeVolumeRestrictionTM", - "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 = encodeModuleCall(STVRParameters, [false, (7 * Math.pow(10, 16)).toString(), false]) - const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { - from: token_owner - }); - assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "TransferManager doesn't get deployed"); - assert.equal( - web3.utils.toAscii(tx.logs[2].args._name) - .replace(/\u0000/g, ''), - "SingleTradeVolumeRestrictionTM", - "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 = encodeModuleCall(STVRParameters, [true, 90, false]); - const tx = await I_SecurityToken.addModule(I_SingleTradeVolumeRestrictionManagerFactory.address, managerArgs, 0, 0, { - from: token_owner - }); - 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, ''), - "SingleTradeVolumeRestrictionTM", - "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("Should allow the primary issuance", async() => { - let snapId = await takeSnapshot(); - await I_SingleTradeVolumeRestrictionManager.setAllowPrimaryIssuance(true, {from: token_owner}); - await catchRevert( - I_SingleTradeVolumeRestrictionManager.setAllowPrimaryIssuance(true, {from: token_owner}) - ) - await revertToSnapshot(snapId); - }) - - it("add exempt wallet -- Not authorised ", async () => { - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.addExemptWallet(accounts[5]) - ); - - await catchRevert( - I_SingleTradeVolumeRestrictionManager.addExemptWallet(zero_address, { from: token_owner }) - ); - - 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 () => { - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.removeExemptWallet(accounts[5]) - ); - // 0 address are not allowed to add - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.removeExemptWallet(zero_address, { from: token_owner }) - ); - - 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 () => { - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 100) - ); - - // Transfer limits can't be set to 0 - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.setTransferLimitInTokens(accounts[4], 0, { from: token_owner }) - ); - - // Transfer limit cannot be set in percentage - await catchRevert( - I_SingleTradeVolumeRestrictionManager.setTransferLimitInPercentage(accounts[4], 10, { from: token_owner }) - ); - - 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); - - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 0, { from: token_owner }) - ); - // Transfer limit can not be set to more 0 - await catchRevert ( - I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentage(accounts[4], 101 * 10 ** 16, { from: token_owner }) - ); - // Transfer limit in tokens can not be set for a manager that has transfer limit set as percentage - await catchRevert ( - I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInTokens(accounts[4], 1, { from: token_owner }) - ); - - 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 () => { - // Non Admins cannot set/remove transfer limits - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[4]) - ); - - // Non Admins cannot set/remove transfer limits - await catchRevert ( - I_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokens(accounts[0], { from: token_owner }) - ); - - 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 () => { - // only owner is allowed - await catchRevert( - I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(100 * 10 ** 18) - ); - //Cannot change global limit in percentage when set to tokens - await catchRevert( - I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInPercentage(100 * 10 ** 18, { from: token_owner }) - ); - // Global limit cannot be set to 0 - await catchRevert( - I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(0, { from: token_owner }) - ); - - let tx = await I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInTokens(10, { - from: token_owner - }); - assert.equal(tx.logs[0].args._amount, 10, "Global Limit not set"); - - //Global limit can be set by non-admins - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89) - ); - // cannot change global limit in tokens if transfer limit is set to percentage - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(89, { from: token_owner }) - ); - // Cannot set global limit in tokens to 0 - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(0, { from: token_owner }) - ); - - tx = await I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(40, { from: token_owner }); - assert.equal(tx.logs[0].args._percentage, 40, "Global Limit not set"); - // Global limit cannot be set to more than 100 - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInPercentage(101 * 10 ** 16, { from: token_owner }) - ); - // Global limit in percentage cannot be set when limit is in tokens - await catchRevert( - I_SingleTradeVolumeRestrictionManager.changeGlobalLimitInPercentage(10, { from: token_owner }) - ); - // Global limit in tokens cannot be set when limit is in percentage - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.changeGlobalLimitInTokens(10, { from: token_owner }) - ); - }); - - it("Should perform batch updates", async () => { - let wallets = [accounts[0], accounts[1], accounts[2]]; - let tokenLimits = [1, 2, 3]; - let percentageLimits = [5, 6, 7]; - - // Exempt wallet multi cannot be empty wallet - await catchRevert( - P_SingleTradeVolumeRestrictionManager.addExemptWalletMulti([], { from: token_owner }) - ); - - // 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"); - } - - // Exempt wallet multi cannot be empty wallet - await catchRevert( - P_SingleTradeVolumeRestrictionManager.removeExemptWalletMulti([], { from: token_owner }) - ); - - // 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"); - } - // wallets cannot be empty - await catchRevert( - P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti([], tokenLimits, { from: token_owner }) - ); - // wallet array length dont match - await catchRevert( - P_SingleTradeVolumeRestrictionManager.setTransferLimitInTokensMulti([accounts[0]], tokenLimits, { from: token_owner }) - ); - - 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]); - } - // Wallets cannot be empty - await catchRevert( - P_SingleTradeVolumeRestrictionManager.removeTransferLimitInTokensMulti([], { from: token_owner }) - ); - 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"); - } - // wallets cannot be empty - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti([], percentageLimits, { from: token_owner }) - ); - // wallets and amounts dont match be empty - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.setTransferLimitInPercentageMulti(wallets, [], { 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++) { - assert.equal(logs[i].args._wallet, wallets[i], "Transfer limit not set for wallet"); - assert.equal(logs[i].args._percentage.toNumber(), percentageLimits[i]); - } - // Wallets cannot be empty - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentageMulti([], { from: token_owner }) - ); - - 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"); - } - // Wallet should not be removed - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.removeTransferLimitInPercentage(wallets[0], { 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(), - 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 - }) - // Transfer should have not happened - await catchRevert( - I_SecurityToken.transfer(account_investor3, web3.utils.toWei('6', 'ether'), { from: account_investor1 }) - ); - - 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 - }); - - // Transfer should have not happened - await catchRevert( - I_SecurityToken.transfer(account_investor4, web3.utils.toWei('7', 'ether'), { from: account_investor5 }) - ); - - 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 - }); - - // Transfer above limit happened - await catchRevert( - I_SecurityToken.transfer(account_investor2, web3.utils.toWei('90', 'ether'), { from: account_investor1 }) - ); - - 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 - }); - // transfer happened above limit - await catchRevert( - I_SecurityToken.transfer(account_investor2, web3.utils.toWei('35', 'ether'), { from: account_investor1 }) - ); - - 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 () => { - // Should not change to percentage again - await catchRevert( - I_SingleTradeVolumeRestrictionPercentageManager.changeTransferLimitToPercentage(1, { from: token_owner }) - ); - - - 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 () => { - // Should not change to tokens again - await catchRevert( - I_SingleTradeVolumeRestrictionManager.changeTransferLimitToTokens(1, { from: token_owner }) - ); - - 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.getSetupCost.call(), 0); - assert.equal((await I_SingleTradeVolumeRestrictionManagerFactory.getTypes.call())[0], 2); - let name = web3.utils.toUtf8(await I_SingleTradeVolumeRestrictionManagerFactory.getName.call()); - assert.equal(name, "SingleTradeVolumeRestrictionTM", "Wrong Module added"); - let desc = await I_SingleTradeVolumeRestrictionManagerFactory.description.call(); - assert.equal(desc, "Imposes volume restriction on a single trade", "Wrong Module added"); - let title = await I_SingleTradeVolumeRestrictionManagerFactory.title.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.version.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/z_volume_restriction_tm.js b/test/y_volume_restriction_tm.js similarity index 100% rename from test/z_volume_restriction_tm.js rename to test/y_volume_restriction_tm.js From 2e220011affc1c5857d572540194160507d17f20 Mon Sep 17 00:00:00 2001 From: satyam Date: Fri, 30 Nov 2018 19:01:10 +0530 Subject: [PATCH 22/56] contract changes --- .../TransferManager/VolumeRestrictionTM.sol | 252 ++--- .../VolumeRestrictionTM_prev.sol | 932 ++++++++++++++++++ test/y_volume_restriction_tm.js | 529 +++++----- 3 files changed, 1279 insertions(+), 434 deletions(-) create mode 100644 contracts/modules/TransferManager/VolumeRestrictionTM_prev.sol diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index 1ccb525be..e3282fd65 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -11,11 +11,13 @@ contract VolumeRestrictionTM is ITransferManager { // permission definition bytes32 public constant ADMIN = "ADMIN"; - enum RestrictionType { Fixed, Variable } + enum RestrictionType { Fixed, Percentage } struct VolumeRestriction { + // If typeOfRestriction is `Percentage` then allowedTokens will be in + // the % (w.r.t to totalSupply) with a multiplier of 10**16 . else it + // will be fixed amount of tokens uint256 allowedTokens; - uint256 allowedPercentageOfTokens; uint256 startTime; uint256 rollingPeriodInDays; uint256 endTime; @@ -23,8 +25,9 @@ contract VolumeRestrictionTM is ITransferManager { } struct BucketDetails { - uint256[] timestamps; + uint256 lastTradedDayTime; uint256 sumOfLastPeriod; // It is the sum of transacted amount within the last rollingPeriodDays + uint256 daysCovered; // No of days covered till (from the startTime of VolumeRestriction) } // Global restriction that applies to all token holders @@ -50,7 +53,6 @@ contract VolumeRestrictionTM is ITransferManager { event AddNewIndividualRestriction( address indexed _holder, uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, @@ -60,7 +62,6 @@ contract VolumeRestrictionTM is ITransferManager { event ModifyIndividualRestriction( address indexed _holder, uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, @@ -69,7 +70,6 @@ contract VolumeRestrictionTM is ITransferManager { // Emit when the new global restriction is added event AddGlobalRestriction( uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, @@ -78,7 +78,6 @@ contract VolumeRestrictionTM is ITransferManager { // Emit when the new daily (global) restriction is added event AddDailyGlobalRestriction( uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, @@ -87,7 +86,6 @@ contract VolumeRestrictionTM is ITransferManager { // Emit when global restriction get modified event ModifyGlobalRestriction( uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, @@ -96,7 +94,6 @@ contract VolumeRestrictionTM is ITransferManager { // Emit when daily global restriction get modified event ModifyDailyGlobalRestriction( uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, @@ -159,16 +156,14 @@ contract VolumeRestrictionTM is ITransferManager { * @notice Use to add the new individual restriction for a given token holder * @param _holder Address of the token holder, whom restriction will be implied * @param _allowedTokens Amount of tokens allowed to be trade for a given address. - * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. * @param _startTime Unix timestamp at which restriction get into effect * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) * @param _endTime Unix timestamp at which restriction effects will gets end. - * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) */ function addIndividualRestriction( address _holder, uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, @@ -180,7 +175,6 @@ contract VolumeRestrictionTM is ITransferManager { _addIndividualRestriction( _holder, _allowedTokens, - _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, @@ -192,16 +186,14 @@ contract VolumeRestrictionTM is ITransferManager { * @notice Use to add the new individual restriction for multiple token holders * @param _holders Array of address of the token holders, whom restriction will be implied * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. - * @param _allowedPercentageOfTokens Array of percentage of tokens w.r.t to totalSupply allowed to transact. * @param _startTimes Array of unix timestamps at which restrictions get into effect * @param _rollingPeriodInDays Array of rolling period in days (Minimum value should be 1 day) * @param _endTimes Array of unix timestamps at which restriction effects will gets end. - * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for variable) + * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) */ function addIndividualRestrictionMulti( address[] _holders, uint256[] _allowedTokens, - uint256[] _allowedPercentageOfTokens, uint256[] _startTimes, uint256[] _rollingPeriodInDays, uint256[] _endTimes, @@ -210,13 +202,12 @@ contract VolumeRestrictionTM is ITransferManager { public withPerm(ADMIN) { - _checkLengthOfArray(_allowedTokens, _allowedPercentageOfTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); + _checkLengthOfArray(_allowedTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); require(_holders.length == _allowedTokens.length, "Length mismatch"); for (uint256 i = 0; i < _holders.length; i++) { _addIndividualRestriction( _holders[i], _allowedTokens[i], - _allowedPercentageOfTokens[i], _startTimes[i], _rollingPeriodInDays[i], _endTimes[i], @@ -228,15 +219,13 @@ contract VolumeRestrictionTM is ITransferManager { /** * @notice Use to add the new global restriction for all token holder * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. - * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. * @param _startTime Unix timestamp at which restriction get into effect * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) * @param _endTime Unix timestamp at which restriction effects will gets end. - * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) */ function addGlobalRestriction( uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, @@ -249,13 +238,12 @@ contract VolumeRestrictionTM is ITransferManager { globalRestriction.endTime < now, "Not allowed" ); - _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); if (globalRestriction.endTime != 0) { removeGlobalRestriction(); } globalRestriction = VolumeRestriction( _allowedTokens, - _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, @@ -263,7 +251,6 @@ contract VolumeRestrictionTM is ITransferManager { ); emit AddGlobalRestriction( _allowedTokens, - _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, @@ -274,14 +261,12 @@ contract VolumeRestrictionTM is ITransferManager { /** * @notice Use to add the new global daily restriction for all token holder * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. - * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. * @param _startTime Unix timestamp at which restriction get into effect * @param _endTime Unix timestamp at which restriction effects will gets end. - * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) */ function addDailyGlobalRestriction( uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _endTime, uint256 _restrictionType @@ -293,10 +278,9 @@ contract VolumeRestrictionTM is ITransferManager { dailyGlobalRestriction.endTime < now, "Not Allowed" ); - _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, 1, _endTime, _restrictionType); + _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); dailyGlobalRestriction = VolumeRestriction( _allowedTokens, - _allowedPercentageOfTokens, _startTime, 1, _endTime, @@ -304,7 +288,6 @@ contract VolumeRestrictionTM is ITransferManager { ); emit AddDailyGlobalRestriction( _allowedTokens, - _allowedPercentageOfTokens, _startTime, 1, _endTime, @@ -335,9 +318,10 @@ contract VolumeRestrictionTM is ITransferManager { */ function removeGlobalRestriction() public withPerm(ADMIN) { require(globalRestriction.endTime != 0); - globalRestriction = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); - globalBucketDetails.timestamps.length = 0; + globalRestriction = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); + globalBucketDetails.lastTradedDayTime = 0; globalBucketDetails.sumOfLastPeriod = 0; + globalBucketDetails.daysCovered = 0; emit GlobalRestrictionRemoved(); } @@ -346,7 +330,7 @@ contract VolumeRestrictionTM is ITransferManager { */ function removeDailyGlobalRestriction() external withPerm(ADMIN) { require(dailyGlobalRestriction.endTime != 0); - dailyGlobalRestriction = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); + dailyGlobalRestriction = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); emit DailyGlobalRestrictionRemoved(); } @@ -354,16 +338,14 @@ contract VolumeRestrictionTM is ITransferManager { * @notice Use to modify the existing individual restriction for a given token holder * @param _holder Address of the token holder, whom restriction will be implied * @param _allowedTokens Amount of tokens allowed to be trade for a given address. - * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. * @param _startTime Unix timestamp at which restriction get into effect * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) * @param _endTime Unix timestamp at which restriction effects will gets end. - * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) */ function modifyIndividualRestriction( address _holder, uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, @@ -375,7 +357,6 @@ contract VolumeRestrictionTM is ITransferManager { _modifyIndividualRestriction( _holder, _allowedTokens, - _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, @@ -387,16 +368,14 @@ contract VolumeRestrictionTM is ITransferManager { * @notice Use to modify the existing individual restriction for multiple token holders * @param _holders Array of address of the token holders, whom restriction will be implied * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. - * @param _allowedPercentageOfTokens Array of percentage of tokens w.r.t to totalSupply allowed to transact. * @param _startTimes Array of unix timestamps at which restrictions get into effect * @param _rollingPeriodInDays Array of rolling period in days (Minimum value should be 1 day) * @param _endTimes Array of unix timestamps at which restriction effects will gets end. - * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for variable) + * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) */ function modifyIndividualRestrictionMulti( address[] _holders, uint256[] _allowedTokens, - uint256[] _allowedPercentageOfTokens, uint256[] _startTimes, uint256[] _rollingPeriodInDays, uint256[] _endTimes, @@ -405,13 +384,12 @@ contract VolumeRestrictionTM is ITransferManager { public withPerm(ADMIN) { - _checkLengthOfArray(_allowedTokens, _allowedPercentageOfTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); + _checkLengthOfArray(_allowedTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); require(_holders.length == _allowedTokens.length, "Length mismatch"); for (uint256 i = 0; i < _holders.length; i++) { _modifyIndividualRestriction( _holders[i], _allowedTokens[i], - _allowedPercentageOfTokens[i], _startTimes[i], _rollingPeriodInDays[i], _endTimes[i], @@ -423,15 +401,13 @@ contract VolumeRestrictionTM is ITransferManager { /** * @notice Use to modify the global restriction for all token holder * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. - * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. * @param _startTime Unix timestamp at which restriction get into effect * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) * @param _endTime Unix timestamp at which restriction effects will gets end. - * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) */ function modifyGlobalRestriction( uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, @@ -441,10 +417,9 @@ contract VolumeRestrictionTM is ITransferManager { withPerm(ADMIN) { require(globalRestriction.startTime > now, "Not allowed"); - _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); globalRestriction = VolumeRestriction( _allowedTokens, - _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, @@ -452,7 +427,6 @@ contract VolumeRestrictionTM is ITransferManager { ); emit ModifyGlobalRestriction( _allowedTokens, - _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, @@ -463,14 +437,12 @@ contract VolumeRestrictionTM is ITransferManager { /** * @notice Use to modify the daily global restriction for all token holder * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. - * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. * @param _startTime Unix timestamp at which restriction get into effect * @param _endTime Unix timestamp at which restriction effects will gets end. - * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) */ function modifyDailyGlobalRestriction( uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _endTime, uint256 _restrictionType @@ -479,10 +451,9 @@ contract VolumeRestrictionTM is ITransferManager { withPerm(ADMIN) { require(dailyGlobalRestriction.startTime > now, "Not allowed"); - _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, 1, _endTime, _restrictionType); + _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); dailyGlobalRestriction = VolumeRestriction( _allowedTokens, - _allowedPercentageOfTokens, _startTime, 1, _endTime, @@ -490,7 +461,6 @@ contract VolumeRestrictionTM is ITransferManager { ); emit ModifyDailyGlobalRestriction( _allowedTokens, - _allowedPercentageOfTokens, _startTime, 1, _endTime, @@ -502,22 +472,21 @@ contract VolumeRestrictionTM is ITransferManager { * @notice Internal function have a logic to validate the txn amount with global restriction */ function _globalRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { - uint256[] memory timestamps = globalBucketDetails.timestamps; uint256 fromTimestamp; uint256 sumOfLastPeriod = 0; - if (timestamps.length == 0) { + uint256 daysCovered = 0; + if (globalBucketDetails.lastTradedDayTime == 0) { // It will execute when the txn is performed first time after the addition of global restriction fromTimestamp = globalRestriction.startTime; } else { // picking up the preivous timestamp - fromTimestamp = timestamps[timestamps.length -1]; + fromTimestamp = globalBucketDetails.lastTradedDayTime; } // Calculating the difference of days uint256 diffDays = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now); - uint256[] memory passedTimestamps = new uint256[](diffDays); // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod // re-using the local variables to avoid the stack too deep error. - (sumOfLastPeriod, fromTimestamp, passedTimestamps) = _bucketCheck( + (sumOfLastPeriod, fromTimestamp, daysCovered) = _bucketCheck( fromTimestamp, diffDays, _from, @@ -533,11 +502,10 @@ contract VolumeRestrictionTM is ITransferManager { if(_isTransfer) { // update the global storage _updateGlobalStorage( - passedTimestamps, fromTimestamp, _amount, - diffDays, - sumOfLastPeriod + sumOfLastPeriod, + daysCovered ); } return Result.NA; @@ -549,30 +517,23 @@ contract VolumeRestrictionTM is ITransferManager { * @notice Internal function to update the state variables related to global restriction */ function _updateGlobalStorage( - uint256[] passedTimestamps, uint256 _fromTime, uint256 _amount, - uint256 _diffDays, - uint256 _sumOfLastPeriod + uint256 _sumOfLastPeriod, + uint256 _daysCovered ) internal { - if (_diffDays != 0) { - for (uint256 i = 0; i < passedTimestamps.length; i++) { - // Add the timestamp that is already passed - globalBucketDetails.timestamps.push(passedTimestamps[i]); - } + if (globalBucketDetails.lastTradedDayTime != _fromTime) { + // Assigning the latest transaction timestamp of the day + globalBucketDetails.lastTradedDayTime = _fromTime; + globalBucketDetails.daysCovered = _daysCovered; } - // This condition is the works only when the transaction performed just after the startTime (_diffDays == 0) - if (globalBucketDetails.timestamps.length == 0) { - globalBucketDetails.timestamps.push(_fromTime); - } - if(_amount != 0) { + if (_amount != 0) { // updating the sumOfLastPeriod globalBucketDetails.sumOfLastPeriod = _sumOfLastPeriod.add(_amount); - // Re-using the local variable to avoid stack too deep error - _fromTime = globalBucketDetails.timestamps[globalBucketDetails.timestamps.length -1]; + // Increasing the total amount of the day by `_amount` globalBucket[_fromTime] = globalBucket[_fromTime].add(_amount); } } @@ -582,49 +543,49 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _fromTime, uint256 _diffDays, address _from, - BucketDetails _bucketDetails, + BucketDetails memory _bucketDetails, bool _isGlobal, VolumeRestriction _restriction ) internal view - returns (uint256, uint256, uint256[]) + returns (uint256, uint256, uint256) { - uint256[] memory passedTimestamps = new uint256[](_diffDays); - uint256 counter = _bucketDetails.timestamps.length; + uint256 counter = _bucketDetails.daysCovered; uint256 i = 0; - if (_diffDays != 0) { + if (_diffDays >= _restriction.rollingPeriodInDays) { + // If the difference of days is greater than the rollingPeriod then sumOfLastPeriod will always be zero + _bucketDetails.sumOfLastPeriod = 0; + + } else { for (i = 0; i < _diffDays; i++) { - // calculating the timestamp that will used as an index of the next bucket - // i.e buckets period will be look like this T1 to T2-1, T2 to T3-1 .... - // where T1,T2,T3 are timestamps having 24 hrs difference - _fromTime = _fromTime.add(1 days); - - // This condition is to check whether the first rolling period is covered or not - // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting - // the earlier value at that index - if (counter >= _restriction.rollingPeriodInDays) { - if (_isGlobal) { - // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod - _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. - sub(globalBucket[_bucketDetails.timestamps[counter.sub(_restriction.rollingPeriodInDays)]]); - } else { - // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod - _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. - sub(bucket[_from][_bucketDetails.timestamps[counter.sub(_restriction.rollingPeriodInDays)]]); - } - + // This condition is to check whether the first rolling period is covered or not + // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting + // the earlier value at that index + if (counter >= _restriction.rollingPeriodInDays) { + if (_isGlobal) { + // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod + _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. + sub(globalBucket[_bucketDetails.lastTradedDayTime.sub((counter.sub(_restriction.rollingPeriodInDays)).mul(1 days))]); + } else { + // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod + _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. + sub(bucket[_from][_bucketDetails.lastTradedDayTime.sub((counter.sub(_restriction.rollingPeriodInDays)).mul(1 days))]); } + } - // Adding the last amount that is transacted on the `_fromTime` not actually doing it but left written to understand - // the alogrithm - //_bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); - // Storing all those timestamps whose total transacted value is 0 - passedTimestamps[i] = _fromTime; - counter++; + // Adding the last amount that is transacted on the `_fromTime` not actually doing it but left written to understand + // the alogrithm + //_bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); + // Storing all those timestamps whose total transacted value is 0 + counter++; } } - return (_bucketDetails.sumOfLastPeriod, _fromTime, passedTimestamps); + // calculating the timestamp that will used as an index of the next bucket + // i.e buckets period will be look like this T1 to T2-1, T2 to T3-1 .... + // where T1,T2,T3 are timestamps having 24 hrs difference + _fromTime = _fromTime.add(_diffDays.mul(1 days)); + return (_bucketDetails.sumOfLastPeriod, _fromTime, counter.add(_diffDays)); } /** @@ -639,21 +600,22 @@ contract VolumeRestrictionTM is ITransferManager { internal returns(Result) { + uint256 daysCovered; uint256 fromTimestamp; uint256 sumOfLastPeriod = 0; - if (bucketToUser[_from].timestamps.length == 0) { + if (bucketToUser[_from].lastTradedDayTime == 0) { // It will execute when the txn is performed first time after the addition of individual restriction fromTimestamp = individualRestriction[_from].startTime; } else { // Picking up the last timestamp - fromTimestamp = bucketToUser[_from].timestamps[bucketToUser[_from].timestamps.length -1]; + fromTimestamp = bucketToUser[_from].lastTradedDayTime; } // Calculating the difference of days uint256 diffDays = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now); - uint256[] memory passedTimestamps = new uint256[](diffDays); + // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod // re-using the local variables to avoid the stack too deep error. - (sumOfLastPeriod, fromTimestamp, passedTimestamps) = _bucketCheck( + (sumOfLastPeriod, fromTimestamp, daysCovered) = _bucketCheck( fromTimestamp, diffDays, _from, @@ -664,12 +626,11 @@ contract VolumeRestrictionTM is ITransferManager { if (_checkValidAmountToTransact(sumOfLastPeriod, _amount, individualRestriction[_from])) { if (_isTransfer) { _updateIndividualStorage( - passedTimestamps, _from, fromTimestamp, _amount, - diffDays, - sumOfLastPeriod + sumOfLastPeriod, + daysCovered ); } return Result.NA; @@ -707,8 +668,8 @@ contract VolumeRestrictionTM is ITransferManager { returns (bool) { uint256 _allowedAmount = 0; - if (_restriction.typeOfRestriction == RestrictionType.Variable) { - _allowedAmount = (_restriction.allowedPercentageOfTokens.mul(ISecurityToken(securityToken).totalSupply()))/ 10 ** 18; + if (_restriction.typeOfRestriction == RestrictionType.Percentage) { + _allowedAmount = (_restriction.allowedTokens.mul(ISecurityToken(securityToken).totalSupply())) / uint256(10) ** 18; } else { _allowedAmount = _restriction.allowedTokens; } @@ -721,30 +682,23 @@ contract VolumeRestrictionTM is ITransferManager { } function _updateIndividualStorage( - uint256[] passedTimestamps, address _from, uint256 _fromTime, uint256 _amount, - uint256 _diffDays, - uint256 _sumOfLastPeriod + uint256 _sumOfLastPeriod, + uint256 _daysCovered ) internal { - if (_diffDays != 0) { - for (uint256 i = 0; i < passedTimestamps.length; i++) { - // Add the timestamp that is already passed - bucketToUser[_from].timestamps.push(passedTimestamps[i]); - } - } - // This condition is the works only when the transaction performed just after the startTime (_diffDays == 0) - if (bucketToUser[_from].timestamps.length == 0) { - bucketToUser[_from].timestamps.push(_fromTime); + if (bucketToUser[_from].lastTradedDayTime != _fromTime) { + // Assigning the latest transaction timestamp of the day + bucketToUser[_from].lastTradedDayTime = _fromTime; + bucketToUser[_from].daysCovered = _daysCovered; } if(_amount != 0) { bucketToUser[_from].sumOfLastPeriod = _sumOfLastPeriod.add(_amount); - // Re-using the local variable to avoid stack too deep error - _fromTime = bucketToUser[_from].timestamps[bucketToUser[_from].timestamps.length -1]; + // Increasing the total amount of the day by `_amount` bucket[_from][_fromTime] = bucket[_from][_fromTime].add(_amount); } @@ -753,16 +707,16 @@ contract VolumeRestrictionTM is ITransferManager { function _removeIndividualRestriction(address _user) internal { require(_user != address(0), "Invalid address"); require(individualRestriction[_user].endTime != 0, "Not present"); - individualRestriction[_user] = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); - bucketToUser[_user].timestamps.length = 0; + individualRestriction[_user] = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); + bucketToUser[_user].lastTradedDayTime = 0; bucketToUser[_user].sumOfLastPeriod = 0; + bucketToUser[_user].daysCovered = 0; emit IndividualRestrictionRemoved(_user); } function _modifyIndividualRestriction( address _holder, uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, @@ -770,12 +724,11 @@ contract VolumeRestrictionTM is ITransferManager { ) internal { - _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); require(individualRestriction[_holder].startTime > now, "Not allowed"); individualRestriction[_holder] = VolumeRestriction( _allowedTokens, - _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, @@ -784,7 +737,6 @@ contract VolumeRestrictionTM is ITransferManager { emit ModifyIndividualRestriction( _holder, _allowedTokens, - _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, @@ -795,7 +747,6 @@ contract VolumeRestrictionTM is ITransferManager { function _addIndividualRestriction( address _holder, uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, @@ -808,14 +759,13 @@ contract VolumeRestrictionTM is ITransferManager { "Already present" ); require(_holder != address(0) && !exemptList[_holder], "Invalid address"); - _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); if (individualRestriction[_holder].endTime != 0) { _removeIndividualRestriction(_holder); } individualRestriction[_holder] = VolumeRestriction( _allowedTokens, - _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, @@ -824,7 +774,6 @@ contract VolumeRestrictionTM is ITransferManager { emit AddNewIndividualRestriction( _holder, _allowedTokens, - _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, @@ -834,7 +783,6 @@ contract VolumeRestrictionTM is ITransferManager { function _checkInputParams( uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, uint256 _startTime, uint256 _rollingPeriodDays, uint256 _endTime, @@ -848,7 +796,7 @@ contract VolumeRestrictionTM is ITransferManager { require(_allowedTokens > 0, "Invalid value"); } else { require( - _allowedPercentageOfTokens > 0 && _allowedPercentageOfTokens <= 100 * 10 ** 16, + _allowedTokens > 0 && _allowedTokens <= 100 * 10 ** 16, "Percentage is not within (0,100]" ); } @@ -860,7 +808,6 @@ contract VolumeRestrictionTM is ITransferManager { function _checkLengthOfArray( uint256[] _allowedTokens, - uint256[] _allowedPercentageOfTokens, uint256[] _startTimes, uint256[] _rollingPeriodInDays, uint256[] _endTimes, @@ -870,8 +817,7 @@ contract VolumeRestrictionTM is ITransferManager { pure { require( - _allowedTokens.length == _allowedPercentageOfTokens.length && - _allowedPercentageOfTokens.length == _startTimes.length && + _allowedTokens.length == _startTimes.length && _startTimes.length == _rollingPeriodInDays.length && _rollingPeriodInDays.length == _endTimes.length && _endTimes.length == _restrictionTypes.length, @@ -882,12 +828,12 @@ contract VolumeRestrictionTM is ITransferManager { /** * @notice Use to get the bucket details for a given address * @param _user Address of the token holder for whom the bucket details has queried - * @return uint256 Array of the timestamps + * @return uint256 lastTradedDayTime * @return uint256 sumOfLastPeriod */ - function getBucketDetailsToUser(address _user) external view returns(uint256[], uint256) { + function getBucketDetailsToUser(address _user) external view returns(uint256, uint256) { return( - bucketToUser[_user].timestamps, + bucketToUser[_user].lastTradedDayTime, bucketToUser[_user].sumOfLastPeriod ); } @@ -903,15 +849,15 @@ contract VolumeRestrictionTM is ITransferManager { /** * @notice Use to get the global bucket details - * @return uint256 Array of timestamps + * @return uint256 lastTradedDayTime * @return uint256 sumOfLastPeriod * @return uint256 Total amount traded on the latest timestamp */ - function getGlobalBucketDetails() external view returns(uint256[], uint256, uint256) { + function getGlobalBucketDetails() external view returns(uint256, uint256, uint256) { return( - globalBucketDetails.timestamps, + globalBucketDetails.lastTradedDayTime, globalBucketDetails.sumOfLastPeriod, - globalBucket[globalBucketDetails.timestamps[globalBucketDetails.timestamps.length - 1]] + globalBucket[globalBucketDetails.lastTradedDayTime] ); } diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM_prev.sol b/contracts/modules/TransferManager/VolumeRestrictionTM_prev.sol new file mode 100644 index 000000000..5a42ede3f --- /dev/null +++ b/contracts/modules/TransferManager/VolumeRestrictionTM_prev.sol @@ -0,0 +1,932 @@ +pragma solidity ^0.4.24; + +import "./ITransferManager.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../../libraries/BokkyPooBahsDateTimeLibrary.sol"; + +contract VolumeRestrictionTM_prev is ITransferManager { + + using SafeMath for uint256; + + // permission definition + bytes32 public constant ADMIN = "ADMIN"; + + enum RestrictionType { Fixed, Variable } + + struct VolumeRestriction { + uint256 allowedTokens; + uint256 allowedPercentageOfTokens; + uint256 startTime; + uint256 rollingPeriodInDays; + uint256 endTime; + RestrictionType typeOfRestriction; + } + + struct BucketDetails { + uint256[] timestamps; + uint256 sumOfLastPeriod; // It is the sum of transacted amount within the last rollingPeriodDays + } + + // Global restriction that applies to all token holders + VolumeRestriction public globalRestriction; + // Daily global restriction that applies to all token holders (Total ST traded daily is restricted) + VolumeRestriction public dailyGlobalRestriction; + // Variable stores the data matrix for the globa restrictions + BucketDetails internal globalBucketDetails; + // Restriction stored corresponds to a particular token holder + mapping(address => VolumeRestriction) public individualRestriction; + // Storing _from => day's timestamp => total amount transact in a day --individual + mapping(address => mapping(uint256 => uint256)) internal bucket; + // Storing the information that used to validate the transaction + mapping(address => BucketDetails) internal bucketToUser; + // List of wallets that are exempted from all the restrictions applied by the this contract + mapping(address => bool) public exemptList; + // Store the amount of tokens get transacted in a day + mapping(uint256 => uint256) public globalBucket; + + // Emit when the token holder is added/removed from the exemption list + event ChangedExemptWalletList(address indexed _wallet, bool _change); + // Emit when the new individual restriction is added corresponds to new token holders + event AddNewIndividualRestriction( + address indexed _holder, + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when the individual restriction is modified for a given address + event ModifyIndividualRestriction( + address indexed _holder, + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when the new global restriction is added + event AddGlobalRestriction( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when the new daily (global) restriction is added + event AddDailyGlobalRestriction( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when global restriction get modified + event ModifyGlobalRestriction( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when daily global restriction get modified + event ModifyDailyGlobalRestriction( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when the individual restriction gets removed + event IndividualRestrictionRemoved(address _user); + // Emit when the global restriction gets removed + event GlobalRestrictionRemoved(); + // Emit when the daily global restriction gets removed + event DailyGlobalRestrictionRemoved(); + + /** + * @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/transferFrom transaction and prevent tranaction + * whose volume of tokens will voilate the maximum volume transfer restriction + * @param _from Address of the sender + * @param _amount The amount of tokens to transfer + * @param _isTransfer Whether or not this is an actual transfer or just a test to see if the tokens would be transferrable + */ + function verifyTransfer(address _from, address /*_to */, uint256 _amount, bytes /*_data*/, bool _isTransfer) public returns (Result) { + // If `_from` is present in the exemptionList or it is `0x0` address then it will not follow the vol restriction + if (!paused && _from != address(0) && !exemptList[_from]) { + // Function must only be called by the associated security token if _isTransfer == true + require(msg.sender == securityToken || !_isTransfer); + // Checking the individual restriction if the `_from` comes in the individual category + if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { + return _individualRestrictionCheck(_from, _amount, _isTransfer); + // If the `_from` doesn't fall under the individual category. It will processed with in the global category automatically + } else if (globalRestriction.endTime >= now && globalRestriction.startTime <= now) { + return _globalRestrictionCheck(_from, _amount, _isTransfer); + } + } + return Result.NA; + } + + /** + * @notice Add/Remove wallet address from the exempt list + * @param _wallet Ethereum wallet/contract address that need to be exempted + * @param _change Boolean value used to add (i.e true) or remove (i.e false) from the list + */ + function changeExemptWalletList(address _wallet, bool _change) public withPerm(ADMIN) { + require(_wallet != address(0), "Invalid address"); + exemptList[_wallet] = _change; + emit ChangedExemptWalletList(_wallet, _change); + } + + /** + * @notice Use to add the new individual restriction for a given token holder + * @param _holder Address of the token holder, whom restriction will be implied + * @param _allowedTokens Amount of tokens allowed to be trade for a given address. + * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function addIndividualRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + _addIndividualRestriction( + _holder, + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + + /** + * @notice Use to add the new individual restriction for multiple token holders + * @param _holders Array of address of the token holders, whom restriction will be implied + * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. + * @param _allowedPercentageOfTokens Array of percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTimes Array of unix timestamps at which restrictions get into effect + * @param _rollingPeriodInDays Array of rolling period in days (Minimum value should be 1 day) + * @param _endTimes Array of unix timestamps at which restriction effects will gets end. + * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function addIndividualRestrictionMulti( + address[] _holders, + uint256[] _allowedTokens, + uint256[] _allowedPercentageOfTokens, + uint256[] _startTimes, + uint256[] _rollingPeriodInDays, + uint256[] _endTimes, + uint256[] _restrictionTypes + ) + public + withPerm(ADMIN) + { + _checkLengthOfArray(_allowedTokens, _allowedPercentageOfTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); + require(_holders.length == _allowedTokens.length, "Length mismatch"); + for (uint256 i = 0; i < _holders.length; i++) { + _addIndividualRestriction( + _holders[i], + _allowedTokens[i], + _allowedPercentageOfTokens[i], + _startTimes[i], + _rollingPeriodInDays[i], + _endTimes[i], + _restrictionTypes[i] + ); + } + } + + /** + * @notice Use to add the new global restriction for all token holder + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function addGlobalRestriction( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + require( + globalRestriction.endTime < now, + "Not allowed" + ); + _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + if (globalRestriction.endTime != 0) { + removeGlobalRestriction(); + } + globalRestriction = VolumeRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + emit AddGlobalRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + + /** + * @notice Use to add the new global daily restriction for all token holder + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function addDailyGlobalRestriction( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + require( + dailyGlobalRestriction.endTime < now, + "Not Allowed" + ); + _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, 1, _endTime, _restrictionType); + dailyGlobalRestriction = VolumeRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + 1, + _endTime, + RestrictionType(_restrictionType) + ); + emit AddDailyGlobalRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + 1, + _endTime, + _restrictionType + ); + } + + /** + * @notice use to remove the individual restriction for a given address + * @param _user Address of the user + */ + function removeIndividualRestriction(address _user) external withPerm(ADMIN) { + _removeIndividualRestriction(_user); + } + + /** + * @notice use to remove the individual restriction for a given address + * @param _users Array of address of the user + */ + function removeIndividualRestrictionMulti(address[] _users) external withPerm(ADMIN) { + for (uint256 i = 0; i < _users.length; i++) { + _removeIndividualRestriction(_users[i]); + } + } + + /** + * @notice Use to remove the global restriction + */ + function removeGlobalRestriction() public withPerm(ADMIN) { + require(globalRestriction.endTime != 0); + globalRestriction = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); + globalBucketDetails.timestamps.length = 0; + globalBucketDetails.sumOfLastPeriod = 0; + emit GlobalRestrictionRemoved(); + } + + /** + * @notice Use to remove the daily global restriction + */ + function removeDailyGlobalRestriction() external withPerm(ADMIN) { + require(dailyGlobalRestriction.endTime != 0); + dailyGlobalRestriction = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); + emit DailyGlobalRestrictionRemoved(); + } + + /** + * @notice Use to modify the existing individual restriction for a given token holder + * @param _holder Address of the token holder, whom restriction will be implied + * @param _allowedTokens Amount of tokens allowed to be trade for a given address. + * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function modifyIndividualRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + _modifyIndividualRestriction( + _holder, + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + + /** + * @notice Use to modify the existing individual restriction for multiple token holders + * @param _holders Array of address of the token holders, whom restriction will be implied + * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. + * @param _allowedPercentageOfTokens Array of percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTimes Array of unix timestamps at which restrictions get into effect + * @param _rollingPeriodInDays Array of rolling period in days (Minimum value should be 1 day) + * @param _endTimes Array of unix timestamps at which restriction effects will gets end. + * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function modifyIndividualRestrictionMulti( + address[] _holders, + uint256[] _allowedTokens, + uint256[] _allowedPercentageOfTokens, + uint256[] _startTimes, + uint256[] _rollingPeriodInDays, + uint256[] _endTimes, + uint256[] _restrictionTypes + ) + public + withPerm(ADMIN) + { + _checkLengthOfArray(_allowedTokens, _allowedPercentageOfTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); + require(_holders.length == _allowedTokens.length, "Length mismatch"); + for (uint256 i = 0; i < _holders.length; i++) { + _modifyIndividualRestriction( + _holders[i], + _allowedTokens[i], + _allowedPercentageOfTokens[i], + _startTimes[i], + _rollingPeriodInDays[i], + _endTimes[i], + _restrictionTypes[i] + ); + } + } + + /** + * @notice Use to modify the global restriction for all token holder + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function modifyGlobalRestriction( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + require(globalRestriction.startTime > now, "Not allowed"); + _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + globalRestriction = VolumeRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + emit ModifyGlobalRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + + /** + * @notice Use to modify the daily global restriction for all token holder + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) + */ + function modifyDailyGlobalRestriction( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + require(dailyGlobalRestriction.startTime > now, "Not allowed"); + _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, 1, _endTime, _restrictionType); + dailyGlobalRestriction = VolumeRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + 1, + _endTime, + RestrictionType(_restrictionType) + ); + emit ModifyDailyGlobalRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + 1, + _endTime, + _restrictionType + ); + } + + /** + * @notice Internal function have a logic to validate the txn amount with global restriction + */ + function _globalRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { + uint256[] memory timestamps = globalBucketDetails.timestamps; + uint256 fromTimestamp; + uint256 sumOfLastPeriod = 0; + if (timestamps.length == 0) { + // It will execute when the txn is performed first time after the addition of global restriction + fromTimestamp = globalRestriction.startTime; + } else { + // picking up the preivous timestamp + fromTimestamp = timestamps[timestamps.length -1]; + } + // Calculating the difference of days + uint256 diffDays = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now); + uint256[] memory passedTimestamps = new uint256[](diffDays); + // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod + // re-using the local variables to avoid the stack too deep error. + (sumOfLastPeriod, fromTimestamp, passedTimestamps) = _bucketCheck( + fromTimestamp, + diffDays, + _from, + globalBucketDetails, + true, + globalRestriction + ); + // Total amout that is transacted uptill now for `fromTimestamp` day + uint256 txSumOfDay = globalBucket[fromTimestamp]; + // Global transaction check + if (_globalTxCheck(sumOfLastPeriod, txSumOfDay, _amount)) { + // allow modification in storage when `_isTransfer` equals true + if(_isTransfer) { + // update the global storage + _updateGlobalStorage( + passedTimestamps, + fromTimestamp, + _amount, + diffDays, + sumOfLastPeriod + ); + } + return Result.NA; + } + return Result.INVALID; + } + + /** + * @notice Internal function to update the state variables related to global restriction + */ + function _updateGlobalStorage( + uint256[] passedTimestamps, + uint256 _fromTime, + uint256 _amount, + uint256 _diffDays, + uint256 _sumOfLastPeriod + ) + internal + { + for (uint256 i = 0; i < passedTimestamps.length; i++) { + // Add the timestamp that is already passed + globalBucketDetails.timestamps.push(passedTimestamps[i]); + } + // This condition is the works only when the transaction performed just after the startTime (_diffDays == 0) + if (globalBucketDetails.timestamps.length == 0) { + globalBucketDetails.timestamps.push(_fromTime); + } + if(_amount != 0) { + // updating the sumOfLastPeriod + globalBucketDetails.sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + + // Re-using the local variable to avoid stack too deep error + _fromTime = globalBucketDetails.timestamps[globalBucketDetails.timestamps.length -1]; + globalBucket[_fromTime] = globalBucket[_fromTime].add(_amount); + } + } + + /// Internal function for the bucket check + function _bucketCheck( + uint256 _fromTime, + uint256 _diffDays, + address _from, + BucketDetails memory _bucketDetails, + bool _isGlobal, + VolumeRestriction _restriction + ) + internal + view + returns (uint256, uint256, uint256[]) + { + uint256[] memory passedTimestamps = new uint256[](_diffDays); + uint256 counter = _bucketDetails.timestamps.length; + uint256 i = 0; + if (_diffDays != 0) { + for (i = 0; i < _diffDays; i++) { + // calculating the timestamp that will used as an index of the next bucket + // i.e buckets period will be look like this T1 to T2-1, T2 to T3-1 .... + // where T1,T2,T3 are timestamps having 24 hrs difference + _fromTime = _fromTime.add(1 days); + + // This condition is to check whether the first rolling period is covered or not + // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting + // the earlier value at that index + if (counter >= _restriction.rollingPeriodInDays) { + if (_isGlobal) { + // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod + _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. + sub(globalBucket[_bucketDetails.timestamps[counter.sub(_restriction.rollingPeriodInDays)]]); + } else { + // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod + _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. + sub(bucket[_from][_bucketDetails.timestamps[counter.sub(_restriction.rollingPeriodInDays)]]); + } + + } + + // Adding the last amount that is transacted on the `_fromTime` not actually doing it but left written to understand + // the alogrithm + //_bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); + // Storing all those timestamps whose total transacted value is 0 + passedTimestamps[i] = _fromTime; + counter++; + } + } + return (_bucketDetails.sumOfLastPeriod, _fromTime, passedTimestamps); + } + + /** + * @notice Internal function used to validate the transaction for a given address + * If it validates then it also update the storage corressponds to the individual restriction + */ + function _individualRestrictionCheck( + address _from, + uint256 _amount, + bool _isTransfer + ) + internal + returns(Result) + { + uint256 fromTimestamp; + uint256 sumOfLastPeriod = 0; + if (bucketToUser[_from].timestamps.length == 0) { + // It will execute when the txn is performed first time after the addition of individual restriction + fromTimestamp = individualRestriction[_from].startTime; + } else { + // Picking up the last timestamp + fromTimestamp = bucketToUser[_from].timestamps[bucketToUser[_from].timestamps.length -1]; + } + // Calculating the difference of days + uint256 diffDays = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now); + uint256[] memory passedTimestamps = new uint256[](diffDays); + // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod + // re-using the local variables to avoid the stack too deep error. + (sumOfLastPeriod, fromTimestamp, passedTimestamps) = _bucketCheck( + fromTimestamp, + diffDays, + _from, + bucketToUser[_from], + false, + individualRestriction[_from] + ); + if (_checkValidAmountToTransact(sumOfLastPeriod, _amount, individualRestriction[_from])) { + if (_isTransfer) { + _updateIndividualStorage( + passedTimestamps, + _from, + fromTimestamp, + _amount, + diffDays, + sumOfLastPeriod + ); + } + return Result.NA; + } + return Result.INVALID; + } + + + function _globalTxCheck( + uint256 _sumOfLastPeriod, + uint256 _dailyAmount, + uint256 _amount + ) + internal + view + returns(bool) + { + // Checking whether the global day restriction is added or not if yes then calculate + // the total amount get traded on a particular day (~ _fromTime) + if (!_checkValidAmountToTransact(_dailyAmount, _amount, dailyGlobalRestriction) && + now <= dailyGlobalRestriction.endTime && now >= dailyGlobalRestriction.startTime + ) { + return false; + } + return _checkValidAmountToTransact(_sumOfLastPeriod, _amount, globalRestriction); + } + + function _checkValidAmountToTransact( + uint256 _sumOfLastPeriod, + uint256 _amountToTransact, + VolumeRestriction _restriction + ) + internal + view + returns (bool) + { + uint256 _allowedAmount = 0; + if (_restriction.typeOfRestriction == RestrictionType.Variable) { + _allowedAmount = (_restriction.allowedPercentageOfTokens.mul(ISecurityToken(securityToken).totalSupply()))/ uint256(10) ** 18; + } else { + _allowedAmount = _restriction.allowedTokens; + } + // Validation on the amount to transact + if (_allowedAmount >= _sumOfLastPeriod.add(_amountToTransact)) { + return true; + } else { + return false; + } + } + + function _updateIndividualStorage( + uint256[] passedTimestamps, + address _from, + uint256 _fromTime, + uint256 _amount, + uint256 _diffDays, + uint256 _sumOfLastPeriod + ) + internal + { + if (_diffDays != 0) { + for (uint256 i = 0; i < passedTimestamps.length; i++) { + // Add the timestamp that is already passed + bucketToUser[_from].timestamps.push(passedTimestamps[i]); + } + } + // This condition is the works only when the transaction performed just after the startTime (_diffDays == 0) + if (bucketToUser[_from].timestamps.length == 0) { + bucketToUser[_from].timestamps.push(_fromTime); + } + if(_amount != 0) { + bucketToUser[_from].sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + + // Re-using the local variable to avoid stack too deep error + _fromTime = bucketToUser[_from].timestamps[bucketToUser[_from].timestamps.length -1]; + bucket[_from][_fromTime] = bucket[_from][_fromTime].add(_amount); + } + + } + + function _removeIndividualRestriction(address _user) internal { + require(_user != address(0), "Invalid address"); + require(individualRestriction[_user].endTime != 0, "Not present"); + individualRestriction[_user] = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); + bucketToUser[_user].timestamps.length = 0; + bucketToUser[_user].sumOfLastPeriod = 0; + emit IndividualRestrictionRemoved(_user); + } + + function _modifyIndividualRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _restrictionType + ) + internal + { + _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + require(individualRestriction[_holder].startTime > now, "Not allowed"); + + individualRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + emit ModifyIndividualRestriction( + _holder, + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + + function _addIndividualRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _restrictionType + ) + internal + { + require( + individualRestriction[_holder].endTime < now, + "Already present" + ); + require(_holder != address(0) && !exemptList[_holder], "Invalid address"); + _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + + if (individualRestriction[_holder].endTime != 0) { + _removeIndividualRestriction(_holder); + } + individualRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + emit AddNewIndividualRestriction( + _holder, + _allowedTokens, + _allowedPercentageOfTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + + function _checkInputParams( + uint256 _allowedTokens, + uint256 _allowedPercentageOfTokens, + uint256 _startTime, + uint256 _rollingPeriodDays, + uint256 _endTime, + uint256 _restrictionType + ) + internal + view + { + require(_restrictionType == 0 || _restrictionType == 1, "Invalid type"); + if (_restrictionType == uint256(RestrictionType.Fixed)) { + require(_allowedTokens > 0, "Invalid value"); + } else { + require( + _allowedPercentageOfTokens > 0 && _allowedPercentageOfTokens <= 100 * 10 ** 16, + "Percentage is not within (0,100]" + ); + } + require(_startTime >= now && _endTime > _startTime); + // Maximum limit for the rollingPeriod is 365 days + require(_rollingPeriodDays >= 1 && _rollingPeriodDays <= 365); + require(BokkyPooBahsDateTimeLibrary.diffDays(_startTime, _endTime) >= _rollingPeriodDays); + } + + function _checkLengthOfArray( + uint256[] _allowedTokens, + uint256[] _allowedPercentageOfTokens, + uint256[] _startTimes, + uint256[] _rollingPeriodInDays, + uint256[] _endTimes, + uint256[] _restrictionTypes + ) + internal + pure + { + require( + _allowedTokens.length == _allowedPercentageOfTokens.length && + _allowedPercentageOfTokens.length == _startTimes.length && + _startTimes.length == _rollingPeriodInDays.length && + _rollingPeriodInDays.length == _endTimes.length && + _endTimes.length == _restrictionTypes.length, + "Array length mismatch" + ); + } + + /** + * @notice Use to get the bucket details for a given address + * @param _user Address of the token holder for whom the bucket details has queried + * @return uint256 Array of the timestamps + * @return uint256 sumOfLastPeriod + */ + function getBucketDetailsToUser(address _user) external view returns(uint256[], uint256) { + return( + bucketToUser[_user].timestamps, + bucketToUser[_user].sumOfLastPeriod + ); + } + + /** + * @notice Use to get the volume of token that being traded at a particular day (`_at` + 24 hours) for a given user + * @param _user Address of the token holder + * @param _at Timestamp + */ + function getTotalTradeByuser(address _user, uint256 _at) external view returns(uint256) { + return bucket[_user][_at]; + } + + /** + * @notice Use to get the global bucket details + * @return uint256 Array of timestamps + * @return uint256 sumOfLastPeriod + * @return uint256 Total amount traded on the latest timestamp + */ + function getGlobalBucketDetails() external view returns(uint256[], uint256, uint256) { + return( + globalBucketDetails.timestamps, + globalBucketDetails.sumOfLastPeriod, + globalBucket[globalBucketDetails.timestamps[globalBucketDetails.timestamps.length - 1]] + ); + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() public view returns(bytes4) { + return bytes4(0); + } + + /** + * @notice Returns the permissions flag that are associated with Percentage transfer Manager + */ + function getPermissions() public view returns(bytes32[]) { + bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = ADMIN; + return allPermissions; + } + +} diff --git a/test/y_volume_restriction_tm.js b/test/y_volume_restriction_tm.js index 48645f5f4..9c33cb1c7 100644 --- a/test/y_volume_restriction_tm.js +++ b/test/y_volume_restriction_tm.js @@ -214,7 +214,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, web3.utils.toWei("12"), - 0, latestTime() + duration.seconds(2), 3, latestTime() + duration.days(10), @@ -231,7 +230,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, web3.utils.toWei("12"), - 0, latestTime() + duration.seconds(2), 3, latestTime() + duration.days(10), @@ -248,7 +246,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, 0, - 0, latestTime() + duration.seconds(2), 3, latestTime() + duration.days(10), @@ -265,7 +262,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, 0, - 0, latestTime() + duration.seconds(2), 3, latestTime() + duration.days(10), @@ -281,7 +277,6 @@ contract('VolumeRestrictionTransferManager', accounts => { await catchRevert( I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, - 0, web3.utils.toWei("10"), latestTime() + duration.seconds(2), 3, @@ -299,7 +294,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, web3.utils.toWei("10"), - 0, latestTime() - duration.seconds(5), 3, latestTime() + duration.days(10), @@ -316,7 +310,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, web3.utils.toWei("10"), - 0, latestTime() + duration.days(2), 3, latestTime() + duration.days(1), @@ -333,7 +326,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, web3.utils.toWei("10"), - 0, latestTime() + duration.days(2), 0, latestTime() + duration.days(10), @@ -350,7 +342,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, web3.utils.toWei("10"), - 0, latestTime() + duration.days(2), 366, latestTime() + duration.days(10), @@ -367,7 +358,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, web3.utils.toWei("10"), - 0, latestTime() + duration.days(2), 3, latestTime() + duration.days(3), @@ -383,7 +373,6 @@ contract('VolumeRestrictionTransferManager', accounts => { let tx = await I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, web3.utils.toWei("12"), - 0, latestTime() + duration.seconds(2), 3, latestTime() + duration.days(5), @@ -402,7 +391,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestrictionMulti( [account_investor2, account_delegate3, account_investor4], [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], - [0,0,0], [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], [3,4,5], [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], @@ -419,7 +407,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestrictionMulti( [account_investor2, account_delegate3], [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], - [0,0,0], [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], [3,4,5], [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], @@ -436,7 +423,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestrictionMulti( [account_investor2, account_delegate3, account_investor4], [web3.utils.toWei("12"), web3.utils.toWei("10")], - [0,0,0], [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], [3,4,5], [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], @@ -453,24 +439,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestrictionMulti( [account_investor2, account_delegate3, account_investor4], [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], - [0,0], - [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], - [3,4,5], - [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], - [0,0,0], - { - from: token_owner - } - ) - ) - }); - - it("Should add the restriction for multiple investor -- failed because of bad parameters i.e length mismatch", async() => { - await catchRevert( - I_VolumeRestrictionTM.addIndividualRestrictionMulti( - [account_investor2, account_delegate3, account_investor4], - [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], - [0,0,0], [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], [3,4,5], [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], @@ -487,7 +455,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestrictionMulti( [account_investor2, account_delegate3, account_investor4], [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], - [0,0,0], [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], [3], [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], @@ -504,7 +471,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestrictionMulti( [account_investor2, account_delegate3, account_investor4], [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], - [0,0,0], [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], [3, 4, 5], [latestTime() + duration.days(5)], @@ -521,7 +487,6 @@ contract('VolumeRestrictionTransferManager', accounts => { I_VolumeRestrictionTM.addIndividualRestrictionMulti( [account_investor2, account_delegate3, account_investor4], [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], - [0,0,0], [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], [3, 4, 5], [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], @@ -537,7 +502,6 @@ contract('VolumeRestrictionTransferManager', accounts => { await I_VolumeRestrictionTM.addIndividualRestrictionMulti( [account_investor2, account_delegate3, account_investor4], [web3.utils.toWei("12"), web3.utils.toWei("10"), web3.utils.toWei("15")], - [0,0,0], [latestTime() + duration.seconds(2), latestTime() + duration.seconds(2), latestTime() + duration.seconds(2)], [3, 4, 5], [latestTime() + duration.days(5), latestTime() + duration.days(6), latestTime() + duration.days(7)], @@ -546,9 +510,9 @@ contract('VolumeRestrictionTransferManager', accounts => { from: token_owner } ); - assert.equal((await I_VolumeRestrictionTM.individualRestriction.call(account_investor2))[3].toNumber(), 3); - assert.equal((await I_VolumeRestrictionTM.individualRestriction.call(account_delegate3))[3].toNumber(), 4); - assert.equal((await I_VolumeRestrictionTM.individualRestriction.call(account_investor4))[3].toNumber(), 5); + assert.equal((await I_VolumeRestrictionTM.individualRestriction.call(account_investor2))[2].toNumber(), 3); + assert.equal((await I_VolumeRestrictionTM.individualRestriction.call(account_delegate3))[2].toNumber(), 4); + assert.equal((await I_VolumeRestrictionTM.individualRestriction.call(account_investor4))[2].toNumber(), 5); }); it("Should remove the restriction multi -- failed because of address is 0", async() => { @@ -588,7 +552,6 @@ contract('VolumeRestrictionTransferManager', accounts => { let tx = await I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, web3.utils.toWei("12"), - 0, latestTime() + duration.seconds(2), 3, latestTime() + duration.days(10), @@ -609,7 +572,7 @@ contract('VolumeRestrictionTransferManager', accounts => { ); }); - it("Should succesfully transact the tokens just after the starttime", async() => { + it("Should succesfully transact the tokens just after the startTime", async() => { // Check the transfer will be valid or not by calling the verifyTransfer() directly by using _isTransfer = false let result = await I_VolumeRestrictionTM.verifyTransfer.call(account_investor1, account_investor3, web3.utils.toWei('.3'), "0x0", false); console.log(result); @@ -623,256 +586,260 @@ contract('VolumeRestrictionTransferManager', accounts => { let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); console.log('\n'); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), 0.3); - assert.equal(data[0][i].toNumber(), (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber()) - } console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length -1} + Timestamps array index: ${data[0].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0])) + .dividedBy(new BigNumber(10).pow(18))} `); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 0.3); - assert.equal(data[0].length -1, 0); - tempArray.push(0.3); - }) - - it("Should successfully transact the tokens after 1 and half days", async() => { - await increaseTime(duration.days(1.5)); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor1}); - // Check the balance of the investors - let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); - // Verifying the balances - assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 33.7); - tempArray.push(1); - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + assert.equal( + (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), + 0.3 + ); + assert.equal( + data[0].toNumber(), + (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber() + ); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber()} - `); - assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i]); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); - } console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length -1} + Days Covered : ${data[0].length -1} `); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 1.3); - assert.equal(data[0].length -1, 1); - }); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 0.3); + tempArray.push(0.3); + }) - it("Should successfully transact more tokens on the same day (Fuzz test)", async() => { - // Check the balance of the investors - let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - let totalAmountTransacted = 0; - for (let i = 0; i < 10; i++) { - let amount = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor1}); - console.log(`${i}: Restricted investor 1 able to transact ${amount} tokens to investor 3`); - totalAmountTransacted += amount; - } + // it("Should successfully transact the tokens after 1 and half days", async() => { + // await increaseTime(duration.days(1.5)); + // let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + // await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor1}); + // // Check the balance of the investors + // let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); + // // Verifying the balances + // assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 33.7); + // tempArray.push(1); + // let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + + // for (let i = 0; i < data[0].length; i++) { + // console.log(` + // Timestamps array index ${i}: ${data[0][i].toNumber()} + // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + // .dividedBy(new BigNumber(10).pow(18)).toNumber()} + // `); + // assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + // .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i]); + // assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); + // } + // console.log(` + // SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + // Last Timestamp Index : ${data[0].length -1} + // `); + // assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 1.3); + // assert.equal(data[0].length -1, 1); + // }); + + // it("Should successfully transact more tokens on the same day (Fuzz test)", async() => { + // // Check the balance of the investors + // let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); + // let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + // let totalAmountTransacted = 0; + // for (let i = 0; i < 10; i++) { + // let amount = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; + // await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor1}); + // console.log(`${i}: Restricted investor 1 able to transact ${amount} tokens to investor 3`); + // totalAmountTransacted += amount; + // } - // Check the balance of the investors - let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); - // Verifying the balances - assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); - tempArray[1] = tempArray[1] + totalAmountTransacted; - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - `); - assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), 1.3 + totalAmountTransacted, 0.000001); - assert.equal(data[0].length -1, 1); - }); - - it("Should successfully transfer the tokens after half days-- should increase the day covered by 1", async() => { - let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - await increaseTime(duration.days(.5)); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2"), {from: account_investor1}); - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - tempArray.push(2); - - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); - } + // // Check the balance of the investors + // let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); + // // Verifying the balances + // assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); + // tempArray[1] = tempArray[1] + totalAmountTransacted; + // let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + + // for (let i = 0; i < data[0].length; i++) { + // console.log(` + // Timestamps array index ${i}: ${data[0][i].toNumber()} + // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + // .dividedBy(new BigNumber(10).pow(18))} + // `); + // assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + // .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); + // assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) + // } + // console.log(` + // SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + // Last Timestamp Index : ${data[0].length - 1} + // `); + // assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), 1.3 + totalAmountTransacted, 0.000001); + // assert.equal(data[0].length -1, 1); + // }); + + // it("Should successfully transfer the tokens after half days-- should increase the day covered by 1", async() => { + // let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + // let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + // await increaseTime(duration.days(.5)); + // await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2"), {from: account_investor1}); + // let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + // tempArray.push(2); + + // for (let i = 0; i < data[0].length; i++) { + // console.log(` + // Timestamps array index ${i}: ${data[0][i].toNumber()} + // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + // .dividedBy(new BigNumber(10).pow(18))} + // `); + // assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) + // assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + // .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); + // } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - `); - assert.equal(data[0].length - 1, 2); - assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), - (oldData[1].plus(new BigNumber(2).times(new BigNumber(10).pow(18)))) - .dividedBy(new BigNumber(10).pow(18)).toNumber()); - }); - - it("Should successfully transfer the tokens in the last day of rolling period", async() => { - // Check the balance of the investors - let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - - let totalAmountTransacted = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(totalAmountTransacted.toString()), {from: account_investor1}); + // console.log(` + // SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + // Last Timestamp Index : ${data[0].length - 1} + // `); + // assert.equal(data[0].length - 1, 2); + // assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), + // (oldData[1].plus(new BigNumber(2).times(new BigNumber(10).pow(18)))) + // .dividedBy(new BigNumber(10).pow(18)).toNumber()); + // }); + + // it("Should successfully transfer the tokens in the last day of rolling period", async() => { + // // Check the balance of the investors + // let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); + // let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + // let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + + // let totalAmountTransacted = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; + // await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(totalAmountTransacted.toString()), {from: account_investor1}); - // Check the balance of the investors - let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); - // Verifying the balances - assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); - tempArray[2] = tempArray[2] + totalAmountTransacted; - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); - } + // // Check the balance of the investors + // let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); + // // Verifying the balances + // assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); + // tempArray[2] = tempArray[2] + totalAmountTransacted; + // let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + // for (let i = 0; i < data[0].length; i++) { + // console.log(` + // Timestamps array index ${i}: ${data[0][i].toNumber()} + // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + // .dividedBy(new BigNumber(10).pow(18))} + // `); + // assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) + // assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + // .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); + // } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - `); - assert.equal(data[0].length - 1, 2); - assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), - oldData[1].dividedBy(new BigNumber(10).pow(18)).toNumber() + totalAmountTransacted, 0.001); - }); - - it("Should fail to transact the tokens more than the allowed tokens in a rolling period", async() => { - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - let minimumAmount = new BigNumber(12).times(new BigNumber(10).pow(18)).minus(data[1]); - let testAmount = minimumAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))) - await catchRevert( - I_SecurityToken.transfer(account_investor3, testAmount, {from: account_investor1}) - ); - }); - - it("Should fail to buy tokens in the new rolling period --failed because amount is more than last two timestamps", async() => { - await increaseTime(duration.days(2)); - await catchRevert( - I_SecurityToken.transfer(account_investor3, web3.utils.toWei("10"), {from: account_investor1}) - ); - }); - - it("Should transfer the tokens in a new rolling period", async() => { - let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - let firstDayAmount; - tempAmount = new BigNumber(0); - for (let i = 0; i < oldData[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${oldData[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - if (i != 2) { - firstDayAmount = await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][0]); - tempAmount = tempAmount.plus(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])); - } - } - - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((oldData[1].toNumber()).toString())} - Last Timestamp Index : ${oldData[0].length -1} - `); - - let currentDayAmount = firstDayAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))); - let tx = await I_SecurityToken.transfer(account_investor3, currentDayAmount, {from: account_investor1}); - tempAmount = tempAmount.minus(currentDayAmount); - tempArray.push(0); - tempArray.push(currentDayAmount.dividedBy(new BigNumber(10).pow(18)).toNumber()); - console.log('\n'); - let newData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - for (let i = 2; i < newData[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${newData[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(newData[0][i].toNumber(), startTime + duration.days(i)) - assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.001); - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((newData[1].toNumber()).toString())} - Last Timestamp Index : ${newData[0].length -1} - `); - }); - - it("Should transfer the more tokens on the same day", async() => { - // Check the balance of the investors - let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - await I_SecurityToken.transfer(account_investor3, tempAmount, {from: account_investor1}); - - // Check the balance of the investors - let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); - // Verifying the balances - assert.closeTo( - (balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), - tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber(), - 0.01 - ); - tempArray[tempArray.length -1] = tempArray[tempArray.length -1] + tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber(); - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - for (let i = 2; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.001); - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length -1} - `); - let sumOflastperiod = 0; - for (let i = tempArray.length - 1; i >= tempArray.length - 3; i--) { - sumOflastperiod += tempArray[i]; - } - assert.equal(data[0].length - 1, 4); - assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), sumOflastperiod, 0.001); - }); + // console.log(` + // SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + // Last Timestamp Index : ${data[0].length - 1} + // `); + // assert.equal(data[0].length - 1, 2); + // assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), + // oldData[1].dividedBy(new BigNumber(10).pow(18)).toNumber() + totalAmountTransacted, 0.001); + // }); + + // it("Should fail to transact the tokens more than the allowed tokens in a rolling period", async() => { + // let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + // let minimumAmount = new BigNumber(12).times(new BigNumber(10).pow(18)).minus(data[1]); + // let testAmount = minimumAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))) + // await catchRevert( + // I_SecurityToken.transfer(account_investor3, testAmount, {from: account_investor1}) + // ); + // }); + + // it("Should fail to buy tokens in the new rolling period --failed because amount is more than last two timestamps", async() => { + // await increaseTime(duration.days(2)); + // await catchRevert( + // I_SecurityToken.transfer(account_investor3, web3.utils.toWei("10"), {from: account_investor1}) + // ); + // }); + + // it("Should transfer the tokens in a new rolling period", async() => { + // let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + // let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + // let firstDayAmount; + // tempAmount = new BigNumber(0); + // for (let i = 0; i < oldData[0].length; i++) { + // console.log(` + // Timestamps array index ${i}: ${oldData[0][i].toNumber()} + // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])) + // .dividedBy(new BigNumber(10).pow(18))} + // `); + // if (i != 2) { + // firstDayAmount = await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][0]); + // tempAmount = tempAmount.plus(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])); + // } + // } + + // console.log(` + // SumOfLastPeriod : ${web3.utils.fromWei((oldData[1].toNumber()).toString())} + // Last Timestamp Index : ${oldData[0].length -1} + // `); + + // let currentDayAmount = firstDayAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))); + // let tx = await I_SecurityToken.transfer(account_investor3, currentDayAmount, {from: account_investor1}); + // tempAmount = tempAmount.minus(currentDayAmount); + // tempArray.push(0); + // tempArray.push(currentDayAmount.dividedBy(new BigNumber(10).pow(18)).toNumber()); + // console.log('\n'); + // let newData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + // for (let i = 2; i < newData[0].length; i++) { + // console.log(` + // Timestamps array index ${i}: ${newData[0][i].toNumber()} + // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) + // .dividedBy(new BigNumber(10).pow(18))} + // `); + // assert.equal(newData[0][i].toNumber(), startTime + duration.days(i)) + // assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) + // .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.001); + // } + // console.log(` + // SumOfLastPeriod : ${web3.utils.fromWei((newData[1].toNumber()).toString())} + // Last Timestamp Index : ${newData[0].length -1} + // `); + // }); + + // it("Should transfer the more tokens on the same day", async() => { + // // Check the balance of the investors + // let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); + // let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + // await I_SecurityToken.transfer(account_investor3, tempAmount, {from: account_investor1}); + + // // Check the balance of the investors + // let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); + // // Verifying the balances + // assert.closeTo( + // (balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), + // tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber(), + // 0.01 + // ); + // tempArray[tempArray.length -1] = tempArray[tempArray.length -1] + tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber(); + // let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + // for (let i = 2; i < data[0].length; i++) { + // console.log(` + // Timestamps array index ${i}: ${data[0][i].toNumber()} + // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + // .dividedBy(new BigNumber(10).pow(18))} + // `); + // assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) + // assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + // .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.001); + // } + // console.log(` + // SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + // Last Timestamp Index : ${data[0].length -1} + // `); + // let sumOflastperiod = 0; + // for (let i = tempArray.length - 1; i >= tempArray.length - 3; i--) { + // sumOflastperiod += tempArray[i]; + // } + // assert.equal(data[0].length - 1, 4); + // assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), sumOflastperiod, 0.001); + // }); }); - describe("Test case for the variable individual restriction", async() => { + describe.skip("Test case for the variable individual restriction", async() => { it("Should add the restriction succesfully", async() => { let tx = await I_VolumeRestrictionTM.addIndividualRestriction( account_investor2, @@ -1054,7 +1021,7 @@ contract('VolumeRestrictionTransferManager', accounts => { }); }); - describe("Add the test cases for global restriction", async() => { + describe.skip("Add the test cases for global restriction", async() => { it("Should successfully add the global restriction", async() => { await I_VolumeRestrictionTM.addGlobalRestriction( @@ -1355,7 +1322,7 @@ contract('VolumeRestrictionTransferManager', accounts => { }); }) - describe("Test for the exemptlist", async() => { + describe.skip("Test for the exemptlist", async() => { it("Should add the token holder in the exemption list -- failed because of bad owner", async() => { await catchRevert( @@ -1373,7 +1340,7 @@ contract('VolumeRestrictionTransferManager', accounts => { }); }); - describe("Test for modify functions", async() => { + describe.skip("Test for modify functions", async() => { it("Should not able to modify the already started restrictions --global", async() =>{ await catchRevert( From 3617971080cb6a7b877ddedaa86f37d484a4c37f Mon Sep 17 00:00:00 2001 From: satyam Date: Sun, 2 Dec 2018 22:16:31 +0530 Subject: [PATCH 23/56] simplification in the code --- .../TransferManager/VolumeRestrictionTM.sol | 47 +- .../VolumeRestrictionTMFactory.sol | 2 +- test/y_volume_restriction_tm.js | 475 +++++++++--------- 3 files changed, 280 insertions(+), 244 deletions(-) diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index e3282fd65..f16dc9de7 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -495,13 +495,14 @@ contract VolumeRestrictionTM is ITransferManager { globalRestriction ); // Total amout that is transacted uptill now for `fromTimestamp` day - uint256 txSumOfDay = globalBucket[fromTimestamp]; - // Global transaction check - if (_globalTxCheck(sumOfLastPeriod, txSumOfDay, _amount)) { + uint256 txSumOfDay = bucket[_from][fromTimestamp]; + // Daily transaction check + if (_dailyTxCheck(sumOfLastPeriod, txSumOfDay, _amount, individualRestriction[_from])) { // allow modification in storage when `_isTransfer` equals true if(_isTransfer) { // update the global storage _updateGlobalStorage( + _from, fromTimestamp, _amount, sumOfLastPeriod, @@ -517,6 +518,7 @@ contract VolumeRestrictionTM is ITransferManager { * @notice Internal function to update the state variables related to global restriction */ function _updateGlobalStorage( + address _from, uint256 _fromTime, uint256 _amount, uint256 _sumOfLastPeriod, @@ -535,6 +537,8 @@ contract VolumeRestrictionTM is ITransferManager { // Increasing the total amount of the day by `_amount` globalBucket[_fromTime] = globalBucket[_fromTime].add(_amount); + // Updating the total day amount traded. + bucket[_from][_fromTime] = bucket[_from][_fromTime].add(_amount); } } @@ -585,7 +589,7 @@ contract VolumeRestrictionTM is ITransferManager { // i.e buckets period will be look like this T1 to T2-1, T2 to T3-1 .... // where T1,T2,T3 are timestamps having 24 hrs difference _fromTime = _fromTime.add(_diffDays.mul(1 days)); - return (_bucketDetails.sumOfLastPeriod, _fromTime, counter.add(_diffDays)); + return (_bucketDetails.sumOfLastPeriod, _fromTime, counter.add(_bucketDetails.daysCovered)); } /** @@ -623,8 +627,14 @@ contract VolumeRestrictionTM is ITransferManager { false, individualRestriction[_from] ); - if (_checkValidAmountToTransact(sumOfLastPeriod, _amount, individualRestriction[_from])) { + // Total amout that is transacted uptill now for `fromTimestamp` day + // re-using the same variable to avoid the stach too deep error. here it will give you the sum of + // tokens traded in the current day (tx day) + diffDays = bucket[_from][fromTimestamp]; + // Daily transaction check + if (_dailyTxCheck(sumOfLastPeriod, diffDays, _amount, individualRestriction[_from])) { if (_isTransfer) { + // update the Individual storage _updateIndividualStorage( _from, fromTimestamp, @@ -639,10 +649,11 @@ contract VolumeRestrictionTM is ITransferManager { } - function _globalTxCheck( + function _dailyTxCheck( uint256 _sumOfLastPeriod, uint256 _dailyAmount, - uint256 _amount + uint256 _amount, + VolumeRestriction _restriction ) internal view @@ -650,12 +661,12 @@ contract VolumeRestrictionTM is ITransferManager { { // Checking whether the global day restriction is added or not if yes then calculate // the total amount get traded on a particular day (~ _fromTime) - if (!_checkValidAmountToTransact(_dailyAmount, _amount, dailyGlobalRestriction) && - now <= dailyGlobalRestriction.endTime && now >= dailyGlobalRestriction.startTime - ) { - return false; + if ( now <= dailyGlobalRestriction.endTime && now >= dailyGlobalRestriction.startTime) + { + if (!_checkValidAmountToTransact(_dailyAmount, _amount, dailyGlobalRestriction)) + return false; } - return _checkValidAmountToTransact(_sumOfLastPeriod, _amount, globalRestriction); + return _checkValidAmountToTransact(_sumOfLastPeriod, _amount, _restriction); } function _checkValidAmountToTransact( @@ -830,11 +841,13 @@ contract VolumeRestrictionTM is ITransferManager { * @param _user Address of the token holder for whom the bucket details has queried * @return uint256 lastTradedDayTime * @return uint256 sumOfLastPeriod + * @return uint256 days covered */ - function getBucketDetailsToUser(address _user) external view returns(uint256, uint256) { + function getBucketDetailsToUser(address _user) external view returns(uint256, uint256, uint256) { return( bucketToUser[_user].lastTradedDayTime, - bucketToUser[_user].sumOfLastPeriod + bucketToUser[_user].sumOfLastPeriod, + bucketToUser[_user].daysCovered ); } @@ -851,13 +864,11 @@ contract VolumeRestrictionTM is ITransferManager { * @notice Use to get the global bucket details * @return uint256 lastTradedDayTime * @return uint256 sumOfLastPeriod - * @return uint256 Total amount traded on the latest timestamp */ - function getGlobalBucketDetails() external view returns(uint256, uint256, uint256) { + function getGlobalBucketDetails() external view returns(uint256, uint256) { return( globalBucketDetails.lastTradedDayTime, - globalBucketDetails.sumOfLastPeriod, - globalBucket[globalBucketDetails.lastTradedDayTime] + globalBucketDetails.sumOfLastPeriod ); } diff --git a/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol b/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol index 03d6e89fc..9ac511183 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol @@ -58,7 +58,7 @@ contract VolumeRestrictionTMFactory is ModuleFactory { * @notice Get the tags related to the module factory */ function getTags() public view returns(bytes32[]) { - bytes32[] memory availableTags = new bytes32[](3); + bytes32[] memory availableTags = new bytes32[](4); availableTags[0] = "Maximum Volume"; availableTags[1] = "Transfer Restriction"; availableTags[2] = "Daily Restriction"; diff --git a/test/y_volume_restriction_tm.js b/test/y_volume_restriction_tm.js index 9c33cb1c7..4be373516 100644 --- a/test/y_volume_restriction_tm.js +++ b/test/y_volume_restriction_tm.js @@ -603,240 +603,237 @@ contract('VolumeRestrictionTransferManager', accounts => { console.log(` SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Days Covered : ${data[0].length -1} + Days Covered : ${data[2]} `); assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 0.3); tempArray.push(0.3); }) - // it("Should successfully transact the tokens after 1 and half days", async() => { - // await increaseTime(duration.days(1.5)); - // let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - // await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor1}); - // // Check the balance of the investors - // let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); - // // Verifying the balances - // assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 33.7); - // tempArray.push(1); - // let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - - // for (let i = 0; i < data[0].length; i++) { - // console.log(` - // Timestamps array index ${i}: ${data[0][i].toNumber()} - // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - // .dividedBy(new BigNumber(10).pow(18)).toNumber()} - // `); - // assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - // .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i]); - // assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); - // } - // console.log(` - // SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - // Last Timestamp Index : ${data[0].length -1} - // `); - // assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 1.3); - // assert.equal(data[0].length -1, 1); - // }); - - // it("Should successfully transact more tokens on the same day (Fuzz test)", async() => { - // // Check the balance of the investors - // let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); - // let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - // let totalAmountTransacted = 0; - // for (let i = 0; i < 10; i++) { - // let amount = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; - // await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor1}); - // console.log(`${i}: Restricted investor 1 able to transact ${amount} tokens to investor 3`); - // totalAmountTransacted += amount; - // } + it("Should successfully transact the tokens after 1 and half days", async() => { + await increaseTime(duration.days(1.5)); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber(); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor1}); + // Check the balance of the investors + let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); + // Verifying the balances + assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 33.7); + tempArray.push(1); + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + + console.log(` + Timestamps array index : ${data[0].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0])) + .dividedBy(new BigNumber(10).pow(18)).toNumber()} + `); + assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[tempArray.length - 1]); + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Days Covered : ${data[2]} + `); + assert.equal(data[0].toNumber(), (startTime + duration.days(data[2]))); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 1.3); + }); + + it.skip("Should successfully transact more tokens on the same day (Fuzz test)", async() => { + // Check the balance of the investors + let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + let totalAmountTransacted = 0; + for (let i = 0; i < 10; i++) { + let amount = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor1}); + console.log(`${i}: Restricted investor 1 able to transact ${amount} tokens to investor 3`); + totalAmountTransacted += amount; + } - // // Check the balance of the investors - // let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); - // // Verifying the balances - // assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); - // tempArray[1] = tempArray[1] + totalAmountTransacted; - // let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - - // for (let i = 0; i < data[0].length; i++) { - // console.log(` - // Timestamps array index ${i}: ${data[0][i].toNumber()} - // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - // .dividedBy(new BigNumber(10).pow(18))} - // `); - // assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - // .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); - // assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - // } - // console.log(` - // SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - // Last Timestamp Index : ${data[0].length - 1} - // `); - // assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), 1.3 + totalAmountTransacted, 0.000001); - // assert.equal(data[0].length -1, 1); - // }); - - // it("Should successfully transfer the tokens after half days-- should increase the day covered by 1", async() => { - // let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - // let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - // await increaseTime(duration.days(.5)); - // await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2"), {from: account_investor1}); - // let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - // tempArray.push(2); - - // for (let i = 0; i < data[0].length; i++) { - // console.log(` - // Timestamps array index ${i}: ${data[0][i].toNumber()} - // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - // .dividedBy(new BigNumber(10).pow(18))} - // `); - // assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - // assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - // .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); - // } + // Check the balance of the investors + let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); + // Verifying the balances + assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); + tempArray[1] = tempArray[1] + totalAmountTransacted; + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + `); + assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), 1.3 + totalAmountTransacted, 0.000001); + assert.equal(data[0].length -1, 1); + }); + + it.skip("Should successfully transfer the tokens after half days-- should increase the day covered by 1", async() => { + let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + await increaseTime(duration.days(.5)); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2"), {from: account_investor1}); + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + tempArray.push(2); + + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) + assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); + } - // console.log(` - // SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - // Last Timestamp Index : ${data[0].length - 1} - // `); - // assert.equal(data[0].length - 1, 2); - // assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), - // (oldData[1].plus(new BigNumber(2).times(new BigNumber(10).pow(18)))) - // .dividedBy(new BigNumber(10).pow(18)).toNumber()); - // }); - - // it("Should successfully transfer the tokens in the last day of rolling period", async() => { - // // Check the balance of the investors - // let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); - // let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - // let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - - // let totalAmountTransacted = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; - // await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(totalAmountTransacted.toString()), {from: account_investor1}); + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + `); + assert.equal(data[0].length - 1, 2); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), + (oldData[1].plus(new BigNumber(2).times(new BigNumber(10).pow(18)))) + .dividedBy(new BigNumber(10).pow(18)).toNumber()); + }); + + it.skip("Should successfully transfer the tokens in the last day of rolling period", async() => { + // Check the balance of the investors + let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + + let totalAmountTransacted = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(totalAmountTransacted.toString()), {from: account_investor1}); - // // Check the balance of the investors - // let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); - // // Verifying the balances - // assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); - // tempArray[2] = tempArray[2] + totalAmountTransacted; - // let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - // for (let i = 0; i < data[0].length; i++) { - // console.log(` - // Timestamps array index ${i}: ${data[0][i].toNumber()} - // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - // .dividedBy(new BigNumber(10).pow(18))} - // `); - // assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - // assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - // .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); - // } + // Check the balance of the investors + let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); + // Verifying the balances + assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); + tempArray[2] = tempArray[2] + totalAmountTransacted; + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + for (let i = 0; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) + assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); + } - // console.log(` - // SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - // Last Timestamp Index : ${data[0].length - 1} - // `); - // assert.equal(data[0].length - 1, 2); - // assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), - // oldData[1].dividedBy(new BigNumber(10).pow(18)).toNumber() + totalAmountTransacted, 0.001); - // }); - - // it("Should fail to transact the tokens more than the allowed tokens in a rolling period", async() => { - // let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - // let minimumAmount = new BigNumber(12).times(new BigNumber(10).pow(18)).minus(data[1]); - // let testAmount = minimumAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))) - // await catchRevert( - // I_SecurityToken.transfer(account_investor3, testAmount, {from: account_investor1}) - // ); - // }); - - // it("Should fail to buy tokens in the new rolling period --failed because amount is more than last two timestamps", async() => { - // await increaseTime(duration.days(2)); - // await catchRevert( - // I_SecurityToken.transfer(account_investor3, web3.utils.toWei("10"), {from: account_investor1}) - // ); - // }); - - // it("Should transfer the tokens in a new rolling period", async() => { - // let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - // let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - // let firstDayAmount; - // tempAmount = new BigNumber(0); - // for (let i = 0; i < oldData[0].length; i++) { - // console.log(` - // Timestamps array index ${i}: ${oldData[0][i].toNumber()} - // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])) - // .dividedBy(new BigNumber(10).pow(18))} - // `); - // if (i != 2) { - // firstDayAmount = await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][0]); - // tempAmount = tempAmount.plus(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])); - // } - // } - - // console.log(` - // SumOfLastPeriod : ${web3.utils.fromWei((oldData[1].toNumber()).toString())} - // Last Timestamp Index : ${oldData[0].length -1} - // `); - - // let currentDayAmount = firstDayAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))); - // let tx = await I_SecurityToken.transfer(account_investor3, currentDayAmount, {from: account_investor1}); - // tempAmount = tempAmount.minus(currentDayAmount); - // tempArray.push(0); - // tempArray.push(currentDayAmount.dividedBy(new BigNumber(10).pow(18)).toNumber()); - // console.log('\n'); - // let newData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - // for (let i = 2; i < newData[0].length; i++) { - // console.log(` - // Timestamps array index ${i}: ${newData[0][i].toNumber()} - // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) - // .dividedBy(new BigNumber(10).pow(18))} - // `); - // assert.equal(newData[0][i].toNumber(), startTime + duration.days(i)) - // assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) - // .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.001); - // } - // console.log(` - // SumOfLastPeriod : ${web3.utils.fromWei((newData[1].toNumber()).toString())} - // Last Timestamp Index : ${newData[0].length -1} - // `); - // }); - - // it("Should transfer the more tokens on the same day", async() => { - // // Check the balance of the investors - // let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); - // let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - // await I_SecurityToken.transfer(account_investor3, tempAmount, {from: account_investor1}); - - // // Check the balance of the investors - // let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); - // // Verifying the balances - // assert.closeTo( - // (balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), - // tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber(), - // 0.01 - // ); - // tempArray[tempArray.length -1] = tempArray[tempArray.length -1] + tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber(); - // let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - // for (let i = 2; i < data[0].length; i++) { - // console.log(` - // Timestamps array index ${i}: ${data[0][i].toNumber()} - // Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - // .dividedBy(new BigNumber(10).pow(18))} - // `); - // assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - // assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - // .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.001); - // } - // console.log(` - // SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - // Last Timestamp Index : ${data[0].length -1} - // `); - // let sumOflastperiod = 0; - // for (let i = tempArray.length - 1; i >= tempArray.length - 3; i--) { - // sumOflastperiod += tempArray[i]; - // } - // assert.equal(data[0].length - 1, 4); - // assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), sumOflastperiod, 0.001); - // }); + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length - 1} + `); + assert.equal(data[0].length - 1, 2); + assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), + oldData[1].dividedBy(new BigNumber(10).pow(18)).toNumber() + totalAmountTransacted, 0.001); + }); + + it.skip("Should fail to transact the tokens more than the allowed tokens in a rolling period", async() => { + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + let minimumAmount = new BigNumber(12).times(new BigNumber(10).pow(18)).minus(data[1]); + let testAmount = minimumAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))) + await catchRevert( + I_SecurityToken.transfer(account_investor3, testAmount, {from: account_investor1}) + ); + }); + + it.skip("Should fail to buy tokens in the new rolling period --failed because amount is more than last two timestamps", async() => { + await increaseTime(duration.days(2)); + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei("10"), {from: account_investor1}) + ); + }); + + it.skip("Should transfer the tokens in a new rolling period", async() => { + let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + let firstDayAmount; + tempAmount = new BigNumber(0); + for (let i = 0; i < oldData[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${oldData[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + if (i != 2) { + firstDayAmount = await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][0]); + tempAmount = tempAmount.plus(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])); + } + } + + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((oldData[1].toNumber()).toString())} + Last Timestamp Index : ${oldData[0].length -1} + `); + + let currentDayAmount = firstDayAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))); + let tx = await I_SecurityToken.transfer(account_investor3, currentDayAmount, {from: account_investor1}); + tempAmount = tempAmount.minus(currentDayAmount); + tempArray.push(0); + tempArray.push(currentDayAmount.dividedBy(new BigNumber(10).pow(18)).toNumber()); + console.log('\n'); + let newData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + for (let i = 2; i < newData[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${newData[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal(newData[0][i].toNumber(), startTime + duration.days(i)) + assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.001); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((newData[1].toNumber()).toString())} + Last Timestamp Index : ${newData[0].length -1} + `); + }); + + it.skip("Should transfer the more tokens on the same day", async() => { + // Check the balance of the investors + let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + await I_SecurityToken.transfer(account_investor3, tempAmount, {from: account_investor1}); + + // Check the balance of the investors + let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); + // Verifying the balances + assert.closeTo( + (balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), + tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber(), + 0.01 + ); + tempArray[tempArray.length -1] = tempArray[tempArray.length -1] + tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber(); + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + for (let i = 2; i < data[0].length; i++) { + console.log(` + Timestamps array index ${i}: ${data[0][i].toNumber()} + Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18))} + `); + assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) + assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.001); + } + console.log(` + SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} + Last Timestamp Index : ${data[0].length -1} + `); + let sumOflastperiod = 0; + for (let i = tempArray.length - 1; i >= tempArray.length - 3; i--) { + sumOflastperiod += tempArray[i]; + } + assert.equal(data[0].length - 1, 4); + assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), sumOflastperiod, 0.001); + }); }); describe.skip("Test case for the variable individual restriction", async() => { @@ -1468,5 +1465,33 @@ contract('VolumeRestrictionTransferManager', accounts => { }); }); + + describe("VolumeRestriction Transfer Manager Factory test cases", async() => { + + it("Should get the exact details of the factory", async() => { + assert.equal(await I_VolumeRestrictionTMFactory.getSetupCost.call(),0); + assert.equal((await I_VolumeRestrictionTMFactory.getTypes.call())[0],2); + assert.equal(web3.utils.toAscii(await I_VolumeRestrictionTMFactory.getName.call()) + .replace(/\u0000/g, ''), + "VolumeRestrictionTM", + "Wrong Module added"); + assert.equal(await I_VolumeRestrictionTMFactory.description.call(), + "Manage transfers based on the volume of tokens that needs to be transact", + "Wrong Module added"); + assert.equal(await I_VolumeRestrictionTMFactory.title.call(), + "Volume Restriction Transfer Manager", + "Wrong Module added"); + assert.equal(await I_VolumeRestrictionTMFactory.getInstructions.call(), + "Module used to restrict the volume of tokens traded by the token holders", + "Wrong Module added"); + assert.equal(await I_VolumeRestrictionTMFactory.version.call(), "1.0.0"); + }); + + it("Should get the tags of the factory", async() => { + let tags = await I_VolumeRestrictionTMFactory.getTags.call(); + assert.equal(tags.length, 4); + assert.equal(web3.utils.toAscii(tags[0]).replace(/\u0000/g, ''), "Maximum Volume"); + }); + }); }); \ No newline at end of file From ee059b04bcf9d5d5885be0d0f829860f43ad35f6 Mon Sep 17 00:00:00 2001 From: satyam Date: Tue, 4 Dec 2018 15:16:30 +0530 Subject: [PATCH 24/56] change business logic of verify transfer --- .../TransferManager/VolumeRestrictionTM.sol | 256 +++-- .../VolumeRestrictionTM_prev.sol | 932 ------------------ 2 files changed, 111 insertions(+), 1077 deletions(-) delete mode 100644 contracts/modules/TransferManager/VolumeRestrictionTM_prev.sol diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index f16dc9de7..5f2df00e4 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -26,8 +26,10 @@ contract VolumeRestrictionTM is ITransferManager { struct BucketDetails { uint256 lastTradedDayTime; - uint256 sumOfLastPeriod; // It is the sum of transacted amount within the last rollingPeriodDays + uint256 sumOfLastPeriod; // It is the sum of transacted amount within the last rollingPeriodDays + uint256 globalSumOfLastPeriod; uint256 daysCovered; // No of days covered till (from the startTime of VolumeRestriction) + uint256 globalDaysCovered; } // Global restriction that applies to all token holders @@ -44,8 +46,6 @@ contract VolumeRestrictionTM is ITransferManager { mapping(address => BucketDetails) internal bucketToUser; // List of wallets that are exempted from all the restrictions applied by the this contract mapping(address => bool) public exemptList; - // Store the amount of tokens get transacted in a day - mapping(uint256 => uint256) public globalBucket; // Emit when the token holder is added/removed from the exemption list event ChangedExemptWalletList(address indexed _wallet, bool _change); @@ -130,13 +130,7 @@ contract VolumeRestrictionTM is ITransferManager { if (!paused && _from != address(0) && !exemptList[_from]) { // Function must only be called by the associated security token if _isTransfer == true require(msg.sender == securityToken || !_isTransfer); - // Checking the individual restriction if the `_from` comes in the individual category - if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { - return _individualRestrictionCheck(_from, _amount, _isTransfer); - // If the `_from` doesn't fall under the individual category. It will processed with in the global category automatically - } else if (globalRestriction.endTime >= now && globalRestriction.startTime <= now) { - return _globalRestrictionCheck(_from, _amount, _isTransfer); - } + return _restrictionCheck(_from, _amount, _isTransfer); } return Result.NA; } @@ -471,16 +465,52 @@ contract VolumeRestrictionTM is ITransferManager { /** * @notice Internal function have a logic to validate the txn amount with global restriction */ - function _globalRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { + function _restrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { + uint256 sumOfLastPeriod = 0; + uint256 daysCovered = 0; + uint256 lastTradedDayTime; + uint256 globalSumOfLastPeriod; + uint256 globalDaysCovered; + bool validIR = true; + bool validGR = true; + if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { + (validIR, sumOfLastPeriod, lastTradedDayTime, daysCovered) = _individualRestrictionCheck(_from, _amount); + } + (validGR, globalSumOfLastPeriod, lastTradedDayTime, globalDaysCovered) = _globalRestrictionCheck(_from, _amount); + // Total amout that is transacted uptill now for `fromTimestamp` day + uint256 txSumOfDay = bucket[_from][lastTradedDayTime]; + // allow modification in storage when `_isTransfer` equals true + if (_isTransfer) { + // update the storage + _updateStorage( + _from, + _amount, + lastTradedDayTime, + sumOfLastPeriod, + globalSumOfLastPeriod, + daysCovered, + globalDaysCovered + ); + } + if (validGR && validIR && _dailyTxCheck(txSumOfDay, _amount)) + return Result.NA; + else + return Result.INVALID; + } + + /** + * @notice Internal function have a logic to validate the txn amount with global restriction + */ + function _globalRestrictionCheck(address _from, uint256 _amount) internal view returns (bool, uint256, uint256, uint256) { + uint256 daysCovered; uint256 fromTimestamp; uint256 sumOfLastPeriod = 0; - uint256 daysCovered = 0; - if (globalBucketDetails.lastTradedDayTime == 0) { + if (bucketToUser[_from].lastTradedDayTime < globalRestriction.startTime) { // It will execute when the txn is performed first time after the addition of global restriction fromTimestamp = globalRestriction.startTime; } else { // picking up the preivous timestamp - fromTimestamp = globalBucketDetails.lastTradedDayTime; + fromTimestamp = bucketToUser[_from].lastTradedDayTime; } // Calculating the difference of days uint256 diffDays = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now); @@ -490,56 +520,49 @@ contract VolumeRestrictionTM is ITransferManager { fromTimestamp, diffDays, _from, - globalBucketDetails, + bucketToUser[_from], true, - globalRestriction + globalRestriction.rollingPeriodInDays ); - // Total amout that is transacted uptill now for `fromTimestamp` day - uint256 txSumOfDay = bucket[_from][fromTimestamp]; - // Daily transaction check - if (_dailyTxCheck(sumOfLastPeriod, txSumOfDay, _amount, individualRestriction[_from])) { - // allow modification in storage when `_isTransfer` equals true - if(_isTransfer) { - // update the global storage - _updateGlobalStorage( - _from, - fromTimestamp, - _amount, - sumOfLastPeriod, - daysCovered - ); - } - return Result.NA; - } - return Result.INVALID; + // validation of the transaction amount + if (_checkValidAmountToTransact(sumOfLastPeriod, _amount, globalRestriction)) { + return (true, sumOfLastPeriod, fromTimestamp, daysCovered); + } + return (false, sumOfLastPeriod, fromTimestamp, daysCovered); } /** - * @notice Internal function to update the state variables related to global restriction + * @notice Internal function used to validate the transaction for a given address + * If it validates then it also update the storage corressponds to the individual restriction */ - function _updateGlobalStorage( - address _from, - uint256 _fromTime, - uint256 _amount, - uint256 _sumOfLastPeriod, - uint256 _daysCovered - ) - internal - { - if (globalBucketDetails.lastTradedDayTime != _fromTime) { - // Assigning the latest transaction timestamp of the day - globalBucketDetails.lastTradedDayTime = _fromTime; - globalBucketDetails.daysCovered = _daysCovered; - } - if (_amount != 0) { - // updating the sumOfLastPeriod - globalBucketDetails.sumOfLastPeriod = _sumOfLastPeriod.add(_amount); - - // Increasing the total amount of the day by `_amount` - globalBucket[_fromTime] = globalBucket[_fromTime].add(_amount); - // Updating the total day amount traded. - bucket[_from][_fromTime] = bucket[_from][_fromTime].add(_amount); + function _individualRestrictionCheck(address _from, uint256 _amount) internal view returns (bool, uint256, uint256, uint256) { + // using the variable to avoid stack too deep error + uint256 daysCovered = individualRestriction[_from].rollingPeriodInDays; + uint256 fromTimestamp; + uint256 sumOfLastPeriod = 0; + if (bucketToUser[_from].lastTradedDayTime < individualRestriction[_from].startTime) { + // It will execute when the txn is performed first time after the addition of individual restriction + fromTimestamp = individualRestriction[_from].startTime; + } else { + // Picking up the last timestamp + fromTimestamp = bucketToUser[_from].lastTradedDayTime; } + + // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod + // re-using the local variables to avoid the stack too deep error. + (sumOfLastPeriod, fromTimestamp, daysCovered) = _bucketCheck( + fromTimestamp, + BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now), + _from, + bucketToUser[_from], + false, + daysCovered + ); + // validation of the transaction amount + if (_checkValidAmountToTransact(sumOfLastPeriod, _amount, individualRestriction[_from])) { + return (true, sumOfLastPeriod, fromTimestamp, daysCovered); + } + return (false, sumOfLastPeriod, fromTimestamp, daysCovered); } /// Internal function for the bucket check @@ -549,35 +572,32 @@ contract VolumeRestrictionTM is ITransferManager { address _from, BucketDetails memory _bucketDetails, bool _isGlobal, - VolumeRestriction _restriction + uint256 _rollingPeriodInDays ) internal view returns (uint256, uint256, uint256) { - uint256 counter = _bucketDetails.daysCovered; + uint256 counter = _bucketDetails.globalDaysCovered; + uint256 sumOfLastPeriod = _bucketDetails.globalSumOfLastPeriod; uint256 i = 0; - if (_diffDays >= _restriction.rollingPeriodInDays) { + if (!_isGlobal) { + counter = _bucketDetails.daysCovered; + sumOfLastPeriod = _bucketDetails.sumOfLastPeriod; + } + if (_diffDays >= _rollingPeriodInDays) { // If the difference of days is greater than the rollingPeriod then sumOfLastPeriod will always be zero - _bucketDetails.sumOfLastPeriod = 0; - + sumOfLastPeriod = 0; } else { for (i = 0; i < _diffDays; i++) { // This condition is to check whether the first rolling period is covered or not // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting // the earlier value at that index - if (counter >= _restriction.rollingPeriodInDays) { - if (_isGlobal) { - // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod - _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. - sub(globalBucket[_bucketDetails.lastTradedDayTime.sub((counter.sub(_restriction.rollingPeriodInDays)).mul(1 days))]); - } else { - // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod - _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. - sub(bucket[_from][_bucketDetails.lastTradedDayTime.sub((counter.sub(_restriction.rollingPeriodInDays)).mul(1 days))]); - } + if (counter >= _rollingPeriodInDays) { + // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod + sumOfLastPeriod = sumOfLastPeriod. + sub(bucket[_from][_bucketDetails.lastTradedDayTime.sub((counter.sub(_rollingPeriodInDays)).mul(1 days))]); } - // Adding the last amount that is transacted on the `_fromTime` not actually doing it but left written to understand // the alogrithm //_bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); @@ -589,71 +609,12 @@ contract VolumeRestrictionTM is ITransferManager { // i.e buckets period will be look like this T1 to T2-1, T2 to T3-1 .... // where T1,T2,T3 are timestamps having 24 hrs difference _fromTime = _fromTime.add(_diffDays.mul(1 days)); - return (_bucketDetails.sumOfLastPeriod, _fromTime, counter.add(_bucketDetails.daysCovered)); + return (sumOfLastPeriod, _fromTime, counter); } - /** - * @notice Internal function used to validate the transaction for a given address - * If it validates then it also update the storage corressponds to the individual restriction - */ - function _individualRestrictionCheck( - address _from, - uint256 _amount, - bool _isTransfer - ) - internal - returns(Result) - { - uint256 daysCovered; - uint256 fromTimestamp; - uint256 sumOfLastPeriod = 0; - if (bucketToUser[_from].lastTradedDayTime == 0) { - // It will execute when the txn is performed first time after the addition of individual restriction - fromTimestamp = individualRestriction[_from].startTime; - } else { - // Picking up the last timestamp - fromTimestamp = bucketToUser[_from].lastTradedDayTime; - } - // Calculating the difference of days - uint256 diffDays = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now); - - // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod - // re-using the local variables to avoid the stack too deep error. - (sumOfLastPeriod, fromTimestamp, daysCovered) = _bucketCheck( - fromTimestamp, - diffDays, - _from, - bucketToUser[_from], - false, - individualRestriction[_from] - ); - // Total amout that is transacted uptill now for `fromTimestamp` day - // re-using the same variable to avoid the stach too deep error. here it will give you the sum of - // tokens traded in the current day (tx day) - diffDays = bucket[_from][fromTimestamp]; - // Daily transaction check - if (_dailyTxCheck(sumOfLastPeriod, diffDays, _amount, individualRestriction[_from])) { - if (_isTransfer) { - // update the Individual storage - _updateIndividualStorage( - _from, - fromTimestamp, - _amount, - sumOfLastPeriod, - daysCovered - ); - } - return Result.NA; - } - return Result.INVALID; - } - - function _dailyTxCheck( - uint256 _sumOfLastPeriod, uint256 _dailyAmount, - uint256 _amount, - VolumeRestriction _restriction + uint256 _amount ) internal view @@ -666,7 +627,7 @@ contract VolumeRestrictionTM is ITransferManager { if (!_checkValidAmountToTransact(_dailyAmount, _amount, dailyGlobalRestriction)) return false; } - return _checkValidAmountToTransact(_sumOfLastPeriod, _amount, _restriction); + return true; } function _checkValidAmountToTransact( @@ -692,25 +653,30 @@ contract VolumeRestrictionTM is ITransferManager { } } - function _updateIndividualStorage( + function _updateStorage( address _from, - uint256 _fromTime, uint256 _amount, + uint256 _lastTradedDayTime, uint256 _sumOfLastPeriod, - uint256 _daysCovered + uint256 _globalSumOfLastPeriod, + uint256 _daysCovered, + uint256 _globalDaysCovered ) internal { - if (bucketToUser[_from].lastTradedDayTime != _fromTime) { + if (bucketToUser[_from].lastTradedDayTime != _lastTradedDayTime) { // Assigning the latest transaction timestamp of the day - bucketToUser[_from].lastTradedDayTime = _fromTime; - bucketToUser[_from].daysCovered = _daysCovered; + bucketToUser[_from].lastTradedDayTime = _lastTradedDayTime; } - if(_amount != 0) { - bucketToUser[_from].sumOfLastPeriod = _sumOfLastPeriod.add(_amount); - + if (_amount != 0) { + if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { + bucketToUser[_from].daysCovered = _daysCovered; + bucketToUser[_from].sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + } + bucketToUser[_from].globalSumOfLastPeriod = _globalSumOfLastPeriod.add(_amount); + bucketToUser[_from].globalDaysCovered = _globalDaysCovered; // Increasing the total amount of the day by `_amount` - bucket[_from][_fromTime] = bucket[_from][_fromTime].add(_amount); + bucket[_from][_lastTradedDayTime] = bucket[_from][_lastTradedDayTime].add(_amount); } } diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM_prev.sol b/contracts/modules/TransferManager/VolumeRestrictionTM_prev.sol deleted file mode 100644 index 5a42ede3f..000000000 --- a/contracts/modules/TransferManager/VolumeRestrictionTM_prev.sol +++ /dev/null @@ -1,932 +0,0 @@ -pragma solidity ^0.4.24; - -import "./ITransferManager.sol"; -import "openzeppelin-solidity/contracts/math/SafeMath.sol"; -import "../../libraries/BokkyPooBahsDateTimeLibrary.sol"; - -contract VolumeRestrictionTM_prev is ITransferManager { - - using SafeMath for uint256; - - // permission definition - bytes32 public constant ADMIN = "ADMIN"; - - enum RestrictionType { Fixed, Variable } - - struct VolumeRestriction { - uint256 allowedTokens; - uint256 allowedPercentageOfTokens; - uint256 startTime; - uint256 rollingPeriodInDays; - uint256 endTime; - RestrictionType typeOfRestriction; - } - - struct BucketDetails { - uint256[] timestamps; - uint256 sumOfLastPeriod; // It is the sum of transacted amount within the last rollingPeriodDays - } - - // Global restriction that applies to all token holders - VolumeRestriction public globalRestriction; - // Daily global restriction that applies to all token holders (Total ST traded daily is restricted) - VolumeRestriction public dailyGlobalRestriction; - // Variable stores the data matrix for the globa restrictions - BucketDetails internal globalBucketDetails; - // Restriction stored corresponds to a particular token holder - mapping(address => VolumeRestriction) public individualRestriction; - // Storing _from => day's timestamp => total amount transact in a day --individual - mapping(address => mapping(uint256 => uint256)) internal bucket; - // Storing the information that used to validate the transaction - mapping(address => BucketDetails) internal bucketToUser; - // List of wallets that are exempted from all the restrictions applied by the this contract - mapping(address => bool) public exemptList; - // Store the amount of tokens get transacted in a day - mapping(uint256 => uint256) public globalBucket; - - // Emit when the token holder is added/removed from the exemption list - event ChangedExemptWalletList(address indexed _wallet, bool _change); - // Emit when the new individual restriction is added corresponds to new token holders - event AddNewIndividualRestriction( - address indexed _holder, - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _typeOfRestriction - ); - // Emit when the individual restriction is modified for a given address - event ModifyIndividualRestriction( - address indexed _holder, - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _typeOfRestriction - ); - // Emit when the new global restriction is added - event AddGlobalRestriction( - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _typeOfRestriction - ); - // Emit when the new daily (global) restriction is added - event AddDailyGlobalRestriction( - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _typeOfRestriction - ); - // Emit when global restriction get modified - event ModifyGlobalRestriction( - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _typeOfRestriction - ); - // Emit when daily global restriction get modified - event ModifyDailyGlobalRestriction( - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _typeOfRestriction - ); - // Emit when the individual restriction gets removed - event IndividualRestrictionRemoved(address _user); - // Emit when the global restriction gets removed - event GlobalRestrictionRemoved(); - // Emit when the daily global restriction gets removed - event DailyGlobalRestrictionRemoved(); - - /** - * @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/transferFrom transaction and prevent tranaction - * whose volume of tokens will voilate the maximum volume transfer restriction - * @param _from Address of the sender - * @param _amount The amount of tokens to transfer - * @param _isTransfer Whether or not this is an actual transfer or just a test to see if the tokens would be transferrable - */ - function verifyTransfer(address _from, address /*_to */, uint256 _amount, bytes /*_data*/, bool _isTransfer) public returns (Result) { - // If `_from` is present in the exemptionList or it is `0x0` address then it will not follow the vol restriction - if (!paused && _from != address(0) && !exemptList[_from]) { - // Function must only be called by the associated security token if _isTransfer == true - require(msg.sender == securityToken || !_isTransfer); - // Checking the individual restriction if the `_from` comes in the individual category - if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { - return _individualRestrictionCheck(_from, _amount, _isTransfer); - // If the `_from` doesn't fall under the individual category. It will processed with in the global category automatically - } else if (globalRestriction.endTime >= now && globalRestriction.startTime <= now) { - return _globalRestrictionCheck(_from, _amount, _isTransfer); - } - } - return Result.NA; - } - - /** - * @notice Add/Remove wallet address from the exempt list - * @param _wallet Ethereum wallet/contract address that need to be exempted - * @param _change Boolean value used to add (i.e true) or remove (i.e false) from the list - */ - function changeExemptWalletList(address _wallet, bool _change) public withPerm(ADMIN) { - require(_wallet != address(0), "Invalid address"); - exemptList[_wallet] = _change; - emit ChangedExemptWalletList(_wallet, _change); - } - - /** - * @notice Use to add the new individual restriction for a given token holder - * @param _holder Address of the token holder, whom restriction will be implied - * @param _allowedTokens Amount of tokens allowed to be trade for a given address. - * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. - * @param _startTime Unix timestamp at which restriction get into effect - * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) - * @param _endTime Unix timestamp at which restriction effects will gets end. - * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) - */ - function addIndividualRestriction( - address _holder, - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _restrictionType - ) - external - withPerm(ADMIN) - { - _addIndividualRestriction( - _holder, - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - _restrictionType - ); - } - - /** - * @notice Use to add the new individual restriction for multiple token holders - * @param _holders Array of address of the token holders, whom restriction will be implied - * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. - * @param _allowedPercentageOfTokens Array of percentage of tokens w.r.t to totalSupply allowed to transact. - * @param _startTimes Array of unix timestamps at which restrictions get into effect - * @param _rollingPeriodInDays Array of rolling period in days (Minimum value should be 1 day) - * @param _endTimes Array of unix timestamps at which restriction effects will gets end. - * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for variable) - */ - function addIndividualRestrictionMulti( - address[] _holders, - uint256[] _allowedTokens, - uint256[] _allowedPercentageOfTokens, - uint256[] _startTimes, - uint256[] _rollingPeriodInDays, - uint256[] _endTimes, - uint256[] _restrictionTypes - ) - public - withPerm(ADMIN) - { - _checkLengthOfArray(_allowedTokens, _allowedPercentageOfTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); - require(_holders.length == _allowedTokens.length, "Length mismatch"); - for (uint256 i = 0; i < _holders.length; i++) { - _addIndividualRestriction( - _holders[i], - _allowedTokens[i], - _allowedPercentageOfTokens[i], - _startTimes[i], - _rollingPeriodInDays[i], - _endTimes[i], - _restrictionTypes[i] - ); - } - } - - /** - * @notice Use to add the new global restriction for all token holder - * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. - * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. - * @param _startTime Unix timestamp at which restriction get into effect - * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) - * @param _endTime Unix timestamp at which restriction effects will gets end. - * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) - */ - function addGlobalRestriction( - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _restrictionType - ) - external - withPerm(ADMIN) - { - require( - globalRestriction.endTime < now, - "Not allowed" - ); - _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); - if (globalRestriction.endTime != 0) { - removeGlobalRestriction(); - } - globalRestriction = VolumeRestriction( - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - RestrictionType(_restrictionType) - ); - emit AddGlobalRestriction( - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - _restrictionType - ); - } - - /** - * @notice Use to add the new global daily restriction for all token holder - * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. - * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. - * @param _startTime Unix timestamp at which restriction get into effect - * @param _endTime Unix timestamp at which restriction effects will gets end. - * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) - */ - function addDailyGlobalRestriction( - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _endTime, - uint256 _restrictionType - ) - external - withPerm(ADMIN) - { - require( - dailyGlobalRestriction.endTime < now, - "Not Allowed" - ); - _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, 1, _endTime, _restrictionType); - dailyGlobalRestriction = VolumeRestriction( - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - 1, - _endTime, - RestrictionType(_restrictionType) - ); - emit AddDailyGlobalRestriction( - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - 1, - _endTime, - _restrictionType - ); - } - - /** - * @notice use to remove the individual restriction for a given address - * @param _user Address of the user - */ - function removeIndividualRestriction(address _user) external withPerm(ADMIN) { - _removeIndividualRestriction(_user); - } - - /** - * @notice use to remove the individual restriction for a given address - * @param _users Array of address of the user - */ - function removeIndividualRestrictionMulti(address[] _users) external withPerm(ADMIN) { - for (uint256 i = 0; i < _users.length; i++) { - _removeIndividualRestriction(_users[i]); - } - } - - /** - * @notice Use to remove the global restriction - */ - function removeGlobalRestriction() public withPerm(ADMIN) { - require(globalRestriction.endTime != 0); - globalRestriction = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); - globalBucketDetails.timestamps.length = 0; - globalBucketDetails.sumOfLastPeriod = 0; - emit GlobalRestrictionRemoved(); - } - - /** - * @notice Use to remove the daily global restriction - */ - function removeDailyGlobalRestriction() external withPerm(ADMIN) { - require(dailyGlobalRestriction.endTime != 0); - dailyGlobalRestriction = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); - emit DailyGlobalRestrictionRemoved(); - } - - /** - * @notice Use to modify the existing individual restriction for a given token holder - * @param _holder Address of the token holder, whom restriction will be implied - * @param _allowedTokens Amount of tokens allowed to be trade for a given address. - * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. - * @param _startTime Unix timestamp at which restriction get into effect - * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) - * @param _endTime Unix timestamp at which restriction effects will gets end. - * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) - */ - function modifyIndividualRestriction( - address _holder, - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _restrictionType - ) - external - withPerm(ADMIN) - { - _modifyIndividualRestriction( - _holder, - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - _restrictionType - ); - } - - /** - * @notice Use to modify the existing individual restriction for multiple token holders - * @param _holders Array of address of the token holders, whom restriction will be implied - * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. - * @param _allowedPercentageOfTokens Array of percentage of tokens w.r.t to totalSupply allowed to transact. - * @param _startTimes Array of unix timestamps at which restrictions get into effect - * @param _rollingPeriodInDays Array of rolling period in days (Minimum value should be 1 day) - * @param _endTimes Array of unix timestamps at which restriction effects will gets end. - * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for variable) - */ - function modifyIndividualRestrictionMulti( - address[] _holders, - uint256[] _allowedTokens, - uint256[] _allowedPercentageOfTokens, - uint256[] _startTimes, - uint256[] _rollingPeriodInDays, - uint256[] _endTimes, - uint256[] _restrictionTypes - ) - public - withPerm(ADMIN) - { - _checkLengthOfArray(_allowedTokens, _allowedPercentageOfTokens, _startTimes, _rollingPeriodInDays, _endTimes, _restrictionTypes); - require(_holders.length == _allowedTokens.length, "Length mismatch"); - for (uint256 i = 0; i < _holders.length; i++) { - _modifyIndividualRestriction( - _holders[i], - _allowedTokens[i], - _allowedPercentageOfTokens[i], - _startTimes[i], - _rollingPeriodInDays[i], - _endTimes[i], - _restrictionTypes[i] - ); - } - } - - /** - * @notice Use to modify the global restriction for all token holder - * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. - * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. - * @param _startTime Unix timestamp at which restriction get into effect - * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) - * @param _endTime Unix timestamp at which restriction effects will gets end. - * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) - */ - function modifyGlobalRestriction( - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _restrictionType - ) - external - withPerm(ADMIN) - { - require(globalRestriction.startTime > now, "Not allowed"); - _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); - globalRestriction = VolumeRestriction( - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - RestrictionType(_restrictionType) - ); - emit ModifyGlobalRestriction( - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - _restrictionType - ); - } - - /** - * @notice Use to modify the daily global restriction for all token holder - * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. - * @param _allowedPercentageOfTokens Percentage of tokens w.r.t to totalSupply allowed to transact. - * @param _startTime Unix timestamp at which restriction get into effect - * @param _endTime Unix timestamp at which restriction effects will gets end. - * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for variable) - */ - function modifyDailyGlobalRestriction( - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _endTime, - uint256 _restrictionType - ) - external - withPerm(ADMIN) - { - require(dailyGlobalRestriction.startTime > now, "Not allowed"); - _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, 1, _endTime, _restrictionType); - dailyGlobalRestriction = VolumeRestriction( - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - 1, - _endTime, - RestrictionType(_restrictionType) - ); - emit ModifyDailyGlobalRestriction( - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - 1, - _endTime, - _restrictionType - ); - } - - /** - * @notice Internal function have a logic to validate the txn amount with global restriction - */ - function _globalRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { - uint256[] memory timestamps = globalBucketDetails.timestamps; - uint256 fromTimestamp; - uint256 sumOfLastPeriod = 0; - if (timestamps.length == 0) { - // It will execute when the txn is performed first time after the addition of global restriction - fromTimestamp = globalRestriction.startTime; - } else { - // picking up the preivous timestamp - fromTimestamp = timestamps[timestamps.length -1]; - } - // Calculating the difference of days - uint256 diffDays = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now); - uint256[] memory passedTimestamps = new uint256[](diffDays); - // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod - // re-using the local variables to avoid the stack too deep error. - (sumOfLastPeriod, fromTimestamp, passedTimestamps) = _bucketCheck( - fromTimestamp, - diffDays, - _from, - globalBucketDetails, - true, - globalRestriction - ); - // Total amout that is transacted uptill now for `fromTimestamp` day - uint256 txSumOfDay = globalBucket[fromTimestamp]; - // Global transaction check - if (_globalTxCheck(sumOfLastPeriod, txSumOfDay, _amount)) { - // allow modification in storage when `_isTransfer` equals true - if(_isTransfer) { - // update the global storage - _updateGlobalStorage( - passedTimestamps, - fromTimestamp, - _amount, - diffDays, - sumOfLastPeriod - ); - } - return Result.NA; - } - return Result.INVALID; - } - - /** - * @notice Internal function to update the state variables related to global restriction - */ - function _updateGlobalStorage( - uint256[] passedTimestamps, - uint256 _fromTime, - uint256 _amount, - uint256 _diffDays, - uint256 _sumOfLastPeriod - ) - internal - { - for (uint256 i = 0; i < passedTimestamps.length; i++) { - // Add the timestamp that is already passed - globalBucketDetails.timestamps.push(passedTimestamps[i]); - } - // This condition is the works only when the transaction performed just after the startTime (_diffDays == 0) - if (globalBucketDetails.timestamps.length == 0) { - globalBucketDetails.timestamps.push(_fromTime); - } - if(_amount != 0) { - // updating the sumOfLastPeriod - globalBucketDetails.sumOfLastPeriod = _sumOfLastPeriod.add(_amount); - - // Re-using the local variable to avoid stack too deep error - _fromTime = globalBucketDetails.timestamps[globalBucketDetails.timestamps.length -1]; - globalBucket[_fromTime] = globalBucket[_fromTime].add(_amount); - } - } - - /// Internal function for the bucket check - function _bucketCheck( - uint256 _fromTime, - uint256 _diffDays, - address _from, - BucketDetails memory _bucketDetails, - bool _isGlobal, - VolumeRestriction _restriction - ) - internal - view - returns (uint256, uint256, uint256[]) - { - uint256[] memory passedTimestamps = new uint256[](_diffDays); - uint256 counter = _bucketDetails.timestamps.length; - uint256 i = 0; - if (_diffDays != 0) { - for (i = 0; i < _diffDays; i++) { - // calculating the timestamp that will used as an index of the next bucket - // i.e buckets period will be look like this T1 to T2-1, T2 to T3-1 .... - // where T1,T2,T3 are timestamps having 24 hrs difference - _fromTime = _fromTime.add(1 days); - - // This condition is to check whether the first rolling period is covered or not - // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting - // the earlier value at that index - if (counter >= _restriction.rollingPeriodInDays) { - if (_isGlobal) { - // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod - _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. - sub(globalBucket[_bucketDetails.timestamps[counter.sub(_restriction.rollingPeriodInDays)]]); - } else { - // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod - _bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod. - sub(bucket[_from][_bucketDetails.timestamps[counter.sub(_restriction.rollingPeriodInDays)]]); - } - - } - - // Adding the last amount that is transacted on the `_fromTime` not actually doing it but left written to understand - // the alogrithm - //_bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); - // Storing all those timestamps whose total transacted value is 0 - passedTimestamps[i] = _fromTime; - counter++; - } - } - return (_bucketDetails.sumOfLastPeriod, _fromTime, passedTimestamps); - } - - /** - * @notice Internal function used to validate the transaction for a given address - * If it validates then it also update the storage corressponds to the individual restriction - */ - function _individualRestrictionCheck( - address _from, - uint256 _amount, - bool _isTransfer - ) - internal - returns(Result) - { - uint256 fromTimestamp; - uint256 sumOfLastPeriod = 0; - if (bucketToUser[_from].timestamps.length == 0) { - // It will execute when the txn is performed first time after the addition of individual restriction - fromTimestamp = individualRestriction[_from].startTime; - } else { - // Picking up the last timestamp - fromTimestamp = bucketToUser[_from].timestamps[bucketToUser[_from].timestamps.length -1]; - } - // Calculating the difference of days - uint256 diffDays = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now); - uint256[] memory passedTimestamps = new uint256[](diffDays); - // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod - // re-using the local variables to avoid the stack too deep error. - (sumOfLastPeriod, fromTimestamp, passedTimestamps) = _bucketCheck( - fromTimestamp, - diffDays, - _from, - bucketToUser[_from], - false, - individualRestriction[_from] - ); - if (_checkValidAmountToTransact(sumOfLastPeriod, _amount, individualRestriction[_from])) { - if (_isTransfer) { - _updateIndividualStorage( - passedTimestamps, - _from, - fromTimestamp, - _amount, - diffDays, - sumOfLastPeriod - ); - } - return Result.NA; - } - return Result.INVALID; - } - - - function _globalTxCheck( - uint256 _sumOfLastPeriod, - uint256 _dailyAmount, - uint256 _amount - ) - internal - view - returns(bool) - { - // Checking whether the global day restriction is added or not if yes then calculate - // the total amount get traded on a particular day (~ _fromTime) - if (!_checkValidAmountToTransact(_dailyAmount, _amount, dailyGlobalRestriction) && - now <= dailyGlobalRestriction.endTime && now >= dailyGlobalRestriction.startTime - ) { - return false; - } - return _checkValidAmountToTransact(_sumOfLastPeriod, _amount, globalRestriction); - } - - function _checkValidAmountToTransact( - uint256 _sumOfLastPeriod, - uint256 _amountToTransact, - VolumeRestriction _restriction - ) - internal - view - returns (bool) - { - uint256 _allowedAmount = 0; - if (_restriction.typeOfRestriction == RestrictionType.Variable) { - _allowedAmount = (_restriction.allowedPercentageOfTokens.mul(ISecurityToken(securityToken).totalSupply()))/ uint256(10) ** 18; - } else { - _allowedAmount = _restriction.allowedTokens; - } - // Validation on the amount to transact - if (_allowedAmount >= _sumOfLastPeriod.add(_amountToTransact)) { - return true; - } else { - return false; - } - } - - function _updateIndividualStorage( - uint256[] passedTimestamps, - address _from, - uint256 _fromTime, - uint256 _amount, - uint256 _diffDays, - uint256 _sumOfLastPeriod - ) - internal - { - if (_diffDays != 0) { - for (uint256 i = 0; i < passedTimestamps.length; i++) { - // Add the timestamp that is already passed - bucketToUser[_from].timestamps.push(passedTimestamps[i]); - } - } - // This condition is the works only when the transaction performed just after the startTime (_diffDays == 0) - if (bucketToUser[_from].timestamps.length == 0) { - bucketToUser[_from].timestamps.push(_fromTime); - } - if(_amount != 0) { - bucketToUser[_from].sumOfLastPeriod = _sumOfLastPeriod.add(_amount); - - // Re-using the local variable to avoid stack too deep error - _fromTime = bucketToUser[_from].timestamps[bucketToUser[_from].timestamps.length -1]; - bucket[_from][_fromTime] = bucket[_from][_fromTime].add(_amount); - } - - } - - function _removeIndividualRestriction(address _user) internal { - require(_user != address(0), "Invalid address"); - require(individualRestriction[_user].endTime != 0, "Not present"); - individualRestriction[_user] = VolumeRestriction(0, 0, 0, 0, 0, RestrictionType(0)); - bucketToUser[_user].timestamps.length = 0; - bucketToUser[_user].sumOfLastPeriod = 0; - emit IndividualRestrictionRemoved(_user); - } - - function _modifyIndividualRestriction( - address _holder, - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _restrictionType - ) - internal - { - _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); - require(individualRestriction[_holder].startTime > now, "Not allowed"); - - individualRestriction[_holder] = VolumeRestriction( - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - RestrictionType(_restrictionType) - ); - emit ModifyIndividualRestriction( - _holder, - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - _restrictionType - ); - } - - function _addIndividualRestriction( - address _holder, - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _restrictionType - ) - internal - { - require( - individualRestriction[_holder].endTime < now, - "Already present" - ); - require(_holder != address(0) && !exemptList[_holder], "Invalid address"); - _checkInputParams(_allowedTokens, _allowedPercentageOfTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); - - if (individualRestriction[_holder].endTime != 0) { - _removeIndividualRestriction(_holder); - } - individualRestriction[_holder] = VolumeRestriction( - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - RestrictionType(_restrictionType) - ); - emit AddNewIndividualRestriction( - _holder, - _allowedTokens, - _allowedPercentageOfTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - _restrictionType - ); - } - - function _checkInputParams( - uint256 _allowedTokens, - uint256 _allowedPercentageOfTokens, - uint256 _startTime, - uint256 _rollingPeriodDays, - uint256 _endTime, - uint256 _restrictionType - ) - internal - view - { - require(_restrictionType == 0 || _restrictionType == 1, "Invalid type"); - if (_restrictionType == uint256(RestrictionType.Fixed)) { - require(_allowedTokens > 0, "Invalid value"); - } else { - require( - _allowedPercentageOfTokens > 0 && _allowedPercentageOfTokens <= 100 * 10 ** 16, - "Percentage is not within (0,100]" - ); - } - require(_startTime >= now && _endTime > _startTime); - // Maximum limit for the rollingPeriod is 365 days - require(_rollingPeriodDays >= 1 && _rollingPeriodDays <= 365); - require(BokkyPooBahsDateTimeLibrary.diffDays(_startTime, _endTime) >= _rollingPeriodDays); - } - - function _checkLengthOfArray( - uint256[] _allowedTokens, - uint256[] _allowedPercentageOfTokens, - uint256[] _startTimes, - uint256[] _rollingPeriodInDays, - uint256[] _endTimes, - uint256[] _restrictionTypes - ) - internal - pure - { - require( - _allowedTokens.length == _allowedPercentageOfTokens.length && - _allowedPercentageOfTokens.length == _startTimes.length && - _startTimes.length == _rollingPeriodInDays.length && - _rollingPeriodInDays.length == _endTimes.length && - _endTimes.length == _restrictionTypes.length, - "Array length mismatch" - ); - } - - /** - * @notice Use to get the bucket details for a given address - * @param _user Address of the token holder for whom the bucket details has queried - * @return uint256 Array of the timestamps - * @return uint256 sumOfLastPeriod - */ - function getBucketDetailsToUser(address _user) external view returns(uint256[], uint256) { - return( - bucketToUser[_user].timestamps, - bucketToUser[_user].sumOfLastPeriod - ); - } - - /** - * @notice Use to get the volume of token that being traded at a particular day (`_at` + 24 hours) for a given user - * @param _user Address of the token holder - * @param _at Timestamp - */ - function getTotalTradeByuser(address _user, uint256 _at) external view returns(uint256) { - return bucket[_user][_at]; - } - - /** - * @notice Use to get the global bucket details - * @return uint256 Array of timestamps - * @return uint256 sumOfLastPeriod - * @return uint256 Total amount traded on the latest timestamp - */ - function getGlobalBucketDetails() external view returns(uint256[], uint256, uint256) { - return( - globalBucketDetails.timestamps, - globalBucketDetails.sumOfLastPeriod, - globalBucket[globalBucketDetails.timestamps[globalBucketDetails.timestamps.length - 1]] - ); - } - - /** - * @notice This function returns the signature of configure function - */ - function getInitFunction() public view returns(bytes4) { - return bytes4(0); - } - - /** - * @notice Returns the permissions flag that are associated with Percentage transfer Manager - */ - function getPermissions() public view returns(bytes32[]) { - bytes32[] memory allPermissions = new bytes32[](1); - allPermissions[0] = ADMIN; - return allPermissions; - } - -} From c161fc912d8e7b1bb2b4c83c5bf9b73861ef3a6d Mon Sep 17 00:00:00 2001 From: satyam Date: Tue, 4 Dec 2018 18:58:59 +0530 Subject: [PATCH 25/56] logic fix --- .../TransferManager/VolumeRestrictionTM.sol | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index 5f2df00e4..61a9c1af7 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -468,34 +468,42 @@ contract VolumeRestrictionTM is ITransferManager { function _restrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { uint256 sumOfLastPeriod = 0; uint256 daysCovered = 0; - uint256 lastTradedDayTime; - uint256 globalSumOfLastPeriod; - uint256 globalDaysCovered; + uint256 lastTradedDayTime = 0; + uint256 globalSumOfLastPeriod = 0; + uint256 globalDaysCovered = 0; bool validIR = true; bool validGR = true; + uint8 _temp = 0; if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { (validIR, sumOfLastPeriod, lastTradedDayTime, daysCovered) = _individualRestrictionCheck(_from, _amount); - } - (validGR, globalSumOfLastPeriod, lastTradedDayTime, globalDaysCovered) = _globalRestrictionCheck(_from, _amount); - // Total amout that is transacted uptill now for `fromTimestamp` day - uint256 txSumOfDay = bucket[_from][lastTradedDayTime]; - // allow modification in storage when `_isTransfer` equals true - if (_isTransfer) { - // update the storage - _updateStorage( - _from, - _amount, - lastTradedDayTime, - sumOfLastPeriod, - globalSumOfLastPeriod, - daysCovered, - globalDaysCovered - ); + _temp = _temp + 1; + } + if (globalRestriction.endTime >= now && globalRestriction.startTime <= now) { + (validGR, globalSumOfLastPeriod, lastTradedDayTime, globalDaysCovered) = _globalRestrictionCheck(_from, _amount); + _temp = _temp + 1; + } + if (_temp > 0) { + // Total amout that is transacted uptill now for `fromTimestamp` day + uint256 txSumOfDay = bucket[_from][lastTradedDayTime]; + // allow modification in storage when `_isTransfer` equals true + if (_isTransfer) { + // update the storage + _updateStorage( + _from, + _amount, + lastTradedDayTime, + sumOfLastPeriod, + globalSumOfLastPeriod, + daysCovered, + globalDaysCovered + ); + } + if (validGR && validIR && _dailyTxCheck(txSumOfDay, _amount)) + return Result.NA; + else + return Result.INVALID; } - if (validGR && validIR && _dailyTxCheck(txSumOfDay, _amount)) - return Result.NA; - else - return Result.INVALID; + return Result.NA; } /** From 69a648ffce457b01fe62ca90c6901afc6488e602 Mon Sep 17 00:00:00 2001 From: satyam Date: Wed, 5 Dec 2018 18:52:06 +0530 Subject: [PATCH 26/56] modification in the _restrictionCheck --- .../TransferManager/VolumeRestrictionTM.sol | 35 ++++-- test/y_volume_restriction_tm.js | 108 ++++++++++++++---- 2 files changed, 107 insertions(+), 36 deletions(-) diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index 61a9c1af7..5ee117e35 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -26,6 +26,7 @@ contract VolumeRestrictionTM is ITransferManager { struct BucketDetails { uint256 lastTradedDayTime; + uint256 globalLastTradedDayTime; uint256 sumOfLastPeriod; // It is the sum of transacted amount within the last rollingPeriodDays uint256 globalSumOfLastPeriod; uint256 daysCovered; // No of days covered till (from the startTime of VolumeRestriction) @@ -468,23 +469,24 @@ contract VolumeRestrictionTM is ITransferManager { function _restrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { uint256 sumOfLastPeriod = 0; uint256 daysCovered = 0; - uint256 lastTradedDayTime = 0; + uint256 lastTradedDayTime = 0; + uint256 globalLastTradedDayTime = 0; uint256 globalSumOfLastPeriod = 0; uint256 globalDaysCovered = 0; bool validIR = true; bool validGR = true; - uint8 _temp = 0; + uint256 txSumOfDay = 0; if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { (validIR, sumOfLastPeriod, lastTradedDayTime, daysCovered) = _individualRestrictionCheck(_from, _amount); - _temp = _temp + 1; + txSumOfDay = txSumOfDay + 1; } if (globalRestriction.endTime >= now && globalRestriction.startTime <= now) { - (validGR, globalSumOfLastPeriod, lastTradedDayTime, globalDaysCovered) = _globalRestrictionCheck(_from, _amount); - _temp = _temp + 1; + (validGR, globalSumOfLastPeriod, globalLastTradedDayTime, globalDaysCovered) = _globalRestrictionCheck(_from, _amount); + txSumOfDay = txSumOfDay + 1; } - if (_temp > 0) { + if (txSumOfDay > 0) { // Total amout that is transacted uptill now for `fromTimestamp` day - uint256 txSumOfDay = bucket[_from][lastTradedDayTime]; + txSumOfDay = bucket[_from][lastTradedDayTime] <= bucket[_from][globalLastTradedDayTime]? bucket[_from][lastTradedDayTime] : bucket[_from][globalLastTradedDayTime]; // allow modification in storage when `_isTransfer` equals true if (_isTransfer) { // update the storage @@ -495,7 +497,8 @@ contract VolumeRestrictionTM is ITransferManager { sumOfLastPeriod, globalSumOfLastPeriod, daysCovered, - globalDaysCovered + globalDaysCovered, + globalLastTradedDayTime ); } if (validGR && validIR && _dailyTxCheck(txSumOfDay, _amount)) @@ -668,14 +671,19 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _sumOfLastPeriod, uint256 _globalSumOfLastPeriod, uint256 _daysCovered, - uint256 _globalDaysCovered + uint256 _globalDaysCovered, + uint256 _globalLastTradedDayTime ) internal { if (bucketToUser[_from].lastTradedDayTime != _lastTradedDayTime) { - // Assigning the latest transaction timestamp of the day + // Assigning the latest transaction timestamp of the day bucketToUser[_from].lastTradedDayTime = _lastTradedDayTime; } + if (bucketToUser[_from].globalLastTradedDayTime != _globalLastTradedDayTime) { + // Assigning the latest transaction timestamp of the day + bucketToUser[_from].globalLastTradedDayTime = _globalLastTradedDayTime; + } if (_amount != 0) { if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { bucketToUser[_from].daysCovered = _daysCovered; @@ -817,11 +825,14 @@ contract VolumeRestrictionTM is ITransferManager { * @return uint256 sumOfLastPeriod * @return uint256 days covered */ - function getBucketDetailsToUser(address _user) external view returns(uint256, uint256, uint256) { + function getBucketDetailsToUser(address _user) external view returns(uint256, uint256, uint256, uint256, uint256, uint256) { return( bucketToUser[_user].lastTradedDayTime, bucketToUser[_user].sumOfLastPeriod, - bucketToUser[_user].daysCovered + bucketToUser[_user].daysCovered, + bucketToUser[_user].globalDaysCovered, + bucketToUser[_user].globalSumOfLastPeriod, + bucketToUser[_user].globalLastTradedDayTime ); } diff --git a/test/y_volume_restriction_tm.js b/test/y_volume_restriction_tm.js index 4be373516..10b0e4f5b 100644 --- a/test/y_volume_restriction_tm.js +++ b/test/y_volume_restriction_tm.js @@ -76,6 +76,21 @@ contract('VolumeRestrictionTransferManager', accounts => { // Initial fee for ticker registry and security token registry const initRegFee = web3.utils.toWei("250"); + async function print(data, account) { + console.log(` + Latest timestamp: ${data[0].toNumber()} + SumOfLastPeriod: ${data[1].dividedBy(new BigNumber(10).pow(18)).toNumber()} + Days Covered: ${data[2].toNumber()} + Global days covered: ${data[3].toNumber()} + Global Sum of LastPeriod: ${data[4].dividedBy(new BigNumber(10).pow(18)).toNumber()} + Global Latest timestamp: ${data[5].toNumber()} + Individual Total Trade Till now : ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account, data[0])) + .dividedBy(new BigNumber(10).pow(18)).toNumber()} + Global Total Trade Till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account, data[5])) + .dividedBy(new BigNumber(10).pow(18)).toNumber()} + `) + } + before(async() => { // Accounts setup account_polymath = accounts[0]; @@ -549,6 +564,21 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should add the restriction succesfully after the expiry of previous one", async() => { await increaseTime(duration.days(5.1)); + console.log( + `Estimated gas: + ${await I_VolumeRestrictionTM.addIndividualRestriction.estimateGas( + account_investor1, + web3.utils.toWei("12"), + latestTime() + duration.seconds(2), + 3, + latestTime() + duration.days(10), + 0, + { + from: token_owner + } + )} + `); + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, web3.utils.toWei("12"), @@ -578,6 +608,9 @@ contract('VolumeRestrictionTransferManager', accounts => { console.log(result); assert.equal(result.toNumber(), 1); // Perform the transaction + console.log(`Estimated Gas: + ${await I_SecurityToken.transfer.estimateGas(account_investor3, web3.utils.toWei('.3'), {from: account_investor1})}` + ); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('.3'), {from: account_investor1}); // Check the balance of the investors let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); @@ -585,12 +618,8 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 34.7); let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - console.log('\n'); - console.log(` - Timestamps array index: ${data[0].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0])) - .dividedBy(new BigNumber(10).pow(18))} - `); + await print(data, account_investor1); + assert.equal( (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0])) .dividedBy(new BigNumber(10).pow(18)).toNumber(), @@ -600,39 +629,70 @@ contract('VolumeRestrictionTransferManager', accounts => { data[0].toNumber(), (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber() ); - - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Days Covered : ${data[2]} - `); assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 0.3); tempArray.push(0.3); }) + it("Should successfully add the global restriction", async() => { + await I_VolumeRestrictionTM.addGlobalRestriction( + web3.utils.toWei("15"), + latestTime() + duration.seconds(2), + 5, + latestTime() + duration.days(10), + 0, + { + from: token_owner + } + ); + + let data = await I_VolumeRestrictionTM.globalRestriction.call(); + assert.equal(data[0].toNumber(), web3.utils.toWei("15")); + assert.equal(data[2].toNumber(), 5); + }); + it("Should successfully transact the tokens after 1 and half days", async() => { await increaseTime(duration.days(1.5)); let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber(); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor1}); + console.log(`Estimated Gas: + ${await I_SecurityToken.transfer.estimateGas(account_investor3, web3.utils.toWei("2"), {from: account_investor1})}` + ); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2"), {from: account_investor1}); // Check the balance of the investors let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); // Verifying the balances - assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 33.7); - tempArray.push(1); + assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 32.7); + tempArray.push(2); let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - console.log(` - Timestamps array index : ${data[0].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0])) - .dividedBy(new BigNumber(10).pow(18)).toNumber()} - `); + await print(data, account_investor1); + assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0])) .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[tempArray.length - 1]); - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Days Covered : ${data[2]} - `); + assert.equal(data[0].toNumber(), (startTime + duration.days(data[2]))); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 1.3); + assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 2.3); + }); + + it("Should add the daily restriction successfully", async() => { + await I_VolumeRestrictionTM.addDailyGlobalRestriction( + web3.utils.toWei("5"), + latestTime() + duration.seconds(2), + latestTime() + duration.days(3), + 0, + { + from: token_owner + } + ); + let data = await I_VolumeRestrictionTM.dailyGlobalRestriction.call(); + assert.equal(data[0].dividedBy(new BigNumber(10).pow(18)).toNumber(), 5); + assert.equal(data[2].toNumber(), 1); + assert.equal(data[4].toNumber(), 0); + }); + + it("Should transfer tokens within the daily limit", async() =>{ + console.log(`Estimated Gas: + ${await I_SecurityToken.transfer.estimateGas(account_investor3, web3.utils.toWei("2"), {from: account_investor1})}` + ); }); it.skip("Should successfully transact more tokens on the same day (Fuzz test)", async() => { From d0b2aa2e9107d0bcb17188545ae0c71509280261 Mon Sep 17 00:00:00 2001 From: satyam Date: Thu, 6 Dec 2018 00:53:56 +0530 Subject: [PATCH 27/56] improvements in the update storage --- .../TransferManager/VolumeRestrictionTM.sol | 19 +- test/y_volume_restriction_tm.js | 250 ++++++++++-------- 2 files changed, 153 insertions(+), 116 deletions(-) diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index 5ee117e35..a79b75771 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -486,7 +486,7 @@ contract VolumeRestrictionTM is ITransferManager { } if (txSumOfDay > 0) { // Total amout that is transacted uptill now for `fromTimestamp` day - txSumOfDay = bucket[_from][lastTradedDayTime] <= bucket[_from][globalLastTradedDayTime]? bucket[_from][lastTradedDayTime] : bucket[_from][globalLastTradedDayTime]; + txSumOfDay = bucket[_from][lastTradedDayTime] >= bucket[_from][globalLastTradedDayTime]? bucket[_from][lastTradedDayTime] : bucket[_from][globalLastTradedDayTime]; // allow modification in storage when `_isTransfer` equals true if (_isTransfer) { // update the storage @@ -516,12 +516,12 @@ contract VolumeRestrictionTM is ITransferManager { uint256 daysCovered; uint256 fromTimestamp; uint256 sumOfLastPeriod = 0; - if (bucketToUser[_from].lastTradedDayTime < globalRestriction.startTime) { + if (bucketToUser[_from].globalLastTradedDayTime < globalRestriction.startTime) { // It will execute when the txn is performed first time after the addition of global restriction fromTimestamp = globalRestriction.startTime; } else { // picking up the preivous timestamp - fromTimestamp = bucketToUser[_from].lastTradedDayTime; + fromTimestamp = bucketToUser[_from].globalLastTradedDayTime; } // Calculating the difference of days uint256 diffDays = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now); @@ -685,14 +685,19 @@ contract VolumeRestrictionTM is ITransferManager { bucketToUser[_from].globalLastTradedDayTime = _globalLastTradedDayTime; } if (_amount != 0) { + if (globalRestriction.endTime >= now && globalRestriction.startTime <= now) { + bucketToUser[_from].globalSumOfLastPeriod = _globalSumOfLastPeriod.add(_amount); + bucketToUser[_from].globalDaysCovered = _globalDaysCovered; + // Increasing the total amount of the day by `_amount` + bucket[_from][_globalLastTradedDayTime] = bucket[_from][_globalLastTradedDayTime].add(_amount); + } if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { bucketToUser[_from].daysCovered = _daysCovered; bucketToUser[_from].sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + if (_lastTradedDayTime != _globalLastTradedDayTime) + // Increasing the total amount of the day by `_amount` + bucket[_from][_lastTradedDayTime] = bucket[_from][_lastTradedDayTime].add(_amount); } - bucketToUser[_from].globalSumOfLastPeriod = _globalSumOfLastPeriod.add(_amount); - bucketToUser[_from].globalDaysCovered = _globalDaysCovered; - // Increasing the total amount of the day by `_amount` - bucket[_from][_lastTradedDayTime] = bucket[_from][_lastTradedDayTime].add(_amount); } } diff --git a/test/y_volume_restriction_tm.js b/test/y_volume_restriction_tm.js index 10b0e4f5b..85ded68d6 100644 --- a/test/y_volume_restriction_tm.js +++ b/test/y_volume_restriction_tm.js @@ -84,13 +84,24 @@ contract('VolumeRestrictionTransferManager', accounts => { Global days covered: ${data[3].toNumber()} Global Sum of LastPeriod: ${data[4].dividedBy(new BigNumber(10).pow(18)).toNumber()} Global Latest timestamp: ${data[5].toNumber()} - Individual Total Trade Till now : ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account, data[0])) + Individual Total Trade on latestTimestamp : ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account, data[0])) .dividedBy(new BigNumber(10).pow(18)).toNumber()} - Global Total Trade Till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account, data[5])) + Global Total Trade on latestTimestamp: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account, data[5])) .dividedBy(new BigNumber(10).pow(18)).toNumber()} `) } + async function calculateSum(rollingPeriod, tempArray) { + let sum = 0; + let start = 0; + if (tempArray.length >= rollingPeriod) + start = tempArray.length - rollingPeriod; + for (let i = start; i < tempArray.length; i++) { + sum += tempArray[i]; + } + return sum; + } + before(async() => { // Accounts setup account_polymath = accounts[0]; @@ -565,13 +576,13 @@ contract('VolumeRestrictionTransferManager', accounts => { await increaseTime(duration.days(5.1)); console.log( - `Estimated gas: + `Estimated gas for addIndividualRestriction: ${await I_VolumeRestrictionTM.addIndividualRestriction.estimateGas( account_investor1, web3.utils.toWei("12"), latestTime() + duration.seconds(2), 3, - latestTime() + duration.days(10), + latestTime() + duration.days(6), 0, { from: token_owner @@ -584,7 +595,7 @@ contract('VolumeRestrictionTransferManager', accounts => { web3.utils.toWei("12"), latestTime() + duration.seconds(2), 3, - latestTime() + duration.days(10), + latestTime() + duration.days(6), 0, { from: token_owner @@ -605,11 +616,10 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should succesfully transact the tokens just after the startTime", async() => { // Check the transfer will be valid or not by calling the verifyTransfer() directly by using _isTransfer = false let result = await I_VolumeRestrictionTM.verifyTransfer.call(account_investor1, account_investor3, web3.utils.toWei('.3'), "0x0", false); - console.log(result); assert.equal(result.toNumber(), 1); // Perform the transaction - console.log(`Estimated Gas: - ${await I_SecurityToken.transfer.estimateGas(account_investor3, web3.utils.toWei('.3'), {from: account_investor1})}` + console.log(` + Gas estimation (Individual): ${await I_SecurityToken.transfer.estimateGas(account_investor3, web3.utils.toWei('.3'), {from: account_investor1})}` ); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('.3'), {from: account_investor1}); // Check the balance of the investors @@ -631,6 +641,7 @@ contract('VolumeRestrictionTransferManager', accounts => { ); assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 0.3); tempArray.push(0.3); + tempArrayGlobal.push(0); }) it("Should successfully add the global restriction", async() => { @@ -653,8 +664,11 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should successfully transact the tokens after 1 and half days", async() => { await increaseTime(duration.days(1.5)); let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber(); - console.log(`Estimated Gas: - ${await I_SecurityToken.transfer.estimateGas(account_investor3, web3.utils.toWei("2"), {from: account_investor1})}` + let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + let globalStartTime = (await I_VolumeRestrictionTM.globalRestriction.call())[1].toNumber(); + let globalRollingPeriod = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); + console.log(` + Gas estimation (Individual + Global): ${await I_SecurityToken.transfer.estimateGas(account_investor3, web3.utils.toWei("2"), {from: account_investor1})}` ); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2"), {from: account_investor1}); // Check the balance of the investors @@ -662,22 +676,33 @@ contract('VolumeRestrictionTransferManager', accounts => { // Verifying the balances assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 32.7); tempArray.push(2); - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + tempArrayGlobal.push(2); + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); await print(data, account_investor1); - assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[tempArray.length - 1]); + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + let globalAmt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[5].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); - assert.equal(data[0].toNumber(), (startTime + duration.days(data[2]))); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 2.3); + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray)); + assert.equal(data[2].toNumber(), 1); + assert.equal(data[3].toNumber(), 1); + assert.equal(data[4].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(globalRollingPeriod, tempArrayGlobal)); + assert.equal(data[5].toNumber(), globalStartTime + duration.days(data[3].toNumber())); + assert.equal(amt, 2); + assert.equal(globalAmt, 2); }); it("Should add the daily restriction successfully", async() => { await I_VolumeRestrictionTM.addDailyGlobalRestriction( web3.utils.toWei("5"), latestTime() + duration.seconds(2), - latestTime() + duration.days(3), + latestTime() + duration.days(1.1), 0, { from: token_owner @@ -689,19 +714,72 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.equal(data[4].toNumber(), 0); }); - it("Should transfer tokens within the daily limit", async() =>{ - console.log(`Estimated Gas: - ${await I_SecurityToken.transfer.estimateGas(account_investor3, web3.utils.toWei("2"), {from: account_investor1})}` + it("Should transfer tokens within the daily limit -- falied because of limit failing", async() =>{ + // Transfer the 3.1 tokens to check the daily limit + await increaseTime(5); + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei("3.1"), {from: account_investor1}) + ); + }); + + it("Should transfer the tokens within the daily limit", async() => { + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber(); + let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + let globalStartTime = (await I_VolumeRestrictionTM.globalRestriction.call())[1].toNumber(); + let globalRollingPeriod = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); + // Calculate the gas estimation + console.log(` + Gas estimation (Individual + Global + daily): ${await I_SecurityToken.transfer.estimateGas(account_investor3, web3.utils.toWei("3"), {from: account_investor1})} + `) + // Transfer the 3 tokens + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("3"), {from: account_investor1}); + tempArray[tempArray.length -1] += 3; + tempArrayGlobal[tempArrayGlobal.length -1] += 3; + // getting the data of the bucket + let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + // print the logs + await print(data, account_investor1); + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + let globalAmt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[5].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray)); + assert.equal(data[2].toNumber(), 1); + assert.equal(data[3].toNumber(), 1); + assert.equal(data[4].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArrayGlobal)); + assert.equal(data[5].toNumber(), globalStartTime + duration.days(data[3].toNumber())); + assert.equal(amt, 5); + assert.equal(globalAmt, 5); + }); + + it("Should transfer tokens within the daily limit -- falied because of limit failing", async() =>{ + // Transfer the 0.1 tokens to check the daily limit + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei("0.1"), {from: account_investor1}) ); }); - it.skip("Should successfully transact more tokens on the same day (Fuzz test)", async() => { + it("Should successfully transact tokens on the next day of the rolling period (Fuzz test)", async() => { + // Increase time half day + await increaseTime(duration.days(.52)); // Check the balance of the investors let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber(); + let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + let globalStartTime = (await I_VolumeRestrictionTM.globalRestriction.call())[1].toNumber(); + let globalRollingPeriod = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); + let totalAmountTransacted = 0; - for (let i = 0; i < 10; i++) { + // transactions performed + for (let i = 0; i < 5; i++) { let amount = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; + // Calculate the gas estimation + console.log(` + Gas estimation (Individual + Global + daily): ${await I_SecurityToken.transfer.estimateGas(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor1})} + `) await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor1}); console.log(`${i}: Restricted investor 1 able to transact ${amount} tokens to investor 3`); totalAmountTransacted += amount; @@ -711,92 +789,30 @@ contract('VolumeRestrictionTransferManager', accounts => { let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); // Verifying the balances assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); - tempArray[1] = tempArray[1] + totalAmountTransacted; - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - `); - assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), 1.3 + totalAmountTransacted, 0.000001); - assert.equal(data[0].length -1, 1); - }); - - it.skip("Should successfully transfer the tokens after half days-- should increase the day covered by 1", async() => { - let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - await increaseTime(duration.days(.5)); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2"), {from: account_investor1}); - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - tempArray.push(2); - - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); - } - - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - `); - assert.equal(data[0].length - 1, 2); - assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), - (oldData[1].plus(new BigNumber(2).times(new BigNumber(10).pow(18)))) - .dividedBy(new BigNumber(10).pow(18)).toNumber()); - }); - - it.skip("Should successfully transfer the tokens in the last day of rolling period", async() => { - // Check the balance of the investors - let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - - let totalAmountTransacted = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(totalAmountTransacted.toString()), {from: account_investor1}); - - // Check the balance of the investors - let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); - // Verifying the balances - assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); - tempArray[2] = tempArray[2] + totalAmountTransacted; + tempArray.push(totalAmountTransacted); + tempArrayGlobal.push(totalAmountTransacted); let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.000001); - } - - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - `); - assert.equal(data[0].length - 1, 2); - assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), - oldData[1].dividedBy(new BigNumber(10).pow(18)).toNumber() + totalAmountTransacted, 0.001); - }); - - it.skip("Should fail to transact the tokens more than the allowed tokens in a rolling period", async() => { + // print the logs + await print(data, account_investor1); + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + let globalAmt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[5].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray), 0.01); + assert.equal(data[2].toNumber(), 2); + assert.equal(data[3].toNumber(), 2); + assert.closeTo(data[4].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArrayGlobal), 0.01); + assert.equal(data[5].toNumber(), globalStartTime + duration.days(data[3].toNumber())); + assert.closeTo(amt, totalAmountTransacted, 0.01); + assert.closeTo(globalAmt, totalAmountTransacted, 0.01); + }); + + it("Should fail to transact the tokens more than the allowed tokens in a rolling period", async() => { + // Increase the time by 3/5 of the day + await increaseTime(duration.days(.6)); let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); let minimumAmount = new BigNumber(12).times(new BigNumber(10).pow(18)).minus(data[1]); let testAmount = minimumAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))) @@ -805,13 +821,29 @@ contract('VolumeRestrictionTransferManager', accounts => { ); }); - it.skip("Should fail to buy tokens in the new rolling period --failed because amount is more than last two timestamps", async() => { - await increaseTime(duration.days(2)); + it("Should fail to buy tokens in the new rolling period --failed because amount is more than last 1 timestamps", async() => { + await increaseTime(duration.days(1)); await catchRevert( I_SecurityToken.transfer(account_investor3, web3.utils.toWei("10"), {from: account_investor1}) ); }); + it("Should add the daily restriction again successfully", async() => { + await I_VolumeRestrictionTM.addDailyGlobalRestriction( + web3.utils.toWei("7"), + latestTime() + duration.seconds(2), + latestTime() + duration.days(10), + 0, + { + from: token_owner + } + ); + let data = await I_VolumeRestrictionTM.dailyGlobalRestriction.call(); + assert.equal(data[0].dividedBy(new BigNumber(10).pow(18)).toNumber(), 7); + assert.equal(data[2].toNumber(), 1); + assert.equal(data[4].toNumber(), 0); + }); + it.skip("Should transfer the tokens in a new rolling period", async() => { let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); From 3f256b840d8b84af246f8b73ff10be18309f5dcb Mon Sep 17 00:00:00 2001 From: satyam Date: Thu, 6 Dec 2018 17:12:06 +0530 Subject: [PATCH 28/56] changes in the business logic --- .../TransferManager/VolumeRestrictionTM.sol | 424 ++++++++---------- 1 file changed, 199 insertions(+), 225 deletions(-) diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index a79b75771..1f400e9ef 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -26,25 +26,25 @@ contract VolumeRestrictionTM is ITransferManager { struct BucketDetails { uint256 lastTradedDayTime; - uint256 globalLastTradedDayTime; - uint256 sumOfLastPeriod; // It is the sum of transacted amount within the last rollingPeriodDays - uint256 globalSumOfLastPeriod; + uint256 sumOfLastPeriod; // It is the sum of transacted amount within the last rollingPeriodDays uint256 daysCovered; // No of days covered till (from the startTime of VolumeRestriction) - uint256 globalDaysCovered; + uint256 dailyLastTradedDayTime; } // Global restriction that applies to all token holders - VolumeRestriction public globalRestriction; + VolumeRestriction public defaultRestriction; // Daily global restriction that applies to all token holders (Total ST traded daily is restricted) - VolumeRestriction public dailyGlobalRestriction; - // Variable stores the data matrix for the globa restrictions - BucketDetails internal globalBucketDetails; + VolumeRestriction public defaultDailyRestriction; // Restriction stored corresponds to a particular token holder mapping(address => VolumeRestriction) public individualRestriction; + // Daily restriction stored corresponds to a particular token holder + mapping(address => VolumeRestriction) public individualDailyRestriction; // Storing _from => day's timestamp => total amount transact in a day --individual mapping(address => mapping(uint256 => uint256)) internal bucket; // Storing the information that used to validate the transaction mapping(address => BucketDetails) internal bucketToUser; + // Storing the information related to default restriction + mapping(address => BucketDetails) internal defaultBucketToUser; // List of wallets that are exempted from all the restrictions applied by the this contract mapping(address => bool) public exemptList; @@ -69,31 +69,31 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _typeOfRestriction ); // Emit when the new global restriction is added - event AddGlobalRestriction( + event AddDefaultRestriction( uint256 _allowedTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, uint256 _typeOfRestriction ); - // Emit when the new daily (global) restriction is added - event AddDailyGlobalRestriction( + // Emit when the new daily (Default) restriction is added + event AddDefaultDailyRestriction( uint256 _allowedTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, uint256 _typeOfRestriction ); - // Emit when global restriction get modified - event ModifyGlobalRestriction( + // Emit when default restriction get modified + event ModifyDefaultRestriction( uint256 _allowedTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, uint256 _typeOfRestriction ); - // Emit when daily global restriction get modified - event ModifyDailyGlobalRestriction( + // Emit when daily default restriction get modified + event ModifyDefaultDailyRestriction( uint256 _allowedTokens, uint256 _startTime, uint256 _rollingPeriodInDays, @@ -102,10 +102,10 @@ contract VolumeRestrictionTM is ITransferManager { ); // Emit when the individual restriction gets removed event IndividualRestrictionRemoved(address _user); - // Emit when the global restriction gets removed - event GlobalRestrictionRemoved(); - // Emit when the daily global restriction gets removed - event DailyGlobalRestrictionRemoved(); + // Emit when the default restriction gets removed + event DefaultRestrictionRemoved(); + // Emit when the daily default restriction gets removed + event DefaultDailyRestrictionRemoved(); /** * @notice Constructor @@ -131,7 +131,17 @@ contract VolumeRestrictionTM is ITransferManager { if (!paused && _from != address(0) && !exemptList[_from]) { // Function must only be called by the associated security token if _isTransfer == true require(msg.sender == securityToken || !_isTransfer); - return _restrictionCheck(_from, _amount, _isTransfer); + // Checking the individual restriction if the `_from` comes in the individual category + if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now + || individualDailyRestriction[_from].endTime >= now && individualDailyRestriction[_from].startTime <= now) { + + return _individualRestrictionCheck(_from, _amount, _isTransfer); + // If the `_from` doesn't fall under the individual category. It will processed with in the global category automatically + } else if (defaultRestriction.endTime >= now && defaultRestriction.startTime <= now + || defaultDailyRestriction.endTime >= now && defaultDailyRestriction.startTime <= now) { + + return _defaultRestrictionCheck(_from, _amount, _isTransfer); + } } return Result.NA; } @@ -212,14 +222,14 @@ contract VolumeRestrictionTM is ITransferManager { } /** - * @notice Use to add the new global restriction for all token holder + * @notice Use to add the new default restriction for all token holder * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. * @param _startTime Unix timestamp at which restriction get into effect * @param _rollingPeriodInDays Rolling period in days (Minimum value should be 1 day) * @param _endTime Unix timestamp at which restriction effects will gets end. * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) */ - function addGlobalRestriction( + function addDefaultRestriction( uint256 _allowedTokens, uint256 _startTime, uint256 _rollingPeriodInDays, @@ -230,21 +240,18 @@ contract VolumeRestrictionTM is ITransferManager { withPerm(ADMIN) { require( - globalRestriction.endTime < now, + defaultRestriction.endTime < now, "Not allowed" ); _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); - if (globalRestriction.endTime != 0) { - removeGlobalRestriction(); - } - globalRestriction = VolumeRestriction( + defaultRestriction = VolumeRestriction( _allowedTokens, _startTime, _rollingPeriodInDays, _endTime, RestrictionType(_restrictionType) ); - emit AddGlobalRestriction( + emit AddDefaultRestriction( _allowedTokens, _startTime, _rollingPeriodInDays, @@ -254,13 +261,13 @@ contract VolumeRestrictionTM is ITransferManager { } /** - * @notice Use to add the new global daily restriction for all token holder + * @notice Use to add the new default daily restriction for all token holder * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. * @param _startTime Unix timestamp at which restriction get into effect * @param _endTime Unix timestamp at which restriction effects will gets end. * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) */ - function addDailyGlobalRestriction( + function addDefaultDailyRestriction( uint256 _allowedTokens, uint256 _startTime, uint256 _endTime, @@ -270,18 +277,18 @@ contract VolumeRestrictionTM is ITransferManager { withPerm(ADMIN) { require( - dailyGlobalRestriction.endTime < now, + defaultDailyRestriction.endTime < now, "Not Allowed" ); _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); - dailyGlobalRestriction = VolumeRestriction( + defaultDailyRestriction = VolumeRestriction( _allowedTokens, _startTime, 1, _endTime, RestrictionType(_restrictionType) ); - emit AddDailyGlobalRestriction( + emit AddDefaultDailyRestriction( _allowedTokens, _startTime, 1, @@ -309,24 +316,21 @@ contract VolumeRestrictionTM is ITransferManager { } /** - * @notice Use to remove the global restriction + * @notice Use to remove the default restriction */ - function removeGlobalRestriction() public withPerm(ADMIN) { - require(globalRestriction.endTime != 0); - globalRestriction = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); - globalBucketDetails.lastTradedDayTime = 0; - globalBucketDetails.sumOfLastPeriod = 0; - globalBucketDetails.daysCovered = 0; - emit GlobalRestrictionRemoved(); + function removeDefaultRestriction() public withPerm(ADMIN) { + require(defaultRestriction.endTime != 0); + defaultRestriction = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); + emit DefaultRestrictionRemoved(); } /** - * @notice Use to remove the daily global restriction + * @notice Use to remove the daily default restriction */ - function removeDailyGlobalRestriction() external withPerm(ADMIN) { - require(dailyGlobalRestriction.endTime != 0); - dailyGlobalRestriction = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); - emit DailyGlobalRestrictionRemoved(); + function removeDefaultDailyRestriction() external withPerm(ADMIN) { + require(defaultDailyRestriction.endTime != 0); + defaultDailyRestriction = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); + emit DefaultDailyRestrictionRemoved(); } /** @@ -401,7 +405,7 @@ contract VolumeRestrictionTM is ITransferManager { * @param _endTime Unix timestamp at which restriction effects will gets end. * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) */ - function modifyGlobalRestriction( + function modifyDefaultRestriction( uint256 _allowedTokens, uint256 _startTime, uint256 _rollingPeriodInDays, @@ -411,16 +415,16 @@ contract VolumeRestrictionTM is ITransferManager { external withPerm(ADMIN) { - require(globalRestriction.startTime > now, "Not allowed"); + require(defaultRestriction.startTime > now, "Not allowed"); _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); - globalRestriction = VolumeRestriction( + defaultRestriction = VolumeRestriction( _allowedTokens, _startTime, _rollingPeriodInDays, _endTime, RestrictionType(_restrictionType) ); - emit ModifyGlobalRestriction( + emit ModifyDefaultRestriction( _allowedTokens, _startTime, _rollingPeriodInDays, @@ -430,13 +434,13 @@ contract VolumeRestrictionTM is ITransferManager { } /** - * @notice Use to modify the daily global restriction for all token holder + * @notice Use to modify the daily default restriction for all token holder * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. * @param _startTime Unix timestamp at which restriction get into effect * @param _endTime Unix timestamp at which restriction effects will gets end. * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) */ - function modifyDailyGlobalRestriction( + function modifyDefaultDailyRestriction( uint256 _allowedTokens, uint256 _startTime, uint256 _endTime, @@ -445,16 +449,16 @@ contract VolumeRestrictionTM is ITransferManager { external withPerm(ADMIN) { - require(dailyGlobalRestriction.startTime > now, "Not allowed"); + require(defaultDailyRestriction.startTime > now, "Not allowed"); _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); - dailyGlobalRestriction = VolumeRestriction( + defaultDailyRestriction = VolumeRestriction( _allowedTokens, _startTime, 1, _endTime, RestrictionType(_restrictionType) ); - emit ModifyDailyGlobalRestriction( + emit ModifyDefaultDailyRestriction( _allowedTokens, _startTime, 1, @@ -464,116 +468,132 @@ contract VolumeRestrictionTM is ITransferManager { } /** - * @notice Internal function have a logic to validate the txn amount with global restriction - */ - function _restrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { - uint256 sumOfLastPeriod = 0; - uint256 daysCovered = 0; - uint256 lastTradedDayTime = 0; - uint256 globalLastTradedDayTime = 0; - uint256 globalSumOfLastPeriod = 0; - uint256 globalDaysCovered = 0; - bool validIR = true; - bool validGR = true; - uint256 txSumOfDay = 0; - if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { - (validIR, sumOfLastPeriod, lastTradedDayTime, daysCovered) = _individualRestrictionCheck(_from, _amount); - txSumOfDay = txSumOfDay + 1; - } - if (globalRestriction.endTime >= now && globalRestriction.startTime <= now) { - (validGR, globalSumOfLastPeriod, globalLastTradedDayTime, globalDaysCovered) = _globalRestrictionCheck(_from, _amount); - txSumOfDay = txSumOfDay + 1; - } - if (txSumOfDay > 0) { - // Total amout that is transacted uptill now for `fromTimestamp` day - txSumOfDay = bucket[_from][lastTradedDayTime] >= bucket[_from][globalLastTradedDayTime]? bucket[_from][lastTradedDayTime] : bucket[_from][globalLastTradedDayTime]; - // allow modification in storage when `_isTransfer` equals true - if (_isTransfer) { - // update the storage - _updateStorage( - _from, - _amount, - lastTradedDayTime, - sumOfLastPeriod, - globalSumOfLastPeriod, - daysCovered, - globalDaysCovered, - globalLastTradedDayTime - ); + * @notice Internal function used to validate the transaction for a given address + * If it validates then it also update the storage corressponds to the default restriction + */ + function _defaultRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { + // using the variable to avoid stack too deep error + uint256 daysCovered = defaultRestriction.rollingPeriodInDays; + uint256 fromTimestamp = 0; + uint256 sumOfLastPeriod = 0; + uint256 dailyTime = 0; + bool allowedDefault = true; + bool allowedDaily; + if (defaultRestriction.endTime >= now && defaultRestriction.startTime <= now) { + if (defaultBucketToUser[_from].lastTradedDayTime < defaultRestriction.startTime) { + // It will execute when the txn is performed first time after the addition of individual restriction + fromTimestamp = defaultRestriction.startTime; + } else { + // Picking up the last timestamp + fromTimestamp = defaultBucketToUser[_from].lastTradedDayTime; + } + + // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod + // re-using the local variables to avoid the stack too deep error. + (sumOfLastPeriod, fromTimestamp, daysCovered) = _bucketCheck( + fromTimestamp, + BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now), + _from, + daysCovered, + defaultBucketToUser[_from] + ); + // validation of the transaction amount + if (!_checkValidAmountToTransact(sumOfLastPeriod, _amount, defaultRestriction)) { + allowedDefault == false; } - if (validGR && validIR && _dailyTxCheck(txSumOfDay, _amount)) - return Result.NA; - else - return Result.INVALID; } - return Result.NA; - } - - /** - * @notice Internal function have a logic to validate the txn amount with global restriction - */ - function _globalRestrictionCheck(address _from, uint256 _amount) internal view returns (bool, uint256, uint256, uint256) { - uint256 daysCovered; - uint256 fromTimestamp; - uint256 sumOfLastPeriod = 0; - if (bucketToUser[_from].globalLastTradedDayTime < globalRestriction.startTime) { - // It will execute when the txn is performed first time after the addition of global restriction - fromTimestamp = globalRestriction.startTime; - } else { - // picking up the preivous timestamp - fromTimestamp = bucketToUser[_from].globalLastTradedDayTime; + (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, fromTimestamp, defaultBucketToUser[_from].dailyLastTradedDayTime, defaultDailyRestriction); + + if (_isTransfer) { + _updateStorage( + _from, + _amount, + fromTimestamp, + sumOfLastPeriod, + daysCovered, + dailyTime, + defaultBucketToUser[_from] + ); } - // Calculating the difference of days - uint256 diffDays = BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now); - // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod - // re-using the local variables to avoid the stack too deep error. - (sumOfLastPeriod, fromTimestamp, daysCovered) = _bucketCheck( - fromTimestamp, - diffDays, - _from, - bucketToUser[_from], - true, - globalRestriction.rollingPeriodInDays - ); - // validation of the transaction amount - if (_checkValidAmountToTransact(sumOfLastPeriod, _amount, globalRestriction)) { - return (true, sumOfLastPeriod, fromTimestamp, daysCovered); - } - return (false, sumOfLastPeriod, fromTimestamp, daysCovered); + return ((allowedDaily && allowedDefault) == true ? Result.NA : Result.INVALID); } /** * @notice Internal function used to validate the transaction for a given address * If it validates then it also update the storage corressponds to the individual restriction */ - function _individualRestrictionCheck(address _from, uint256 _amount) internal view returns (bool, uint256, uint256, uint256) { + function _individualRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { // using the variable to avoid stack too deep error uint256 daysCovered = individualRestriction[_from].rollingPeriodInDays; - uint256 fromTimestamp; + uint256 fromTimestamp = 0; uint256 sumOfLastPeriod = 0; - if (bucketToUser[_from].lastTradedDayTime < individualRestriction[_from].startTime) { - // It will execute when the txn is performed first time after the addition of individual restriction - fromTimestamp = individualRestriction[_from].startTime; - } else { - // Picking up the last timestamp - fromTimestamp = bucketToUser[_from].lastTradedDayTime; + uint256 dailyTime = 0; + bool allowedIndividual = true; + bool allowedDaily; + if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { + if (bucketToUser[_from].lastTradedDayTime < individualRestriction[_from].startTime) { + // It will execute when the txn is performed first time after the addition of individual restriction + fromTimestamp = individualRestriction[_from].startTime; + } else { + // Picking up the last timestamp + fromTimestamp = bucketToUser[_from].lastTradedDayTime; + } + + // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod + // re-using the local variables to avoid the stack too deep error. + (sumOfLastPeriod, fromTimestamp, daysCovered) = _bucketCheck( + fromTimestamp, + BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now), + _from, + daysCovered, + bucketToUser[_from] + ); + // validation of the transaction amount + if (!_checkValidAmountToTransact(sumOfLastPeriod, _amount, individualRestriction[_from])) { + allowedIndividual == false; + } } - - // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod - // re-using the local variables to avoid the stack too deep error. - (sumOfLastPeriod, fromTimestamp, daysCovered) = _bucketCheck( - fromTimestamp, - BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now), - _from, - bucketToUser[_from], - false, - daysCovered - ); - // validation of the transaction amount - if (_checkValidAmountToTransact(sumOfLastPeriod, _amount, individualRestriction[_from])) { - return (true, sumOfLastPeriod, fromTimestamp, daysCovered); - } - return (false, sumOfLastPeriod, fromTimestamp, daysCovered); + (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, fromTimestamp, bucketToUser[_from].dailyLastTradedDayTime, individualDailyRestriction[_from]); + + if (_isTransfer) { + _updateStorage( + _from, + _amount, + fromTimestamp, + sumOfLastPeriod, + daysCovered, + dailyTime, + bucketToUser[_from] + ); + } + + return ((allowedDaily && allowedIndividual) == true ? Result.NA : Result.INVALID); + } + + function _dailyTxCheck( + address from, + uint256 amount, + uint256 fromTimestamp, + uint256 dailyLastTradedDayTime, + VolumeRestriction restriction + ) + internal + view + returns(bool, uint256) + { + // Checking whether the daily restriction is added or not if yes then calculate + // the total amount get traded on a particular day (~ _fromTime) + if ( now <= restriction.endTime && now >= restriction.startTime) { + uint256 txSumOfDay = 0; + if (now.sub(dailyLastTradedDayTime) < 1 days || dailyLastTradedDayTime == 0) { + txSumOfDay = bucket[from][dailyLastTradedDayTime] >= bucket[from][fromTimestamp] ? bucket[from][dailyLastTradedDayTime] : bucket[from][fromTimestamp]; + } else { + txSumOfDay = 0; + dailyLastTradedDayTime = now; + } + return (_checkValidAmountToTransact(txSumOfDay, amount, restriction), dailyLastTradedDayTime); + } + return (true, dailyLastTradedDayTime); } /// Internal function for the bucket check @@ -581,21 +601,16 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _fromTime, uint256 _diffDays, address _from, - BucketDetails memory _bucketDetails, - bool _isGlobal, - uint256 _rollingPeriodInDays + uint256 _rollingPeriodInDays, + BucketDetails memory _bucketDetails ) internal view returns (uint256, uint256, uint256) { - uint256 counter = _bucketDetails.globalDaysCovered; - uint256 sumOfLastPeriod = _bucketDetails.globalSumOfLastPeriod; + uint256 counter = _bucketDetails.daysCovered; + uint256 sumOfLastPeriod = _bucketDetails.sumOfLastPeriod; uint256 i = 0; - if (!_isGlobal) { - counter = _bucketDetails.daysCovered; - sumOfLastPeriod = _bucketDetails.sumOfLastPeriod; - } if (_diffDays >= _rollingPeriodInDays) { // If the difference of days is greater than the rollingPeriod then sumOfLastPeriod will always be zero sumOfLastPeriod = 0; @@ -623,24 +638,6 @@ contract VolumeRestrictionTM is ITransferManager { return (sumOfLastPeriod, _fromTime, counter); } - function _dailyTxCheck( - uint256 _dailyAmount, - uint256 _amount - ) - internal - view - returns(bool) - { - // Checking whether the global day restriction is added or not if yes then calculate - // the total amount get traded on a particular day (~ _fromTime) - if ( now <= dailyGlobalRestriction.endTime && now >= dailyGlobalRestriction.startTime) - { - if (!_checkValidAmountToTransact(_dailyAmount, _amount, dailyGlobalRestriction)) - return false; - } - return true; - } - function _checkValidAmountToTransact( uint256 _sumOfLastPeriod, uint256 _amountToTransact, @@ -657,11 +654,7 @@ contract VolumeRestrictionTM is ITransferManager { _allowedAmount = _restriction.allowedTokens; } // Validation on the amount to transact - if (_allowedAmount >= _sumOfLastPeriod.add(_amountToTransact)) { - return true; - } else { - return false; - } + return (_allowedAmount >= _sumOfLastPeriod.add(_amountToTransact)); } function _updateStorage( @@ -669,37 +662,33 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _amount, uint256 _lastTradedDayTime, uint256 _sumOfLastPeriod, - uint256 _globalSumOfLastPeriod, uint256 _daysCovered, - uint256 _globalDaysCovered, - uint256 _globalLastTradedDayTime + uint256 _dailyLastTradedDayTime, + BucketDetails storage _details ) internal - { - if (bucketToUser[_from].lastTradedDayTime != _lastTradedDayTime) { + { + // Cheap storage technique + if (_details.lastTradedDayTime != _lastTradedDayTime) { // Assigning the latest transaction timestamp of the day - bucketToUser[_from].lastTradedDayTime = _lastTradedDayTime; + _details.lastTradedDayTime = _lastTradedDayTime; } - if (bucketToUser[_from].globalLastTradedDayTime != _globalLastTradedDayTime) { + if (_lastTradedDayTime != 0 && _lastTradedDayTime != _dailyLastTradedDayTime) { + // Assigning the latest transaction timestamp of the day + _details.dailyLastTradedDayTime = _lastTradedDayTime; + } else if (_details.dailyLastTradedDayTime != _dailyLastTradedDayTime) { // Assigning the latest transaction timestamp of the day - bucketToUser[_from].globalLastTradedDayTime = _globalLastTradedDayTime; + _details.dailyLastTradedDayTime = _dailyLastTradedDayTime; } - if (_amount != 0) { - if (globalRestriction.endTime >= now && globalRestriction.startTime <= now) { - bucketToUser[_from].globalSumOfLastPeriod = _globalSumOfLastPeriod.add(_amount); - bucketToUser[_from].globalDaysCovered = _globalDaysCovered; - // Increasing the total amount of the day by `_amount` - bucket[_from][_globalLastTradedDayTime] = bucket[_from][_globalLastTradedDayTime].add(_amount); - } - if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { - bucketToUser[_from].daysCovered = _daysCovered; - bucketToUser[_from].sumOfLastPeriod = _sumOfLastPeriod.add(_amount); - if (_lastTradedDayTime != _globalLastTradedDayTime) - // Increasing the total amount of the day by `_amount` - bucket[_from][_lastTradedDayTime] = bucket[_from][_lastTradedDayTime].add(_amount); - } + if (_details.daysCovered != _daysCovered) { + _details.daysCovered = _daysCovered; } - + if (_amount != 0) { + _details.sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + _dailyLastTradedDayTime = _details.dailyLastTradedDayTime; + // Increasing the total amount of the day by `_amount` + bucket[_from][_dailyLastTradedDayTime] = bucket[_from][_dailyLastTradedDayTime].add(_amount); + } } function _removeIndividualRestriction(address _user) internal { @@ -830,14 +819,11 @@ contract VolumeRestrictionTM is ITransferManager { * @return uint256 sumOfLastPeriod * @return uint256 days covered */ - function getBucketDetailsToUser(address _user) external view returns(uint256, uint256, uint256, uint256, uint256, uint256) { + function getBucketDetailsToUser(address _user) external view returns(uint256, uint256, uint256) { return( bucketToUser[_user].lastTradedDayTime, bucketToUser[_user].sumOfLastPeriod, - bucketToUser[_user].daysCovered, - bucketToUser[_user].globalDaysCovered, - bucketToUser[_user].globalSumOfLastPeriod, - bucketToUser[_user].globalLastTradedDayTime + bucketToUser[_user].daysCovered ); } @@ -850,18 +836,6 @@ contract VolumeRestrictionTM is ITransferManager { return bucket[_user][_at]; } - /** - * @notice Use to get the global bucket details - * @return uint256 lastTradedDayTime - * @return uint256 sumOfLastPeriod - */ - function getGlobalBucketDetails() external view returns(uint256, uint256) { - return( - globalBucketDetails.lastTradedDayTime, - globalBucketDetails.sumOfLastPeriod - ); - } - /** * @notice This function returns the signature of configure function */ @@ -878,4 +852,4 @@ contract VolumeRestrictionTM is ITransferManager { return allPermissions; } -} +} \ No newline at end of file From 6bbe51dd77962385f368e6d81447b516a2308325 Mon Sep 17 00:00:00 2001 From: satyam Date: Thu, 6 Dec 2018 20:12:33 +0530 Subject: [PATCH 29/56] add functions for the individual daily restrictions --- .../TransferManager/VolumeRestrictionTM.sol | 251 ++++++++++++++++-- 1 file changed, 227 insertions(+), 24 deletions(-) diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index 1f400e9ef..d80ab217e 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -84,6 +84,15 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _endTime, uint256 _typeOfRestriction ); + // Emit when the new daily (Individual) restriction is added + event AddIndividualDailyRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); // Emit when default restriction get modified event ModifyDefaultRestriction( uint256 _allowedTokens, @@ -187,6 +196,69 @@ contract VolumeRestrictionTM is ITransferManager { ); } + /** + * @notice Use to add the new individual daily restriction for all token holder + * @param _holder Address of the token holder, whom restriction will be implied + * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function addIndividualDailyRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + _addIndividualDailyRestriction( + _holder, + _allowedTokens, + _startTime, + _endTime, + _restrictionType + ); + } + + /** + * @notice Use to add the new individual daily restriction for multiple token holders + * @param _holders Array of address of the token holders, whom restriction will be implied + * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. + * @param _startTimes Array of unix timestamps at which restrictions get into effect + * @param _endTimes Array of unix timestamps at which restriction effects will gets end. + * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function addIndividualDailyRestrictionMulti( + address[] _holders, + uint256[] _allowedTokens, + uint256[] _startTimes, + uint256[] _endTimes, + uint256[] _restrictionTypes + ) + public + withPerm(ADMIN) + { + require( + _allowedTokens.length == _startTimes.length && + _startTimes.length == _holders.length && + _holders.length == _endTimes.length && + _endTimes.length == _restrictionTypes.length, + "Array length mismatch" + ); + for (uint256 i = 0; i < _holders.length; i++) { + _addIndividualDailyRestriction( + _holders[i], + _allowedTokens[i], + _startTimes[i], + _endTimes[i], + _restrictionTypes[i] + ); + } + } + /** * @notice Use to add the new individual restriction for multiple token holders * @param _holders Array of address of the token holders, whom restriction will be implied @@ -363,6 +435,70 @@ contract VolumeRestrictionTM is ITransferManager { ); } + /** + * @notice Use to modify the existing individual daily restriction for a given token holder + * @param _holder Address of the token holder, whom restriction will be implied + * @param _allowedTokens Amount of tokens allowed to be trade for a given address. + * @param _startTime Unix timestamp at which restriction get into effect + * @param _endTime Unix timestamp at which restriction effects will gets end. + * @param _restrictionType It will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function modifyIndividualDailyRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _endTime, + uint256 _restrictionType + ) + external + withPerm(ADMIN) + { + _modifyIndividualDailyRestriction( + _holder, + _allowedTokens, + _startTime, + _endTime, + _restrictionType + ); + } + + /** + * @notice Use to modify the existing individual daily restriction for multiple token holders + * @param _holders Array of address of the token holders, whom restriction will be implied + * @param _allowedTokens Array of amount of tokens allowed to be trade for a given address. + * @param _startTimes Array of unix timestamps at which restrictions get into effect + * @param _endTimes Array of unix timestamps at which restriction effects will gets end. + * @param _restrictionTypes Array of restriction types value will be 0 or 1 (i.e 0 for fixed while 1 for Percentage) + */ + function modifyIndividualDailyRestrictionMulti( + address[] _holders, + uint256[] _allowedTokens, + uint256[] _startTimes, + uint256[] _endTimes, + uint256[] _restrictionTypes + ) + public + withPerm(ADMIN) + { + require( + _allowedTokens.length == _startTimes.length && + _startTimes.length == _holders.length && + _holders.length == _endTimes.length && + _endTimes.length == _restrictionTypes.length, + "Array length mismatch" + ); + require(_holders.length == _allowedTokens.length, "Length mismatch"); + for (uint256 i = 0; i < _holders.length; i++) { + _modifyIndividualDailyRestriction( + _holders[i], + _allowedTokens[i], + _startTimes[i], + _endTimes[i], + _restrictionTypes[i] + ); + } + } + /** * @notice Use to modify the existing individual restriction for multiple token holders * @param _holders Array of address of the token holders, whom restriction will be implied @@ -473,6 +609,7 @@ contract VolumeRestrictionTM is ITransferManager { */ function _defaultRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { // using the variable to avoid stack too deep error + BucketDetails memory bucketDetails = defaultBucketToUser[_from]; uint256 daysCovered = defaultRestriction.rollingPeriodInDays; uint256 fromTimestamp = 0; uint256 sumOfLastPeriod = 0; @@ -480,12 +617,12 @@ contract VolumeRestrictionTM is ITransferManager { bool allowedDefault = true; bool allowedDaily; if (defaultRestriction.endTime >= now && defaultRestriction.startTime <= now) { - if (defaultBucketToUser[_from].lastTradedDayTime < defaultRestriction.startTime) { + if (bucketDetails.lastTradedDayTime < defaultRestriction.startTime) { // It will execute when the txn is performed first time after the addition of individual restriction fromTimestamp = defaultRestriction.startTime; } else { // Picking up the last timestamp - fromTimestamp = defaultBucketToUser[_from].lastTradedDayTime; + fromTimestamp = bucketDetails.lastTradedDayTime; } // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod @@ -495,14 +632,14 @@ contract VolumeRestrictionTM is ITransferManager { BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now), _from, daysCovered, - defaultBucketToUser[_from] + bucketDetails ); // validation of the transaction amount if (!_checkValidAmountToTransact(sumOfLastPeriod, _amount, defaultRestriction)) { allowedDefault == false; } } - (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, fromTimestamp, defaultBucketToUser[_from].dailyLastTradedDayTime, defaultDailyRestriction); + (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, fromTimestamp, bucketDetails.dailyLastTradedDayTime, defaultDailyRestriction); if (_isTransfer) { _updateStorage( @@ -512,7 +649,7 @@ contract VolumeRestrictionTM is ITransferManager { sumOfLastPeriod, daysCovered, dailyTime, - defaultBucketToUser[_from] + true ); } return ((allowedDaily && allowedDefault) == true ? Result.NA : Result.INVALID); @@ -522,21 +659,24 @@ contract VolumeRestrictionTM is ITransferManager { * @notice Internal function used to validate the transaction for a given address * If it validates then it also update the storage corressponds to the individual restriction */ - function _individualRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { + function _individualRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) private returns (Result) { // using the variable to avoid stack too deep error + BucketDetails memory bucketDetails = bucketToUser[_from]; + VolumeRestriction memory dailyRestriction = individualDailyRestriction[_from]; + VolumeRestriction memory restriction = individualRestriction[_from]; uint256 daysCovered = individualRestriction[_from].rollingPeriodInDays; uint256 fromTimestamp = 0; uint256 sumOfLastPeriod = 0; uint256 dailyTime = 0; bool allowedIndividual = true; bool allowedDaily; - if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) { - if (bucketToUser[_from].lastTradedDayTime < individualRestriction[_from].startTime) { + if (restriction.endTime >= now && restriction.startTime <= now) { + if (bucketDetails.lastTradedDayTime < restriction.startTime) { // It will execute when the txn is performed first time after the addition of individual restriction - fromTimestamp = individualRestriction[_from].startTime; + fromTimestamp = restriction.startTime; } else { // Picking up the last timestamp - fromTimestamp = bucketToUser[_from].lastTradedDayTime; + fromTimestamp = bucketDetails.lastTradedDayTime; } // Check with the bucket and parse all the new timestamps to calculate the sumOfLastPeriod @@ -546,14 +686,14 @@ contract VolumeRestrictionTM is ITransferManager { BokkyPooBahsDateTimeLibrary.diffDays(fromTimestamp, now), _from, daysCovered, - bucketToUser[_from] + bucketDetails ); // validation of the transaction amount - if (!_checkValidAmountToTransact(sumOfLastPeriod, _amount, individualRestriction[_from])) { + if (!_checkValidAmountToTransact(sumOfLastPeriod, _amount, restriction)) { allowedIndividual == false; } } - (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, fromTimestamp, bucketToUser[_from].dailyLastTradedDayTime, individualDailyRestriction[_from]); + (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, fromTimestamp, bucketDetails.dailyLastTradedDayTime, dailyRestriction); if (_isTransfer) { _updateStorage( @@ -563,7 +703,7 @@ contract VolumeRestrictionTM is ITransferManager { sumOfLastPeriod, daysCovered, dailyTime, - bucketToUser[_from] + false ); } @@ -664,28 +804,33 @@ contract VolumeRestrictionTM is ITransferManager { uint256 _sumOfLastPeriod, uint256 _daysCovered, uint256 _dailyLastTradedDayTime, - BucketDetails storage _details + bool isDefault ) internal { + BucketDetails storage details; + if (isDefault) + details = defaultBucketToUser[_from]; + else + details = bucketToUser[_from]; // Cheap storage technique - if (_details.lastTradedDayTime != _lastTradedDayTime) { + if (details.lastTradedDayTime != _lastTradedDayTime) { // Assigning the latest transaction timestamp of the day - _details.lastTradedDayTime = _lastTradedDayTime; + details.lastTradedDayTime = _lastTradedDayTime; } if (_lastTradedDayTime != 0 && _lastTradedDayTime != _dailyLastTradedDayTime) { // Assigning the latest transaction timestamp of the day - _details.dailyLastTradedDayTime = _lastTradedDayTime; - } else if (_details.dailyLastTradedDayTime != _dailyLastTradedDayTime) { + details.dailyLastTradedDayTime = _lastTradedDayTime; + } else if (details.dailyLastTradedDayTime != _dailyLastTradedDayTime) { // Assigning the latest transaction timestamp of the day - _details.dailyLastTradedDayTime = _dailyLastTradedDayTime; + details.dailyLastTradedDayTime = _dailyLastTradedDayTime; } - if (_details.daysCovered != _daysCovered) { - _details.daysCovered = _daysCovered; + if (details.daysCovered != _daysCovered) { + details.daysCovered = _daysCovered; } if (_amount != 0) { - _details.sumOfLastPeriod = _sumOfLastPeriod.add(_amount); - _dailyLastTradedDayTime = _details.dailyLastTradedDayTime; + details.sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + _dailyLastTradedDayTime = details.dailyLastTradedDayTime; // Increasing the total amount of the day by `_amount` bucket[_from][_dailyLastTradedDayTime] = bucket[_from][_dailyLastTradedDayTime].add(_amount); } @@ -768,6 +913,64 @@ contract VolumeRestrictionTM is ITransferManager { ); } + function _addIndividualDailyRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _endTime, + uint256 _restrictionType + ) + internal + { + require( + individualDailyRestriction[_holder].endTime < now, + "Not Allowed" + ); + _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); + individualDailyRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _startTime, + 1, + _endTime, + RestrictionType(_restrictionType) + ); + emit AddIndividualDailyRestriction( + _holder, + _allowedTokens, + _startTime, + 1, + _endTime, + _restrictionType + ); + } + + function _modifyIndividualDailyRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _endTime, + uint256 _restrictionType + ) + internal + { + require(individualDailyRestriction[_holder].startTime > now, "Not allowed"); + _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); + individualDailyRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _startTime, + 1, + _endTime, + RestrictionType(_restrictionType) + ); + emit ModifyDefaultDailyRestriction( + _allowedTokens, + _startTime, + 1, + _endTime, + _restrictionType + ); + } + function _checkInputParams( uint256 _allowedTokens, uint256 _startTime, From e9a46364bca02e5940c84cdd9f4bf83d5c137a2f Mon Sep 17 00:00:00 2001 From: satyam Date: Fri, 7 Dec 2018 10:34:30 +0530 Subject: [PATCH 30/56] VRTM smart contract completion --- .../TransferManager/VolumeRestrictionTM.sol | 367 +++++++++--------- .../VolumeRestrictionTMFactory.sol | 16 +- .../VolumeRestrictionTMStorage.sol | 45 +++ contracts/proxy/VolumeRestrictionTMProxy.sol | 30 ++ migrations/2_deploy_contracts.js | 8 +- test/helpers/createInstances.js | 6 +- test/y_volume_restriction_tm.js | 22 +- 7 files changed, 301 insertions(+), 193 deletions(-) create mode 100644 contracts/modules/TransferManager/VolumeRestrictionTMStorage.sol create mode 100644 contracts/proxy/VolumeRestrictionTMProxy.sol diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index d80ab217e..3b481d00b 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -1,53 +1,17 @@ pragma solidity ^0.4.24; import "./ITransferManager.sol"; +import "./VolumeRestrictionTMStorage.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; import "../../libraries/BokkyPooBahsDateTimeLibrary.sol"; -contract VolumeRestrictionTM is ITransferManager { +contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { using SafeMath for uint256; // permission definition bytes32 public constant ADMIN = "ADMIN"; - enum RestrictionType { Fixed, Percentage } - - struct VolumeRestriction { - // If typeOfRestriction is `Percentage` then allowedTokens will be in - // the % (w.r.t to totalSupply) with a multiplier of 10**16 . else it - // will be fixed amount of tokens - uint256 allowedTokens; - uint256 startTime; - uint256 rollingPeriodInDays; - uint256 endTime; - RestrictionType typeOfRestriction; - } - - struct BucketDetails { - uint256 lastTradedDayTime; - uint256 sumOfLastPeriod; // It is the sum of transacted amount within the last rollingPeriodDays - uint256 daysCovered; // No of days covered till (from the startTime of VolumeRestriction) - uint256 dailyLastTradedDayTime; - } - - // Global restriction that applies to all token holders - VolumeRestriction public defaultRestriction; - // Daily global restriction that applies to all token holders (Total ST traded daily is restricted) - VolumeRestriction public defaultDailyRestriction; - // Restriction stored corresponds to a particular token holder - mapping(address => VolumeRestriction) public individualRestriction; - // Daily restriction stored corresponds to a particular token holder - mapping(address => VolumeRestriction) public individualDailyRestriction; - // Storing _from => day's timestamp => total amount transact in a day --individual - mapping(address => mapping(uint256 => uint256)) internal bucket; - // Storing the information that used to validate the transaction - mapping(address => BucketDetails) internal bucketToUser; - // Storing the information related to default restriction - mapping(address => BucketDetails) internal defaultBucketToUser; - // List of wallets that are exempted from all the restrictions applied by the this contract - mapping(address => bool) public exemptList; - // Emit when the token holder is added/removed from the exemption list event ChangedExemptWalletList(address indexed _wallet, bool _change); // Emit when the new individual restriction is added corresponds to new token holders @@ -111,6 +75,8 @@ contract VolumeRestrictionTM is ITransferManager { ); // Emit when the individual restriction gets removed event IndividualRestrictionRemoved(address _user); + // Emit when individual daily restriction removed + event IndividualDailyRestrictionRemoved(address _user); // Emit when the default restriction gets removed event DefaultRestrictionRemoved(); // Emit when the daily default restriction gets removed @@ -196,6 +162,44 @@ contract VolumeRestrictionTM is ITransferManager { ); } + /// @notice Internal function to facilitate the addition of individual restriction + function _addIndividualRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _restrictionType + ) + internal + { + require( + individualRestriction[_holder].endTime < now, + "Already present" + ); + require(_holder != address(0) && !exemptList[_holder], "Invalid address"); + _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + + if (individualRestriction[_holder].endTime != 0) { + _removeIndividualRestriction(_holder); + } + individualRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + emit AddNewIndividualRestriction( + _holder, + _allowedTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + /** * @notice Use to add the new individual daily restriction for all token holder * @param _holder Address of the token holder, whom restriction will be implied @@ -223,6 +227,38 @@ contract VolumeRestrictionTM is ITransferManager { ); } + /// @notice Internal function to facilitate the addition of individual daily restriction + function _addIndividualDailyRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _endTime, + uint256 _restrictionType + ) + internal + { + require( + individualDailyRestriction[_holder].endTime < now, + "Not Allowed" + ); + _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); + individualDailyRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _startTime, + 1, + _endTime, + RestrictionType(_restrictionType) + ); + emit AddIndividualDailyRestriction( + _holder, + _allowedTokens, + _startTime, + 1, + _endTime, + _restrictionType + ); + } + /** * @notice Use to add the new individual daily restriction for multiple token holders * @param _holders Array of address of the token holders, whom restriction will be implied @@ -377,6 +413,17 @@ contract VolumeRestrictionTM is ITransferManager { _removeIndividualRestriction(_user); } + /// @notice Internal function to facilitate the removal of individual restriction + function _removeIndividualRestriction(address _user) internal { + require(_user != address(0), "Invalid address"); + require(individualRestriction[_user].endTime != 0, "Not present"); + individualRestriction[_user] = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); + bucketToUser[_user].lastTradedDayTime = 0; + bucketToUser[_user].sumOfLastPeriod = 0; + bucketToUser[_user].daysCovered = 0; + emit IndividualRestrictionRemoved(_user); + } + /** * @notice use to remove the individual restriction for a given address * @param _users Array of address of the user @@ -387,6 +434,33 @@ contract VolumeRestrictionTM is ITransferManager { } } + /** + * @notice use to remove the individual daily restriction for a given address + * @param _user Address of the user + */ + function removeIndividualDailyRestriction(address _user) external withPerm(ADMIN) { + _removeIndividualDailyRestriction(_user); + } + + /// @notice Internal function to facilitate the removal of individual daily restriction + function _removeIndividualDailyRestriction(address _user) internal { + require(_user != address(0), "Invalid address"); + require(individualDailyRestriction[_user].endTime != 0, "Not present"); + individualDailyRestriction[_user] = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); + bucketToUser[_user].dailyLastTradedDayTime = 0; + emit IndividualDailyRestrictionRemoved(_user); + } + + /** + * @notice use to remove the individual daily restriction for a given address + * @param _users Array of address of the user + */ + function removeIndividualDailyRestrictionMulti(address[] _users) external withPerm(ADMIN) { + for (uint256 i = 0; i < _users.length; i++) { + _removeIndividualDailyRestriction(_users[i]); + } + } + /** * @notice Use to remove the default restriction */ @@ -435,6 +509,37 @@ contract VolumeRestrictionTM is ITransferManager { ); } + /// @notice Internal function to facilitate the modification of individual restriction + function _modifyIndividualRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _restrictionType + ) + internal + { + _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + require(individualRestriction[_holder].startTime > now, "Not allowed"); + + individualRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + RestrictionType(_restrictionType) + ); + emit ModifyIndividualRestriction( + _holder, + _allowedTokens, + _startTime, + _rollingPeriodInDays, + _endTime, + _restrictionType + ); + } + /** * @notice Use to modify the existing individual daily restriction for a given token holder * @param _holder Address of the token holder, whom restriction will be implied @@ -462,6 +567,34 @@ contract VolumeRestrictionTM is ITransferManager { ); } + /// @notice Internal function to facilitate the modification of individual daily restriction + function _modifyIndividualDailyRestriction( + address _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _endTime, + uint256 _restrictionType + ) + internal + { + require(individualDailyRestriction[_holder].startTime > now, "Not allowed"); + _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); + individualDailyRestriction[_holder] = VolumeRestriction( + _allowedTokens, + _startTime, + 1, + _endTime, + RestrictionType(_restrictionType) + ); + emit ModifyDefaultDailyRestriction( + _allowedTokens, + _startTime, + 1, + _endTime, + _restrictionType + ); + } + /** * @notice Use to modify the existing individual daily restriction for multiple token holders * @param _holders Array of address of the token holders, whom restriction will be implied @@ -836,141 +969,6 @@ contract VolumeRestrictionTM is ITransferManager { } } - function _removeIndividualRestriction(address _user) internal { - require(_user != address(0), "Invalid address"); - require(individualRestriction[_user].endTime != 0, "Not present"); - individualRestriction[_user] = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); - bucketToUser[_user].lastTradedDayTime = 0; - bucketToUser[_user].sumOfLastPeriod = 0; - bucketToUser[_user].daysCovered = 0; - emit IndividualRestrictionRemoved(_user); - } - - function _modifyIndividualRestriction( - address _holder, - uint256 _allowedTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _restrictionType - ) - internal - { - _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); - require(individualRestriction[_holder].startTime > now, "Not allowed"); - - individualRestriction[_holder] = VolumeRestriction( - _allowedTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - RestrictionType(_restrictionType) - ); - emit ModifyIndividualRestriction( - _holder, - _allowedTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - _restrictionType - ); - } - - function _addIndividualRestriction( - address _holder, - uint256 _allowedTokens, - uint256 _startTime, - uint256 _rollingPeriodInDays, - uint256 _endTime, - uint256 _restrictionType - ) - internal - { - require( - individualRestriction[_holder].endTime < now, - "Already present" - ); - require(_holder != address(0) && !exemptList[_holder], "Invalid address"); - _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); - - if (individualRestriction[_holder].endTime != 0) { - _removeIndividualRestriction(_holder); - } - individualRestriction[_holder] = VolumeRestriction( - _allowedTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - RestrictionType(_restrictionType) - ); - emit AddNewIndividualRestriction( - _holder, - _allowedTokens, - _startTime, - _rollingPeriodInDays, - _endTime, - _restrictionType - ); - } - - function _addIndividualDailyRestriction( - address _holder, - uint256 _allowedTokens, - uint256 _startTime, - uint256 _endTime, - uint256 _restrictionType - ) - internal - { - require( - individualDailyRestriction[_holder].endTime < now, - "Not Allowed" - ); - _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); - individualDailyRestriction[_holder] = VolumeRestriction( - _allowedTokens, - _startTime, - 1, - _endTime, - RestrictionType(_restrictionType) - ); - emit AddIndividualDailyRestriction( - _holder, - _allowedTokens, - _startTime, - 1, - _endTime, - _restrictionType - ); - } - - function _modifyIndividualDailyRestriction( - address _holder, - uint256 _allowedTokens, - uint256 _startTime, - uint256 _endTime, - uint256 _restrictionType - ) - internal - { - require(individualDailyRestriction[_holder].startTime > now, "Not allowed"); - _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); - individualDailyRestriction[_holder] = VolumeRestriction( - _allowedTokens, - _startTime, - 1, - _endTime, - RestrictionType(_restrictionType) - ); - emit ModifyDefaultDailyRestriction( - _allowedTokens, - _startTime, - 1, - _endTime, - _restrictionType - ); - } - function _checkInputParams( uint256 _allowedTokens, uint256 _startTime, @@ -1021,12 +1019,31 @@ contract VolumeRestrictionTM is ITransferManager { * @return uint256 lastTradedDayTime * @return uint256 sumOfLastPeriod * @return uint256 days covered + * @return uint256 24h lastTradedDayTime */ - function getBucketDetailsToUser(address _user) external view returns(uint256, uint256, uint256) { + function getIndividualBucketDetailsToUser(address _user) external view returns(uint256, uint256, uint256, uint256) { return( bucketToUser[_user].lastTradedDayTime, bucketToUser[_user].sumOfLastPeriod, - bucketToUser[_user].daysCovered + bucketToUser[_user].daysCovered, + bucketToUser[_user].dailyLastTradedDayTime + ); + } + + /** + * @notice Use to get the bucket details for a given address + * @param _user Address of the token holder for whom the bucket details has queried + * @return uint256 lastTradedDayTime + * @return uint256 sumOfLastPeriod + * @return uint256 days covered + * @return uint256 24h lastTradedDayTime + */ + function getDefaultBucketDetailsToUser(address _user) external view returns(uint256, uint256, uint256, uint256) { + return( + defaultBucketToUser[_user].lastTradedDayTime, + defaultBucketToUser[_user].sumOfLastPeriod, + defaultBucketToUser[_user].daysCovered, + defaultBucketToUser[_user].dailyLastTradedDayTime ); } diff --git a/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol b/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol index 9ac511183..7d2e27cc0 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTMFactory.sol @@ -1,25 +1,30 @@ pragma solidity ^0.4.24; -import "./VolumeRestrictionTM.sol"; +import "../../proxy/VolumeRestrictionTMProxy.sol"; import "../ModuleFactory.sol"; /** * @title Factory for deploying VolumeRestrictionTM module */ contract VolumeRestrictionTMFactory is ModuleFactory { + + address public logicContract; + /** * @notice Constructor * @param _polyAddress Address of the polytoken */ - constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public + constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost, address _logicContract) public ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) - { + { + require(_logicContract != address(0), "Invalid address"); version = "1.0.0"; name = "VolumeRestrictionTM"; title = "Volume Restriction Transfer Manager"; description = "Manage transfers based on the volume of tokens that needs to be transact"; compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + logicContract = _logicContract; } @@ -30,7 +35,7 @@ contract VolumeRestrictionTMFactory is ModuleFactory { function deploy(bytes /* _data */) external returns(address) { if (setupCost > 0) require(polyToken.transferFrom(msg.sender, owner, setupCost), "Insufficent Allowance"); - address volumeRestrictionTransferManager = new VolumeRestrictionTM(msg.sender, address(polyToken)); + address volumeRestrictionTransferManager = new VolumeRestrictionTMProxy(msg.sender, address(polyToken), logicContract); /*solium-disable-next-line security/no-block-members*/ emit GenerateModuleFromFactory(volumeRestrictionTransferManager, getName(), address(this), msg.sender, setupCost, now); return volumeRestrictionTransferManager; @@ -58,11 +63,12 @@ contract VolumeRestrictionTMFactory is ModuleFactory { * @notice Get the tags related to the module factory */ function getTags() public view returns(bytes32[]) { - bytes32[] memory availableTags = new bytes32[](4); + bytes32[] memory availableTags = new bytes32[](5); availableTags[0] = "Maximum Volume"; availableTags[1] = "Transfer Restriction"; availableTags[2] = "Daily Restriction"; availableTags[3] = "Individual Restriction"; + availableTags[4] = "Default Restriction"; return availableTags; } diff --git a/contracts/modules/TransferManager/VolumeRestrictionTMStorage.sol b/contracts/modules/TransferManager/VolumeRestrictionTMStorage.sol new file mode 100644 index 000000000..9e614213a --- /dev/null +++ b/contracts/modules/TransferManager/VolumeRestrictionTMStorage.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.4.24; + +/** + * @title Storage layout for VolumeRestrictionTM + */ +contract VolumeRestrictionTMStorage { + + enum RestrictionType { Fixed, Percentage } + + struct VolumeRestriction { + // If typeOfRestriction is `Percentage` then allowedTokens will be in + // the % (w.r.t to totalSupply) with a multiplier of 10**16 . else it + // will be fixed amount of tokens + uint256 allowedTokens; + uint256 startTime; + uint256 rollingPeriodInDays; + uint256 endTime; + RestrictionType typeOfRestriction; + } + + struct BucketDetails { + uint256 lastTradedDayTime; + uint256 sumOfLastPeriod; // It is the sum of transacted amount within the last rollingPeriodDays + uint256 daysCovered; // No of days covered till (from the startTime of VolumeRestriction) + uint256 dailyLastTradedDayTime; + } + + // Global restriction that applies to all token holders + VolumeRestriction public defaultRestriction; + // Daily global restriction that applies to all token holders (Total ST traded daily is restricted) + VolumeRestriction public defaultDailyRestriction; + // Restriction stored corresponds to a particular token holder + mapping(address => VolumeRestriction) public individualRestriction; + // Daily restriction stored corresponds to a particular token holder + mapping(address => VolumeRestriction) public individualDailyRestriction; + // Storing _from => day's timestamp => total amount transact in a day --individual + mapping(address => mapping(uint256 => uint256)) internal bucket; + // Storing the information that used to validate the transaction + mapping(address => BucketDetails) internal bucketToUser; + // Storing the information related to default restriction + mapping(address => BucketDetails) internal defaultBucketToUser; + // List of wallets that are exempted from all the restrictions applied by the this contract + mapping(address => bool) public exemptList; + +} \ No newline at end of file diff --git a/contracts/proxy/VolumeRestrictionTMProxy.sol b/contracts/proxy/VolumeRestrictionTMProxy.sol new file mode 100644 index 000000000..e8c24e6be --- /dev/null +++ b/contracts/proxy/VolumeRestrictionTMProxy.sol @@ -0,0 +1,30 @@ +pragma solidity ^0.4.24; + +import "../modules/TransferManager/VolumeRestrictionTMStorage.sol"; +import "./OwnedProxy.sol"; +import "../Pausable.sol"; +import "../modules/ModuleStorage.sol"; + +/** + * @title Transfer Manager module for core transfer validation functionality + */ +contract VolumeRestrictionTMProxy is VolumeRestrictionTMStorage, ModuleStorage, Pausable, OwnedProxy { + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + * @param _implementation representing the address of the new implementation to be set + */ + constructor (address _securityToken, address _polyAddress, address _implementation) + public + ModuleStorage(_securityToken, _polyAddress) + { + require( + _implementation != address(0), + "Implementation address should not be 0x" + ); + __implementation = _implementation; + } + +} diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 2a1a02905..61b13f38e 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -17,6 +17,7 @@ const USDTieredSTOFactory = artifacts.require('./USDTieredSTOFactory.sol') const SecurityTokenRegistry = artifacts.require('./SecurityTokenRegistry.sol') const SecurityTokenRegistryProxy = artifacts.require('./SecurityTokenRegistryProxy.sol') const VolumeRestrictionTMFactory = artifacts.require('./VolumeRestrictionTMFactory.sol') +const VolumeRestrictionTMLogic = artifacts.require('./VolumeRestrictionTM.sol'); const FeatureRegistry = artifacts.require('./FeatureRegistry.sol') const STFactory = artifacts.require('./tokens/STFactory.sol') const DevPolyToken = artifacts.require('./helpers/PolyTokenFaucet.sol') @@ -161,6 +162,10 @@ module.exports = function (deployer, network, accounts) { // B) Deploy the GeneralTransferManagerLogic Contract (Factory used to generate the GeneralTransferManager contract and this // manager attach with the securityToken contract at the time of deployment) return deployer.deploy(EtherDividendCheckpointLogic, "0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", {from: PolymathAccount}); + }).then(() => { + // B) Deploy the VolumeRestrictionTMLogic Contract (Factory used to generate the VolumeRestrictionTM contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(VolumeRestrictionTMLogic, "0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", {from: PolymathAccount}); }).then(() => { // B) Deploy the GeneralTransferManagerFactory Contract (Factory used to generate the GeneralTransferManager contract and this // manager attach with the securityToken contract at the time of deployment) @@ -188,7 +193,7 @@ module.exports = function (deployer, network, accounts) { }).then(() => { // D) Deploy the VolumeRestrictionTMFactory Contract (Factory used to generate the VolumeRestrictionTM contract use // to provide the functionality of restricting the token volume) - return deployer.deploy(VolumeRestrictionTMFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); + return deployer.deploy(VolumeRestrictionTMFactory, PolyToken, 0, 0, 0, VolumeRestrictionTMLogic.address, {from: PolymathAccount}); }).then(() => { // D) Deploy the ManualApprovalTransferManagerFactory Contract (Factory used to generate the ManualApprovalTransferManager contract use // to manual approve the transfer that will overcome the other transfer restrictions) @@ -348,6 +353,7 @@ module.exports = function (deployer, network, accounts) { EtherDividendCheckpointFactory: ${EtherDividendCheckpointFactory.address} ERC20DividendCheckpointFactory: ${ERC20DividendCheckpointFactory.address} VolumeRestrictionTMFactory: ${VolumeRestrictionTMFactory.address} + VolumeRestrictionTMLogic: ${VolumeRestrictionTMLogic.address} --------------------------------------------------------------------------------- `); console.log('\n'); diff --git a/test/helpers/createInstances.js b/test/helpers/createInstances.js index d76ac82c9..9b138ef15 100644 --- a/test/helpers/createInstances.js +++ b/test/helpers/createInstances.js @@ -33,6 +33,7 @@ const DummySTOFactory = artifacts.require("./DummySTOFactory.sol"); const MockBurnFactory = artifacts.require("./MockBurnFactory.sol"); const MockWrongTypeFactory = artifacts.require("./MockWrongTypeFactory.sol"); const VolumeRestrictionTMFactory = artifacts.require("./VolumeRestrictionTMFactory.sol"); +const VolumeRestrictionTM = artifacts.require("./VolumeRestrictionTM.sol"); const Web3 = require("web3"); const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port @@ -57,6 +58,7 @@ let I_GeneralPermissionManagerFactory; let I_GeneralTransferManagerLogic; let I_GeneralTransferManagerFactory; let I_GeneralTransferManager; +let I_VolumeRestrictionTMLogic; let I_ModuleRegistryProxy; let I_PreSaleSTOFactory; let I_ModuleRegistry; @@ -227,7 +229,9 @@ export async function deployGTMAndVerifyed(accountPolymath, MRProxyInstance, pol } export async function deployVRTMAndVerifyed(accountPolymath, MRProxyInstance, polyToken, setupCost) { - I_VolumeRestrictionTMFactory = await VolumeRestrictionTMFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); + I_VolumeRestrictionTMLogic = await VolumeRestrictionTM.new("0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", { from: accountPolymath }); + + I_VolumeRestrictionTMFactory = await VolumeRestrictionTMFactory.new(polyToken, setupCost, 0, 0, I_VolumeRestrictionTMLogic.address, { from: accountPolymath }); assert.notEqual( I_VolumeRestrictionTMFactory.address.valueOf(), diff --git a/test/y_volume_restriction_tm.js b/test/y_volume_restriction_tm.js index 85ded68d6..87020a170 100644 --- a/test/y_volume_restriction_tm.js +++ b/test/y_volume_restriction_tm.js @@ -613,7 +613,7 @@ contract('VolumeRestrictionTransferManager', accounts => { ); }); - it("Should succesfully transact the tokens just after the startTime", async() => { + it.skip("Should succesfully transact the tokens just after the startTime", async() => { // Check the transfer will be valid or not by calling the verifyTransfer() directly by using _isTransfer = false let result = await I_VolumeRestrictionTM.verifyTransfer.call(account_investor1, account_investor3, web3.utils.toWei('.3'), "0x0", false); assert.equal(result.toNumber(), 1); @@ -644,7 +644,7 @@ contract('VolumeRestrictionTransferManager', accounts => { tempArrayGlobal.push(0); }) - it("Should successfully add the global restriction", async() => { + it.skip("Should successfully add the global restriction", async() => { await I_VolumeRestrictionTM.addGlobalRestriction( web3.utils.toWei("15"), latestTime() + duration.seconds(2), @@ -661,7 +661,7 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.equal(data[2].toNumber(), 5); }); - it("Should successfully transact the tokens after 1 and half days", async() => { + it.skip("Should successfully transact the tokens after 1 and half days", async() => { await increaseTime(duration.days(1.5)); let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber(); let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); @@ -698,7 +698,7 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.equal(globalAmt, 2); }); - it("Should add the daily restriction successfully", async() => { + it.skip("Should add the daily restriction successfully", async() => { await I_VolumeRestrictionTM.addDailyGlobalRestriction( web3.utils.toWei("5"), latestTime() + duration.seconds(2), @@ -714,7 +714,7 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.equal(data[4].toNumber(), 0); }); - it("Should transfer tokens within the daily limit -- falied because of limit failing", async() =>{ + it.skip("Should transfer tokens within the daily limit -- falied because of limit failing", async() =>{ // Transfer the 3.1 tokens to check the daily limit await increaseTime(5); await catchRevert( @@ -722,7 +722,7 @@ contract('VolumeRestrictionTransferManager', accounts => { ); }); - it("Should transfer the tokens within the daily limit", async() => { + it.skip("Should transfer the tokens within the daily limit", async() => { let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber(); let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); let globalStartTime = (await I_VolumeRestrictionTM.globalRestriction.call())[1].toNumber(); @@ -755,14 +755,14 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.equal(globalAmt, 5); }); - it("Should transfer tokens within the daily limit -- falied because of limit failing", async() =>{ + it.skip("Should transfer tokens within the daily limit -- falied because of limit failing", async() =>{ // Transfer the 0.1 tokens to check the daily limit await catchRevert( I_SecurityToken.transfer(account_investor3, web3.utils.toWei("0.1"), {from: account_investor1}) ); }); - it("Should successfully transact tokens on the next day of the rolling period (Fuzz test)", async() => { + it.skip("Should successfully transact tokens on the next day of the rolling period (Fuzz test)", async() => { // Increase time half day await increaseTime(duration.days(.52)); // Check the balance of the investors @@ -810,7 +810,7 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.closeTo(globalAmt, totalAmountTransacted, 0.01); }); - it("Should fail to transact the tokens more than the allowed tokens in a rolling period", async() => { + it.skip("Should fail to transact the tokens more than the allowed tokens in a rolling period", async() => { // Increase the time by 3/5 of the day await increaseTime(duration.days(.6)); let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); @@ -821,14 +821,14 @@ contract('VolumeRestrictionTransferManager', accounts => { ); }); - it("Should fail to buy tokens in the new rolling period --failed because amount is more than last 1 timestamps", async() => { + it.skip("Should fail to buy tokens in the new rolling period --failed because amount is more than last 1 timestamps", async() => { await increaseTime(duration.days(1)); await catchRevert( I_SecurityToken.transfer(account_investor3, web3.utils.toWei("10"), {from: account_investor1}) ); }); - it("Should add the daily restriction again successfully", async() => { + it.skip("Should add the daily restriction again successfully", async() => { await I_VolumeRestrictionTM.addDailyGlobalRestriction( web3.utils.toWei("7"), latestTime() + duration.seconds(2), From aa6e8a1c552486262e58442174cd0d022a48da7f Mon Sep 17 00:00:00 2001 From: satyam Date: Fri, 7 Dec 2018 15:45:35 +0530 Subject: [PATCH 31/56] reinvent test cases --- .../TransferManager/VolumeRestrictionTM.sol | 20 +- test/y_volume_restriction_tm.js | 209 +++++++++++++++++- 2 files changed, 211 insertions(+), 18 deletions(-) diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index 3b481d00b..2f8d1868d 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -107,13 +107,13 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { // Function must only be called by the associated security token if _isTransfer == true require(msg.sender == securityToken || !_isTransfer); // Checking the individual restriction if the `_from` comes in the individual category - if (individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now - || individualDailyRestriction[_from].endTime >= now && individualDailyRestriction[_from].startTime <= now) { + if ((individualRestriction[_from].endTime >= now && individualRestriction[_from].startTime <= now) + || (individualDailyRestriction[_from].endTime >= now && individualDailyRestriction[_from].startTime <= now)) { return _individualRestrictionCheck(_from, _amount, _isTransfer); // If the `_from` doesn't fall under the individual category. It will processed with in the global category automatically - } else if (defaultRestriction.endTime >= now && defaultRestriction.startTime <= now - || defaultDailyRestriction.endTime >= now && defaultDailyRestriction.startTime <= now) { + } else if ((defaultRestriction.endTime >= now && defaultRestriction.startTime <= now) + || (defaultDailyRestriction.endTime >= now && defaultDailyRestriction.startTime <= now)) { return _defaultRestrictionCheck(_from, _amount, _isTransfer); } @@ -769,7 +769,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { ); // validation of the transaction amount if (!_checkValidAmountToTransact(sumOfLastPeriod, _amount, defaultRestriction)) { - allowedDefault == false; + allowedDefault = false; } } (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, fromTimestamp, bucketDetails.dailyLastTradedDayTime, defaultDailyRestriction); @@ -823,7 +823,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { ); // validation of the transaction amount if (!_checkValidAmountToTransact(sumOfLastPeriod, _amount, restriction)) { - allowedIndividual == false; + allowedIndividual = false; } } (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, fromTimestamp, bucketDetails.dailyLastTradedDayTime, dailyRestriction); @@ -840,7 +840,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { ); } - return ((allowedDaily && allowedIndividual) == true ? Result.NA : Result.INVALID); + return ((allowedDaily && allowedIndividual) ? Result.NA : Result.INVALID); } function _dailyTxCheck( @@ -860,6 +860,8 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { uint256 txSumOfDay = 0; if (now.sub(dailyLastTradedDayTime) < 1 days || dailyLastTradedDayTime == 0) { txSumOfDay = bucket[from][dailyLastTradedDayTime] >= bucket[from][fromTimestamp] ? bucket[from][dailyLastTradedDayTime] : bucket[from][fromTimestamp]; + if (dailyLastTradedDayTime == 0) + dailyLastTradedDayTime = now; } else { txSumOfDay = 0; dailyLastTradedDayTime = now; @@ -962,7 +964,9 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { details.daysCovered = _daysCovered; } if (_amount != 0) { - details.sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + if (_lastTradedDayTime !=0) { + details.sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + } _dailyLastTradedDayTime = details.dailyLastTradedDayTime; // Increasing the total amount of the day by `_amount` bucket[_from][_dailyLastTradedDayTime] = bucket[_from][_dailyLastTradedDayTime].add(_amount); diff --git a/test/y_volume_restriction_tm.js b/test/y_volume_restriction_tm.js index 87020a170..f6fd60de8 100644 --- a/test/y_volume_restriction_tm.js +++ b/test/y_volume_restriction_tm.js @@ -81,13 +81,11 @@ contract('VolumeRestrictionTransferManager', accounts => { Latest timestamp: ${data[0].toNumber()} SumOfLastPeriod: ${data[1].dividedBy(new BigNumber(10).pow(18)).toNumber()} Days Covered: ${data[2].toNumber()} - Global days covered: ${data[3].toNumber()} - Global Sum of LastPeriod: ${data[4].dividedBy(new BigNumber(10).pow(18)).toNumber()} - Global Latest timestamp: ${data[5].toNumber()} + Latest timestamp daily: ${data[3].toNumber()} Individual Total Trade on latestTimestamp : ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account, data[0])) .dividedBy(new BigNumber(10).pow(18)).toNumber()} - Global Total Trade on latestTimestamp: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account, data[5])) - .dividedBy(new BigNumber(10).pow(18)).toNumber()} + Individual Total Trade on daily latestTimestamp : ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account, data[3])) + .dividedBy(new BigNumber(10).pow(18)).toNumber()} `) } @@ -215,6 +213,7 @@ contract('VolumeRestrictionTransferManager', accounts => { // Mint some tokens and transferred to whitelisted addresses await I_SecurityToken.mint(account_investor1, web3.utils.toWei("40", "ether"), {from: token_owner}); await I_SecurityToken.mint(account_investor2, web3.utils.toWei("30", "ether"), {from: token_owner}); + await I_SecurityToken.mint(account_investor3, web3.utils.toWei("30", "ether"), {from: token_owner}); // Check the balance of the investors let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); @@ -229,7 +228,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('5', 'ether'), { from: account_investor1 }); let bal1 = await I_SecurityToken.balanceOf.call(account_investor3); // Verifying the balances - assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 5); + assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 35); }); }) @@ -613,7 +612,7 @@ contract('VolumeRestrictionTransferManager', accounts => { ); }); - it.skip("Should succesfully transact the tokens just after the startTime", async() => { + it("Should succesfully transact the tokens just after the startTime", async() => { // Check the transfer will be valid or not by calling the verifyTransfer() directly by using _isTransfer = false let result = await I_VolumeRestrictionTM.verifyTransfer.call(account_investor1, account_investor3, web3.utils.toWei('.3'), "0x0", false); assert.equal(result.toNumber(), 1); @@ -627,7 +626,7 @@ contract('VolumeRestrictionTransferManager', accounts => { // Verifying the balances assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 34.7); - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor1); await print(data, account_investor1); assert.equal( @@ -641,9 +640,199 @@ contract('VolumeRestrictionTransferManager', accounts => { ); assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 0.3); tempArray.push(0.3); - tempArrayGlobal.push(0); + }); + + it("Should fail to add the individual daily restriction -- Bad msg.sender", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor3, + web3.utils.toWei("6"), + latestTime() + duration.seconds(1), + latestTime() + duration.days(4), + 0, + { + from: account_investor1 + } + ) + ); + }) + + it("Should fail to add the individual daily restriction -- Bad params value", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor3, + web3.utils.toWei("6"), + latestTime() + duration.seconds(1), + latestTime() + duration.days(4), + 1, + { + from: token_owner + } + ) + ); }) + it("Should fail to add the individual daily restriction -- Bad params value", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor3, + 0, + latestTime() + duration.seconds(1), + latestTime() + duration.days(4), + 0, + { + from: token_owner + } + ) + ); + }) + + it("Should fail to add the individual daily restriction -- Bad params value", async() => { + await catchRevert( + I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor3, + web3.utils.toWei("6"), + latestTime() + duration.days(5), + latestTime() + duration.days(4), + 0, + { + from: token_owner + } + ) + ); + }) + + it("Should add the individual daily default restriction", async() => { + let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor3, + web3.utils.toWei("6"), + latestTime() + duration.seconds(1), + latestTime() + duration.days(4), + 0, + { + from: token_owner + } + ); + + assert.equal(tx.logs[0].args._holder, account_investor3); + assert.equal(tx.logs[0].args._typeOfRestriction, 0); + assert.equal((tx.logs[0].args._allowedTokens).toNumber(), web3.utils.toWei("6")); + let dataRestriction = await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor3); + console.log(` + *** Individual Daily restriction data *** + Allowed Tokens: ${dataRestriction[0].dividedBy(new BigNumber(10).pow(18)).toNumber()} + StartTime : ${dataRestriction[1].toNumber()} + Rolling Period in days : ${dataRestriction[2].toNumber()} + EndTime : ${dataRestriction[3].toNumber()} + Type of Restriction: ${dataRestriction[4].toNumber()} + `); + }); + + it("Should transfer the tokens within the individual daily restriction limits", async() => { + // transfer 2 tokens as per the limit + let txTime = latestTime(); + console.log(` + Gas Estimation for the Individual daily tx - ${await I_SecurityToken.transfer.estimateGas(account_investor2, web3.utils.toWei("2"), {from: account_investor3})} + `) + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("2"), {from: account_investor3}); + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + await increaseTime(duration.minutes(15)); + + console.log(` + Gas Estimation for the Individual daily tx - ${await I_SecurityToken.transfer.estimateGas(account_investor2, web3.utils.toWei("4"), {from: account_investor3})} + `) + // transfer the 4 tokens which is under the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("4"), {from: account_investor3}); + let newData = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(newData, account_investor3); + + assert.equal(newData[3].toNumber(), data[3].toNumber()); + assert.closeTo(data[3].toNumber(), txTime, 2); + assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[3])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), 6); + }); + + it("Should fail to transfer more tokens --because of the above limit", async() => { + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei(".1"), {from: account_investor3}) + ); + }); + + it("Should try to send after the one day completion", async() => { + // increase the EVM time by one day + await increaseTime(duration.days(1)); + + let txTime = latestTime(); + console.log(` + Gas Estimation for the Individual daily tx - ${await I_SecurityToken.transfer.estimateGas(account_investor2, web3.utils.toWei("2"), {from: account_investor3})} + `) + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("2"), {from: account_investor3}); + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + assert.closeTo(data[3].toNumber(), txTime, 2); + assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[3])) + .dividedBy(new BigNumber(10).pow(18)).toNumber(), 2); + }); + + it("Should add the daily restriction on the investor 1", async() => { + let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor1, + web3.utils.toWei("5"), + latestTime() + duration.seconds(1), + latestTime() + duration.days(4), + 0, + { + from: token_owner + } + ); + + assert.equal(tx.logs[0].args._holder, account_investor1); + assert.equal(tx.logs[0].args._typeOfRestriction, 0); + assert.equal((tx.logs[0].args._allowedTokens).toNumber(), web3.utils.toWei("5")); + let dataRestriction = await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor1); + console.log(` + *** Individual Daily restriction data *** + Allowed Tokens: ${dataRestriction[0].dividedBy(new BigNumber(10).pow(18)).toNumber()} + StartTime : ${dataRestriction[1].toNumber()} + Rolling Period in days : ${dataRestriction[2].toNumber()} + EndTime : ${dataRestriction[3].toNumber()} + Type of Restriction: ${dataRestriction[4].toNumber()} + `); + }); + + it("Should transfer tokens on the 2 day by investor1 (Individual + Individual daily)", async() => { + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber(); + let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); + + console.log(` + Gas estimation (Individual + Individual daily): ${await I_SecurityToken.transfer.estimateGas(account_investor2, web3.utils.toWei("2"), {from: account_investor1})}` + ); + + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("2"), {from: account_investor1}); + // Check the balance of the investors + let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); + // Verifying the balances + assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 32.7); + tempArray.push(2); + + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor1); + await print(data, account_investor1); + + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray)); + assert.equal(data[2].toNumber(), 1); + assert.equal(data[3].toNumber(), data[0].toNumber()); + assert.equal(amt, 2); + }); + it.skip("Should successfully add the global restriction", async() => { await I_VolumeRestrictionTM.addGlobalRestriction( web3.utils.toWei("15"), @@ -1581,7 +1770,7 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should get the tags of the factory", async() => { let tags = await I_VolumeRestrictionTMFactory.getTags.call(); - assert.equal(tags.length, 4); + assert.equal(tags.length, 5); assert.equal(web3.utils.toAscii(tags[0]).replace(/\u0000/g, ''), "Maximum Volume"); }); }); From 40eac85d36891e71ec512bbe7d6349d7eb4d7017 Mon Sep 17 00:00:00 2001 From: satyam Date: Sat, 8 Dec 2018 22:56:35 +0530 Subject: [PATCH 32/56] contract fully tested with different scenarios of selling of tokens --- .../TransferManager/VolumeRestrictionTM.sol | 213 ++-- test/y_volume_restriction_tm.js | 1064 ++++++----------- 2 files changed, 479 insertions(+), 798 deletions(-) diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index 2f8d1868d..04240ea35 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -74,9 +74,9 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { uint256 _typeOfRestriction ); // Emit when the individual restriction gets removed - event IndividualRestrictionRemoved(address _user); + event IndividualRestrictionRemoved(address _holder); // Emit when individual daily restriction removed - event IndividualDailyRestrictionRemoved(address _user); + event IndividualDailyRestrictionRemoved(address _holder); // Emit when the default restriction gets removed event DefaultRestrictionRemoved(); // Emit when the daily default restriction gets removed @@ -173,19 +173,25 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { ) internal { + + uint256 startTime = _startTime; + if (_startTime == 0) { + startTime = now; + } require( individualRestriction[_holder].endTime < now, "Already present" ); require(_holder != address(0) && !exemptList[_holder], "Invalid address"); - _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + require(startTime >= now, "Invalid startTime"); + _checkInputParams(_allowedTokens, startTime, _rollingPeriodInDays, _endTime, _restrictionType); if (individualRestriction[_holder].endTime != 0) { _removeIndividualRestriction(_holder); } individualRestriction[_holder] = VolumeRestriction( _allowedTokens, - _startTime, + startTime, _rollingPeriodInDays, _endTime, RestrictionType(_restrictionType) @@ -193,7 +199,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { emit AddNewIndividualRestriction( _holder, _allowedTokens, - _startTime, + startTime, _rollingPeriodInDays, _endTime, _restrictionType @@ -237,14 +243,20 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { ) internal { + + uint256 startTime = _startTime; + if (_startTime == 0) { + startTime = now; + } require( individualDailyRestriction[_holder].endTime < now, "Not Allowed" ); - _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); + require(startTime >= now, "Invalid startTime"); + _checkInputParams(_allowedTokens, startTime, 1, _endTime, _restrictionType); individualDailyRestriction[_holder] = VolumeRestriction( _allowedTokens, - _startTime, + startTime, 1, _endTime, RestrictionType(_restrictionType) @@ -252,7 +264,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { emit AddIndividualDailyRestriction( _holder, _allowedTokens, - _startTime, + startTime, 1, _endTime, _restrictionType @@ -347,21 +359,26 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { external withPerm(ADMIN) { + uint256 startTime = _startTime; + if (_startTime == 0) { + startTime = now; + } require( defaultRestriction.endTime < now, "Not allowed" ); - _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + require(startTime >= now, "Invalid startTime"); + _checkInputParams(_allowedTokens, startTime, _rollingPeriodInDays, _endTime, _restrictionType); defaultRestriction = VolumeRestriction( _allowedTokens, - _startTime, + startTime, _rollingPeriodInDays, _endTime, RestrictionType(_restrictionType) ); emit AddDefaultRestriction( _allowedTokens, - _startTime, + startTime, _rollingPeriodInDays, _endTime, _restrictionType @@ -384,21 +401,26 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { external withPerm(ADMIN) { + uint256 startTime = _startTime; + if (_startTime == 0) { + startTime = now; + } require( defaultDailyRestriction.endTime < now, "Not Allowed" ); - _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); + require(startTime >= now, "Invalid startTime"); + _checkInputParams(_allowedTokens, startTime, 1, _endTime, _restrictionType); defaultDailyRestriction = VolumeRestriction( _allowedTokens, - _startTime, + startTime, 1, _endTime, RestrictionType(_restrictionType) ); emit AddDefaultDailyRestriction( _allowedTokens, - _startTime, + startTime, 1, _endTime, _restrictionType @@ -407,57 +429,57 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { /** * @notice use to remove the individual restriction for a given address - * @param _user Address of the user + * @param _holder Address of the user */ - function removeIndividualRestriction(address _user) external withPerm(ADMIN) { - _removeIndividualRestriction(_user); + function removeIndividualRestriction(address _holder) external withPerm(ADMIN) { + _removeIndividualRestriction(_holder); } /// @notice Internal function to facilitate the removal of individual restriction - function _removeIndividualRestriction(address _user) internal { - require(_user != address(0), "Invalid address"); - require(individualRestriction[_user].endTime != 0, "Not present"); - individualRestriction[_user] = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); - bucketToUser[_user].lastTradedDayTime = 0; - bucketToUser[_user].sumOfLastPeriod = 0; - bucketToUser[_user].daysCovered = 0; - emit IndividualRestrictionRemoved(_user); + function _removeIndividualRestriction(address _holder) internal { + require(_holder != address(0), "Invalid address"); + require(individualRestriction[_holder].endTime != 0, "Not present"); + individualRestriction[_holder] = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); + bucketToUser[_holder].lastTradedDayTime = 0; + bucketToUser[_holder].sumOfLastPeriod = 0; + bucketToUser[_holder].daysCovered = 0; + emit IndividualRestrictionRemoved(_holder); } /** * @notice use to remove the individual restriction for a given address - * @param _users Array of address of the user + * @param _holders Array of address of the user */ - function removeIndividualRestrictionMulti(address[] _users) external withPerm(ADMIN) { - for (uint256 i = 0; i < _users.length; i++) { - _removeIndividualRestriction(_users[i]); + function removeIndividualRestrictionMulti(address[] _holders) external withPerm(ADMIN) { + for (uint256 i = 0; i < _holders.length; i++) { + _removeIndividualRestriction(_holders[i]); } } /** * @notice use to remove the individual daily restriction for a given address - * @param _user Address of the user + * @param _holder Address of the user */ - function removeIndividualDailyRestriction(address _user) external withPerm(ADMIN) { - _removeIndividualDailyRestriction(_user); + function removeIndividualDailyRestriction(address _holder) external withPerm(ADMIN) { + _removeIndividualDailyRestriction(_holder); } /// @notice Internal function to facilitate the removal of individual daily restriction - function _removeIndividualDailyRestriction(address _user) internal { - require(_user != address(0), "Invalid address"); - require(individualDailyRestriction[_user].endTime != 0, "Not present"); - individualDailyRestriction[_user] = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); - bucketToUser[_user].dailyLastTradedDayTime = 0; - emit IndividualDailyRestrictionRemoved(_user); + function _removeIndividualDailyRestriction(address _holder) internal { + require(_holder != address(0), "Invalid address"); + require(individualDailyRestriction[_holder].endTime != 0, "Not present"); + individualDailyRestriction[_holder] = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); + bucketToUser[_holder].dailyLastTradedDayTime = 0; + emit IndividualDailyRestrictionRemoved(_holder); } /** * @notice use to remove the individual daily restriction for a given address - * @param _users Array of address of the user + * @param _holders Array of address of the user */ - function removeIndividualDailyRestrictionMulti(address[] _users) external withPerm(ADMIN) { - for (uint256 i = 0; i < _users.length; i++) { - _removeIndividualDailyRestriction(_users[i]); + function removeIndividualDailyRestrictionMulti(address[] _holders) external withPerm(ADMIN) { + for (uint256 i = 0; i < _holders.length; i++) { + _removeIndividualDailyRestriction(_holders[i]); } } @@ -520,12 +542,16 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { ) internal { - _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + uint256 startTime = _startTime; + if (_startTime == 0) { + startTime = now; + } require(individualRestriction[_holder].startTime > now, "Not allowed"); - + require(startTime >= now, "Invalid startTime"); + _checkInputParams(_allowedTokens, startTime, _rollingPeriodInDays, _endTime, _restrictionType); individualRestriction[_holder] = VolumeRestriction( _allowedTokens, - _startTime, + startTime, _rollingPeriodInDays, _endTime, RestrictionType(_restrictionType) @@ -533,7 +559,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { emit ModifyIndividualRestriction( _holder, _allowedTokens, - _startTime, + startTime, _rollingPeriodInDays, _endTime, _restrictionType @@ -542,6 +568,9 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { /** * @notice Use to modify the existing individual daily restriction for a given token holder + * @dev It is possible to intialize the dailyLatestTradedTime by 0 for the given holder then + * changing of startTime will affect the 24 hrs span. i.e if in earlier restriction days start with + * morning and end on midnight while after the change day may start with afternoon and end with other day afternoon * @param _holder Address of the token holder, whom restriction will be implied * @param _allowedTokens Amount of tokens allowed to be trade for a given address. * @param _startTime Unix timestamp at which restriction get into effect @@ -576,19 +605,24 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { uint256 _restrictionType ) internal - { - require(individualDailyRestriction[_holder].startTime > now, "Not allowed"); - _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); + { + uint256 startTime = _startTime; + if (_startTime == 0) { + startTime = now; + } + _checkInputParams(_allowedTokens, startTime, 1, _endTime, _restrictionType); + // Initializing the value with 0 to allow the trade with new startTime of the restriction + bucketToUser[_holder].dailyLastTradedDayTime = 0; individualDailyRestriction[_holder] = VolumeRestriction( _allowedTokens, - _startTime, + startTime, 1, _endTime, RestrictionType(_restrictionType) ); emit ModifyDefaultDailyRestriction( _allowedTokens, - _startTime, + startTime, 1, _endTime, _restrictionType @@ -684,18 +718,23 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { external withPerm(ADMIN) { - require(defaultRestriction.startTime > now, "Not allowed"); - _checkInputParams(_allowedTokens, _startTime, _rollingPeriodInDays, _endTime, _restrictionType); + require(defaultRestriction.startTime > now, "Not allowed"); + uint256 startTime = _startTime; + if (_startTime == 0) { + startTime = now; + } + require(startTime >= now, "Invalid startTime"); + _checkInputParams(_allowedTokens, startTime, _rollingPeriodInDays, _endTime, _restrictionType); defaultRestriction = VolumeRestriction( _allowedTokens, - _startTime, + startTime, _rollingPeriodInDays, _endTime, RestrictionType(_restrictionType) ); emit ModifyDefaultRestriction( _allowedTokens, - _startTime, + startTime, _rollingPeriodInDays, _endTime, _restrictionType @@ -704,6 +743,9 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { /** * @notice Use to modify the daily default restriction for all token holder + * @dev If the start time is modified then it only affect the startTime of the restriction + * while it may carries the earlier dailyLatestStartDayTime value. Not possible to initialize the value + * of dailyLatestStartDayTime for every holder to 0. * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. * @param _startTime Unix timestamp at which restriction get into effect * @param _endTime Unix timestamp at which restriction effects will gets end. @@ -718,18 +760,21 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { external withPerm(ADMIN) { - require(defaultDailyRestriction.startTime > now, "Not allowed"); - _checkInputParams(_allowedTokens, _startTime, 1, _endTime, _restrictionType); + uint256 startTime = _startTime; + if (_startTime == 0) { + startTime = now; + } + _checkInputParams(_allowedTokens, startTime, 1, _endTime, _restrictionType); defaultDailyRestriction = VolumeRestriction( _allowedTokens, - _startTime, + startTime, 1, _endTime, RestrictionType(_restrictionType) ); emit ModifyDefaultDailyRestriction( _allowedTokens, - _startTime, + startTime, 1, _endTime, _restrictionType @@ -772,7 +817,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { allowedDefault = false; } } - (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, fromTimestamp, bucketDetails.dailyLastTradedDayTime, defaultDailyRestriction); + (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, bucketDetails.dailyLastTradedDayTime, defaultDailyRestriction); if (_isTransfer) { _updateStorage( @@ -782,6 +827,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { sumOfLastPeriod, daysCovered, dailyTime, + defaultDailyRestriction.endTime, true ); } @@ -792,7 +838,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { * @notice Internal function used to validate the transaction for a given address * If it validates then it also update the storage corressponds to the individual restriction */ - function _individualRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) private returns (Result) { + function _individualRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { // using the variable to avoid stack too deep error BucketDetails memory bucketDetails = bucketToUser[_from]; VolumeRestriction memory dailyRestriction = individualDailyRestriction[_from]; @@ -826,7 +872,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { allowedIndividual = false; } } - (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, fromTimestamp, bucketDetails.dailyLastTradedDayTime, dailyRestriction); + (allowedDaily, dailyTime) = _dailyTxCheck(_from, _amount, bucketDetails.dailyLastTradedDayTime, dailyRestriction); if (_isTransfer) { _updateStorage( @@ -836,6 +882,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { sumOfLastPeriod, daysCovered, dailyTime, + dailyRestriction.endTime, false ); } @@ -846,7 +893,6 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { function _dailyTxCheck( address from, uint256 amount, - uint256 fromTimestamp, uint256 dailyLastTradedDayTime, VolumeRestriction restriction ) @@ -858,14 +904,13 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { // the total amount get traded on a particular day (~ _fromTime) if ( now <= restriction.endTime && now >= restriction.startTime) { uint256 txSumOfDay = 0; - if (now.sub(dailyLastTradedDayTime) < 1 days || dailyLastTradedDayTime == 0) { - txSumOfDay = bucket[from][dailyLastTradedDayTime] >= bucket[from][fromTimestamp] ? bucket[from][dailyLastTradedDayTime] : bucket[from][fromTimestamp]; - if (dailyLastTradedDayTime == 0) - dailyLastTradedDayTime = now; - } else { - txSumOfDay = 0; - dailyLastTradedDayTime = now; - } + if (dailyLastTradedDayTime == 0) + // This if condition will be executed when the individual daily restriction executed first time + dailyLastTradedDayTime = restriction.startTime.add(BokkyPooBahsDateTimeLibrary.diffDays(restriction.startTime, now).mul(1 days)); + else if (now.sub(dailyLastTradedDayTime) >= 1 days) + dailyLastTradedDayTime = dailyLastTradedDayTime.add(BokkyPooBahsDateTimeLibrary.diffDays(dailyLastTradedDayTime, now).mul(1 days)); + // Assgining total sum traded on dailyLastTradedDayTime timestamp + txSumOfDay = bucket[from][dailyLastTradedDayTime]; return (_checkValidAmountToTransact(txSumOfDay, amount, restriction), dailyLastTradedDayTime); } return (true, dailyLastTradedDayTime); @@ -897,7 +942,8 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { if (counter >= _rollingPeriodInDays) { // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod sumOfLastPeriod = sumOfLastPeriod. - sub(bucket[_from][_bucketDetails.lastTradedDayTime.sub((counter.sub(_rollingPeriodInDays)).mul(1 days))]); + sub(bucket[_from][_bucketDetails.lastTradedDayTime. + sub((_bucketDetails.daysCovered.sub(counter.sub(_rollingPeriodInDays))).mul(1 days))]); } // Adding the last amount that is transacted on the `_fromTime` not actually doing it but left written to understand // the alogrithm @@ -939,6 +985,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { uint256 _sumOfLastPeriod, uint256 _daysCovered, uint256 _dailyLastTradedDayTime, + uint256 _endTime, bool isDefault ) internal @@ -953,10 +1000,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { // Assigning the latest transaction timestamp of the day details.lastTradedDayTime = _lastTradedDayTime; } - if (_lastTradedDayTime != 0 && _lastTradedDayTime != _dailyLastTradedDayTime) { - // Assigning the latest transaction timestamp of the day - details.dailyLastTradedDayTime = _lastTradedDayTime; - } else if (details.dailyLastTradedDayTime != _dailyLastTradedDayTime) { + if (details.dailyLastTradedDayTime != _dailyLastTradedDayTime) { // Assigning the latest transaction timestamp of the day details.dailyLastTradedDayTime = _dailyLastTradedDayTime; } @@ -966,10 +1010,13 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { if (_amount != 0) { if (_lastTradedDayTime !=0) { details.sumOfLastPeriod = _sumOfLastPeriod.add(_amount); + // Increasing the total amount of the day by `_amount` + bucket[_from][_lastTradedDayTime] = bucket[_from][_lastTradedDayTime].add(_amount); + } + if ((_dailyLastTradedDayTime != _lastTradedDayTime) && _dailyLastTradedDayTime != 0 && now <= _endTime) { + // Increasing the total amount of the day by `_amount` + bucket[_from][_dailyLastTradedDayTime] = bucket[_from][_dailyLastTradedDayTime].add(_amount); } - _dailyLastTradedDayTime = details.dailyLastTradedDayTime; - // Increasing the total amount of the day by `_amount` - bucket[_from][_dailyLastTradedDayTime] = bucket[_from][_dailyLastTradedDayTime].add(_amount); } } @@ -981,7 +1028,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { uint256 _restrictionType ) internal - view + pure { require(_restrictionType == 0 || _restrictionType == 1, "Invalid type"); if (_restrictionType == uint256(RestrictionType.Fixed)) { @@ -992,10 +1039,10 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { "Percentage is not within (0,100]" ); } - require(_startTime >= now && _endTime > _startTime); + require(_endTime > _startTime, "Invalid times"); // Maximum limit for the rollingPeriod is 365 days - require(_rollingPeriodDays >= 1 && _rollingPeriodDays <= 365); - require(BokkyPooBahsDateTimeLibrary.diffDays(_startTime, _endTime) >= _rollingPeriodDays); + require(_rollingPeriodDays >= 1 && _rollingPeriodDays <= 365, "Invalid rollingperiod"); + require(BokkyPooBahsDateTimeLibrary.diffDays(_startTime, _endTime) >= _rollingPeriodDays, "Invalid start & end time"); } function _checkLengthOfArray( diff --git a/test/y_volume_restriction_tm.js b/test/y_volume_restriction_tm.js index f6fd60de8..115bfed7a 100644 --- a/test/y_volume_restriction_tm.js +++ b/test/y_volume_restriction_tm.js @@ -70,7 +70,7 @@ contract('VolumeRestrictionTransferManager', accounts => { let tempAmount = new BigNumber(0); let tempArray = new Array(); - let tempArrayVariable = new Array(); + let tempArray3 = new Array(); let tempArrayGlobal = new Array(); // Initial fee for ticker registry and security token registry @@ -571,7 +571,7 @@ contract('VolumeRestrictionTransferManager', accounts => { ) }); - it("Should add the restriction succesfully after the expiry of previous one", async() => { + it("Should add the restriction succesfully after the expiry of previous one for investor 1", async() => { await increaseTime(duration.days(5.1)); console.log( @@ -612,7 +612,7 @@ contract('VolumeRestrictionTransferManager', accounts => { ); }); - it("Should succesfully transact the tokens just after the startTime", async() => { + it("Should succesfully transact the tokens by investor 1 just after the startTime", async() => { // Check the transfer will be valid or not by calling the verifyTransfer() directly by using _isTransfer = false let result = await I_VolumeRestrictionTM.verifyTransfer.call(account_investor1, account_investor3, web3.utils.toWei('.3'), "0x0", false); assert.equal(result.toNumber(), 1); @@ -702,7 +702,7 @@ contract('VolumeRestrictionTransferManager', accounts => { ); }) - it("Should add the individual daily default restriction", async() => { + it("Should add the individual daily restriction for investor 3", async() => { let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( account_investor3, web3.utils.toWei("6"), @@ -730,7 +730,8 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should transfer the tokens within the individual daily restriction limits", async() => { // transfer 2 tokens as per the limit - let txTime = latestTime(); + await increaseTime(5); // increase 5 seconds to layoff the time gap + let startTime = (await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor3))[1].toNumber(); console.log(` Gas Estimation for the Individual daily tx - ${await I_SecurityToken.transfer.estimateGas(account_investor2, web3.utils.toWei("2"), {from: account_investor3})} `) @@ -749,7 +750,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(newData, account_investor3); assert.equal(newData[3].toNumber(), data[3].toNumber()); - assert.closeTo(data[3].toNumber(), txTime, 2); + assert.equal(data[3].toNumber(), startTime); assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[3])) .dividedBy(new BigNumber(10).pow(18)).toNumber(), 6); }); @@ -764,7 +765,7 @@ contract('VolumeRestrictionTransferManager', accounts => { // increase the EVM time by one day await increaseTime(duration.days(1)); - let txTime = latestTime(); + let startTime = (await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor3))[1].toNumber(); console.log(` Gas Estimation for the Individual daily tx - ${await I_SecurityToken.transfer.estimateGas(account_investor2, web3.utils.toWei("2"), {from: account_investor3})} `) @@ -772,7 +773,7 @@ contract('VolumeRestrictionTransferManager', accounts => { let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); await print(data, account_investor3); - assert.closeTo(data[3].toNumber(), txTime, 2); + assert.equal(data[3].toNumber(), startTime + duration.days(1)); assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[3])) .dividedBy(new BigNumber(10).pow(18)).toNumber(), 2); }); @@ -780,22 +781,22 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should add the daily restriction on the investor 1", async() => { let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( account_investor1, - web3.utils.toWei("5"), - latestTime() + duration.seconds(1), - latestTime() + duration.days(4), + new BigNumber(5).times(new BigNumber(10).pow(16)), 0, + latestTime() + duration.days(4), + 1, { from: token_owner } ); assert.equal(tx.logs[0].args._holder, account_investor1); - assert.equal(tx.logs[0].args._typeOfRestriction, 0); - assert.equal((tx.logs[0].args._allowedTokens).toNumber(), web3.utils.toWei("5")); + assert.equal((tx.logs[0].args._typeOfRestriction).toNumber(), 1); + assert.equal((tx.logs[0].args._allowedTokens).dividedBy(new BigNumber(10).pow(16)).toNumber(), 5); let dataRestriction = await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor1); console.log(` *** Individual Daily restriction data *** - Allowed Tokens: ${dataRestriction[0].dividedBy(new BigNumber(10).pow(18)).toNumber()} + Allowed Tokens: ${dataRestriction[0].dividedBy(new BigNumber(10).pow(16)).toNumber()} % of TotalSupply StartTime : ${dataRestriction[1].toNumber()} Rolling Period in days : ${dataRestriction[2].toNumber()} EndTime : ${dataRestriction[3].toNumber()} @@ -803,7 +804,7 @@ contract('VolumeRestrictionTransferManager', accounts => { `); }); - it("Should transfer tokens on the 2 day by investor1 (Individual + Individual daily)", async() => { + it("Should transfer tokens on the 2nd day by investor1 (Individual + Individual daily)", async() => { let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber(); let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); @@ -829,778 +830,423 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray)); assert.equal(data[2].toNumber(), 1); - assert.equal(data[3].toNumber(), data[0].toNumber()); + assert.equal(data[3].toNumber(), + (await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor1))[1].toNumber()); assert.equal(amt, 2); }); - it.skip("Should successfully add the global restriction", async() => { - await I_VolumeRestrictionTM.addGlobalRestriction( - web3.utils.toWei("15"), + it("Should fail to transfer by investor 1 -- because voilating the individual daily", async() => { + // transfer 4 tokens -- voilate the daily restriction + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei("4"), {from: account_investor1}) + ); + }); + + it("Should add the individual restriction to investor 3", async() => { + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor3, + new BigNumber(15.36).times(new BigNumber(10).pow(16)), // 15.36 tokens as totalsupply is 1000 latestTime() + duration.seconds(2), - 5, - latestTime() + duration.days(10), - 0, + 6, + latestTime() + duration.days(15), + 1, { from: token_owner } ); - - let data = await I_VolumeRestrictionTM.globalRestriction.call(); - assert.equal(data[0].toNumber(), web3.utils.toWei("15")); - assert.equal(data[2].toNumber(), 5); + + assert.equal(tx.logs[0].args._holder, account_investor3); + assert.equal(tx.logs[0].args._typeOfRestriction, 1); }); - it.skip("Should successfully transact the tokens after 1 and half days", async() => { - await increaseTime(duration.days(1.5)); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber(); - let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - let globalStartTime = (await I_VolumeRestrictionTM.globalRestriction.call())[1].toNumber(); - let globalRollingPeriod = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); + it("Should transfer the token by the investor 3 with in the (Individual + Individual daily limit)", async() => { + await increaseTime(4); + // Allowed 4 tokens to transfer + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[1].toNumber(); + let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[2].toNumber(); + let startTimeDaily = (await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor3))[1].toNumber(); console.log(` - Gas estimation (Individual + Global): ${await I_SecurityToken.transfer.estimateGas(account_investor3, web3.utils.toWei("2"), {from: account_investor1})}` + Gas estimation (Individual + Individual daily): ${await I_SecurityToken.transfer.estimateGas(account_investor2, web3.utils.toWei("4"), {from: account_investor3})}` ); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2"), {from: account_investor1}); // Check the balance of the investors - let bal1 = await I_SecurityToken.balanceOf.call(account_investor1); + let bal1 = await I_SecurityToken.balanceOf.call(account_investor3); + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("4"), {from: account_investor3}); + tempArray3.push(4); + // Check the balance of the investors + let bal2 = await I_SecurityToken.balanceOf.call(account_investor3); // Verifying the balances - assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 32.7); - tempArray.push(2); - tempArrayGlobal.push(2); + assert.equal(web3.utils.fromWei(((bal1.minus(bal2)).toNumber()).toString()), 4); - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - await print(data, account_investor1); + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0].toNumber())) - .dividedBy(new BigNumber(10).pow(18)).toNumber(); - let globalAmt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[5].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); - assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray)); - assert.equal(data[2].toNumber(), 1); - assert.equal(data[3].toNumber(), 1); - assert.equal(data[4].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(globalRollingPeriod, tempArrayGlobal)); - assert.equal(data[5].toNumber(), globalStartTime + duration.days(data[3].toNumber())); - assert.equal(amt, 2); - assert.equal(globalAmt, 2); - }); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), 4); + assert.equal(data[2].toNumber(), 0); + assert.equal(data[3].toNumber(), startTimeDaily + duration.days(1)); + assert.equal(amt, 4); - it.skip("Should add the daily restriction successfully", async() => { - await I_VolumeRestrictionTM.addDailyGlobalRestriction( - web3.utils.toWei("5"), - latestTime() + duration.seconds(2), - latestTime() + duration.days(1.1), - 0, - { - from: token_owner - } - ); - let data = await I_VolumeRestrictionTM.dailyGlobalRestriction.call(); - assert.equal(data[0].dividedBy(new BigNumber(10).pow(18)).toNumber(), 5); - assert.equal(data[2].toNumber(), 1); - assert.equal(data[4].toNumber(), 0); }); - it.skip("Should transfer tokens within the daily limit -- falied because of limit failing", async() =>{ - // Transfer the 3.1 tokens to check the daily limit - await increaseTime(5); + it("Should fail during transferring more tokens by investor3 -- Voilating the daily Limit", async() => { await catchRevert( - I_SecurityToken.transfer(account_investor3, web3.utils.toWei("3.1"), {from: account_investor1}) + I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1"), {from: account_investor3}) ); }); - it.skip("Should transfer the tokens within the daily limit", async() => { - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber(); - let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - let globalStartTime = (await I_VolumeRestrictionTM.globalRestriction.call())[1].toNumber(); - let globalRollingPeriod = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); - // Calculate the gas estimation - console.log(` - Gas estimation (Individual + Global + daily): ${await I_SecurityToken.transfer.estimateGas(account_investor3, web3.utils.toWei("3"), {from: account_investor1})} - `) - // Transfer the 3 tokens - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("3"), {from: account_investor1}); - tempArray[tempArray.length -1] += 3; - tempArrayGlobal[tempArrayGlobal.length -1] += 3; - // getting the data of the bucket - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - // print the logs - await print(data, account_investor1); - // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0].toNumber())) - .dividedBy(new BigNumber(10).pow(18)).toNumber(); - let globalAmt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[5].toNumber())) - .dividedBy(new BigNumber(10).pow(18)).toNumber(); - // Verify the storage changes - assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); - assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray)); - assert.equal(data[2].toNumber(), 1); - assert.equal(data[3].toNumber(), 1); - assert.equal(data[4].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArrayGlobal)); - assert.equal(data[5].toNumber(), globalStartTime + duration.days(data[3].toNumber())); - assert.equal(amt, 5); - assert.equal(globalAmt, 5); - }); - - it.skip("Should transfer tokens within the daily limit -- falied because of limit failing", async() =>{ - // Transfer the 0.1 tokens to check the daily limit + it("Should remove the daily individual limit and transfer more tokens on a same day -- failed because of bad owner", async() => { + // remove the Individual daily restriction await catchRevert( - I_SecurityToken.transfer(account_investor3, web3.utils.toWei("0.1"), {from: account_investor1}) + I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor3, {from: account_investor4}) ); - }); + }) + + it("Should remove the daily individual limit and transfer more tokens on a same day", async() => { + // remove the Individual daily restriction + let tx = await I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor3, {from: token_owner}); + assert.equal(tx.logs[0].args._holder, account_investor3); + + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[1].toNumber(); + + // transfer more tokens on the same day + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("4"), {from: account_investor3}); + tempArray3[tempArray3.length -1] += 4; + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); - it.skip("Should successfully transact tokens on the next day of the rolling period (Fuzz test)", async() => { - // Increase time half day - await increaseTime(duration.days(.52)); - // Check the balance of the investors - let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[1].toNumber(); - let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - let globalStartTime = (await I_VolumeRestrictionTM.globalRestriction.call())[1].toNumber(); - let globalRollingPeriod = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); - - let totalAmountTransacted = 0; - // transactions performed - for (let i = 0; i < 5; i++) { - let amount = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; - // Calculate the gas estimation - console.log(` - Gas estimation (Individual + Global + daily): ${await I_SecurityToken.transfer.estimateGas(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor1})} - `) - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor1}); - console.log(`${i}: Restricted investor 1 able to transact ${amount} tokens to investor 3`); - totalAmountTransacted += amount; - } - - // Check the balance of the investors - let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); - // Verifying the balances - assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); - tempArray.push(totalAmountTransacted); - tempArrayGlobal.push(totalAmountTransacted); - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - // print the logs - await print(data, account_investor1); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0].toNumber())) - .dividedBy(new BigNumber(10).pow(18)).toNumber(); - let globalAmt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[5].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); + // Verify the storage changes assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); - assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray), 0.01); - assert.equal(data[2].toNumber(), 2); - assert.equal(data[3].toNumber(), 2); - assert.closeTo(data[4].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArrayGlobal), 0.01); - assert.equal(data[5].toNumber(), globalStartTime + duration.days(data[3].toNumber())); - assert.closeTo(amt, totalAmountTransacted, 0.01); - assert.closeTo(globalAmt, totalAmountTransacted, 0.01); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), 8); + assert.equal(data[2].toNumber(), 0); + assert.equal(data[3].toNumber(), 0); + assert.equal(amt, 8); }); - it.skip("Should fail to transact the tokens more than the allowed tokens in a rolling period", async() => { - // Increase the time by 3/5 of the day - await increaseTime(duration.days(.6)); - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - let minimumAmount = new BigNumber(12).times(new BigNumber(10).pow(18)).minus(data[1]); - let testAmount = minimumAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))) - await catchRevert( - I_SecurityToken.transfer(account_investor3, testAmount, {from: account_investor1}) - ); - }); - - it.skip("Should fail to buy tokens in the new rolling period --failed because amount is more than last 1 timestamps", async() => { - await increaseTime(duration.days(1)); - await catchRevert( - I_SecurityToken.transfer(account_investor3, web3.utils.toWei("10"), {from: account_investor1}) - ); - }); - - it.skip("Should add the daily restriction again successfully", async() => { - await I_VolumeRestrictionTM.addDailyGlobalRestriction( - web3.utils.toWei("7"), - latestTime() + duration.seconds(2), - latestTime() + duration.days(10), + it("Should add the new Individual daily restriction and transact the tokens", async() => { + // add new restriction + let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor3, + web3.utils.toWei("2"), + latestTime() + duration.days(1), + latestTime() + duration.days(4), 0, { from: token_owner } ); - let data = await I_VolumeRestrictionTM.dailyGlobalRestriction.call(); - assert.equal(data[0].dividedBy(new BigNumber(10).pow(18)).toNumber(), 7); - assert.equal(data[2].toNumber(), 1); - assert.equal(data[4].toNumber(), 0); - }); - - it.skip("Should transfer the tokens in a new rolling period", async() => { - let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - let firstDayAmount; - tempAmount = new BigNumber(0); - for (let i = 0; i < oldData[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${oldData[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - if (i != 2) { - firstDayAmount = await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][0]); - tempAmount = tempAmount.plus(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, oldData[0][i])); - } - } + assert.equal(tx.logs[0].args._holder, account_investor3); + assert.equal(tx.logs[0].args._typeOfRestriction, 0); + assert.equal((tx.logs[0].args._allowedTokens).toNumber(), web3.utils.toWei("2")); + let dataRestriction = await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor3); console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((oldData[1].toNumber()).toString())} - Last Timestamp Index : ${oldData[0].length -1} + *** Individual Daily restriction data *** + Allowed Tokens: ${dataRestriction[0].dividedBy(new BigNumber(10).pow(18)).toNumber()} + StartTime : ${dataRestriction[1].toNumber()} + Rolling Period in days : ${dataRestriction[2].toNumber()} + EndTime : ${dataRestriction[3].toNumber()} + Type of Restriction: ${dataRestriction[4].toNumber()} `); - let currentDayAmount = firstDayAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))); - let tx = await I_SecurityToken.transfer(account_investor3, currentDayAmount, {from: account_investor1}); - tempAmount = tempAmount.minus(currentDayAmount); - tempArray.push(0); - tempArray.push(currentDayAmount.dividedBy(new BigNumber(10).pow(18)).toNumber()); - console.log('\n'); - let newData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - for (let i = 2; i < newData[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${newData[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(newData[0][i].toNumber(), startTime + duration.days(i)) - assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, newData[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.001); - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((newData[1].toNumber()).toString())} - Last Timestamp Index : ${newData[0].length -1} - `); - }); + let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[2].toNumber(); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[1].toNumber(); + // Increase the time by one day + await increaseTime(duration.days(1.1)); - it.skip("Should transfer the more tokens on the same day", async() => { - // Check the balance of the investors - let balBefore = await I_SecurityToken.balanceOf.call(account_investor1); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor1))[2].toNumber(); - await I_SecurityToken.transfer(account_investor3, tempAmount, {from: account_investor1}); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("2"), {from: account_investor3}); + tempArray3.push(2); - // Check the balance of the investors - let balAfter = await I_SecurityToken.balanceOf.call(account_investor1); - // Verifying the balances - assert.closeTo( - (balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), - tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber(), - 0.01 + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toNumber(), 1); + assert.equal(data[3].toNumber(), dataRestriction[1].toNumber()); + assert.equal(amt, 2); + + // Fail to sell more tokens than the limit + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei("2"), {from: account_investor3}) ); - tempArray[tempArray.length -1] = tempArray[tempArray.length -1] + tempAmount.dividedBy(new BigNumber(10).pow(18)).toNumber(); - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor1); - for (let i = 2; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArray[i], 0.001); - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length -1} - `); - let sumOflastperiod = 0; - for (let i = tempArray.length - 1; i >= tempArray.length - 3; i--) { - sumOflastperiod += tempArray[i]; - } - assert.equal(data[0].length - 1, 4); - assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), sumOflastperiod, 0.001); }); - }); - describe.skip("Test case for the variable individual restriction", async() => { - it("Should add the restriction succesfully", async() => { - let tx = await I_VolumeRestrictionTM.addIndividualRestriction( - account_investor2, + it("Should fail to modify the individual daily restriction -- bad owner", async() => { + await catchRevert( + I_VolumeRestrictionTM.modifyIndividualDailyRestriction( + account_investor3, + web3.utils.toWei('3'), + latestTime(), + latestTime() + duration.days(5), 0, - new BigNumber(20).times(new BigNumber(10).pow(16)), - latestTime() + duration.seconds(2), - 3, - latestTime() + duration.days(10), - 1, { - from: token_owner + from: account_polymath } - ); - - assert.equal(tx.logs[0].args._holder, account_investor2); - assert.equal(tx.logs[0].args._typeOfRestriction, 1); - }); - - it("Should not successfully transact the tokens -- failed because volume is above the limit", async() => { - await increaseTime(duration.seconds(10)); - await catchRevert( - I_SecurityToken.transfer(account_investor3, web3.utils.toWei("15"), { from: account_investor2}) + ) ); }); + + it("Should modify the individual daily restriction", async() => { + await I_VolumeRestrictionTM.modifyIndividualDailyRestriction( + account_investor3, + web3.utils.toWei('3'), + 0, + latestTime() + duration.days(5), + 0, + { + from: token_owner + } + ); - it("Should succesfully transact the tokens just after the starttime", async() => { - // Check the transfer will be valid or not by calling the verifyTransfer() directly by using _isTransfer = false - let result = await I_VolumeRestrictionTM.verifyTransfer.call(account_investor2, account_investor3, web3.utils.toWei('.3'), "0x0", false); - console.log(result); - assert.equal(result.toNumber(), 1); - // Perform the transaction - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei('.3'), {from: account_investor2}); - // Check the balance of the investors - let bal1 = await I_SecurityToken.balanceOf.call(account_investor2); - // Verifying the balances - assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 29.7); - - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor2); - console.log('\n'); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), 0.3); - assert.equal(data[0][i].toNumber(), (await I_VolumeRestrictionTM.individualRestriction.call(account_investor2))[2].toNumber()) - } + let dataRestriction = await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor3); console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length -1} + *** Modify Individual Daily restriction data *** + Allowed Tokens: ${dataRestriction[0].dividedBy(new BigNumber(10).pow(18)).toNumber()} + StartTime : ${dataRestriction[1].toNumber()} + Rolling Period in days : ${dataRestriction[2].toNumber()} + EndTime : ${dataRestriction[3].toNumber()} + Type of Restriction: ${dataRestriction[4].toNumber()} `); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 0.3); - assert.equal(data[0].length -1, 0); - tempArrayVariable.push(0.3); }); - it("Should successfully transact the tokens after 1 and half days", async() => { - await increaseTime(duration.days(1.5)); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor2))[2].toNumber(); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor2}); - // Check the balance of the investors - let bal1 = await I_SecurityToken.balanceOf.call(account_investor2); - // Verifying the balances - assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 28.7); - tempArrayVariable.push(1); - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor2); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayVariable[i]); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length -1} - `); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 1.3); - assert.equal(data[0].length -1, 1); - }); + it("Should allow to sell to transfer more tokens by investor3", async() => { + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[1].toNumber(); + let startTimedaily = (await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor3))[1].toNumber(); + let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[2].toNumber(); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("3"), {from: account_investor3}); + tempArray3[tempArray3.length -1] += 3; - it("Should successfully transact more tokens on the same day (Fuzz test)", async() => { - // Check the balance of the investors - let balBefore = await I_SecurityToken.balanceOf.call(account_investor2); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor2))[2].toNumber(); - let totalAmountTransacted = 0; - for (let i = 0; i < 10; i++) { - let amount = Math.floor(Math.random() * (1000 - 100) + 100) / 1000; - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(amount.toString()), {from: account_investor2}); - console.log(`${i}: Restricted investor 2 able to transact ${amount} tokens to investor 3`); - totalAmountTransacted += amount; - } - - // Check the balance of the investors - let balAfter = await I_SecurityToken.balanceOf.call(account_investor2); - tempArrayVariable[1] = tempArrayVariable[1] + totalAmountTransacted; - // Verifying the balances - assert.closeTo((balBefore.minus(balAfter).dividedBy(new BigNumber(10).pow(18))).toNumber(), totalAmountTransacted, 0.01); - - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor2); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayVariable[i], 0.000001); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - `); - assert.closeTo(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), 1.3 + totalAmountTransacted, 0.000001); - assert.equal(data[0].length -1, 1); - }); + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); - it("Should successfully transfer the tokens after half days-- should increase the day covered by 1", async() => { - let oldData = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor2); - let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor2))[2].toNumber(); - await increaseTime(duration.days(.5)); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("2"), {from: account_investor2}); - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor2); - tempArrayVariable.push(2); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)) - assert.closeTo((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor2, data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayVariable[i], 0.000001); - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - `); - assert.equal(data[0].length - 1, 2); - assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), - (oldData[1].plus(new BigNumber(2).times(new BigNumber(10).pow(18)))) - .dividedBy(new BigNumber(10).pow(18)).toNumber()); + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toNumber(), 1); + assert.equal(data[3].toNumber(), startTimedaily); + assert.equal(amt, 5); }); - it("Should fail to transfer the tokens on third day -- because it is more than the rolling period allowed", async() => { - let data = await I_VolumeRestrictionTM.getBucketDetailsToUser.call(account_investor2); - let res = await I_VolumeRestrictionTM.individualRestriction.call(account_investor2); - let allowedAmount = (res[1].mul(await I_SecurityToken.totalSupply.call())).dividedBy(new BigNumber(10).pow(18)); - let remainingAmount = allowedAmount.minus(data[1]); + it("Should allow to transact the tokens on the other day", async() => { + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[1].toNumber(); + let startTimedaily = (await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor3))[1].toNumber(); + let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[2].toNumber(); - await catchRevert( - I_SecurityToken.transfer( - account_investor3, - remainingAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))), - { - from: account_investor2 - } - ) - ); - tempAmount = remainingAmount; + await increaseTime(duration.days(1)); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("2.36"), {from: account_investor3}); + tempArray3.push(2.36); + + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toNumber(), 2); + assert.equal(data[3].toNumber(), startTimedaily + duration.days(1)); + assert.equal(amt, 2.36); }); - it("Should successfully transfer tokens more than the allowed as totalsupply get increased", async() => { - await I_SecurityToken.mint(account_investor2, web3.utils.toWei("10"), { from: token_owner }); - - await I_SecurityToken.transfer( - account_investor3, - tempAmount.plus(new BigNumber(1).times(new BigNumber(10).pow(18))), - { - from: account_investor2 - } + it("Should fail to transfer the tokens after completion of the total amount", async() => { + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei("0.3"), {from: account_investor3}) ); - }); - }); + }) - describe.skip("Add the test cases for global restriction", async() => { + it("Should sell more tokens on the same day after changing the total supply", async() => { + await I_SecurityToken.mint(account_investor3, web3.utils.toWei("10"), {from: token_owner}); - it("Should successfully add the global restriction", async() => { - await I_VolumeRestrictionTM.addGlobalRestriction( - web3.utils.toWei("20"), - 0, - latestTime() + duration.seconds(2), - 3, - latestTime() + duration.days(10), - 0, - { - from: token_owner - } - ); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[1].toNumber(); + let startTimedaily = (await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor3))[1].toNumber(); + let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[2].toNumber(); - let data = await I_VolumeRestrictionTM.globalRestriction.call(); - assert.equal(data[0], web3.utils.toWei("20")); - assert.equal(data[3], 3); - }); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei(".50"), {from: account_investor3}); + tempArray3[tempArray3.length -1] += .50; - it("Should successfully add the 2 more address in the whitelist", async() => { - await I_GeneralTransferManager.modifyWhitelistMulti( - [account_investor4, account_delegate3], - [latestTime(), latestTime()], - [latestTime(), latestTime()], - [latestTime() + duration.days(30), latestTime() + duration.days(30)], - [true, true], - { - from: token_owner - } - ); + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); - // Mint some tokens and transferred to whitelisted addresses - await I_SecurityToken.mint(account_investor4, web3.utils.toWei("40", "ether"), {from: token_owner}); - await I_SecurityToken.mint(account_delegate3, web3.utils.toWei("30", "ether"), {from: token_owner}); - - // Check the balance of the investors - let bal1 = await I_SecurityToken.balanceOf.call(account_investor4); - let bal2 = await I_SecurityToken.balanceOf.call(account_delegate3); - // Verifying the balances - assert.equal(web3.utils.fromWei((bal1.toNumber()).toString()), 40); - assert.equal(web3.utils.fromWei((bal2.toNumber()).toString()), 30); + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toNumber(), 2); + assert.equal(data[3].toNumber(), startTimedaily + duration.days(1)); + assert.equal(amt, 2.86); }); - it("Should successfully transfer the tokens with in the global range", async() => { - let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor4}); - let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); - let latestTimestamp = 0; - tempArrayGlobal.push(1); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); - assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); + it("Should fail to transact tokens more than the allowed in the second rolling period", async() => { + await increaseTime(duration.days(5)); + let i + for (i = 0; i < 3; i++) { + tempArray3.push(0); } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - `); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 1); - assert.equal(data[0].length - 1, 0); - - console.log(`Transfer the tokens from the another investor comes under the global category`); - await increaseTime(duration.minutes(10)); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("4"), {from: account_delegate3}); - tempArrayGlobal[tempArrayGlobal.length - 1] += 4; - data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); + let allowedAmount = (tempArray3[0] + 1.1); + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei(allowedAmount.toString()), {from: account_investor3}) + ); + }) + + it("Should successfully to transact tokens in the second rolling period", async() => { + // Should transact freely tokens daily limit is also ended + + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[1].toNumber(); + let startTimedaily = (await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor3))[1].toNumber(); + let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[2].toNumber(); + let allowedAmount = (tempArray3[0] + 1); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei(allowedAmount.toString()), {from: account_investor3}); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); - assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); - latestTimestamp = data[0][i]; - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} - `); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 5); - assert.equal(data[0].length - 1, 0); - assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 5); - }); + tempArray3.push(allowedAmount); + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); - it("Should transfer the tokens on the another day", async() => { - await increaseTime(duration.days(1.2)); - let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei("5"), { from: account_investor3}); - tempArrayGlobal.push(5); - let latestTimestamp = 0; - let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); - assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); - latestTimestamp = data[0][i]; - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} - `); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 10); - assert.equal(data[0].length - 1, 1); - assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 5); + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toNumber(), 7); + assert.equal(data[3].toNumber(), startTimedaily + duration.days(1)); + assert.equal(amt, allowedAmount); }); - it("Should transfer tokens on the third day of rolling period", async() => { - await increaseTime(duration.days(1)); - let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("6"), {from: account_investor4}); - let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); - tempArrayGlobal.push(6); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); - assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} - `); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 16); - assert.equal(data[0].length - 1, 2); - assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 6); - - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("4"), {from: account_delegate3}); - data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); - tempArrayGlobal[tempArrayGlobal.length -1] += 4; - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); - assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} - `); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 20); - assert.equal(data[0].length - 1, 2); - assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 10); + it("Should sell more tokens on the net day of rolling period", async() => { + await increaseTime(duration.days(3)); + + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[1].toNumber(); + let startTimedaily = (await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor3))[1].toNumber(); + let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[2].toNumber(); + + tempArray3.push(0); + tempArray3.push(0); + + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("7"), {from: account_investor3}); + + tempArray3.push(7) + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toNumber(), 10); + assert.equal(data[3].toNumber(), startTimedaily + duration.days(1)); + assert.equal(amt, 7); }) - it("Should transfer of tokens get failed - limit of global token txn get reached", async() => { - let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); - let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); - let latestTimestamp = 0; - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - latestTimestamp = data[0][i]; + it("Should transfer after the 5 days", async() => { + await increaseTime(duration.days(4.5)); + + for (let i = 0; i <3; i++) { + tempArray3.push(0); } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} - `); - // Already 20 tokens transferred - await catchRevert( - I_SecurityToken.transfer(account_delegate3, web3.utils.toWei("5"), {from: account_investor3}) - ); + let startTime = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[1].toNumber(); + let startTimedaily = (await I_VolumeRestrictionTM.individualDailyRestriction.call(account_investor3))[1].toNumber(); + let rollingPeriod = (await I_VolumeRestrictionTM.individualRestriction.call(account_investor3))[2].toNumber(); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("25"), {from: account_investor2}); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("8"), {from: account_investor3}); + tempArray3.push(8); + + let data = await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toNumber(), 14); + assert.equal(data[3].toNumber(), startTimedaily + duration.days(1)); + assert.equal(amt, 8); }); - it("Should allow to transact on the next rolling period", async() => { - await increaseTime(duration.days(1.7)); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("1"), {from: account_investor4}); - let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); - let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); - let latestTimestamp = 0; - tempArrayGlobal.push(1); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); - assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); - latestTimestamp = data[0][i]; - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} - `); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 16); - assert.equal(data[0].length - 1, 3); - assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 1); + it("Should freely transfer the tokens after one day (completion of individual restriction)", async() => { + // increase one time + await increaseTime(duration.days(1)); + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("17"), {from: account_investor3}); }); + }); - it("Should add the daily restriction successfully", async() => { - await I_VolumeRestrictionTM.addDailyGlobalRestriction( - web3.utils.toWei("5"), - 0, + describe("Should add the Default restriction", async() => { + + it("Should successfully add the default restriction", async() => { + await I_VolumeRestrictionTM.addDefaultRestriction( + web3.utils.toWei("10"), latestTime() + duration.seconds(2), - latestTime() + duration.days(3), + 5, + latestTime() + duration.days(10), 0, { from: token_owner } ); - let data = await I_VolumeRestrictionTM.dailyGlobalRestriction.call(); - assert.equal(data[0].dividedBy(new BigNumber(10).pow(18)).toNumber(), 5); - assert.equal(data[1].toNumber(), 0); - assert.equal(data[5].toNumber(), 0); - }); - it("Should failed to transact tokens -- failed because amount of tx is more than the daily limit",async() => { - let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); - await increaseTime(duration.days(2)); - // Failed because amount of tokens is greater than the daily limit - await catchRevert( - I_SecurityToken.transfer(account_delegate3, web3.utils.toWei("5.1"), {from: account_investor4}) - ); + let data = await I_VolumeRestrictionTM.defaultRestriction.call(); + assert.equal(data[0].toNumber(), web3.utils.toWei("10")); + assert.equal(data[2].toNumber(), 5); }); - it("Should transfer the tokens with in the daily limit", async() => { - let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); - await I_SecurityToken.transfer(account_delegate3, web3.utils.toWei("4.9"), {from: account_investor4}); - tempArrayGlobal.push(0); - tempArrayGlobal.push(4.9); - let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); - assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} - `); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 5.9); - assert.equal(data[0].length - 1, 5); - assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 4.9); + it("Should add the investor 4 in the whitelist", async() => { + await I_GeneralTransferManager.modifyWhitelist( + account_investor4, + latestTime(), + latestTime(), + latestTime() + duration.days(30), + true, + { + from: token_owner + } + ); }); - it("Should transact the tokens more than the daily limit because daily limit restriction is ended", async() => { - let startTime = (await I_VolumeRestrictionTM.globalRestriction.call())[2].toNumber(); - await increaseTime(duration.days(1)); - await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("10"), {from: account_delegate3}); - tempArrayGlobal.push(10); - let data = await I_VolumeRestrictionTM.getGlobalBucketDetails.call(); - for (let i = 0; i < data[0].length; i++) { - console.log(` - Timestamps array index ${i}: ${data[0][i].toNumber()} - Total Trade till now: ${(await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18))} - `); - assert.equal(data[0][i].toNumber(), startTime + duration.days(i)); - assert.equal((await I_VolumeRestrictionTM.globalBucket.call(data[0][i])) - .dividedBy(new BigNumber(10).pow(18)).toNumber(), tempArrayGlobal[i]); - } - console.log(` - SumOfLastPeriod : ${web3.utils.fromWei((data[1].toNumber()).toString())} - Last Timestamp Index : ${data[0].length - 1} - Total amount traded in a day: ${web3.utils.fromWei((data[2].toNumber()).toString())} - `); - assert.equal(web3.utils.fromWei((data[1].toNumber()).toString()), 14.9); - assert.equal(data[0].length - 1, 6); - assert.equal(web3.utils.fromWei((data[2].toNumber()).toString()), 10); + it("Should mint some tokens to investor 4", async() => { + await I_SecurityToken.mint(account_investor4, web3.utils.toWei("15"), {from: token_owner}); }); }) - describe.skip("Test for the exemptlist", async() => { + describe("Test for the exemptlist", async() => { it("Should add the token holder in the exemption list -- failed because of bad owner", async() => { await catchRevert( @@ -1618,13 +1264,12 @@ contract('VolumeRestrictionTransferManager', accounts => { }); }); - describe.skip("Test for modify functions", async() => { + describe("Test for modify functions", async() => { - it("Should not able to modify the already started restrictions --global", async() =>{ + it.skip("Should not able to modify the already started restrictions --global", async() =>{ await catchRevert( - I_VolumeRestrictionTM.modifyGlobalRestriction( + I_VolumeRestrictionTM.modifyDefaultDailyRestriction( web3.utils.toWei("50"), - 0, latestTime() + duration.seconds(50), 10, latestTime() + duration.days(20), @@ -1636,11 +1281,10 @@ contract('VolumeRestrictionTransferManager', accounts => { ); }); - it("Should not able to modify the already started restrictions -- daily global", async() =>{ + it.skip("Should not able to modify the already started restrictions -- daily global", async() =>{ await catchRevert( - I_VolumeRestrictionTM.modifyDailyGlobalRestriction( + I_VolumeRestrictionTM.modifyDefaultDailyRestriction( web3.utils.toWei("50"), - 0, latestTime() + duration.seconds(50), latestTime() + duration.days(20), 0, @@ -1651,12 +1295,11 @@ contract('VolumeRestrictionTransferManager', accounts => { ); }); - it("Should not able to modify the already started restrictions -- Individual", async() =>{ + it.skip("Should not able to modify the already started restrictions -- Individual", async() =>{ await catchRevert( I_VolumeRestrictionTM.modifyIndividualRestriction( account_investor2, web3.utils.toWei("50"), - 0, latestTime() + duration.seconds(50), 10, latestTime() + duration.days(20), @@ -1668,12 +1311,11 @@ contract('VolumeRestrictionTransferManager', accounts => { ); }); - it("Should not able to modify the already started transaction -- multi Individuals", async() => { + it.skip("Should not able to modify the already started transaction -- multi Individuals", async() => { await catchRevert( I_VolumeRestrictionTM.modifyIndividualRestrictionMulti( [account_investor2, account_investor1], [web3.utils.toWei("50"), web3.utils.toWei("50")], - [0, 0], [latestTime() + duration.seconds(50), latestTime() + duration.seconds(50)], [10, 20], [latestTime() + duration.days(20), latestTime() + duration.days(50)], @@ -1688,8 +1330,7 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should add the individual restriction for multiple investor", async() => { await I_VolumeRestrictionTM.addIndividualRestrictionMulti( [account_investor3, account_delegate3], - [web3.utils.toWei("15"), 0], - [0, new BigNumber(12.78).times(new BigNumber(10).pow(16))], + [web3.utils.toWei("15"), new BigNumber(12.78).times(new BigNumber(10).pow(16))], [latestTime() + duration.days(1), latestTime() + duration.days(2)], [15, 20], [latestTime() + duration.days(40), latestTime() + duration.days(60)], @@ -1703,23 +1344,19 @@ contract('VolumeRestrictionTransferManager', accounts => { let indi2 = await I_VolumeRestrictionTM.individualRestriction.call(account_delegate3); assert.equal(indi1[0].dividedBy(new BigNumber(10).pow(18)), 15); - assert.equal(indi2[0].dividedBy(new BigNumber(10).pow(18)), 0); + assert.equal(indi2[0].dividedBy(new BigNumber(10).pow(16)), 12.78); - assert.equal(indi1[1].dividedBy(new BigNumber(10).pow(18)), 0); - assert.equal(indi2[1].dividedBy(new BigNumber(10).pow(16)), 12.78); + assert.equal(indi1[2].toNumber(), 15); + assert.equal(indi2[2].toNumber(), 20); - assert.equal(indi1[3].toNumber(), 15); - assert.equal(indi2[3].toNumber(), 20); - - assert.equal(indi1[5].toNumber(), 0); - assert.equal(indi2[5].toNumber(), 1); + assert.equal(indi1[4].toNumber(), 0); + assert.equal(indi2[4].toNumber(), 1); }); it("Should modify the details before the starttime passed", async() => { await I_VolumeRestrictionTM.modifyIndividualRestrictionMulti( [account_investor3, account_delegate3], - [0, web3.utils.toWei("15")], - [new BigNumber(12.78).times(new BigNumber(10).pow(16)), 0], + [new BigNumber(12.78).times(new BigNumber(10).pow(16)), web3.utils.toWei("15")], [latestTime() + duration.days(1), latestTime() + duration.days(2)], [20, 15], [latestTime() + duration.days(40), latestTime() + duration.days(60)], @@ -1733,16 +1370,13 @@ contract('VolumeRestrictionTransferManager', accounts => { let indi2 = await I_VolumeRestrictionTM.individualRestriction.call(account_delegate3); assert.equal(indi2[0].dividedBy(new BigNumber(10).pow(18)), 15); - assert.equal(indi1[0].dividedBy(new BigNumber(10).pow(18)), 0); - - assert.equal(indi2[1].dividedBy(new BigNumber(10).pow(18)), 0); - assert.equal(indi1[1].dividedBy(new BigNumber(10).pow(16)), 12.78); + assert.equal(indi1[0].dividedBy(new BigNumber(10).pow(16)), 12.78); - assert.equal(indi2[3].toNumber(), 15); - assert.equal(indi1[3].toNumber(), 20); + assert.equal(indi2[2].toNumber(), 15); + assert.equal(indi1[2].toNumber(), 20); - assert.equal(indi2[5].toNumber(), 0); - assert.equal(indi1[5].toNumber(), 1); + assert.equal(indi2[4].toNumber(), 0); + assert.equal(indi1[4].toNumber(), 1); }); }); From 97fe1cfee07065ef88e11f66b15a11f1076bbaa9 Mon Sep 17 00:00:00 2001 From: satyam Date: Sun, 9 Dec 2018 16:35:24 +0530 Subject: [PATCH 33/56] more test cases and fix bug in algo --- .../TransferManager/VolumeRestrictionTM.sol | 4 +- test/y_volume_restriction_tm.js | 300 +++++++++++++----- 2 files changed, 224 insertions(+), 80 deletions(-) diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index 04240ea35..e525d81b8 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -934,8 +934,10 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { if (_diffDays >= _rollingPeriodInDays) { // If the difference of days is greater than the rollingPeriod then sumOfLastPeriod will always be zero sumOfLastPeriod = 0; + counter = counter.add(_diffDays); } else { for (i = 0; i < _diffDays; i++) { + counter++; // This condition is to check whether the first rolling period is covered or not // if not then it continues and adding 0 value into sumOfLastPeriod without subtracting // the earlier value at that index @@ -948,8 +950,6 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { // Adding the last amount that is transacted on the `_fromTime` not actually doing it but left written to understand // the alogrithm //_bucketDetails.sumOfLastPeriod = _bucketDetails.sumOfLastPeriod.add(uint256(0)); - // Storing all those timestamps whose total transacted value is 0 - counter++; } } // calculating the timestamp that will used as an index of the next bucket diff --git a/test/y_volume_restriction_tm.js b/test/y_volume_restriction_tm.js index 115bfed7a..cfd955a91 100644 --- a/test/y_volume_restriction_tm.js +++ b/test/y_volume_restriction_tm.js @@ -203,7 +203,7 @@ contract('VolumeRestrictionTransferManager', accounts => { [account_investor1, account_investor2, account_investor3], [latestTime(), latestTime(), latestTime()], [latestTime(), latestTime(), latestTime()], - [latestTime() + duration.days(30), latestTime() + duration.days(30), latestTime() + duration.days(30)], + [latestTime() + duration.days(60), latestTime() + duration.days(60), latestTime() + duration.days(60)], [true, true, true], { from: token_owner @@ -1105,11 +1105,12 @@ contract('VolumeRestrictionTransferManager', accounts => { }); it("Should fail to transact tokens more than the allowed in the second rolling period", async() => { - await increaseTime(duration.days(5)); + await increaseTime(duration.days(4)); let i for (i = 0; i < 3; i++) { tempArray3.push(0); } + console.log(`Diff Days: ${(latestTime() - ((await I_VolumeRestrictionTM.getIndividualBucketDetailsToUser.call(account_investor3))[0]).toNumber()) / 86400}`); let allowedAmount = (tempArray3[0] + 1.1); await catchRevert( I_SecurityToken.transfer(account_investor2, web3.utils.toWei(allowedAmount.toString()), {from: account_investor3}) @@ -1137,7 +1138,7 @@ contract('VolumeRestrictionTransferManager', accounts => { // Verify the storage changes assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); - assert.equal(data[2].toNumber(), 7); + assert.equal(data[2].toNumber(), 6); assert.equal(data[3].toNumber(), startTimedaily + duration.days(1)); assert.equal(amt, allowedAmount); }); @@ -1166,7 +1167,7 @@ contract('VolumeRestrictionTransferManager', accounts => { // Verify the storage changes assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); - assert.equal(data[2].toNumber(), 10); + assert.equal(data[2].toNumber(), 9); assert.equal(data[3].toNumber(), startTimedaily + duration.days(1)); assert.equal(amt, 7); }) @@ -1197,24 +1198,98 @@ contract('VolumeRestrictionTransferManager', accounts => { // Verify the storage changes assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); - assert.equal(data[2].toNumber(), 14); + assert.equal(data[2].toNumber(), 13); assert.equal(data[3].toNumber(), startTimedaily + duration.days(1)); assert.equal(amt, 8); }); it("Should freely transfer the tokens after one day (completion of individual restriction)", async() => { // increase one time - await increaseTime(duration.days(1)); + await increaseTime(duration.days(2)); await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("17"), {from: account_investor3}); }); }); - describe("Should add the Default restriction", async() => { + describe("Test cases for the Default restrictions", async() => { + + it("Should add the investor 4 in the whitelist", async() => { + await I_GeneralTransferManager.modifyWhitelist( + account_investor4, + latestTime(), + latestTime(), + latestTime() + duration.days(30), + true, + { + from: token_owner + } + ); + }); + + it("Should mint some tokens to investor 4", async() => { + await I_SecurityToken.mint(account_investor4, web3.utils.toWei("20"), {from: token_owner}); + }); + + it("Should add the default daily restriction successfully", async() => { + await I_VolumeRestrictionTM.addDefaultDailyRestriction( + new BigNumber(2.75).times(new BigNumber(10).pow(16)), + 0, + latestTime() + duration.days(3), + 1, + { + from: token_owner + } + ); + + let dataRestriction = await I_VolumeRestrictionTM.defaultDailyRestriction.call(); + console.log(` + *** Add Individual Daily restriction data *** + Allowed Tokens: ${dataRestriction[0].dividedBy(new BigNumber(10).pow(16)).toNumber()} % of TotalSupply + StartTime : ${dataRestriction[1].toNumber()} + Rolling Period in days : ${dataRestriction[2].toNumber()} + EndTime : ${dataRestriction[3].toNumber()} + Type of Restriction: ${dataRestriction[4].toNumber()} + `); + }); + + it("Should fail to transfer above the daily limit", async() => { + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei("5"), {from: account_investor4}) + ) + }) + + it("Should transfer the token by investor 4", async() => { + let startTimedaily = (await I_VolumeRestrictionTM.defaultDailyRestriction.call())[1].toNumber(); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("3.57"), {from: account_investor4}); + + let data = await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor4); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor4, data[3].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + + // Verify the storage changes + assert.equal(data[0].toNumber(), 0); + assert.equal(data[1].toNumber(), 0); + assert.equal(data[2].toNumber(), 0); + assert.equal(data[3].toNumber(), startTimedaily); + assert.equal(amt, 3.57); + }); + + it("Should transfer the tokens freely after ending the default daily restriction", async() => { + await increaseTime(duration.days(3)); + //sell tokens upto the limit + let tx = await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("5"), {from: account_investor4}); + assert.equal((tx.logs[0].args.value).toNumber(), web3.utils.toWei("5")); + // Transfer the tokens again to investor 3 + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei("40"), {from: account_investor2}); + }) it("Should successfully add the default restriction", async() => { await I_VolumeRestrictionTM.addDefaultRestriction( web3.utils.toWei("10"), - latestTime() + duration.seconds(2), + 0, 5, latestTime() + duration.days(10), 0, @@ -1226,23 +1301,153 @@ contract('VolumeRestrictionTransferManager', accounts => { let data = await I_VolumeRestrictionTM.defaultRestriction.call(); assert.equal(data[0].toNumber(), web3.utils.toWei("10")); assert.equal(data[2].toNumber(), 5); + let dataRestriction = await I_VolumeRestrictionTM.defaultRestriction.call(); + console.log(` + *** Add Individual restriction data *** + Allowed Tokens: ${dataRestriction[0].dividedBy(new BigNumber(10).pow(18)).toNumber()} + StartTime : ${dataRestriction[1].toNumber()} + Rolling Period in days : ${dataRestriction[2].toNumber()} + EndTime : ${dataRestriction[3].toNumber()} + Type of Restriction: ${dataRestriction[4].toNumber()} + `); }); - it("Should add the investor 4 in the whitelist", async() => { - await I_GeneralTransferManager.modifyWhitelist( - account_investor4, - latestTime(), - latestTime(), - latestTime() + duration.days(30), - true, + it("Should transfer tokens on by investor 3 (comes under the Default restriction)", async() => { + tempArray3.length = 0; + let startTime = (await I_VolumeRestrictionTM.defaultRestriction.call())[1].toNumber(); + let startTimedaily = (await I_VolumeRestrictionTM.defaultDailyRestriction.call())[1].toNumber(); + let rollingPeriod = (await I_VolumeRestrictionTM.defaultRestriction.call())[2].toNumber(); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("5"), {from: account_investor3}); + tempArray3.push(5); + + let data = await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toNumber(), 0); + assert.equal(data[3].toNumber(), 0); + assert.equal(amt, 5); + + // Transfer tokens on another day + + await increaseTime(duration.days(1)); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("3"), {from: account_investor3}); + tempArray3.push(3); + + data = await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toNumber(), 1); + assert.equal(data[3].toNumber(), 0); + assert.equal(amt, 3); + }); + + it("Should fail to transfer more tokens than the available default limit", async() => { + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei("3"), {from: account_investor3}) + ); + }); + + it("Should able to transfer tokens in the next rolling period", async() => { + await increaseTime(duration.days(4.1)); + console.log(`Diff days: ${(latestTime() - ((await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor3))[0]).toNumber()) / 86400}`) + for (let i = 0; i < 3; i++) { + tempArray3.push(0); + } + + let startTime = (await I_VolumeRestrictionTM.defaultRestriction.call())[1].toNumber(); + let startTimedaily = (await I_VolumeRestrictionTM.defaultDailyRestriction.call())[1].toNumber(); + let rollingPeriod = (await I_VolumeRestrictionTM.defaultRestriction.call())[2].toNumber(); + + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("7"), {from: account_investor3}); + tempArray3.push(7); + + let data = await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toNumber(), 5); + assert.equal(data[3].toNumber(), 0); + assert.equal(amt, 7); + + // Try to transact more on the same day but fail + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1"), {from: account_investor3}) + ); + }); + + it("Should add the daily default restriction again", async() => { + await I_VolumeRestrictionTM.addDefaultDailyRestriction( + web3.utils.toWei("2"), + 0, + latestTime() + duration.days(3), + 0, { from: token_owner } ); + + let dataRestriction = await I_VolumeRestrictionTM.defaultDailyRestriction.call(); + console.log(` + *** Add Individual Daily restriction data *** + Allowed Tokens: ${dataRestriction[0].dividedBy(new BigNumber(10).pow(16)).toNumber()} + StartTime : ${dataRestriction[1].toNumber()} + Rolling Period in days : ${dataRestriction[2].toNumber()} + EndTime : ${dataRestriction[3].toNumber()} + Type of Restriction: ${dataRestriction[4].toNumber()} + `); + }); + + it("Should not able to transfer tokens more than the default daily restriction", async() => { + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei("3"), {from: account_investor3}) + ); }); - it("Should mint some tokens to investor 4", async() => { - await I_SecurityToken.mint(account_investor4, web3.utils.toWei("15"), {from: token_owner}); + it("Should able to transfer tokens within the limit of (daily default + default) restriction", async() => { + await increaseTime(duration.days(1)); + let startTime = (await I_VolumeRestrictionTM.defaultRestriction.call())[1].toNumber(); + let startTimedaily = (await I_VolumeRestrictionTM.defaultDailyRestriction.call())[1].toNumber(); + let rollingPeriod = (await I_VolumeRestrictionTM.defaultRestriction.call())[2].toNumber(); + //sell tokens upto the limit + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("2"), {from: account_investor3}); + tempArray3[tempArray3.length -1] += 2; + + let data = await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor3); + await print(data, account_investor3); + + // get the trade amount using the timestamp + let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + .dividedBy(new BigNumber(10).pow(18)).toNumber(); + + // Verify the storage changes + assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); + assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); + assert.equal(data[2].toNumber(), 5); + assert.equal(data[3].toNumber(), startTimedaily); + assert.equal(amt, 2); }); }) @@ -1266,67 +1471,6 @@ contract('VolumeRestrictionTransferManager', accounts => { describe("Test for modify functions", async() => { - it.skip("Should not able to modify the already started restrictions --global", async() =>{ - await catchRevert( - I_VolumeRestrictionTM.modifyDefaultDailyRestriction( - web3.utils.toWei("50"), - latestTime() + duration.seconds(50), - 10, - latestTime() + duration.days(20), - 0, - { - from: token_owner - } - ) - ); - }); - - it.skip("Should not able to modify the already started restrictions -- daily global", async() =>{ - await catchRevert( - I_VolumeRestrictionTM.modifyDefaultDailyRestriction( - web3.utils.toWei("50"), - latestTime() + duration.seconds(50), - latestTime() + duration.days(20), - 0, - { - from: token_owner - } - ) - ); - }); - - it.skip("Should not able to modify the already started restrictions -- Individual", async() =>{ - await catchRevert( - I_VolumeRestrictionTM.modifyIndividualRestriction( - account_investor2, - web3.utils.toWei("50"), - latestTime() + duration.seconds(50), - 10, - latestTime() + duration.days(20), - 0, - { - from: token_owner - } - ) - ); - }); - - it.skip("Should not able to modify the already started transaction -- multi Individuals", async() => { - await catchRevert( - I_VolumeRestrictionTM.modifyIndividualRestrictionMulti( - [account_investor2, account_investor1], - [web3.utils.toWei("50"), web3.utils.toWei("50")], - [latestTime() + duration.seconds(50), latestTime() + duration.seconds(50)], - [10, 20], - [latestTime() + duration.days(20), latestTime() + duration.days(50)], - [0, 0], - { - from: token_owner - } - ) - ); - }); - it("Should add the individual restriction for multiple investor", async() => { await I_VolumeRestrictionTM.addIndividualRestrictionMulti( [account_investor3, account_delegate3], From 7447fb6d66b584d0162914d19b5a2efcc3cabbc7 Mon Sep 17 00:00:00 2001 From: satyam Date: Sun, 9 Dec 2018 16:52:12 +0530 Subject: [PATCH 34/56] CLI & CountTransferManager changes readded --- CLI/commands/helpers/time.js | 50 ++++++++ CLI/polymath-cli.js | 47 +++++--- .../TransferManager/CountTransferManager.sol | 8 +- .../CountTransferManagerFactory.sol | 2 +- test/d_count_transfer_manager.js | 107 ++++++++++++++++++ 5 files changed, 196 insertions(+), 18 deletions(-) create mode 100644 CLI/commands/helpers/time.js diff --git a/CLI/commands/helpers/time.js b/CLI/commands/helpers/time.js new file mode 100644 index 000000000..68477a554 --- /dev/null +++ b/CLI/commands/helpers/time.js @@ -0,0 +1,50 @@ +const moment = require('moment'); +const chalk = require('chalk'); + function jumpToTime(timestamp) { + const id = Date.now(); + return new Promise((resolve, reject) => { + web3.currentProvider.send( + { + jsonrpc: "2.0", + method: "evm_mine", + params: [timestamp], + id: id + }, + (err, res) => { + return err ? reject(err) : resolve(res); + } + ); + }); +} + async function increaseTimeByDate(toTime) { + if (await web3.eth.net.getId() === 15) { + if (toTime.isValid()) { + let currentTime = (await web3.eth.getBlock('latest')).timestamp; + if (toTime.unix() > currentTime) { + await jumpToTime(toTime.unix()); + currentTime = (await web3.eth.getBlock('latest')).timestamp; + console.log(chalk.gree(`Current datetime is ${currentTime} or ${moment.unix(currentTime).format("MM/DD/YYYY HH:mm:ss")}`)); + } else { + console.log(chalk.red(`It is not possible to go back in time. Please try again with a time in the future to travel to`)); + } + } else { + console.log(chalk.red(`Date format is not valid. Please use a valid Unix epoch time`)); + } + } else { + console.log(chalk.red(`Time traveling is only possible over develpment network`)); + } +} + function increaseTimeByDuration(duration) { + let currentTime = moment().unix(); + let toTime = currentTime.add(duration, "seconds"); + return increaseTimeByDate(toTime); +} + function increaseTimeToDate(date) { + var toDate = moment(date, ['MM-DD-YYYY', 'MM-DD-YYYY HH:mm:ss']); + return increaseTimeByDate(toDate); +} + function increaseTimeToEpochDate(epochDate) { + var toTime = moment.unix(epochDate); + return increaseTimeByDate(toTime); +} + module.exports = { increaseTimeByDuration, increaseTimeToDate, increaseTimeToEpochDate }; \ No newline at end of file diff --git a/CLI/polymath-cli.js b/CLI/polymath-cli.js index 2ce101a4d..d3394c8eb 100644 --- a/CLI/polymath-cli.js +++ b/CLI/polymath-cli.js @@ -1,19 +1,21 @@ #!/usr/bin/env node -var faucet = require('./commands/faucet'); -var investor_portal = require('./commands/investor_portal'); -var token_manager = require('./commands/token_manager'); -var st20generator = require('./commands/ST20Generator'); -var sto_manager = require('./commands/sto_manager'); -var transfer = require('./commands/transfer'); -var transfer_ownership = require('./commands/transfer_ownership'); -var dividends_manager = require('./commands/dividends_manager'); -var transfer_manager = require('./commands/transfer_manager'); -var contract_manager = require('./commands/contract_manager'); -var strMigrator = require('./commands/strMigrator'); -var permission_manager = require('./commands/permission_manager'); -var program = require('commander'); -var gbl = require('./commands/common/global'); +const faucet = require('./commands/faucet'); +const investor_portal = require('./commands/investor_portal'); +const token_manager = require('./commands/token_manager'); +const st20generator = require('./commands/ST20Generator'); +const sto_manager = require('./commands/sto_manager'); +const transfer = require('./commands/transfer'); +const transfer_ownership = require('./commands/transfer_ownership'); +const dividends_manager = require('./commands/dividends_manager'); +const transfer_manager = require('./commands/transfer_manager'); +const contract_manager = require('./commands/contract_manager'); +const strMigrator = require('./commands/strMigrator'); +const permission_manager = require('./commands/permission_manager'); +const time = require('./commands/helpers/time'); +const gbl = require('./commands/common/global'); +const program = require('commander'); +const moment = require('moment'); const yaml = require('js-yaml'); const fs = require('fs'); @@ -151,6 +153,23 @@ program await permission_manager.executeApp(); }); + program + .command('time_travel') + .alias('tt') + .option('-p, --period ', 'Period of time in seconds to increase') + .option('-d, --toDate ', 'Human readable date ("MM/DD/YY [HH:mm:ss]") to travel to') + .option('-e, --toEpochTime ', 'Unix Epoch time to travel to') + .description('Increases time on EVM according to given value.') + .action(async function (cmd) { + await gbl.initialize(program.remoteNode); + if (cmd.period) { + await time.increaseTimeByDuration(parseInt(cmd.period)); + } else if (cmd.toDate) { + await time.increaseTimeToDate(cmd.toDate); + } else if (cmd.toEpochTime) { + await time.increaseTimeToEpochDate(cmd.toEpochTime); + } + }); program.parse(process.argv); if (typeof program.commands.length == 0) { diff --git a/contracts/modules/TransferManager/CountTransferManager.sol b/contracts/modules/TransferManager/CountTransferManager.sol index b54870251..86a537ebe 100644 --- a/contracts/modules/TransferManager/CountTransferManager.sol +++ b/contracts/modules/TransferManager/CountTransferManager.sol @@ -26,12 +26,14 @@ contract CountTransferManager is ITransferManager { } /** @notice Used to verify the transfer transaction and prevent a transfer if it passes the allowed amount of token holders + * @param _from Address of the sender * @param _to Address of the receiver + * @param _amount Amount to send */ function verifyTransfer( - address /* _from */, + address _from, address _to, - uint256 /* _amount */, + uint256 _amount, bytes /* _data */, bool /* _isTransfer */ ) @@ -41,7 +43,7 @@ contract CountTransferManager is ITransferManager { if (!paused) { if (maxHolderCount < ISecurityToken(securityToken).getInvestorCount()) { // Allow transfers to existing maxHolders - if (ISecurityToken(securityToken).balanceOf(_to) != 0) { + if (ISecurityToken(securityToken).balanceOf(_to) != 0 || ISecurityToken(securityToken).balanceOf(_from) == _amount) { return Result.NA; } return Result.INVALID; diff --git a/contracts/modules/TransferManager/CountTransferManagerFactory.sol b/contracts/modules/TransferManager/CountTransferManagerFactory.sol index d9b1328f7..c42cebc61 100644 --- a/contracts/modules/TransferManager/CountTransferManagerFactory.sol +++ b/contracts/modules/TransferManager/CountTransferManagerFactory.sol @@ -16,7 +16,7 @@ contract CountTransferManagerFactory is ModuleFactory { constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) { - version = "1.0.0"; + version = "2.1.0"; name = "CountTransferManager"; title = "Count Transfer Manager"; description = "Restrict the number of investors"; diff --git a/test/d_count_transfer_manager.js b/test/d_count_transfer_manager.js index 1a1e7717a..3107c4bf7 100644 --- a/test/d_count_transfer_manager.js +++ b/test/d_count_transfer_manager.js @@ -39,7 +39,9 @@ contract("CountTransferManager", accounts => { let I_CountTransferManagerFactory; let I_GeneralPermissionManager; let I_CountTransferManager; + let I_CountTransferManager2; let I_GeneralTransferManager; + let I_GeneralTransferManager2; let I_ExchangeTransferManager; let I_ModuleRegistry; let I_ModuleRegistryProxy; @@ -49,12 +51,14 @@ contract("CountTransferManager", accounts => { let I_SecurityTokenRegistry; let I_STFactory; let I_SecurityToken; + let I_SecurityToken2; let I_PolyToken; let I_PolymathRegistry; // SecurityToken Details const name = "Team"; const symbol = "sap"; + const symbol2 = "sapp"; const tokenDetails = "This is equity type of issuance"; const decimals = 18; const contact = "team@polymath.network"; @@ -81,6 +85,7 @@ contract("CountTransferManager", accounts => { account_investor1 = accounts[7]; account_investor2 = accounts[8]; account_investor3 = accounts[9]; + account_investor4 = accounts[6]; // Step 1: Deploy the genral PM ecosystem let instances = await setUpPolymathNetwork(account_polymath, token_owner); @@ -347,6 +352,108 @@ contract("CountTransferManager", accounts => { let perm = await I_CountTransferManager.getPermissions.call(); assert.equal(perm.length, 1); }); + describe("Test cases for adding and removing acc holder at the same time", async () => { + it("deploy a new token & auto attach modules", async () => { + + //register ticker and deploy token + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol2, contact, { from: token_owner }); + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let _blockNo = latestBlock(); + let tx2 = await I_STRProxied.generateSecurityToken(name, symbol2, tokenDetails, false, { from: token_owner }); + + I_SecurityToken2 = SecurityToken.at(tx2.logs[1].args._securityTokenAddress); + let moduleData = (await I_SecurityToken2.getModulesByType(2))[0]; + I_GeneralTransferManager2 = GeneralTransferManager.at(moduleData); + }); + it("add 3 holders to the token", async () => { + await I_GeneralTransferManager2.modifyWhitelist( + account_investor1, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer, + gas: 500000 + } + ); + await I_GeneralTransferManager2.modifyWhitelist( + account_investor2, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer, + gas: 500000 + } + ); + await I_GeneralTransferManager2.modifyWhitelist( + account_investor3, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer, + gas: 500000 + } + ); + + await I_GeneralTransferManager2.modifyWhitelist( + account_investor4, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer, + gas: 500000 + } + ); + + // Jump time + await increaseTime(5000); + // Add 3 holders to the token + await I_SecurityToken2.mint(account_investor1, web3.utils.toWei("1", "ether"), { from: token_owner }); + await I_SecurityToken2.mint(account_investor2, web3.utils.toWei("1", "ether"), { from: token_owner }); + await I_SecurityToken2.mint(account_investor3, web3.utils.toWei("1", "ether"), { from: token_owner }); + assert.equal((await I_SecurityToken2.balanceOf(account_investor1)).toNumber(), web3.utils.toWei("1", "ether")); + }); + it("Should intialize the auto attached modules", async () => { + let moduleData = (await I_SecurityToken2.getModulesByType(2))[0]; + I_GeneralTransferManager2 = GeneralTransferManager.at(moduleData); + }); + it("Should successfully attach the CountTransferManager factory with the security token", async () => { + await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); + await catchRevert( + I_SecurityToken2.addModule(P_CountTransferManagerFactory.address, bytesSTO, web3.utils.toWei("500", "ether"), 0, { + from: token_owner + }) + ); + }); + it("Should successfully attach the CountTransferManager with the security token and set max holder to 2", async () => { + const tx = await I_SecurityToken2.addModule(I_CountTransferManagerFactory.address, bytesSTO, 0, 0, { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "CountTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name).replace(/\u0000/g, ""), + "CountTransferManager", + "CountTransferManager module was not added" + ); + I_CountTransferManager2 = CountTransferManager.at(tx.logs[2].args._module); + await I_CountTransferManager2.changeHolderCount(2, {from: token_owner}); + console.log('current max holder number is '+ await I_CountTransferManager2.maxHolderCount({from: token_owner})); + }); + it("Should allow add a new token holder while transfer all the tokens at one go", async () => { + let amount = (await I_SecurityToken2.balanceOf(account_investor2)).toNumber(); + let investorCount = await I_SecurityToken2.getInvestorCount({from: account_investor2 }); + console.log("current investor count is " + investorCount); + await I_SecurityToken2.transfer(account_investor4, amount, { from: account_investor2 }); + assert((await I_SecurityToken2.balanceOf(account_investor4)).toNumber(), amount, {from: account_investor2 }); + assert(await I_SecurityToken2.getInvestorCount({from: account_investor2 }), investorCount); + }); + }); describe("Test cases for the factory", async () => { it("should get the exact details of the factory", async () => { From 57bde3a81ba6fd4753f3b3f0da7fdbe1764b80bc Mon Sep 17 00:00:00 2001 From: satyam Date: Mon, 10 Dec 2018 11:32:35 +0530 Subject: [PATCH 35/56] minor fix in test case --- test/y_volume_restriction_tm.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/y_volume_restriction_tm.js b/test/y_volume_restriction_tm.js index cfd955a91..260df3ef4 100644 --- a/test/y_volume_restriction_tm.js +++ b/test/y_volume_restriction_tm.js @@ -1433,7 +1433,7 @@ contract('VolumeRestrictionTransferManager', accounts => { let rollingPeriod = (await I_VolumeRestrictionTM.defaultRestriction.call())[2].toNumber(); //sell tokens upto the limit await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("2"), {from: account_investor3}); - tempArray3[tempArray3.length -1] += 2; + tempArray3.push(2); let data = await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor3); await print(data, account_investor3); @@ -1445,8 +1445,8 @@ contract('VolumeRestrictionTransferManager', accounts => { // Verify the storage changes assert.equal(data[0].toNumber(), startTime + duration.days(data[2].toNumber())); assert.equal(data[1].dividedBy(new BigNumber(10).pow(18)).toNumber(), await calculateSum(rollingPeriod, tempArray3)); - assert.equal(data[2].toNumber(), 5); - assert.equal(data[3].toNumber(), startTimedaily); + assert.equal(data[2].toNumber(), 6); + assert.equal(data[3].toNumber(), startTimedaily + duration.days(1)); assert.equal(amt, 2); }); }) From 74c3c4fc0a59b9b87e3bca35c38b291c2b679e81 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Mon, 10 Dec 2018 13:54:36 +0530 Subject: [PATCH 36/56] Increase Travis test timeout to 25 minutes --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2265b2d88..520c82130 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ jobs: - stage: Tests and Coverage after_install: wget -O node_modules/solidity-coverage/lib/app.js https://raw.githubusercontent.com/maxsam4/solidity-coverage/relative-path/lib/app.js before_script: truffle version - script: npm run test + script: travis_wait 25 npm run test - stage: Docs env: 'TASK=docs' before_install: @@ -24,4 +24,4 @@ jobs: script: npm run docs notifications: slack: - secure: W4FZSabLrzF74f317hutolEHnlq2GBlQxU6b85L5XymrjgLEhlgE16c5Qz7Emoyt6le6PXL+sfG2ujJc3XYys/6hppgrHSAasuJnKCdQNpmMZ9BNyMs6WGkmB3enIf3K/FLXb26AQdwpQdIXuOeJUTf879u+YoiZV0eZH8d3+fsIOyovq9N6X5pKOpDM9iT8gGB4t7fie7xf51s+iUaHxyO9G7jDginZ4rBXHcU7mxCub9z+Z1H8+kCTnPWaF+KKVEXx4Z0nI3+urboD7E4OIP02LwrThQls2CppA3X0EoesTcdvj/HLErY/JvsXIFiFEEHZzB1Wi+k2TiOeLcYwEuHIVij+HPxxlJNX/j8uy01Uk8s4rd+0EhvfdKHJqUKqxH4YN2npcKfHEss7bU3y7dUinXQfYShW5ZewHdvc7pnnxBTfhvmdi64HdNrXAPq+s1rhciH7MmnU+tsm4lhrpr+FBuHzUMA9fOCr7b0SQytZEgWpiUls88gdbh3yG8TjyZxmZJGx09cwEP0q7VoH0UwFh7mIu5XmYdd5tWUhavTiO7YV8cUPn7MvwMsTltB3YBpF/fB26L7ka8zBhCsjm9prW6SVYU/dyO3m91VeZtO/zJFHRDA6Q58JGVW2rgzO39z193qC1EGRXqTie96VwAAtNg8+hRb+bI/CWDVzSPc= \ No newline at end of file + secure: W4FZSabLrzF74f317hutolEHnlq2GBlQxU6b85L5XymrjgLEhlgE16c5Qz7Emoyt6le6PXL+sfG2ujJc3XYys/6hppgrHSAasuJnKCdQNpmMZ9BNyMs6WGkmB3enIf3K/FLXb26AQdwpQdIXuOeJUTf879u+YoiZV0eZH8d3+fsIOyovq9N6X5pKOpDM9iT8gGB4t7fie7xf51s+iUaHxyO9G7jDginZ4rBXHcU7mxCub9z+Z1H8+kCTnPWaF+KKVEXx4Z0nI3+urboD7E4OIP02LwrThQls2CppA3X0EoesTcdvj/HLErY/JvsXIFiFEEHZzB1Wi+k2TiOeLcYwEuHIVij+HPxxlJNX/j8uy01Uk8s4rd+0EhvfdKHJqUKqxH4YN2npcKfHEss7bU3y7dUinXQfYShW5ZewHdvc7pnnxBTfhvmdi64HdNrXAPq+s1rhciH7MmnU+tsm4lhrpr+FBuHzUMA9fOCr7b0SQytZEgWpiUls88gdbh3yG8TjyZxmZJGx09cwEP0q7VoH0UwFh7mIu5XmYdd5tWUhavTiO7YV8cUPn7MvwMsTltB3YBpF/fB26L7ka8zBhCsjm9prW6SVYU/dyO3m91VeZtO/zJFHRDA6Q58JGVW2rgzO39z193qC1EGRXqTie96VwAAtNg8+hRb+bI/CWDVzSPc= From e44af3d7c6e487bb3db273d24379803d8946dd63 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Mon, 10 Dec 2018 14:17:17 +0530 Subject: [PATCH 37/56] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 520c82130..91a00c5ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ jobs: - stage: Tests and Coverage after_install: wget -O node_modules/solidity-coverage/lib/app.js https://raw.githubusercontent.com/maxsam4/solidity-coverage/relative-path/lib/app.js before_script: truffle version - script: travis_wait 25 npm run test + script: travis_wait 120 sleep infinity & npm run test - stage: Docs env: 'TASK=docs' before_install: From e5799470f3a5e64e272f779202048b27c7238711 Mon Sep 17 00:00:00 2001 From: satyam Date: Mon, 10 Dec 2018 16:56:35 +0530 Subject: [PATCH 38/56] minor fix in the naming convention --- .../TransferManager/VolumeRestrictionTM.sol | 34 +++++++++---------- .../VolumeRestrictionTMStorage.sol | 4 +-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index e525d81b8..fff5f71f4 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -440,9 +440,9 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { require(_holder != address(0), "Invalid address"); require(individualRestriction[_holder].endTime != 0, "Not present"); individualRestriction[_holder] = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); - bucketToUser[_holder].lastTradedDayTime = 0; - bucketToUser[_holder].sumOfLastPeriod = 0; - bucketToUser[_holder].daysCovered = 0; + userToBucket[_holder].lastTradedDayTime = 0; + userToBucket[_holder].sumOfLastPeriod = 0; + userToBucket[_holder].daysCovered = 0; emit IndividualRestrictionRemoved(_holder); } @@ -469,7 +469,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { require(_holder != address(0), "Invalid address"); require(individualDailyRestriction[_holder].endTime != 0, "Not present"); individualDailyRestriction[_holder] = VolumeRestriction(0, 0, 0, 0, RestrictionType(0)); - bucketToUser[_holder].dailyLastTradedDayTime = 0; + userToBucket[_holder].dailyLastTradedDayTime = 0; emit IndividualDailyRestrictionRemoved(_holder); } @@ -612,7 +612,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { } _checkInputParams(_allowedTokens, startTime, 1, _endTime, _restrictionType); // Initializing the value with 0 to allow the trade with new startTime of the restriction - bucketToUser[_holder].dailyLastTradedDayTime = 0; + userToBucket[_holder].dailyLastTradedDayTime = 0; individualDailyRestriction[_holder] = VolumeRestriction( _allowedTokens, startTime, @@ -787,7 +787,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { */ function _defaultRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { // using the variable to avoid stack too deep error - BucketDetails memory bucketDetails = defaultBucketToUser[_from]; + BucketDetails memory bucketDetails = defaultUserToBucket[_from]; uint256 daysCovered = defaultRestriction.rollingPeriodInDays; uint256 fromTimestamp = 0; uint256 sumOfLastPeriod = 0; @@ -840,7 +840,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { */ function _individualRestrictionCheck(address _from, uint256 _amount, bool _isTransfer) internal returns (Result) { // using the variable to avoid stack too deep error - BucketDetails memory bucketDetails = bucketToUser[_from]; + BucketDetails memory bucketDetails = userToBucket[_from]; VolumeRestriction memory dailyRestriction = individualDailyRestriction[_from]; VolumeRestriction memory restriction = individualRestriction[_from]; uint256 daysCovered = individualRestriction[_from].rollingPeriodInDays; @@ -992,9 +992,9 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { { BucketDetails storage details; if (isDefault) - details = defaultBucketToUser[_from]; + details = defaultUserToBucket[_from]; else - details = bucketToUser[_from]; + details = userToBucket[_from]; // Cheap storage technique if (details.lastTradedDayTime != _lastTradedDayTime) { // Assigning the latest transaction timestamp of the day @@ -1074,10 +1074,10 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { */ function getIndividualBucketDetailsToUser(address _user) external view returns(uint256, uint256, uint256, uint256) { return( - bucketToUser[_user].lastTradedDayTime, - bucketToUser[_user].sumOfLastPeriod, - bucketToUser[_user].daysCovered, - bucketToUser[_user].dailyLastTradedDayTime + userToBucket[_user].lastTradedDayTime, + userToBucket[_user].sumOfLastPeriod, + userToBucket[_user].daysCovered, + userToBucket[_user].dailyLastTradedDayTime ); } @@ -1091,10 +1091,10 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { */ function getDefaultBucketDetailsToUser(address _user) external view returns(uint256, uint256, uint256, uint256) { return( - defaultBucketToUser[_user].lastTradedDayTime, - defaultBucketToUser[_user].sumOfLastPeriod, - defaultBucketToUser[_user].daysCovered, - defaultBucketToUser[_user].dailyLastTradedDayTime + defaultUserToBucket[_user].lastTradedDayTime, + defaultUserToBucket[_user].sumOfLastPeriod, + defaultUserToBucket[_user].daysCovered, + defaultUserToBucket[_user].dailyLastTradedDayTime ); } diff --git a/contracts/modules/TransferManager/VolumeRestrictionTMStorage.sol b/contracts/modules/TransferManager/VolumeRestrictionTMStorage.sol index 9e614213a..e995676a0 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTMStorage.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTMStorage.sol @@ -36,9 +36,9 @@ contract VolumeRestrictionTMStorage { // Storing _from => day's timestamp => total amount transact in a day --individual mapping(address => mapping(uint256 => uint256)) internal bucket; // Storing the information that used to validate the transaction - mapping(address => BucketDetails) internal bucketToUser; + mapping(address => BucketDetails) internal userToBucket; // Storing the information related to default restriction - mapping(address => BucketDetails) internal defaultBucketToUser; + mapping(address => BucketDetails) internal defaultUserToBucket; // List of wallets that are exempted from all the restrictions applied by the this contract mapping(address => bool) public exemptList; From 216d3206bf8f16b134337679c948cc8f3d79084c Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Wed, 12 Dec 2018 16:22:20 +0530 Subject: [PATCH 39/56] Added code comments --- .../TransferManager/VolumeRestrictionTM.sol | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index fff5f71f4..aa6d9d311 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -943,9 +943,21 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { // the earlier value at that index if (counter >= _rollingPeriodInDays) { // Subtracting the former value(Sum of all the txn amount of that day) from the sumOfLastPeriod - sumOfLastPeriod = sumOfLastPeriod. - sub(bucket[_from][_bucketDetails.lastTradedDayTime. - sub((_bucketDetails.daysCovered.sub(counter.sub(_rollingPeriodInDays))).mul(1 days))]); + // The below line subtracts (the traded volume on days no longer covered by rolling period) from sumOfLastPeriod. + // Every loop execution subtracts one day's trade volume. + // Loop starts from the first day covered in sumOfLastPeriod upto the day that is covered by rolling period. + sumOfLastPeriod = + sumOfLastPeriod.sub( + bucket[_from][_bucketDetails.lastTradedDayTime.sub( + ( + _bucketDetails.daysCovered.sub( + counter.sub( + _rollingPeriodInDays + ) + ) + ).mul(1 days) + )] + ); } // Adding the last amount that is transacted on the `_fromTime` not actually doing it but left written to understand // the alogrithm From a0700ea124d5f5325081c54d7c2513d4c7d3b9ac Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Wed, 12 Dec 2018 17:02:05 +0530 Subject: [PATCH 40/56] Removed uninitialized pointer --- .../TransferManager/VolumeRestrictionTM.sol | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index aa6d9d311..f619f2427 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -1002,11 +1002,29 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { ) internal { - BucketDetails storage details; - if (isDefault) - details = defaultUserToBucket[_from]; - else - details = userToBucket[_from]; + + if (isDefault){ + BucketDetails storage defaultUserToBucketDetails = defaultUserToBucket[_from]; + _updateStorageActual(_from, _amount, _lastTradedDayTime, _sumOfLastPeriod, _daysCovered, _dailyLastTradedDayTime, _endTime, defaultUserToBucketDetails); + } + else { + BucketDetails storage userToBucketDetails = userToBucket[_from]; + _updateStorageActual(_from, _amount, _lastTradedDayTime, _sumOfLastPeriod, _daysCovered, _dailyLastTradedDayTime, _endTime, userToBucketDetails); + } + } + + function _updateStorageActual( + address _from, + uint256 _amount, + uint256 _lastTradedDayTime, + uint256 _sumOfLastPeriod, + uint256 _daysCovered, + uint256 _dailyLastTradedDayTime, + uint256 _endTime, + BucketDetails storage details + ) + internal + { // Cheap storage technique if (details.lastTradedDayTime != _lastTradedDayTime) { // Assigning the latest transaction timestamp of the day From 34efb95ae345889edd69242a8ff0e90923fd94f5 Mon Sep 17 00:00:00 2001 From: satyam Date: Wed, 12 Dec 2018 19:33:05 +0530 Subject: [PATCH 41/56] trying by adding some buffer in time travel --- test/y_volume_restriction_tm.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/y_volume_restriction_tm.js b/test/y_volume_restriction_tm.js index 260df3ef4..f84ef101c 100644 --- a/test/y_volume_restriction_tm.js +++ b/test/y_volume_restriction_tm.js @@ -1278,7 +1278,7 @@ contract('VolumeRestrictionTransferManager', accounts => { }); it("Should transfer the tokens freely after ending the default daily restriction", async() => { - await increaseTime(duration.days(3)); + await increaseTime(duration.days(3) + 10); //sell tokens upto the limit let tx = await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("5"), {from: account_investor4}); assert.equal((tx.logs[0].args.value).toNumber(), web3.utils.toWei("5")); @@ -1313,6 +1313,7 @@ contract('VolumeRestrictionTransferManager', accounts => { }); it("Should transfer tokens on by investor 3 (comes under the Default restriction)", async() => { + await increaseTime(10); tempArray3.length = 0; let startTime = (await I_VolumeRestrictionTM.defaultRestriction.call())[1].toNumber(); let startTimedaily = (await I_VolumeRestrictionTM.defaultDailyRestriction.call())[1].toNumber(); @@ -1336,7 +1337,6 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.equal(amt, 5); // Transfer tokens on another day - await increaseTime(duration.days(1)); //sell tokens upto the limit await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("3"), {from: account_investor3}); @@ -1365,7 +1365,7 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should able to transfer tokens in the next rolling period", async() => { await increaseTime(duration.days(4.1)); - console.log(`Diff days: ${(latestTime() - ((await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor3))[0]).toNumber()) / 86400}`) + console.log(`*** Diff days: ${(latestTime() - ((await I_VolumeRestrictionTM.getDefaultBucketDetailsToUser.call(account_investor3))[0]).toNumber()) / 86400}`) for (let i = 0; i < 3; i++) { tempArray3.push(0); } From bc0b62ebb7f0ede95ba20d1a8e6a2ac7203c66a6 Mon Sep 17 00:00:00 2001 From: satyam Date: Thu, 13 Dec 2018 10:58:25 +0530 Subject: [PATCH 42/56] modify function changes --- .../TransferManager/VolumeRestrictionTM.sol | 62 ++++++++++++------- test/y_volume_restriction_tm.js | 40 ++++++------ 2 files changed, 60 insertions(+), 42 deletions(-) diff --git a/contracts/modules/TransferManager/VolumeRestrictionTM.sol b/contracts/modules/TransferManager/VolumeRestrictionTM.sol index f619f2427..c452b4663 100644 --- a/contracts/modules/TransferManager/VolumeRestrictionTM.sol +++ b/contracts/modules/TransferManager/VolumeRestrictionTM.sol @@ -15,7 +15,16 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { // Emit when the token holder is added/removed from the exemption list event ChangedExemptWalletList(address indexed _wallet, bool _change); // Emit when the new individual restriction is added corresponds to new token holders - event AddNewIndividualRestriction( + event AddIndividualRestriction( + address indexed _holder, + uint256 _allowedTokens, + uint256 _startTime, + uint256 _rollingPeriodInDays, + uint256 _endTime, + uint256 _typeOfRestriction + ); + // Emit when the new daily (Individual) restriction is added + event AddIndividualDailyRestriction( address indexed _holder, uint256 _allowedTokens, uint256 _startTime, @@ -32,25 +41,25 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { uint256 _endTime, uint256 _typeOfRestriction ); - // Emit when the new global restriction is added - event AddDefaultRestriction( + // Emit when individual daily restriction get modified + event ModifyIndividualDailyRestriction( + address indexed _holder, uint256 _allowedTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, uint256 _typeOfRestriction ); - // Emit when the new daily (Default) restriction is added - event AddDefaultDailyRestriction( + // Emit when the new global restriction is added + event AddDefaultRestriction( uint256 _allowedTokens, uint256 _startTime, uint256 _rollingPeriodInDays, uint256 _endTime, uint256 _typeOfRestriction ); - // Emit when the new daily (Individual) restriction is added - event AddIndividualDailyRestriction( - address _holder, + // Emit when the new daily (Default) restriction is added + event AddDefaultDailyRestriction( uint256 _allowedTokens, uint256 _startTime, uint256 _rollingPeriodInDays, @@ -74,9 +83,9 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { uint256 _typeOfRestriction ); // Emit when the individual restriction gets removed - event IndividualRestrictionRemoved(address _holder); + event IndividualRestrictionRemoved(address indexed _holder); // Emit when individual daily restriction removed - event IndividualDailyRestrictionRemoved(address _holder); + event IndividualDailyRestrictionRemoved(address indexed _holder); // Emit when the default restriction gets removed event DefaultRestrictionRemoved(); // Emit when the daily default restriction gets removed @@ -196,7 +205,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { _endTime, RestrictionType(_restrictionType) ); - emit AddNewIndividualRestriction( + emit AddIndividualRestriction( _holder, _allowedTokens, startTime, @@ -568,8 +577,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { /** * @notice Use to modify the existing individual daily restriction for a given token holder - * @dev It is possible to intialize the dailyLatestTradedTime by 0 for the given holder then - * changing of startTime will affect the 24 hrs span. i.e if in earlier restriction days start with + * @dev Changing of startTime will affect the 24 hrs span. i.e if in earlier restriction days start with * morning and end on midnight while after the change day may start with afternoon and end with other day afternoon * @param _holder Address of the token holder, whom restriction will be implied * @param _allowedTokens Amount of tokens allowed to be trade for a given address. @@ -611,8 +619,12 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { startTime = now; } _checkInputParams(_allowedTokens, startTime, 1, _endTime, _restrictionType); - // Initializing the value with 0 to allow the trade with new startTime of the restriction - userToBucket[_holder].dailyLastTradedDayTime = 0; + // If old startTime is already passed then new startTime should be greater than or equal to the + // old startTime otherwise any past startTime can be allowed in compare to earlier startTime. + if (individualDailyRestriction[_holder].startTime <= now) + require(startTime >= individualDailyRestriction[_holder].startTime, "Invalid StartTime"); + else + require(startTime >= now); individualDailyRestriction[_holder] = VolumeRestriction( _allowedTokens, startTime, @@ -620,7 +632,8 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { _endTime, RestrictionType(_restrictionType) ); - emit ModifyDefaultDailyRestriction( + emit ModifyIndividualDailyRestriction( + _holder, _allowedTokens, startTime, 1, @@ -743,9 +756,8 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { /** * @notice Use to modify the daily default restriction for all token holder - * @dev If the start time is modified then it only affect the startTime of the restriction - * while it may carries the earlier dailyLatestStartDayTime value. Not possible to initialize the value - * of dailyLatestStartDayTime for every holder to 0. + * @dev Changing of startTime will affect the 24 hrs span. i.e if in earlier restriction days start with + * morning and end on midnight while after the change day may start with afternoon and end with other day afternoon. * @param _allowedTokens Amount of tokens allowed to be traded for all token holder. * @param _startTime Unix timestamp at which restriction get into effect * @param _endTime Unix timestamp at which restriction effects will gets end. @@ -763,8 +775,14 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { uint256 startTime = _startTime; if (_startTime == 0) { startTime = now; - } + } _checkInputParams(_allowedTokens, startTime, 1, _endTime, _restrictionType); + // If old startTime is already passed then new startTime should be greater than or equal to the + // old startTime otherwise any past startTime can be allowed in compare to earlier startTime. + if (defaultDailyRestriction.startTime <= now) + require(startTime >= defaultDailyRestriction.startTime, "Invalid StartTime"); + else + require(startTime >= now); defaultDailyRestriction = VolumeRestriction( _allowedTokens, startTime, @@ -904,7 +922,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { // the total amount get traded on a particular day (~ _fromTime) if ( now <= restriction.endTime && now >= restriction.startTime) { uint256 txSumOfDay = 0; - if (dailyLastTradedDayTime == 0) + if (dailyLastTradedDayTime == 0 || dailyLastTradedDayTime < restriction.startTime) // This if condition will be executed when the individual daily restriction executed first time dailyLastTradedDayTime = restriction.startTime.add(BokkyPooBahsDateTimeLibrary.diffDays(restriction.startTime, now).mul(1 days)); else if (now.sub(dailyLastTradedDayTime) >= 1 days) @@ -1133,7 +1151,7 @@ contract VolumeRestrictionTM is VolumeRestrictionTMStorage, ITransferManager { * @param _user Address of the token holder * @param _at Timestamp */ - function getTotalTradeByuser(address _user, uint256 _at) external view returns(uint256) { + function getTotalTradedByUser(address _user, uint256 _at) external view returns(uint256) { return bucket[_user][_at]; } diff --git a/test/y_volume_restriction_tm.js b/test/y_volume_restriction_tm.js index f84ef101c..1075a64e4 100644 --- a/test/y_volume_restriction_tm.js +++ b/test/y_volume_restriction_tm.js @@ -82,9 +82,9 @@ contract('VolumeRestrictionTransferManager', accounts => { SumOfLastPeriod: ${data[1].dividedBy(new BigNumber(10).pow(18)).toNumber()} Days Covered: ${data[2].toNumber()} Latest timestamp daily: ${data[3].toNumber()} - Individual Total Trade on latestTimestamp : ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account, data[0])) + Individual Total Trade on latestTimestamp : ${(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account, data[0])) .dividedBy(new BigNumber(10).pow(18)).toNumber()} - Individual Total Trade on daily latestTimestamp : ${(await I_VolumeRestrictionTM.getTotalTradeByuser.call(account, data[3])) + Individual Total Trade on daily latestTimestamp : ${(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account, data[3])) .dividedBy(new BigNumber(10).pow(18)).toNumber()} `) } @@ -630,7 +630,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor1); assert.equal( - (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0])) + (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor1, data[0])) .dividedBy(new BigNumber(10).pow(18)).toNumber(), 0.3 ); @@ -751,7 +751,7 @@ contract('VolumeRestrictionTransferManager', accounts => { assert.equal(newData[3].toNumber(), data[3].toNumber()); assert.equal(data[3].toNumber(), startTime); - assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[3])) + assert.equal((await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[3])) .dividedBy(new BigNumber(10).pow(18)).toNumber(), 6); }); @@ -774,7 +774,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); assert.equal(data[3].toNumber(), startTime + duration.days(1)); - assert.equal((await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[3])) + assert.equal((await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[3])) .dividedBy(new BigNumber(10).pow(18)).toNumber(), 2); }); @@ -823,7 +823,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor1); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor1, data[0].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor1, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -881,7 +881,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -920,7 +920,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -970,7 +970,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -1036,7 +1036,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -1061,7 +1061,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -1093,7 +1093,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -1132,7 +1132,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -1161,7 +1161,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -1192,7 +1192,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -1266,7 +1266,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor4, data[3].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor4, data[3].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -1326,7 +1326,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -1346,7 +1346,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -1382,7 +1382,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes @@ -1439,7 +1439,7 @@ contract('VolumeRestrictionTransferManager', accounts => { await print(data, account_investor3); // get the trade amount using the timestamp - let amt = (await I_VolumeRestrictionTM.getTotalTradeByuser.call(account_investor3, data[0].toNumber())) + let amt = (await I_VolumeRestrictionTM.getTotalTradedByUser.call(account_investor3, data[0].toNumber())) .dividedBy(new BigNumber(10).pow(18)).toNumber(); // Verify the storage changes From 62d4bb5e2430a8d0698389686eed24d2cc8da76e Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 12 Dec 2018 16:31:26 -0300 Subject: [PATCH 43/56] CLI - WIP VolumeRestrictionTM support --- CLI/commands/helpers/contract_abis.js | 5 + CLI/commands/transfer_manager.js | 339 +++++++++++++++++++++++++- 2 files changed, 338 insertions(+), 6 deletions(-) diff --git a/CLI/commands/helpers/contract_abis.js b/CLI/commands/helpers/contract_abis.js index 8a551b70b..86b42cea7 100644 --- a/CLI/commands/helpers/contract_abis.js +++ b/CLI/commands/helpers/contract_abis.js @@ -10,6 +10,7 @@ let generalTransferManagerABI; let manualApprovalTransferManagerABI; let countTransferManagerABI; let percentageTransferManagerABI; +let volumeRestrictionTMABI; let generalPermissionManagerABI; let polyTokenABI; let cappedSTOFactoryABI; @@ -35,6 +36,7 @@ try { manualApprovalTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/ManualApprovalTransferManager.json').toString()).abi; countTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/CountTransferManager.json').toString()).abi; percentageTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/PercentageTransferManager.json').toString()).abi; + volumeRestrictionTMABI = JSON.parse(require('fs').readFileSync('./build/contracts/VolumeRestrictionTM.json').toString()).abi; generalPermissionManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/GeneralPermissionManager.json').toString()).abi; polyTokenABI = JSON.parse(require('fs').readFileSync('./build/contracts/PolyTokenFaucet.json').toString()).abi; cappedSTOFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/CappedSTOFactory.json').toString()).abi; @@ -88,6 +90,9 @@ module.exports = { percentageTransferManager: function () { return percentageTransferManagerABI; }, + volumeRestrictionTM: function () { + return volumeRestrictionTMABI; + }, generalPermissionManager: function () { return generalPermissionManagerABI; }, diff --git a/CLI/commands/transfer_manager.js b/CLI/commands/transfer_manager.js index 16af0e34e..45a253025 100644 --- a/CLI/commands/transfer_manager.js +++ b/CLI/commands/transfer_manager.js @@ -13,6 +13,8 @@ const { table } = require('table') const WHITELIST_DATA_CSV = './CLI/data/Transfer/GTM/whitelist_data.csv'; const PERCENTAGE_WHITELIST_DATA_CSV = './CLI/data/Transfer/PercentageTM/whitelist_data.csv'; +const RESTRICTION_TYPES = ['Fixed', 'Percentage']; + // App flow let tokenSymbol; let securityToken; @@ -211,6 +213,11 @@ async function configExistingModules(tmModules) { currentTransferManager.setProvider(web3.currentProvider); await percentageTransferManager(); break; + case 'VolumeRestrictionTM': + currentTransferManager = new web3.eth.Contract(abis.volumeRestrictionTM(), tmModules[index].address); + currentTransferManager.setProvider(web3.currentProvider); + await volumeRestrictionTM(); + break; case 'SingleTradeVolumeRestrictionTM': //currentTransferManager = new web3.eth.Contract(abis.singleTradeVolumeRestrictionTM(), tmModules[index].address); //currentTransferManager.setProvider(web3.currentProvider); @@ -412,10 +419,10 @@ async function generalTransferManager() { limitMessage: "Must be a valid address" }); let now = Math.floor(Date.now() / 1000); - let fromTime = readlineSync.questionInt(`Enter the time(Unix Epoch time) when the sale lockup period ends and the investor can freely sell his tokens(now = ${now}): `, { defaultInput: now }); - let toTime = readlineSync.questionInt(`Enter the time(Unix Epoch time) when the purchase lockup period ends and the investor can freely purchase tokens from others(now = ${now}): `, { defaultInput: now }); + let fromTime = readlineSync.questionInt(`Enter the time (Unix Epoch time) when the sale lockup period ends and the investor can freely sell his tokens (now = ${now}): `, { defaultInput: now }); + let toTime = readlineSync.questionInt(`Enter the time (Unix Epoch time) when the purchase lockup period ends and the investor can freely purchase tokens from others (now = ${now}): `, { defaultInput: now }); let oneHourFromNow = Math.floor(Date.now() / 1000 + 3600); - let expiryTime = readlineSync.questionInt(`Enter the time till investors KYC will be validated(after that investor need to do re - KYC) (1 hour from now = ${oneHourFromNow}): `, { defaultInput: oneHourFromNow }); + let expiryTime = readlineSync.questionInt(`Enter the time till investors KYC will be validated (after that investor need to do re - KYC) (1 hour from now = ${oneHourFromNow}): `, { defaultInput: oneHourFromNow }); let canBuyFromSTO = readlineSync.keyInYNStrict('Is the investor a restricted investor?'); let modifyWhitelistAction = currentTransferManager.methods.modifyWhitelist(investor, fromTime, toTime, expiryTime, canBuyFromSTO); let modifyWhitelistReceipt = await common.sendTransaction(modifyWhitelistAction); @@ -617,7 +624,7 @@ async function manualApprovalTransferManager() { if (!await getManualApproval(from, to)) { let allowance = readlineSync.question('Enter the amount of tokens which will be approved: '); let oneHourFromNow = Math.floor(Date.now() / 1000 + 3600); - let expiryTime = readlineSync.questionInt(`Enter the time(Unix Epoch time) until which the transfer is allowed(1 hour from now = ${oneHourFromNow}): `, { defaultInput: oneHourFromNow }); + let expiryTime = readlineSync.questionInt(`Enter the time (Unix Epoch time) until which the transfer is allowed (1 hour from now = ${oneHourFromNow}): `, { defaultInput: oneHourFromNow }); let addManualApprovalAction = currentTransferManager.methods.addManualApproval(from, to, web3.utils.toWei(allowance), expiryTime); let addManualApprovalReceipt = await common.sendTransaction(addManualApprovalAction); let addManualApprovalEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addManualApprovalReceipt.logs, 'AddManualApproval'); @@ -685,7 +692,7 @@ async function manualApprovalTransferManager() { }); if (!await getManualBlocking(from, to)) { let oneHourFromNow = Math.floor(Date.now() / 1000 + 3600); - let expiryTime = readlineSync.questionInt(`Enter the time(Unix Epoch time) until which the transfer is blocked(1 hour from now = ${oneHourFromNow}): `, { defaultInput: oneHourFromNow }); + let expiryTime = readlineSync.questionInt(`Enter the time (Unix Epoch time) until which the transfer is blocked (1 hour from now = ${oneHourFromNow}): `, { defaultInput: oneHourFromNow }); let addManualBlockingAction = currentTransferManager.methods.addManualBlocking(from, to, expiryTime); let addManualBlockingReceipt = await common.sendTransaction(addManualBlockingAction); let addManualBlockingEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addManualBlockingReceipt.logs, 'AddManualBlocking'); @@ -869,6 +876,326 @@ async function percentageTransferManager() { } } +async function volumeRestrictionTM() { + console.log('\n', chalk.blue(`Volume Restriction Transfer Manager at ${currentTransferManager.options.address}`, '\n')); + + let defaultDailyRestriction = await currentTransferManager.methods.defaultDailyRestriction().call(); + let hasDefaultDailyRestriction = parseInt(defaultDailyRestriction.startTime) !== 0; + let defaultRestriction = await currentTransferManager.methods.defaultRestriction().call(); + let hasDefaultRestriction = parseInt(defaultRestriction.startTime) !== 0; + + console.log(`- Default daily restriction: ${hasDefaultDailyRestriction ? '' : 'None'}`); + if (hasDefaultDailyRestriction) { + console.log(` Type: ${RESTRICTION_TYPES[defaultDailyRestriction.typeOfRestriction]}`); + console.log(` Allowed tokens: ${defaultDailyRestriction.typeOfRestriction === "0" ? `${web3.utils.fromWei(defaultDailyRestriction.allowedTokens)} ${tokenSymbol}` : `${fromWeiPercentage(defaultDailyRestriction.allowedTokens)}%`}`); + console.log(` Start time: ${moment.unix(defaultDailyRestriction.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(` Rolling period: ${defaultDailyRestriction.rollingPeriodInDays} days`); + console.log(` End time: ${moment.unix(defaultDailyRestriction.endTime).format('MMMM Do YYYY, HH:mm:ss')} `); + } + console.log(`- Default restriction: ${hasDefaultRestriction ? '' : 'None'} `); + if (hasDefaultRestriction) { + console.log(` Type: ${RESTRICTION_TYPES[defaultRestriction.typeOfRestriction]}`); + console.log(` Allowed tokens: ${defaultRestriction.typeOfRestriction === "0" ? `${web3.utils.fromWei(defaultRestriction.allowedTokens)} ${tokenSymbol}` : `${fromWeiPercentage(defaultRestriction.allowedTokens)}%`}`); + console.log(` Start time: ${moment.unix(defaultRestriction.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(` Rolling period: ${defaultRestriction.rollingPeriodInDays} days`); + console.log(` End time: ${moment.unix(defaultRestriction.endTime).format('MMMM Do YYYY, HH:mm:ss')} `); + } + + let options = [ + 'Change exempt wallet', + 'Change default restrictions', + 'Change individual restrictions', + 'Explore account', + 'Operate with multiple restrictions' + ]; + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Change exempt wallet': + await changeExemptWallet(); + break; + case 'Change default restrictions': + await changeDefaultRestrictions(hasDefaultDailyRestriction, hasDefaultRestriction); + break; + case 'Change individual restrictions': + await changeIndividualRestrictions(); + break; + case 'Explore account': + await exploreAccount(); + break; + case 'Operate with multiple restrictions': + await operateWithMultipleRestrictions(); + break; + case 'RETURN': + return; + } + + await volumeRestrictionTM(); +} + +async function changeExemptWallet() { + let options = [ + 'Add exempt wallet', + 'Remove exempt wallet' + ]; + + let change; + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Add exempt wallet': + change = true; + break; + case 'Remove exempt wallet': + change = false; + break; + case 'RETURN': + return; + } + + let wallet = readlineSync.question('Enter the wallet to change: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + let changeExemptWalletAction = currentTransferManager.methods.changeExemptWalletList(wallet, change); + let changeExemptWalletReceipt = await common.sendTransaction(changeExemptWalletAction); + let changeExemptWalletEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeExemptWalletReceipt.logs, 'ChangedExemptWalletList'); + console.log(chalk.green(`${changeExemptWalletEvent._wallet} has been ${changeExemptWalletEvent._change ? `added to` : `removed from`} exempt wallets successfully!`)); +} + +async function changeDefaultRestrictions(hasDefaultDailyRestriction, hasDefaultRestriction) { + let options = []; + if (!hasDefaultDailyRestriction) { + options.push('Add default daily restriction'); + } else { + options.push('Modify default daily restriction', 'Remove default daily restriction'); + } + + if (!hasDefaultRestriction) { + options.push('Add default restriction'); + } else { + options.push('Modify default restriction', 'Remove default restriction'); + } + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Add default daily restriction': + let defaultDailyRestrictoToAdd = inputRestrictionData(true); + let addDefaultDailyRestrictionAction = currentTransferManager.methods.addDefaultDailyRestriction( + defaultDailyRestrictoToAdd.allowedTokens, + defaultDailyRestrictoToAdd.startTime, + defaultDailyRestrictoToAdd.endTime, + defaultDailyRestrictoToAdd.restrictionType + ); + let addDefaultDailyRestrictionReceipt = await common.sendTransaction(addDefaultDailyRestrictionAction); + let addDefaultDailyRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addDefaultDailyRestrictionReceipt.logs, 'AddDefaultDailyRestriction'); + console.log(chalk.green(`Daily default restriction has been added successfully!`)); + break; + case 'Modify default daily restriction': + let defaultDailyRestrictoToModify = inputRestrictionData(true); + let modifyDefaultDailyRestrictionAction = currentTransferManager.methods.modifyDefaultDailyRestriction( + defaultDailyRestrictoToModify.allowedTokens, + defaultDailyRestrictoToModify.startTime, + defaultDailyRestrictoToModify.endTime, + defaultDailyRestrictoToModify.restrictionType + ); + let modifyDefaultDailyRestrictionReceipt = await common.sendTransaction(modifyDefaultDailyRestrictionAction); + let modifyDefaultDailyRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, modifyDefaultDailyRestrictionReceipt.logs, 'ModifyDefaultDailyRestriction'); + console.log(chalk.green(`Daily default restriction has been modified successfully!`)); + break; + case 'Remove default daily restriction': + let removeDefaultDailyRestrictionAction = currentTransferManager.methods.removeDefaultDailyRestriction(); + let removeDefaultDailyRestrictionReceipt = await common.sendTransaction(removeDefaultDailyRestrictionAction); + let removeDefaultDailyRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, removeDefaultDailyRestrictionReceipt.logs, 'DefaultDailyRestrictionRemoved'); + console.log(chalk.green(`Daily default restriction has been removed successfully!`)); + break; + case 'Add default restriction': + let defaultRestrictoToAdd = inputRestrictionData(false); + let addDefaultRestrictionAction = currentTransferManager.methods.addDefaultRestriction( + defaultRestrictoToAdd.allowedTokens, + defaultRestrictoToAdd.startTime, + defaultRestrictoToAdd.rollingPeriodInDays, + defaultRestrictoToAdd.endTime, + defaultRestrictoToAdd.restrictionType + ); + let addDefaultRestrictionReceipt = await common.sendTransaction(addDefaultRestrictionAction); + let addDefaultRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addDefaultRestrictionReceipt.logs, 'AddDefaultRestriction'); + console.log(chalk.green(`Default restriction has been added successfully!`)); + break; + case 'Modify default restriction': + let defaultRestrictoToModify = inputRestrictionData(false); + let modifyDefaultRestrictionAction = currentTransferManager.methods.modifyDefaultRestriction( + defaultRestrictoToModify.allowedTokens, + defaultRestrictoToModify.startTime, + defaultRestrictoToModify.rollingPeriodInDays, + defaultRestrictoToModify.endTime, + defaultRestrictoToModify.restrictionType + ); + let modifyDefaultRestrictionReceipt = await common.sendTransaction(modifyDefaultRestrictionAction); + let modifyDefaultRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, modifyDefaultRestrictionReceipt.logs, 'ModifyDefaultRestriction'); + console.log(chalk.green(`Default restriction has been modified successfully!`)); + break; + case 'Remove default restriction': + let removeDefaultRestrictionAction = currentTransferManager.methods.removeDefaultRestriction(); + let removeDefaultRestrictionReceipt = await common.sendTransaction(removeDefaultRestrictionAction); + let removeDefaultRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, removeDefaultRestrictionReceipt.logs, 'DefaultRestrictionRemoved'); + console.log(chalk.green(`Default restriction has been removed successfully!`)); + break; + } +} + +async function changeIndividualRestrictions() { + let holder = readlineSync.question('Enter the address of the token holder, whom restriction will be implied: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + + let currentDailyRestriction = await currentTransferManager.methods.individualDailyRestriction(holder).call(); + let hasDailyRestriction = parseInt(currentDailyRestriction.startTime) !== 0; + let currentRestriction = await currentTransferManager.methods.individualRestriction(holder).call(); + let hasRestriction = parseInt(currentRestriction.startTime) !== 0; + + console.log(`*** Current restrictions for ${holder} ***`, '\n'); + + console.log(`- Daily restriction: ${hasDailyRestriction ? '' : 'None'}`); + if (hasDailyRestriction) { + console.log(` Type: ${RESTRICTION_TYPES[currentDailyRestriction.typeOfRestriction]}`); + console.log(` Allowed tokens: ${currentDailyRestriction.typeOfRestriction === "0" ? `${web3.utils.fromWei(currentDailyRestriction.allowedTokens)} ${tokenSymbol}` : `${fromWeiPercentage(currentDailyRestriction.allowedTokens)}%`}`); + console.log(` Start time: ${moment.unix(currentDailyRestriction.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(` Rolling period: ${currentDailyRestriction.rollingPeriodInDays} days`); + console.log(` End time: ${moment.unix(currentDailyRestriction.endTime).format('MMMM Do YYYY, HH:mm:ss')} `); + } + console.log(`- Default daily restriction: ${hasRestriction ? '' : 'None'} `); + if (hasRestriction) { + console.log(` Type: ${RESTRICTION_TYPES[currentRestriction.typeOfRestriction]}`); + console.log(` Allowed tokens: ${currentRestriction.typeOfRestriction === "0" ? `${web3.utils.fromWei(currentRestriction.allowedTokens)} ${tokenSymbol}` : `${fromWeiPercentage(currentRestriction.allowedTokens)}%`}`); + console.log(` Start time: ${moment.unix(currentRestriction.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(` Rolling period: ${currentRestriction.rollingPeriodInDays} days`); + console.log(` End time: ${moment.unix(currentRestriction.endTime).format('MMMM Do YYYY, HH:mm:ss')} `); + } + + let options = []; + if (!hasDailyRestriction) { + options.push('Add individual daily restriction'); + } else { + options.push('Modify individual daily restriction', 'Remove individual daily restriction'); + } + + if (!hasRestriction) { + options.push('Add individual restriction'); + } else { + options.push('Modify individual restriction', 'Remove individual restriction'); + } + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Add individual daily restriction': + let dailyRestrictoToAdd = inputRestrictionData(true); + let addDailyRestrictionAction = currentTransferManager.methods.addIndividualDailyRestriction( + holder, + dailyRestrictoToAdd.allowedTokens, + dailyRestrictoToAdd.startTime, + dailyRestrictoToAdd.endTime, + dailyRestrictoToAdd.restrictionType + ); + let addDailyRestrictionReceipt = await common.sendTransaction(addDailyRestrictionAction); + let addDailyRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addDailyRestrictionReceipt.logs, 'AddIndividualDailyRestriction'); + console.log(chalk.green(`Daily restriction for ${addDailyRestrictionEvent._holder} has been added successfully!`)); + break; + case 'Modify individual daily restriction': + let dailyRestrictoToModify = inputRestrictionData(true); + let modifyDailyRestrictionAction = currentTransferManager.methods.modifyIndividualDailyRestriction( + holder, + dailyRestrictoToModify.allowedTokens, + dailyRestrictoToModify.startTime, + dailyRestrictoToModify.endTime, + dailyRestrictoToModify.restrictionType + ); + let modifyDailyRestrictionReceipt = await common.sendTransaction(modifyDailyRestrictionAction); + let modifyDailyRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, modifyDailyRestrictionReceipt.logs, 'ModifyIndividualDailyRestriction'); + console.log(chalk.green(`Daily restriction for ${modifyDailyRestrictionEvent._holder} has been modified successfully!`)); + break; + case 'Remove individual daily restriction': + let removeDailyRestrictionAction = currentTransferManager.methods.removeIndividualDailyRestriction(holder); + let removeDailyRestrictionReceipt = await common.sendTransaction(removeDailyRestrictionAction); + let removeDailyRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, removeDailyRestrictionReceipt.logs, 'IndividualDailyRestrictionRemoved'); + console.log(chalk.green(`Daily restriction for ${removeDailyRestrictionEvent._holder} has been removed successfully!`)); + break; + case 'Add individual restriction': + let restrictoToAdd = inputRestrictionData(false); + let addRestrictionAction = currentTransferManager.methods.addIndividualRestriction( + holder, + restrictoToAdd.allowedTokens, + restrictoToAdd.startTime, + restrictoToAdd.rollingPeriodInDays, + restrictoToAdd.endTime, + restrictoToAdd.restrictionType + ); + let addRestrictionReceipt = await common.sendTransaction(addRestrictionAction); + let addRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addRestrictionReceipt.logs, 'AddIndividualRestriction'); + console.log(chalk.green(`Restriction for ${addRestrictionEvent._holder} has been added successfully!`)); + break; + case 'Modify individual restriction': + let restrictoToModify = inputRestrictionData(false); + let modifyRestrictionAction = currentTransferManager.methods.modifyIndividualRestriction( + holder, + restrictoToModify.allowedTokens, + restrictoToModify.startTime, + restrictoToModify.rollingPeriodInDays, + restrictoToModify.endTime, + restrictoToModify.restrictionType + ); + let modifyRestrictionReceipt = await common.sendTransaction(modifyRestrictionAction); + let modifyRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, modifyRestrictionReceipt.logs, 'ModifyIndividualRestriction'); + console.log(chalk.green(`Restriction for ${modifyRestrictionEvent._holder} has been modified successfully!`)); + break; + case 'Remove individual restriction': + let removeRestrictionAction = currentTransferManager.methods.removeIndividualRestriction(holder); + let removeRestrictionReceipt = await common.sendTransaction(removeRestrictionAction); + let removeRestrictionEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, removeRestrictionReceipt.logs, 'IndividualRestrictionRemoved'); + console.log(chalk.green(`Restriction for ${removeRestrictionEvent._holder} has been removed successfully!`)); + break; + case 'RETURN': + return; + } +} + +function inputRestrictionData(isDaily) { + let restriction = {}; + restriction.restrictionType = readlineSync.keyInSelect(RESTRICTION_TYPES, 'How do you want to set the allowance? ', { cancel: false }); + if (restriction.restrictionType == RESTRICTION_TYPES.indexOf('Fixed')) { + restriction.allowedTokens = web3.utils.toWei(readlineSync.questionInt(`Enter the maximum amount of tokens allowed to be traded every ${isDaily ? 'day' : 'rolling period'}: `).toString()); + } else { + restriction.allowedTokens = toWeiPercentage(readlineSync.questionInt(`Enter the maximum percentage of total supply allowed to be traded every ${isDaily ? 'day' : 'rolling period'}: `).toString()); + } + if (isDaily) { + restriction.rollingPeriodInDays = 1; + } else { + restriction.rollingPeriodInDays = readlineSync.questionInt(`Enter the rolling period in days (10 days): `, { defaultInput: 10 }); + } + restriction.startTime = readlineSync.questionInt(`Enter the time (Unix Epoch time) at which restriction get into effect (now = 0): `, { defaultInput: 0 }); + let oneMonthFromNow = Math.floor(Date.now() / 1000) + gbl.constants.DURATION.days(30); + restriction.endTime = readlineSync.question(`Enter the time (Unix Epoch time) when the purchase lockup period ends and the investor can freely purchase tokens from others (1 week from now = ${oneMonthFromNow}): `, { + limit: function (input) { + return input > restriction.startTime + gbl.constants.DURATION.days(restriction.rollingPeriodInDays); + }, + limitMessage: 'Must be greater than startTime + rolling period', + defaultInput: oneMonthFromNow + }); + return restriction; +} + async function singleTradeVolumeRestrictionTM() { console.log(chalk.blue(`Single Trade Volume Restriction Transfer Manager at ${currentTransferManager.options.address} `)); console.log(); @@ -1178,7 +1505,7 @@ async function selectToken() { async function logTotalInvestors() { let investorsCount = await securityToken.methods.getInvestorCount().call(); - console.log(chalk.yellow(`Total investors at the moment: ${investorsCount}`)); + console.log(chalk.yellow(`Total investors at the moment: ${investorsCount} `)); } async function logBalance(from, totalSupply) { From 70f6cac5b6a9076982db461479779326f0c57367 Mon Sep 17 00:00:00 2001 From: Victor Date: Wed, 12 Dec 2018 16:33:48 -0300 Subject: [PATCH 44/56] CLI - Removed STVR TM from CLI --- CLI/commands/transfer_manager.js | 248 ------------------------------- 1 file changed, 248 deletions(-) diff --git a/CLI/commands/transfer_manager.js b/CLI/commands/transfer_manager.js index 45a253025..4df500208 100644 --- a/CLI/commands/transfer_manager.js +++ b/CLI/commands/transfer_manager.js @@ -218,24 +218,6 @@ async function configExistingModules(tmModules) { currentTransferManager.setProvider(web3.currentProvider); await volumeRestrictionTM(); break; - case 'SingleTradeVolumeRestrictionTM': - //currentTransferManager = new web3.eth.Contract(abis.singleTradeVolumeRestrictionTM(), tmModules[index].address); - //currentTransferManager.setProvider(web3.currentProvider); - //await singleTradeVolumeRestrictionTM(); - console.log(chalk.red(` - ********************************* - This option is not yet available. - *********************************` - )); - break; - case 'LookupVolumeRestrictionTM': - //await lookupVolumeRestrictionTM(); - console.log(chalk.red(` - ********************************* - This option is not yet available. - *********************************` - )); - break; } } @@ -267,56 +249,6 @@ async function addTransferManagerModule() { let configurePercentageTM = abis.percentageTransferManager().find(o => o.name === 'configure' && o.type === 'function'); bytes = web3.eth.abi.encodeFunctionCall(configurePercentageTM, [maxHolderPercentage, allowPercentagePrimaryIssuance]); break; - case 'SingleTradeVolumeRestrictionTM': - /* - let isTransferLimitInPercentage = !!readlineSync.keyInSelect(['In tokens', 'In percentage'], 'How do you want to set the transfer limit? ', {cancel: false}); - let globalTransferLimitInPercentageOrToken; - if (isTransferLimitInPercentage) { - globalTransferLimitInPercentageOrToken = toWeiPercentage(readlineSync.question('Enter the percentage for default limit: ', { - limit: function(input) { - return (parseInt(input) > 0 && parseInt(input) <= 100); - }, - limitMessage: "Must be greater than 0 and less than 100" - })); - } else { - globalTransferLimitInPercentageOrToken = web3.utils.toWei(readlineSync.question('Enter the amount of tokens for default limit: ', { - limit: function(input) { - return parseInt(input) > 0; - }, - limitMessage: "Must be greater than 0" - })); - } - let allowPrimaryIssuance = readlineSync.keyInYNStrict(`Do you want to allow all primary issuance transfers? `); - bytes = web3.eth.abi.encodeFunctionCall( { - name: 'configure', - type: 'function', - inputs: [ - { - type: 'bool', - name: '_isTransferLimitInPercentage' - },{ - type: 'uint256', - name: '_globalTransferLimitInPercentageOrToken' - },{ - type: 'bool', - name: '_isTransferLimitInPercentage' - } - ] - }, [isTransferLimitInPercentage, globalTransferLimitInPercentageOrToken, allowPrimaryIssuance]); - */ - console.log(chalk.red(` - ********************************* - This option is not yet available. - *********************************` - )); - break; - case 'LookupVolumeRestrictionTM': - console.log(chalk.red(` - ********************************* - This option is not yet available. - *********************************` - )); - break; } let selectedTMFactoryAddress = await contracts.getModuleFactoryAddressByName(securityToken.options.address, gbl.constants.MODULES_TYPES.TRANSFER, options[index]); let addModuleAction = securityToken.methods.addModule(selectedTMFactoryAddress, bytes, 0, 0); @@ -1196,186 +1128,6 @@ function inputRestrictionData(isDaily) { return restriction; } -async function singleTradeVolumeRestrictionTM() { - console.log(chalk.blue(`Single Trade Volume Restriction Transfer Manager at ${currentTransferManager.options.address} `)); - console.log(); - - // Show current data - let displayIsInPercentage = await currentTransferManager.methods.isTransferLimitInPercentage().call(); - let displayGlobalTransferLimit; - if (displayIsInPercentage) { - displayGlobalTransferLimit = fromWeiPercentage(await currentTransferManager.methods.globalTransferLimitInPercentage().call()); - } else { - displayGlobalTransferLimit = web3.utils.fromWei(await currentTransferManager.methods.globalTransferLimitInTokens().call()); - } - let displayAllowPrimaryIssuance = await currentTransferManager.methods.allowPrimaryIssuance().call(); - - console.log(`- Limit type: ${displayIsInPercentage ? `Percentage` : `Tokens`} `); - console.log(`- Default transfer limit: ${displayGlobalTransferLimit} ${displayIsInPercentage ? `%` : `${tokenSymbol}`} `); - console.log(`- Allow primary issuance: ${displayAllowPrimaryIssuance ? `YES` : `NO`} `); - // ------------------ - - let options = []; - if (displayAllowPrimaryIssuance) { - options.push('Disallow primary issuance'); - } else { - options.push('Allow primary issuance'); - } - options.push('Add exempted wallet', 'Remove exempted wallet'); - if (displayIsInPercentage) { - options.push('Change transfer limit to tokens', 'Change default percentage limit', - 'Set percentage transfer limit per account', 'Remove percentage transfer limit per account'); - } else { - options.push('Change transfer limit to percentage', 'Change default tokens limit', - 'Set tokens transfer limit per account', 'Remove tokens transfer limit per account'); - } - - let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'Return' }); - let optionSelected = options[index]; - console.log('Selected:', index != -1 ? optionSelected : 'Return', '\n'); - switch (optionSelected) { - case 'Allow primary issuance': - case 'Disallow primary issuance': - let disallowPrimaryIssuanceAction = currentTransferManager.methods.setAllowPrimaryIssuance(!displayAllowPrimaryIssuance); - await common.sendTransaction(disallowPrimaryIssuanceAction); - break; - case 'Add exempted wallet': - let walletToExempt = readlineSync.question('Enter the wallet to exempt: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - let addExemptWalletAction = currentTransferManager.methods.addExemptWallet(walletToExempt); - let addExemptWalletReceipt = await common.sendTransaction(addExemptWalletAction); - let addExemptWalletEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addExemptWalletReceipt.logs, 'ExemptWalletAdded'); - console.log(chalk.green(`${addExemptWalletEvent._wallet} has been exempted sucessfully!`)); - break; - case 'Remove exempted wallet': - let exemptedWallet = readlineSync.question('Enter the wallet to remove from exempt: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - let removeExemptWalletAction = currentTransferManager.methods.removeExemptWallet(exemptedWallet); - let removeExemptWalletReceipt = await common.sendTransaction(removeExemptWalletAction); - let removeExemptWalletEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, removeExemptWalletReceipt.logs, 'ExemptWalletRemoved'); - console.log(chalk.green(`${removeExemptWalletEvent._wallet} has been removed from exempt wallets sucessfully!`)); - break; - case 'Change transfer limit to tokens': - let newDefaultLimitInTokens = web3.utils.toWei(readlineSync.question('Enter the amount of tokens for default limit: ', { - limit: function (input) { - return parseInt(input) > 0; - }, - limitMessage: "Must be greater than zero" - })); - let changeTransferLimitToTokensAction = currentTransferManager.methods.changeTransferLimitToTokens(newDefaultLimitInTokens); - let changeTransferLimitToTokensReceipt = await common.sendTransaction(changeTransferLimitToTokensAction); - let changeTransferLimitToTokensEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeTransferLimitToTokensReceipt.logs, 'GlobalTransferLimitInTokensSet'); - console.log(chalk.green(`Transfer limit has been set to tokens sucessfully!`)); - console.log(chalk.green(`The default transfer limit is ${web3.utils.fromWei(changeTransferLimitToTokensEvent._amount)} ${tokenSymbol} `)); - break; - case 'Change transfer limit to percentage': - let newDefaultLimitInPercentage = toWeiPercentage(readlineSync.question('Enter the percentage for default limit: ', { - limit: function (input) { - return (parseInt(input) > 0 && parseInt(input) <= 100); - }, - limitMessage: "Must be greater than 0 and less than 100" - })); - let changeTransferLimitToPercentageAction = currentTransferManager.methods.changeTransferLimitToPercentage(newDefaultLimitInPercentage); - let changeTransferLimitToPercentageReceipt = await common.sendTransaction(changeTransferLimitToPercentageAction); - let changeTransferLimitToPercentageEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeTransferLimitToPercentageReceipt.logs, 'GlobalTransferLimitInPercentageSet'); - console.log(chalk.green(`Transfer limit has been set to tokens sucessfully!`)); - console.log(chalk.green(`The default transfer limit is ${fromWeiPercentage(changeTransferLimitToPercentageEvent._percentage)} % `)); - break; - case 'Change default percentage limit': - let defaultLimitInPercentage = toWeiPercentage(readlineSync.question('Enter the percentage for default limit: ', { - limit: function (input) { - return (parseInt(input) > 0 && parseInt(input) <= 100); - }, - limitMessage: "Must be greater than 0 and less than 100" - })); - let changeGlobalLimitInPercentageAction = currentTransferManager.methods.changeGlobalLimitInPercentage(defaultLimitInPercentage); - let changeGlobalLimitInPercentageReceipt = await common.sendTransaction(changeGlobalLimitInPercentageAction); - let changeGlobalLimitInPercentageEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeGlobalLimitInPercentageReceipt.logs, 'GlobalTransferLimitInPercentageSet'); - console.log(chalk.green(`The default transfer limit is ${fromWeiPercentage(changeGlobalLimitInPercentageEvent._percentage)} % `)); - break; - case 'Change default tokens limit': - let defaultLimitInTokens = web3.utils.toWei(readlineSync.question('Enter the amount of tokens for default limit: ', { - limit: function (input) { - return parseInt(input) > 0; - }, - limitMessage: "Must be greater than zero" - })); - let changeGlobalLimitInTokensAction = currentTransferManager.methods.changeGlobalLimitInTokens(defaultLimitInTokens); - let changeGlobalLimitInTokensReceipt = await common.sendTransaction(changeGlobalLimitInTokensAction); - let changeGlobalLimitInTokensEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, changeGlobalLimitInTokensReceipt.logs, 'GlobalTransferLimitInTokensSet'); - console.log(chalk.green(`The default transfer limit is ${web3.utils.fromWei(changeGlobalLimitInTokensEvent._amount)} ${tokenSymbol} `)); - break; - case 'Set percentage transfer limit per account': - let percentageAccount = readlineSync.question('Enter the wallet: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - let accountLimitInPercentage = toWeiPercentage(readlineSync.question(`Enter the transfer limit for ${percentageAccount} in percentage: `, { - limit: function (input) { - return (parseInt(input) > 0 && parseInt(input) <= 100); - }, - limitMessage: "Must be greater than 0 and less than 100" - })); - let setTransferLimitInPercentageAction = currentTransferManager.methods.setTransferLimitInPercentage(percentageAccount, accountLimitInPercentage); - let setTransferLimitInPercentageReceipt = await common.sendTransaction(setTransferLimitInPercentageAction); - let setTransferLimitInPercentageEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, setTransferLimitInPercentageReceipt.logs, 'TransferLimitInPercentageSet'); - console.log(chalk.green(`The transfer limit for ${setTransferLimitInPercentageEvent._wallet} is ${fromWeiPercentage(setTransferLimitInPercentageEvent._percentage)} % `)); - break; - case 'Set tokens transfer limit per account': - let tokensAccount = readlineSync.question('Enter the wallet: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - let accountLimitInTokens = web3.utils.toWei(readlineSync.question(`Enter the transfer limit for ${tokensAccount} in amount of tokens: `, { - limit: function (input) { - return parseInt(input) > 0; - }, - limitMessage: "Must be greater than zero" - })); - let setTransferLimitInTokensAction = currentTransferManager.methods.setTransferLimitInTokens(tokensAccount, accountLimitInTokens); - let setTransferLimitInTokensReceipt = await common.sendTransaction(setTransferLimitInTokensAction); - let setTransferLimitInTokensEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, setTransferLimitInTokensReceipt.logs, 'TransferLimitInTokensSet'); - console.log(chalk.green(`The transfer limit for ${setTransferLimitInTokensEvent._wallet} is ${web3.utils.fromWei(setTransferLimitInTokensEvent._amount)} ${tokenSymbol} `)); - break; - case 'Remove percentage transfer limit per account': - let percentageAccountToRemove = readlineSync.question('Enter the wallet to remove: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - let removeTransferLimitInPercentageAction = currentTransferManager.methods.removeTransferLimitInPercentage(percentageAccountToRemove); - let removeTransferLimitInPercentageReceipt = await common.sendTransaction(removeTransferLimitInPercentageAction); - let removeTransferLimitInPercentageEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, removeTransferLimitInPercentageReceipt.logs, 'TransferLimitInPercentageRemoved'); - console.log(chalk.green(`The transfer limit for ${removeTransferLimitInPercentageEvent._wallet} is the default limit`)); - break; - case 'Remove tokens transfer limit per account': - let tokensAccountToRemove = readlineSync.question('Enter the wallet to remove: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - let removeTransferLimitInTokensAction = currentTransferManager.methods.removeTransferLimitInTokens(tokensAccountToRemove); - let removeTransferLimitInTokensReceipt = await common.sendTransaction(removeTransferLimitInTokensAction); - let removeTransferLimitInTokensEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, removeTransferLimitInTokensReceipt.logs, 'TransferLimitInTokensRemoved'); - console.log(chalk.green(`The transfer limit for ${removeTransferLimitInTokensEvent._wallet} is the default limit`)); - break; - } -} - /* // Copied from tests function signData(tmAddress, investorAddress, fromTime, toTime, expiryTime, restricted, validFrom, validTo, pk) { From 125c1b858e33a59544babcede966cfbcc307ed9f Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 13 Dec 2018 20:13:32 -0300 Subject: [PATCH 45/56] CLI - Operations in batch for VRTM --- CLI/commands/transfer_manager.js | 239 ++++++++++++++++++ .../VRTM/add_daily_restriction_data.csv | 8 + .../Transfer/VRTM/add_restriction_data.csv | 8 + .../VRTM/modify_daily_restriction_data.csv | 5 + .../Transfer/VRTM/modify_restriction_data.csv | 5 + .../VRTM/remove_daily_restriction_data.csv | 6 + .../Transfer/VRTM/remove_restriction_data.csv | 2 + 7 files changed, 273 insertions(+) create mode 100644 CLI/data/Transfer/VRTM/add_daily_restriction_data.csv create mode 100644 CLI/data/Transfer/VRTM/add_restriction_data.csv create mode 100644 CLI/data/Transfer/VRTM/modify_daily_restriction_data.csv create mode 100644 CLI/data/Transfer/VRTM/modify_restriction_data.csv create mode 100644 CLI/data/Transfer/VRTM/remove_daily_restriction_data.csv create mode 100644 CLI/data/Transfer/VRTM/remove_restriction_data.csv diff --git a/CLI/commands/transfer_manager.js b/CLI/commands/transfer_manager.js index 4df500208..9d6641519 100644 --- a/CLI/commands/transfer_manager.js +++ b/CLI/commands/transfer_manager.js @@ -12,6 +12,12 @@ const { table } = require('table') // Constants const WHITELIST_DATA_CSV = './CLI/data/Transfer/GTM/whitelist_data.csv'; const PERCENTAGE_WHITELIST_DATA_CSV = './CLI/data/Transfer/PercentageTM/whitelist_data.csv'; +const ADD_DAILY_RESTRICTIONS_DATA_CSV = './CLI/data/Transfer/VRTM/add_daily_restriction_data.csv'; +const MODIFY_DAILY_RESTRICTIONS_DATA_CSV = './CLI/data/Transfer/VRTM/modify_daily_restriction_data.csv'; +const REMOVE_DAILY_RESTRICTIONS_DATA_CSV = './CLI/data/Transfer/VRTM/remove_daily_restriction_data.csv'; +const ADD_RESTRICTIONS_DATA_CSV = './CLI/data/Transfer/VRTM/add_restriction_data.csv'; +const MODIFY_RESTRICTIONS_DATA_CSV = './CLI/data/Transfer/VRTM/modify_restriction_data.csv'; +const REMOVE_RESTRICTIONS_DATA_CSV = './CLI/data/Transfer/VRTM/remove_restriction_data.csv'; const RESTRICTION_TYPES = ['Fixed', 'Percentage']; @@ -1103,6 +1109,239 @@ async function changeIndividualRestrictions() { } } +async function operateWithMultipleRestrictions() { + let options = [ + 'Add multiple individual daily restrictions', + 'Modify multiple individual daily restrictions', + 'Remove multiple individual daily restrictions', + 'Add multiple individual restrictions', + 'Modify multiple individual restrictions', + 'Remove multiple individual restrictions' + ]; + + let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'RETURN' }); + let optionSelected = index !== -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + switch (optionSelected) { + case 'Add multiple individual daily restrictions': + await addDailyRestrictionsInBatch(); + break; + case 'Modify multiple individual daily restrictions': + await modifyDailyRestrictionsInBatch(); + break; + case 'Remove multiple individual daily restrictions': + await removeDailyRestrictionsInBatch(); + break; + case 'Add multiple individual restrictions': + await addRestrictionsInBatch(); + break; + case 'Modify multiple individual restrictions': + await modifyRestrictionsInBatch(); + break; + case 'Remove multiple individual restrictions': + await removeRestrictionsInBatch(); + break; + } +} + +async function addDailyRestrictionsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${ADD_DAILY_RESTRICTIONS_DATA_CSV}): `, { + defaultInput: ADD_DAILY_RESTRICTIONS_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => web3.utils.isAddress(row[0]) && + !isNaN(row[1]) && + moment.unix(row[2]).isValid() && + moment.unix(row[3]).isValid() && + typeof row[4] === 'string' && RESTRICTION_TYPES.includes(row[4])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [holderArray, allowanceArray, startTimeArray, endTimeArray, restrictionTypeArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to add daily restrictions to the following accounts: \n\n`, holderArray[batch], '\n'); + allowanceArray[batch] = allowanceArray[batch].map(n => web3.utils.toWei(n.toString())); + restrictionTypeArray[batch] = restrictionTypeArray[batch].map(n => RESTRICTION_TYPES.indexOf(n)); + let action = currentTransferManager.methods.addIndividualDailyRestrictionMulti(holderArray[batch], allowanceArray[batch], startTimeArray[batch], endTimeArray[batch], restrictionTypeArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Add multiple daily restrictions transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function modifyDailyRestrictionsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${MODIFY_DAILY_RESTRICTIONS_DATA_CSV}): `, { + defaultInput: MODIFY_DAILY_RESTRICTIONS_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => web3.utils.isAddress(row[0]) && + !isNaN(row[1]) && + moment.unix(row[2]).isValid() && + moment.unix(row[3]).isValid() && + typeof row[4] === 'string' && RESTRICTION_TYPES.includes(row[4])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [holderArray, allowanceArray, startTimeArray, endTimeArray, restrictionTypeArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to modify daily restrictions to the following accounts: \n\n`, holderArray[batch], '\n'); + allowanceArray[batch] = allowanceArray[batch].map(n => web3.utils.toWei(n.toString())); + restrictionTypeArray[batch] = restrictionTypeArray[batch].map(n => RESTRICTION_TYPES.indexOf(n)); + let action = currentTransferManager.methods.modifyIndividualDailyRestrictionMulti(holderArray[batch], allowanceArray[batch], startTimeArray[batch], endTimeArray[batch], restrictionTypeArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Modify multiple daily restrictions transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function removeDailyRestrictionsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${REMOVE_DAILY_RESTRICTIONS_DATA_CSV}): `, { + defaultInput: REMOVE_DAILY_RESTRICTIONS_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter(row => web3.utils.isAddress(row[0])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [holderArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to remove daily restrictions to the following accounts: \n\n`, holderArray[batch], '\n'); + let action = currentTransferManager.methods.removeIndividualDailyRestrictionMulti(holderArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Remove multiple daily restrictions transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function addRestrictionsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${ADD_RESTRICTIONS_DATA_CSV}): `, { + defaultInput: ADD_RESTRICTIONS_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => web3.utils.isAddress(row[0]) && + !isNaN(row[1]) && + moment.unix(row[2]).isValid() && + (!isNaN(row[3]) && (parseFloat(row[3]) % 1 === 0)) && + moment.unix(row[4]).isValid() && + typeof row[5] === 'string' && RESTRICTION_TYPES.includes(row[5])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [holderArray, allowanceArray, startTimeArray, rollingPeriodArray, endTimeArray, restrictionTypeArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to add restrictions to the following accounts: \n\n`, holderArray[batch], '\n'); + allowanceArray[batch] = allowanceArray[batch].map(n => web3.utils.toWei(n.toString())); + restrictionTypeArray[batch] = restrictionTypeArray[batch].map(n => RESTRICTION_TYPES.indexOf(n)); + let action = currentTransferManager.methods.addIndividualRestrictionMulti(holderArray[batch], allowanceArray[batch], startTimeArray[batch], rollingPeriodArray[batch], endTimeArray[batch], restrictionTypeArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Add multiple restrictions transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function modifyRestrictionsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${MODIFY_RESTRICTIONS_DATA_CSV}): `, { + defaultInput: MODIFY_RESTRICTIONS_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter( + row => web3.utils.isAddress(row[0]) && + !isNaN(row[1]) && + moment.unix(row[2]).isValid() && + (!isNaN(row[3]) && (parseFloat(row[3]) % 1 === 0)) && + moment.unix(row[4]).isValid() && + typeof row[5] === 'string' && RESTRICTION_TYPES.includes(row[5])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [holderArray, allowanceArray, startTimeArray, rollingPeriodArray, endTimeArray, restrictionTypeArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to modify restrictions to the following accounts: \n\n`, holderArray[batch], '\n'); + allowanceArray[batch] = allowanceArray[batch].map(n => web3.utils.toWei(n.toString())); + restrictionTypeArray[batch] = restrictionTypeArray[batch].map(n => RESTRICTION_TYPES.indexOf(n)); + let action = currentTransferManager.methods.modifyIndividualRestrictionMulti(holderArray[batch], allowanceArray[batch], startTimeArray[batch], rollingPeriodArray[batch], endTimeArray[batch], restrictionTypeArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Modify multiple restrictions transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function removeRestrictionsInBatch() { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${REMOVE_RESTRICTIONS_DATA_CSV}): `, { + defaultInput: REMOVE_RESTRICTIONS_DATA_CSV + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter(row => web3.utils.isAddress(row[0])); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + let batches = common.splitIntoBatches(validData, batchSize); + let [holderArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to remove restrictions to the following accounts: \n\n`, holderArray[batch], '\n'); + let action = currentTransferManager.methods.removeIndividualRestrictionMulti(holderArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Remove multiple restrictions transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + function inputRestrictionData(isDaily) { let restriction = {}; restriction.restrictionType = readlineSync.keyInSelect(RESTRICTION_TYPES, 'How do you want to set the allowance? ', { cancel: false }); diff --git a/CLI/data/Transfer/VRTM/add_daily_restriction_data.csv b/CLI/data/Transfer/VRTM/add_daily_restriction_data.csv new file mode 100644 index 000000000..782c1107d --- /dev/null +++ b/CLI/data/Transfer/VRTM/add_daily_restriction_data.csv @@ -0,0 +1,8 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,1000,1/8/2019,10/10/2019,"Fixed" +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,2000,1/8/2019,10/10/2019,"Fixed" +0xac297053173b02b02a737d47f7b4a718e5b170ef,500,1/8/2019,10/10/2019,"Fixed" +0x49fc0b78238dab644698a90fa351b4c749e123d2,0.15,1/8/2019,10/10/2019,"Percentage" +0x10223927009b8add0960359dd90d1449415b7ca9,0.25,1/8/2019,10/10/2019,"Percentage" +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399,0.1,1/8/2019,10/10/2019,"Percentage" +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,1234,1/8/2019,10/10/2019,"Fixed" +0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,5678,1/8/2019,10/10/2019,"Fixed" \ No newline at end of file diff --git a/CLI/data/Transfer/VRTM/add_restriction_data.csv b/CLI/data/Transfer/VRTM/add_restriction_data.csv new file mode 100644 index 000000000..072151a64 --- /dev/null +++ b/CLI/data/Transfer/VRTM/add_restriction_data.csv @@ -0,0 +1,8 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,1000,1/8/2019,90,10/10/2019,"Fixed" +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,2000,1/8/2019,30,10/10/2019,"Fixed" +0xac297053173b02b02a737d47f7b4a718e5b170ef,500,1/8/2019,15,10/10/2019,"Fixed" +0x49fc0b78238dab644698a90fa351b4c749e123d2,0.15,1/8/2019,90,10/10/2019,"Percentage" +0x10223927009b8add0960359dd90d1449415b7ca9,0.25,1/8/2019,30,10/10/2019,"Percentage" +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399,0.1,1/8/2019,15,10/10/2019,"Percentage" +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98,1234,1/8/2019,10,10/10/2019,"Fixed" +0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3,5678,1/8/2019,2,10/10/2019,"Fixed" \ No newline at end of file diff --git a/CLI/data/Transfer/VRTM/modify_daily_restriction_data.csv b/CLI/data/Transfer/VRTM/modify_daily_restriction_data.csv new file mode 100644 index 000000000..194e9b1d5 --- /dev/null +++ b/CLI/data/Transfer/VRTM/modify_daily_restriction_data.csv @@ -0,0 +1,5 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,2000,1/8/2019,10/10/2019,"Fixed" +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,2000,2/8/2019,10/10/2019,"Fixed" +0xac297053173b02b02a737d47f7b4a718e5b170ef,500,1/8/2019,10/10/2019,"Fixed" +0x49fc0b78238dab644698a90fa351b4c749e123d2,0.15,1/8/2019,20/10/2019,"Percentage" +0x10223927009b8add0960359dd90d1449415b7ca9,25,1/8/2019,10/10/2019,"Fixed" diff --git a/CLI/data/Transfer/VRTM/modify_restriction_data.csv b/CLI/data/Transfer/VRTM/modify_restriction_data.csv new file mode 100644 index 000000000..588e39211 --- /dev/null +++ b/CLI/data/Transfer/VRTM/modify_restriction_data.csv @@ -0,0 +1,5 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1,2000,1/8/2019,90,10/10/2019,"Fixed" +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1,2000,2/8/2019,30,10/10/2019,"Fixed" +0xac297053173b02b02a737d47f7b4a718e5b170ef,500,1/8/2019,20,10/10/2019,"Fixed" +0x49fc0b78238dab644698a90fa351b4c749e123d2,0.15,1/8/2019,90,20/10/2019,"Percentage" +0x10223927009b8add0960359dd90d1449415b7ca9,25,1/8/2019,30,10/10/2019,"Fixed" \ No newline at end of file diff --git a/CLI/data/Transfer/VRTM/remove_daily_restriction_data.csv b/CLI/data/Transfer/VRTM/remove_daily_restriction_data.csv new file mode 100644 index 000000000..a7fef30c4 --- /dev/null +++ b/CLI/data/Transfer/VRTM/remove_daily_restriction_data.csv @@ -0,0 +1,6 @@ +0xee7ae74d964f2be7d72c1b187b38e2ed3615d4d1 +0x2f0fd672bf222413cc69dc1f4f1d7e93ad1763a1 +0xac297053173b02b02a737d47f7b4a718e5b170ef +0x49fc0b78238dab644698a90fa351b4c749e123d2 +0x10223927009b8add0960359dd90d1449415b7ca9 +0x3c65cfe3de848cf38e9d76e9c3e57a2f1140b399 diff --git a/CLI/data/Transfer/VRTM/remove_restriction_data.csv b/CLI/data/Transfer/VRTM/remove_restriction_data.csv new file mode 100644 index 000000000..d927ee57b --- /dev/null +++ b/CLI/data/Transfer/VRTM/remove_restriction_data.csv @@ -0,0 +1,2 @@ +0xabf60de3265b3017db7a1be66fc8b364ec1dbb98 +0xb841fe5a89da1bbef2d0805fbd7ffcbbb2fca5e3 From 67d3429a388902d840f5fe4b4f7684d16fad8e89 Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 17 Dec 2018 16:32:46 -0300 Subject: [PATCH 46/56] CLI - Explore account --- CLI/commands/transfer_manager.js | 78 +++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/CLI/commands/transfer_manager.js b/CLI/commands/transfer_manager.js index 9d6641519..3d6dd8906 100644 --- a/CLI/commands/transfer_manager.js +++ b/CLI/commands/transfer_manager.js @@ -1002,7 +1002,7 @@ async function changeIndividualRestrictions() { let currentRestriction = await currentTransferManager.methods.individualRestriction(holder).call(); let hasRestriction = parseInt(currentRestriction.startTime) !== 0; - console.log(`*** Current restrictions for ${holder} ***`, '\n'); + console.log(`*** Current individual restrictions for ${holder} ***`, '\n'); console.log(`- Daily restriction: ${hasDailyRestriction ? '' : 'None'}`); if (hasDailyRestriction) { @@ -1012,7 +1012,7 @@ async function changeIndividualRestrictions() { console.log(` Rolling period: ${currentDailyRestriction.rollingPeriodInDays} days`); console.log(` End time: ${moment.unix(currentDailyRestriction.endTime).format('MMMM Do YYYY, HH:mm:ss')} `); } - console.log(`- Default daily restriction: ${hasRestriction ? '' : 'None'} `); + console.log(`- Other restriction: ${hasRestriction ? '' : 'None'} `); if (hasRestriction) { console.log(` Type: ${RESTRICTION_TYPES[currentRestriction.typeOfRestriction]}`); console.log(` Allowed tokens: ${currentRestriction.typeOfRestriction === "0" ? `${web3.utils.fromWei(currentRestriction.allowedTokens)} ${tokenSymbol}` : `${fromWeiPercentage(currentRestriction.allowedTokens)}%`}`); @@ -1109,6 +1109,80 @@ async function changeIndividualRestrictions() { } } +async function exploreAccount() { + let account = readlineSync.question('Enter the account to explore: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + + let appliyngdDailyRestriction = null; + let applyingOtherRestriction = null; + let hasIndividualRestrictions = false; + let isExempted = await currentTransferManager.methods.exemptList(account).call(); + if (!isExempted) { + let indiviaulDailyRestriction = await currentTransferManager.methods.individualDailyRestriction(account).call(); + if (parseInt(indiviaulDailyRestriction.endTime) !== 0) { + appliyngdDailyRestriction = indiviaulDailyRestriction; + } + let otherRestriction = await currentTransferManager.methods.individualRestriction(account).call(); + if (parseInt(otherRestriction.endTime) !== 0) { + applyingOtherRestriction = otherRestriction; + } + + hasIndividualRestrictions = applyingOtherRestriction || appliyngdDailyRestriction; + + if (!hasIndividualRestrictions) { + let defaultDailyRestriction = await currentTransferManager.methods.defaultDailyRestriction(account).call(); + if (parseInt(defaultDailyRestriction.endTime) !== 0) { + appliyngdDailyRestriction = defaultDailyRestriction; + } + let defaultOtherRestriction = await currentTransferManager.methods.defaultRestriction().call(); + if (parseInt(defaultOtherRestriction.endTime) === 0) { + applyingOtherRestriction = defaultOtherRestriction; + } + } + } + + console.log(`*** Applying restrictions for ${account} ***`, '\n'); + + console.log(`- Daily restriction: ${appliyngdDailyRestriction ? (!hasIndividualRestrictions ? 'default' : '') : 'None'}`); + if (appliyngdDailyRestriction) { + console.log(` Type: ${RESTRICTION_TYPES[appliyngdDailyRestriction.typeOfRestriction]}`); + console.log(` Allowed tokens: ${appliyngdDailyRestriction.typeOfRestriction === "0" ? `${web3.utils.fromWei(appliyngdDailyRestriction.allowedTokens)} ${tokenSymbol}` : `${fromWeiPercentage(appliyngdDailyRestriction.allowedTokens)}%`}`); + console.log(` Start time: ${moment.unix(appliyngdDailyRestriction.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(` Rolling period: ${appliyngdDailyRestriction.rollingPeriodInDays} days`); + console.log(` End time: ${moment.unix(appliyngdDailyRestriction.endTime).format('MMMM Do YYYY, HH:mm:ss')} `); + } + console.log(`- Other restriction: ${applyingOtherRestriction ? (!hasIndividualRestrictions ? 'default' : '') : 'None'} `); + if (applyingOtherRestriction) { + console.log(` Type: ${RESTRICTION_TYPES[applyingOtherRestriction.typeOfRestriction]}`); + console.log(` Allowed tokens: ${applyingOtherRestriction.typeOfRestriction === "0" ? `${web3.utils.fromWei(applyingOtherRestriction.allowedTokens)} ${tokenSymbol}` : `${fromWeiPercentage(applyingOtherRestriction.allowedTokens)}%`}`); + console.log(` Start time: ${moment.unix(applyingOtherRestriction.startTime).format('MMMM Do YYYY, HH:mm:ss')}`); + console.log(` Rolling period: ${applyingOtherRestriction.rollingPeriodInDays} days`); + console.log(` End time: ${moment.unix(applyingOtherRestriction.endTime).format('MMMM Do YYYY, HH:mm:ss')} `); + } + + if (applyingOtherRestriction || appliyngdDailyRestriction) { + let bucketDetails; + if (hasIndividualRestrictions) { + bucketDetails = await currentTransferManager.methods.getIndividualBucketDetailsToUser(account).call(); + } else { + bucketDetails = await currentTransferManager.methods.getDefaultBucketDetailsToUser(account).call(); + } + let now = Math.floor(Date.now() / 1000) - gbl.constants.DURATION.days(1); + let tradedByUserLastDay = await currentTransferManager.methods.getTotalTradedByUser(account, now).call(); + console.log(); + console.log(`Last trade: ${bucketDetails[0]}`); + console.log(`Last daily trade: ${bucketDetails[3]}`); + console.log(`Days since rolling period started: ${bucketDetails[2]}`); + console.log(`Transacted amount since rolling period started: ${web3.utils.fromWei(bucketDetails[1])}`); + console.log(`Transacted amount within last 24 hours: ${web3.utils.fromWei(tradedByUserLastDay)}`); + console.log(); + } +} + async function operateWithMultipleRestrictions() { let options = [ 'Add multiple individual daily restrictions', From 33860722276507c2e29097d1433771396ff8f269 Mon Sep 17 00:00:00 2001 From: comeonbuddy Date: Thu, 20 Dec 2018 15:22:13 +0700 Subject: [PATCH 47/56] wip - issue with test not finishing --- ...zer_volumn_restriction_transfer_manager.js | 309 ++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 test/z_fuzzer_volumn_restriction_transfer_manager.js diff --git a/test/z_fuzzer_volumn_restriction_transfer_manager.js b/test/z_fuzzer_volumn_restriction_transfer_manager.js new file mode 100644 index 000000000..9152a73c3 --- /dev/null +++ b/test/z_fuzzer_volumn_restriction_transfer_manager.js @@ -0,0 +1,309 @@ +import latestTime from './helpers/latestTime'; +import {signData} from './helpers/signData'; +import { pk } from './helpers/testprivateKey'; +import { duration, promisifyLogWatch, latestBlock } from './helpers/utils'; +import { takeSnapshot, increaseTime, revertToSnapshot } from './helpers/time'; +import { catchRevert } from "./helpers/exceptions"; +import { setUpPolymathNetwork, deployVRTMAndVerifyed } from "./helpers/createInstances"; + +const SecurityToken = artifacts.require('./SecurityToken.sol'); +const GeneralTransferManager = artifacts.require('./GeneralTransferManager.sol'); +const VolumeRestrictionTM = artifacts.require('./VolumeRestrictionTM.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('VolumeRestrictionTransferManager', accounts => { + + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let token_owner_pk; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + let account_delegate; + let account_delegate2; + let account_delegate3; + // investor Details + let fromTime = latestTime(); + let toTime = latestTime(); + let expiryTime = toTime + duration.days(15); + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let I_VolumeRestrictionTMFactory; + let P_VolumeRestrictionTMFactory; + let I_SecurityTokenRegistryProxy; + let P_VolumeRestrictionTM; + let I_GeneralTransferManagerFactory; + let I_VolumeRestrictionTM; + let I_GeneralTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_DummySTOFactory; + let I_STFactory; + let I_SecurityToken; + let I_MRProxied; + let I_STRProxied; + let I_PolyToken; + let I_PolymathRegistry; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + const delegateDetails = "Hello I am legit delegate"; + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + let tempAmount = new BigNumber(0); + let tempArray = new Array(); + let tempArray3 = new Array(); + let tempArrayGlobal = new Array(); + + // Initial fee for ticker registry and security token registry + const initRegFee = web3.utils.toWei("250"); + + async function print(data, account) { + console.log(` + Latest timestamp: ${data[0].toNumber()} + SumOfLastPeriod: ${data[1].dividedBy(new BigNumber(10).pow(18)).toNumber()} + Days Covered: ${data[2].toNumber()} + Latest timestamp daily: ${data[3].toNumber()} + Individual Total Trade on latestTimestamp : ${(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account, data[0])) + .dividedBy(new BigNumber(10).pow(18)).toNumber()} + Individual Total Trade on daily latestTimestamp : ${(await I_VolumeRestrictionTM.getTotalTradedByUser.call(account, data[3])) + .dividedBy(new BigNumber(10).pow(18)).toNumber()} + `) + } + + async function calculateSum(rollingPeriod, tempArray) { + let sum = 0; + let start = 0; + if (tempArray.length >= rollingPeriod) + start = tempArray.length - rollingPeriod; + for (let i = start; i < tempArray.length; i++) { + sum += tempArray[i]; + } + return sum; + } + + before(async() => { + // Accounts setup + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + token_owner_pk = pk.account_1; + + account_investor1 = accounts[8]; + account_investor2 = accounts[9]; + account_investor3 = accounts[4]; + account_investor4 = accounts[3]; + account_delegate = accounts[7]; + account_delegate2 = accounts[6]; + account_delegate3 = accounts[5]; + + // Step 1: Deploy the genral PM ecosystem + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied + ] = instances; + + // STEP 5: Deploy the VolumeRestrictionTMFactory + [I_VolumeRestrictionTMFactory] = await deployVRTMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, 0); + // STEP 6: Deploy the VolumeRestrictionTMFactory + [P_VolumeRestrictionTMFactory] = await deployVRTMAndVerifyed(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500")); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistryProxy ${I_ModuleRegistryProxy.address} + ModuleRegistry: ${I_ModuleRegistry.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + VolumeRestrictionTMFactory: ${I_VolumeRestrictionTMFactory.address} + ----------------------------------------------------------------------------- + `); + }); + + 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, true, { 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._types[0].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.getModulesByType(2))[0]; + I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + }); + }); + + describe("Attach the VRTMaaaaa", async() => { + it("Deploy the VRTM and attach with the ST", async()=> { + let tx = await I_SecurityToken.addModule(I_VolumeRestrictionTMFactory.address, 0, 0, 0, {from: token_owner }); + assert.equal(tx.logs[2].args._moduleFactory, I_VolumeRestrictionTMFactory.address); + assert.equal( + web3.utils.toUtf8(tx.logs[2].args._name), + "VolumeRestrictionTM", + "VolumeRestrictionTMFactory doesn not added"); + I_VolumeRestrictionTM = VolumeRestrictionTM.at(tx.logs[2].args._module); + }); + + it("Transfer some tokens to different account", async() => { + // Add tokens in to the whitelist + await I_GeneralTransferManager.modifyWhitelistMulti( + [account_investor1, account_investor2, account_investor3], + [latestTime(), latestTime(), latestTime()], + [latestTime(), latestTime(), latestTime()], + [latestTime() + duration.days(60), latestTime() + duration.days(60), latestTime() + duration.days(60)], + [true, true, true], + { + from: token_owner + } + ); + + // Mint some tokens and transferred to whitelisted addresses + await I_SecurityToken.mint(account_investor1, web3.utils.toWei("40", "ether"), {from: token_owner}); + await I_SecurityToken.mint(account_investor2, web3.utils.toWei("30", "ether"), {from: token_owner}); + await I_SecurityToken.mint(account_investor3, web3.utils.toWei("30", "ether"), {from: token_owner}); + + }); + + }); + + describe("Fuzz test - multiple transaction within 1 day with Individual and daily Restrictions", async() => { + + let snapId = await takeSnapshot(); + + var testRepeat = 2; + var sumOfLastPeriod = 0; + var txAmount = []; + var txDates = []; + + for (var i = 0; i < testRepeat; i++) { + + var individualRestrictTotalAmount = 3; + var dailyRestrictionAmount = 6; + var rollingPeriod = 2; + var transferAmount = Math.floor(Math.random() * 10); + var timePassed = Math.floor(Math.random() * 10); + console.log("individualRestrictTotalAmount : " + individualRestrictTotalAmount + "transferAmount : " + transferAmount + "timePassed : " + timePassed); + + // 1 - add individual restriction with a random number + it("Should add individual restriction succesfully", async() => { + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei(individualRestrictTotalAmount.toString()), + latestTime() + duration.seconds(2), + rollingPeriod, + latestTime() + duration.days(3), + 0, + { + from: token_owner + } + ); + }); + + it("Should add daily restriction succesfully", async() => { + + let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor1, + web3.utils.toWei(dailyRestrictionAmount.toString()), + latestTime() + duration.seconds(1), + latestTime() + duration.days(4), + 0, + { + from: token_owner + } + ); + + }); + + it("transaction tests", async() => { + + var txNumber = 2; // define fuzz test amount for tx within 24 hrs + + for (var j=0; j individualRestrictTotalAmount || accumulatedTxValue > dailyRestrictionAmount){ + + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) + ); + + console.log("tx failed as expected due to over limit"); + + } else if (accumulatedTxValue < individualRestrictTotalAmount && accumulatedTxValue < dailyRestrictionAmount ){ + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + sumOfLastPeriod = sumOfLastPeriod + transactionAmount; + + console.log("tx succeeded"); + } + + await increaseTime(duration.seconds(20/txNumber*3600)); + + } + + }); + + await revertToSnapshot(snapId); + } + + }); + +}); \ No newline at end of file From 43cc95a7065b2512fe4d0b3afbfeca4c6f34e191 Mon Sep 17 00:00:00 2001 From: comeonbuddy Date: Thu, 20 Dec 2018 22:14:24 +0700 Subject: [PATCH 48/56] WIP-multiple transaction within 1 day with Individual and daily Restrictions --- ...zer_volumn_restriction_transfer_manager.js | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/test/z_fuzzer_volumn_restriction_transfer_manager.js b/test/z_fuzzer_volumn_restriction_transfer_manager.js index 9152a73c3..b606ad1bf 100644 --- a/test/z_fuzzer_volumn_restriction_transfer_manager.js +++ b/test/z_fuzzer_volumn_restriction_transfer_manager.js @@ -219,26 +219,25 @@ contract('VolumeRestrictionTransferManager', accounts => { }); - describe("Fuzz test - multiple transaction within 1 day with Individual and daily Restrictions", async() => { - - let snapId = await takeSnapshot(); - - var testRepeat = 2; - var sumOfLastPeriod = 0; - var txAmount = []; - var txDates = []; - - for (var i = 0; i < testRepeat; i++) { - - var individualRestrictTotalAmount = 3; - var dailyRestrictionAmount = 6; - var rollingPeriod = 2; - var transferAmount = Math.floor(Math.random() * 10); - var timePassed = Math.floor(Math.random() * 10); - console.log("individualRestrictTotalAmount : " + individualRestrictTotalAmount + "transferAmount : " + transferAmount + "timePassed : " + timePassed); + describe("Fuzz test", async() => { + + it("Should work with multiple transaction within 1 day with Individual and daily Restrictions", async() => { + // let snapId = await takeSnapshot(); - // 1 - add individual restriction with a random number - it("Should add individual restriction succesfully", async() => { + var testRepeat = 2; + + for (var i = 0; i < testRepeat; i++) { + + console.log("fuzzer number " + i); + + var individualRestrictTotalAmount = 7; + var dailyRestrictionAmount = 6; + var rollingPeriod = 2; + var sumOfLastPeriod = 0; + + + // 1 - add individual restriction with a random number + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, web3.utils.toWei(individualRestrictTotalAmount.toString()), @@ -250,11 +249,9 @@ contract('VolumeRestrictionTransferManager', accounts => { from: token_owner } ); - }); - it("Should add daily restriction succesfully", async() => { - - let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( + + tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( account_investor1, web3.utils.toWei(dailyRestrictionAmount.toString()), latestTime() + duration.seconds(1), @@ -264,46 +261,49 @@ contract('VolumeRestrictionTransferManager', accounts => { from: token_owner } ); - - }); - - it("transaction tests", async() => { + - var txNumber = 2; // define fuzz test amount for tx within 24 hrs + var txNumber = 3; // define fuzz test amount for tx within 24 hrs for (var j=0; j individualRestrictTotalAmount || accumulatedTxValue > dailyRestrictionAmount){ + console.log("tx should fail"); + await catchRevert( I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) ); console.log("tx failed as expected due to over limit"); - } else if (accumulatedTxValue < individualRestrictTotalAmount && accumulatedTxValue < dailyRestrictionAmount ){ + } else if (accumulatedTxValue <= individualRestrictTotalAmount && accumulatedTxValue <= dailyRestrictionAmount ){ + console.log("tx should succeed"); + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); sumOfLastPeriod = sumOfLastPeriod + transactionAmount; console.log("tx succeeded"); } - - await increaseTime(duration.seconds(20/txNumber*3600)); - + console.log("2"); } - - }); - - await revertToSnapshot(snapId); - } - + } + // await revertToSnapshot(snapId); + }); }); }); \ No newline at end of file From 4e1e37b3fb985e03c1b6302a7d320aacbf86dc32 Mon Sep 17 00:00:00 2001 From: comeonbuddy Date: Fri, 21 Dec 2018 12:33:10 +0700 Subject: [PATCH 49/56] fixed bug with the initial test --- ...zer_volumn_restriction_transfer_manager.js | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/test/z_fuzzer_volumn_restriction_transfer_manager.js b/test/z_fuzzer_volumn_restriction_transfer_manager.js index b606ad1bf..645856e52 100644 --- a/test/z_fuzzer_volumn_restriction_transfer_manager.js +++ b/test/z_fuzzer_volumn_restriction_transfer_manager.js @@ -211,7 +211,7 @@ contract('VolumeRestrictionTransferManager', accounts => { ); // Mint some tokens and transferred to whitelisted addresses - await I_SecurityToken.mint(account_investor1, web3.utils.toWei("40", "ether"), {from: token_owner}); + await I_SecurityToken.mint(account_investor1, web3.utils.toWei("100", "ether"), {from: token_owner}); await I_SecurityToken.mint(account_investor2, web3.utils.toWei("30", "ether"), {from: token_owner}); await I_SecurityToken.mint(account_investor3, web3.utils.toWei("30", "ether"), {from: token_owner}); @@ -224,20 +224,20 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should work with multiple transaction within 1 day with Individual and daily Restrictions", async() => { // let snapId = await takeSnapshot(); - var testRepeat = 2; + var testRepeat = 5; for (var i = 0; i < testRepeat; i++) { console.log("fuzzer number " + i); - var individualRestrictTotalAmount = 7; - var dailyRestrictionAmount = 6; - var rollingPeriod = 2; + var individualRestrictTotalAmount = Math.floor(Math.random() * 10); + var dailyRestrictionAmount = Math.floor(Math.random() * 10); + var rollingPeriod = 2; var sumOfLastPeriod = 0; + console.log("a"); // 1 - add individual restriction with a random number - let tx = await I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, web3.utils.toWei(individualRestrictTotalAmount.toString()), @@ -250,7 +250,7 @@ contract('VolumeRestrictionTransferManager', accounts => { } ); - + console.log("b"); tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( account_investor1, web3.utils.toWei(dailyRestrictionAmount.toString()), @@ -262,8 +262,8 @@ contract('VolumeRestrictionTransferManager', accounts => { } ); - - var txNumber = 3; // define fuzz test amount for tx within 24 hrs + console.log("c"); + var txNumber = 10; //define fuzz test amount for tx within 24 hrs for (var j=0; j { } console.log("2"); } + + // await revertToSnapshot(snapId); + await I_VolumeRestrictionTM.removeIndividualRestriction(account_investor1, {from: token_owner}); + await I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor1, {from: token_owner}); } - // await revertToSnapshot(snapId); + }); }); From 2de1bb869518aefc5ad5ae1d9b6354cada092320 Mon Sep 17 00:00:00 2001 From: comeonbuddy Date: Fri, 21 Dec 2018 16:07:45 +0700 Subject: [PATCH 50/56] added scenario 2 and 3 --- ...zer_volumn_restriction_transfer_manager.js | 242 ++++++++++++++++++ 1 file changed, 242 insertions(+) diff --git a/test/z_fuzzer_volumn_restriction_transfer_manager.js b/test/z_fuzzer_volumn_restriction_transfer_manager.js index 645856e52..61ea5c32a 100644 --- a/test/z_fuzzer_volumn_restriction_transfer_manager.js +++ b/test/z_fuzzer_volumn_restriction_transfer_manager.js @@ -308,6 +308,248 @@ contract('VolumeRestrictionTransferManager', accounts => { } }); + + + it("Should work with fuzz test for individual restriction and general restriction", async() => { + // let snapId = await takeSnapshot(); + + var testRepeat = 5; + + for (var i = 0; i < testRepeat; i++) { + + console.log("fuzzer number " + i); + + var individualRestrictTotalAmount = Math.floor(Math.random() * 10); + var defaultRestrictionAmount = Math.floor(Math.random() * 10); + var rollingPeriod = 2; + var sumOfLastPeriod = 0; + + console.log("a"); + + // 1 - add individual restriction with a random number + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei(individualRestrictTotalAmount.toString()), + latestTime() + duration.seconds(1), + rollingPeriod, + latestTime() + duration.days(3), + 0, + { + from: token_owner + } + ); + + console.log("b"); + tx = await I_VolumeRestrictionTM.addDefaultRestriction( + account_investor1, + latestTime() + duration.seconds(1), + rollingPeriod, + latestTime() + duration.days(4), + 0, + { + from: token_owner + } + ); + + console.log("c"); + var txNumber = 10; //define fuzz test amount for tx + + for (var j=0; j individualRestrictTotalAmount || accumulatedTxValue > defaultRestrictionAmount){ + + console.log("tx should fail"); + + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) + ); + + console.log("tx failed as expected due to over limit"); + + } else if (accumulatedTxValue <= individualRestrictTotalAmount ){ + + console.log("tx should succeed"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + sumOfLastPeriod = sumOfLastPeriod + transactionAmount; + + console.log("tx succeeded"); + } + console.log("3"); + }; + + + // remove individual restriction and it should fall to default restriction + await I_VolumeRestrictionTM.removeIndividualRestriction(account_investor1, {from: token_owner}); + console.log("individual restriction now removed --> fall back to default restriction"); + + for (var j=0; j defaultRestrictionAmount){ + + console.log("tx should fail"); + + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) + ); + + console.log("tx failed as expected due to over limit"); + + } else if ( accumulatedTxValue <= defaultRestrictionAmount ){ + + console.log("tx should succeed"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + sumOfLastPeriod = sumOfLastPeriod + transactionAmount; + + console.log("tx succeeded"); + } + console.log("5"); + } + + + // await revertToSnapshot(snapId); + await I_VolumeRestrictionTM.removeDefaultRestriction(account_investor1, {from: token_owner}); + } + + }); + + + + it("Should work with fuzz test for randomly adding / removing individual daily restriction and perform multipel transactions", async() => { + + + var testRepeat = 5; + var txNumber = 10; + var dailyRestriction = false; + var startTime = 1; + var sumOfLastPeriod = 0; + var accumulatedTimeIncrease = 0; + var dailyLimitUsed = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + + for (var i = 0; i < testRepeat; i++) { + + //randomly add or removing existing daily restriction + var random_action = Math.random() >= 0.5; // true -> add false -> remove + + if ( dailyRestriction == false && random_action == true ){ + + console.log("1"); + + var dailyRestrictionAmount = Math.floor(Math.random() * 10); + + //add daily restriction + let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( + account_investor1, + web3.utils.toWei(dailyRestrictionAmount.toString()), + latestTime() + duration.seconds(startTime), + latestTime() + duration.days(50), + 0, + { + from: token_owner + } + ); + + dailyRestriction = true; + + console.log("added daily restriction"); + + } else if ( dailyRestriction == true && random_action == false ) { + + console.log("2"); + + //remove daily restriction + await I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor1, {from: token_owner}); + console.log("removed daily restriction"); + + dailyRestriction = false; + } + + + // perform multiple transactions + + for (var j=0; j dailyRestrictionAmount){ + + console.log("tx should fail"); + + await catchRevert( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) + ); + + console.log("tx failed as expected due to over limit"); + + } else if ( (todayLimitUsed + transactionAmount) <= dailyRestrictionAmount ){ + + console.log("tx should succeed"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + dailyLimitUsed[dayNumber] = dailyLimitUsed[dayNumber] + transactionAmount; + + console.log("tx succeeded"); + } + console.log("5"); + } + + if ( dailyRestriction == true ) { + + //remove daily restriction + await I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor1, {from: token_owner}); + console.log("removed daily restriction"); + } + + } + + + + }); + + + }); }); \ No newline at end of file From 40343b9fed447906ae935da57f47a57a950cd296 Mon Sep 17 00:00:00 2001 From: comeonbuddy Date: Fri, 21 Dec 2018 16:34:27 +0700 Subject: [PATCH 51/56] fix input random number issue --- ...zer_volumn_restriction_transfer_manager.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/test/z_fuzzer_volumn_restriction_transfer_manager.js b/test/z_fuzzer_volumn_restriction_transfer_manager.js index 61ea5c32a..def755227 100644 --- a/test/z_fuzzer_volumn_restriction_transfer_manager.js +++ b/test/z_fuzzer_volumn_restriction_transfer_manager.js @@ -231,7 +231,14 @@ contract('VolumeRestrictionTransferManager', accounts => { console.log("fuzzer number " + i); var individualRestrictTotalAmount = Math.floor(Math.random() * 10); + if ( individualRestrictTotalAmount == 0 ) { + individualRestrictTotalAmount = 1; + } + var dailyRestrictionAmount = Math.floor(Math.random() * 10); + if ( dailyRestrictionAmount == 0 ) { + dailyRestrictionAmount = 1; + } var rollingPeriod = 2; var sumOfLastPeriod = 0; @@ -313,13 +320,16 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should work with fuzz test for individual restriction and general restriction", async() => { // let snapId = await takeSnapshot(); - var testRepeat = 5; + var testRepeat = 0; for (var i = 0; i < testRepeat; i++) { console.log("fuzzer number " + i); var individualRestrictTotalAmount = Math.floor(Math.random() * 10); + if ( individualRestrictTotalAmount == 0 ) { + individualRestrictTotalAmount = 1; + } var defaultRestrictionAmount = Math.floor(Math.random() * 10); var rollingPeriod = 2; var sumOfLastPeriod = 0; @@ -443,7 +453,7 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should work with fuzz test for randomly adding / removing individual daily restriction and perform multipel transactions", async() => { - var testRepeat = 5; + var testRepeat = 0; var txNumber = 10; var dailyRestriction = false; var startTime = 1; @@ -461,6 +471,9 @@ contract('VolumeRestrictionTransferManager', accounts => { console.log("1"); var dailyRestrictionAmount = Math.floor(Math.random() * 10); + if ( dailyRestrictionAmount == 0) { + dailyRestrictionAmount = 1; + } //add daily restriction let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( @@ -535,7 +548,7 @@ contract('VolumeRestrictionTransferManager', accounts => { console.log("5"); } - if ( dailyRestriction == true ) { + if ( dailyRestriction == true ) { //remove daily restriction await I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor1, {from: token_owner}); From 214863666bfa54258cf7d49db1ea12d922fff4af Mon Sep 17 00:00:00 2001 From: comeonbuddy Date: Mon, 31 Dec 2018 14:32:08 +0800 Subject: [PATCH 52/56] finished test case 4 --- ...zer_volumn_restriction_transfer_manager.js | 150 ++++++++++++------ 1 file changed, 102 insertions(+), 48 deletions(-) diff --git a/test/z_fuzzer_volumn_restriction_transfer_manager.js b/test/z_fuzzer_volumn_restriction_transfer_manager.js index def755227..4d8d5db76 100644 --- a/test/z_fuzzer_volumn_restriction_transfer_manager.js +++ b/test/z_fuzzer_volumn_restriction_transfer_manager.js @@ -224,7 +224,7 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should work with multiple transaction within 1 day with Individual and daily Restrictions", async() => { // let snapId = await takeSnapshot(); - var testRepeat = 5; + var testRepeat = 1; for (var i = 0; i < testRepeat; i++) { @@ -320,14 +320,14 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should work with fuzz test for individual restriction and general restriction", async() => { // let snapId = await takeSnapshot(); - var testRepeat = 0; + var testRepeat = 1; for (var i = 0; i < testRepeat; i++) { console.log("fuzzer number " + i); var individualRestrictTotalAmount = Math.floor(Math.random() * 10); - if ( individualRestrictTotalAmount == 0 ) { + if (individualRestrictTotalAmount == 0 ) { individualRestrictTotalAmount = 1; } var defaultRestrictionAmount = Math.floor(Math.random() * 10); @@ -362,33 +362,30 @@ contract('VolumeRestrictionTransferManager', accounts => { ); console.log("c"); - var txNumber = 10; //define fuzz test amount for tx - - for (var j=0; j individualRestrictTotalAmount || accumulatedTxValue > defaultRestrictionAmount){ - + if (accumulatedTxValue > individualRestrictTotalAmount || accumulatedTxValue > defaultRestrictionAmount) { console.log("tx should fail"); - await catchRevert( + await catchRevert( I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) - ); + ); console.log("tx failed as expected due to over limit"); + } else if (accumulatedTxValue <= individualRestrictTotalAmount) { - } else if (accumulatedTxValue <= individualRestrictTotalAmount ){ - console.log("tx should succeed"); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); @@ -418,17 +415,13 @@ contract('VolumeRestrictionTransferManager', accounts => { // check against daily and total restrictions to determine if the transaction should pass or not - if(accumulatedTxValue > defaultRestrictionAmount){ - + if (accumulatedTxValue > defaultRestrictionAmount) { console.log("tx should fail"); - await catchRevert( I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) - ); - + ); console.log("tx failed as expected due to over limit"); - - } else if ( accumulatedTxValue <= defaultRestrictionAmount ){ + } else if ( accumulatedTxValue <= defaultRestrictionAmount ) { console.log("tx should succeed"); @@ -453,29 +446,28 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should work with fuzz test for randomly adding / removing individual daily restriction and perform multipel transactions", async() => { - var testRepeat = 0; + var testRepeat = 1; var txNumber = 10; var dailyRestriction = false; var startTime = 1; var sumOfLastPeriod = 0; var accumulatedTimeIncrease = 0; - var dailyLimitUsed = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + var dailyLimitUsed = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; for (var i = 0; i < testRepeat; i++) { - //randomly add or removing existing daily restriction + // randomly add or removing existing daily restriction var random_action = Math.random() >= 0.5; // true -> add false -> remove - if ( dailyRestriction == false && random_action == true ){ - + if (dailyRestriction === false && random_action === true) { console.log("1"); var dailyRestrictionAmount = Math.floor(Math.random() * 10); - if ( dailyRestrictionAmount == 0) { + if (dailyRestrictionAmount === 0) { dailyRestrictionAmount = 1; } - //add daily restriction + // add daily restriction let tx = await I_VolumeRestrictionTM.addIndividualDailyRestriction( account_investor1, web3.utils.toWei(dailyRestrictionAmount.toString()), @@ -490,24 +482,19 @@ contract('VolumeRestrictionTransferManager', accounts => { dailyRestriction = true; console.log("added daily restriction"); - - } else if ( dailyRestriction == true && random_action == false ) { - + } else if (dailyRestriction === true && random_action === false) { console.log("2"); - - //remove daily restriction + // remove daily restriction await I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor1, {from: token_owner}); console.log("removed daily restriction"); dailyRestriction = false; } - // perform multiple transactions - for (var j=0; j { // generate a random amount var transactionAmount = Math.floor(Math.random() * 10); - //check today's limit + // check today's limit var dayNumber = Math.floor(accumulatedTimeIncrease/(24*3600)) + 1; var todayLimitUsed = dailyLimitUsed[dayNumber]; console.log("todayLimitUsed is " + todayLimitUsed + " transactionAmount is " + transactionAmount + " dayNumber is " + dayNumber + " dailyRestrictionAmount is " + dailyRestrictionAmount); - // check against daily and total restrictions to determine if the transaction should pass or not - if((todayLimitUsed + transactionAmount) > dailyRestrictionAmount){ - + if ((todayLimitUsed + transactionAmount) > dailyRestrictionAmount) { console.log("tx should fail"); - await catchRevert( + await catchRevert( I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) - ); + ); console.log("tx failed as expected due to over limit"); - - } else if ( (todayLimitUsed + transactionAmount) <= dailyRestrictionAmount ){ - + } else if ((todayLimitUsed + transactionAmount) <= dailyRestrictionAmount) { + console.log("tx should succeed"); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); @@ -548,20 +532,90 @@ contract('VolumeRestrictionTransferManager', accounts => { console.log("5"); } - if ( dailyRestriction == true ) { + if (dailyRestriction === true) { - //remove daily restriction + // remove daily restriction await I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor1, {from: token_owner}); console.log("removed daily restriction"); } } + }); - }); + it("fuzz test exception list", async () => { + var testRepeat = 1; + + for (var i = 0; i < testRepeat; i++) { + console.log("fuzzer number " + i); + + var individualRestrictTotalAmount = Math.floor(Math.random() * 10); + if (individualRestrictTotalAmount === 0 ) { + individualRestrictTotalAmount = 1; + } + var defaultRestrictionAmount = Math.floor(Math.random() * 10); + var rollingPeriod = 2; + var sumOfLastPeriod = 0; + + console.log("a"); + + // 1 - add individual restriction with a random number + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei(individualRestrictTotalAmount.toString()), + latestTime() + duration.seconds(1), + rollingPeriod, + latestTime() + duration.days(3), + 0, + { + from: token_owner + } + ); + + tx = await I_VolumeRestrictionTM.changeExemptWalletList(account_investor1, true, {from: token_owner}); + + console.log("b"); + + var txNumber = 10; // define fuzz test amount for tx + + for (var j = 0; j < txNumber; j++) { + await increaseTime(duration.seconds(5)); + console.log("2"); + + // generate a random amount + var transactionAmount = Math.floor(Math.random() * 10); + var accumulatedTxValue = transactionAmount + sumOfLastPeriod; + + console.log("sumOfLastPeriod is " + sumOfLastPeriod + " transactionAmount is " + transactionAmount + " individualRestrictTotalAmount is " + individualRestrictTotalAmount + " defaultRestrictionAmount is " + defaultRestrictionAmount); + + // check against daily and total restrictions to determine if the transaction should pass or not + if (accumulatedTxValue > individualRestrictTotalAmount) { + console.log("tx should fail but still succeed due to investor in exempt list"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + console.log("tx passed as expected"); + } else if (accumulatedTxValue <= individualRestrictTotalAmount) { + + console.log("tx should succeed"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + sumOfLastPeriod = sumOfLastPeriod + transactionAmount; + + console.log("tx succeeded"); + } + console.log("3" + txNumber); + }; + + await I_VolumeRestrictionTM.removeIndividualRestriction(account_investor1, {from: token_owner}); + console.log("removed daily restriction"); + } + + }); }); From 7b7f3ab18762199b75d7d52bb4b31a5ccd87d561 Mon Sep 17 00:00:00 2001 From: comeonbuddy Date: Wed, 2 Jan 2019 13:53:44 +0800 Subject: [PATCH 53/56] WIP --- ...zer_volumn_restriction_transfer_manager.js | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/test/z_fuzzer_volumn_restriction_transfer_manager.js b/test/z_fuzzer_volumn_restriction_transfer_manager.js index 4d8d5db76..0bb7b864c 100644 --- a/test/z_fuzzer_volumn_restriction_transfer_manager.js +++ b/test/z_fuzzer_volumn_restriction_transfer_manager.js @@ -219,12 +219,12 @@ contract('VolumeRestrictionTransferManager', accounts => { }); - describe("Fuzz test", async() => { + describe("Fuzz test", async () => { it("Should work with multiple transaction within 1 day with Individual and daily Restrictions", async() => { // let snapId = await takeSnapshot(); - var testRepeat = 1; + var testRepeat = 2; for (var i = 0; i < testRepeat; i++) { @@ -268,9 +268,9 @@ contract('VolumeRestrictionTransferManager', accounts => { from: token_owner } ); - + console.log("c"); - var txNumber = 10; //define fuzz test amount for tx within 24 hrs + var txNumber = 10; // define fuzz test amount for tx within 24 hrs for (var j=0; j { console.log("sumOfLastPeriod is " + sumOfLastPeriod + " transactionAmount is " + transactionAmount + " individualRestrictTotalAmount is " + individualRestrictTotalAmount + " dailyRestrictionAmount is " + dailyRestrictionAmount); - // check against daily and total restrictions to determine if the transaction should pass or not - if(accumulatedTxValue > individualRestrictTotalAmount || accumulatedTxValue > dailyRestrictionAmount){ - + if (accumulatedTxValue > individualRestrictTotalAmount || accumulatedTxValue > dailyRestrictionAmount) { console.log("tx should fail"); - await catchRevert( + await catchRevert( I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) - ); + ); console.log("tx failed as expected due to over limit"); - } else if (accumulatedTxValue <= individualRestrictTotalAmount && accumulatedTxValue <= dailyRestrictionAmount ){ - + } else if (accumulatedTxValue <= individualRestrictTotalAmount && accumulatedTxValue <= dailyRestrictionAmount) { console.log("tx should succeed"); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); @@ -313,14 +310,11 @@ contract('VolumeRestrictionTransferManager', accounts => { await I_VolumeRestrictionTM.removeIndividualRestriction(account_investor1, {from: token_owner}); await I_VolumeRestrictionTM.removeIndividualDailyRestriction(account_investor1, {from: token_owner}); } - }); - it("Should work with fuzz test for individual restriction and general restriction", async() => { - // let snapId = await takeSnapshot(); - - var testRepeat = 1; + // let snapId = await takeSnapshot(); + var testRepeat = 0; for (var i = 0; i < testRepeat; i++) { @@ -446,7 +440,7 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should work with fuzz test for randomly adding / removing individual daily restriction and perform multipel transactions", async() => { - var testRepeat = 1; + var testRepeat = 0; var txNumber = 10; var dailyRestriction = false; var startTime = 1; @@ -546,8 +540,8 @@ contract('VolumeRestrictionTransferManager', accounts => { - it("fuzz test exception list", async () => { - var testRepeat = 1; + it("should work in all cases if a sender is added in the exception list", async () => { + var testRepeat = 0; for (var i = 0; i < testRepeat; i++) { console.log("fuzzer number " + i); From 40cfa3df5d2ea2537d1905ddf57bb0d919363b30 Mon Sep 17 00:00:00 2001 From: comeonbuddy Date: Thu, 3 Jan 2019 10:58:28 +0800 Subject: [PATCH 54/56] WIP --- ...zer_volumn_restriction_transfer_manager.js | 94 ++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/test/z_fuzzer_volumn_restriction_transfer_manager.js b/test/z_fuzzer_volumn_restriction_transfer_manager.js index 0bb7b864c..ce668ad26 100644 --- a/test/z_fuzzer_volumn_restriction_transfer_manager.js +++ b/test/z_fuzzer_volumn_restriction_transfer_manager.js @@ -224,7 +224,7 @@ contract('VolumeRestrictionTransferManager', accounts => { it("Should work with multiple transaction within 1 day with Individual and daily Restrictions", async() => { // let snapId = await takeSnapshot(); - var testRepeat = 2; + var testRepeat = 0; for (var i = 0; i < testRepeat; i++) { @@ -605,6 +605,98 @@ contract('VolumeRestrictionTransferManager', accounts => { console.log("3" + txNumber); }; + await I_VolumeRestrictionTM.removeIndividualRestriction(account_investor1, {from: token_owner}); + console.log("removed daily restriction"); + } + }); + + it("should work if IR is modified", async () => { + var testRepeat = 1; + + for (var i = 0; i < testRepeat; i++) { + console.log("fuzzer number " + i); + + var individualRestrictTotalAmount = Math.floor(Math.random() * 10); + if (individualRestrictTotalAmount === 0 ) { + individualRestrictTotalAmount = 1; + } + var defaultRestrictionAmount = Math.floor(Math.random() * 10); + var rollingPeriod = 2; + var sumOfLastPeriod = 0; + + console.log("a"); + + // 1 - add individual restriction with a random number + let tx = await I_VolumeRestrictionTM.addIndividualRestriction( + account_investor1, + web3.utils.toWei(individualRestrictTotalAmount.toString()), + latestTime() + duration.days(2), + rollingPeriod, + latestTime() + duration.days(5), + 0, + { + from: token_owner + } + ); + + console.log("b"); + + var txNumber = 10; // define fuzz test amount for tx + + for (var j = 0; j < txNumber; j++) { + console.log("starting test number "+j); + + // modify IR + var newIR = Math.floor(Math.random() * 10); + if (newIR === 0 ) { + newIR = 1; + } + + console.log("3"); + console.log(latestTime()); + console.log((await I_VolumeRestrictionTM.individualRestriction(account_investor1, {from: token_owner}))); + console.log(latestTime() + duration.days(1+j)); + await I_VolumeRestrictionTM.modifyIndividualRestriction( + account_investor1, + web3.utils.toWei(newIR.toString()), + latestTime() + duration.days(1+j), + rollingPeriod, + latestTime() + duration.days(5+j), + 0, + { from: token_owner } + ); + + console.log("4"); + let snapId = await takeSnapshot(); + await increaseTime(duration.days(2+j)); + + // generate a random amount + var transactionAmount = Math.floor(Math.random() * 10); + var accumulatedTxValue = transactionAmount + sumOfLastPeriod; + + console.log("sumOfLastPeriod is " + sumOfLastPeriod + " transactionAmount is " + transactionAmount + " individualRestrictTotalAmount is " + individualRestrictTotalAmount + " defaultRestrictionAmount is " + defaultRestrictionAmount); + + // check against daily and total restrictions to determine if the transaction should pass or not + if (accumulatedTxValue > newIR) { + console.log("tx should fail but still succeed due to investor in exempt list"); + + await catchRevert (I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1})); + + console.log("tx failed as expected"); + } else if (accumulatedTxValue <= newIR) { + + console.log("tx should succeed"); + + await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); + + sumOfLastPeriod = sumOfLastPeriod + transactionAmount; + + console.log("tx succeeded"); + } + await revertToSnapshot(snapId); + console.log("finished test number "+j); + }; + await I_VolumeRestrictionTM.removeIndividualRestriction(account_investor1, {from: token_owner}); console.log("removed daily restriction"); } From b13e08ca9032c6de7313427b5c2b91bcf9da1514 Mon Sep 17 00:00:00 2001 From: satyam Date: Thu, 3 Jan 2019 13:44:47 +0530 Subject: [PATCH 55/56] reinvent the test case --- ...zer_volumn_restriction_transfer_manager.js | 77 +++-- yarn.lock | 319 ++++++------------ 2 files changed, 152 insertions(+), 244 deletions(-) diff --git a/test/z_fuzzer_volumn_restriction_transfer_manager.js b/test/z_fuzzer_volumn_restriction_transfer_manager.js index ce668ad26..b03ab6fdc 100644 --- a/test/z_fuzzer_volumn_restriction_transfer_manager.js +++ b/test/z_fuzzer_volumn_restriction_transfer_manager.js @@ -100,6 +100,16 @@ contract('VolumeRestrictionTransferManager', accounts => { return sum; } + async function printIR(data) { + console.log(` + Allowed Tokens : ${data[0].dividedBy(new BigNumber(10).pow(18)).toNumber()} + StartTime : ${data[1].toNumber()} + Rolling Period : ${data[2].toNumber()} + EndTime : ${data[3].toNumber()} + Restriction Type: ${data[4].toNumber() == 0 ? "Fixed" : "Percentage"} + `) + } + before(async() => { // Accounts setup account_polymath = accounts[0]; @@ -610,26 +620,25 @@ contract('VolumeRestrictionTransferManager', accounts => { } }); - it("should work if IR is modified", async () => { + it("Should work if IR is modified", async () => { + + console.log(`\t\t Starting of the IR modification test case`.blue); + var testRepeat = 1; for (var i = 0; i < testRepeat; i++) { - console.log("fuzzer number " + i); - - var individualRestrictTotalAmount = Math.floor(Math.random() * 10); - if (individualRestrictTotalAmount === 0 ) { - individualRestrictTotalAmount = 1; - } - var defaultRestrictionAmount = Math.floor(Math.random() * 10); + console.log("\t\t fuzzer number " + i); + let precision = 100; + var individualRestrictionTotalAmount = Math.floor(Math.random() * (10 * precision - 1 * precision) + 1 * precision) / (1*precision); var rollingPeriod = 2; var sumOfLastPeriod = 0; - console.log("a"); - + console.log(`\t\t Add individual restriction with TotalAmount: ${individualRestrictionTotalAmount}\n`.green); + // 1 - add individual restriction with a random number let tx = await I_VolumeRestrictionTM.addIndividualRestriction( account_investor1, - web3.utils.toWei(individualRestrictTotalAmount.toString()), + web3.utils.toWei(individualRestrictionTotalAmount.toString()), latestTime() + duration.days(2), rollingPeriod, latestTime() + duration.days(5), @@ -639,23 +648,20 @@ contract('VolumeRestrictionTransferManager', accounts => { } ); - console.log("b"); + console.log(`\t\t Restriction successfully added \n`); var txNumber = 10; // define fuzz test amount for tx for (var j = 0; j < txNumber; j++) { - console.log("starting test number "+j); + console.log(`\t\t Test number: ${j}\n`); // modify IR - var newIR = Math.floor(Math.random() * 10); - if (newIR === 0 ) { - newIR = 1; - } + var newIR = Math.floor(Math.random() * (10 * precision - 1 * precision) + 1 * precision) / (1*precision); - console.log("3"); - console.log(latestTime()); - console.log((await I_VolumeRestrictionTM.individualRestriction(account_investor1, {from: token_owner}))); - console.log(latestTime() + duration.days(1+j)); + printIR(await I_VolumeRestrictionTM.individualRestriction(account_investor1, {from: token_owner})); + + console.log(`\t\t Modification of the IR with new startTime: ${latestTime() + duration.days(1+j)} and new total amount: ${newIR} `.green); + await I_VolumeRestrictionTM.modifyIndividualRestriction( account_investor1, web3.utils.toWei(newIR.toString()), @@ -666,39 +672,36 @@ contract('VolumeRestrictionTransferManager', accounts => { { from: token_owner } ); - console.log("4"); + console.log(`\t\t Successfully IR modified`); let snapId = await takeSnapshot(); await increaseTime(duration.days(2+j)); // generate a random amount - var transactionAmount = Math.floor(Math.random() * 10); - var accumulatedTxValue = transactionAmount + sumOfLastPeriod; - - console.log("sumOfLastPeriod is " + sumOfLastPeriod + " transactionAmount is " + transactionAmount + " individualRestrictTotalAmount is " + individualRestrictTotalAmount + " defaultRestrictionAmount is " + defaultRestrictionAmount); + var transactionAmount = Math.floor(Math.random() * (10 * precision - 1 * precision) + 1 * precision) / (1*precision); // check against daily and total restrictions to determine if the transaction should pass or not - if (accumulatedTxValue > newIR) { - console.log("tx should fail but still succeed due to investor in exempt list"); + if (transactionAmount > newIR) { + console.log("\t\t Tx should fail"); - await catchRevert (I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1})); + await catchRevert ( + I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}) + ); - console.log("tx failed as expected"); - } else if (accumulatedTxValue <= newIR) { + console.log("\t\t Tx failed as expected"); + } else if (transactionAmount <= newIR) { - console.log("tx should succeed"); + console.log("\t\t Tx should succeed"); await I_SecurityToken.transfer(account_investor3, web3.utils.toWei(transactionAmount.toString()), {from: account_investor1}); - sumOfLastPeriod = sumOfLastPeriod + transactionAmount; - - console.log("tx succeeded"); + console.log("\t\t Tx succeeded"); } await revertToSnapshot(snapId); - console.log("finished test number "+j); + console.log("\t\t Finished test number "+j); }; await I_VolumeRestrictionTM.removeIndividualRestriction(account_investor1, {from: token_owner}); - console.log("removed daily restriction"); + console.log("\t\t Removed daily restriction"); } }); diff --git a/yarn.lock b/yarn.lock index 63f3ead17..810d278d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,14 +5,12 @@ "@babel/code-frame@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" - integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== dependencies: "@babel/highlight" "^7.0.0" "@babel/highlight@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" - integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw== dependencies: chalk "^2.0.0" esutils "^2.0.2" @@ -38,7 +36,6 @@ "@types/node@^10.3.2": version "10.12.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.2.tgz#d77f9faa027cadad9c912cd47f4f8b07b0fb0864" - integrity sha512-53ElVDSnZeFUUFIYzI8WLQ25IhWzb6vbddNp8UHlXQyU0ET2RhV5zg0NfubzU7iNMh5bBXb0htCzfvrSVNgzaQ== abbrev@1: version "1.1.1" @@ -73,15 +70,9 @@ acorn-dynamic-import@^2.0.0: dependencies: acorn "^4.0.3" -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" - dependencies: - acorn "^3.0.4" - -acorn@^3.0.4: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" +acorn-jsx@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" acorn@^4.0.3: version "4.0.13" @@ -94,7 +85,6 @@ acorn@^5.0.0: acorn@^6.0.2: version "6.0.4" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.4.tgz#77377e7353b72ec5104550aa2d2097a2fd40b754" - integrity sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg== aes-js@3.0.0: version "3.0.0" @@ -104,10 +94,6 @@ aes-js@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.1.tgz#89fd1f94ae51b4c72d62466adc1a7323ff52f072" -ajv-keywords@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" - ajv-keywords@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" @@ -130,6 +116,15 @@ ajv@^6.1.0: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^6.5.3, ajv@^6.6.1: + version "6.6.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.6.2.tgz#caceccf474bf3fc3ce3b147443711a24063cc30d" + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -161,11 +156,15 @@ ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" +ansi-regex@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.0.0.tgz#70de791edf021404c3fd615aa89118ae0432e5a9" + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" dependencies: @@ -206,13 +205,6 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -arguments-extended@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/arguments-extended/-/arguments-extended-0.0.3.tgz#6107e4917d0eb6f0a4dd66320fc15afc72ef4946" - dependencies: - extended "~0.0.3" - is-extended "~0.0.8" - arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -231,14 +223,6 @@ arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" -array-extended@~0.0.3, array-extended@~0.0.4, array-extended@~0.0.5: - version "0.0.11" - resolved "https://registry.yarnpkg.com/array-extended/-/array-extended-0.0.11.tgz#d7144ae748de93ca726f121009dbff1626d164bd" - dependencies: - arguments-extended "~0.0.3" - extended "~0.0.3" - is-extended "~0.0.3" - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -293,6 +277,10 @@ assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" @@ -982,19 +970,14 @@ big.js@^3.1.3: bignumber.js@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-5.0.0.tgz#fbce63f09776b3000a83185badcde525daf34833" - integrity sha512-KWTu6ZMVk9sxlDJQh2YH1UOnfDP8O8TpxUxgQG/vKASoSnEjK9aVuOueFaPcQEYQ5fyNXNTOYwYw3099RYebWg== bignumber.js@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-4.1.0.tgz#db6f14067c140bd46624815a7916c92d9b6c24b1" -bignumber.js@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-5.0.0.tgz#fbce63f09776b3000a83185badcde525daf34833" - -bignumber.js@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-6.0.0.tgz#bbfa047644609a5af093e9cbd83b0461fa3f6002" +bignumber.js@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" "bignumber.js@git+https://github.com/debris/bignumber.js#master": version "2.0.7" @@ -1092,7 +1075,7 @@ borc@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/borc/-/borc-2.0.3.tgz#08845ea73a6d3211120928ee3929f8dc2de9f52e" dependencies: - bignumber.js "^7.2.1" + bignumber.js "^6.0.0" commander "^2.15.0" ieee754 "^1.1.8" json-text-sequence "^0.1" @@ -1373,9 +1356,9 @@ chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chardet@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" checkpoint-store@^1.1.0: version "1.1.0" @@ -1530,13 +1513,7 @@ colors@^1.1.2: version "1.3.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.2.tgz#2df8ff573dfbf255af562f8ce7181d6b971a359b" -combined-stream@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" - dependencies: - delayed-stream "~1.0.0" - -combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" dependencies: @@ -1576,15 +1553,6 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - console-browserify@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" @@ -1705,7 +1673,6 @@ cross-spawn@^5.0.1: cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" path-key "^2.0.1" @@ -1761,14 +1728,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -date-extended@~0.0.3: - version "0.0.6" - resolved "https://registry.yarnpkg.com/date-extended/-/date-extended-0.0.6.tgz#23802d57dd1bf7818813fe0c32e851a86da267c9" - dependencies: - array-extended "~0.0.3" - extended "~0.0.3" - is-extended "~0.0.3" - date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" @@ -1805,10 +1764,6 @@ decamelize@^1.0.0, decamelize@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" -declare.js@~0.0.4: - version "0.0.8" - resolved "https://registry.yarnpkg.com/declare.js/-/declare.js-0.0.8.tgz#0478adff9564c004f51df73d8bc134019d28dcde" - decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -2246,9 +2201,9 @@ escope@^3.6.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-config-standard@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-11.0.0.tgz#87ee0d3c9d95382dc761958cbb23da9eea31e0ba" +eslint-config-standard@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz#638b4c65db0bd5a41319f96bba1f15ddad2107d9" eslint-import-resolver-node@^0.3.1: version "0.3.2" @@ -2267,7 +2222,6 @@ eslint-module-utils@^2.2.0: eslint-plugin-es@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.3.1.tgz#5acb2565db4434803d1d46a9b4cbc94b345bd028" - integrity sha512-9XcVyZiQRVeFjqHw8qHNDAZcQLqaHlOGGpeYqzYh8S4JYCWTCO3yzyen8yVmA5PratfzTRWDwCOFphtDEG+w/w== dependencies: eslint-utils "^1.3.0" regexpp "^2.0.0" @@ -2287,9 +2241,9 @@ eslint-plugin-import@^2.10.0: read-pkg-up "^2.0.0" resolve "^1.6.0" -eslint-plugin-node@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-6.0.1.tgz#bf19642298064379315d7a4b2a75937376fa05e4" +eslint-plugin-node@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-8.0.0.tgz#fb9e8911f4543514f154bb6a5924b599aa645568" dependencies: eslint-plugin-es "^1.3.1" eslint-utils "^1.3.1" @@ -2298,17 +2252,17 @@ eslint-plugin-node@^6.0.1: resolve "^1.8.1" semver "^5.5.0" -eslint-plugin-promise@^3.7.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz#65ebf27a845e3c1e9d6f6a5622ddd3801694b621" +eslint-plugin-promise@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz#2d074b653f35a23d1ba89d8e976a985117d1c6a2" -eslint-plugin-standard@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz#2a9e21259ba4c47c02d53b2d0c9135d4b1022d47" +eslint-plugin-standard@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.0.tgz#f845b45109c99cd90e77796940a344546c8f6b5c" -eslint-scope@^3.7.1: - version "3.7.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" +eslint-scope@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" @@ -2316,15 +2270,14 @@ eslint-scope@^3.7.1: eslint-utils@^1.3.0, eslint-utils@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512" - integrity sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q== eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" -eslint@^4.19.1: - version "4.19.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" +eslint@^5.8.0: + version "5.11.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.11.1.tgz#8deda83db9f354bf9d3f53f9677af7e0e13eadda" dependencies: "@babel/code-frame" "^7.0.0" ajv "^6.5.3" @@ -2335,7 +2288,7 @@ eslint@^4.19.1: eslint-scope "^4.0.0" eslint-utils "^1.3.1" eslint-visitor-keys "^1.0.0" - espree "^4.0.0" + espree "^5.0.0" esquery "^1.0.1" esutils "^2.0.2" file-entry-cache "^2.0.0" @@ -2345,7 +2298,6 @@ eslint@^4.19.1: ignore "^4.0.6" imurmurhash "^0.1.4" inquirer "^6.1.0" - is-resolvable "^1.1.0" js-yaml "^3.12.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.3.0" @@ -2365,9 +2317,9 @@ eslint@^4.19.1: table "^5.0.2" text-table "^0.2.0" -espree@^3.5.4: - version "3.5.4" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" +espree@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.0.tgz#fc7f984b62b36a0f543b13fb9cd7b9f4a7f5b65c" dependencies: acorn "^6.0.2" acorn-jsx "^5.0.0" @@ -2616,9 +2568,9 @@ ethereumjs-wallet@^0.6.0: utf8 "^3.0.0" uuid "^3.3.2" -ethers@^3.0.15: - version "3.0.29" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-3.0.29.tgz#ce8139955b4ed44456eb6764b089bb117c86775d" +ethers@^4.0.7: + version "4.0.20" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.20.tgz#2b072b283bb19f4870daf42cf5593e5375697504" dependencies: "@types/node" "^10.3.2" aes-js "3.0.0" @@ -2767,27 +2719,15 @@ extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" -extended@0.0.6, extended@~0.0.3: - version "0.0.6" - resolved "https://registry.yarnpkg.com/extended/-/extended-0.0.6.tgz#7fb8bf7b9dae397586e48570acfd642c78e50669" - dependencies: - extender "~0.0.5" - -extender@~0.0.5: - version "0.0.10" - resolved "https://registry.yarnpkg.com/extender/-/extender-0.0.10.tgz#589c07482be61a1460b6d81f9c24aa67e8f324cd" - dependencies: - declare.js "~0.0.4" - extendr@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/extendr/-/extendr-2.1.0.tgz#301aa0bbea565f4d2dc8f570f2a22611a8527b56" dependencies: typechecker "~2.0.1" -external-editor@^2.0.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" +external-editor@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" dependencies: chardet "^0.7.0" iconv-lite "^0.4.24" @@ -2836,15 +2776,6 @@ fake-merkle-patricia-tree@^1.0.1: dependencies: checkpoint-store "^1.1.0" -fast-csv@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/fast-csv/-/fast-csv-2.4.1.tgz#bd7dd268391f729367b59445b8dd0ad026881b26" - dependencies: - extended "0.0.6" - is-extended "0.0.10" - object-extended "0.0.7" - string-extended "0.0.8" - fast-deep-equal@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" @@ -2980,7 +2911,7 @@ form-data@~2.3.2: resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" dependencies: asynckit "^0.4.0" - combined-stream "^1.0.6" + combined-stream "1.0.6" mime-types "^2.1.12" forwarded@~0.1.2: @@ -3053,9 +2984,9 @@ fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" -fs@0.0.1-security: - version "0.0.1-security" - resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.1-security.tgz#8a7bd37186b6dddf3813f23858b57ecaaf5e41d4" +fs@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.2.tgz#e1f244ef3933c1b2a64bd4799136060d0f5914f8" fsevents@^1.0.0, fsevents@^1.2.2: version "1.2.4" @@ -3348,7 +3279,6 @@ hash-base@^3.0.0: hash.js@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" - integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== dependencies: inherits "^2.0.3" minimalistic-assert "^1.0.0" @@ -3453,9 +3383,13 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" -ignore@^3.3.3, ignore@^3.3.6: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + +ignore@^5.0.2: + version "5.0.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.0.4.tgz#33168af4a21e99b00c5d41cbadb6a6cb49903a45" ignorefs@^1.0.0: version "1.2.0" @@ -3499,9 +3433,9 @@ ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" -inquirer@^3.0.6: - version "3.3.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" +inquirer@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.1.tgz#9943fc4882161bdb0b0c9276769c75b32dbfcd52" dependencies: ansi-escapes "^3.0.0" chalk "^2.0.0" @@ -3514,7 +3448,7 @@ inquirer@^3.0.6: run-async "^2.2.0" rxjs "^6.1.0" string-width "^2.1.0" - strip-ansi "^4.0.0" + strip-ansi "^5.0.0" through "^2.3.6" interpret@^1.0.0: @@ -3623,12 +3557,6 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" -is-extended@0.0.10, is-extended@~0.0.3, is-extended@~0.0.8: - version "0.0.10" - resolved "https://registry.yarnpkg.com/is-extended/-/is-extended-0.0.10.tgz#244e140df75bb1c9a3106f412ff182fb534a6d62" - dependencies: - extended "~0.0.3" - is-extglob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" @@ -3761,10 +3689,6 @@ is-regex@^1.0.4: dependencies: has "^1.0.1" -is-resolvable@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - is-retry-allowed@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" @@ -4132,7 +4056,7 @@ lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" -lodash@4.x, lodash@^4.13.1, lodash@^4.14.2, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5: +lodash@4.x, lodash@^4.13.1, lodash@^4.14.2, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" @@ -4338,15 +4262,15 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@~1.36.0: - version "1.36.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397" +mime-db@~1.37.0: + version "1.37.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.18, mime-types@~2.1.19: version "2.1.20" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19" dependencies: - mime-db "~1.37.0" + mime-db "~1.36.0" mime@1.4.1: version "1.4.1" @@ -4567,7 +4491,6 @@ next-tick@1: nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== node-async-loop@^1.2.2: version "1.2.2" @@ -4719,14 +4642,6 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-extended@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/object-extended/-/object-extended-0.0.7.tgz#84fd23f56b15582aeb3e88b05cb55d2432d68a33" - dependencies: - array-extended "~0.0.4" - extended "~0.0.3" - is-extended "~0.0.3" - object-inspect@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" @@ -5210,7 +5125,7 @@ react-dom@^16.2.0: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.10.0" + schedule "^0.5.0" react@^16.2.0: version "16.5.2" @@ -5219,7 +5134,7 @@ react@^16.2.0: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.10.0" + schedule "^0.5.0" read-pkg-up@^1.0.1: version "1.0.1" @@ -5338,9 +5253,9 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexpp@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" +regexpp@^2.0.0, regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" regexpu-core@^2.0.0: version "2.0.0" @@ -5533,15 +5448,11 @@ rustbn.js@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" -rx-lite-aggregates@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" +rxjs@^6.1.0: + version "6.3.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.3.tgz#3c6a7fa420e844a81390fb1158a9ec614f4bad55" dependencies: - rx-lite "*" - -rx-lite@*, rx-lite@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" + tslib "^1.9.0" safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" @@ -5585,9 +5496,9 @@ scandirectory@^2.5.0: safefs "^3.1.2" taskgroup "^4.0.5" -schedule@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/schedule/-/schedule-0.5.0.tgz#c128fffa0b402488b08b55ae74bb9df55cc29cc8" +scheduler@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.10.0.tgz#7988de90fe7edccc774ea175a783e69c40c521e1" dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -5596,9 +5507,9 @@ scrypt-async@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/scrypt-async/-/scrypt-async-1.3.1.tgz#a11fd6fac981b4b823ee01dee0221169500ddae9" -scrypt-js@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4" +scrypt-js@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" scrypt.js@0.2.0, scrypt.js@^0.2.0: version "0.2.0" @@ -5784,10 +5695,12 @@ slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" -slice-ansi@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" +slice-ansi@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.0.0.tgz#5373bdb8559b45676e8541c66916cdd6251612e7" dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" snapdragon-node@^2.0.1: @@ -6071,15 +5984,6 @@ strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" -string-extended@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/string-extended/-/string-extended-0.0.8.tgz#741957dff487b0272a79eec5a44f239ee6f17ccd" - dependencies: - array-extended "~0.0.5" - date-extended "~0.0.3" - extended "~0.0.3" - is-extended "~0.0.3" - string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -6125,6 +6029,12 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.0.0.tgz#f78f68b5d0866c20b2c9b8c61b5298508dc8756f" + dependencies: + ansi-regex "^4.0.0" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -6207,13 +6117,13 @@ swarm-js@0.1.37: tar.gz "^1.0.5" xhr-request-promise "^0.1.2" -table@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" +table@^5.0.2: + version "5.1.1" + resolved "https://registry.yarnpkg.com/table/-/table-5.1.1.tgz#92030192f1b7b51b6eeab23ed416862e47b70837" dependencies: - ajv "^6.5.3" - lodash "^4.17.10" - slice-ansi "1.0.0" + ajv "^6.6.1" + lodash "^4.17.11" + slice-ansi "2.0.0" string-width "^2.1.1" tapable@^0.2.7: @@ -6419,9 +6329,9 @@ truffle-error@^0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/truffle-error/-/truffle-error-0.0.3.tgz#4bf55242e14deee1c7194932709182deff2c97ca" -truffle-hdwallet-provider-privkey@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/truffle-hdwallet-provider-privkey/-/truffle-hdwallet-provider-privkey-0.1.0.tgz#9417047a74ad37d923df926154b6486ffb57f6c9" +truffle-hdwallet-provider-privkey@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/truffle-hdwallet-provider-privkey/-/truffle-hdwallet-provider-privkey-0.2.0.tgz#91e9e8a6a5005970a5b442fa89fc198ecd1f71ef" dependencies: ethereumjs-tx "^1.3.4" ethereumjs-wallet "^0.6.0" @@ -6447,7 +6357,6 @@ truffle@4.1.14: tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" - integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== tty-browserify@0.0.0: version "0.0.0" @@ -6500,10 +6409,6 @@ typedarray-to-buffer@^3.1.2: dependencies: is-typedarray "^1.0.0" -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - uglify-js@^2.8.29: version "2.8.29" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" From 1299ca3102486701d6da95eba770e6262e54e1c8 Mon Sep 17 00:00:00 2001 From: Victor Date: Thu, 3 Jan 2019 09:55:42 -0300 Subject: [PATCH 56/56] Merge conflicts --- CLI/commands/helpers/contract_abis.js | 53 +-- CLI/commands/transfer_manager.js | 543 ++++++++++++++++++++------ 2 files changed, 446 insertions(+), 150 deletions(-) diff --git a/CLI/commands/helpers/contract_abis.js b/CLI/commands/helpers/contract_abis.js index 86b42cea7..e394b2eac 100644 --- a/CLI/commands/helpers/contract_abis.js +++ b/CLI/commands/helpers/contract_abis.js @@ -22,32 +22,34 @@ let ownableABI; let iSTOABI; let iTransferManagerABI; let moduleFactoryABI; +let erc20ABI; try { - polymathRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/PolymathRegistry.json').toString()).abi; - securityTokenRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/SecurityTokenRegistry.json').toString()).abi; - featureRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/FeatureRegistry.json').toString()).abi; - moduleRegistryABI = JSON.parse(require('fs').readFileSync('./build/contracts/ModuleRegistry.json').toString()).abi; - securityTokenABI = JSON.parse(require('fs').readFileSync('./build/contracts/SecurityToken.json').toString()).abi; - stoInterfaceABI = JSON.parse(require('fs').readFileSync('./build/contracts/ISTO.json').toString()).abi; - cappedSTOABI = JSON.parse(require('fs').readFileSync('./build/contracts/CappedSTO.json').toString()).abi; - usdTieredSTOABI = JSON.parse(require('fs').readFileSync('./build/contracts/USDTieredSTO.json').toString()).abi; - generalTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/GeneralTransferManager.json').toString()).abi; - manualApprovalTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/ManualApprovalTransferManager.json').toString()).abi; - countTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/CountTransferManager.json').toString()).abi; - percentageTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/PercentageTransferManager.json').toString()).abi; - volumeRestrictionTMABI = JSON.parse(require('fs').readFileSync('./build/contracts/VolumeRestrictionTM.json').toString()).abi; - generalPermissionManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/GeneralPermissionManager.json').toString()).abi; - polyTokenABI = JSON.parse(require('fs').readFileSync('./build/contracts/PolyTokenFaucet.json').toString()).abi; - cappedSTOFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/CappedSTOFactory.json').toString()).abi; - usdTieredSTOFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/USDTieredSTOFactory.json').toString()).abi; - erc20DividendCheckpointABI = JSON.parse(require('fs').readFileSync('./build/contracts/ERC20DividendCheckpoint.json').toString()).abi; - etherDividendCheckpointABI = JSON.parse(require('fs').readFileSync('./build/contracts/EtherDividendCheckpoint.json').toString()).abi; - moduleInterfaceABI = JSON.parse(require('fs').readFileSync('./build/contracts/IModule.json').toString()).abi; - ownableABI = JSON.parse(require('fs').readFileSync('./build/contracts/Ownable.json').toString()).abi; - iSTOABI = JSON.parse(require('fs').readFileSync('./build/contracts/ISTO.json').toString()).abi - iTransferManagerABI = JSON.parse(require('fs').readFileSync('./build/contracts/ITransferManager.json').toString()).abi - moduleFactoryABI = JSON.parse(require('fs').readFileSync('./build/contracts/ModuleFactory.json').toString()).abi; + polymathRegistryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PolymathRegistry.json`).toString()).abi; + securityTokenRegistryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/SecurityTokenRegistry.json`).toString()).abi; + featureRegistryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/FeatureRegistry.json`).toString()).abi; + moduleRegistryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ModuleRegistry.json`).toString()).abi; + securityTokenABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/SecurityToken.json`).toString()).abi; + stoInterfaceABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ISTO.json`).toString()).abi; + cappedSTOABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/CappedSTO.json`).toString()).abi; + usdTieredSTOABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/USDTieredSTO.json`).toString()).abi; + generalTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/GeneralTransferManager.json`).toString()).abi; + manualApprovalTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ManualApprovalTransferManager.json`).toString()).abi; + countTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/CountTransferManager.json`).toString()).abi; + percentageTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PercentageTransferManager.json`).toString()).abi; + volumeRestrictionTMABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/VolumeRestrictionTM.json`).toString()).abi; + generalPermissionManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/GeneralPermissionManager.json`).toString()).abi; + polyTokenABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/PolyTokenFaucet.json`).toString()).abi; + cappedSTOFactoryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/CappedSTOFactory.json`).toString()).abi; + usdTieredSTOFactoryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/USDTieredSTOFactory.json`).toString()).abi; + erc20DividendCheckpointABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ERC20DividendCheckpoint.json`).toString()).abi; + etherDividendCheckpointABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/EtherDividendCheckpoint.json`).toString()).abi; + moduleInterfaceABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/IModule.json`).toString()).abi; + ownableABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/Ownable.json`).toString()).abi; + iSTOABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ISTO.json`).toString()).abi + iTransferManagerABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ITransferManager.json`).toString()).abi + moduleFactoryABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/ModuleFactory.json`).toString()).abi; + erc20ABI = JSON.parse(require('fs').readFileSync(`${__dirname}/../../../build/contracts/DetailedERC20.json`).toString()).abi; } catch (err) { console.log('\x1b[31m%s\x1b[0m', "Couldn't find contracts' artifacts. Make sure you ran truffle compile first"); throw err; @@ -125,5 +127,8 @@ module.exports = { }, moduleFactory: function () { return moduleFactoryABI; + }, + erc20: function () { + return erc20ABI; } } \ No newline at end of file diff --git a/CLI/commands/transfer_manager.js b/CLI/commands/transfer_manager.js index 3d6dd8906..378ab2d22 100644 --- a/CLI/commands/transfer_manager.js +++ b/CLI/commands/transfer_manager.js @@ -10,6 +10,7 @@ const { table } = require('table') /////////////////// // Constants +<<<<<<< HEAD const WHITELIST_DATA_CSV = './CLI/data/Transfer/GTM/whitelist_data.csv'; const PERCENTAGE_WHITELIST_DATA_CSV = './CLI/data/Transfer/PercentageTM/whitelist_data.csv'; const ADD_DAILY_RESTRICTIONS_DATA_CSV = './CLI/data/Transfer/VRTM/add_daily_restriction_data.csv'; @@ -20,6 +21,25 @@ const MODIFY_RESTRICTIONS_DATA_CSV = './CLI/data/Transfer/VRTM/modify_restrictio const REMOVE_RESTRICTIONS_DATA_CSV = './CLI/data/Transfer/VRTM/remove_restriction_data.csv'; const RESTRICTION_TYPES = ['Fixed', 'Percentage']; +======= +const WHITELIST_DATA_CSV = `${__dirname}/../data/Transfer/GTM/whitelist_data.csv`; +const PERCENTAGE_WHITELIST_DATA_CSV = `${__dirname}/../data/Transfer/PercentageTM/whitelist_data.csv`; +const ADD_MANUAL_APPROVAL_DATA_CSV = `${__dirname}/../data/Transfer/MATM/add_manualapproval_data.csv`; +const MODIFY_MANUAL_APPROVAL_DATA_CSV = `${__dirname}/../data/Transfer/MATM/modify_manualapproval_data.csv`; +const REVOKE_MANUAL_APPROVAL_DATA_CSV = `${__dirname}/../data/Transfer/MATM/revoke_manualapproval_data.csv`; + +const MATM_MENU_ADD = 'Add new manual approval'; +const MATM_MENU_MANAGE = 'Manage existing approvals'; +const MATM_MENU_EXPLORE = 'Explore account'; +const MATM_MENU_OPERATE = 'Operate with multiple approvals'; +const MATM_MENU_MANAGE_INCRESE = 'Increase allowance'; +const MATM_MENU_MANAGE_DECREASE = 'Decrease allowance'; +const MATM_MENU_MANAGE_TIME = 'Modify expiry time and/or description'; +const MATM_MENU_MANAGE_REVOKE = 'Revoke this approval'; +const MATM_MENU_OPERATE_ADD = 'Add multiple approvals in batch'; +const MATM_MENU_OPERATE_MODIFY = 'Modify multiple approvals in batch'; +const MATM_MENU_OPERATE_REVOKE = 'Revoke multiple approvals in batch'; +>>>>>>> dev-2.1.0 // App flow let tokenSymbol; @@ -477,17 +497,27 @@ function showWhitelistTable(investorsArray, fromTimeArray, toTimeArray, expiryTi console.log(table(dataTable)); } -async function modifyWhitelistInBatch() { - let csvFilePath = readlineSync.question(`Enter the path for csv data file (${WHITELIST_DATA_CSV}): `, { - defaultInput: WHITELIST_DATA_CSV - }); - let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { - limit: function (input) { - return parseInt(input) > 0; - }, - limitMessage: 'Must be greater than 0', - defaultInput: gbl.constants.DEFAULT_BATCH_SIZE - }); +async function modifyWhitelistInBatch(_csvFilePath, _batchSize) { + let csvFilePath; + if (typeof _csvFilePath === 'undefined') { + csvFilePath = readlineSync.question(`Enter the path for csv data file (${WHITELIST_DATA_CSV}): `, { + defaultInput: WHITELIST_DATA_CSV + }); + } else { + csvFilePath = _csvFilePath; + } + let batchSize; + if (typeof _batchSize === 'undefined') { + batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + } else { + batchSize = _batchSize; + } let parsedData = csvParse(csvFilePath); let validData = parsedData.filter(row => web3.utils.isAddress(row[0]) && @@ -514,38 +544,27 @@ async function modifyWhitelistInBatch() { async function manualApprovalTransferManager() { console.log(chalk.blue(`Manual Approval Transfer Manager at ${currentTransferManager.options.address} `), '\n'); - let options = ['Check manual approval', 'Add manual approval', 'Revoke manual approval', - 'Check manual blocking', 'Add manual blocking', 'Revoke manual blocking']; + let totalApprovals = await currentTransferManager.methods.getTotalApprovalsLength().call(); + console.log(`- Current active approvals: ${totalApprovals}`); + + let matmOptions = [ + MATM_MENU_ADD, + MATM_MENU_MANAGE, + MATM_MENU_EXPLORE, + MATM_MENU_OPERATE + ]; + + let index = readlineSync.keyInSelect(matmOptions, 'What do you want to do?', { + cancel: 'RETURN' + }); + let optionSelected = index != -1 ? matmOptions[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); - let index = readlineSync.keyInSelect(options, 'What do you want to do?', { cancel: 'Return' }); - let optionSelected = options[index]; - console.log('Selected:', index != -1 ? optionSelected : 'Return', '\n'); - let from; - let to; switch (optionSelected) { - case 'Check manual approval': - from = readlineSync.question('Enter the address from which transfers would be approved: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - to = readlineSync.question('Enter the address to which transfers would be approved: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - console.log(); - let manualApproval = await getManualApproval(from, to); - if (manualApproval) { - console.log(`Manual approval found!`); - console.log(`Allowance: ${web3.utils.fromWei(manualApproval.allowance)}`); - console.log(`Expiry time: ${moment.unix(manualApproval.expiryTime).format('MMMM Do YYYY, HH:mm:ss')}`); - } else { - console.log(chalk.yellow(`There are no manual approvals from ${from} to ${to}.`)); - } + case MATM_MENU_ADD: + await matmAdd(); break; +<<<<<<< HEAD case 'Add manual approval': from = readlineSync.question('Enter the address from which transfers will be approved: ', { limit: function (input) { @@ -570,64 +589,86 @@ async function manualApprovalTransferManager() { } else { console.log(chalk.red(`A manual approval already exists from ${from} to ${to}.Revoke it first if you want to add a new one.`)); } +======= + case MATM_MENU_MANAGE: + await matmManage(); +>>>>>>> dev-2.1.0 break; - case 'Revoke manual approval': - from = readlineSync.question('Enter the address from which transfers were approved: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - to = readlineSync.question('Enter the address to which transfers were approved: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - if (await getManualApproval(from, to)) { - let revokeManualApprovalAction = currentTransferManager.methods.revokeManualApproval(from, to); - let revokeManualApprovalReceipt = await common.sendTransaction(revokeManualApprovalAction); - let revokeManualApprovalEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, revokeManualApprovalReceipt.logs, 'RevokeManualApproval'); - console.log(chalk.green(`Manual approval has been revoked successfully!`)); - } else { - console.log(chalk.red(`Manual approval from ${from} to ${to} does not exist.`)); - } + case MATM_MENU_EXPLORE: + await matmExplore(); break; - case 'Check manual blocking': - from = readlineSync.question('Enter the address from which transfers would be blocked: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - to = readlineSync.question('Enter the address to which transfers would be blocked: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - console.log(); - let manualBlocking = await getManualBlocking(from, to); - if (manualBlocking) { - console.log(`Manual blocking found!`); - console.log(`Expiry time: ${moment.unix(manualBlocking).format('MMMM Do YYYY, HH:mm:ss')}; `) - } else { - console.log(chalk.yellow(`There are no manual blockings from ${from} to ${to}.`)); - } + case MATM_MENU_OPERATE: + await matmOperate(); break; - case 'Add manual blocking': - from = readlineSync.question('Enter the address from which transfers will be blocked: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - to = readlineSync.question('Enter the address to which transfers will be blocked: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" + case 'RETURN': + return; + } + + await manualApprovalTransferManager(); +} + +async function matmAdd() { + let from = readlineSync.question('Enter the address from which transfers will be approved: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + let to = readlineSync.question('Enter the address to which transfers will be approved: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address" + }); + if (!await getManualApproval(from, to)) { + let description = readlineSync.question('Enter the description for the manual approval: ', { + limit: function (input) { + return input != "" && getBinarySize(input) < 33 + }, + limitMessage: "Description is required" + }); + let allowance = readlineSync.question('Enter the amount of tokens which will be approved: '); + let oneHourFromNow = Math.floor(Date.now() / 1000 + 3600); + let expiryTime = readlineSync.questionInt(`Enter the time (Unix Epoch time) until which the transfer is allowed (1 hour from now = ${oneHourFromNow}): `, { defaultInput: oneHourFromNow }); + let addManualApprovalAction = currentTransferManager.methods.addManualApproval(from, to, web3.utils.toWei(allowance), expiryTime, web3.utils.fromAscii(description)); + let addManualApprovalReceipt = await common.sendTransaction(addManualApprovalAction); + let addManualApprovalEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, addManualApprovalReceipt.logs, 'AddManualApproval'); + console.log(chalk.green(`Manual approval has been added successfully!`)); + } else { + console.log(chalk.red(`A manual approval already exists from ${from} to ${to}. Revoke it first if you want to add a new one or modify the existing one.`)); + } +} + +async function matmManage() { + + let manageOptions = [ + MATM_MENU_MANAGE_INCRESE, + MATM_MENU_MANAGE_DECREASE, + MATM_MENU_MANAGE_TIME, + MATM_MENU_MANAGE_REVOKE + ]; + + let getApprovals = await getApprovalsArray(); + + if (getApprovals.length > 0) { + let options = [] + getApprovals.forEach((item) => { + options.push(`${web3.utils.toAscii(item.description)}\n From: ${item.from}\n To: ${item.to}\n Amount: ${web3.utils.fromWei(item.allowance)} ${tokenSymbol}\n Expiry date: ${moment.unix(item.expiryTime).format('MM/DD/YYYY HH:mm')}\n`) + }) + + let index = readlineSync.keyInSelect(options, 'Select an existing approval: ', { + cancel: 'RETURN' + }); + let optionSelected = index != -1 ? options[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + + if (optionSelected !== 'RETURN') { + let selectedApproval = getApprovals[index]; + + let index2 = readlineSync.keyInSelect(manageOptions, 'What do you want to do?', { + cancel: 'RETURN' }); +<<<<<<< HEAD if (!await getManualBlocking(from, to)) { let oneHourFromNow = Math.floor(Date.now() / 1000 + 3600); let expiryTime = readlineSync.questionInt(`Enter the time (Unix Epoch time) until which the transfer is blocked (1 hour from now = ${oneHourFromNow}): `, { defaultInput: oneHourFromNow }); @@ -637,53 +678,295 @@ async function manualApprovalTransferManager() { console.log(chalk.green(`Manual blocking has been added successfully!`)); } else { console.log(chalk.red(`A manual blocking already exists from ${from} to ${to}.Revoke it first if you want to add a new one.`)); +======= + let optionSelected2 = index2 != -1 ? manageOptions[index2] : 'RETURN'; + console.log('Selected:', optionSelected2, '\n'); + + if (optionSelected2 !== 'RETURN') { + switch (optionSelected2) { + case MATM_MENU_MANAGE_INCRESE: + await matmManageIncrese(selectedApproval); + break; + case MATM_MENU_MANAGE_DECREASE: + await matmManageDecrease(selectedApproval); + break; + case MATM_MENU_MANAGE_TIME: + await matmManageTimeOrDescription(selectedApproval); + break; + case MATM_MENU_MANAGE_REVOKE: + await matmManageRevoke(selectedApproval); + break; + } +>>>>>>> dev-2.1.0 } + } + } else { + console.log(chalk.yellow(`There are no existing approvals to show`)); + } +} + +async function matmExplore() { + let getApprovals = await getApprovalsArray(); + getApprovals.forEach((item) => { + printMatmRow(item.from, item.to, item.allowance, item.expiryTime, item.description); + }) +} + +async function matmOperate() { + let operateOptions = [ + MATM_MENU_OPERATE_ADD, + MATM_MENU_OPERATE_MODIFY, + MATM_MENU_OPERATE_REVOKE + ]; + + let index = readlineSync.keyInSelect(operateOptions, 'What do you want to do?', { + cancel: 'RETURN' + }); + let optionSelected = index != -1 ? operateOptions[index] : 'RETURN'; + console.log('Selected:', optionSelected, '\n'); + + switch (optionSelected) { + case MATM_MENU_OPERATE_ADD: + await addManualApproveInBatch(); break; - case 'Revoke manual blocking': - from = readlineSync.question('Enter the address from which transfers were blocked: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - to = readlineSync.question('Enter the address to which transfers were blocked: ', { - limit: function (input) { - return web3.utils.isAddress(input); - }, - limitMessage: "Must be a valid address" - }); - if (await getManualBlocking(from, to)) { - let revokeManualBlockingAction = currentTransferManager.methods.revokeManualBlocking(from, to); - let revokeManualBlockingReceipt = await common.sendTransaction(revokeManualBlockingAction); - let revokeManualBlockingEvent = common.getEventFromLogs(currentTransferManager._jsonInterface, revokeManualBlockingReceipt.logs, 'RevokeManualBlocking'); - console.log(chalk.green(`Manual blocking has been revoked successfully!`)); - } else { - console.log(chalk.red(`Manual blocking from ${from} to ${to} does not exist.`)); - } + case MATM_MENU_OPERATE_MODIFY: + await modifyManualApproveInBatch(); break; + case MATM_MENU_OPERATE_REVOKE: + await revokeManualApproveInBatch(); + break; + } +} + +async function matmManageIncrese(selectedApproval) { + let allowance = readlineSync.question(`Enter a value to increase allowance (current allowance = ${web3.utils.fromWei(selectedApproval.allowance)}): `, { + limit: function (input) { + return parseFloat(input) > 0 + }, + limitMessage: "Amount must be bigger than 0" + }); + + if (readlineSync.keyInYNStrict(`Do you want to modify expiry time or description?`)) { + let { expiryTime, description } = readExpiryTimeAndDescription(selectedApproval); + selectedApproval.expiryTime = expiryTime; + selectedApproval.description = web3.utils.fromAscii(description); + } + + let modifyManualApprovalAction = currentTransferManager.methods.modifyManualApproval(selectedApproval.from, selectedApproval.to, parseInt(selectedApproval.expiryTime), web3.utils.toWei(allowance), selectedApproval.description, 1); + await common.sendTransaction(modifyManualApprovalAction); + console.log(chalk.green(`The approval allowance has been increased successfully!`)); +} + +async function matmManageDecrease(selectedApproval) { + let allowance = readlineSync.question(`Enter a value to decrease allowance (current allowance = ${web3.utils.fromWei(selectedApproval.allowance)}): `, { + limit: function (input) { + return parseFloat(input) > 0 + }, + limitMessage: "Amount must be bigger than 0" + }); + + if (readlineSync.keyInYNStrict(`Do you want to modify expiry time or description?`)) { + let { expiryTime, description } = readExpiryTimeAndDescription(selectedApproval); + selectedApproval.expiryTime = expiryTime; + selectedApproval.description = web3.utils.fromAscii(description); + } + + let modifyManualApprovalAction = currentTransferManager.methods.modifyManualApproval(selectedApproval.from, selectedApproval.to, parseInt(selectedApproval.expiryTime), web3.utils.toWei(allowance), selectedApproval.description, 0); + await common.sendTransaction(modifyManualApprovalAction); + console.log(chalk.green(`The approval allowance has been decreased successfully!`)); +} + +async function matmManageTimeOrDescription(selectedApproval) { + let { expiryTime, description } = readExpiryTimeAndDescription(selectedApproval); + + let modifyManualApprovalAction = currentTransferManager.methods.modifyManualApproval(selectedApproval.from, selectedApproval.to, parseInt(expiryTime), selectedApproval.allowance, web3.utils.fromAscii(description), 2); + await common.sendTransaction(modifyManualApprovalAction); + console.log(chalk.green(`The approval expiry time has been modified successfully!`)); +} + +function readExpiryTimeAndDescription(selectedApproval) { + let expiryTime = readlineSync.questionInt(`Enter the new expiry time (Unix Epoch time) until which the transfer is allowed or leave empty to keep the current (${selectedApproval.expiryTime}): `, { + limit: function (input) { + return parseFloat(input) > 0; + }, + limitMessage: "Enter Unix Epoch time", + defaultInput: selectedApproval.expiryTime + }); + let description = readlineSync.question(`Enter the new description for the manual approval or leave empty to keep the current (${web3.utils.toAscii(selectedApproval.description)}): `, { + limit: function (input) { + return input != "" && getBinarySize(input) < 33; + }, + limitMessage: "Description is required" + }); + return { expiryTime, description }; +} + +async function matmManageRevoke(selectedApproval) { + let modifyManualApprovalAction = currentTransferManager.methods.revokeManualApproval(selectedApproval.from, selectedApproval.to); + await common.sendTransaction(modifyManualApprovalAction); + console.log(chalk.green(`The approval has been revoked successfully!`)); +} + +async function getApprovalsArray() { + let address = readlineSync.question('Enter an address to filter or leave empty to get all the approvals: ', { + limit: function (input) { + return web3.utils.isAddress(input); + }, + limitMessage: "Must be a valid address", + defaultInput: gbl.constants.ADDRESS_ZERO + }); + if (address == gbl.constants.ADDRESS_ZERO) { + return await getApprovals(); + } else { + let approvals = await getApprovalsToAnAddress(address); + if (!approvals.length) { + console.log(chalk.red(`\nThe address is not listed\n`)) + } + return approvals; + } +} + +function printMatmRow(from, to, allowance, time, description) { + console.log(`\nDescription: ${web3.utils.toAscii(description)}\nFrom ${from} to ${to}\nAllowance: ${web3.utils.fromWei(allowance)}\nExpiry time: ${moment.unix(time).format('MMMM Do YYYY HH:mm')}\n`); +} + +async function getApprovals() { + function ApprovalDetail(_from, _to, _allowance, _expiryTime, _description) { + this.from = _from; + this.to = _to; + this.allowance = _allowance; + this.expiryTime = _expiryTime; + this.description = _description; + } + + let results = []; + let approvalDetails = await currentTransferManager.methods.getAllApprovals().call(); + for (let i = 0; i < approvalDetails[0].length; i++) { + results.push(new ApprovalDetail(approvalDetails[0][i], approvalDetails[1][i], approvalDetails[2][i], approvalDetails[3][i], approvalDetails[4][i])); } + return results; +} + +async function getApprovalsToAnAddress(address) { + function ApprovalDetail(_from, _to, _allowance, _expiryTime, _description) { + this.from = _from; + this.to = _to; + this.allowance = _allowance; + this.expiryTime = _expiryTime; + this.description = _description; + } + + let results = []; + let approvals = await currentTransferManager.methods.getActiveApprovalsToUser(address).call(); + for (let i = 0; i < approvals[0].length; i++) { + results.push(new ApprovalDetail(approvals[0][i], approvals[1][i], approvals[2][i], approvals[3][i], approvals[4][i])); + } + return results; } async function getManualApproval(_from, _to) { let result = null; - let manualApproval = await currentTransferManager.methods.manualApprovals(_from, _to).call(); - if (manualApproval.expiryTime !== "0") { + let manualApproval = await currentTransferManager.methods.getApprovalDetails(_from, _to).call(); + if ((manualApproval[0] >= new Date()) && (manualApproval[1] != 0)) { result = manualApproval; } - return result; } -async function getManualBlocking(_from, _to) { - let result = null; +async function matmGenericCsv(path, f) { + let csvFilePath = readlineSync.question(`Enter the path for csv data file (${path}): `, { + defaultInput: path + }); + let batchSize = readlineSync.question(`Enter the max number of records per transaction or batch size (${gbl.constants.DEFAULT_BATCH_SIZE}): `, { + limit: function (input) { + return parseInt(input) > 0; + }, + limitMessage: 'Must be greater than 0', + defaultInput: gbl.constants.DEFAULT_BATCH_SIZE + }); + let parsedData = csvParse(csvFilePath); + let validData = parsedData.filter(row => f(row)); + let invalidRows = parsedData.filter(row => !validData.includes(row)); + if (invalidRows.length > 0) { + console.log(chalk.red(`The following lines from csv file are not valid: ${invalidRows.map(r => parsedData.indexOf(r) + 1).join(',')} `)); + } + return common.splitIntoBatches(validData, batchSize); +} - let manualBlocking = await currentTransferManager.methods.manualBlockings(_from, _to).call(); - if (manualBlocking !== "0") { - result = manualBlocking; +async function addManualApproveInBatch() { + + var f = (row) => { + return (web3.utils.isAddress(row[0]) && + web3.utils.isAddress(row[1]) && + parseFloat(row[2]) > 0 && + moment.unix(row[3]).isValid() && + typeof row[4] === 'string' && + getBinarySize(row[4]) < 33) } - return result; + let batches = await matmGenericCsv(ADD_MANUAL_APPROVAL_DATA_CSV, f) + + let [fromArray, toArray, allowanceArray, expiryArray, descriptionArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to add manual approvals: \n\n`, descriptionArray[batch], '\n'); + descriptionArray[batch] = descriptionArray[batch].map(d => web3.utils.fromAscii(d)); + allowanceArray[batch] = allowanceArray[batch].map(a => web3.utils.toWei(new web3.utils.BN(a))); + let action = await currentTransferManager.methods.addManualApprovalMulti(fromArray[batch], toArray[batch], allowanceArray[batch], expiryArray[batch], descriptionArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Add multiple manual approvals transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function revokeManualApproveInBatch() { + + var f = (row) => { + return (web3.utils.isAddress(row[0]) && + web3.utils.isAddress(row[1])) + } + + let batches = await matmGenericCsv(REVOKE_MANUAL_APPROVAL_DATA_CSV, f) + + let [fromArray, toArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to revoke manual approvals`, '\n'); + let action = await currentTransferManager.methods.revokeManualApprovalMulti(fromArray[batch], toArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Revoke multip;e manual approvals transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +async function modifyManualApproveInBatch() { + + var f = (row) => { + return (web3.utils.isAddress(row[0]) && + web3.utils.isAddress(row[1]) && + moment.unix(row[2]).isValid() && + parseFloat(row[3]) > 0 && + typeof row[4] === 'string' && + getBinarySize(row[4]) < 33 && + typeof parseInt(row[5])) === 'number' + } + + let batches = await matmGenericCsv(MODIFY_MANUAL_APPROVAL_DATA_CSV, f) + + let [fromArray, toArray, expiryArray, allowanceArray, descriptionArray, changesArray] = common.transposeBatches(batches); + for (let batch = 0; batch < batches.length; batch++) { + console.log(`Batch ${batch + 1} - Attempting to modify manual approvals: \n\n`, descriptionArray[batch], '\n'); + descriptionArray[batch] = descriptionArray[batch].map(d => web3.utils.fromAscii(d)); + allowanceArray[batch] = allowanceArray[batch].map(a => web3.utils.toWei(new web3.utils.BN(a))); + changesArray[batch] = changesArray[batch].map(c => parseInt(c)); + let action = await currentTransferManager.methods.modifyManualApprovalMulti(fromArray[batch], toArray[batch], expiryArray[batch], allowanceArray[batch], descriptionArray[batch], changesArray[batch]); + let receipt = await common.sendTransaction(action); + console.log(chalk.green('Modify multiple manual approvals transaction was successful.')); + console.log(`${receipt.gasUsed} gas used.Spent: ${web3.utils.fromWei((new web3.utils.BN(receipt.gasUsed)).mul(new web3.utils.BN(defaultGasPrice)))} ETH`); + } +} + +function getBinarySize(string) { + return Buffer.byteLength(string, 'utf8'); } async function countTransferManager() { @@ -1484,7 +1767,7 @@ async function getAllModulesByType(type) { let nameTemp = web3.utils.hexToUtf8(details[0]); let pausedTemp = null; if (type == gbl.constants.MODULES_TYPES.STO || type == gbl.constants.MODULES_TYPES.TRANSFER) { - let abiTemp = JSON.parse(require('fs').readFileSync(`./build/contracts/${nameTemp}.json`).toString()).abi; + let abiTemp = JSON.parse(require('fs').readFileSync(`${__dirname}/../../build/contracts/${nameTemp}.json`).toString()).abi; let contractTemp = new web3.eth.Contract(abiTemp, details[1]); pausedTemp = await contractTemp.methods.paused().call(); } @@ -1586,6 +1869,14 @@ module.exports = { }, addTransferManagerModule: async function (_tokenSymbol) { await initialize(_tokenSymbol); - return addTransferManagerModule() + return addTransferManagerModule(); + }, + modifyWhitelistInBatch: async function (_tokenSymbol, _csvFilePath, _batchSize) { + await initialize(_tokenSymbol); + let gmtModules = await securityToken.methods.getModulesByName(web3.utils.toHex('GeneralTransferManager')).call(); + let generalTransferManagerAddress = gmtModules[0]; + currentTransferManager = new web3.eth.Contract(abis.generalTransferManager(), generalTransferManagerAddress); + currentTransferManager.setProvider(web3.currentProvider); + return modifyWhitelistInBatch(_csvFilePath, _batchSize); } } \ No newline at end of file