From e75b255c4be5d2fb9a0c9ca117499d05aa29d641 Mon Sep 17 00:00:00 2001 From: satyam Date: Tue, 8 May 2018 18:39:50 +0530 Subject: [PATCH 1/3] burn securities --- contracts/interfaces/IST20.sol | 3 +++ contracts/tokens/SecurityToken.sol | 12 ++++++++++++ test/security_token.js | 20 ++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/contracts/interfaces/IST20.sol b/contracts/interfaces/IST20.sol index de367d14d..542256bba 100644 --- a/contracts/interfaces/IST20.sol +++ b/contracts/interfaces/IST20.sol @@ -11,4 +11,7 @@ contract IST20 { //used to create tokens function mint(address _investor, uint256 _amount) public returns (bool success); + + //burn the tokens + function burn(uint256 _value) public; } diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index 15ddb1d99..5def2408f 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -56,6 +56,7 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 { 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); + event Burn(address indexed _burner, uint256 _value); //if _fallback is true, then we only allow the module if it is set, if it is not set we only allow the owner modifier onlyModule(uint8 _moduleType, bool _fallback) { @@ -280,4 +281,15 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 { } } } + + function burn(uint256 _value) checkGranularity(_value) public { + require(_value <= balances[msg.sender]); + // no need to require value <= totalSupply, since that would imply the + // sender's balance is greater than the totalSupply, which *should* be an assertion failure + + balances[msg.sender] = balances[msg.sender].sub(_value); + totalSupply_ = totalSupply_.sub(_value); + emit Burn(msg.sender, _value); + emit Transfer(msg.sender, address(0), _value); + } } diff --git a/test/security_token.js b/test/security_token.js index dee17040c..013d913a2 100644 --- a/test/security_token.js +++ b/test/security_token.js @@ -625,6 +625,26 @@ contract('SecurityToken', accounts => { assert.ok(errorThrown, message); }); + it("should fail in burning the securities token because of granularity check", async() => { + let balance = await I_SecurityToken.balanceOf(account_temp); + let errorThrown = false; + try { + await I_SecurityToken.burn(55555, {from: account_temp}); + } catch (error) { + console.log(`Granularity failure`); + errorThrown = true; + ensureException(error); + } + assert.ok(errorThrown, message); + + }); + + it("should burn the securities token", async() => { + let balance = await I_SecurityToken.balanceOf(account_temp); + await I_SecurityToken.burn(balance, {from: account_temp}); + assert.equal((await I_SecurityToken.balanceOf(account_temp)).toNumber(), 0); + }); + }); }); \ No newline at end of file From 46e8a5698d76e0acd6d91d7635c71905441c2ca5 Mon Sep 17 00:00:00 2001 From: satyam Date: Wed, 9 May 2018 15:19:33 +0530 Subject: [PATCH 2/3] token burner contract added --- CHANGELOG.md | 5 ++++- contracts/helpers/TokenBurner.sol | 21 +++++++++++++++++++++ contracts/interfaces/IST20.sol | 4 ++-- contracts/tokens/SecurityToken.sol | 24 ++++++++++++++++++++++-- 4 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 contracts/helpers/TokenBurner.sol diff --git a/CHANGELOG.md b/CHANGELOG.md index 57f78ee85..ecd6cb743 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,10 @@ All notable changes to this project will be documented in this file. * `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. * `_securityTokenAddress` get indexed in the `LogNewSecurityToken` event. -* Now each investor have its `expiryTime` for the KYC. After the expiryTime limit reached, investor will not abe to use transfer related functions. +* Now each investor have its `expiryTime` for the KYC. After the expiryTime limit reached, investor will not abe to use transfer related functions. +* Transfers of tokens gets paused at the level of all TM as well as on the ST level. To facilitate this 3 functions get added namely +`pause()`, `unpause()`,`freezeTransfers()`. All 3 functions are called by the issuer of the securityToken only. +* Security token has got a new feature of burning the tokens, To use this feature user need to call the `burn()` function but before that issuer need to deploy the `TokenBurner` contract and set its address into the SecurityToken contract using the function `setTokenBurner()`. ## Remove diff --git a/contracts/helpers/TokenBurner.sol b/contracts/helpers/TokenBurner.sol new file mode 100644 index 000000000..dba8313df --- /dev/null +++ b/contracts/helpers/TokenBurner.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.4.23; + +import "../interfaces/ISecurityToken.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; + +contract TokenBurner { + + address public securityToken; + + constructor (address _securityToken) { + securityToken = _securityToken; + } + + function burn(address _burner, uint256 _value) public returns(bool) { + require(msg.sender == securityToken); + // Add the schematics for the burner( token holder) that backing the burning of the securities + return true; + } + + +} \ No newline at end of file diff --git a/contracts/interfaces/IST20.sol b/contracts/interfaces/IST20.sol index 542256bba..18f34f13f 100644 --- a/contracts/interfaces/IST20.sol +++ b/contracts/interfaces/IST20.sol @@ -9,9 +9,9 @@ contract IST20 { //transfer, transferFrom must respect use respect the result of verifyTransfer function verifyTransfer(address _from, address _to, uint256 _amount) public view returns (bool success); - //used to create tokens + // used to create tokens function mint(address _investor, uint256 _amount) public returns (bool success); - //burn the tokens + // used to burn the tokens function burn(uint256 _value) public; } diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index 5def2408f..62ded2aad 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -11,7 +11,7 @@ import "../interfaces/IST20.sol"; import "../modules/TransferManager/ITransferManager.sol"; import "../modules/PermissionManager/IPermissionManager.sol"; import "../interfaces/ISecurityTokenRegistry.sol"; - +import "../helpers/TokenBurner.sol"; /** * @title SecurityToken @@ -26,6 +26,9 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 { bytes32 public securityTokenVersion = "0.0.1"; + // Reference to token burner contract + TokenBurner public tokenBurner; + // Reference to the POLY token. ERC20 public polyToken; @@ -52,11 +55,13 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 { uint256 _timestamp ); + event LogUpdateTokenDetails(bytes32 _oldDetails, bytes32 _newDetails); 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); event Burn(address indexed _burner, uint256 _value); + //if _fallback is true, then we only allow the module if it is set, if it is not set we only allow the owner modifier onlyModule(uint8 _moduleType, bool _fallback) { @@ -215,6 +220,15 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 { emit LogModuleBudgetChanged(_moduleType, modules[_moduleType][_moduleIndex].moduleAddress, _budget); } + /** + * @dev change the tokenDetails + */ + function updateTokenDetails(bytes32 _newTokenDetails) public onlyOwner { + bytes32 _oldTokenDetails = tokenDetails; + tokenDetails = _newTokenDetails; + emit LogUpdateTokenDetails(_oldTokenDetails, tokenDetails); + } + /** * @dev allows owner to change token granularity */ @@ -282,12 +296,18 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 { } } + function setTokenBurner(address _tokenBurner) public onlyOwner { + tokenBurner = TokenBurner(_tokenBurner); + } + function burn(uint256 _value) checkGranularity(_value) public { - require(_value <= balances[msg.sender]); + require(tokenBurner != address(0), "Token Burner contract address is not set yet"); + require(_value <= balances[msg.sender], "Value should no be greater than the balance of meg.sender"); // no need to require value <= totalSupply, since that would imply the // sender's balance is greater than the totalSupply, which *should* be an assertion failure balances[msg.sender] = balances[msg.sender].sub(_value); + require(tokenBurner.burn(msg.sender, _value), "Token burner process is not validated"); totalSupply_ = totalSupply_.sub(_value); emit Burn(msg.sender, _value); emit Transfer(msg.sender, address(0), _value); From e4011d06b782beee35dc346a20fdb52fbbd60366 Mon Sep 17 00:00:00 2001 From: satyam Date: Wed, 9 May 2018 22:20:44 +0530 Subject: [PATCH 3/3] add test cases --- contracts/tokens/SecurityToken.sol | 10 +++++----- test/security_token.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index 05f90e680..a711f8e15 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -61,8 +61,8 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 { 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); - event Burn(address indexed _burner, uint256 _value); + event Minted(address indexed to, uint256 amount); + event Burnt(address indexed _burner, uint256 _value); event LogFreezeTransfers(bool _freeze, uint256 _timestamp); //if _fallback is true, then we only allow the module if it is set, if it is not set we only allow the owner @@ -298,7 +298,7 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 { require(verifyTransfer(address(0), _investor, _amount), "Transfer is not valid"); totalSupply_ = totalSupply_.add(_amount); balances[_investor] = balances[_investor].add(_amount); - emit Mint(_investor, _amount); + emit Minted(_investor, _amount); emit Transfer(address(0), _investor, _amount); return true; } @@ -324,14 +324,14 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 { function burn(uint256 _value) checkGranularity(_value) public { require(tokenBurner != address(0), "Token Burner contract address is not set yet"); - require(_value <= balances[msg.sender], "Value should no be greater than the balance of meg.sender"); + require(_value <= balances[msg.sender], "Value should no be greater than the balance of msg.sender"); // no need to require value <= totalSupply, since that would imply the // sender's balance is greater than the totalSupply, which *should* be an assertion failure balances[msg.sender] = balances[msg.sender].sub(_value); require(tokenBurner.burn(msg.sender, _value), "Token burner process is not validated"); totalSupply_ = totalSupply_.sub(_value); - emit Burn(msg.sender, _value); + emit Burnt(msg.sender, _value); emit Transfer(msg.sender, address(0), _value); } } diff --git a/test/security_token.js b/test/security_token.js index 4f4255532..2fd0ddc8f 100644 --- a/test/security_token.js +++ b/test/security_token.js @@ -15,6 +15,7 @@ const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); const PolyToken = artifacts.require('./PolyToken.sol'); const PolyTokenFaucet = artifacts.require('./helpers/contracts/PolyTokenFaucet.sol'); +const TokenBurner = artifacts.require('./TokenBurner.sol'); const Web3 = require('web3'); const BigNumber = require('bignumber.js'); @@ -56,6 +57,7 @@ contract('SecurityToken', accounts => { let I_SecurityToken; let I_CappedSTO; let I_PolyToken; + let I_TokenBurner; // SecurityToken Details (Launched ST on the behalf of the issuer) const swarmHash = "dagwrgwgvwergwrvwrg"; @@ -684,6 +686,32 @@ contract('SecurityToken', accounts => { console.log(await I_SecurityToken.balanceOf(account_investor1)); await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), {from: account_temp}); }); + + it("Should fail to call the burn the tokens because token burner contract is not set", async() => { + // Deploy the token burner contract + I_TokenBurner = await TokenBurner.new(I_SecurityToken.address, { from: token_owner }); + + let errorThrown = false; + try { + await I_SecurityToken.burn(web3.utils.toWei('1', 'ether'),{ from: account_temp }); + } catch(error) { + console.log('failed in calling burn function because token burner contract is not set'); + errorThrown = true; + ensureException(error); + } + assert.ok(errorThrown, message); + }); + + it("Should burn the tokens", async ()=> { + // Deploy the token burner contract + I_TokenBurner = await TokenBurner.new(I_SecurityToken.address, { from: token_owner }); + + await I_SecurityToken.setTokenBurner(I_TokenBurner.address, { from: token_owner }); + assert.equal(await I_SecurityToken.tokenBurner.call(), I_TokenBurner.address); + + let tx = await I_SecurityToken.burn(web3.utils.toWei('1', 'ether'),{ from: account_temp }); + assert.equal(tx.logs[0].args._value, web3.utils.toWei('1', 'ether')); + }); }); }); \ No newline at end of file