Skip to content

Commit

Permalink
Merge pull request #315 from PolymathNetwork/off-chain-validation
Browse files Browse the repository at this point in the history
Add additional transfer / mint / burn functions with _data parameter
  • Loading branch information
pabloruiz55 authored Oct 4, 2018
2 parents 6ab03f1 + de69a71 commit 7992f9d
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 68 deletions.
84 changes: 60 additions & 24 deletions contracts/interfaces/ISecurityToken.sol
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit 7992f9d

Please sign in to comment.