Skip to content

Commit

Permalink
Merge pull request #568 from PolymathNetwork/add-protocolVersion
Browse files Browse the repository at this point in the history
Allows explicit token factory version in generateSecurityToken()
  • Loading branch information
satyamakgec authored Mar 4, 2019
2 parents 0b9e51d + c56fb1d commit 92807d1
Show file tree
Hide file tree
Showing 36 changed files with 249 additions and 79 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ All notable changes to this project will be documented in this file.
* Replaced `updatePolyTokenAddress()` function with `updateFromRegistry()` in `SecurityTokenRegistry`.
* Migrate all the getters of `SecurityTokenRegsitry.sol` to `STRGetter.sol` contract.
* Removed `_polyToken` parameter from `initialize` function in `SecurityTokenRegistry`.
* Allows an explicit token factory version to be used during creation of securityToken.
* Rename the `getProtocolVersion()` to `getLatestProtocolVersion()`.
* Return SecurityToken version in the `getSecurityTokenData()` function.

## GeneralTransferManager
Expand Down
2 changes: 1 addition & 1 deletion contracts/STRGetter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ contract STRGetter is EternalStorage {
/**
* @notice Gets Protocol version
*/
function getProtocolVersion() public view returns(uint8[] memory) {
function getLatestProtocolVersion() public view returns(uint8[] memory) {
return VersionUtils.unpack(uint24(getUintValue(Encoder.getKey("latestVersion"))));
}

Expand Down
48 changes: 37 additions & 11 deletions contracts/SecurityTokenRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
address _registrant,
bool _fromAdmin,
uint256 _usdFee,
uint256 _polyFee
uint256 _polyFee,
uint256 _protocolVersion
);
// Emit after ticker registration
event RegisterTicker(
Expand Down Expand Up @@ -487,41 +488,64 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
* @param _ticker is the ticker symbol of the security token
* @param _tokenDetails is the off-chain details of the token
* @param _divisible is whether or not the token is divisible
* @param _protocolVersion Version of securityToken contract
* - `_protocolVersion` is the packed value of uin8[3] array (it will be calculated offchain)
* - if _protocolVersion == 0 then latest version of securityToken will be generated
*/
function generateSecurityToken(
string calldata _name,
string calldata _ticker,
string calldata _tokenDetails,
bool _divisible
bool _divisible,
uint256 _protocolVersion
)
external
whenNotPausedOrOwner
{
uint256 protocolVersion = _protocolVersion;
require(bytes(_name).length > 0 && bytes(_ticker).length > 0, "Bad ticker");
if (_protocolVersion == 0) {
protocolVersion = getUintValue(Encoder.getKey("latestVersion"));
}
require(protocolVersion != uint256(0), "Invalid version");
string memory ticker = Util.upper(_ticker);
bytes32 statusKey = Encoder.getKey("registeredTickers_status", ticker);
require(!getBoolValue(statusKey), "Already deployed");
set(statusKey, true);
require(_tickerOwner(ticker) == msg.sender, "Not authorised");
/*solium-disable-next-line security/no-block-members*/
require(getUintValue(Encoder.getKey("registeredTickers_expiryDate", ticker)) >= now, "Ticker expired");
(uint256 _usdFee, uint256 _polyFee) = _takeFee(STLAUNCHFEE);
_deployToken(_name, ticker, _tokenDetails, msg.sender, _divisible, protocolVersion);
}

address newSecurityTokenAddress = ISTFactory(getAddressValue(Encoder.getKey("protocolVersionST", getUintValue(Encoder.getKey("latestVersion"))))).deployToken(
function _deployToken(
string memory _name,
string memory _ticker,
string memory _tokenDetails,
address _issuer,
bool _divisible,
uint256 _protocolVersion
)
internal
{
(uint256 _usdFee, uint256 _polyFee) = _takeFee(STLAUNCHFEE);
address newSecurityTokenAddress = ISTFactory(getAddressValue(Encoder.getKey("protocolVersionST", _protocolVersion))).deployToken(
_name,
ticker,
_ticker,
18,
_tokenDetails,
msg.sender,
_issuer,
_divisible,
getAddressValue(POLYMATHREGISTRY)
);

/*solium-disable-next-line security/no-block-members*/
_storeSecurityTokenData(newSecurityTokenAddress, ticker, _tokenDetails, now);
set(Encoder.getKey("tickerToSecurityToken", ticker), newSecurityTokenAddress);
_storeSecurityTokenData(newSecurityTokenAddress, _ticker, _tokenDetails, now);
set(Encoder.getKey("tickerToSecurityToken", _ticker), newSecurityTokenAddress);
/*solium-disable-next-line security/no-block-members*/
emit NewSecurityToken(ticker, _name, newSecurityTokenAddress, msg.sender, now, msg.sender, false, _usdFee, _polyFee);
emit NewSecurityToken(
_ticker, _name, newSecurityTokenAddress, msg.sender, now, msg.sender, false, _usdFee, _polyFee, _protocolVersion
);
}

/**
Expand All @@ -543,7 +567,7 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
)
external
onlyOwner
{
{
require(bytes(_name).length > 0 && bytes(_ticker).length > 0, "Bad data");
require(bytes(_ticker).length <= 10, "Bad ticker");
require(_deployedAt != 0 && _owner != address(0), "Bad data");
Expand All @@ -559,7 +583,9 @@ contract SecurityTokenRegistry is EternalStorage, Proxy {
set(Encoder.getKey("tickerToSecurityToken", ticker), _securityToken);
_modifyTicker(_owner, ticker, _name, registrationTime, expiryTime, true);
_storeSecurityTokenData(_securityToken, ticker, _tokenDetails, _deployedAt);
emit NewSecurityToken(ticker, _name, _securityToken, _owner, _deployedAt, msg.sender, true, uint256(0), uint256(0));
emit NewSecurityToken(
ticker, _name, _securityToken, _owner, _deployedAt, msg.sender, true, uint256(0), uint256(0), 0
);
}

/**
Expand Down
23 changes: 16 additions & 7 deletions contracts/interfaces/ISecurityTokenRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,22 @@ pragma solidity ^0.5.0;
*/
interface ISecurityTokenRegistry {
/**
* @notice Creates a new Security Token and saves it to the registry
* @param _name Name of the token
* @param _ticker Ticker ticker of the security token
* @param _tokenDetails Off-chain details of the token
* @param _divisible Whether the token is divisible or not
*/
function generateSecurityToken(string calldata _name, string calldata _ticker, string calldata _tokenDetails, bool _divisible) external;
* @notice Deploys an instance of a new Security Token and records it to the registry
* @param _name is the name of the token
* @param _ticker is the ticker symbol of the security token
* @param _tokenDetails is the off-chain details of the token
* @param _divisible is whether or not the token is divisible
* @param _protocolVersion Version of securityToken contract
* - `_protocolVersion` is the packed value of uin8[3] array (it will be calculated offchain)
* - if _protocolVersion == 0 then latest version of securityToken will be generated
*/
function generateSecurityToken(
string calldata _name,
string calldata _ticker,
string calldata _tokenDetails,
bool _divisible,
uint256 _protocolVersion
) external;

/**
* @notice Adds a new custom Security Token and saves it to the registry. (Token should follow the ISecurityToken interface)
Expand Down
52 changes: 52 additions & 0 deletions contracts/mocks/STFactoryMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
pragma solidity ^0.5.0;

import "./SecurityTokenMock.sol";
import "../interfaces/ISTFactory.sol";
import "../datastore/DataStoreFactory.sol";

/**
* @title Proxy for deploying SecurityToken instances
*/
contract STFactoryMock is ISTFactory {
address public transferManagerFactory;
address public stDelegate;
DataStoreFactory public dataStoreFactory;

constructor(address _transferManagerFactory, address _dataStoreFactory, address _stDelegate) public {
transferManagerFactory = _transferManagerFactory;
dataStoreFactory = DataStoreFactory(_dataStoreFactory);
stDelegate = _stDelegate;
}

/**
* @notice deploys the token and adds default modules like the GeneralTransferManager.
* Future versions of the proxy can attach different modules or pass different parameters.
*/
function deployToken(
string calldata _name,
string calldata _symbol,
uint8 _decimals,
string calldata _tokenDetails,
address _issuer,
bool _divisible,
address _polymathRegistry
)
external
returns(address)
{
SecurityTokenMock newSecurityToken = new SecurityTokenMock(
_name,
_symbol,
_decimals,
_divisible ? 1 : uint256(10) ** _decimals,
_tokenDetails,
_polymathRegistry,
stDelegate
);
//NB When dataStore is generated, the security token address is automatically set via the constructor in DataStoreProxy.
newSecurityToken.changeDataStore(dataStoreFactory.generateDataStore(address(newSecurityToken)));
newSecurityToken.addModule(transferManagerFactory, "", 0, 0);
newSecurityToken.transferOwnership(_issuer);
return address(newSecurityToken);
}
}
43 changes: 43 additions & 0 deletions contracts/mocks/SecurityTokenMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
pragma solidity ^0.5.0;

import "../tokens/SecurityToken.sol";

/**
* @title Security Token contract
* @notice SecurityToken is an ERC1400 token with added capabilities:
* @notice - Implements the ERC1400 Interface
* @notice - Transfers are restricted
* @notice - Modules can be attached to it to control its behaviour
* @notice - ST should not be deployed directly, but rather the SecurityTokenRegistry should be used
* @notice - ST does not inherit from ISecurityToken due to:
* @notice - https://github.com/ethereum/solidity/issues/4847
*/
contract SecurityTokenMock is SecurityToken {

/**
* @notice constructor
* @param _name Name of the SecurityToken
* @param _symbol Symbol of the Token
* @param _decimals Decimals for the securityToken
* @param _granularity granular level of the token
* @param _tokenDetails Details of the token that are stored off-chain
* @param _polymathRegistry Contract address of the polymath registry
* @param _delegate Contract address of the delegate
*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals,
uint256 _granularity,
string memory _tokenDetails,
address _polymathRegistry,
address _delegate
)
public
SecurityToken(_name, _symbol, _decimals, _granularity, _tokenDetails, _polymathRegistry,_delegate)
{
securityTokenVersion = SemanticVersion(2, 2, 0);
}


}
3 changes: 1 addition & 2 deletions contracts/tokens/SecurityToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import "../interfaces/ITransferManager.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol";

/**
Expand Down Expand Up @@ -144,7 +143,7 @@ contract SecurityToken is ERC20, ERC20Detailed, Ownable, ReentrancyGuard, Securi
delegate = _delegate;
tokenDetails = _tokenDetails;
granularity = _granularity;
securityTokenVersion = SemanticVersion(2, 0, 0);
securityTokenVersion = SemanticVersion(3, 0, 0);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions test/b_capped_sto.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ contract("CappedSTO", async (accounts) => {
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 });
let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, 0, { from: token_owner });

// Verify the successful generation of the security token
assert.equal(tx.logs[2].args._ticker, symbol, "SecurityToken doesn't get deployed");
Expand Down Expand Up @@ -622,7 +622,7 @@ contract("CappedSTO", async (accounts) => {
it("POLY: 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(P_name, P_symbol, P_tokenDetails, false, { from: token_owner });
let tx = await I_STRProxied.generateSecurityToken(P_name, P_symbol, P_tokenDetails, false, 0, { from: token_owner });

// Verify the successful generation of the security token
assert.equal(tx.logs[2].args._ticker, P_symbol, "SecurityToken doesn't get deployed");
Expand Down
2 changes: 1 addition & 1 deletion test/c_checkpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ contract("Checkpoints", async function(accounts) {

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 });
let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, 0, { 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");
Expand Down
4 changes: 2 additions & 2 deletions test/d_count_transfer_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ contract("CountTransferManager", async (accounts) => {
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 });
let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, 0, { 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");

Expand Down Expand Up @@ -363,7 +363,7 @@ contract("CountTransferManager", async (accounts) => {

await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner });

let tx2 = await I_STRProxied.generateSecurityToken(name, symbol2, tokenDetails, false, { from: token_owner });
let tx2 = await I_STRProxied.generateSecurityToken(name, symbol2, tokenDetails, false, 0, { from: token_owner });

I_SecurityToken2 = await SecurityToken.at(tx2.logs[2].args._securityTokenAddress);
stGetter2 = await STGetter.at(I_SecurityToken2.address);
Expand Down
2 changes: 1 addition & 1 deletion test/e_erc20_dividends.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ contract("ERC20DividendCheckpoint", async (accounts) => {
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 });
let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, 0, { 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");
Expand Down
2 changes: 1 addition & 1 deletion test/f_ether_dividends.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ contract("EtherDividendCheckpoint", async (accounts) => {
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 });
let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, 0, { 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");
Expand Down
4 changes: 2 additions & 2 deletions test/g_general_permission_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ contract("GeneralPermissionManager", async (accounts) => {
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 });
let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, 0, { 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");
Expand Down Expand Up @@ -329,7 +329,7 @@ contract("GeneralPermissionManager", async (accounts) => {

await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner });
let _blockNo = latestBlock();
let tx2 = await I_STRProxied.generateSecurityToken(name, "DEL", tokenDetails, false, { from: token_owner });
let tx2 = await I_STRProxied.generateSecurityToken(name, "DEL", tokenDetails, false, 0, { from: token_owner });

// Verify the successful generation of the security token
assert.equal(tx2.logs[2].args._ticker, "DEL", "SecurityToken doesn't get deployed");
Expand Down
2 changes: 1 addition & 1 deletion test/h_general_transfer_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ contract("GeneralTransferManager", async (accounts) => {
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 });
let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, 0, { 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");

Expand Down
2 changes: 1 addition & 1 deletion test/i_Issuance.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ contract("Issuance", async (accounts) => {
it("POLYMATH: Should generate the new security token with the same symbol as registered above", async () => {
await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: account_polymath });

let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: account_polymath });
let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, 0, { from: account_polymath });

// Verify the successful generation of the security token
assert.equal(tx.logs[2].args._ticker, symbol, "SecurityToken doesn't get deployed");
Expand Down
2 changes: 1 addition & 1 deletion test/j_manual_approval_transfer_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ contract("ManualApprovalTransferManager", accounts => {
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 });
let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, 0, { 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");
Expand Down
Loading

0 comments on commit 92807d1

Please sign in to comment.