Skip to content

Commit d205c98

Browse files
authored
Merge pull request #280 from PolymathNetwork/token_owners_str
Add getTokensByOwner to STR
2 parents 8fcaa4c + 2666c1d commit d205c98

File tree

7 files changed

+96
-10
lines changed

7 files changed

+96
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
66
[__1.5.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __15-08-18__
77

88
## Added
9+
* Added `getTokensByOwner` to STR
910
* Added withholding tax to ether & erc20 dividends
1011
* Generalised MakerDAO oracle to allow different instances referencing different currencies
1112
* Added DAI as a fundraising currency to USDTieredSTO

contracts/SecurityTokenRegistry.sol

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pragma solidity ^0.4.24;
22

33
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
4-
import "./interfaces/IOwner.sol";
4+
import "./interfaces/IOwnable.sol";
55
import "./interfaces/ISTFactory.sol";
66
import "./interfaces/IERC20.sol";
77
import "./interfaces/ISecurityTokenRegistry.sol";
@@ -25,6 +25,9 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage {
2525
address public owner;
2626
address public polymathRegistry;
2727
28+
address[] public activeUsers;
29+
mapping(address => bool) public seenUsers;
30+
2831
mapping(address => bytes32[]) userToTickers;
2932
mapping(string => address) tickerToSecurityToken;
3033
mapping(string => uint) tickerIndex;
@@ -143,6 +146,11 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage {
143146
require(IERC20(getAddress(Encoder.getKey("polyToken"))).transferFrom(msg.sender, address(this), getUint(Encoder.getKey("tickerRegFee"))), "Sufficent allowance is not provided");
144147
string memory ticker = Util.upper(_ticker);
145148
require(_tickerAvailable(ticker), "Ticker is already reserved");
149+
// Check whether ticker was previously registered (and expired)
150+
address previousOwner = getAddress(Encoder.getKey("registeredTickers_owner", _ticker));
151+
if (previousOwner != address(0)) {
152+
_deleteTickerOwnership(previousOwner, _ticker);
153+
}
146154
_addTicker(_owner, ticker, _tokenName, now, now.add(getUint(Encoder.getKey("expiryLimit"))), false, false);
147155
}
148156

@@ -232,6 +240,10 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage {
232240
uint256 length = uint256(getArrayBytes32(Encoder.getKey("userToTickers", _owner)).length);
233241
pushArray(Encoder.getKey("userToTickers", _owner), Util.stringToBytes32(_ticker));
234242
set(Encoder.getKey("tickerIndex", _ticker), length);
243+
if (!getBool(Encoder.getKey("seenUsers", _owner))) {
244+
pushArray(Encoder.getKey("activeUsers"), _owner);
245+
set(Encoder.getKey("seenUsers", _owner), true);
246+
}
235247
}
236248

237249
/**
@@ -280,6 +292,7 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage {
280292
*/
281293
function _deleteTickerOwnership(address _owner, string _ticker) internal {
282294
uint256 _index = uint256(getUint(Encoder.getKey("tickerIndex", _ticker)));
295+
assert(_index < getArrayBytes32(Encoder.getKey("userToTickers", _owner)).length);
283296
// deleting the _index from the data strucutre userToTickers[_oldowner][_index];
284297
deleteArrayBytes32(Encoder.getKey("userToTickers", _owner), _index);
285298

@@ -318,6 +331,48 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage {
318331
return tempList;
319332
}
320333

334+
/**
335+
* @notice Returns the list of tokens owned by the selected address
336+
* @param _owner is the address which owns the list of tickers
337+
* @dev Intention is that this is called off-chain so block gas limit is not relevant
338+
*/
339+
function getTokensByOwner(address _owner) external view returns(address[]) {
340+
// Loop over all active users, then all associated tickers of those users
341+
// This ensures we find tokens, even if their owner has been modified
342+
address[] memory activeUsers = getArrayAddress(Encoder.getKey("activeUsers"));
343+
bytes32[] memory tickers;
344+
address token;
345+
uint256 count = 0;
346+
uint256 i = 0;
347+
uint256 j = 0;
348+
for (i = 0; i < activeUsers.length; i++) {
349+
tickers = getArrayBytes32(Encoder.getKey("userToTickers", activeUsers[i]));
350+
for (j = 0; j < tickers.length; j++) {
351+
token = getAddress(Encoder.getKey("tickerToSecurityToken", Util.bytes32ToString(tickers[j])));
352+
if (token != address(0)) {
353+
if (IOwnable(token).owner() == _owner) {
354+
count = count + 1;
355+
}
356+
}
357+
}
358+
}
359+
uint256 index = 0;
360+
address[] memory result = new address[](count);
361+
for (i = 0; i < activeUsers.length; i++) {
362+
tickers = getArrayBytes32(Encoder.getKey("userToTickers", activeUsers[i]));
363+
for (j = 0; j < tickers.length; j++) {
364+
token = getAddress(Encoder.getKey("tickerToSecurityToken", Util.bytes32ToString(tickers[j])));
365+
if (token != address(0)) {
366+
if (IOwnable(token).owner() == _owner) {
367+
result[index] = token;
368+
index = index + 1;
369+
}
370+
}
371+
}
372+
}
373+
return result;
374+
}
375+
321376
/**
322377
* @notice Returns the owner and timestamp for a given ticker
323378
* @param _ticker is the ticker symbol
@@ -442,7 +497,7 @@ contract SecurityTokenRegistry is ISecurityTokenRegistry, EternalStorage {
442497
function getSecurityTokenData(address _securityToken) external view returns (string, address, string, uint256) {
443498
return (
444499
getString(Encoder.getKey("securityTokens_ticker", _securityToken)),
445-
IOwner(_securityToken).owner(),
500+
IOwnable(_securityToken).owner(),
446501
getString(Encoder.getKey("securityTokens_tokenDetails", _securityToken)),
447502
getUint(Encoder.getKey("securityTokens_deployedAt", _securityToken))
448503
);

contracts/interfaces/IOwnable.sol

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
pragma solidity ^0.4.24;
2+
3+
4+
/**
5+
* @title Ownable
6+
* @dev The Ownable contract has an owner address, and provides basic authorization control
7+
* functions, this simplifies the implementation of "user permissions".
8+
*/
9+
interface IOwnable {
10+
/**
11+
* @dev Returns owner
12+
*/
13+
function owner() external returns (address);
14+
15+
/**
16+
* @dev Allows the current owner to relinquish control of the contract.
17+
*/
18+
function renounceOwnership() external;
19+
20+
/**
21+
* @dev Allows the current owner to transfer control of the contract to a newOwner.
22+
* @param _newOwner The address to transfer ownership to.
23+
*/
24+
function transferOwnership(address _newOwner) external;
25+
26+
}

contracts/interfaces/IOwner.sol

Lines changed: 0 additions & 5 deletions
This file was deleted.

contracts/interfaces/ISecurityTokenRegistry.sol

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ interface ISecurityTokenRegistry {
5555
* @dev Allows the current owner to transfer control of the contract to a newOwner.
5656
* @param _newOwner The address to transfer ownership to.
5757
*/
58-
function transferOwnership(address _newOwner) external;
58+
function transferOwnership(address _newOwner) external;
5959

6060
/**
6161
* @notice Get security token address by ticker name
@@ -85,6 +85,13 @@ interface ISecurityTokenRegistry {
8585
*/
8686
function getTickersByOwner(address _owner) external view returns(bytes32[]);
8787

88+
/**
89+
* @notice Returns the list of tokens owned by the selected address
90+
* @param _owner is the address which owns the list of tickers
91+
* @dev Intention is that this is called off-chain so block gas limit is not relevant
92+
*/
93+
function getTokensByOwner(address _owner) external view returns(address[]);
94+
8895
/**
8996
* @notice Returns the owner and timestamp for a given ticker
9097
* @param _ticker ticker

test/n_security_token_registry.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,9 @@ contract('SecurityTokenRegistry', accounts => {
618618
assert.equal(tx.logs[1].args._ticker, symbol2, "SecurityToken doesn't get deployed");
619619

620620
I_SecurityToken002 = SecurityToken.at(tx.logs[1].args._securityTokenAddress);
621+
let tokens = await I_STRProxied.getTokensByOwner.call(token_owner);
622+
assert.equal(tokens[0], I_SecurityToken.address);
623+
assert.equal(tokens[1], I_SecurityToken002.address);
621624

622625
const log = await promisifyLogWatch(I_SecurityToken002.LogModuleAdded({from: _blockNo}), 1);
623626
// Verify that GeneralTransferManager module get added successfully or not
@@ -1084,7 +1087,7 @@ contract('SecurityTokenRegistry', accounts => {
10841087
let tickersList = await I_STRProxied.getTickersByOwner.call(token_owner);
10851088
assert.equal(tickersList.length, 4);
10861089
let tickersListArray = await I_STRProxied.getTickersByOwner.call(account_temp);
1087-
assert.equal(tickersListArray.length, 3);
1090+
assert.equal(tickersListArray.length, 2);
10881091
});
10891092

10901093
});

test/o_security_token.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,6 @@ contract('SecurityToken', accounts => {
282282
await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner });
283283
let _blockNo = latestBlock();
284284
let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner, gas:60000000 });
285-
286285
// Verify the successful generation of the security token
287286
assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed");
288287

0 commit comments

Comments
 (0)