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 better redemption mechanics #291

Merged
merged 12 commits into from
Oct 2, 2018
23 changes: 15 additions & 8 deletions contracts/interfaces/ISecurityToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,30 @@ interface ISecurityToken {
event Approval(address indexed owner, address indexed spender, uint256 value);

//transfer, transferFrom must respect use respect the result of verifyTransfer
function verifyTransfer(address _from, address _to, uint256 _amount) external returns (bool success);
function verifyTransfer(address _from, address _to, uint256 _value) external returns (bool success);

/**
* @notice 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)
* @param _investor address the tokens will be minted to
* @param _amount is the amount of tokens that will be minted to the investor
* @param _value is the amount of tokens that will be minted to the investor
*/
function mint(address _investor, uint256 _amount) external returns (bool success);
function mint(address _investor, uint256 _value) external returns (bool success);

/**
* @notice Burn function used to burn the securityToken
* @param _value No. of tokens that get burned
*/
function burn(uint256 _value) external returns (bool success);

event Minted(address indexed to, uint256 amount);
/**
* @notice Burn function used to burn the securityToken on behalf of someone else
* @param _from Address for whom to burn tokens
* @param _value No. of token that get burned
*/
function burnFrom(address _from, uint256 _value) external returns (bool success);

event Minted(address indexed _to, uint256 _value);
event Burnt(address indexed _burner, uint256 _value);

// Permissions this to a Permission module, which has a key of 1
Expand Down Expand Up @@ -118,9 +125,9 @@ interface ISecurityToken {
/**
* @notice allows the owner to withdraw unspent POLY stored by them on the ST.
* @dev Owner can transfer POLY to the ST which will be used to pay for modules that require a POLY fee.
* @param _amount amount of POLY to withdraw
* @param _value amount of POLY to withdraw
*/
function withdrawPoly(uint256 _amount) external;
function withdrawPoly(uint256 _value) external;

/**
* @notice allows owner to approve more POLY to one of the modules
Expand Down Expand Up @@ -168,10 +175,10 @@ interface ISecurityToken {
* @notice mints new tokens and assigns them to the target investors.
* Can only be called by the STO attached to the token or by the Issuer (Security Token contract owner)
* @param _investors A list of addresses to whom the minted tokens will be delivered
* @param _amounts A list of the amount of tokens to mint to corresponding addresses from _investor[] list
* @param _values A list of the amount of tokens to mint to corresponding addresses from _investor[] list
* @return success
*/
function mintMulti(address[] _investors, uint256[] _amounts) external returns (bool success);
function mintMulti(address[] _investors, uint256[] _values) external returns (bool success);

/**
* @notice used to set the token Burner address. It can only be called by the owner
Expand Down
53 changes: 53 additions & 0 deletions contracts/libraries/KindMath.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
pragma solidity ^0.4.24;

// Copied from OpenZeppelin and modified to be friendlier

/**
* @title SafeMath
adamdossa marked this conversation as resolved.
Show resolved Hide resolved
* @dev Math operations with safety checks that throw on error
*/
library KindMath {

/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than requireing 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}

c = a * b;
require(c / a == b, "mul overflow");
return c;
}

/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// require(b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = a / b;
// require(a == b * c + a % b); // There is no case in which this doesn't hold
return a / b;
}

/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "sub overflow");
return a - b;
}

/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a + b;
require(c >= a, "add overflow");
return c;
}
}
8 changes: 8 additions & 0 deletions contracts/modules/Burn/IBurn.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pragma solidity ^0.4.24;

/**
* @title Interface to be implemented by all checkpoint modules
*/
interface IBurn {

}
52 changes: 52 additions & 0 deletions contracts/modules/Burn/TrackedRedemption.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
pragma solidity ^0.4.24;

import "./IBurn.sol";
import "../Module.sol";
import "../../interfaces/ISecurityToken.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";

/**
* @title Burn module for burning tokens and keeping track of burnt amounts
*/
contract TrackedRedemption is IBurn, Module {
using SafeMath for uint256;

mapping (address => uint256) redeemedTokens;

event Redeemed(address _investor, uint256 _value, uint256 _timestamp);

/**
* @notice Constructor
* @param _securityToken Address of the security token
* @param _polyAddress Address of the polytoken
*/
constructor (address _securityToken, address _polyAddress) public
Module(_securityToken, _polyAddress)
{
}

/**
* @notice This function returns the signature of configure function
*/
function getInitFunction() public pure returns (bytes4) {
return bytes4(0);
}

/**
* @notice Redeem tokens and track redemptions
* @param _value The number of tokens to redeem
*/
function redeemTokens(uint256 _value) public {
require(ISecurityToken(securityToken).burnFrom(msg.sender, _value), "Unable to redeem tokens");
redeemedTokens[msg.sender] = redeemedTokens[msg.sender].add(_value);
emit Redeemed(msg.sender, _value, now);
}

/**
* @notice Return the permissions flag that are associated with CountTransferManager
*/
function getPermissions() public view returns(bytes32[]) {
bytes32[] memory allPermissions = new bytes32[](0);
return allPermissions;
}
}
100 changes: 100 additions & 0 deletions contracts/modules/Burn/TrackedRedemptionFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
pragma solidity ^0.4.24;

import "./TrackedRedemption.sol";
import "../ModuleFactory.sol";

/**
* @title Factory for deploying GeneralTransferManager module
*/
contract TrackedRedemptionFactory is ModuleFactory {

/**
* @notice Constructor
* @param _polyAddress Address of the polytoken
* @param _setupCost Setup cost of module
* @param _usageCost Usage cost of module
* @param _subscriptionCost Monthly cost of module
*/
constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public
ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost)
{
version = "1.0.0";
name = "TrackedRedemption";
title = "Tracked Redemption";
description = "Track token redemptions";
compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0));
compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0));
}

/**
* @notice used to launch the Module with the help of factory
* @return address Contract address of the Module
*/
function deploy(bytes /* _data */) external returns(address) {
if (setupCost > 0)
require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided");
address trackedRedemption = new TrackedRedemption(msg.sender, address(polyToken));
emit GenerateModuleFromFactory(address(trackedRedemption), getName(), address(this), msg.sender, setupCost, now);
return address(trackedRedemption);
}

/**
* @notice Type of the Module factory
*/
function getType() public view returns(uint8) {
return 5;
}

/**
* @notice Get the name of the Module
*/
function getName() public view returns(bytes32) {
return name;
}

/**
* @notice Get the description of the Module
*/
function getDescription() public view returns(string) {
return description;
}

/**
* @notice Get the version of the Module
*/
function getVersion() external view returns(string) {
return version;
}

/**
* @notice Get the title of the Module
*/
function getTitle() public view returns(string) {
return title;
}

/**
* @notice Get the setup cost of the module
*/
function getSetupCost() external view returns (uint256) {
return setupCost;
}

/**
* @notice Get the Instructions that helped to used the module
*/
function getInstructions() public view returns(string) {
return "Allows an investor to redeem security tokens which are tracked by this module";
}

/**
* @notice Get the tags related to the module factory
*/
function getTags() public view returns(bytes32[]) {
bytes32[] memory availableTags = new bytes32[](2);
availableTags[0] = "Redemption";
availableTags[1] = "Tracked";
return availableTags;
}

}
Loading