Skip to content
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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
21 changes: 21 additions & 0 deletions contracts/helpers/TokenBurner.sol
Original file line number Diff line number Diff line change
@@ -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;
}


}
5 changes: 4 additions & 1 deletion contracts/interfaces/IST20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +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);

// used to burn the tokens
function burn(uint256 _value) public;
}
38 changes: 35 additions & 3 deletions contracts/tokens/SecurityToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -25,6 +25,10 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 {
using SafeMath for uint256;

bytes32 public securityTokenVersion = "0.0.1";

// Reference to token burner contract
TokenBurner public tokenBurner;

// Use to halt all the transactions
bool public freeze = false;
// Reference to the POLY token.
Expand Down Expand Up @@ -53,10 +57,12 @@ 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 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
Expand Down Expand Up @@ -216,6 +222,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
*/
Expand Down Expand Up @@ -283,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;
}
Expand All @@ -302,4 +317,21 @@ contract SecurityToken is ISecurityToken, StandardToken, DetailedERC20 {
}
}
}

function setTokenBurner(address _tokenBurner) public onlyOwner {
tokenBurner = TokenBurner(_tokenBurner);
}

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 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 Burnt(msg.sender, _value);
emit Transfer(msg.sender, address(0), _value);
}
}
28 changes: 28 additions & 0 deletions test/security_token.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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'));
});
});

});