Skip to content

Commit 2cad781

Browse files
maxsam4adamdossa
authored andcommitted
Refresh / Upgrade tokens (#632)
* Added refresh token function * Reduced STR size * Added test cases for refreshToken * reuse function * Added refresh token event
1 parent 87aa859 commit 2cad781

File tree

3 files changed

+157
-42
lines changed

3 files changed

+157
-42
lines changed

contracts/SecurityTokenRegistry.sol

Lines changed: 92 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
55
import "./interfaces/IOwnable.sol";
66
import "./interfaces/ISTFactory.sol";
77
import "./interfaces/ISecurityTokenRegistry.sol";
8+
import "./interfaces/ISecurityToken.sol";
89
import "./interfaces/IPolymathRegistry.sol";
910
import "./interfaces/IOracle.sol";
1011
import "./storage/EternalStorage.sol";
@@ -121,6 +122,16 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
121122
uint256 _usdFee,
122123
uint256 _polyFee
123124
);
125+
// Emit at when issuer refreshes exisiting token
126+
event SecurityTokenRefreshed(
127+
string _ticker,
128+
string _name,
129+
address indexed _securityTokenAddress,
130+
address indexed _owner,
131+
uint256 _addedAt,
132+
address _registrant,
133+
uint256 _protocolVersion
134+
);
124135
event ProtocolFactorySet(address indexed _STFactory, uint8 _major, uint8 _minor, uint8 _patch);
125136
event LatestVersionSet(uint8 _major, uint8 _minor, uint8 _patch);
126137
event ProtocolFactoryRemoved(address indexed _STFactory, uint8 _major, uint8 _minor, uint8 _patch);
@@ -140,11 +151,13 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
140151
* @notice Modifier to make a function callable only when the contract is not paused.
141152
*/
142153
modifier whenNotPausedOrOwner() {
143-
if (msg.sender == owner()) _;
144-
else {
154+
_whenNotPausedOrOwner();
155+
_;
156+
}
157+
158+
function _whenNotPausedOrOwner() internal view {
159+
if (msg.sender != owner())
145160
require(!isPaused(), "Paused");
146-
_;
147-
}
148161
}
149162

150163
/**
@@ -182,7 +195,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
182195
address _owner,
183196
address _getterContract
184197
)
185-
external
198+
public
186199
payable
187200
{
188201
require(!getBoolValue(INITIALIZE),"Initialized");
@@ -266,7 +279,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
266279
* @param _ticker is unique token ticker
267280
* @param _tokenName is the name of the token
268281
*/
269-
function registerTicker(address _owner, string calldata _ticker, string calldata _tokenName) external whenNotPausedOrOwner {
282+
function registerTicker(address _owner, string memory _ticker, string memory _tokenName) public whenNotPausedOrOwner {
270283
require(_owner != address(0), "Bad address");
271284
require(bytes(_ticker).length > 0 && bytes(_ticker).length <= 10, "Bad ticker");
272285
// Attempt to charge the reg fee if it is > 0 USD
@@ -315,13 +328,13 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
315328
*/
316329
function modifyTicker(
317330
address _owner,
318-
string calldata _ticker,
319-
string calldata _tokenName,
331+
string memory _ticker,
332+
string memory _tokenName,
320333
uint256 _registrationDate,
321334
uint256 _expiryDate,
322335
bool _status
323336
)
324-
external
337+
public
325338
onlyOwner
326339
{
327340
require(bytes(_ticker).length > 0 && bytes(_ticker).length <= 10, "Bad ticker");
@@ -367,7 +380,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
367380
* @notice Removes the ticker details, associated ownership & security token mapping
368381
* @param _ticker is the token ticker
369382
*/
370-
function removeTicker(string calldata _ticker) external onlyOwner {
383+
function removeTicker(string memory _ticker) public onlyOwner {
371384
string memory ticker = Util.upper(_ticker);
372385
address owner = _tickerOwner(ticker);
373386
require(owner != address(0), "Bad ticker");
@@ -428,23 +441,23 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
428441
internal
429442
{
430443
bytes32 key = Encoder.getKey("registeredTickers_owner", _ticker);
431-
if (getAddressValue(key) != _owner) set(key, _owner);
444+
set(key, _owner);
432445
key = Encoder.getKey("registeredTickers_registrationDate", _ticker);
433-
if (getUintValue(key) != _registrationDate) set(key, _registrationDate);
446+
set(key, _registrationDate);
434447
key = Encoder.getKey("registeredTickers_expiryDate", _ticker);
435-
if (getUintValue(key) != _expiryDate) set(key, _expiryDate);
448+
set(key, _expiryDate);
436449
key = Encoder.getKey("registeredTickers_tokenName", _ticker);
437-
if (Encoder.getKey(getStringValue(key)) != Encoder.getKey(_tokenName)) set(key, _tokenName);
450+
set(key, _tokenName);
438451
key = Encoder.getKey("registeredTickers_status", _ticker);
439-
if (getBoolValue(key) != _status) set(key, _status);
452+
set(key, _status);
440453
}
441454

442455
/**
443456
* @notice Transfers the ownership of the ticker
444457
* @param _newOwner is the address of the new owner of the ticker
445458
* @param _ticker is the ticker symbol
446459
*/
447-
function transferTickerOwnership(address _newOwner, string calldata _ticker) external whenNotPausedOrOwner {
460+
function transferTickerOwnership(address _newOwner, string memory _ticker) public whenNotPausedOrOwner {
448461
string memory ticker = Util.upper(_ticker);
449462
require(_newOwner != address(0), "Bad address");
450463
bytes32 ownerKey = Encoder.getKey("registeredTickers_owner", ticker);
@@ -479,7 +492,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
479492
* @notice Changes the expiry time for the token ticker. Only available to Polymath.
480493
* @param _newExpiry is the new expiry for newly generated tickers
481494
*/
482-
function changeExpiryLimit(uint256 _newExpiry) external onlyOwner {
495+
function changeExpiryLimit(uint256 _newExpiry) public onlyOwner {
483496
require(_newExpiry >= 1 days, "Bad dates");
484497
emit ChangeExpiryLimit(getUintValue(EXPIRYLIMIT), _newExpiry);
485498
set(EXPIRYLIMIT, _newExpiry);
@@ -501,14 +514,14 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
501514
* - if _protocolVersion == 0 then latest version of securityToken will be generated
502515
*/
503516
function generateSecurityToken(
504-
string calldata _name,
505-
string calldata _ticker,
506-
string calldata _tokenDetails,
517+
string memory _name,
518+
string memory _ticker,
519+
string memory _tokenDetails,
507520
bool _divisible,
508521
address _treasuryWallet,
509522
uint256 _protocolVersion
510523
)
511-
external
524+
public
512525
whenNotPausedOrOwner
513526
{
514527
uint256 protocolVersion = _protocolVersion;
@@ -525,7 +538,46 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
525538
require(_tickerOwner(ticker) == msg.sender, "Not authorised");
526539
/*solium-disable-next-line security/no-block-members*/
527540
require(getUintValue(Encoder.getKey("registeredTickers_expiryDate", ticker)) >= now, "Ticker expired");
528-
_deployToken(_name, ticker, _tokenDetails, msg.sender, _divisible, _treasuryWallet, protocolVersion);
541+
(uint256 _usdFee, uint256 _polyFee) = _takeFee(STLAUNCHFEE);
542+
address newSecurityTokenAddress =
543+
_deployToken(_name, ticker, _tokenDetails, msg.sender, _divisible, _treasuryWallet, protocolVersion, _usdFee, _polyFee);
544+
emit NewSecurityToken(
545+
_ticker, _name, newSecurityTokenAddress, msg.sender, now, msg.sender, false, _usdFee, _polyFee, protocolVersion
546+
);
547+
}
548+
549+
/**
550+
* @notice Deploys an instance of a new Security Token and replaces the old one in the registry
551+
* This can be used to upgrade from version 2.0 of ST to 3.0 or in case something goes wrong with earlier ST
552+
* @dev This function needs to be in STR 3.0. Defined public to avoid stack overflow
553+
* @param _name is the name of the token
554+
* @param _ticker is the ticker symbol of the security token
555+
* @param _tokenDetails is the off-chain details of the token
556+
* @param _divisible is whether or not the token is divisible
557+
*/
558+
function refreshSecurityToken(
559+
string memory _name,
560+
string memory _ticker,
561+
string memory _tokenDetails,
562+
bool _divisible,
563+
address _treasuryWallet
564+
)
565+
public whenNotPausedOrOwner returns (address)
566+
{
567+
require(bytes(_name).length > 0 && bytes(_ticker).length > 0, "Bad ticker");
568+
require(_treasuryWallet != address(0), "0x0 not allowed");
569+
string memory ticker = Util.upper(_ticker);
570+
require(_tickerStatus(ticker), "not deployed");
571+
address st = getAddressValue(Encoder.getKey("tickerToSecurityToken", ticker));
572+
address stOwner = IOwnable(st).owner();
573+
require(msg.sender == stOwner, "Unauthroized");
574+
require(ISecurityToken(st).transfersFrozen(), "Transfers not frozen");
575+
uint256 protocolVersion = getUintValue(Encoder.getKey("latestVersion"));
576+
address newSecurityTokenAddress =
577+
_deployToken(_name, ticker, _tokenDetails, stOwner, _divisible, _treasuryWallet, protocolVersion, 0, 0);
578+
emit SecurityTokenRefreshed(
579+
_ticker, _name, newSecurityTokenAddress, stOwner, now, stOwner, protocolVersion
580+
);
529581
}
530582

531583
function _deployToken(
@@ -535,12 +587,14 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
535587
address _issuer,
536588
bool _divisible,
537589
address _wallet,
538-
uint256 _protocolVersion
590+
uint256 _protocolVersion,
591+
uint256 _usdFee,
592+
uint256 _polyFee
539593
)
540594
internal
595+
returns(address newSecurityTokenAddress)
541596
{
542-
(uint256 _usdFee, uint256 _polyFee) = _takeFee(STLAUNCHFEE);
543-
address newSecurityTokenAddress = ISTFactory(getAddressValue(Encoder.getKey("protocolVersionST", _protocolVersion))).deployToken(
597+
newSecurityTokenAddress = ISTFactory(getAddressValue(Encoder.getKey("protocolVersionST", _protocolVersion))).deployToken(
544598
_name,
545599
_ticker,
546600
18,
@@ -554,10 +608,6 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
554608
/*solium-disable-next-line security/no-block-members*/
555609
_storeSecurityTokenData(newSecurityTokenAddress, _ticker, _tokenDetails, now);
556610
set(Encoder.getKey("tickerToSecurityToken", _ticker), newSecurityTokenAddress);
557-
/*solium-disable-next-line security/no-block-members*/
558-
emit NewSecurityToken(
559-
_ticker, _name, newSecurityTokenAddress, msg.sender, now, msg.sender, false, _usdFee, _polyFee, _protocolVersion
560-
);
561611
}
562612

563613
/**
@@ -570,14 +620,14 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
570620
* @param _deployedAt is the timestamp at which the security token is deployed
571621
*/
572622
function modifySecurityToken(
573-
string calldata _name,
574-
string calldata _ticker,
623+
string memory _name,
624+
string memory _ticker,
575625
address _owner,
576626
address _securityToken,
577-
string calldata _tokenDetails,
627+
string memory _tokenDetails,
578628
uint256 _deployedAt
579629
)
580-
external
630+
public
581631
onlyOwner
582632
{
583633
require(bytes(_name).length > 0 && bytes(_ticker).length > 0, "Bad data");
@@ -631,7 +681,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
631681
* @dev Allows the current owner to transfer control of the contract to a newOwner.
632682
* @param _newOwner The address to transfer ownership to.
633683
*/
634-
function transferOwnership(address _newOwner) external onlyOwner {
684+
function transferOwnership(address _newOwner) public onlyOwner {
635685
require(_newOwner != address(0), "Bad address");
636686
emit OwnershipTransferred(getAddressValue(OWNER), _newOwner);
637687
set(OWNER, _newOwner);
@@ -659,7 +709,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
659709
* @notice Sets the ticker registration fee in USD tokens. Only Polymath.
660710
* @param _tickerRegFee is the registration fee in USD tokens (base 18 decimals)
661711
*/
662-
function changeTickerRegistrationFee(uint256 _tickerRegFee) external onlyOwner {
712+
function changeTickerRegistrationFee(uint256 _tickerRegFee) public onlyOwner {
663713
uint256 fee = getUintValue(TICKERREGFEE);
664714
require(fee != _tickerRegFee, "Bad fee");
665715
_changeTickerRegistrationFee(fee, _tickerRegFee);
@@ -674,7 +724,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
674724
* @notice Sets the ticker registration fee in USD tokens. Only Polymath.
675725
* @param _stLaunchFee is the registration fee in USD tokens (base 18 decimals)
676726
*/
677-
function changeSecurityLaunchFee(uint256 _stLaunchFee) external onlyOwner {
727+
function changeSecurityLaunchFee(uint256 _stLaunchFee) public onlyOwner {
678728
uint256 fee = getUintValue(STLAUNCHFEE);
679729
require(fee != _stLaunchFee, "Bad fee");
680730
_changeSecurityLaunchFee(fee, _stLaunchFee);
@@ -691,7 +741,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
691741
* @param _stLaunchFee is the st generation fee (base 18 decimals)
692742
* @param _isFeeInPoly defines if the fee is in poly or usd
693743
*/
694-
function changeFeesAmountAndCurrency(uint256 _tickerRegFee, uint256 _stLaunchFee, bool _isFeeInPoly) external onlyOwner {
744+
function changeFeesAmountAndCurrency(uint256 _tickerRegFee, uint256 _stLaunchFee, bool _isFeeInPoly) public onlyOwner {
695745
uint256 tickerFee = getUintValue(TICKERREGFEE);
696746
uint256 stFee = getUintValue(STLAUNCHFEE);
697747
bool isOldFeesInPoly = getBoolValue(IS_FEE_IN_POLY);
@@ -706,7 +756,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
706756
* @notice Reclaims all ERC20Basic compatible tokens
707757
* @param _tokenContract is the address of the token contract
708758
*/
709-
function reclaimERC20(address _tokenContract) external onlyOwner {
759+
function reclaimERC20(address _tokenContract) public onlyOwner {
710760
require(_tokenContract != address(0), "Bad address");
711761
IERC20 token = IERC20(_tokenContract);
712762
uint256 balance = token.balanceOf(address(this));
@@ -722,7 +772,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
722772
* @param _minor Minor version of the proxy.
723773
* @param _patch Patch version of the proxy
724774
*/
725-
function setProtocolFactory(address _STFactoryAddress, uint8 _major, uint8 _minor, uint8 _patch) external onlyOwner {
775+
function setProtocolFactory(address _STFactoryAddress, uint8 _major, uint8 _minor, uint8 _patch) public onlyOwner {
726776
_setProtocolFactory(_STFactoryAddress, _major, _minor, _patch);
727777
}
728778

@@ -740,7 +790,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
740790
* @param _minor Minor version of the proxy.
741791
* @param _patch Patch version of the proxy
742792
*/
743-
function removeProtocolFactory(uint8 _major, uint8 _minor, uint8 _patch) external onlyOwner {
793+
function removeProtocolFactory(uint8 _major, uint8 _minor, uint8 _patch) public onlyOwner {
744794
uint24 _packedVersion = VersionUtils.pack(_major, _minor, _patch);
745795
require(getUintValue(Encoder.getKey("latestVersion")) != _packedVersion, "Cannot remove latestVersion");
746796
emit ProtocolFactoryRemoved(getAddressValue(Encoder.getKey("protocolVersionST", _packedVersion)), _major, _minor, _patch);
@@ -755,7 +805,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
755805
* @param _minor Minor version of the proxy.
756806
* @param _patch Patch version of the proxy
757807
*/
758-
function setLatestVersion(uint8 _major, uint8 _minor, uint8 _patch) external onlyOwner {
808+
function setLatestVersion(uint8 _major, uint8 _minor, uint8 _patch) public onlyOwner {
759809
_setLatestVersion(_major, _minor, _patch);
760810
}
761811

@@ -770,7 +820,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
770820
* @notice Changes the PolyToken address. Only Polymath.
771821
* @param _newAddress is the address of the polytoken.
772822
*/
773-
function updatePolyTokenAddress(address _newAddress) external onlyOwner {
823+
function updatePolyTokenAddress(address _newAddress) public onlyOwner {
774824
require(_newAddress != address(0), "Bad address");
775825
set(POLYTOKEN, _newAddress);
776826
}

contracts/interfaces/ISecurityToken.sol

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,4 +463,9 @@ interface ISecurityToken {
463463
* @return bool `true` signifies the minting is allowed. While `false` denotes the end of minting
464464
*/
465465
function isIssuable() external view returns (bool);
466+
467+
/**
468+
* @notice Returns if transfers are currently frozen or not
469+
*/
470+
function transfersFrozen() external view returns (bool);
466471
}

test/n_security_token_registry.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,66 @@ contract("SecurityTokenRegistry", async (accounts) => {
11221122
});
11231123
});
11241124

1125+
describe("Test case for refreshing a security token", async () => {
1126+
it("Should fail if msg.sender is not ST owner", async () => {
1127+
await catchRevert(
1128+
I_STRProxied.refreshSecurityToken("refreshedToken", symbol, "refreshedToken", true, token_owner, {
1129+
from: account_delegate
1130+
})
1131+
);
1132+
});
1133+
1134+
it("Should fail if ticker is not deployed", async () => {
1135+
await catchRevert(
1136+
I_STRProxied.refreshSecurityToken("refreshedToken", "LOGLOG3", "refreshedToken", true, token_owner, {
1137+
from: token_owner
1138+
})
1139+
);
1140+
});
1141+
1142+
it("Should fail if name is 0 length", async () => {
1143+
await catchRevert(
1144+
I_STRProxied.refreshSecurityToken("", symbol, "refreshedToken", true, token_owner, {
1145+
from: token_owner
1146+
})
1147+
);
1148+
});
1149+
1150+
it("Should fail if null treasurey wallet", async () => {
1151+
await catchRevert(
1152+
I_STRProxied.refreshSecurityToken("refreshedToken", symbol, "refreshedToken", true, address_zero, {
1153+
from: token_owner
1154+
})
1155+
);
1156+
});
1157+
1158+
it("Should fail if transfers not frozen", async () => {
1159+
await catchRevert(
1160+
I_STRProxied.refreshSecurityToken("refreshedToken", symbol, "refreshedToken", true, token_owner, {
1161+
from: token_owner
1162+
})
1163+
);
1164+
});
1165+
1166+
it("Should refresh security token", async () => {
1167+
let snapid = await takeSnapshot();
1168+
let oldStAddress = await I_Getter.getSecurityTokenAddress(symbol);
1169+
let oldSt = await SecurityToken.at(oldStAddress);
1170+
await oldSt.freezeTransfers({ from:token_owner });
1171+
let tx = await I_STRProxied.refreshSecurityToken("refreshedToken", symbol, "refreshedToken", true, token_owner, {
1172+
from: token_owner
1173+
});
1174+
assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken not deployed");
1175+
let newStAddress = await I_Getter.getSecurityTokenAddress(symbol);
1176+
let securityTokenTmp = tx.logs[1].args._securityTokenAddress;
1177+
assert.equal(newStAddress, securityTokenTmp, "ST address not updated");
1178+
let newST = await SecurityToken.at(newStAddress);
1179+
assert.notEqual(oldStAddress, newStAddress, "new ST not generated");
1180+
assert.equal(await newST.name(), "refreshedToken", "ST not deployed properly");
1181+
await revertToSnapshot(snapid);
1182+
});
1183+
});
1184+
11251185
describe("Test cases for getters", async () => {
11261186
it("Should get the security token address", async () => {
11271187
let address = await I_Getter.getSecurityTokenAddress.call(symbol);

0 commit comments

Comments
 (0)