From 4a27fe25cc0f6cda37d11de5e5000e8cc1718086 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Fri, 11 Jan 2019 15:04:42 +0530 Subject: [PATCH 01/19] Test cases for data store --- test/za_datastore.js | 125 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 test/za_datastore.js diff --git a/test/za_datastore.js b/test/za_datastore.js new file mode 100644 index 000000000..6f6702da4 --- /dev/null +++ b/test/za_datastore.js @@ -0,0 +1,125 @@ +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) => { + describe("Should attach to security token securely", async () => { + it("Should be attached to a security token upon deployment", async () => { + + }); + + it("Should not allow non-issuer to change security token address", async () => { + + }); + + it("Should not allow DATA module to change security token address", async () => { + + }); + + it("Should allow issuer to change security token address", async () => { + + }); + }); + + describe("Should not allow unautohrized modification to data", async () => { + it("Should not allow random addresses to modify data", async () => { + + }); + + it("Should not allow modules that does not belong to DATA type to modify data", async () => { + + }); + + it("Should not allow archived modules to modify data", async () => { + + }); + }); + + describe("Should set data correctly", async () => { + it("Should set uint256 correctly", async () => { + + }); + + it("Should set bytes32 correctly", async () => { + + }); + + it("Should set address correctly", async () => { + + }); + + it("Should set string correctly", async () => { + + }); + + it("Should set bytes correctly", async () => { + + }); + + it("Should set bool correctly", async () => { + + }); + + it("Should set uint256 array correctly", async () => { + + }); + + it("Should set bytes32 array correctly", async () => { + + }); + + it("Should set address array correctly", async () => { + + }); + + it("Should set bool array correctly", async () => { + + }); + }); + + describe("Should fetch data correctly", async () => { + it("Should fetch uint256 correctly", async () => { + + }); + + it("Should fetch bytes32 correctly", async () => { + + }); + + it("Should fetch address correctly", async () => { + + }); + + it("Should fetch string correctly", async () => { + + }); + + it("Should fetch bytes correctly", async () => { + + }); + + it("Should fetch bool correctly", async () => { + + }); + + it("Should fetch uint256 array correctly", async () => { + + }); + + it("Should fetch bytes32 array correctly", async () => { + + }); + + it("Should fetch address array correctly", async () => { + + }); + + it("Should fetch bool array correctly", async () => { + + }); + + it("Should fetch multiple data types in a single call correctly", async () => { + + }); + }); +}); \ No newline at end of file From 0dd7ce32c09953328404e9978f9f8adeaa4d39d6 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Fri, 11 Jan 2019 16:20:11 +0530 Subject: [PATCH 02/19] Rebase conflicts resolved --- contracts/DataStore.sol | 119 ++++++++++++++++++++++++ contracts/interfaces/ISecurityToken.sol | 7 ++ contracts/tokens/SecurityToken.sol | 11 ++- test/za_datastore.js | 4 - 4 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 contracts/DataStore.sol diff --git a/contracts/DataStore.sol b/contracts/DataStore.sol new file mode 100644 index 000000000..048cfa3b5 --- /dev/null +++ b/contracts/DataStore.sol @@ -0,0 +1,119 @@ +pragma solidity ^0.5.0; + +import "./interfaces/ISecurityToken.sol"; +import "./interfaces/IOwnable.sol"; + +contract DataStore { + ISecurityToken public associatedToken; + + 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 = 10; + + modifier onlyDataModuleWithValidKey(bytes32 _key) { + require(_key != bytes32(0), "Missing key"); + require(associatedToken.isModule(msg.sender, DATA_KEY), "Unauthorized"); + _; + } + + modifier onlyOwner() { + require(msg.sender == IOwnable(address(associatedToken)).owner(), "Unauthorized"); + _; + } + + function setSecurityToken(address _securityToken) public { + if(address(associatedToken) != address(0)) { + require(msg.sender == IOwnable(address(associatedToken)).owner(), "Unauthorized"); + } + associatedToken = ISecurityToken(_securityToken); + } + + function setData(bytes32 _key, uint256 _data) external onlyDataModuleWithValidKey(_key) { + uintData[_key] = _data; + } + + function setData(bytes32 _key, bytes32 _data) external onlyDataModuleWithValidKey(_key) { + bytes32Data[_key] = _data; + } + + function setData(bytes32 _key, address _data) external onlyDataModuleWithValidKey(_key) { + addressData[_key] = _data; + } + + function setData(bytes32 _key, string calldata _data) external onlyDataModuleWithValidKey(_key) { + stringData[_key] = _data; + } + + function setData(bytes32 _key, bytes calldata _data) external onlyDataModuleWithValidKey(_key) { + bytesData[_key] = _data; + } + + function setData(bytes32 _key, bool _data) external onlyDataModuleWithValidKey(_key) { + boolData[_key] = _data; + } + + function setData(bytes32 _key, uint256[] calldata _data) external onlyDataModuleWithValidKey(_key) { + uintArrayData[_key] = _data; + } + + function setData(bytes32 _key, bytes32[] calldata _data) external onlyDataModuleWithValidKey(_key) { + bytes32ArrayData[_key] = _data; + } + + function setData(bytes32 _key, address[] calldata _data) external onlyDataModuleWithValidKey(_key) { + addressArrayData[_key] = _data; + } + + function setData(bytes32 _key, bool[] calldata _data) external onlyDataModuleWithValidKey(_key) { + boolArrayData[_key] = _data; + } + + function getUint(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 getUintArray(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]; + } +} diff --git a/contracts/interfaces/ISecurityToken.sol b/contracts/interfaces/ISecurityToken.sol index f280d6c19..2bdbf6339 100644 --- a/contracts/interfaces/ISecurityToken.sol +++ b/contracts/interfaces/ISecurityToken.sol @@ -21,6 +21,13 @@ interface ISecurityToken { //transfer, transferFrom must respect the result of verifyTransfer function verifyTransfer(address _from, address _to, uint256 _value) 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) diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index 3c27d7d94..5e05891a0 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -734,7 +734,16 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater return false; } - function _checkAndBurn(address _from, uint256 _value, bytes _data) internal returns(bool) { + /** + * @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); _adjustTotalSupplyCheckpoints(); _burn(_from, _value); diff --git a/test/za_datastore.js b/test/za_datastore.js index 6f6702da4..9b590e78f 100644 --- a/test/za_datastore.js +++ b/test/za_datastore.js @@ -117,9 +117,5 @@ contract("Data store", async (accounts) => { it("Should fetch bool array correctly", async () => { }); - - it("Should fetch multiple data types in a single call correctly", async () => { - - }); }); }); \ No newline at end of file From f1ef7c2f40beb23aca01bbc80424d479aa594068 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Mon, 14 Jan 2019 11:51:52 +0530 Subject: [PATCH 03/19] wip ds proxy --- contracts/{ => datastore}/DataStore.sol | 7 +++--- contracts/datastore/DataStoreFactory.sol | 18 +++++++++++++++ contracts/datastore/DataStoreStorage.sol | 20 ++++++++++++++++ contracts/proxy/DataStoreProxy.sol | 29 ++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 3 deletions(-) rename contracts/{ => datastore}/DataStore.sol (96%) create mode 100644 contracts/datastore/DataStoreFactory.sol create mode 100644 contracts/datastore/DataStoreStorage.sol create mode 100644 contracts/proxy/DataStoreProxy.sol diff --git a/contracts/DataStore.sol b/contracts/datastore/DataStore.sol similarity index 96% rename from contracts/DataStore.sol rename to contracts/datastore/DataStore.sol index 048cfa3b5..b639d18ba 100644 --- a/contracts/DataStore.sol +++ b/contracts/datastore/DataStore.sol @@ -1,9 +1,10 @@ pragma solidity ^0.5.0; -import "./interfaces/ISecurityToken.sol"; -import "./interfaces/IOwnable.sol"; +import "../interfaces/ISecurityToken.sol"; +import "../interfaces/IOwnable.sol"; +import "./DataStoreStorage.sol"; -contract DataStore { +contract DataStore is DataStoreStorage { ISecurityToken public associatedToken; mapping (bytes32 => uint256) internal uintData; 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..2dba41ede --- /dev/null +++ b/contracts/datastore/DataStoreStorage.sol @@ -0,0 +1,20 @@ +pragma solidity ^0.5.0; + +import "../interfaces/ISecurityToken.sol"; + +contract DataStoreStorage { + ISecurityToken public associatedToken; + + 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 = 10; +} diff --git a/contracts/proxy/DataStoreProxy.sol b/contracts/proxy/DataStoreProxy.sol new file mode 100644 index 000000000..eaeb64a3a --- /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" + ); + associatedToken = ISecurityToken(_securityToken); + __implementation = _implementation; + } + +} From cbce1306548cb8d299f52759ac9265086cf9d85a Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Mon, 14 Jan 2019 12:22:50 +0530 Subject: [PATCH 04/19] DS deployment --- contracts/datastore/DataStore.sol | 14 - contracts/proxy/DataStoreProxy.sol | 2 +- contracts/tokens/STFactory.sol | 12 +- contracts/tokens/SecurityToken.sol | 29 +- migrations/2_deploy_contracts.js | 545 ++++++++++++++++++----------- 5 files changed, 372 insertions(+), 230 deletions(-) diff --git a/contracts/datastore/DataStore.sol b/contracts/datastore/DataStore.sol index b639d18ba..0ef45bf13 100644 --- a/contracts/datastore/DataStore.sol +++ b/contracts/datastore/DataStore.sol @@ -5,20 +5,6 @@ import "../interfaces/IOwnable.sol"; import "./DataStoreStorage.sol"; contract DataStore is DataStoreStorage { - ISecurityToken public associatedToken; - - 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 = 10; modifier onlyDataModuleWithValidKey(bytes32 _key) { require(_key != bytes32(0), "Missing key"); diff --git a/contracts/proxy/DataStoreProxy.sol b/contracts/proxy/DataStoreProxy.sol index eaeb64a3a..6d2ce2482 100644 --- a/contracts/proxy/DataStoreProxy.sol +++ b/contracts/proxy/DataStoreProxy.sol @@ -1,7 +1,7 @@ pragma solidity ^0.5.0; import "./OwnedProxy.sol"; -import "../DataStore/DataStoreStorage.sol"; +import "../datastore/DataStoreStorage.sol"; /** * @title DataStoreProxy Proxy diff --git a/contracts/tokens/STFactory.sol b/contracts/tokens/STFactory.sol index 85153e816..3ae47f9b7 100644 --- a/contracts/tokens/STFactory.sol +++ b/contracts/tokens/STFactory.sol @@ -2,6 +2,7 @@ pragma solidity ^0.4.24; import "./SecurityToken.sol"; import "../interfaces/ISTFactory.sol"; +import "../datastore/DataStoreFactory.sol"; /** * @title Proxy for deploying SecurityToken instances @@ -9,9 +10,11 @@ import "../interfaces/ISTFactory.sol"; 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); } /** @@ -35,8 +38,9 @@ contract STFactory is ISTFactory { _tokenDetails, _polymathRegistry ); - SecurityToken(newSecurityTokenAddress).addModule(transferManagerFactory, "", 0, 0); - SecurityToken(newSecurityTokenAddress).transferOwnership(_issuer); - return newSecurityTokenAddress; + newSecurityToken.addModule(transferManagerFactory, "", 0, 0); + newSecurityToken.setDataStore(dataStoreFactory.generateDataStore(address(newSecurityToken))); + newSecurityToken.transferOwnership(_issuer); + return address(newSecurityToken); } } diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index 5e05891a0..b31ffe3e8 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 = 10; uint256 public granularity; @@ -64,6 +65,8 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater // Address whitelisted by issuer as controller address public controller; + address public dataStore; + // Records added modules - module list should be order agnostic! mapping (uint8 => address[]) modules; @@ -211,20 +214,24 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater updateFromRegistry(); tokenDetails = _tokenDetails; granularity = _granularity; - securityTokenVersion = SemanticVersion(2,0,0); + 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. - // * @param _moduleFactory is the address of the module factory to be added - // * @param _data is data packed into bytes used to further configure the module (See STO usage) - // * @param _maxCost max amount of POLY willing to pay to the module. - // * @param _budget max amount of ongoing POLY willing to assign to the module. - // * @param _label custom module label. - // */ + function setDataStore(address _dataStore) public { + require(_dataStore != address(0), "Invalid address"); + dataStore = _dataStore; + } + /** + * @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. + * @param _moduleFactory is the address of the module factory to be added + * @param _data is data packed into bytes used to further configure the module (See STO usage) + * @param _maxCost max amount of POLY willing to pay to the module. + * @param _budget max amount of ongoing POLY willing to assign to the module. + * @param _label custom module label. + */ function addModuleWithLabel( address _moduleFactory, bytes _data, diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 14bdf9fed..79a99b20d 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -1,29 +1,42 @@ -const PolymathRegistry = artifacts.require('./PolymathRegistry.sol') -const GeneralTransferManagerFactory = artifacts.require('./GeneralTransferManagerFactory.sol') -const GeneralPermissionManagerFactory = artifacts.require('./GeneralPermissionManagerFactory.sol') -const PercentageTransferManagerFactory = artifacts.require('./PercentageTransferManagerFactory.sol') -const USDTieredSTOProxyFactory = artifacts.require('./USDTieredSTOProxyFactory.sol'); -const CountTransferManagerFactory = artifacts.require('./CountTransferManagerFactory.sol') -const EtherDividendCheckpointFactory = artifacts.require('./EtherDividendCheckpointFactory.sol') -const ERC20DividendCheckpointFactory = artifacts.require('./ERC20DividendCheckpointFactory.sol') -const ModuleRegistry = artifacts.require('./ModuleRegistry.sol'); -const ModuleRegistryProxy = artifacts.require('./ModuleRegistryProxy.sol'); -const ManualApprovalTransferManagerFactory = artifacts.require('./ManualApprovalTransferManagerFactory.sol') -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 FeatureRegistry = artifacts.require('./FeatureRegistry.sol') -const STFactory = artifacts.require('./tokens/STFactory.sol') -const DevPolyToken = artifacts.require('./helpers/PolyTokenFaucet.sol') -const MockOracle = artifacts.require('./MockOracle.sol') -const TokenLib = artifacts.require('./TokenLib.sol'); -const SecurityToken = artifacts.require('./tokens/SecurityToken.sol') +const PolymathRegistry = artifacts.require("./PolymathRegistry.sol"); +const GeneralTransferManagerFactory = artifacts.require("./GeneralTransferManagerFactory.sol"); +const GeneralTransferManagerLogic = artifacts.require("./GeneralTransferManager.sol"); +const GeneralPermissionManagerLogic = artifacts.require("./GeneralPermissionManager.sol"); +const GeneralPermissionManagerFactory = artifacts.require("./GeneralPermissionManagerFactory.sol"); +const PercentageTransferManagerLogic = artifacts.require("./PercentageTransferManager.sol"); +const PercentageTransferManagerFactory = artifacts.require("./PercentageTransferManagerFactory.sol"); +const USDTieredSTOLogic = artifacts.require("./USDTieredSTO.sol"); +const CountTransferManagerFactory = artifacts.require("./CountTransferManagerFactory.sol"); +const CountTransferManagerLogic = artifacts.require("./CountTransferManager.sol"); +const EtherDividendCheckpointLogic = artifacts.require("./EtherDividendCheckpoint.sol"); +const ERC20DividendCheckpointLogic = artifacts.require("./ERC20DividendCheckpoint.sol"); +const EtherDividendCheckpointFactory = artifacts.require("./EtherDividendCheckpointFactory.sol"); +const ERC20DividendCheckpointFactory = artifacts.require("./ERC20DividendCheckpointFactory.sol"); +const ModuleRegistry = artifacts.require("./ModuleRegistry.sol"); +const ModuleRegistryProxy = artifacts.require("./ModuleRegistryProxy.sol"); +const ManualApprovalTransferManagerFactory = artifacts.require("./ManualApprovalTransferManagerFactory.sol"); +const ManualApprovalTransferManagerLogic = artifacts.require("./ManualApprovalTransferManager.sol"); +const CappedSTOFactory = artifacts.require("./CappedSTOFactory.sol"); +const CappedSTOLogic = artifacts.require("./CappedSTO.sol"); +const USDTieredSTOFactory = artifacts.require("./USDTieredSTOFactory.sol"); +const SecurityTokenRegistry = artifacts.require("./SecurityTokenRegistry.sol"); +const SecurityTokenRegistryProxy = artifacts.require("./SecurityTokenRegistryProxy.sol"); +const FeatureRegistry = artifacts.require("./FeatureRegistry.sol"); +const STFactory = artifacts.require("./tokens/STFactory.sol"); +const DevPolyToken = artifacts.require("./helpers/PolyTokenFaucet.sol"); +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'); -let BigNumber = require('bignumber.js'); -const cappedSTOSetupCost = new BigNumber(20000).times(new BigNumber(10).pow(18)); // 20K POLY fee -const usdTieredSTOSetupCost = new BigNumber(100000).times(new BigNumber(10).pow(18)); // 100K POLY fee -const initRegFee = new BigNumber(250).times(new BigNumber(10).pow(18)); // 250 POLY fee for registering ticker or security token in registry +const Web3 = require("web3"); +let BN = Web3.utils.BN; +const nullAddress = "0x0000000000000000000000000000000000000000"; +const cappedSTOSetupCost = new BN(20000).mul(new BN(10).pow(new BN(18))); // 20K POLY fee +const usdTieredSTOSetupCost = new BN(100000).mul(new BN(10).pow(new BN(18))); // 100K POLY fee +const initRegFee = new BN(250).mul(new BN(10).pow(new BN(18))); // 250 POLY fee for registering ticker or security token in registry let PolyToken; let UsdToken; let ETHOracle; @@ -121,181 +134,313 @@ module.exports = function (deployer, network, accounts) { }] }; - // POLYMATH NETWORK Configuration :: DO THIS ONLY ONCE - // A) Deploy the PolymathRegistry contract - return deployer.deploy(PolymathRegistry, {from: PolymathAccount}).then(() => { - return PolymathRegistry.deployed(); - }).then((_polymathRegistry) => { - polymathRegistry = _polymathRegistry; - return polymathRegistry.changeAddress("PolyToken", PolyToken, {from: PolymathAccount}); - }).then(() => { - // Deploy libraries - return deployer.deploy(TokenLib, {from: PolymathAccount}); - }).then(() => { - // Link libraries - deployer.link(TokenLib, SecurityToken); - deployer.link(TokenLib, STFactory); - // A) Deploy the ModuleRegistry Contract (It contains the list of verified ModuleFactory) - return deployer.deploy(ModuleRegistry, {from: PolymathAccount}); - }).then(() => { - return deployer.deploy(ModuleRegistryProxy, {from: PolymathAccount}); - }).then(() => { - let bytesProxyMR = web3.eth.abi.encodeFunctionCall(functionSignatureProxyMR, [polymathRegistry.address, PolymathAccount]); - return ModuleRegistryProxy.at(ModuleRegistryProxy.address).upgradeToAndCall("1.0.0", ModuleRegistry.address, bytesProxyMR, {from: PolymathAccount}); - }).then(() => { - moduleRegistry = ModuleRegistry.at(ModuleRegistryProxy.address); - // Add module registry to polymath registry - return polymathRegistry.changeAddress("ModuleRegistry", ModuleRegistryProxy.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) - return deployer.deploy(GeneralTransferManagerFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); - }).then(() => { - // C) Deploy the GeneralPermissionManagerFactory Contract (Factory used to generate the GeneralPermissionManager contract and - // this manager attach with the securityToken contract at the time of deployment) - return deployer.deploy(GeneralPermissionManagerFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); - }).then(() => { - // D) Deploy the CountTransferManagerFactory Contract (Factory used to generate the CountTransferManager contract use - // to track the counts of the investors of the security token) - return deployer.deploy(CountTransferManagerFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); - }).then(() => { - // D) Deploy the PercentageTransferManagerFactory Contract (Factory used to generate the PercentageTransferManager contract use - // to track the percentage of investment the investors could do for a particular security token) - return deployer.deploy(PercentageTransferManagerFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); - }).then(() => { - // D) Deploy the EtherDividendCheckpointFactory Contract (Factory used to generate the EtherDividendCheckpoint contract use - // to provide the functionality of the dividend in terms of ETH) - return deployer.deploy(EtherDividendCheckpointFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); - }).then(() => { - // 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 ManualApprovalTransferManagerFactory Contract (Factory used to generate the ManualApprovalTransferManager contract use - // to manual approve the transfer that will overcome the other transfer restrictions) - return deployer.deploy(ManualApprovalTransferManagerFactory, PolyToken, 0, 0, 0, {from: PolymathAccount}); - }).then(() => { - // H) Deploy the STVersionProxy001 Contract which contains the logic of deployment of securityToken. - return deployer.deploy(STFactory, GeneralTransferManagerFactory.address, {from: PolymathAccount}); - }).then(() => { - // K) Deploy the FeatureRegistry contract to control feature switches - return deployer.deploy(FeatureRegistry, PolymathRegistry.address, {from: PolymathAccount}); - }).then(() => { - // Assign the address into the FeatureRegistry key - return polymathRegistry.changeAddress("FeatureRegistry", FeatureRegistry.address, {from: PolymathAccount}); - }).then(() => { - // J) Deploy the SecurityTokenRegistry contract (Used to hold the deployed secuirtyToken details. It also act as the interface to deploy the SecurityToken) - return deployer.deploy(SecurityTokenRegistry, {from: PolymathAccount}) - }).then(()=> { - return deployer.deploy(SecurityTokenRegistryProxy, {from: PolymathAccount}); - }).then(() => { - let bytesProxy = web3.eth.abi.encodeFunctionCall(functionSignatureProxy, [PolymathRegistry.address, STFactory.address, initRegFee, initRegFee, PolyToken, PolymathAccount]); - return SecurityTokenRegistryProxy.at(SecurityTokenRegistryProxy.address).upgradeToAndCall("1.0.0", SecurityTokenRegistry.address, bytesProxy, {from: PolymathAccount}); - }).then(() => { - // Assign the address into the SecurityTokenRegistry key - return polymathRegistry.changeAddress("SecurityTokenRegistry", SecurityTokenRegistryProxy.address, {from: PolymathAccount}); - }).then(() => { - // Update all addresses into the registry contract by calling the function updateFromregistry - return moduleRegistry.updateFromRegistry({from: PolymathAccount}); - }).then(() => { - // D) Register the PercentageTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the PercentageTransferManager contract. - return moduleRegistry.registerModule(PercentageTransferManagerFactory.address, {from: PolymathAccount}); - }).then(() => { - // D) Register the CountTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the CountTransferManager contract. - return moduleRegistry.registerModule(CountTransferManagerFactory.address, {from: PolymathAccount}); - }).then(() => { - // D) Register the GeneralTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the GeneralTransferManager contract. - return moduleRegistry.registerModule(GeneralTransferManagerFactory.address, {from: PolymathAccount}); - }).then(() => { - // 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(GeneralPermissionManagerFactory.address, {from: PolymathAccount}); - }).then(() => { - // 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 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. - return moduleRegistry.registerModule(ManualApprovalTransferManagerFactory.address, {from: PolymathAccount}); - }).then(() => { - // E) Register the ERC20DividendCheckpointFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the ERC20DividendCheckpoint contract. - return moduleRegistry.registerModule(ERC20DividendCheckpointFactory.address, {from: PolymathAccount}); - }).then(() => { - // F) Once the GeneralTransferManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken - // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. - // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. - return moduleRegistry.verifyModule(GeneralTransferManagerFactory.address, true, {from: PolymathAccount}); - }).then(() => { - // G) Once the CountTransferManagerFactory 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(CountTransferManagerFactory.address, true, {from: PolymathAccount}); - }).then(() => { - // G) Once the PercentageTransferManagerFactory 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(PercentageTransferManagerFactory.address, true, {from: PolymathAccount}); - }).then(() => { - // G) Once the GeneralPermissionManagerFactory 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(GeneralPermissionManagerFactory.address, true, {from: PolymathAccount}) - }).then(() => { - // G) Once the EtherDividendCheckpointFactory 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(EtherDividendCheckpointFactory.address, true, {from: PolymathAccount}); - }).then(() => { - // G) Once the ERC20DividendCheckpointFactory 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(ERC20DividendCheckpointFactory.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. - // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. - return moduleRegistry.verifyModule(ManualApprovalTransferManagerFactory.address, true, {from: PolymathAccount}); - }).then(() => { - // M) Deploy the CappedSTOFactory (Use to generate the CappedSTO contract which will used to collect the funds ). - return deployer.deploy(CappedSTOFactory, PolyToken, cappedSTOSetupCost, 0, 0, {from: PolymathAccount}) - }).then(() => { - // N) Register the CappedSTOFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the CappedSTOFactory contract. - return moduleRegistry.registerModule(CappedSTOFactory.address, {from: PolymathAccount}) - }).then(()=>{ - // G) Once the CappedSTOFactory 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(CappedSTOFactory.address, true, {from: PolymathAccount}) - }).then(() => { - // Deploy the proxy factory - return deployer.deploy(USDTieredSTOProxyFactory, {from: PolymathAccount}); - }).then(() => { - // H) Deploy the USDTieredSTOFactory (Use to generate the USDTieredSTOFactory contract which will used to collect the funds ). - return deployer.deploy(USDTieredSTOFactory, PolyToken, usdTieredSTOSetupCost, 0, 0, USDTieredSTOProxyFactory.address, {from: PolymathAccount}) - }).then(() => { - // I) Register the USDTieredSTOFactory in the ModuleRegistry to make the factory available at the protocol level. - // So any securityToken can use that factory to generate the USDTieredSTOFactory contract. - return moduleRegistry.registerModule(USDTieredSTOFactory.address, {from: PolymathAccount}) - }).then(()=>{ - // J) Once the USDTieredSTOFactory 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(USDTieredSTOFactory.address, true, {from: PolymathAccount}) - }).then(() => { - return polymathRegistry.changeAddress("PolyUsdOracle", POLYOracle, {from: PolymathAccount}); - }).then(() => { - return polymathRegistry.changeAddress("EthUsdOracle", ETHOracle, {from: PolymathAccount}); - }).then(() => { - return deployer.deploy(SecurityToken, 'a', 'a', 18, 1, 'a', polymathRegistry.address, {from: PolymathAccount}); - }).then(() => { - console.log('\n'); - console.log(` + // POLYMATH NETWORK Configuration :: DO THIS ONLY ONCE + // A) Deploy the PolymathRegistry contract + return deployer + .deploy(PolymathRegistry, { from: PolymathAccount }) + .then(() => { + return PolymathRegistry.deployed(); + }) + .then(_polymathRegistry => { + polymathRegistry = _polymathRegistry; + return polymathRegistry.changeAddress("PolyToken", PolyToken, { from: PolymathAccount }); + }) + .then(() => { + // Deploy libraries + return deployer.deploy(TokenLib, { from: PolymathAccount }); + }) + .then(() => { + // Link libraries + deployer.link(TokenLib, SecurityToken); + deployer.link(TokenLib, STFactory); + // A) Deploy the ModuleRegistry Contract (It contains the list of verified ModuleFactory) + return deployer.deploy(ModuleRegistry, { from: PolymathAccount }); + }) + .then(() => { + return deployer.deploy(ModuleRegistryProxy, { from: PolymathAccount }); + }) + .then(() => { + return ModuleRegistryProxy.at(ModuleRegistryProxy.address); + }) + .then(moduleRegistryProxy => { + let bytesProxyMR = web3.eth.abi.encodeFunctionCall(functionSignatureProxyMR, [polymathRegistry.address, PolymathAccount]); + return moduleRegistryProxy.upgradeToAndCall("1.0.0", ModuleRegistry.address, bytesProxyMR, { from: PolymathAccount }); + }) + .then(() => { + return ModuleRegistry.at(ModuleRegistryProxy.address); + }) + .then(moduleRegistryInstance => { + moduleRegistry = moduleRegistryInstance; + // Add module registry to polymath registry + return polymathRegistry.changeAddress("ModuleRegistry", ModuleRegistryProxy.address, { from: PolymathAccount }); + }) + .then(() => { + // 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(GeneralTransferManagerLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the GeneralPermissionManagerLogic Contract (Factory used to generate the GeneralPermissionManager contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(GeneralPermissionManagerLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the CountTransferManagerLogic Contract (Factory used to generate the CountTransferManager contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(CountTransferManagerLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the ManualApprovalTransferManagerLogic Contract (Factory used to generate the ManualApprovalTransferManager contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(ManualApprovalTransferManagerLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the PercentageTransferManagerLogic Contract (Factory used to generate the PercentageTransferManager contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(PercentageTransferManagerLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the ERC20DividendCheckpointLogic Contract (Factory used to generate the ERC20DividendCheckpoint contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(ERC20DividendCheckpointLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the EtherDividendCheckpointLogic Contract (Factory used to generate the EtherDividendCheckpoint contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(EtherDividendCheckpointLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the USDTieredSTOLogic Contract (Factory used to generate the USDTieredSTO contract and this + // manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(USDTieredSTOLogic, nullAddress, nullAddress, { from: PolymathAccount }); + }) + .then(() => { + // B) Deploy the CappedSTOLogic Contract (Factory used to generate the CappedSTO contract and this + // 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) + return deployer.deploy(GeneralTransferManagerFactory, new BN(0), new BN(0), new BN(0), GeneralTransferManagerLogic.address, { + from: PolymathAccount + }); + }) + .then(() => { + // C) Deploy the GeneralPermissionManagerFactory Contract (Factory used to generate the GeneralPermissionManager contract and + // this manager attach with the securityToken contract at the time of deployment) + return deployer.deploy(GeneralPermissionManagerFactory, new BN(0), new BN(0), new BN(0), GeneralPermissionManagerLogic.address, { + from: PolymathAccount + }); + }) + .then(() => { + // D) Deploy the CountTransferManagerFactory Contract (Factory used to generate the CountTransferManager contract use + // to track the counts of the investors of the security token) + return deployer.deploy(CountTransferManagerFactory, new BN(0), new BN(0), new BN(0), CountTransferManagerLogic.address, { + from: PolymathAccount + }); + }) + .then(() => { + // D) Deploy the PercentageTransferManagerFactory Contract (Factory used to generate the PercentageTransferManager contract use + // to track the percentage of investment the investors could do for a particular security token) + return deployer.deploy(PercentageTransferManagerFactory, new BN(0), new BN(0), new BN(0), PercentageTransferManagerLogic.address, { + from: PolymathAccount + }); + }) + .then(() => { + // D) Deploy the EtherDividendCheckpointFactory Contract (Factory used to generate the EtherDividendCheckpoint contract use + // to provide the functionality of the dividend in terms of ETH) + return deployer.deploy(EtherDividendCheckpointFactory, new BN(0), new BN(0), new BN(0), EtherDividendCheckpointLogic.address, { + from: PolymathAccount + }); + }) + .then(() => { + // 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, new BN(0), new BN(0), new BN(0), ERC20DividendCheckpointLogic.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) + return deployer.deploy(ManualApprovalTransferManagerFactory, new BN(0), new BN(0), new BN(0), ManualApprovalTransferManagerLogic.address, { + from: PolymathAccount + }); + }) + .then(() => { + // H) Deploy the STVersionProxy001 Contract which contains the logic of deployment of securityToken. + return deployer.deploy(STFactory, GeneralTransferManagerFactory.address, DataStoreFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // K) Deploy the FeatureRegistry contract to control feature switches + return deployer.deploy(FeatureRegistry, PolymathRegistry.address, { from: PolymathAccount }); + }) + .then(() => { + // Assign the address into the FeatureRegistry key + return polymathRegistry.changeAddress("FeatureRegistry", FeatureRegistry.address, { from: PolymathAccount }); + }) + .then(() => { + // J) Deploy the SecurityTokenRegistry contract (Used to hold the deployed secuirtyToken details. It also act as the interface to deploy the SecurityToken) + return deployer.deploy(SecurityTokenRegistry, { from: PolymathAccount }); + }) + .then(() => { + return deployer.deploy(SecurityTokenRegistryProxy, { from: PolymathAccount }); + }) + .then(() => { + return deployer.deploy(STRGetter, {from: PolymathAccount}); + }) + .then(() => { + return SecurityTokenRegistryProxy.at(SecurityTokenRegistryProxy.address); + }) + .then((securityTokenRegistryProxy) => { + let bytesProxy = web3.eth.abi.encodeFunctionCall(functionSignatureProxy, [ + PolymathRegistry.address, + STFactory.address, + initRegFee, + initRegFee, + PolymathAccount, + STRGetter.address + ]); + return securityTokenRegistryProxy.upgradeToAndCall("1.0.0", SecurityTokenRegistry.address, bytesProxy, { + from: PolymathAccount + }); + }) + .then(() => { + // Assign the address into the SecurityTokenRegistry key + return polymathRegistry.changeAddress("SecurityTokenRegistry", SecurityTokenRegistryProxy.address, { from: PolymathAccount }); + }) + .then(() => { + // Update all addresses into the registry contract by calling the function updateFromregistry + return moduleRegistry.updateFromRegistry({ from: PolymathAccount }); + }) + .then(() => { + // D) Register the PercentageTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the PercentageTransferManager contract. + return moduleRegistry.registerModule(PercentageTransferManagerFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // D) Register the CountTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the CountTransferManager contract. + return moduleRegistry.registerModule(CountTransferManagerFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // D) Register the GeneralTransferManagerFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the GeneralTransferManager contract. + return moduleRegistry.registerModule(GeneralTransferManagerFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // 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(GeneralPermissionManagerFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // 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 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. + return moduleRegistry.registerModule(ManualApprovalTransferManagerFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // E) Register the ERC20DividendCheckpointFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the ERC20DividendCheckpoint contract. + return moduleRegistry.registerModule(ERC20DividendCheckpointFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // F) Once the GeneralTransferManagerFactory registered with the ModuleRegistry contract then for making them accessble to the securityToken + // contract, Factory should comes under the verified list of factories or those factories deployed by the securityToken issuers only. + // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. + return moduleRegistry.verifyModule(GeneralTransferManagerFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the CountTransferManagerFactory 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(CountTransferManagerFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the PercentageTransferManagerFactory 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(PercentageTransferManagerFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the GeneralPermissionManagerFactory 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(GeneralPermissionManagerFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the EtherDividendCheckpointFactory 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(EtherDividendCheckpointFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the ERC20DividendCheckpointFactory 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(ERC20DividendCheckpointFactory.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. + // Here it gets verified because it is deployed by the third party account (Polymath Account) not with the issuer accounts. + return moduleRegistry.verifyModule(ManualApprovalTransferManagerFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // M) Deploy the CappedSTOFactory (Use to generate the CappedSTO contract which will used to collect the funds ). + return deployer.deploy(CappedSTOFactory, cappedSTOSetupCost, new BN(0), new BN(0), CappedSTOLogic.address, { from: PolymathAccount }); + }) + .then(() => { + // N) Register the CappedSTOFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the CappedSTOFactory contract. + return moduleRegistry.registerModule(CappedSTOFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // G) Once the CappedSTOFactory 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(CappedSTOFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + // H) Deploy the USDTieredSTOFactory (Use to generate the USDTieredSTOFactory contract which will used to collect the funds ). + return deployer.deploy(USDTieredSTOFactory, usdTieredSTOSetupCost, new BN(0), new BN(0), USDTieredSTOLogic.address, { from: PolymathAccount }); + }) + .then(() => { + // I) Register the USDTieredSTOFactory in the ModuleRegistry to make the factory available at the protocol level. + // So any securityToken can use that factory to generate the USDTieredSTOFactory contract. + return moduleRegistry.registerModule(USDTieredSTOFactory.address, { from: PolymathAccount }); + }) + .then(() => { + // J) Once the USDTieredSTOFactory 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(USDTieredSTOFactory.address, true, { from: PolymathAccount }); + }) + .then(() => { + return polymathRegistry.changeAddress("PolyUsdOracle", POLYOracle, { from: PolymathAccount }); + }) + .then(() => { + return polymathRegistry.changeAddress("EthUsdOracle", ETHOracle, { from: PolymathAccount }); + }) + .then(() => { + return deployer.deploy(SecurityToken, "a", "a", 18, 1, "a", polymathRegistry.address, { from: PolymathAccount }); + }) + .then(() => { + console.log("\n"); + console.log(` ----------------------- Polymath Network Smart Contracts: ----------------------- PolymathRegistry: ${PolymathRegistry.address} SecurityTokenRegistry (Proxy): ${SecurityTokenRegistryProxy.address} From 12cb8dc70e888d50f541112102fd815590ca91af Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Mon, 14 Jan 2019 12:44:38 +0530 Subject: [PATCH 05/19] Fixed create instance helper --- test/helpers/createInstances.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/helpers/createInstances.js b/test/helpers/createInstances.js index 2021d350e..626fd91e5 100644 --- a/test/helpers/createInstances.js +++ b/test/helpers/createInstances.js @@ -30,6 +30,8 @@ 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 DataStoreLogic = artifacts.require('./DataStore.sol'); +const DataStoreFactory = artifacts.require('./DataStoreFactory.sol'); const Web3 = require("web3"); const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port @@ -140,7 +142,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"); From 535371bca54b5a3509520433f7810ffbcbe9cf9b Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Mon, 14 Jan 2019 13:00:54 +0530 Subject: [PATCH 06/19] tests fixed --- test/n_security_token_registry.js | 8 ++++++-- test/u_module_registry_proxy.js | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/test/n_security_token_registry.js b/test/n_security_token_registry.js index 9af7bd000..278eb268b 100644 --- a/test/n_security_token_registry.js +++ b/test/n_security_token_registry.js @@ -11,7 +11,9 @@ const SecurityTokenRegistryProxy = artifacts.require("./SecurityTokenRegistryPro 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"); const BigNumber = require("bignumber.js"); @@ -555,8 +557,10 @@ contract("SecurityTokenRegistry", 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/u_module_registry_proxy.js b/test/u_module_registry_proxy.js index 6d00b8fe2..657177051 100644 --- a/test/u_module_registry_proxy.js +++ b/test/u_module_registry_proxy.js @@ -11,6 +11,9 @@ const STFactory = artifacts.require("./STFactory.sol"); const SecurityToken = artifacts.require("./SecurityToken.sol"); 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"); const BigNumber = require("bignumber.js"); @@ -139,7 +142,9 @@ contract("ModuleRegistryProxy", 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(), From a649748e0415f8b9480c0f38268bab8759e6b6f5 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 15 Jan 2019 11:19:51 +0530 Subject: [PATCH 07/19] Added KYC TM module --- contracts/interfaces/ISecurityToken.sol | 2 + .../TransferManager/KYCTransferManager.sol | 81 +++++++++++++++++++ .../KYCTransferManagerFactory.sol | 63 +++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 contracts/modules/Experimental/TransferManager/KYCTransferManager.sol create mode 100644 contracts/modules/Experimental/TransferManager/KYCTransferManagerFactory.sol diff --git a/contracts/interfaces/ISecurityToken.sol b/contracts/interfaces/ISecurityToken.sol index 9ebfe160a..e1a0f104e 100644 --- a/contracts/interfaces/ISecurityToken.sol +++ b/contracts/interfaces/ISecurityToken.sol @@ -17,6 +17,8 @@ interface ISecurityToken { event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); + function dataStore() external view returns (address); + /** * @notice Validates a transfer with a TransferManager module if it exists * @dev TransferManager module has a key of 2 diff --git a/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol b/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol new file mode 100644 index 000000000..3c2075576 --- /dev/null +++ b/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol @@ -0,0 +1,81 @@ +pragma solidity ^0.5.0; + +import "../../TransferManager/TransferManager.sol"; +import "../../../datastore/DataStore.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_STATUS = "KYC_STATUS"; //We will standardize what key to use for what. + + bytes32 public constant KYC_PROVIDER = "KYC_PROVIDER"; + + /** + * @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) { + bytes32 key = _getKYCKey(_to); + DataStore dataStore = DataStore(ISecurityToken(securityToken).dataStore()); + if (dataStore.getBool(key)) + 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 + { + bytes32 key = _getKYCKey(_investor); + DataStore dataStore = DataStore(ISecurityToken(securityToken).dataStore()); + dataStore.setData(key, _kycStatus); + } + + /** + * @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 _getKYCKey(address _identity) internal pure returns(bytes32) { + return bytes32(keccak256(abi.encodePacked(KYC_STATUS, _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; + } + +} From 6439b35dd92c1c95bddfe6469599605ec9851ea8 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 15 Jan 2019 11:45:44 +0530 Subject: [PATCH 08/19] Added array operation --- contracts/datastore/DataStore.sol | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/contracts/datastore/DataStore.sol b/contracts/datastore/DataStore.sol index 0ef45bf13..12b8374ee 100644 --- a/contracts/datastore/DataStore.sol +++ b/contracts/datastore/DataStore.sol @@ -64,6 +64,54 @@ contract DataStore is DataStoreStorage { boolArrayData[_key] = _data; } + function insertData(bytes32 _key, uint256 _data) external onlyDataModuleWithValidKey(_key) { + uintArrayData[_key].push(_data); + } + + function insertData(bytes32 _key, bytes32 _data) external onlyDataModuleWithValidKey(_key) { + bytes32ArrayData[_key].push(_data); + } + + function insertData(bytes32 _key, address _data) external onlyDataModuleWithValidKey(_key) { + addressArrayData[_key].push(_data); + } + + function insertData(bytes32 _key, bool _data) external onlyDataModuleWithValidKey(_key) { + boolArrayData[_key].push(_data); + } + + function deleteUint(bytes32 _key, uint256 _index) external onlyDataModuleWithValidKey(_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) external onlyDataModuleWithValidKey(_key) { + require(bytes32ArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow + if(bytes32ArrayData[_key].length - 1 != _index) { + bytes32ArrayData[_key][_index] = bytes32ArrayData[_key][uintArrayData[_key].length - 1]; + } + bytes32ArrayData[_key].length--; + } + + function deleteAddress(bytes32 _key, uint256 _index) external onlyDataModuleWithValidKey(_key) { + require(addressArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow + if(addressArrayData[_key].length - 1 != _index) { + addressArrayData[_key][_index] = addressArrayData[_key][uintArrayData[_key].length - 1]; + } + addressArrayData[_key].length--; + } + + function deleteBool(bytes32 _key, uint256 _index) external onlyDataModuleWithValidKey(_key) { + require(boolArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow + if(boolArrayData[_key].length - 1 != _index) { + boolArrayData[_key][_index] = boolArrayData[_key][uintArrayData[_key].length - 1]; + } + boolArrayData[_key].length--; + } + function getUint(bytes32 _key) external view returns(uint256) { return uintData[_key]; } @@ -103,4 +151,20 @@ contract DataStore is DataStoreStorage { function getBoolArray(bytes32 _key) external view returns(bool[] memory) { return boolArrayData[_key]; } + + function getUintArrayLength(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; + } } From bdba97596a01810d1548d7353f69323db3f5b62d Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 15 Jan 2019 13:23:38 +0530 Subject: [PATCH 09/19] Added features --- contracts/datastore/DataStore.sol | 16 +++++ .../TransferManager/KYCTransferManager.sol | 59 +++++++++++++------ 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/contracts/datastore/DataStore.sol b/contracts/datastore/DataStore.sol index 12b8374ee..9d3ebabb3 100644 --- a/contracts/datastore/DataStore.sol +++ b/contracts/datastore/DataStore.sol @@ -167,4 +167,20 @@ contract DataStore is DataStoreStorage { function getBoolArrayLength(bytes32 _key) external view returns(uint256) { return boolArrayData[_key].length; } + + function getUintArrayElement(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]; + } } diff --git a/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol b/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol index 3c2075576..9c6d8ddd3 100644 --- a/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol +++ b/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol @@ -12,10 +12,12 @@ contract KYCTransferManager is TransferManager { using SafeMath for uint256; - bytes32 public constant KYC_STATUS = "KYC_STATUS"; //We will standardize what key to use for what. + 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 @@ -34,35 +36,42 @@ contract KYCTransferManager is TransferManager { return bytes4(0); } - function verifyTransfer(address /*_from*/, address _to, uint256 /*_amount*/, bytes memory /* _data */, bool /* _isTransfer */) public returns(Result) { + function verifyTransfer(address /*_from*/, address _to, uint256 /*_amount*/, bytes memory /* _data */, bool /* _isTransfer */) + public + returns (Result) + { if (!paused) { bytes32 key = _getKYCKey(_to); DataStore dataStore = DataStore(ISecurityToken(securityToken).dataStore()); - if (dataStore.getBool(key)) + if (dataStore.getUint(key) > 0) return Result.VALID; } return Result.NA; } - function modifyKYC( - address _investor, - bool _kycStatus - ) - public - withPerm(KYC_PROVIDER) - { + function modifyKYC( address _investor, bool _kycStatus) public withPerm(KYC_PROVIDER) { _modifyKYC(_investor, _kycStatus); } - function _modifyKYC( - address _investor, - bool _kycStatus - ) - internal - { - bytes32 key = _getKYCKey(_investor); + function _modifyKYC(address _investor, bool _kycStatus) internal { DataStore dataStore = DataStore(ISecurityToken(securityToken).dataStore()); - dataStore.setData(key, _kycStatus); + bytes32 key = _getKYCKey(_investor); + uint256 kycNumber = dataStore.getUint(key); //index in address array + 1 + uint256 kycTotal = dataStore.getAddressArrayLength(KYC_ARRAY); + if(_kycStatus) { + require(kycNumber == 0, "KYC exists"); + dataStore.setData(key, kycTotal + 1); + dataStore.insertData(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.setData(_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. } /** @@ -74,8 +83,20 @@ contract KYCTransferManager is TransferManager { return allPermissions; } + function getKYCAddresses() public view returns(address[] memory) { + DataStore dataStore = DataStore(ISecurityToken(securityToken).dataStore()); + return dataStore.getAddressArray(KYC_ARRAY); + } + + function checkKYC(address _investor) public view returns (bool kyc) { + bytes32 key = _getKYCKey(_investor); + DataStore dataStore = DataStore(ISecurityToken(securityToken).dataStore()); + if (dataStore.getUint(key) > 0) + kyc = true; + } + function _getKYCKey(address _identity) internal pure returns(bytes32) { - return bytes32(keccak256(abi.encodePacked(KYC_STATUS, _identity))); + return bytes32(keccak256(abi.encodePacked(KYC_NUMBER, _identity))); } } From 6b2bb547d76adb7d295ffad29873089c67f483ba Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Fri, 18 Jan 2019 13:43:37 +0530 Subject: [PATCH 10/19] Added batch functions --- contracts/datastore/DataStore.sol | 267 ++++++++++++++++++----- contracts/datastore/DataStoreStorage.sol | 3 +- contracts/proxy/DataStoreProxy.sol | 2 +- 3 files changed, 216 insertions(+), 56 deletions(-) diff --git a/contracts/datastore/DataStore.sol b/contracts/datastore/DataStore.sol index 9d3ebabb3..841d9a8ef 100644 --- a/contracts/datastore/DataStore.sol +++ b/contracts/datastore/DataStore.sol @@ -5,111 +5,182 @@ import "../interfaces/IOwnable.sol"; import "./DataStoreStorage.sol"; contract DataStore is DataStoreStorage { + //Delegate with MANAGEDATA permission can modify data. + + 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 onlyDataModuleWithValidKey(bytes32 _key) { + modifier validKey(bytes32 _key) { require(_key != bytes32(0), "Missing key"); - require(associatedToken.isModule(msg.sender, DATA_KEY), "Unauthorized"); + _; + } + + modifier validInputLength(uint256 _keyLength, uint256 _dataLength) { + require(_keyLength == _dataLength, "Array length mismatch"); _; } modifier onlyOwner() { - require(msg.sender == IOwnable(address(associatedToken)).owner(), "Unauthorized"); + require(msg.sender == IOwnable(address(securityToken)).owner(), "Unauthorized"); _; } function setSecurityToken(address _securityToken) public { - if(address(associatedToken) != address(0)) { - require(msg.sender == IOwnable(address(associatedToken)).owner(), "Unauthorized"); + if(address(securityToken) != address(0)) { + require(msg.sender == IOwnable(address(securityToken)).owner(), "Unauthorized"); } - associatedToken = ISecurityToken(_securityToken); + securityToken = ISecurityToken(_securityToken); } - function setData(bytes32 _key, uint256 _data) external onlyDataModuleWithValidKey(_key) { - uintData[_key] = _data; + function setData(bytes32 _key, uint256 _data) external onlyAuthorized { + _setData(_key, _data); } - function setData(bytes32 _key, bytes32 _data) external onlyDataModuleWithValidKey(_key) { - bytes32Data[_key] = _data; + function setData(bytes32 _key, bytes32 _data) external onlyAuthorized { + _setData(_key, _data); } - function setData(bytes32 _key, address _data) external onlyDataModuleWithValidKey(_key) { - addressData[_key] = _data; + function setData(bytes32 _key, address _data) external onlyAuthorized { + _setData(_key, _data); } - function setData(bytes32 _key, string calldata _data) external onlyDataModuleWithValidKey(_key) { - stringData[_key] = _data; + function setData(bytes32 _key, string calldata _data) external onlyAuthorized { + _setData(_key, _data); } - function setData(bytes32 _key, bytes calldata _data) external onlyDataModuleWithValidKey(_key) { - bytesData[_key] = _data; + function setData(bytes32 _key, bytes calldata _data) external onlyAuthorized { + _setData(_key, _data); } - function setData(bytes32 _key, bool _data) external onlyDataModuleWithValidKey(_key) { - boolData[_key] = _data; + function setData(bytes32 _key, bool _data) external onlyAuthorized { + _setData(_key, _data); } - function setData(bytes32 _key, uint256[] calldata _data) external onlyDataModuleWithValidKey(_key) { - uintArrayData[_key] = _data; + function setData(bytes32 _key, uint256[] calldata _data) external onlyAuthorized { + _setData(_key, _data); } - function setData(bytes32 _key, bytes32[] calldata _data) external onlyDataModuleWithValidKey(_key) { - bytes32ArrayData[_key] = _data; + function setData(bytes32 _key, bytes32[] calldata _data) external onlyAuthorized { + _setData(_key, _data); } - function setData(bytes32 _key, address[] calldata _data) external onlyDataModuleWithValidKey(_key) { - addressArrayData[_key] = _data; + function setData(bytes32 _key, address[] calldata _data) external onlyAuthorized { + _setData(_key, _data); } - function setData(bytes32 _key, bool[] calldata _data) external onlyDataModuleWithValidKey(_key) { - boolArrayData[_key] = _data; + function setData(bytes32 _key, bool[] calldata _data) external onlyAuthorized { + _setData(_key, _data); } - function insertData(bytes32 _key, uint256 _data) external onlyDataModuleWithValidKey(_key) { - uintArrayData[_key].push(_data); + function insertData(bytes32 _key, uint256 _data) external onlyAuthorized { + _insertData(_key, _data); } - function insertData(bytes32 _key, bytes32 _data) external onlyDataModuleWithValidKey(_key) { - bytes32ArrayData[_key].push(_data); + function insertData(bytes32 _key, bytes32 _data) external onlyAuthorized { + _insertData(_key, _data); } - function insertData(bytes32 _key, address _data) external onlyDataModuleWithValidKey(_key) { - addressArrayData[_key].push(_data); + function insertData(bytes32 _key, address _data) external onlyAuthorized { + _insertData(_key, _data); } - function insertData(bytes32 _key, bool _data) external onlyDataModuleWithValidKey(_key) { - boolArrayData[_key].push(_data); + function insertData(bytes32 _key, bool _data) external onlyAuthorized { + _insertData(_key, _data); } - function deleteUint(bytes32 _key, uint256 _index) external onlyDataModuleWithValidKey(_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]; + function deleteUint(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); + } + + function setDataMulti(bytes32[] calldata _keys, uint256[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i]); } - uintArrayData[_key].length--; } - function deleteBytes32(bytes32 _key, uint256 _index) external onlyDataModuleWithValidKey(_key) { - require(bytes32ArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow - if(bytes32ArrayData[_key].length - 1 != _index) { - bytes32ArrayData[_key][_index] = bytes32ArrayData[_key][uintArrayData[_key].length - 1]; + function setDataMulti(bytes32[] calldata _keys, bytes32[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i]); } - bytes32ArrayData[_key].length--; } - function deleteAddress(bytes32 _key, uint256 _index) external onlyDataModuleWithValidKey(_key) { - require(addressArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow - if(addressArrayData[_key].length - 1 != _index) { - addressArrayData[_key][_index] = addressArrayData[_key][uintArrayData[_key].length - 1]; + function setDataMulti(bytes32[] calldata _keys, address[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i]); } - addressArrayData[_key].length--; } - function deleteBool(bytes32 _key, uint256 _index) external onlyDataModuleWithValidKey(_key) { - require(boolArrayData[_key].length > _index, "Invalid Index"); //Also prevents undeflow - if(boolArrayData[_key].length - 1 != _index) { - boolArrayData[_key][_index] = boolArrayData[_key][uintArrayData[_key].length - 1]; + function setDataMulti(bytes32[] calldata _keys, bool[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _setData(_keys[i], _data[i]); + } + } + + function insertDataMulti(bytes32[] calldata _keys, uint256[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _insertData(_keys[i], _data[i]); + } + } + + function insertDataMulti(bytes32[] calldata _keys, bytes32[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _insertData(_keys[i], _data[i]); + } + } + + function insertDataMulti(bytes32[] calldata _keys, address[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _insertData(_keys[i], _data[i]); + } + } + + function insertDataMulti(bytes32[] calldata _keys, bool[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _insertData(_keys[i], _data[i]); + } + } + + function deleteUintMulti(bytes32[] calldata _keys, uint256[] calldata _indexes) external onlyAuthorized validInputLength(_keys.length, _indexes.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _deleteUint(_keys[i], _indexes[i]); + } + } + + function deleteBytes32Multi(bytes32[] calldata _keys, uint256[] calldata _indexes) external onlyAuthorized validInputLength(_keys.length, _indexes.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _deleteBytes32(_keys[i], _indexes[i]); + } + } + + function deleteAddressMulti(bytes32[] calldata _keys, uint256[] calldata _indexes) external onlyAuthorized validInputLength(_keys.length, _indexes.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _deleteAddress(_keys[i], _indexes[i]); + } + } + + function deleteBoolMulti(bytes32[] calldata _keys, uint256[] calldata _indexes) external onlyAuthorized validInputLength(_keys.length, _indexes.length) { + for (uint256 i = 0; i < _keys.length; i++) { + _deleteBool(_keys[i], _indexes[i]); } - boolArrayData[_key].length--; } function getUint(bytes32 _key) external view returns(uint256) { @@ -183,4 +254,92 @@ contract DataStore is DataStoreStorage { 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][uintArrayData[_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][uintArrayData[_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][uintArrayData[_key].length - 1]; + } + boolArrayData[_key].length--; + } } diff --git a/contracts/datastore/DataStoreStorage.sol b/contracts/datastore/DataStoreStorage.sol index 2dba41ede..dd869ce20 100644 --- a/contracts/datastore/DataStoreStorage.sol +++ b/contracts/datastore/DataStoreStorage.sol @@ -3,7 +3,7 @@ pragma solidity ^0.5.0; import "../interfaces/ISecurityToken.sol"; contract DataStoreStorage { - ISecurityToken public associatedToken; + ISecurityToken public securityToken; mapping (bytes32 => uint256) internal uintData; mapping (bytes32 => bytes32) internal bytes32Data; @@ -17,4 +17,5 @@ contract DataStoreStorage { mapping (bytes32 => bool[]) internal boolArrayData; uint8 constant DATA_KEY = 10; + bytes32 public constant MANAGEDATA = "MANAGEDATA"; } diff --git a/contracts/proxy/DataStoreProxy.sol b/contracts/proxy/DataStoreProxy.sol index 6d2ce2482..b79f5579d 100644 --- a/contracts/proxy/DataStoreProxy.sol +++ b/contracts/proxy/DataStoreProxy.sol @@ -22,7 +22,7 @@ contract DataStoreProxy is DataStoreStorage, OwnedProxy { require(_implementation != address(0) && _securityToken != address(0), "Address should not be 0x" ); - associatedToken = ISecurityToken(_securityToken); + securityToken = ISecurityToken(_securityToken); __implementation = _implementation; } From 602dc051f9b830efa2a4d6c6a48bcbf177a41617 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Fri, 18 Jan 2019 13:53:15 +0530 Subject: [PATCH 11/19] Removed multi delete --- contracts/datastore/DataStore.sol | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/contracts/datastore/DataStore.sol b/contracts/datastore/DataStore.sol index 841d9a8ef..50d9c87a0 100644 --- a/contracts/datastore/DataStore.sol +++ b/contracts/datastore/DataStore.sol @@ -32,7 +32,7 @@ contract DataStore is DataStoreStorage { _; } - function setSecurityToken(address _securityToken) public { + function setSecurityToken(address _securityToken) public onlyOwner { if(address(securityToken) != address(0)) { require(msg.sender == IOwnable(address(securityToken)).owner(), "Unauthorized"); } @@ -159,30 +159,6 @@ contract DataStore is DataStoreStorage { } } - function deleteUintMulti(bytes32[] calldata _keys, uint256[] calldata _indexes) external onlyAuthorized validInputLength(_keys.length, _indexes.length) { - for (uint256 i = 0; i < _keys.length; i++) { - _deleteUint(_keys[i], _indexes[i]); - } - } - - function deleteBytes32Multi(bytes32[] calldata _keys, uint256[] calldata _indexes) external onlyAuthorized validInputLength(_keys.length, _indexes.length) { - for (uint256 i = 0; i < _keys.length; i++) { - _deleteBytes32(_keys[i], _indexes[i]); - } - } - - function deleteAddressMulti(bytes32[] calldata _keys, uint256[] calldata _indexes) external onlyAuthorized validInputLength(_keys.length, _indexes.length) { - for (uint256 i = 0; i < _keys.length; i++) { - _deleteAddress(_keys[i], _indexes[i]); - } - } - - function deleteBoolMulti(bytes32[] calldata _keys, uint256[] calldata _indexes) external onlyAuthorized validInputLength(_keys.length, _indexes.length) { - for (uint256 i = 0; i < _keys.length; i++) { - _deleteBool(_keys[i], _indexes[i]); - } - } - function getUint(bytes32 _key) external view returns(uint256) { return uintData[_key]; } From 1b16ad4b3ad9614ab1fd691c51d079ba7ceb42eb Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 22 Jan 2019 11:59:28 +0530 Subject: [PATCH 12/19] Removed overloaded external functions --- contracts/datastore/DataStore.sol | 62 ++++++++++--------- .../TransferManager/KYCTransferManager.sol | 12 ++-- contracts/tokens/SecurityToken.sol | 1 + 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/contracts/datastore/DataStore.sol b/contracts/datastore/DataStore.sol index 50d9c87a0..072a9e3c3 100644 --- a/contracts/datastore/DataStore.sol +++ b/contracts/datastore/DataStore.sol @@ -5,6 +5,8 @@ import "../interfaces/IOwnable.sol"; import "./DataStoreStorage.sol"; contract DataStore is DataStoreStorage { + //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. modifier onlyAuthorized() { @@ -39,63 +41,63 @@ contract DataStore is DataStoreStorage { securityToken = ISecurityToken(_securityToken); } - function setData(bytes32 _key, uint256 _data) external onlyAuthorized { + function setUint256(bytes32 _key, uint256 _data) external onlyAuthorized { _setData(_key, _data); } - function setData(bytes32 _key, bytes32 _data) external onlyAuthorized { + function setBytes32(bytes32 _key, bytes32 _data) external onlyAuthorized { _setData(_key, _data); } - function setData(bytes32 _key, address _data) external onlyAuthorized { + function setAddress(bytes32 _key, address _data) external onlyAuthorized { _setData(_key, _data); } - function setData(bytes32 _key, string calldata _data) external onlyAuthorized { + function setString(bytes32 _key, string calldata _data) external onlyAuthorized { _setData(_key, _data); } - function setData(bytes32 _key, bytes calldata _data) external onlyAuthorized { + function setBytes(bytes32 _key, bytes calldata _data) external onlyAuthorized { _setData(_key, _data); } - function setData(bytes32 _key, bool _data) external onlyAuthorized { + function setBool(bytes32 _key, bool _data) external onlyAuthorized { _setData(_key, _data); } - function setData(bytes32 _key, uint256[] calldata _data) external onlyAuthorized { + function setUint256Array(bytes32 _key, uint256[] calldata _data) external onlyAuthorized { _setData(_key, _data); } - function setData(bytes32 _key, bytes32[] calldata _data) external onlyAuthorized { + function setBytes32Array(bytes32 _key, bytes32[] calldata _data) external onlyAuthorized { _setData(_key, _data); } - function setData(bytes32 _key, address[] calldata _data) external onlyAuthorized { + function setAddressArray(bytes32 _key, address[] calldata _data) external onlyAuthorized { _setData(_key, _data); } - function setData(bytes32 _key, bool[] calldata _data) external onlyAuthorized { + function setBoolArray(bytes32 _key, bool[] calldata _data) external onlyAuthorized { _setData(_key, _data); } - function insertData(bytes32 _key, uint256 _data) external onlyAuthorized { + function insertUint256(bytes32 _key, uint256 _data) external onlyAuthorized { _insertData(_key, _data); } - function insertData(bytes32 _key, bytes32 _data) external onlyAuthorized { + function insertBytes32(bytes32 _key, bytes32 _data) external onlyAuthorized { _insertData(_key, _data); } - function insertData(bytes32 _key, address _data) external onlyAuthorized { + function insertAddress(bytes32 _key, address _data) external onlyAuthorized { _insertData(_key, _data); } - function insertData(bytes32 _key, bool _data) external onlyAuthorized { + function insertBool(bytes32 _key, bool _data) external onlyAuthorized { _insertData(_key, _data); } - function deleteUint(bytes32 _key, uint256 _index) external onlyAuthorized { + function deleteUint256(bytes32 _key, uint256 _index) external onlyAuthorized { _deleteUint(_key, _index); } @@ -111,55 +113,55 @@ contract DataStore is DataStoreStorage { _deleteBool(_key, _index); } - function setDataMulti(bytes32[] calldata _keys, uint256[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + function setUint256Multi(bytes32[] calldata _keys, uint256[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { for (uint256 i = 0; i < _keys.length; i++) { _setData(_keys[i], _data[i]); } } - function setDataMulti(bytes32[] calldata _keys, bytes32[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + function setBytes32Multi(bytes32[] calldata _keys, bytes32[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { for (uint256 i = 0; i < _keys.length; i++) { _setData(_keys[i], _data[i]); } } - function setDataMulti(bytes32[] calldata _keys, address[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + function setAddressMulti(bytes32[] calldata _keys, address[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { for (uint256 i = 0; i < _keys.length; i++) { _setData(_keys[i], _data[i]); } } - function setDataMulti(bytes32[] calldata _keys, bool[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + function setBoolMulti(bytes32[] calldata _keys, bool[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { for (uint256 i = 0; i < _keys.length; i++) { _setData(_keys[i], _data[i]); } } - function insertDataMulti(bytes32[] calldata _keys, uint256[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + function insertUint256Multi(bytes32[] calldata _keys, uint256[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { for (uint256 i = 0; i < _keys.length; i++) { _insertData(_keys[i], _data[i]); } } - function insertDataMulti(bytes32[] calldata _keys, bytes32[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + function insertBytes32Multi(bytes32[] calldata _keys, bytes32[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { for (uint256 i = 0; i < _keys.length; i++) { _insertData(_keys[i], _data[i]); } } - function insertDataMulti(bytes32[] calldata _keys, address[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + function insertAddressMulti(bytes32[] calldata _keys, address[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { for (uint256 i = 0; i < _keys.length; i++) { _insertData(_keys[i], _data[i]); } } - function insertDataMulti(bytes32[] calldata _keys, bool[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { + function insertBoolMulti(bytes32[] calldata _keys, bool[] calldata _data) external onlyAuthorized validInputLength(_keys.length, _data.length) { for (uint256 i = 0; i < _keys.length; i++) { _insertData(_keys[i], _data[i]); } } - function getUint(bytes32 _key) external view returns(uint256) { + function getUint256(bytes32 _key) external view returns(uint256) { return uintData[_key]; } @@ -183,7 +185,7 @@ contract DataStore is DataStoreStorage { return boolData[_key]; } - function getUintArray(bytes32 _key) external view returns(uint256[] memory) { + function getUint256Array(bytes32 _key) external view returns(uint256[] memory) { return uintArrayData[_key]; } @@ -199,7 +201,7 @@ contract DataStore is DataStoreStorage { return boolArrayData[_key]; } - function getUintArrayLength(bytes32 _key) external view returns(uint256) { + function getUint256ArrayLength(bytes32 _key) external view returns(uint256) { return uintArrayData[_key].length; } @@ -215,7 +217,7 @@ contract DataStore is DataStoreStorage { return boolArrayData[_key].length; } - function getUintArrayElement(bytes32 _key, uint256 _index) external view returns(uint256) { + function getUint256ArrayElement(bytes32 _key, uint256 _index) external view returns(uint256) { return uintArrayData[_key][_index]; } @@ -298,7 +300,7 @@ contract DataStore is DataStoreStorage { 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][uintArrayData[_key].length - 1]; + bytes32ArrayData[_key][_index] = bytes32ArrayData[_key][bytes32ArrayData[_key].length - 1]; } bytes32ArrayData[_key].length--; } @@ -306,7 +308,7 @@ contract DataStore is DataStoreStorage { 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][uintArrayData[_key].length - 1]; + addressArrayData[_key][_index] = addressArrayData[_key][addressArrayData[_key].length - 1]; } addressArrayData[_key].length--; } @@ -314,7 +316,7 @@ contract DataStore is DataStoreStorage { 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][uintArrayData[_key].length - 1]; + boolArrayData[_key][_index] = boolArrayData[_key][boolArrayData[_key].length - 1]; } boolArrayData[_key].length--; } diff --git a/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol b/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol index 9c6d8ddd3..a48c20fa1 100644 --- a/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol +++ b/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol @@ -43,7 +43,7 @@ contract KYCTransferManager is TransferManager { if (!paused) { bytes32 key = _getKYCKey(_to); DataStore dataStore = DataStore(ISecurityToken(securityToken).dataStore()); - if (dataStore.getUint(key) > 0) + if (dataStore.getUint256(key) > 0) return Result.VALID; } return Result.NA; @@ -56,19 +56,19 @@ contract KYCTransferManager is TransferManager { function _modifyKYC(address _investor, bool _kycStatus) internal { DataStore dataStore = DataStore(ISecurityToken(securityToken).dataStore()); bytes32 key = _getKYCKey(_investor); - uint256 kycNumber = dataStore.getUint(key); //index in address array + 1 + uint256 kycNumber = dataStore.getUint256(key); //index in address array + 1 uint256 kycTotal = dataStore.getAddressArrayLength(KYC_ARRAY); if(_kycStatus) { require(kycNumber == 0, "KYC exists"); - dataStore.setData(key, kycTotal + 1); - dataStore.insertData(KYC_ARRAY, _investor); + 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.setData(_getKYCKey(lastAddress), kycNumber); + 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. @@ -91,7 +91,7 @@ contract KYCTransferManager is TransferManager { function checkKYC(address _investor) public view returns (bool kyc) { bytes32 key = _getKYCKey(_investor); DataStore dataStore = DataStore(ISecurityToken(securityToken).dataStore()); - if (dataStore.getUint(key) > 0) + if (dataStore.getUint256(key) > 0) kyc = true; } diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index 1adce5bac..80e5abb0f 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -65,6 +65,7 @@ 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! From 82a99c15a193cf41404121d231bf16f73bbaea19 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Tue, 22 Jan 2019 11:59:41 +0530 Subject: [PATCH 13/19] Added test cases --- test/za_datastore.js | 349 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 294 insertions(+), 55 deletions(-) diff --git a/test/za_datastore.js b/test/za_datastore.js index 9b590e78f..e6cab2318 100644 --- a/test/za_datastore.js +++ b/test/za_datastore.js @@ -1,121 +1,360 @@ +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) => { - describe("Should attach to security token securely", async () => { - it("Should be attached to a security token upon deployment", async () => { - - }); + // Accounts Variable declaration + let account_polymath; + let token_owner; - it("Should not allow non-issuer to change security token address", async () => { - + // 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 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"; + + 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 not allow DATA module to change security token address", async () => { - + 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 allow issuer to change security token address", async () => { - + it("Should fetch data store address", async () => { + I_DataStore = await DataStore.at(await I_SecurityToken.dataStore()); }); }); - describe("Should not allow unautohrized modification to data", async () => { - it("Should not allow random addresses to modify data", async () => { - + 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 modules that does not belong to DATA type to modify data", async () => { - + it("Should not allow non-issuer to change security token address", async () => { + await catchRevert(I_DataStore.setSecurityToken(address_one, { from: account_polymath })); }); - it("Should not allow archived modules to modify data", async () => { - + 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 uint256 correctly", async () => { + it("Should set and fetch uint256 correctly", async () => { + 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 set bytes32 correctly", async () => { - + 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 set address correctly", async () => { - + 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 set string correctly", async () => { - + 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 set bytes correctly", async () => { - + 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 set bool correctly", async () => { - + 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 uint256 array correctly", async () => { + 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 set bytes32 array correctly", async () => { - + it("Should not allow unauthorized addresses to modify address", async () => { + await catchRevert(I_DataStore.setAddress(key, address_one, { from: account_polymath })); }); - it("Should set address array correctly", async () => { - + it("Should not allow unauthorized addresses to modify string", async () => { + await catchRevert(I_DataStore.setString(key, name, { from: account_polymath })); }); - it("Should set bool array correctly", async () => { - + it("Should not allow unauthorized addresses to modify bytes", async () => { + await catchRevert(I_DataStore.setBytes32(key, bytes32data, { from: account_polymath })); }); - }); - describe("Should fetch data correctly", async () => { - it("Should fetch uint256 correctly", async () => { + 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 fetch bytes32 correctly", async () => { - + 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 fetch address correctly", async () => { - + 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 fetch string correctly", async () => { - + 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 fetch bytes correctly", async () => { - + 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 fetch bool correctly", async () => { - + it("Should not allow unauthorized addresses to insert bytes32 into Array", async () => { + await catchRevert(I_DataStore.insertBytes32(key, bytes32data, { from: account_polymath })); }); - it("Should fetch uint256 array correctly", async () => { + 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 fetch bytes32 array correctly", async () => { - + it("Should not allow unauthorized addresses to delete bytes32 from Array", async () => { + await catchRevert(I_DataStore.deleteBytes32(key, 0, { from: account_polymath })); }); - it("Should fetch address array correctly", async () => { - + it("Should not allow unauthorized addresses to delete address from Array", async () => { + await catchRevert(I_DataStore.deleteAddress(key, 0, { from: account_polymath })); }); - it("Should fetch bool array correctly", async () => { - + it("Should not allow unauthorized addresses to delete bool from Array", async () => { + await catchRevert(I_DataStore.deleteBool(key, 0, { from: account_polymath })); }); }); }); \ No newline at end of file From 2d24fe588de9b23c6ef656640abe9c89fdb82d16 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Wed, 23 Jan 2019 12:34:28 +0530 Subject: [PATCH 14/19] Code cleanup and comments --- contracts/datastore/DataStore.sol | 43 +++++- contracts/datastore/DataStoreStorage.sol | 2 +- contracts/interfaces/IDataStore.sol | 128 ++++++++++++++++++ contracts/interfaces/ISecurityToken.sol | 14 +- .../TransferManager/KYCTransferManager.sol | 10 +- contracts/tokens/STFactory.sol | 2 +- contracts/tokens/SecurityToken.sol | 18 ++- 7 files changed, 199 insertions(+), 18 deletions(-) create mode 100644 contracts/interfaces/IDataStore.sol diff --git a/contracts/datastore/DataStore.sol b/contracts/datastore/DataStore.sol index 072a9e3c3..29d2e2b1d 100644 --- a/contracts/datastore/DataStore.sol +++ b/contracts/datastore/DataStore.sol @@ -2,9 +2,13 @@ pragma solidity ^0.5.0; import "../interfaces/ISecurityToken.sol"; import "../interfaces/IOwnable.sol"; +import "../interfaces/IDataStore.sol"; import "./DataStoreStorage.sol"; -contract DataStore is DataStoreStorage { +/** + * @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. @@ -34,13 +38,22 @@ contract DataStore is DataStoreStorage { _; } - function setSecurityToken(address _securityToken) public onlyOwner { + /** + * @dev Changes security token atatched to this data store + * @param _securityToken address of the security token + */ + function setSecurityToken(address _securityToken) external onlyOwner { if(address(securityToken) != address(0)) { require(msg.sender == IOwnable(address(securityToken)).owner(), "Unauthorized"); } 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); } @@ -65,6 +78,11 @@ contract DataStore is DataStoreStorage { _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); } @@ -81,6 +99,11 @@ contract DataStore is DataStoreStorage { _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); } @@ -97,6 +120,12 @@ contract DataStore is DataStoreStorage { _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); } @@ -113,6 +142,11 @@ contract DataStore is DataStoreStorage { _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 validInputLength(_keys.length, _data.length) { for (uint256 i = 0; i < _keys.length; i++) { _setData(_keys[i], _data[i]); @@ -137,6 +171,11 @@ contract DataStore is DataStoreStorage { } } + /** + * @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 validInputLength(_keys.length, _data.length) { for (uint256 i = 0; i < _keys.length; i++) { _insertData(_keys[i], _data[i]); diff --git a/contracts/datastore/DataStoreStorage.sol b/contracts/datastore/DataStoreStorage.sol index dd869ce20..9d724f070 100644 --- a/contracts/datastore/DataStoreStorage.sol +++ b/contracts/datastore/DataStoreStorage.sol @@ -16,6 +16,6 @@ contract DataStoreStorage { mapping (bytes32 => address[]) internal addressArrayData; mapping (bytes32 => bool[]) internal boolArrayData; - uint8 constant DATA_KEY = 10; + 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 e1a0f104e..c1671db53 100644 --- a/contracts/interfaces/ISecurityToken.sol +++ b/contracts/interfaces/ISecurityToken.sol @@ -17,8 +17,6 @@ interface ISecurityToken { event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); - function dataStore() external view returns (address); - /** * @notice Validates a transfer with a TransferManager module if it exists * @dev TransferManager module has a key of 2 @@ -163,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 index a48c20fa1..f43a9cd64 100644 --- a/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol +++ b/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol @@ -1,7 +1,7 @@ pragma solidity ^0.5.0; import "../../TransferManager/TransferManager.sol"; -import "../../../datastore/DataStore.sol"; +import "../../../interfaces/IDataStore.sol"; import "../../../interfaces/ISecurityToken.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; @@ -42,7 +42,7 @@ contract KYCTransferManager is TransferManager { { if (!paused) { bytes32 key = _getKYCKey(_to); - DataStore dataStore = DataStore(ISecurityToken(securityToken).dataStore()); + IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); if (dataStore.getUint256(key) > 0) return Result.VALID; } @@ -54,7 +54,7 @@ contract KYCTransferManager is TransferManager { } function _modifyKYC(address _investor, bool _kycStatus) internal { - DataStore dataStore = DataStore(ISecurityToken(securityToken).dataStore()); + 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); @@ -84,13 +84,13 @@ contract KYCTransferManager is TransferManager { } function getKYCAddresses() public view returns(address[] memory) { - DataStore dataStore = DataStore(ISecurityToken(securityToken).dataStore()); + IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); return dataStore.getAddressArray(KYC_ARRAY); } function checkKYC(address _investor) public view returns (bool kyc) { bytes32 key = _getKYCKey(_investor); - DataStore dataStore = DataStore(ISecurityToken(securityToken).dataStore()); + IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); if (dataStore.getUint256(key) > 0) kyc = true; } diff --git a/contracts/tokens/STFactory.sol b/contracts/tokens/STFactory.sol index 5528d533a..d00e7e192 100644 --- a/contracts/tokens/STFactory.sol +++ b/contracts/tokens/STFactory.sol @@ -41,7 +41,7 @@ contract STFactory is ISTFactory { _polymathRegistry ); newSecurityToken.addModule(transferManagerFactory, "", 0, 0); - newSecurityToken.setDataStore(dataStoreFactory.generateDataStore(address(newSecurityToken))); + 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 80e5abb0f..7a0cb0d5e 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -46,7 +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 = 10; + uint8 constant DATA_KEY = 6; uint256 public granularity; @@ -211,12 +211,7 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater securityTokenVersion = SemanticVersion(2, 0, 0); } - function setDataStore(address _dataStore) public { - require(_dataStore != address(0), "Invalid address"); - dataStore = _dataStore; - } - - /** + /** * @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. @@ -377,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) public 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 From bc882153062fd550779e6c270eb10e129fed92d7 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Wed, 23 Jan 2019 12:41:31 +0530 Subject: [PATCH 15/19] Added test cases for security token --- contracts/tokens/SecurityToken.sol | 2 +- test/o_security_token.js | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index 7a0cb0d5e..1eb1acd65 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -376,7 +376,7 @@ contract SecurityToken is ERC20, ERC20Detailed, ReentrancyGuard, RegistryUpdater * @notice Allows owner to change data store * @param _dataStore Address of the token data store */ - function changeDataStore(address _dataStore) public onlyOwner { + function changeDataStore(address _dataStore) external onlyOwner { require(_dataStore != address(0), "Invalid address"); dataStore = _dataStore; } diff --git a/test/o_security_token.js b/test/o_security_token.js index aac62b9e0..fe51ca271 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()); + 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 }); From cda7408eb7059248c93ec7bba131137132598480 Mon Sep 17 00:00:00 2001 From: Mudit Gupta Date: Wed, 23 Jan 2019 13:03:51 +0530 Subject: [PATCH 16/19] Added more test cases for data store --- test/o_security_token.js | 2 +- test/za_datastore.js | 108 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/test/o_security_token.js b/test/o_security_token.js index fe51ca271..d6adbbc99 100644 --- a/test/o_security_token.js +++ b/test/o_security_token.js @@ -613,7 +613,7 @@ contract("SecurityToken", async (accounts) => { let ds = await I_SecurityToken.dataStore(); await I_SecurityToken.changeDataStore(one_address, { from: token_owner }); assert.equal(one_address, await I_SecurityToken.dataStore()); - I_SecurityToken.changeDataStore(ds, { from: token_owner }); + 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 () => { diff --git a/test/za_datastore.js b/test/za_datastore.js index e6cab2318..b89646bac 100644 --- a/test/za_datastore.js +++ b/test/za_datastore.js @@ -36,6 +36,7 @@ contract("Data store", async (accounts) => { 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"; @@ -44,6 +45,7 @@ contract("Data store", async (accounts) => { const address_zero = "0x0000000000000000000000000000000000000000"; const address_one = "0x0000000000000000000000000000000000000001"; + const address_two = "0x0000000000000000000000000000000000000002"; before(async () => { account_polymath = accounts[0]; @@ -134,6 +136,7 @@ contract("Data store", async (accounts) => { 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"); }); @@ -278,6 +281,79 @@ contract("Data store", async (accounts) => { 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 () => { @@ -356,5 +432,37 @@ contract("Data store", async (accounts) => { 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 From f269c99a3484030917afb0fb473bb83b3f09056b Mon Sep 17 00:00:00 2001 From: satyam Date: Mon, 28 Jan 2019 14:02:41 +0530 Subject: [PATCH 17/19] minor fixes --- contracts/datastore/DataStore.sol | 18 +++++++++--------- .../TransferManager/KYCTransferManager.sol | 7 ++----- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/contracts/datastore/DataStore.sol b/contracts/datastore/DataStore.sol index 29d2e2b1d..3bef0b6a0 100644 --- a/contracts/datastore/DataStore.sol +++ b/contracts/datastore/DataStore.sol @@ -28,7 +28,7 @@ contract DataStore is DataStoreStorage, IDataStore { _; } - modifier validInputLength(uint256 _keyLength, uint256 _dataLength) { + modifier validArrayLength(uint256 _keyLength, uint256 _dataLength) { require(_keyLength == _dataLength, "Array length mismatch"); _; } @@ -147,25 +147,25 @@ contract DataStore is DataStoreStorage, IDataStore { * @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 validInputLength(_keys.length, _data.length) { + 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 validInputLength(_keys.length, _data.length) { + 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 validInputLength(_keys.length, _data.length) { + 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 validInputLength(_keys.length, _data.length) { + 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]); } @@ -176,25 +176,25 @@ contract DataStore is DataStoreStorage, IDataStore { * @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 validInputLength(_keys.length, _data.length) { + 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 validInputLength(_keys.length, _data.length) { + 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 validInputLength(_keys.length, _data.length) { + 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 validInputLength(_keys.length, _data.length) { + 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]); } diff --git a/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol b/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol index f43a9cd64..7a314220e 100644 --- a/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol +++ b/contracts/modules/Experimental/TransferManager/KYCTransferManager.sol @@ -40,11 +40,8 @@ contract KYCTransferManager is TransferManager { public returns (Result) { - if (!paused) { - bytes32 key = _getKYCKey(_to); - IDataStore dataStore = IDataStore(ISecurityToken(securityToken).dataStore()); - if (dataStore.getUint256(key) > 0) - return Result.VALID; + if (!paused && checkKYC(_to)) { + return Result.VALID; } return Result.NA; } From 71631d7481ebfa167280968db15aa5fda48153e9 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 29 Jan 2019 14:37:08 -0400 Subject: [PATCH 18/19] Small update to setSecurityToken --- contracts/datastore/DataStore.sol | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/contracts/datastore/DataStore.sol b/contracts/datastore/DataStore.sol index 3bef0b6a0..6ab6b4d47 100644 --- a/contracts/datastore/DataStore.sol +++ b/contracts/datastore/DataStore.sol @@ -13,11 +13,13 @@ contract DataStore is DataStoreStorage, IDataStore { //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), + require(isOwner || + securityToken.isModule(msg.sender, DATA_KEY) || + securityToken.checkPermission(msg.sender, address(this), MANAGEDATA), "Unauthorized" ); _; @@ -43,11 +45,10 @@ contract DataStore is DataStoreStorage, IDataStore { * @param _securityToken address of the security token */ function setSecurityToken(address _securityToken) external onlyOwner { - if(address(securityToken) != address(0)) { - require(msg.sender == IOwnable(address(securityToken)).owner(), "Unauthorized"); - } + require(_securityToken != address(0), "Invalid address"); + emit SecurityTokenChanged(securityToken, _securityToken); securityToken = ISecurityToken(_securityToken); - } + } /** * @dev Stores a uint256 data against a key From 81ff0dcd9e1ac8a93a9a8267ec624d0a70ef2de7 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 29 Jan 2019 14:53:25 -0400 Subject: [PATCH 19/19] Fix --- contracts/datastore/DataStore.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/datastore/DataStore.sol b/contracts/datastore/DataStore.sol index 6ab6b4d47..3c68aac97 100644 --- a/contracts/datastore/DataStore.sol +++ b/contracts/datastore/DataStore.sol @@ -46,7 +46,7 @@ contract DataStore is DataStoreStorage, IDataStore { */ function setSecurityToken(address _securityToken) external onlyOwner { require(_securityToken != address(0), "Invalid address"); - emit SecurityTokenChanged(securityToken, _securityToken); + emit SecurityTokenChanged(address(securityToken), _securityToken); securityToken = ISecurityToken(_securityToken); }