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 additional transfer / mint / burn functions with _data parameter #315

Merged
merged 1 commit into from Oct 4, 2018
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
84 changes: 60 additions & 24 deletions contracts/interfaces/ISecurityToken.sol
Expand Up @@ -30,17 +30,28 @@ interface ISecurityToken {
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
* @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 _value is the amount of tokens that will be minted to the investor
* @param _data data to indicate validation
*/
function burn(uint256 _value) external;
function mintWithData(address _investor, uint256 _value, bytes _data) external returns (bool success);

/**
* @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
* @param _data data to indicate validation
*/
function burnFrom(address _from, uint256 _value) external;
function burnFromWithData(address _from, uint256 _value, bytes _data) external;

/**
* @notice Burn function used to burn the securityToken
* @param _value No. of tokens that get burned
* @param _data data to indicate validation
*/
function burnWithData(uint256 _value, bytes _data) external;

event Minted(address indexed _to, uint256 _value);
event Burnt(address indexed _burner, uint256 _value);
Expand Down Expand Up @@ -175,10 +186,22 @@ interface ISecurityToken {
function mintMulti(address[] _investors, uint256[] _values) external returns (bool success);

/**
* @notice Removes a module attached to the SecurityToken
* @param _module address of module to archive
*/
function removeModule(address _module) external;
* @notice Function used to attach a module to the security token
* @dev E.G.: On deployment (through the STR) ST gets a TransferManager module attached to it
* @dev to control restrictions on transfers.
* @dev You are allowed to add a new moduleType if:
* @dev - there is no existing module of that type yet added
* @dev - the last member of the module list is replacable
* @param _moduleFactory is the address of the module factory to be added
* @param _data is data packed into bytes used to further configure the module (See STO usage)
* @param _maxCost max amount of POLY willing to pay to module. (WIP)
*/
function addModule(
address _moduleFactory,
bytes _data,
uint256 _maxCost,
uint256 _budget
) external;

/**
* @notice Archives a module attached to the SecurityToken
Expand All @@ -193,18 +216,10 @@ interface ISecurityToken {
function unarchiveModule(address _module) external;

/**
* @notice Function used to attach the module in security token
* @param _moduleFactory Contract address of the module factory that needs to be attached
* @param _data Data used for the intialization of the module factory variables
* @param _maxCost Maximum cost of the Module factory
* @param _budget Budget of the Module factory
*/
function addModule(
address _moduleFactory,
bytes _data,
uint256 _maxCost,
uint256 _budget
) external;
* @notice Removes a module attached to the SecurityToken
* @param _module address of module to archive
*/
function removeModule(address _module) external;

/**
* @notice Use by the issuer to set the controller addresses
Expand All @@ -217,17 +232,19 @@ interface ISecurityToken {
* @param _from address from which to take tokens
* @param _to address where to send tokens
* @param _value amount of tokens to transfer
* @param _data data attached to the transfer by controller to emit in event
* @param _data data to indicate validation
* @param _log data attached to the transfer by controller to emit in event
*/
function forceTransfer(address _from, address _to, uint256 _value, bytes _data) external;
function forceTransfer(address _from, address _to, uint256 _value, bytes _data, bytes _log) external;

/**
* @notice Use by a controller to execute a foced burn
* @param _from address from which to take tokens
* @param _value amount of tokens to transfer
* @param _data data attached to the transfer by controller to emit in event
* @param _data data to indicate validation
* @param _log data attached to the transfer by controller to emit in event
*/
function forceBurn(address _from, uint256 _value, bytes _data) external;
function forceBurn(address _from, uint256 _value, bytes _data, bytes _log) external;

/**
* @notice Use by the issuer to permanently disable controller functionality
Expand All @@ -244,4 +261,23 @@ interface ISecurityToken {
* @notice gets the investor count
*/
function getInvestorCount() external view returns(uint256);

/**
* @notice Overloaded version of the transfer function
* @param _to receiver of transfer
* @param _value value of transfer
* @param _data data to indicate validation
* @return bool success
*/
function transferWithData(address _to, uint256 _value, bytes _data) external returns (bool success);

/**
* @notice Overloaded version of the transferFrom function
* @param _from sender of transfer
* @param _to receiver of transfer
* @param _value value of transfer
* @param _data data to indicate validation
* @return bool success
*/
function transferFromWithData(address _from, address _to, uint256 _value, bytes _data) external returns(bool);
}
2 changes: 1 addition & 1 deletion contracts/modules/Burn/TrackedRedemption.sol
Expand Up @@ -37,7 +37,7 @@ contract TrackedRedemption is IBurn, Module {
* @param _value The number of tokens to redeem
*/
function redeemTokens(uint256 _value) public {
ISecurityToken(securityToken).burnFrom(msg.sender, _value);
ISecurityToken(securityToken).burnFromWithData(msg.sender, _value, "");
redeemedTokens[msg.sender] = redeemedTokens[msg.sender].add(_value);
emit Redeemed(msg.sender, _value, now);
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/modules/TransferManager/CountTransferManager.sol
Expand Up @@ -26,7 +26,7 @@ contract CountTransferManager is ITransferManager {
}

/// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers
function verifyTransfer(address /* _from */, address _to, uint256 /* _amount */, bool /* _isTransfer */) public returns(Result) {
function verifyTransfer(address /* _from */, address _to, uint256 /* _amount */, bytes /* _data */, bool /* _isTransfer */) public returns(Result) {
if (!paused) {
if (maxHolderCount < ISecurityToken(securityToken).getInvestorCount()) {
// Allow transfers to existing maxHolders
Expand Down
Expand Up @@ -151,7 +151,7 @@ contract GeneralTransferManager is ITransferManager {
* b) Seller's sale lockup period is over
* c) Buyer's purchase lockup is over
*/
function verifyTransfer(address _from, address _to, uint256 /*_amount*/, bool /* _isTransfer */) public returns(Result) {
function verifyTransfer(address _from, address _to, uint256 /*_amount*/, bytes /* _data */, bool /* _isTransfer */) public returns(Result) {
if (!paused) {
if (allowAllTransfers) {
//All transfers allowed, regardless of whitelist
Expand Down
2 changes: 1 addition & 1 deletion contracts/modules/TransferManager/ITransferManager.sol
Expand Up @@ -16,7 +16,7 @@ contract ITransferManager is Module, Pausable {
// NA, then the result from this TM is ignored
enum Result {INVALID, NA, VALID, FORCE_VALID}

function verifyTransfer(address _from, address _to, uint256 _amount, bool _isTransfer) public returns(Result);
function verifyTransfer(address _from, address _to, uint256 _amount, bytes _data, bool _isTransfer) public returns(Result);

function unpause() onlyOwner public {
super._unpause();
Expand Down
Expand Up @@ -87,7 +87,7 @@ contract ManualApprovalTransferManager is ITransferManager {
* b) Seller's sale lockup period is over
* c) Buyer's purchase lockup is over
*/
function verifyTransfer(address _from, address _to, uint256 _amount, bool _isTransfer) public returns(Result) {
function verifyTransfer(address _from, address _to, uint256 _amount, bytes /* _data */, bool _isTransfer) public returns(Result) {
// function must only be called by the associated security token if _isTransfer == true
require(_isTransfer == false || msg.sender == securityToken, "Sender is not owner");
// manual blocking takes precidence over manual approval
Expand Down
Expand Up @@ -38,7 +38,7 @@ contract PercentageTransferManager is ITransferManager {
}

/// @notice Used to verify the transfer transaction according to the rule implemented in the trnasfer managers
function verifyTransfer(address /* _from */, address _to, uint256 _amount, bool /* _isTransfer */) public returns(Result) {
function verifyTransfer(address /* _from */, address _to, uint256 _amount, bytes /* _data */, bool /* _isTransfer */) public returns(Result) {
if (!paused) {
// If an address is on the whitelist, it is allowed to hold more than maxHolderPercentage of the tokens.
if (whitelist[_to]) {
Expand Down
89 changes: 65 additions & 24 deletions contracts/tokens/SecurityToken.sol
Expand Up @@ -464,7 +464,18 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
* @return bool success
*/
function transfer(address _to, uint256 _value) public returns (bool success) {
require(_updateTransfer(msg.sender, _to, _value), "Transfer not valid");
return transferWithData(_to, _value, "");
}

/**
* @notice Overloaded version of the transfer function
* @param _to receiver of transfer
* @param _value value of transfer
* @param _data data to indicate validation
* @return bool success
*/
function transferWithData(address _to, uint256 _value, bytes _data) public returns (bool success) {
require(_updateTransfer(msg.sender, _to, _value, _data), "Transfer not valid");
require(super.transfer(_to, _value));
return true;
}
Expand All @@ -477,16 +488,28 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
* @return bool success
*/
function transferFrom(address _from, address _to, uint256 _value) public returns(bool) {
require(_updateTransfer(_from, _to, _value), "Transfer not valid");
return transferFromWithData(_from, _to, _value, "");
}

/**
* @notice Overloaded version of the transferFrom function
* @param _from sender of transfer
* @param _to receiver of transfer
* @param _value value of transfer
* @param _data data to indicate validation
* @return bool success
*/
function transferFromWithData(address _from, address _to, uint256 _value, bytes _data) public returns(bool) {
require(_updateTransfer(_from, _to, _value, _data), "Transfer not valid");
require(super.transferFrom(_from, _to, _value));
return true;
}

function _updateTransfer(address _from, address _to, uint256 _value) internal returns(bool) {
function _updateTransfer(address _from, address _to, uint256 _value, bytes _data) internal returns(bool) {
_adjustInvestorCount(_from, _to, _value);
_adjustBalanceCheckpoints(_from);
_adjustBalanceCheckpoints(_to);
return _verifyTransfer(_from, _to, _value, true);
return _verifyTransfer(_from, _to, _value, _data, true);
}

/**
Expand All @@ -495,10 +518,11 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
* @param _from sender of transfer
* @param _to receiver of transfer
* @param _value value of transfer
* @param _data data to indicate validation
* @param _isTransfer whether transfer is being executed
* @return bool
*/
function _verifyTransfer(address _from, address _to, uint256 _value, bool _isTransfer) internal checkGranularity(_value) returns (bool) {
function _verifyTransfer(address _from, address _to, uint256 _value, bytes _data, bool _isTransfer) internal checkGranularity(_value) returns (bool) {
if (!transfersFrozen) {
if (modules[TRANSFER_KEY].length == 0) {
return true;
Expand All @@ -512,7 +536,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
module = modules[TRANSFER_KEY][i];
if (!modulesToData[module].isArchived) {
unarchived = true;
ITransferManager.Result valid = ITransferManager(module).verifyTransfer(_from, _to, _value, _isTransfer);
ITransferManager.Result valid = ITransferManager(module).verifyTransfer(_from, _to, _value, _data, _isTransfer);
if (valid == ITransferManager.Result.INVALID) {
isInvalid = true;
}
Expand All @@ -536,10 +560,11 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
* @param _from sender of transfer
* @param _to receiver of transfer
* @param _value value of transfer
* @param _data data to indicate validation
* @return bool
*/
function verifyTransfer(address _from, address _to, uint256 _value) public returns (bool) {
return _verifyTransfer(_from, _to, _value, false);
function verifyTransfer(address _from, address _to, uint256 _value, bytes _data) public returns (bool) {
return _verifyTransfer(_from, _to, _value, _data, false);
}

/**
Expand All @@ -558,9 +583,21 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
* @param _value Number of tokens be minted
* @return success
*/
function mint(address _investor, uint256 _value) public onlyModuleOrOwner(MINT_KEY) checkGranularity(_value) isMintingAllowed() returns (bool success) {
function mint(address _investor, uint256 _value) public returns (bool success) {
return mintWithData(_investor, _value, "");
}

/**
* @notice mints new tokens and assigns them to the target _investor.
* @dev Can only be called by the issuer or STO attached to the token
* @param _investor Address where the minted tokens will be delivered
* @param _value Number of tokens be minted
* @param _data data to indicate validation
* @return success
*/
function mintWithData(address _investor, uint256 _value, bytes _data) public onlyModuleOrOwner(MINT_KEY) isMintingAllowed() returns (bool success) {
require(_investor != address(0), "Investor is 0");
require(_updateTransfer(address(0), _investor, _value), "Transfer not valid");
require(_updateTransfer(address(0), _investor, _value, _data), "Transfer not valid");
_adjustTotalSupplyCheckpoints();
totalSupply_ = totalSupply_.add(_value);
balances[_investor] = balances[_investor].add(_value);
Expand Down Expand Up @@ -597,9 +634,9 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
return TokenLib.checkPermission(modules[PERMISSION_KEY], _delegate, _module, _perm);
}

function _burn(address _from, uint256 _value) internal returns(bool) {
function _burn(address _from, uint256 _value, bytes _data) internal returns(bool) {
require(_value <= balances[_from], "Value too high");
bool verified = _updateTransfer(_from, address(0), _value);
bool verified = _updateTransfer(_from, address(0), _value, _data);
_adjustTotalSupplyCheckpoints();
balances[_from] = balances[_from].sub(_value);
totalSupply_ = totalSupply_.sub(_value);
Expand All @@ -611,20 +648,22 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
/**
* @notice Burn function used to burn the securityToken
* @param _value No. of tokens that get burned
* @param _data data to indicate validation
*/
function burn(uint256 _value) checkGranularity(_value) onlyModule(BURN_KEY) public {
require(_burn(msg.sender, _value), "Burn not valid");
function burnWithData(uint256 _value, bytes _data) onlyModule(BURN_KEY) public {
require(_burn(msg.sender, _value, _data), "Burn not valid");
}

/**
* @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 tokens that get burned
* @param _data data to indicate validation
*/
function burnFrom(address _from, uint256 _value) checkGranularity(_value) onlyModule(BURN_KEY) public {
function burnFromWithData(address _from, uint256 _value, bytes _data) onlyModule(BURN_KEY) public {
require(_value <= allowed[_from][msg.sender], "Value too high");
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
require(_burn(_from, _value), "Burn not valid");
require(_burn(_from, _value, _data), "Burn not valid");
}

/**
Expand Down Expand Up @@ -693,27 +732,29 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
* @param _from address from which to take tokens
* @param _to address where to send tokens
* @param _value amount of tokens to transfer
* @param _data data attached to the transfer by controller to emit in event
* @param _data data to indicate validation
* @param _log data attached to the transfer by controller to emit in event
*/
function forceTransfer(address _from, address _to, uint256 _value, bytes _data) public onlyController {
function forceTransfer(address _from, address _to, uint256 _value, bytes _data, bytes _log) public onlyController {
require(_to != address(0));
require(_value <= balances[_from]);
bool verified = _updateTransfer(_from, _to, _value);
bool verified = _updateTransfer(_from, _to, _value, _data);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
emit ForceTransfer(msg.sender, _from, _to, _value, verified, _data);
emit ForceTransfer(msg.sender, _from, _to, _value, verified, _log);
emit Transfer(_from, _to, _value);
}

/**
* @notice Use by a controller to execute a foced burn
* @param _from address from which to take tokens
* @param _value amount of tokens to transfer
* @param _data data attached to the transfer by controller to emit in event
* @param _data data to indicate validation
* @param _log data attached to the transfer by controller to emit in event
*/
function forceBurn(address _from, uint256 _value, bytes _data) public onlyController {
bool verified = _burn(_from, _value);
emit ForceBurn(msg.sender, _from, _value, verified, _data);
function forceBurn(address _from, uint256 _value, bytes _data, bytes _log) public onlyController {
bool verified = _burn(_from, _value, _data);
emit ForceBurn(msg.sender, _from, _value, verified, _log);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion test/c_checkpoints.js
Expand Up @@ -389,7 +389,7 @@ contract('Checkpoints', accounts => {
}
n = n.toFixed(0);
console.log("Burning: " + n.toString() + " from: " + burner);
await I_SecurityToken.forceBurn(burner, n, "", { from: token_owner });
await I_SecurityToken.forceBurn(burner, n, "", "", { from: token_owner });
}
console.log("Checking Interim...");
for (let k = 0; k < cps.length; k++) {
Expand Down