Skip to content

Commit e9efd58

Browse files
authored
Merge pull request #99 from PolymathNetwork/burnable-token
burn securities
2 parents 9e11eeb + e4011d0 commit e9efd58

File tree

5 files changed

+92
-5
lines changed

5 files changed

+92
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ All notable changes to this project will be documented in this file.
2323
* `takeFee()` new function introduced to extract the POLY token from the factory. It only be called by the owner of the factory.
2424
* Added ability for issuer to provide a signed piece of data to allow investors to whitelist themselves.
2525
* `_securityTokenAddress` get indexed in the `LogNewSecurityToken` event.
26-
* Now each investor have its `expiryTime` for the KYC. After the expiryTime limit reached, investor will not abe to use transfer related functions.
26+
* Now each investor have its `expiryTime` for the KYC. After the expiryTime limit reached, investor will not abe to use transfer related functions.
27+
* 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
28+
`pause()`, `unpause()`,`freezeTransfers()`. All 3 functions are called by the issuer of the securityToken only.
29+
* 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()`.
2730

2831
## Remove
2932

contracts/helpers/TokenBurner.sol

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
pragma solidity ^0.4.23;
2+
3+
import "../interfaces/ISecurityToken.sol";
4+
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
5+
6+
contract TokenBurner {
7+
8+
address public securityToken;
9+
10+
constructor (address _securityToken) {
11+
securityToken = _securityToken;
12+
}
13+
14+
function burn(address _burner, uint256 _value) public returns(bool) {
15+
require(msg.sender == securityToken);
16+
// Add the schematics for the burner( token holder) that backing the burning of the securities
17+
return true;
18+
}
19+
20+
21+
}

contracts/interfaces/IST20.sol

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ contract IST20 is StandardToken, DetailedERC20 {
1111
//transfer, transferFrom must respect use respect the result of verifyTransfer
1212
function verifyTransfer(address _from, address _to, uint256 _amount) public view returns (bool success);
1313

14-
//used to create tokens
14+
// used to create tokens
1515
function mint(address _investor, uint256 _amount) public returns (bool success);
16+
17+
// used to burn the tokens
18+
function burn(uint256 _value) public;
1619
}

contracts/tokens/SecurityToken.sol

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import "../interfaces/IST20.sol";
99
import "../modules/TransferManager/ITransferManager.sol";
1010
import "../modules/PermissionManager/IPermissionManager.sol";
1111
import "../interfaces/ISecurityTokenRegistry.sol";
12-
12+
import "../helpers/TokenBurner.sol";
1313

1414
/**
1515
* @title SecurityToken
@@ -23,6 +23,10 @@ contract SecurityToken is ISecurityToken {
2323
using SafeMath for uint256;
2424

2525
bytes32 public securityTokenVersion = "0.0.1";
26+
27+
// Reference to token burner contract
28+
TokenBurner public tokenBurner;
29+
2630
// Use to halt all the transactions
2731
bool public freeze = false;
2832
// Reference to the POLY token.
@@ -51,10 +55,12 @@ contract SecurityToken is ISecurityToken {
5155
uint256 _timestamp
5256
);
5357

58+
event LogUpdateTokenDetails(bytes32 _oldDetails, bytes32 _newDetails);
5459
event LogGranularityChanged(uint256 _oldGranularity, uint256 _newGranularity);
5560
event LogModuleRemoved(uint8 indexed _type, address _module, uint256 _timestamp);
5661
event LogModuleBudgetChanged(uint8 indexed _moduleType, address _module, uint256 _budget);
57-
event Mint(address indexed to, uint256 amount);
62+
event Minted(address indexed to, uint256 amount);
63+
event Burnt(address indexed _burner, uint256 _value);
5864
event LogFreezeTransfers(bool _freeze, uint256 _timestamp);
5965

6066
//if _fallback is true, then we only allow the module if it is set, if it is not set we only allow the owner
@@ -214,6 +220,15 @@ contract SecurityToken is ISecurityToken {
214220
emit LogModuleBudgetChanged(_moduleType, modules[_moduleType][_moduleIndex].moduleAddress, _budget);
215221
}
216222

223+
/**
224+
* @dev change the tokenDetails
225+
*/
226+
function updateTokenDetails(bytes32 _newTokenDetails) public onlyOwner {
227+
bytes32 _oldTokenDetails = tokenDetails;
228+
tokenDetails = _newTokenDetails;
229+
emit LogUpdateTokenDetails(_oldTokenDetails, tokenDetails);
230+
}
231+
217232
/**
218233
* @dev allows owner to change token granularity
219234
*/
@@ -310,7 +325,7 @@ contract SecurityToken is ISecurityToken {
310325
require(verifyTransfer(address(0), _investor, _amount), "Transfer is not valid");
311326
totalSupply_ = totalSupply_.add(_amount);
312327
balances[_investor] = balances[_investor].add(_amount);
313-
emit Mint(_investor, _amount);
328+
emit Minted(_investor, _amount);
314329
emit Transfer(address(0), _investor, _amount);
315330
return true;
316331
}
@@ -329,4 +344,21 @@ contract SecurityToken is ISecurityToken {
329344
}
330345
}
331346
}
347+
348+
function setTokenBurner(address _tokenBurner) public onlyOwner {
349+
tokenBurner = TokenBurner(_tokenBurner);
350+
}
351+
352+
function burn(uint256 _value) checkGranularity(_value) public {
353+
require(tokenBurner != address(0), "Token Burner contract address is not set yet");
354+
require(_value <= balances[msg.sender], "Value should no be greater than the balance of msg.sender");
355+
// no need to require value <= totalSupply, since that would imply the
356+
// sender's balance is greater than the totalSupply, which *should* be an assertion failure
357+
358+
balances[msg.sender] = balances[msg.sender].sub(_value);
359+
require(tokenBurner.burn(msg.sender, _value), "Token burner process is not validated");
360+
totalSupply_ = totalSupply_.sub(_value);
361+
emit Burnt(msg.sender, _value);
362+
emit Transfer(msg.sender, address(0), _value);
363+
}
332364
}

test/security_token.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const GeneralTransferManager = artifacts.require('./GeneralTransferManager');
1515
const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager');
1616
const PolyToken = artifacts.require('./PolyToken.sol');
1717
const PolyTokenFaucet = artifacts.require('./helpers/contracts/PolyTokenFaucet.sol');
18+
const TokenBurner = artifacts.require('./TokenBurner.sol');
1819

1920
const Web3 = require('web3');
2021
const BigNumber = require('bignumber.js');
@@ -56,6 +57,7 @@ contract('SecurityToken', accounts => {
5657
let I_SecurityToken;
5758
let I_CappedSTO;
5859
let I_PolyToken;
60+
let I_TokenBurner;
5961

6062
// SecurityToken Details (Launched ST on the behalf of the issuer)
6163
const swarmHash = "dagwrgwgvwergwrvwrg";
@@ -684,6 +686,32 @@ contract('SecurityToken', accounts => {
684686
console.log(await I_SecurityToken.balanceOf(account_investor1));
685687
await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), {from: account_temp});
686688
});
689+
690+
it("Should fail to call the burn the tokens because token burner contract is not set", async() => {
691+
// Deploy the token burner contract
692+
I_TokenBurner = await TokenBurner.new(I_SecurityToken.address, { from: token_owner });
693+
694+
let errorThrown = false;
695+
try {
696+
await I_SecurityToken.burn(web3.utils.toWei('1', 'ether'),{ from: account_temp });
697+
} catch(error) {
698+
console.log('failed in calling burn function because token burner contract is not set');
699+
errorThrown = true;
700+
ensureException(error);
701+
}
702+
assert.ok(errorThrown, message);
703+
});
704+
705+
it("Should burn the tokens", async ()=> {
706+
// Deploy the token burner contract
707+
I_TokenBurner = await TokenBurner.new(I_SecurityToken.address, { from: token_owner });
708+
709+
await I_SecurityToken.setTokenBurner(I_TokenBurner.address, { from: token_owner });
710+
assert.equal(await I_SecurityToken.tokenBurner.call(), I_TokenBurner.address);
711+
712+
let tx = await I_SecurityToken.burn(web3.utils.toWei('1', 'ether'),{ from: account_temp });
713+
assert.equal(tx.logs[0].args._value, web3.utils.toWei('1', 'ether'));
714+
});
687715
});
688716

689717
});

0 commit comments

Comments
 (0)