Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add granularity to Security Tokens #90

Merged
merged 3 commits into from
May 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file.

## Added

* generateSecurityToken in SecurityTokenRegistry takes an additional parameter specifying whether the token is divisible.
* IModule contract takes the polyToken contract address as the constructor argument to wrapping all the factories with the polyToken contract address.
* `takeFee()` new function introduced to extract the POLY token from the factory. It only be called by the owner of the factory.
* Added ability for issuer to provide a signed piece of data to allow investors to whitelist themselves.
Expand Down
5 changes: 3 additions & 2 deletions contracts/SecurityTokenRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ contract SecurityTokenRegistry is Ownable, ISecurityTokenRegistry, Util {
* @param _decimals Decimals value for token
* @param _tokenDetails off-chain details of the token
*/
function generateSecurityToken(string _name, string _symbol, uint8 _decimals, bytes32 _tokenDetails) public {
function generateSecurityToken(string _name, string _symbol, uint8 _decimals, bytes32 _tokenDetails, bool _divisible) public {
require(bytes(_name).length > 0 && bytes(_symbol).length > 0, "Name and Symbol string length should be greater than 0");
require(ITickerRegistry(tickerRegistry).checkValidity(_symbol, msg.sender, _name), "Trying to use non-valid symbol");
string memory symbol = upper(_symbol);
Expand All @@ -48,7 +48,8 @@ contract SecurityTokenRegistry is Ownable, ISecurityTokenRegistry, Util {
symbol,
_decimals,
_tokenDetails,
msg.sender
msg.sender,
_divisible
);

securityTokens[newSecurityTokenAddress] = SecurityTokenData(symbol, _tokenDetails);
Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/ISTProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ pragma solidity ^0.4.23;

contract ISTProxy {

function deployToken(string _name, string _symbol, uint8 _decimals, bytes32 _tokenDetails, address _issuer)
function deployToken(string _name, string _symbol, uint8 _decimals, bytes32 _tokenDetails, address _issuer, bool _divisible)
public returns (address);
}
1 change: 1 addition & 0 deletions contracts/interfaces/ISecurityToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ contract ISecurityToken is IST20, Ownable {
uint8 public constant PERMISSIONMANAGER_KEY = 1;
uint8 public constant TRANSFERMANAGER_KEY = 2;
uint8 public constant STO_KEY = 3;
uint256 public granularity;

//TODO: Factor out more stuff here
function checkPermission(address _delegate, address _module, bytes32 _perm) public view returns(bool);
Expand Down
4 changes: 2 additions & 2 deletions contracts/interfaces/ISecurityTokenRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ contract ISecurityTokenRegistry {

/**
* @dev Creates a new Security Token and saves it to the registry
* @param _name Name of the token
* @param _name Name of the token
* @param _symbol Ticker symbol of the security token
* @param _decimals Decimals value for token
* @param _tokenDetails off-chain details of the token
*/
function generateSecurityToken(string _name, string _symbol, uint8 _decimals, bytes32 _tokenDetails) public;
function generateSecurityToken(string _name, string _symbol, uint8 _decimals, bytes32 _tokenDetails, bool _divisible) public;

function setProtocolVersion(address _stVersionProxyAddress, bytes32 _version) public;

Expand Down
3 changes: 2 additions & 1 deletion contracts/tokens/STVersionProxy001.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ contract STVersionProxy001 is ISTProxy {
* @dev deploys the token and adds default modules like permission manager and transfer manager.
* Future versions of the proxy can attach different modules or pass some other paramters.
*/
function deployToken(string _name, string _symbol, uint8 _decimals, bytes32 _tokenDetails, address _issuer)
function deployToken(string _name, string _symbol, uint8 _decimals, bytes32 _tokenDetails, address _issuer, bool _divisible)
public returns (address) {
address newSecurityTokenAddress = new SecurityToken(
_name,
_symbol,
_decimals,
_divisible ? 1 : uint256(10)**_decimals,
_tokenDetails,
msg.sender
);
Expand Down
3 changes: 2 additions & 1 deletion contracts/tokens/STVersionProxy002.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ contract STVersionProxy002 is ISTProxy {
transferManagerFactory = _transferManagerFactory;
}

function deployToken(string _name, string _symbol, uint8 _decimals, bytes32 _tokenDetails, address _issuer)
function deployToken(string _name, string _symbol, uint8 _decimals, bytes32 _tokenDetails, address _issuer, bool _divisible)
public returns (address)
{
address newSecurityTokenAddress = new SecurityTokenV2(
_name,
_symbol,
_decimals,
_divisible ? 1 : uint256(10)**_decimals,
_tokenDetails,
msg.sender
);
Expand Down
21 changes: 19 additions & 2 deletions contracts/tokens/SecurityToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 {
uint256 _timestamp
);

event LogGranularityChanged(uint256 _oldGranularity, uint256 _newGranularity);
event LogModuleRemoved(uint8 indexed _type, address _module, uint256 _timestamp);
event LogModuleBudgetChanged(uint8 indexed _moduleType, address _module, uint256 _budget);
event Mint(address indexed to, uint256 amount);
Expand All @@ -71,10 +72,16 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 {
_;
}

modifier checkGranularity(uint256 _amount) {
require(_amount.div(granularity).mul(granularity) == _amount, "Unable to modify token balances at this granularity");
_;
}

constructor (
string _name,
string _symbol,
uint8 _decimals,
uint256 _granularity,
bytes32 _tokenDetails,
address _securityTokenRegistry
)
Expand All @@ -85,6 +92,7 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 {
moduleRegistry = ISecurityTokenRegistry(_securityTokenRegistry).moduleRegistry();
polyToken = ERC20(ISecurityTokenRegistry(_securityTokenRegistry).polyAddress());
tokenDetails = _tokenDetails;
granularity = _granularity;
}

function addModule(
Expand Down Expand Up @@ -206,6 +214,15 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 {
emit LogModuleBudgetChanged(_moduleType, modules[_moduleType][_moduleIndex].moduleAddress, _budget);
}

/**
* @dev allows owner to change token granularity
*/
function changeGranularity(uint256 _granularity) public onlyOwner {
require(_granularity != 0, "Granularity can not be 0");
emit LogGranularityChanged(granularity, _granularity);
granularity = _granularity;
}

/**
* @dev Overloaded version of the transfer function
*/
Expand All @@ -224,7 +241,7 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 {

// Permissions this to a TransferManager module, which has a key of 2
// If no TransferManager return true
function verifyTransfer(address _from, address _to, uint256 _amount) public view returns (bool success) {
function verifyTransfer(address _from, address _to, uint256 _amount) public view checkGranularity(_amount) returns (bool success) {
if (modules[TRANSFERMANAGER_KEY].length == 0) {
return true;
}
Expand All @@ -240,7 +257,7 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 {
* @dev 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)
*/
function mint(address _investor, uint256 _amount) public onlyModule(STO_KEY, true) returns (bool success) {
function mint(address _investor, uint256 _amount) public onlyModule(STO_KEY, true) checkGranularity(_amount) returns (bool success) {
require(verifyTransfer(address(0), _investor, _amount), "Transfer is not valid");
totalSupply_ = totalSupply_.add(_amount);
balances[_investor] = balances[_investor].add(_amount);
Expand Down
2 changes: 2 additions & 0 deletions contracts/tokens/SecurityTokenV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ contract SecurityTokenV2 is SecurityToken {
string _name,
string _symbol,
uint8 _decimals,
uint256 _granularity,
bytes32 _tokenDetails,
address _securityTokenRegistry
)
Expand All @@ -22,6 +23,7 @@ contract SecurityTokenV2 is SecurityToken {
_name,
_symbol,
_decimals,
_granularity,
_tokenDetails,
_securityTokenRegistry)
{
Expand Down
2 changes: 1 addition & 1 deletion test/Issuance.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ contract('Issuance', accounts => {
});

it("POLYMATH: Should generate the new security token with the same symbol as registered above", async () => {
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, { from: account_polymath });
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, false, { from: account_polymath });

// Verify the successful generation of the security token
assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed");
Expand Down
36 changes: 34 additions & 2 deletions test/capped_sto.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ contract('CappedSTO', accounts => {
});

it("Should generate the new security token with the same symbol as registered above", async () => {
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, { from: token_owner });
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, false, { from: token_owner });

// Verify the successful generation of the security token
assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed");
Expand Down Expand Up @@ -389,6 +389,22 @@ contract('CappedSTO', accounts => {
assert.ok(errorThrown, message);
});

it("Should buy the tokens -- Failed due to wrong granularity", async () => {
let errorThrown = false;
try {
await web3.eth.sendTransaction({
from: account_investor1,
to: I_CappedSTO.address,
value: web3.utils.toWei('0.1111', 'ether')
});
} catch(error) {
console.log(`Failed due to wrong purchase granularity`);
ensureException(error);
errorThrown = true;
}
assert.ok(errorThrown, message);
});

it("Should Buy the tokens", async() => {
balanceOfReceiver = await web3.eth.getBalance(account_fundsReceiver);
// Add the Investor in to the whitelist
Expand Down Expand Up @@ -448,6 +464,22 @@ contract('CappedSTO', accounts => {
TokenPurchase.stopWatching();
});

it("Should buy the tokens -- Failed due to wrong granularity", async () => {
let errorThrown = false;
try {
await web3.eth.sendTransaction({
from: account_investor1,
to: I_CappedSTO.address,
value: web3.utils.toWei('0.1111', 'ether')
});
} catch(error) {
console.log(`Failed due to wrong purchase granularity`);
ensureException(error);
errorThrown = true;
}
assert.ok(errorThrown, message);
});

it("Should restrict to buy tokens after hiting the cap in second tx first tx pass", async() => {
let tx = await I_GeneralTransferManager.modifyWhitelist(
account_investor2,
Expand Down Expand Up @@ -540,7 +572,7 @@ contract('CappedSTO', accounts => {
});

it("POLY: Should generate the new security token with the same symbol as registered above", async () => {
let tx = await I_SecurityTokenRegistry.generateSecurityToken(P_name, P_symbol, P_decimals, P_tokenDetails, { from: token_owner });
let tx = await I_SecurityTokenRegistry.generateSecurityToken(P_name, P_symbol, P_decimals, P_tokenDetails, false, { from: token_owner });

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

it("Should generate the new security token with the same symbol as registered above", async () => {
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, { from: token_owner });
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, false, { from: token_owner });

// Verify the successful generation of the security token
assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed");
Expand Down
2 changes: 1 addition & 1 deletion test/general_transfer_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ contract('GeneralTransferManager', accounts => {
});

it("Should generate the new security token with the same symbol as registered above", async () => {
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, { from: token_owner });
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, false, { from: token_owner });

// Verify the successful generation of the security token
assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed");
Expand Down
2 changes: 1 addition & 1 deletion test/module_registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ contract('ModuleRegistry', accounts => {
});

it("Should generate the new security token with the same symbol as registered above", async () => {
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, { from: token_owner });
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, false, { from: token_owner });

// Verify the successful generation of the security token
assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed");
Expand Down
22 changes: 21 additions & 1 deletion test/security_token.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ contract('SecurityToken', accounts => {
});

it("Should generate the new security token with the same symbol as registered above", async () => {
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, { from: token_owner });
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, false, { from: token_owner });

// Verify the successful generation of the security token
assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed");
Expand Down Expand Up @@ -453,6 +453,26 @@ contract('SecurityToken', accounts => {
assert.isTrue(tx.logs[0].args._allowAllTransfers, "AllowTransfer variable is not successfully updated");
});


it("Should fail to send tokens with the wrong granularity", async() => {
let errorThrown = false;
try {
await I_SecurityToken.transfer(accounts[7], Math.pow(10, 17), { from : account_investor1});
} catch (error) {
console.log('Failed due to incorrect token granularity - expected');
errorThrown = true;
ensureException(error);
}
assert.ok(errorThrown, message);
});

it("Should adjust granularity", async() => {
let errorThrown = false;
await I_SecurityToken.changeGranularity(Math.pow(10, 17), {from: token_owner });
await I_SecurityToken.transfer(accounts[7], Math.pow(10, 17), { from : account_investor1});
await I_SecurityToken.transfer(account_investor1, Math.pow(10, 17), { from : accounts[7]});
});

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], (10 * Math.pow(10, 18)), { from : account_investor1});

Expand Down
6 changes: 3 additions & 3 deletions test/security_token_registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ contract('SecurityTokenRegistry', accounts => {
});

it("Should generate the new security token with the same symbol as registered above", async () => {
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, { from: token_owner });
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name, symbol, decimals, tokenDetails, false, { from: token_owner });

// Verify the successful generation of the security token
assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed");
Expand Down Expand Up @@ -281,7 +281,7 @@ contract('SecurityTokenRegistry', accounts => {
});

it("Should generate the new security token with version 2", async() => {
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name2, symbol2, decimals, tokenDetails, { from: token_owner });
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name2, symbol2, decimals, tokenDetails, false, { from: token_owner });

// Verify the successful generation of the security token
assert.equal(tx.logs[1].args._ticker, symbol2, "SecurityToken doesn't get deployed");
Expand Down Expand Up @@ -329,7 +329,7 @@ contract('SecurityTokenRegistry', accounts => {
});

it("Should generate the new security token with version 3", async() => {
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name2, "DET3", decimals, tokenDetails, { from: token_owner });
let tx = await I_SecurityTokenRegistry.generateSecurityToken(name2, "DET3", decimals, tokenDetails, false, { from: token_owner });

// Verify the successful generation of the security token
assert.equal(tx.logs[1].args._ticker, "DET3", "SecurityToken doesn't get deployed");
Expand Down