diff --git a/contracts/datastore/DataStore.sol b/contracts/datastore/DataStore.sol new file mode 100644 index 000000000..3c68aac97 --- /dev/null +++ b/contracts/datastore/DataStore.sol @@ -0,0 +1,363 @@ +pragma solidity ^0.5.0; + +import "../interfaces/ISecurityToken.sol"; +import "../interfaces/IOwnable.sol"; +import "../interfaces/IDataStore.sol"; +import "./DataStoreStorage.sol"; + +/** + * @title Data store contract that stores data for all the modules in a central contract. + */ +contract DataStore is DataStoreStorage, IDataStore { + //NB To modify a specific element of an array, First push a new element to the array and then delete the old element. + //Whenver an element is deleted from an Array, last element of that array is moved to the index of deleted element. + //Delegate with MANAGEDATA permission can modify data. + + event SecurityTokenChanged(address indexed _oldSecurityToken, address indexed _newSecurityToken); + + modifier onlyAuthorized() { + bool isOwner = msg.sender == IOwnable(address(securityToken)).owner(); + require(isOwner || + securityToken.isModule(msg.sender, DATA_KEY) || + securityToken.checkPermission(msg.sender, address(this), MANAGEDATA), + "Unauthorized" + ); + _; + } + + modifier validKey(bytes32 _key) { + require(_key != bytes32(0), "Missing key"); + _; + } + + modifier validArrayLength(uint256 _keyLength, uint256 _dataLength) { + require(_keyLength == _dataLength, "Array length mismatch"); + _; + } + + modifier onlyOwner() { + require(msg.sender == IOwnable(address(securityToken)).owner(), "Unauthorized"); + _; + } + + /** + * @dev Changes security token atatched to this data store + * @param _securityToken address of the security token + */ + function setSecurityToken(address _securityToken) external onlyOwner { + require(_securityToken != address(0), "Invalid address"); + emit SecurityTokenChanged(address(securityToken), _securityToken); + securityToken = ISecurityToken(_securityToken); + } + + /** + * @dev Stores a uint256 data against a key + * @param _key Unique key to identify the data + * @param _data Data to be stored against the key + */ + function setUint256(bytes32 _key, uint256 _data) external onlyAuthorized { + _setData(_key, _data); + } + + function setBytes32(bytes32 _key, bytes32 _data) external onlyAuthorized { + _setData(_key, _data); + } + + function setAddress(bytes32 _key, address _data) external onlyAuthorized { + _setData(_key, _data); + } + + function setString(bytes32 _key, string calldata _data) external onlyAuthorized { + _setData(_key, _data); + } + + function setBytes(bytes32 _key, bytes calldata _data) external onlyAuthorized { + _setData(_key, _data); + } + + function setBool(bytes32 _key, bool _data) external onlyAuthorized { + _setData(_key, _data); + } + + /** + * @dev Stores a uint256 array against a key + * @param _key Unique key to identify the array + * @param _data Array to be stored against the key + */ + function setUint256Array(bytes32 _key, uint256[] calldata _data) external onlyAuthorized { + _setData(_key, _data); + } + + function setBytes32Array(bytes32 _key, bytes32[] calldata _data) external onlyAuthorized { + _setData(_key, _data); + } + + function setAddressArray(bytes32 _key, address[] calldata _data) external onlyAuthorized { + _setData(_key, _data); + } + + function setBoolArray(bytes32 _key, bool[] calldata _data) external onlyAuthorized { + _setData(_key, _data); + } + + /** + * @dev Inserts a uint256 element to the array identified by the key + * @param _key Unique key to identify the array + * @param _data Element to push into the array + */ + function insertUint256(bytes32 _key, uint256 _data) external onlyAuthorized { + _insertData(_key, _data); + } + + function insertBytes32(bytes32 _key, bytes32 _data) external onlyAuthorized { + _insertData(_key, _data); + } + + function insertAddress(bytes32 _key, address _data) external onlyAuthorized { + _insertData(_key, _data); + } + + function insertBool(bytes32 _key, bool _data) external onlyAuthorized { + _insertData(_key, _data); + } + + /** + * @dev Deletes an element from the array identified by the key. + * When an element is deleted from an Array, last element of that array is moved to the index of deleted element. + * @param _key Unique key to identify the array + * @param _index Index of the element to delete + */ + function deleteUint256(bytes32 _key, uint256 _index) external onlyAuthorized { + _deleteUint(_key, _index); + } + + function deleteBytes32(bytes32 _key, uint256 _index) external onlyAuthorized { + _deleteBytes32(_key, _index); + } + + function deleteAddress(bytes32 _key, uint256 _index) external onlyAuthorized { + _deleteAddress(_key, _index); + } + + function deleteBool(bytes32 _key, uint256 _index) external onlyAuthorized { + _deleteBool(_key, _index); + } + + /** + * @dev Stores multiple uint256 data against respective keys + * @param _keys Array of keys to identify the data + * @param _data Array of data to be stored against the respective keys + */ + function setUint256Multi(bytes32[] calldata _keys, uint256[] calldata _data) external onlyAuthorized validArrayLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i]); + } + } + + function setBytes32Multi(bytes32[] calldata _keys, bytes32[] calldata _data) external onlyAuthorized validArrayLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i]); + } + } + + function setAddressMulti(bytes32[] calldata _keys, address[] calldata _data) external onlyAuthorized validArrayLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i]); + } + } + + function setBoolMulti(bytes32[] calldata _keys, bool[] calldata _data) external onlyAuthorized validArrayLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i]); + } + } + + /** + * @dev Inserts multiple uint256 elements to the array identified by the respective keys + * @param _keys Array of keys to identify the data + * @param _data Array of data to be inserted in arrays of the respective keys + */ + function insertUint256Multi(bytes32[] calldata _keys, uint256[] calldata _data) external onlyAuthorized validArrayLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _insertData(_keys[i], _data[i]); + } + } + + function insertBytes32Multi(bytes32[] calldata _keys, bytes32[] calldata _data) external onlyAuthorized validArrayLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _insertData(_keys[i], _data[i]); + } + } + + function insertAddressMulti(bytes32[] calldata _keys, address[] calldata _data) external onlyAuthorized validArrayLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _insertData(_keys[i], _data[i]); + } + } + + function insertBoolMulti(bytes32[] calldata _keys, bool[] calldata _data) external onlyAuthorized validArrayLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _insertData(_keys[i], _data[i]); + } + } + + function getUint256(bytes32 _key) external view returns(uint256) { + return uintData[_key]; + } + + function getBytes32(bytes32 _key) external view returns(bytes32) { + return bytes32Data[_key]; + } + + function getAddress(bytes32 _key) external view returns(address) { + return addressData[_key]; + } + + function getString(bytes32 _key) external view returns(string memory) { + return stringData[_key]; + } + + function getBytes(bytes32 _key) external view returns(bytes memory) { + return bytesData[_key]; + } + + function getBool(bytes32 _key) external view returns(bool) { + return boolData[_key]; + } + + function getUint256Array(bytes32 _key) external view returns(uint256[] memory) { + return uintArrayData[_key]; + } + + function getBytes32Array(bytes32 _key) external view returns(bytes32[] memory) { + return bytes32ArrayData[_key]; + } + + function getAddressArray(bytes32 _key) external view returns(address[] memory) { + return addressArrayData[_key]; + } + + function getBoolArray(bytes32 _key) external view returns(bool[] memory) { + return boolArrayData[_key]; + } + + function getUint256ArrayLength(bytes32 _key) external view returns(uint256) { + return uintArrayData[_key].length; + } + + function getBytes32ArrayLength(bytes32 _key) external view returns(uint256) { + return bytes32ArrayData[_key].length; + } + + function getAddressArrayLength(bytes32 _key) external view returns(uint256) { + return addressArrayData[_key].length; + } + + function getBoolArrayLength(bytes32 _key) external view returns(uint256) { + return boolArrayData[_key].length; + } + + function getUint256ArrayElement(bytes32 _key, uint256 _index) external view returns(uint256) { + return uintArrayData[_key][_index]; + } + + function getBytes32ArrayElement(bytes32 _key, uint256 _index) external view returns(bytes32) { + return bytes32ArrayData[_key][_index]; + } + + function getAddressArrayElement(bytes32 _key, uint256 _index) external view returns(address) { + return addressArrayData[_key][_index]; + } + + function getBoolArrayElement(bytes32 _key, uint256 _index) external view returns(bool) { + return boolArrayData[_key][_index]; + } + + function _setData(bytes32 _key, uint256 _data) internal validKey(_key) { + uintData[_key] = _data; + } + + function _setData(bytes32 _key, bytes32 _data) internal validKey(_key) { + bytes32Data[_key] = _data; + } + + function _setData(bytes32 _key, address _data) internal validKey(_key) { + addressData[_key] = _data; + } + + function _setData(bytes32 _key, string memory _data) internal validKey(_key) { + stringData[_key] = _data; + } + + function _setData(bytes32 _key, bytes memory _data) internal validKey(_key) { + bytesData[_key] = _data; + } + + function _setData(bytes32 _key, bool _data) internal validKey(_key) { + boolData[_key] = _data; + } + + function _setData(bytes32 _key, uint256[] memory _data) internal validKey(_key) { + uintArrayData[_key] = _data; + } + + function _setData(bytes32 _key, bytes32[] memory _data) internal validKey(_key) { + bytes32ArrayData[_key] = _data; + } + + function _setData(bytes32 _key, address[] memory _data) internal validKey(_key) { + addressArrayData[_key] = _data; + } + + function _setData(bytes32 _key, bool[] memory _data) internal validKey(_key) { + boolArrayData[_key] = _data; + } + + function _insertData(bytes32 _key, uint256 _data) internal validKey(_key) { + uintArrayData[_key].push(_data); + } + + function _insertData(bytes32 _key, bytes32 _data) internal validKey(_key) { + bytes32ArrayData[_key].push(_data); + } + + function _insertData(bytes32 _key, address _data) internal validKey(_key) { + addressArrayData[_key].push(_data); + } + + function _insertData(bytes32 _key, bool _data) internal validKey(_key) { + boolArrayData[_key].push(_data); + } + + function _deleteUint(bytes32 _key, uint256 _index) internal validKey(_key) { + require(uintArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow + if(uintArrayData[_key].length - 1 != _index) { + uintArrayData[_key][_index] = uintArrayData[_key][uintArrayData[_key].length - 1]; + } + uintArrayData[_key].length--; + } + + function _deleteBytes32(bytes32 _key, uint256 _index) internal validKey(_key) { + require(bytes32ArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow + if(bytes32ArrayData[_key].length - 1 != _index) { + bytes32ArrayData[_key][_index] = bytes32ArrayData[_key][bytes32ArrayData[_key].length - 1]; + } + bytes32ArrayData[_key].length--; + } + + function _deleteAddress(bytes32 _key, uint256 _index) internal validKey(_key) { + require(addressArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow + if(addressArrayData[_key].length - 1 != _index) { + addressArrayData[_key][_index] = addressArrayData[_key][addressArrayData[_key].length - 1]; + } + addressArrayData[_key].length--; + } + + function _deleteBool(bytes32 _key, uint256 _index) internal validKey(_key) { + require(boolArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow + if(boolArrayData[_key].length - 1 != _index) { + boolArrayData[_key][_index] = boolArrayData[_key][boolArrayData[_key].length - 1]; + } + boolArrayData[_key].length--; + } +} diff --git a/contracts/datastore/DataStoreFactory.sol b/contracts/datastore/DataStoreFactory.sol new file mode 100644 index 000000000..f4a3cc6d2 --- /dev/null +++ b/contracts/datastore/DataStoreFactory.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.5.0; + +import "../proxy/DataStoreProxy.sol"; + +contract DataStoreFactory { + + address public implementation; + + constructor(address _implementation) public { + require(_implementation != address(0), "Address should not be 0x"); + implementation = _implementation; + } + + function generateDataStore(address _securityToken) public returns (address) { + DataStoreProxy dsProxy = new DataStoreProxy(_securityToken, implementation); + return address(dsProxy); + } +} diff --git a/contracts/datastore/DataStoreStorage.sol b/contracts/datastore/DataStoreStorage.sol new file mode 100644 index 000000000..9d724f070 --- /dev/null +++ b/contracts/datastore/DataStoreStorage.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.5.0; + +import "../interfaces/ISecurityToken.sol"; + +contract DataStoreStorage { + ISecurityToken public securityToken; + + mapping (bytes32 => uint256) internal uintData; + mapping (bytes32 => bytes32) internal bytes32Data; + mapping (bytes32 => address) internal addressData; + mapping (bytes32 => string) internal stringData; + mapping (bytes32 => bytes) internal bytesData; + mapping (bytes32 => bool) internal boolData; + mapping (bytes32 => uint256[]) internal uintArrayData; + mapping (bytes32 => bytes32[]) internal bytes32ArrayData; + mapping (bytes32 => address[]) internal addressArrayData; + mapping (bytes32 => bool[]) internal boolArrayData; + + uint8 constant DATA_KEY = 6; + bytes32 public constant MANAGEDATA = "MANAGEDATA"; +} diff --git a/contracts/interfaces/IDataStore.sol b/contracts/interfaces/IDataStore.sol new file mode 100644 index 000000000..bb1aa5aa4 --- /dev/null +++ b/contracts/interfaces/IDataStore.sol @@ -0,0 +1,128 @@ +pragma solidity ^0.5.0; + +interface IDataStore { + /** + * @dev Changes security token atatched to this data store + * @param _securityToken address of the security token + */ + function setSecurityToken(address _securityToken) external; + + /** + * @dev Stores a uint256 data against a key + * @param _key Unique key to identify the data + * @param _data Data to be stored against the key + */ + function setUint256(bytes32 _key, uint256 _data) external; + + function setBytes32(bytes32 _key, bytes32 _data) external; + + function setAddress(bytes32 _key, address _data) external; + + function setString(bytes32 _key, string calldata _data) external; + + function setBytes(bytes32 _key, bytes calldata _data) external; + + function setBool(bytes32 _key, bool _data) external; + + /** + * @dev Stores a uint256 array against a key + * @param _key Unique key to identify the array + * @param _data Array to be stored against the key + */ + function setUint256Array(bytes32 _key, uint256[] calldata _data) external; + + function setBytes32Array(bytes32 _key, bytes32[] calldata _data) external ; + + function setAddressArray(bytes32 _key, address[] calldata _data) external; + + function setBoolArray(bytes32 _key, bool[] calldata _data) external; + + /** + * @dev Inserts a uint256 element to the array identified by the key + * @param _key Unique key to identify the array + * @param _data Element to push into the array + */ + function insertUint256(bytes32 _key, uint256 _data) external; + + function insertBytes32(bytes32 _key, bytes32 _data) external; + + function insertAddress(bytes32 _key, address _data) external; + + function insertBool(bytes32 _key, bool _data) external; + + /** + * @dev Deletes an element from the array identified by the key. + * When an element is deleted from an Array, last element of that array is moved to the index of deleted element. + * @param _key Unique key to identify the array + * @param _index Index of the element to delete + */ + function deleteUint256(bytes32 _key, uint256 _index) external; + + function deleteBytes32(bytes32 _key, uint256 _index) external; + + function deleteAddress(bytes32 _key, uint256 _index) external; + + function deleteBool(bytes32 _key, uint256 _index) external; + + /** + * @dev Stores multiple uint256 data against respective keys + * @param _keys Array of keys to identify the data + * @param _data Array of data to be stored against the respective keys + */ + function setUint256Multi(bytes32[] calldata _keys, uint256[] calldata _data) external; + + function setBytes32Multi(bytes32[] calldata _keys, bytes32[] calldata _data) external; + + function setAddressMulti(bytes32[] calldata _keys, address[] calldata _data) external; + + function setBoolMulti(bytes32[] calldata _keys, bool[] calldata _data) external; + + /** + * @dev Inserts multiple uint256 elements to the array identified by the respective keys + * @param _keys Array of keys to identify the data + * @param _data Array of data to be inserted in arrays of the respective keys + */ + function insertUint256Multi(bytes32[] calldata _keys, uint256[] calldata _data) external; + + function insertBytes32Multi(bytes32[] calldata _keys, bytes32[] calldata _data) external; + + function insertAddressMulti(bytes32[] calldata _keys, address[] calldata _data) external; + + function insertBoolMulti(bytes32[] calldata _keys, bool[] calldata _data) external; + + function getUint256(bytes32 _key) external view returns(uint256); + + function getBytes32(bytes32 _key) external view returns(bytes32); + + function getAddress(bytes32 _key) external view returns(address); + + function getString(bytes32 _key) external view returns(string memory); + + function getBytes(bytes32 _key) external view returns(bytes memory); + + function getBool(bytes32 _key) external view returns(bool); + + function getUint256Array(bytes32 _key) external view returns(uint256[] memory); + + function getBytes32Array(bytes32 _key) external view returns(bytes32[] memory); + + function getAddressArray(bytes32 _key) external view returns(address[] memory); + + function getBoolArray(bytes32 _key) external view returns(bool[] memory); + + function getUint256ArrayLength(bytes32 _key) external view returns(uint256); + + function getBytes32ArrayLength(bytes32 _key) external view returns(uint256); + + function getAddressArrayLength(bytes32 _key) external view returns(uint256); + + function getBoolArrayLength(bytes32 _key) external view returns(uint256); + + function getUint256ArrayElement(bytes32 _key, uint256 _index) external view returns(uint256); + + function getBytes32ArrayElement(bytes32 _key, uint256 _index) external view returns(bytes32); + + function getAddressArrayElement(bytes32 _key, uint256 _index) external view returns(address); + + function getBoolArrayElement(bytes32 _key, uint256 _index) external view returns(bool); +} diff --git a/contracts/interfaces/ISecurityToken.sol b/contracts/interfaces/ISecurityToken.sol index 3228d8740..c1671db53 100644 --- a/contracts/interfaces/ISecurityToken.sol +++ b/contracts/interfaces/ISecurityToken.sol @@ -27,6 +27,13 @@ interface ISecurityToken { */ function verifyTransfer(address _from, address _to, uint256 _value, bytes calldata _data) external returns(bool success); + /** + * @notice Checks if an address is a module of certain type + * @param _module Address to check + * @param _type type to check against + */ + function isModule(address _module, uint8 _type) external view returns(bool); + /** * @notice Mints new tokens and assigns them to the target _investor. * Can only be called by the STO attached to the token (Or by the ST owner if there's no STO attached yet) @@ -154,6 +161,18 @@ interface ISecurityToken { */ function currentCheckpointId() external view returns(uint256); + /** + * @notice Gets data store address + * @return data store address + */ + function dataStore() external view returns (address); + + /** + * @notice Allows owner to change data store + * @param _dataStore Address of the token data store + */ + function changeDataStore(address _dataStore) external; + /** * @notice Allows the owner to withdraw unspent POLY stored by them on the ST or any ERC20 token. * @dev Owner can transfer POLY to the ST which will be used to pay for modules that require a POLY fee. diff --git a/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol b/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol new file mode 100644 index 000000000..7a314220e --- /dev/null +++ b/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol @@ -0,0 +1,99 @@ +pragma solidity ^0.5.0; + +import "../../TransferManager/TransferManager.sol"; +import "../../../interfaces/IDataStore.sol"; +import "../../../interfaces/ISecurityToken.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; + +/** + * @title Transfer Manager module for core transfer validation functionality + */ +contract KYCTransferManager is TransferManager { + + using SafeMath for uint256; + + bytes32 public constant KYC_NUMBER = "KYC_NUMBER"; //We will standardize what key to use for what. + + bytes32 public constant KYC_PROVIDER = "KYC_PROVIDER"; + + bytes32 public constant KYC_ARRAY = "KYC_ARRAY"; + + /** + * @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 This function returns the signature of configure function + */ + function getInitFunction() public pure returns (bytes4) { + return bytes4(0); + } + + function verifyTransfer(address /*_from*/, address _to, uint256 /*_amount*/, bytes memory /* _data */, bool /* _isTransfer */) + public + returns (Result) + { + if (!paused && checkKYC(_to)) { + return Result.VALID; + } + return Result.NA; + } + + function modifyKYC( address _investor, bool _kycStatus) public withPerm(KYC_PROVIDER) { + _modifyKYC(_investor, _kycStatus); + } + + function _modifyKYC(address _investor, bool _kycStatus) internal { + IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); + bytes32 key = _getKYCKey(_investor); + uint256 kycNumber = dataStore.getUint256(key); //index in address array + 1 + uint256 kycTotal = dataStore.getAddressArrayLength(KYC_ARRAY); + if(_kycStatus) { + require(kycNumber == 0, "KYC exists"); + dataStore.setUint256(key, kycTotal + 1); + dataStore.insertAddress(KYC_ARRAY, _investor); + } else { + require(kycNumber != 0, "KYC does not exist"); + address lastAddress = dataStore.getAddressArrayElement(KYC_ARRAY, kycTotal - 1); + dataStore.deleteAddress(KYC_ARRAY, kycNumber - 1); + + //Corrects the index of last element as delete fucntions move last element to index. + dataStore.setUint256(_getKYCKey(lastAddress), kycNumber); + } + //Alternatively, we can just emit an event and not maintain the KYC array on chain. + //I am maintaining the array to showcase how it can be done in cases where it might be needed. + } + + /** + * @notice Return the permissions flag that are associated with general trnasfer manager + */ + function getPermissions() public view returns(bytes32[] memory) { + bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = KYC_PROVIDER; + return allPermissions; + } + + function getKYCAddresses() public view returns(address[] memory) { + IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); + return dataStore.getAddressArray(KYC_ARRAY); + } + + function checkKYC(address _investor) public view returns (bool kyc) { + bytes32 key = _getKYCKey(_investor); + IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); + if (dataStore.getUint256(key) > 0) + kyc = true; + } + + function _getKYCKey(address _identity) internal pure returns(bytes32) { + return bytes32(keccak256(abi.encodePacked(KYC_NUMBER, _identity))); + } + +} diff --git a/contracts/modules/Experimental/TransferManager/KYCTransferManagerFactory.sol b/contracts/modules/Experimental/TransferManager/KYCTransferManagerFactory.sol new file mode 100644 index 000000000..13c9bc5a2 --- /dev/null +++ b/contracts/modules/Experimental/TransferManager/KYCTransferManagerFactory.sol @@ -0,0 +1,63 @@ +pragma solidity ^0.5.0; + +import "./KYCTransferManager.sol"; +import "./../../ModuleFactory.sol"; + + +contract KYCTransferManagerFactory is ModuleFactory { + + /** + * @notice Constructor + */ + constructor (uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public + ModuleFactory(_setupCost, _usageCost, _subscriptionCost) + { + version = "1.0.0"; + name = "KYCTransferManager"; + title = "KYC Transfer Manager"; + description = "Manages KYC"; + 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 calldata /* _data */) external returns(address) { + address polyToken = _takeFee(); + KYCTransferManager kycTransferManager = new KYCTransferManager(msg.sender, polyToken); + /*solium-disable-next-line security/no-block-members*/ + emit GenerateModuleFromFactory(address(kycTransferManager), getName(), address(this), msg.sender, setupCost, now); + return address(kycTransferManager); + } + + + /** + * @notice Type of the Module factory + */ + function getTypes() external view returns(uint8[] memory) { + 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 memory) { + /*solium-disable-next-line max-len*/ + return "pray it works"; + } + + /** + * @notice Get the tags related to the module factory + */ + function getTags() public view returns(bytes32[] memory) { + bytes32[] memory availableTags = new bytes32[](1); + availableTags[0] = "KYC"; + return availableTags; + } + +} diff --git a/contracts/proxy/DataStoreProxy.sol b/contracts/proxy/DataStoreProxy.sol new file mode 100644 index 000000000..b79f5579d --- /dev/null +++ b/contracts/proxy/DataStoreProxy.sol @@ -0,0 +1,29 @@ +pragma solidity ^0.5.0; + +import "./OwnedProxy.sol"; +import "../datastore/DataStoreStorage.sol"; + +/** + * @title DataStoreProxy Proxy + */ +contract DataStoreProxy is DataStoreStorage, OwnedProxy { + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _implementation representing the address of the new implementation to be set + */ + constructor( + address _securityToken, + address _implementation + ) + public + { + require(_implementation != address(0) && _securityToken != address(0), + "Address should not be 0x" + ); + securityToken = ISecurityToken(_securityToken); + __implementation = _implementation; + } + +} diff --git a/contracts/tokens/STFactory.sol b/contracts/tokens/STFactory.sol index 702d118f8..d00e7e192 100644 --- a/contracts/tokens/STFactory.sol +++ b/contracts/tokens/STFactory.sol @@ -2,15 +2,18 @@ pragma solidity ^0.5.0; import "./SecurityToken.sol"; import "../interfaces/ISTFactory.sol"; +import "../datastore/DataStoreFactory.sol"; /** * @title Proxy for deploying SecurityToken instances */ contract STFactory is ISTFactory { address public transferManagerFactory; + DataStoreFactory public dataStoreFactory; - constructor(address _transferManagerFactory) public { + constructor(address _transferManagerFactory, address _dataStoreFactory) public { transferManagerFactory = _transferManagerFactory; + dataStoreFactory = DataStoreFactory(_dataStoreFactory); } /** @@ -38,6 +41,7 @@ contract STFactory is ISTFactory { _polymathRegistry ); newSecurityToken.addModule(transferManagerFactory, "", 0, 0); + newSecurityToken.changeDataStore(dataStoreFactory.generateDataStore(address(newSecurityToken))); newSecurityToken.transferOwnership(_issuer); return address(newSecurityToken); } diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index ae0c26d21..7e7e14ba2 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -46,6 +46,7 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater uint8 constant MINT_KEY = 3; uint8 constant CHECKPOINT_KEY = 4; uint8 constant BURN_KEY = 5; + uint8 constant DATA_KEY = 6; uint256 public granularity; @@ -64,6 +65,9 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater // Address whitelisted by issuer as controller address public controller; + // Address of the data store used to store shared data + address public dataStore; + // Records added modules - module list should be order agnostic! mapping(uint8 => address[]) modules; @@ -207,7 +211,7 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater securityTokenVersion = SemanticVersion(2, 0, 0); } - /** + /** * @notice Attachs a module to the SecurityToken * @dev E.G.: On deployment (through the STR) ST gets a TransferManager module attached to it * @dev to control restrictions on transfers. @@ -368,6 +372,15 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater granularity = _granularity; } + /** + * @notice Allows owner to change data store + * @param _dataStore Address of the token data store + */ + function changeDataStore(address _dataStore) external onlyOwner { + require(_dataStore != address(0), "Invalid address"); + dataStore = _dataStore; + } + /** * @notice Keeps track of the number of non-zero token holders * @param _from sender of transfer @@ -683,6 +696,15 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater return false; } + /** + * @notice Checks if an address is a module of certain type + * @param _module Address to check + * @param _type type to check against + */ + function isModule(address _module, uint8 _type) public view returns(bool) { + return _isModule(_module, _type); + } + function _checkAndBurn(address _from, uint256 _value, bytes memory _data) internal returns(bool) { bool verified = _updateTransfer(_from, address(0), _value, _data); _burn(_from, _value); diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 47e2ef484..d6bd8efcd 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -28,6 +28,8 @@ const MockOracle = artifacts.require("./MockOracle.sol"); const TokenLib = artifacts.require("./TokenLib.sol"); const SecurityToken = artifacts.require("./tokens/SecurityToken.sol"); const STRGetter = artifacts.require('./STRGetter.sol'); +const DataStoreLogic = artifacts.require('./DataStore.sol'); +const DataStoreFactory = artifacts.require('./DataStoreFactory.sol'); const VolumeRestrictionTMFactory = artifacts.require('./VolumeRestrictionTMFactory.sol') const VolumeRestrictionTMLogic = artifacts.require('./VolumeRestrictionTM.sol'); const VolumeRestrictionLib = artifacts.require('./VolumeRestrictionLib.sol'); @@ -257,6 +259,14 @@ module.exports = function(deployer, network, accounts) { // manager attach with the securityToken contract at the time of deployment) return deployer.deploy(CappedSTOLogic, nullAddress, nullAddress, { from: PolymathAccount }); }) + .then(() => { + // B) Deploy the DataStoreLogic Contract + return deployer.deploy(DataStoreLogic, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the DataStoreFactory Contract + return deployer.deploy(DataStoreFactory, DataStoreLogic.address, { 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) @@ -313,7 +323,7 @@ module.exports = function(deployer, network, accounts) { }) .then(() => { // H) Deploy the STVersionProxy001 Contract which contains the logic of deployment of securityToken. - return deployer.deploy(STFactory, GeneralTransferManagerFactory.address, { from: PolymathAccount }); + return deployer.deploy(STFactory, GeneralTransferManagerFactory.address, DataStoreFactory.address, { from: PolymathAccount }); }) .then(() => { // K) Deploy the FeatureRegistry contract to control feature switches diff --git a/test/helpers/createInstances.js b/test/helpers/createInstances.js index 130f0ff62..a28a7c957 100644 --- a/test/helpers/createInstances.js +++ b/test/helpers/createInstances.js @@ -40,6 +40,8 @@ const DummySTO = artifacts.require("./DummySTO.sol"); const MockBurnFactory = artifacts.require("./MockBurnFactory.sol"); const STRGetter = artifacts.require("./STRGetter.sol"); const MockWrongTypeFactory = artifacts.require("./MockWrongTypeFactory.sol"); +const DataStoreLogic = artifacts.require('./DataStore.sol'); +const DataStoreFactory = artifacts.require('./DataStoreFactory.sol'); const VolumeRestrictionTMFactory = artifacts.require("./VolumeRestrictionTMFactory.sol"); const VolumeRestrictionTM = artifacts.require("./VolumeRestrictionTM.sol"); const VestingEscrowWalletFactory = artifacts.require("./VestingEscrowWalletFactory.sol"); @@ -205,7 +207,9 @@ async function deployGTM(account_polymath) { } async function deploySTFactory(account_polymath) { - I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + let I_DataStoreLogic = await DataStoreLogic.new({ from: account_polymath }); + let I_DataStoreFactory = await DataStoreFactory.new(I_DataStoreLogic.address, { from: account_polymath }); + I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, I_DataStoreFactory.address, { from: account_polymath }); assert.notEqual(I_STFactory.address.valueOf(), "0x0000000000000000000000000000000000000000", "STFactory contract was not deployed"); diff --git a/test/n_security_token_registry.js b/test/n_security_token_registry.js index bc7a2731d..d723f4e45 100644 --- a/test/n_security_token_registry.js +++ b/test/n_security_token_registry.js @@ -12,6 +12,9 @@ const SecurityTokenRegistry = artifacts.require("./SecurityTokenRegistry.sol"); const SecurityTokenRegistryMock = artifacts.require("./SecurityTokenRegistryMock.sol"); const STFactory = artifacts.require("./STFactory.sol"); const STRGetter = artifacts.require('./STRGetter.sol'); +const DataStoreLogic = artifacts.require('./DataStore.sol'); +const DataStoreFactory = artifacts.require('./DataStoreFactory.sol'); + const Web3 = require("web3"); let BN = Web3.utils.BN; @@ -572,8 +575,10 @@ contract("SecurityTokenRegistry", async (accounts) => { describe("Generate SecurityToken v2", async () => { it("Should deploy the st version 2", async () => { // Step 7: Deploy the STFactory contract + let I_DataStoreLogic = await DataStoreLogic.new({ from: account_polymath }); + let I_DataStoreFactory = await DataStoreFactory.new(I_DataStoreLogic.address, { from: account_polymath }); - I_STFactory002 = await STFactory.new(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + I_STFactory002 = await STFactory.new(I_GeneralTransferManagerFactory.address, I_DataStoreFactory.address, { from: account_polymath }); assert.notEqual( I_STFactory002.address.valueOf(), diff --git a/test/o_security_token.js b/test/o_security_token.js index aac62b9e0..d6adbbc99 100644 --- a/test/o_security_token.js +++ b/test/o_security_token.js @@ -591,7 +591,7 @@ contract("SecurityToken", async (accounts) => { await catchRevert(I_SecurityToken.transfer(accounts[7], new BN(10).pow(new BN(17)), { from: account_investor1 })); }); - it("Should adjust granularity", async () => { + it("Should not allow 0 granularity", async () => { await catchRevert(I_SecurityToken.changeGranularity(0, { from: token_owner })); }); @@ -601,6 +601,21 @@ contract("SecurityToken", async (accounts) => { await I_SecurityToken.transfer(account_investor1, new BN(10).pow(new BN(17)), { from: accounts[7], gas: 2500000 }); }); + it("Should not allow unauthorized address to change data store", async () => { + await catchRevert(I_SecurityToken.changeDataStore(one_address, { from: account_polymath })); + }); + + it("Should not allow 0x0 address as data store", async () => { + await catchRevert(I_SecurityToken.changeDataStore(address_zero, { from: token_owner })); + }); + + it("Should change data store", async () => { + let ds = await I_SecurityToken.dataStore(); + await I_SecurityToken.changeDataStore(one_address, { from: token_owner }); + assert.equal(one_address, await I_SecurityToken.dataStore()); + await I_SecurityToken.changeDataStore(ds, { from: token_owner }); + }); + it("Should transfer from whitelist investor to non-whitelist investor in first tx and in 2nd tx non-whitelist to non-whitelist transfer", async () => { await I_SecurityToken.transfer(accounts[7], new BN(10).mul(new BN(10).pow(new BN(18))), { from: account_investor1, gas: 2500000 }); diff --git a/test/u_module_registry_proxy.js b/test/u_module_registry_proxy.js index 3c52dc693..624217939 100644 --- a/test/u_module_registry_proxy.js +++ b/test/u_module_registry_proxy.js @@ -13,6 +13,8 @@ const GeneralTransferManagerLogic = artifacts.require("./GeneralTransferManager. const GeneralTransferManagerFactory = artifacts.require("./GeneralTransferManagerFactory.sol"); const GeneralPermissionManagerFactory = artifacts.require("./GeneralPermissionManagerFactory.sol"); const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager.sol"); +const DataStoreLogic = artifacts.require('./DataStore.sol'); +const DataStoreFactory = artifacts.require('./DataStoreFactory.sol'); const Web3 = require("web3"); let BN = Web3.utils.BN; @@ -151,7 +153,9 @@ contract("ModuleRegistryProxy", async (accounts) => { await I_MRProxied.verifyModule(I_GeneralTransferManagerFactory.address, true, { from: account_polymath }); // Step 3: Deploy the STFactory contract - I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, { from: account_polymath }); + let I_DataStoreLogic = await DataStoreLogic.new({ from: account_polymath }); + let I_DataStoreFactory = await DataStoreFactory.new(I_DataStoreLogic.address, { from: account_polymath }); + I_STFactory = await STFactory.new(I_GeneralTransferManagerFactory.address, I_DataStoreFactory.address, { from: account_polymath }); assert.notEqual(I_STFactory.address.valueOf(), address_zero, "STFactory contract was not deployed"); }); diff --git a/test/za_datastore.js b/test/za_datastore.js new file mode 100644 index 000000000..b89646bac --- /dev/null +++ b/test/za_datastore.js @@ -0,0 +1,468 @@ +import latestTime from "./helpers/latestTime"; +import { catchRevert } from "./helpers/exceptions"; +import takeSnapshot, { increaseTime, revertToSnapshot } from "./helpers/time"; +import { setUpPolymathNetwork } from "./helpers/createInstances"; +const SecurityToken = artifacts.require("./SecurityToken.sol"); +const DataStore = artifacts.require("./DataStore.sol"); + +const Web3 = require("web3"); +let BN = Web3.utils.BN; +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port + +contract("Data store", async (accounts) => { + // Accounts Variable declaration + let account_polymath; + let token_owner; + + // Contract Instance Declaration + let I_GeneralTransferManagerFactory; + let I_SecurityTokenRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_STRProxied; + let I_STFactory; + let I_SecurityToken; + let I_PolyToken; + let I_PolymathRegistry; + let I_DataStore; + let I_ModuleRegistryProxy; + let I_MRProxied; + let I_STRGetter; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const contact = "team@polymath.network"; + const key = "0x41"; + const key2 = "0x42"; + const bytes32data = "0x4200000000000000000000000000000000000000000000000000000000000000"; + const bytes32data2 = "0x4400000000000000000000000000000000000000000000000000000000000000"; + + // Initial fee for ticker registry and security token registry + const initRegFee = new BN(web3.utils.toWei("250")); + + const address_zero = "0x0000000000000000000000000000000000000000"; + const address_one = "0x0000000000000000000000000000000000000001"; + const address_two = "0x0000000000000000000000000000000000000002"; + + before(async () => { + account_polymath = accounts[0]; + token_owner = accounts[1]; + + // 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, + I_STRGetter + ] = instances; + + + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistry: ${I_ModuleRegistry.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.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 tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', { filter: { transactionHash: tx.transactionHash } }))[0]; + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toNumber(), 2); + assert.equal(web3.utils.toUtf8(log.args._name), "GeneralTransferManager"); + }); + + it("Should fetch data store address", async () => { + I_DataStore = await DataStore.at(await I_SecurityToken.dataStore()); + }); + }); + + describe("Should attach to security token securely", async () => { + it("Should be attached to a security token upon deployment", async () => { + assert.equal(await I_DataStore.securityToken(), I_SecurityToken.address, "Incorrect Security Token attached"); + }); + + it("Should not allow non-issuer to change security token address", async () => { + await catchRevert(I_DataStore.setSecurityToken(address_one, { from: account_polymath })); + }); + + it("Should allow issuer to change security token address", async () => { + let snapId = await takeSnapshot(); + await I_DataStore.setSecurityToken(address_one, { from: token_owner }); + assert.equal(await I_DataStore.securityToken(), address_one, "Incorrect Security Token attached"); + await revertToSnapshot(snapId); + assert.equal(await I_DataStore.securityToken(), I_SecurityToken.address, "Incorrect Security Token attached"); + }); + }); + + describe("Should set data correctly", async () => { + it("Should set and fetch uint256 correctly", async () => { + await catchRevert(I_DataStore.setUint256("0x0", 1, { from: token_owner })); + await I_DataStore.setUint256(key, 1, { from: token_owner }); + assert.equal((await I_DataStore.getUint256(key)).toNumber(), 1, "Incorrect Data Inserted"); + }); + + it("Should set and fetch bytes32 correctly", async () => { + await I_DataStore.setBytes32(key, bytes32data, { from: token_owner }); + assert.equal(await I_DataStore.getBytes32(key), bytes32data, "Incorrect Data Inserted"); + }); + + it("Should set and fetch address correctly", async () => { + await I_DataStore.setAddress(key, address_one, { from: token_owner }); + assert.equal(await I_DataStore.getAddress(key), address_one, "Incorrect Data Inserted"); + }); + + it("Should set and fetch string correctly", async () => { + await I_DataStore.setString(key, name, { from: token_owner }); + assert.equal(await I_DataStore.getString(key), name, "Incorrect Data Inserted"); + }); + + it("Should set and fetch bytes correctly", async () => { + await I_DataStore.setBytes(key, bytes32data, { from: token_owner }); + assert.equal(await I_DataStore.getBytes(key), bytes32data, "Incorrect Data Inserted"); + }); + + it("Should set and fetch bool correctly", async () => { + await I_DataStore.setBool(key, true, { from: token_owner }); + assert.equal(await I_DataStore.getBool(key), true, "Incorrect Data Inserted"); + }); + + it("Should set and fetch uint256 array correctly", async () => { + let arr = [1, 2]; + await I_DataStore.setUint256Array(key, arr, { from: token_owner }); + let arr2 = await I_DataStore.getUint256Array(key); + let arrLen = await I_DataStore.getUint256ArrayLength(key); + let arrElement2 = await I_DataStore.getUint256ArrayElement(key, 1); + assert.equal(arr2[0].toNumber(), arr[0], "Incorrect Data Inserted"); + assert.equal(arr2[1].toNumber(), arr[1], "Incorrect Data Inserted"); + assert.equal(arrLen, arr.length, "Incorrect Array Length"); + assert.equal(arrElement2.toNumber(), arr[1], "Incorrect array element"); + }); + + it("Should set and fetch bytes32 array correctly", async () => { + let arr = [bytes32data, bytes32data2]; + await I_DataStore.setBytes32Array(key, arr, { from: token_owner }); + let arr2 = await I_DataStore.getBytes32Array(key); + let arrLen = await I_DataStore.getBytes32ArrayLength(key); + let arrElement2 = await I_DataStore.getBytes32ArrayElement(key, 1); + assert.equal(arr2[0], arr[0], "Incorrect Data Inserted"); + assert.equal(arr2[1], arr[1], "Incorrect Data Inserted"); + assert.equal(arrLen, arr.length, "Incorrect Array Length"); + assert.equal(arrElement2, arr[1], "Incorrect array element"); + }); + + it("Should set and fetch address array correctly", async () => { + let arr = [address_zero, address_one]; + await I_DataStore.setAddressArray(key, arr, { from: token_owner }); + let arr2 = await I_DataStore.getAddressArray(key); + let arrLen = await I_DataStore.getAddressArrayLength(key); + let arrElement2 = await I_DataStore.getAddressArrayElement(key, 1); + assert.equal(arr2[0], arr[0], "Incorrect Data Inserted"); + assert.equal(arr2[1], arr[1], "Incorrect Data Inserted"); + assert.equal(arrLen, arr.length, "Incorrect Array Length"); + assert.equal(arrElement2, arr[1], "Incorrect array element"); + }); + + it("Should set and fetch bool array correctly", async () => { + let arr = [false, true]; + await I_DataStore.setBoolArray(key, arr, { from: token_owner }); + let arr2 = await I_DataStore.getBoolArray(key); + let arrLen = await I_DataStore.getBoolArrayLength(key); + let arrElement2 = await I_DataStore.getBoolArrayElement(key, 1); + assert.equal(arr2[0], arr[0], "Incorrect Data Inserted"); + assert.equal(arr2[1], arr[1], "Incorrect Data Inserted"); + assert.equal(arrLen, arr.length, "Incorrect Array Length"); + assert.equal(arrElement2, arr[1], "Incorrect array element"); + }); + + it("Should insert uint256 into Array", async () => { + let arrLen = await I_DataStore.getUint256ArrayLength(key); + await I_DataStore.insertUint256(key, new BN(10), { from: token_owner }); + let arrElement = await I_DataStore.getUint256ArrayElement(key, arrLen.toNumber()); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getUint256ArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement.toNumber(), 10, "Incorrect array element"); + }); + + it("Should insert bytes32 into Array", async () => { + let arrLen = await I_DataStore.getBytes32ArrayLength(key); + await I_DataStore.insertBytes32(key, bytes32data, { from: token_owner }); + let arrElement = await I_DataStore.getBytes32ArrayElement(key, arrLen.toNumber()); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getBytes32ArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement, bytes32data, "Incorrect array element"); + }); + + it("Should insert address into Array", async () => { + let arrLen = await I_DataStore.getAddressArrayLength(key); + await I_DataStore.insertAddress(key, address_one, { from: token_owner }); + let arrElement = await I_DataStore.getAddressArrayElement(key, arrLen.toNumber()); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getAddressArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement, address_one, "Incorrect array element"); + }); + + it("Should insert bool into Array", async () => { + let arrLen = await I_DataStore.getBoolArrayLength(key); + await I_DataStore.insertBool(key, true, { from: token_owner }); + let arrElement = await I_DataStore.getBoolArrayElement(key, arrLen.toNumber()); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getBoolArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement, true, "Incorrect array element"); + }); + + it("Should delete uint256 from Array", async () => { + let arrLen = await I_DataStore.getUint256ArrayLength(key); + let indexToDelete = arrLen.toNumber() - 2; + let lastElement = await I_DataStore.getUint256ArrayElement(key, arrLen.toNumber() - 1); + await I_DataStore.deleteUint256(key, indexToDelete, { from: token_owner }); + assert.equal(arrLen.toNumber() - 1, (await I_DataStore.getUint256ArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(lastElement.toNumber(), (await I_DataStore.getUint256ArrayElement(key, indexToDelete)).toNumber(), "Incorrect array element"); + }); + + it("Should delete bytes32 from Array", async () => { + let arrLen = await I_DataStore.getBytes32ArrayLength(key); + let indexToDelete = arrLen.toNumber() - 2; + let lastElement = await I_DataStore.getBytes32ArrayElement(key, arrLen.toNumber() - 1); + await I_DataStore.deleteBytes32(key, indexToDelete, { from: token_owner }); + assert.equal(arrLen.toNumber() - 1, (await I_DataStore.getBytes32ArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(lastElement, await I_DataStore.getBytes32ArrayElement(key, indexToDelete), "Incorrect array element"); + }); + + it("Should delete address from Array", async () => { + let arrLen = await I_DataStore.getAddressArrayLength(key); + let indexToDelete = arrLen.toNumber() - 2; + let lastElement = await I_DataStore.getAddressArrayElement(key, arrLen.toNumber() - 1); + await I_DataStore.deleteAddress(key, indexToDelete, { from: token_owner }); + assert.equal(arrLen.toNumber() - 1, (await I_DataStore.getAddressArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(lastElement, await I_DataStore.getAddressArrayElement(key, indexToDelete), "Incorrect array element"); + }); + + it("Should delete bool from Array", async () => { + let arrLen = await I_DataStore.getBoolArrayLength(key); + let indexToDelete = arrLen.toNumber() - 2; + let lastElement = await I_DataStore.getBoolArrayElement(key, arrLen.toNumber() - 1); + await I_DataStore.deleteBool(key, indexToDelete, { from: token_owner }); + assert.equal(arrLen.toNumber() - 1, (await I_DataStore.getBoolArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(lastElement, await I_DataStore.getBoolArrayElement(key, indexToDelete), "Incorrect array element"); + }); + + it("Should set and fetch multiple uint256 correctly", async () => { + await catchRevert(I_DataStore.setUint256Multi([key], [1,2], { from: token_owner })); + await I_DataStore.setUint256Multi([key, key2], [1,2], { from: token_owner }); + assert.equal((await I_DataStore.getUint256(key)).toNumber(), 1, "Incorrect Data Inserted"); + assert.equal((await I_DataStore.getUint256(key2)).toNumber(), 2, "Incorrect Data Inserted"); + }); + + it("Should set and fetch multiple bytes32 correctly", async () => { + await I_DataStore.setBytes32Multi([key, key2], [bytes32data, bytes32data2], { from: token_owner }); + assert.equal(await I_DataStore.getBytes32(key), bytes32data, "Incorrect Data Inserted"); + assert.equal(await I_DataStore.getBytes32(key2), bytes32data2, "Incorrect Data Inserted"); + }); + + it("Should set and fetch multiple address correctly", async () => { + await I_DataStore.setAddressMulti([key, key2], [address_one, address_two], { from: token_owner }); + assert.equal(await I_DataStore.getAddress(key), address_one, "Incorrect Data Inserted"); + assert.equal(await I_DataStore.getAddress(key2), address_two, "Incorrect Data Inserted"); + }); + + it("Should set and fetch multiple bool correctly", async () => { + await I_DataStore.setBoolMulti([key, key2], [true, true], { from: token_owner }); + assert.equal(await I_DataStore.getBool(key), true, "Incorrect Data Inserted"); + assert.equal(await I_DataStore.getBool(key2), true, "Incorrect Data Inserted"); + }); + + it("Should insert multiple uint256 into multiple Array", async () => { + let arrLen = await I_DataStore.getUint256ArrayLength(key); + let arrLen2 = await I_DataStore.getUint256ArrayLength(key2); + await I_DataStore.insertUint256Multi([key, key2], [10, 20], { from: token_owner }); + let arrElement = await I_DataStore.getUint256ArrayElement(key, arrLen.toNumber()); + let arrElement2 = await I_DataStore.getUint256ArrayElement(key2, arrLen2.toNumber()); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getUint256ArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement.toNumber(), 10, "Incorrect array element"); + assert.equal(arrLen2.toNumber() + 1, (await I_DataStore.getUint256ArrayLength(key2)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement2.toNumber(), 20, "Incorrect array element"); + }); + + it("Should insert multiple bytes32 into multiple Array", async () => { + let arrLen = await I_DataStore.getBytes32ArrayLength(key); + let arrLen2 = await I_DataStore.getBytes32ArrayLength(key2); + await I_DataStore.insertBytes32Multi([key, key2], [bytes32data, bytes32data2], { from: token_owner }); + let arrElement = await I_DataStore.getBytes32ArrayElement(key, arrLen.toNumber()); + let arrElement2 = await I_DataStore.getBytes32ArrayElement(key2, arrLen2.toNumber()); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getBytes32ArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrLen2.toNumber() + 1, (await I_DataStore.getBytes32ArrayLength(key2)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement, bytes32data, "Incorrect array element"); + assert.equal(arrElement2, bytes32data2, "Incorrect array element"); + }); + + it("Should insert multiple address into multiple Array", async () => { + let arrLen = await I_DataStore.getAddressArrayLength(key); + let arrLen2 = await I_DataStore.getAddressArrayLength(key2); + await I_DataStore.insertAddressMulti([key, key2], [address_one, address_two], { from: token_owner }); + let arrElement = await I_DataStore.getAddressArrayElement(key, arrLen.toNumber()); + let arrElement2 = await I_DataStore.getAddressArrayElement(key2, arrLen2.toNumber()); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getAddressArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrLen2.toNumber() + 1, (await I_DataStore.getAddressArrayLength(key2)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement, address_one, "Incorrect array element"); + assert.equal(arrElement2, address_two, "Incorrect array element"); + }); + + it("Should insert multiple bool into multiple Array", async () => { + let arrLen = await I_DataStore.getBoolArrayLength(key); + let arrLen2 = await I_DataStore.getBoolArrayLength(key2); + await I_DataStore.insertBoolMulti([key, key2], [true, true], { from: token_owner }); + let arrElement = await I_DataStore.getBoolArrayElement(key, arrLen.toNumber()); + let arrElement2 = await I_DataStore.getBoolArrayElement(key2, arrLen2.toNumber()); + assert.equal(arrLen.toNumber() + 1, (await I_DataStore.getBoolArrayLength(key)).toNumber(), "Incorrect Array Length"); + assert.equal(arrLen2.toNumber() + 1, (await I_DataStore.getBoolArrayLength(key2)).toNumber(), "Incorrect Array Length"); + assert.equal(arrElement, true, "Incorrect array element"); + assert.equal(arrElement2, true, "Incorrect array element"); + }); + }); + + describe("Should not allow unautohrized modification to data", async () => { + it("Should not allow unauthorized addresses to modify uint256", async () => { + await catchRevert(I_DataStore.setUint256(key, new BN(1), { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify bytes32", async () => { + await catchRevert(I_DataStore.setBytes32(key, bytes32data, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify address", async () => { + await catchRevert(I_DataStore.setAddress(key, address_one, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify string", async () => { + await catchRevert(I_DataStore.setString(key, name, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify bytes", async () => { + await catchRevert(I_DataStore.setBytes32(key, bytes32data, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify bool", async () => { + await catchRevert(I_DataStore.setBool(key, true, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify uint256 array", async () => { + let arr = [1, 2]; + await catchRevert(I_DataStore.setUint256Array(key, arr, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify bytes32 array", async () => { + let arr = [bytes32data, bytes32data2]; + await catchRevert(I_DataStore.setBytes32Array(key, arr, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify address array", async () => { + let arr = [address_zero, address_one]; + await catchRevert(I_DataStore.setAddressArray(key, arr, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify bool array", async () => { + let arr = [false, true]; + await catchRevert(I_DataStore.setBoolArray(key, arr, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert uint256 into Array", async () => { + await catchRevert(I_DataStore.insertUint256(key, new BN(10), { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert bytes32 into Array", async () => { + await catchRevert(I_DataStore.insertBytes32(key, bytes32data, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert address into Array", async () => { + await catchRevert(I_DataStore.insertAddress(key, address_one, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert bool into Array", async () => { + await catchRevert(I_DataStore.insertBool(key, true, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to delete uint256 from Array", async () => { + await catchRevert(I_DataStore.deleteUint256(key, 0, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to delete bytes32 from Array", async () => { + await catchRevert(I_DataStore.deleteBytes32(key, 0, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to delete address from Array", async () => { + await catchRevert(I_DataStore.deleteAddress(key, 0, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to delete bool from Array", async () => { + await catchRevert(I_DataStore.deleteBool(key, 0, { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify multiple uint256", async () => { + await catchRevert(I_DataStore.setUint256Multi([key, key2], [1,2], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify multiple bytes32", async () => { + await catchRevert(I_DataStore.setBytes32Multi([key, key2], [bytes32data, bytes32data2], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify multiple address", async () => { + await catchRevert(I_DataStore.setAddressMulti([key, key2], [address_one, address_two], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to modify multiple bool", async () => { + await catchRevert(I_DataStore.setBoolMulti([key, key2], [true, true], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert multiple uint256 into multiple Array", async () => { + await catchRevert(I_DataStore.insertUint256Multi([key, key2], [10, 20], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert multiple bytes32 into multiple Array", async () => { + await catchRevert(I_DataStore.insertBytes32Multi([key, key2], [bytes32data, bytes32data2], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert multiple address into multiple Array", async () => { + await catchRevert(I_DataStore.insertAddressMulti([key, key2], [address_one, address_two], { from: account_polymath })); + }); + + it("Should not allow unauthorized addresses to insert multiple bool into multiple Array", async () => { + await catchRevert(I_DataStore.insertBoolMulti([key, key2], [true, true], { from: account_polymath })); + }); + }); +}); \ No newline at end of file