From e4cedab89883c112acafe09ca3231517788d4cfd Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 25 Sep 2018 23:08:31 +0100 Subject: [PATCH 1/6] WIP --- contracts/SecurityTokenRegistry.sol | 68 ++++++++++++++++++++++++++++- contracts/interfaces/IOwnable.sol | 26 +++++++++++ contracts/interfaces/IOwner.sol | 5 --- test/n_security_token_registry.js | 3 ++ test/o_security_token.js | 1 - 5 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 contracts/interfaces/IOwnable.sol delete mode 100644 contracts/interfaces/IOwner.sol diff --git a/contracts/SecurityTokenRegistry.sol b/contracts/SecurityTokenRegistry.sol index 36af1baf4..12c0889a8 100644 --- a/contracts/SecurityTokenRegistry.sol +++ b/contracts/SecurityTokenRegistry.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.24; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; -import "./interfaces/IOwner.sol"; +import "./interfaces/IOwnable.sol"; import "./interfaces/ISTFactory.sol"; import "./interfaces/IERC20.sol"; import "./interfaces/ISecurityTokenRegistry.sol"; @@ -25,6 +25,9 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { address public owner; address public polymathRegistry; + address[] public activeUsers; + mapping(address => bool) public seenUsers; + mapping(address => bytes32[]) userToTickers; mapping(string => address) tickerToSecurityToken; mapping(string => uint) tickerIndex; @@ -143,6 +146,11 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { require(IERC20(getAddress(Encoder.getKey("polyToken"))).transferFrom(msg.sender, address(this), getUint(Encoder.getKey("tickerRegFee"))), "Sufficent allowance is not provided"); string memory ticker = Util.upper(_ticker); require(_tickerAvailable(ticker), "Ticker is already reserved"); + // Check whether ticker was previously registered (and expired) + address previousOwner = getAddress(Encoder.getKey("registeredTickers_owner", _ticker)); + if (previousOwner != address(0)) { + _deleteTickerOwnership(previousOwner, _ticker); + } _addTicker(_owner, ticker, _tokenName, now, now.add(getUint(Encoder.getKey("expiryLimit"))), false, false); } @@ -232,6 +240,10 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { uint256 length = uint256(getArrayBytes32(Encoder.getKey("userToTickers", _owner)).length); pushArray(Encoder.getKey("userToTickers", _owner), Util.stringToBytes32(_ticker)); set(Encoder.getKey("tickerIndex", _ticker), length); + if (!getBool(Encoder.getKey("seenUsers", _owner))) { + pushArray(Encoder.getKey("activeUsers"), _owner); + set(Encoder.getKey("seenUsers", _owner), true); + } } /** @@ -280,6 +292,7 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { */ function _deleteTickerOwnership(address _owner, string _ticker) internal { uint256 _index = uint256(getUint(Encoder.getKey("tickerIndex", _ticker))); + assert(_index < getArrayBytes32(Encoder.getKey("userToTickers", _owner)).length); // deleting the _index from the data strucutre userToTickers[_oldowner][_index]; deleteArrayBytes32(Encoder.getKey("userToTickers", _owner), _index); @@ -317,6 +330,57 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { } return tempList; } + event Log(uint256 _log); + event LogA(address _log); + event LogB(bytes32 _log); + + /** + * @notice Returns the list of tokens owned by the selected address + * @param _owner is the address which owns the list of tickers + * @dev Intention is that this is called off-chain so block gas limit is not relevant + */ + function getTokensByOwner(address _owner) external returns(address[]) { + // Loop over all active users, then all associated tickers of those users + // This ensures we find tokens, even if their owner has been modified + address[] memory activeUsers = getArrayAddress(Encoder.getKey("activeUsers")); + emit Log(activeUsers.length); + bytes32[] memory tickers; + address token; + uint256 count = 0; + uint256 i = 0; + uint256 j = 0; + for (i = 0; i < activeUsers.length; i++) { + emit LogA(activeUsers[i]); + tickers = getArrayBytes32(Encoder.getKey("userToTickers", activeUsers[i])); + emit Log(tickers.length); + for (j = 0; j < tickers.length; j++) { + emit LogB(tickers[j]); + token = getAddress(Encoder.getKey("tickerToSecurityToken", Util.bytes32ToString(tickers[j]))); + emit LogA(token); + if (token != address(0)) { + if (IOwnable(token).owner() == _owner) { + count = count + 1; + } + } + } + } + emit Log(count); + uint256 index = 0; + address[] memory result = new address[](count); + for (i = 0; i < activeUsers.length; i++) { + tickers = getArrayBytes32(Encoder.getKey("userToTickers", activeUsers[i])); + for (j = 0; j < tickers.length; j++) { + token = getAddress(Encoder.getKey("tickerToSecurityToken", Util.bytes32ToString(tickers[j]))); + if (token != address(0)) { + if (IOwnable(token).owner() == _owner) { + result[index] = token; + index = index + 1; + } + } + } + } + return result; + } /** * @notice Returns the owner and timestamp for a given ticker @@ -442,7 +506,7 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { function getSecurityTokenData(address _securityToken) external view returns (string, address, string, uint256) { return ( getString(Encoder.getKey("securityTokens_ticker", _securityToken)), - IOwner(_securityToken).owner(), + IOwnable(_securityToken).owner(), getString(Encoder.getKey("securityTokens_tokenDetails", _securityToken)), getUint(Encoder.getKey("securityTokens_deployedAt", _securityToken)) ); diff --git a/contracts/interfaces/IOwnable.sol b/contracts/interfaces/IOwnable.sol new file mode 100644 index 000000000..15849e181 --- /dev/null +++ b/contracts/interfaces/IOwnable.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.4.24; + + +/** + * @title Ownable + * @dev The Ownable contract has an owner address, and provides basic authorization control + * functions, this simplifies the implementation of "user permissions". + */ +interface IOwnable { + /** + * @dev Returns owner + */ + function owner() external returns (address); + + /** + * @dev Allows the current owner to relinquish control of the contract. + */ + function renounceOwnership() external; + + /** + * @dev Allows the current owner to transfer control of the contract to a newOwner. + * @param _newOwner The address to transfer ownership to. + */ + function transferOwnership(address _newOwner) external; + +} diff --git a/contracts/interfaces/IOwner.sol b/contracts/interfaces/IOwner.sol deleted file mode 100644 index f76b3516b..000000000 --- a/contracts/interfaces/IOwner.sol +++ /dev/null @@ -1,5 +0,0 @@ -pragma solidity ^0.4.24; - -contract IOwner { - address public owner; -} \ No newline at end of file diff --git a/test/n_security_token_registry.js b/test/n_security_token_registry.js index 38e28315e..ffdd2cd21 100644 --- a/test/n_security_token_registry.js +++ b/test/n_security_token_registry.js @@ -614,6 +614,9 @@ contract('SecurityTokenRegistry', accounts => { let _blockNo = latestBlock(); let tx = await I_STRProxied.generateSecurityToken(name2, symbol2, tokenDetails, false, { from: token_owner, gas:60000000 }); + console.log(await I_STRProxied.getTokensByOwner(token_owner)); + assert.isTrue(false); + // Verify the successful generation of the security token assert.equal(tx.logs[1].args._ticker, symbol2, "SecurityToken doesn't get deployed"); diff --git a/test/o_security_token.js b/test/o_security_token.js index ce24a9be0..f94354bae 100644 --- a/test/o_security_token.js +++ b/test/o_security_token.js @@ -282,7 +282,6 @@ contract('SecurityToken', accounts => { await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); let _blockNo = latestBlock(); let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner, gas:60000000 }); - // Verify the successful generation of the security token assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed"); From feb6774bafbad037f8d8833f5eb6efea2274c568 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 25 Sep 2018 23:13:30 +0100 Subject: [PATCH 2/6] Remove Log events --- contracts/SecurityTokenRegistry.sol | 9 --------- test/n_security_token_registry.js | 6 +++--- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/contracts/SecurityTokenRegistry.sol b/contracts/SecurityTokenRegistry.sol index 12c0889a8..17a1df77d 100644 --- a/contracts/SecurityTokenRegistry.sol +++ b/contracts/SecurityTokenRegistry.sol @@ -330,9 +330,6 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { } return tempList; } - event Log(uint256 _log); - event LogA(address _log); - event LogB(bytes32 _log); /** * @notice Returns the list of tokens owned by the selected address @@ -343,20 +340,15 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { // Loop over all active users, then all associated tickers of those users // This ensures we find tokens, even if their owner has been modified address[] memory activeUsers = getArrayAddress(Encoder.getKey("activeUsers")); - emit Log(activeUsers.length); bytes32[] memory tickers; address token; uint256 count = 0; uint256 i = 0; uint256 j = 0; for (i = 0; i < activeUsers.length; i++) { - emit LogA(activeUsers[i]); tickers = getArrayBytes32(Encoder.getKey("userToTickers", activeUsers[i])); - emit Log(tickers.length); for (j = 0; j < tickers.length; j++) { - emit LogB(tickers[j]); token = getAddress(Encoder.getKey("tickerToSecurityToken", Util.bytes32ToString(tickers[j]))); - emit LogA(token); if (token != address(0)) { if (IOwnable(token).owner() == _owner) { count = count + 1; @@ -364,7 +356,6 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { } } } - emit Log(count); uint256 index = 0; address[] memory result = new address[](count); for (i = 0; i < activeUsers.length; i++) { diff --git a/test/n_security_token_registry.js b/test/n_security_token_registry.js index ffdd2cd21..5f87db8f5 100644 --- a/test/n_security_token_registry.js +++ b/test/n_security_token_registry.js @@ -614,13 +614,13 @@ contract('SecurityTokenRegistry', accounts => { let _blockNo = latestBlock(); let tx = await I_STRProxied.generateSecurityToken(name2, symbol2, tokenDetails, false, { from: token_owner, gas:60000000 }); - console.log(await I_STRProxied.getTokensByOwner(token_owner)); - assert.isTrue(false); - // Verify the successful generation of the security token assert.equal(tx.logs[1].args._ticker, symbol2, "SecurityToken doesn't get deployed"); I_SecurityToken002 = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + let tokens = await I_STRProxied.getTokensByOwner.call(token_owner); + assert.equal(tokens[0], I_SecurityToken.address); + assert.equal(tokens[1], I_SecurityToken002.address); const log = await promisifyLogWatch(I_SecurityToken002.LogModuleAdded({from: _blockNo}), 1); // Verify that GeneralTransferManager module get added successfully or not From ecbf96fd5171f090927e7aca2ae7dc9460e1c782 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 25 Sep 2018 23:16:49 +0100 Subject: [PATCH 3/6] Fixes --- contracts/interfaces/ISecurityTokenRegistry.sol | 9 ++++++++- test/n_security_token_registry.js | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/contracts/interfaces/ISecurityTokenRegistry.sol b/contracts/interfaces/ISecurityTokenRegistry.sol index 4a30ce99c..974297b37 100644 --- a/contracts/interfaces/ISecurityTokenRegistry.sol +++ b/contracts/interfaces/ISecurityTokenRegistry.sol @@ -55,7 +55,7 @@ interface ISecurityTokenRegistry { * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ - function transferOwnership(address _newOwner) external; + function transferOwnership(address _newOwner) external; /** * @notice Get security token address by ticker name @@ -85,6 +85,13 @@ interface ISecurityTokenRegistry { */ function getTickersByOwner(address _owner) external view returns(bytes32[]); + /** + * @notice Returns the list of tokens owned by the selected address + * @param _owner is the address which owns the list of tickers + * @dev Intention is that this is called off-chain so block gas limit is not relevant + */ + function getTokensByOwner(address _owner) external returns(address[]); + /** * @notice Returns the owner and timestamp for a given ticker * @param _ticker ticker diff --git a/test/n_security_token_registry.js b/test/n_security_token_registry.js index 5f87db8f5..ceef4c9f3 100644 --- a/test/n_security_token_registry.js +++ b/test/n_security_token_registry.js @@ -1087,7 +1087,7 @@ contract('SecurityTokenRegistry', accounts => { let tickersList = await I_STRProxied.getTickersByOwner.call(token_owner); assert.equal(tickersList.length, 4); let tickersListArray = await I_STRProxied.getTickersByOwner.call(account_temp); - assert.equal(tickersListArray.length, 3); + assert.equal(tickersListArray.length, 2); }); }); From 1b23e17cead628d418732834b65d201100266e74 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Tue, 25 Sep 2018 23:18:01 +0100 Subject: [PATCH 4/6] Update change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82175d71f..8f66897d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. [__1.5.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __15-08-18__ ## Added +* Added `getTokensByOwner` to STR * Generalised MakerDAO oracle to allow different instances referencing different currencies * Added DAI as a fundraising currency to USDTieredSTO * `transferTickerOwnership()` function is introduced in `TickerRegistry` to transfer the ticker ownership after the registeration #191. From 42c876ccd48f7a7e8254ca8c32c2f0d29507ee1b Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Wed, 26 Sep 2018 00:05:56 +0100 Subject: [PATCH 5/6] Mark function as view --- contracts/SecurityTokenRegistry.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/SecurityTokenRegistry.sol b/contracts/SecurityTokenRegistry.sol index 17a1df77d..189328f09 100644 --- a/contracts/SecurityTokenRegistry.sol +++ b/contracts/SecurityTokenRegistry.sol @@ -336,7 +336,7 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage { * @param _owner is the address which owns the list of tickers * @dev Intention is that this is called off-chain so block gas limit is not relevant */ - function getTokensByOwner(address _owner) external returns(address[]) { + function getTokensByOwner(address _owner) external view returns(address[]) { // Loop over all active users, then all associated tickers of those users // This ensures we find tokens, even if their owner has been modified address[] memory activeUsers = getArrayAddress(Encoder.getKey("activeUsers")); From ae76ed719075251ed15f9d81ef8cbd30654e036b Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Wed, 26 Sep 2018 08:46:42 +0100 Subject: [PATCH 6/6] Fix view in interface --- contracts/interfaces/ISecurityTokenRegistry.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/interfaces/ISecurityTokenRegistry.sol b/contracts/interfaces/ISecurityTokenRegistry.sol index 974297b37..3d5cc0736 100644 --- a/contracts/interfaces/ISecurityTokenRegistry.sol +++ b/contracts/interfaces/ISecurityTokenRegistry.sol @@ -90,7 +90,7 @@ interface ISecurityTokenRegistry { * @param _owner is the address which owns the list of tickers * @dev Intention is that this is called off-chain so block gas limit is not relevant */ - function getTokensByOwner(address _owner) external returns(address[]); + function getTokensByOwner(address _owner) external view returns(address[]); /** * @notice Returns the owner and timestamp for a given ticker