Skip to content

Commit

Permalink
Merge pull request #90 from PolymathNetwork/add_granularity
Browse files Browse the repository at this point in the history
Add granularity to Security Tokens
  • Loading branch information
adamdossa authored May 2, 2018
2 parents 297b0d6 + d690113 commit 45993cb
Show file tree
Hide file tree
Showing 16 changed files with 95 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,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 @@ -235,7 +235,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 @@ -238,7 +238,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 @@ -391,6 +391,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 @@ -451,6 +467,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 @@ -544,7 +576,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 @@ -236,7 +236,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 @@ -229,7 +229,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 @@ -240,7 +240,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 @@ -455,6 +455,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

0 comments on commit 45993cb

Please sign in to comment.