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
133 changes: 73 additions & 60 deletions contracts/modules/TransferManager/GeneralTransferManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, ITransferManag
// Emit when there is change in the flag variable called signingAddress
event ChangeSigningAddress(address _signingAddress);
// Emit when investor details get modified related to their whitelisting
event ChangeDefaults(uint64 _defaultFromTime, uint64 _defaultToTime);
event ChangeDefaults(uint64 _defaultCanSendAfter, uint64 _defaultCanReceiveAfter);

// _fromTime is the time from which the _investor can send tokens
// _toTime is the time from which the _investor can receive tokens
// if allowAllWhitelistIssuances is TRUE, then _toTime is ignored when receiving tokens from the issuance address
// if allowAllWhitelistTransfers is TRUE, then _toTime and _fromTime is ignored when sending or receiving tokens
// _canSendAfter is the time from which the _investor can send tokens
// _canReceiveAfter is the time from which the _investor can receive tokens
// if allowAllWhitelistIssuances is TRUE, then _canReceiveAfter is ignored when receiving tokens from the issuance address
// if allowAllWhitelistTransfers is TRUE, then _canReceiveAfter and _canSendAfter is ignored when sending or receiving tokens
// in any case, any investor sending or receiving tokens, must have a _expiryTime in the future
event ModifyWhitelist(
address indexed _investor,
uint256 _dateAdded,
address indexed _addedBy,
uint256 _fromTime,
uint256 _toTime,
uint256 _canSendAfter,
uint256 _canReceiveAfter,
uint256 _expiryTime,
bool _canBuyFromSTO
);
Expand All @@ -60,21 +60,25 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, ITransferManag
}

/**
* @notice Used to change the default times used when fromTime / toTime are zero
* @param _defaultFromTime default for zero fromTime
* @param _defaultToTime default for zero toTime
* @notice Used to change the default times used when canSendAfter / canReceiveAfter are zero
* @param _defaultCanSendAfter default for zero canSendAfter
* @param _defaultCanReceiveAfter default for zero canReceiveAfter
*/
function changeDefaults(uint64 _defaultFromTime, uint64 _defaultToTime) public withPerm(FLAGS) {
defaults.fromTime = _defaultFromTime;
defaults.toTime = _defaultToTime;
emit ChangeDefaults(_defaultFromTime, _defaultToTime);
function changeDefaults(uint64 _defaultCanSendAfter, uint64 _defaultCanReceiveAfter) public withPerm(FLAGS) {
/* 0 values are also allowed as they represent that the Issuer
does not want a default value for these variables.
0 is also the default value of these variables */
defaults.canSendAfter = _defaultCanSendAfter;
defaults.canReceiveAfter = _defaultCanReceiveAfter;
emit ChangeDefaults(_defaultCanSendAfter, _defaultCanReceiveAfter);
}

/**
* @notice Used to change the Issuance Address
* @param _issuanceAddress new address for the issuance
*/
function changeIssuanceAddress(address _issuanceAddress) public withPerm(FLAGS) {
// address(0x0) is also a valid value and in most cases, the address that issues tokens is 0x0.
issuanceAddress = _issuanceAddress;
emit ChangeIssuanceAddress(_issuanceAddress);
}
Expand All @@ -84,6 +88,9 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, ITransferManag
* @param _signingAddress new address for the signing
*/
function changeSigningAddress(address _signingAddress) public withPerm(FLAGS) {
/* address(0x0) is also a valid value as an Issuer might want to
give this permission to nobody (except their own address).
0x0 is also the default value */
signingAddress = _signingAddress;
emit ChangeSigningAddress(_signingAddress);
}
Expand Down Expand Up @@ -156,7 +163,7 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, ITransferManag
return (_onWhitelist(_to) && _onWhitelist(_from)) ? Result.VALID : Result.NA;
}

(uint64 adjustedFromTime, uint64 adjustedToTime) = _adjustTimes(whitelist[_from].fromTime, whitelist[_to].toTime);
(uint64 adjustedCanSendAfter, uint64 adjustedCanReceiveAfter) = _adjustTimes(whitelist[_from].canSendAfter, whitelist[_to].canReceiveAfter);
if (_from == issuanceAddress) {
// Possible STO transaction, but investor not allowed to purchased from STO
if ((whitelist[_to].canBuyFromSTO == 0) && _isSTOAttached()) {
Expand All @@ -166,51 +173,51 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, ITransferManag
if (allowAllWhitelistIssuances) {
return _onWhitelist(_to) ? Result.VALID : Result.NA;
} else {
return (_onWhitelist(_to) && (adjustedToTime <= uint64(now))) ? Result.VALID : Result.NA;
return (_onWhitelist(_to) && (adjustedCanReceiveAfter <= uint64(now))) ? Result.VALID : Result.NA;
}
}

//Anyone on the whitelist can transfer provided the blocknumber is large enough
/*solium-disable-next-line security/no-block-members*/
return ((_onWhitelist(_from) && (adjustedFromTime <= uint64(now))) &&
(_onWhitelist(_to) && (adjustedToTime <= uint64(now)))) ? Result.VALID : Result.NA; /*solium-disable-line security/no-block-members*/
return ((_onWhitelist(_from) && (adjustedCanSendAfter <= uint64(now))) &&
(_onWhitelist(_to) && (adjustedCanReceiveAfter <= uint64(now)))) ? Result.VALID : Result.NA; /*solium-disable-line security/no-block-members*/
}
return Result.NA;
}

/**
* @notice Adds or removes addresses from the whitelist.
* @param _investor is the address to whitelist
* @param _fromTime is the moment when the sale lockup period ends and the investor can freely sell his tokens
* @param _toTime is the moment when the purchase lockup period ends and the investor can freely purchase tokens from others
* @param _canSendAfter the moment when the sale lockup period ends and the investor can freely sell or transfer away their tokens
* @param _canReceiveAfter the moment when the purchase lockup period ends and the investor can freely purchase or receive from others
* @param _expiryTime is the moment till investors KYC will be validated. After that investor need to do re-KYC
* @param _canBuyFromSTO is used to know whether the investor is restricted investor or not.
*/
function modifyWhitelist(
address _investor,
uint256 _fromTime,
uint256 _toTime,
uint256 _canSendAfter,
uint256 _canReceiveAfter,
uint256 _expiryTime,
bool _canBuyFromSTO
)
public
withPerm(WHITELIST)
{
_modifyWhitelist(_investor, _fromTime, _toTime, _expiryTime, _canBuyFromSTO);
_modifyWhitelist(_investor, _canSendAfter, _canReceiveAfter, _expiryTime, _canBuyFromSTO);
}

/**
* @notice Adds or removes addresses from the whitelist.
* @param _investor is the address to whitelist
* @param _fromTime is the moment when the sale lockup period ends and the investor can freely sell his tokens
* @param _toTime is the moment when the purchase lockup period ends and the investor can freely purchase tokens from others
* @param _canSendAfter is the moment when the sale lockup period ends and the investor can freely sell his tokens
* @param _canReceiveAfter is the moment when the purchase lockup period ends and the investor can freely purchase tokens from others
* @param _expiryTime is the moment till investors KYC will be validated. After that investor need to do re-KYC
* @param _canBuyFromSTO is used to know whether the investor is restricted investor or not.
*/
function _modifyWhitelist(
address _investor,
uint256 _fromTime,
uint256 _toTime,
uint256 _canSendAfter,
uint256 _canReceiveAfter,
uint256 _expiryTime,
bool _canBuyFromSTO
)
Expand All @@ -224,39 +231,45 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, ITransferManag
if (whitelist[_investor].added == uint8(0)) {
investors.push(_investor);
}
whitelist[_investor] = TimeRestriction(uint64(_fromTime), uint64(_toTime), uint64(_expiryTime), canBuyFromSTO, uint8(1));
emit ModifyWhitelist(_investor, now, msg.sender, _fromTime, _toTime, _expiryTime, _canBuyFromSTO);
require(
uint64(_canSendAfter) == _canSendAfter &&
uint64(_canReceiveAfter) == _canReceiveAfter &&
uint64(_expiryTime) == _expiryTime,
"uint64 overflow"
);
whitelist[_investor] = TimeRestriction(uint64(_canSendAfter), uint64(_canReceiveAfter), uint64(_expiryTime), canBuyFromSTO, uint8(1));
emit ModifyWhitelist(_investor, now, msg.sender, _canSendAfter, _canReceiveAfter, _expiryTime, _canBuyFromSTO);
}

/**
* @notice Adds or removes addresses from the whitelist.
* @param _investors List of the addresses to whitelist
* @param _fromTimes An array of the moment when the sale lockup period ends and the investor can freely sell his tokens
* @param _toTimes An array of the moment when the purchase lockup period ends and the investor can freely purchase tokens from others
* @param _canSendAfters An array of the moment when the sale lockup period ends and the investor can freely sell his tokens
* @param _canReceiveAfters An array of the moment when the purchase lockup period ends and the investor can freely purchase tokens from others
* @param _expiryTimes An array of the moment till investors KYC will be validated. After that investor need to do re-KYC
* @param _canBuyFromSTO An array of boolean values
*/
function modifyWhitelistMulti(
address[] _investors,
uint256[] _fromTimes,
uint256[] _toTimes,
uint256[] _canSendAfters,
uint256[] _canReceiveAfters,
uint256[] _expiryTimes,
bool[] _canBuyFromSTO
) public withPerm(WHITELIST) {
require(_investors.length == _fromTimes.length, "Mismatched input lengths");
require(_fromTimes.length == _toTimes.length, "Mismatched input lengths");
require(_toTimes.length == _expiryTimes.length, "Mismatched input lengths");
require(_canBuyFromSTO.length == _toTimes.length, "Mismatched input length");
require(_investors.length == _canSendAfters.length, "Mismatched input lengths");
require(_canSendAfters.length == _canReceiveAfters.length, "Mismatched input lengths");
require(_canReceiveAfters.length == _expiryTimes.length, "Mismatched input lengths");
require(_canBuyFromSTO.length == _canReceiveAfters.length, "Mismatched input length");
for (uint256 i = 0; i < _investors.length; i++) {
_modifyWhitelist(_investors[i], _fromTimes[i], _toTimes[i], _expiryTimes[i], _canBuyFromSTO[i]);
_modifyWhitelist(_investors[i], _canSendAfters[i], _canReceiveAfters[i], _expiryTimes[i], _canBuyFromSTO[i]);
}
}

/**
* @notice Adds or removes addresses from the whitelist - can be called by anyone with a valid signature
* @param _investor is the address to whitelist
* @param _fromTime is the moment when the sale lockup period ends and the investor can freely sell his tokens
* @param _toTime is the moment when the purchase lockup period ends and the investor can freely purchase tokens from others
* @param _canSendAfter is the moment when the sale lockup period ends and the investor can freely sell his tokens
* @param _canReceiveAfter is the moment when the purchase lockup period ends and the investor can freely purchase tokens from others
* @param _expiryTime is the moment till investors KYC will be validated. After that investor need to do re-KYC
* @param _canBuyFromSTO is used to know whether the investor is restricted investor or not.
* @param _validFrom is the time that this signature is valid from
Expand All @@ -268,8 +281,8 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, ITransferManag
*/
function modifyWhitelistSigned(
address _investor,
uint256 _fromTime,
uint256 _toTime,
uint256 _canSendAfter,
uint256 _canReceiveAfter,
uint256 _expiryTime,
bool _canBuyFromSTO,
uint256 _validFrom,
Expand All @@ -286,18 +299,18 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, ITransferManag
require(!nonceMap[_investor][_nonce], "Already used signature");
nonceMap[_investor][_nonce] = true;
bytes32 hash = keccak256(
abi.encodePacked(this, _investor, _fromTime, _toTime, _expiryTime, _canBuyFromSTO, _validFrom, _validTo, _nonce)
abi.encodePacked(this, _investor, _canSendAfter, _canReceiveAfter, _expiryTime, _canBuyFromSTO, _validFrom, _validTo, _nonce)
);
_checkSig(hash, _v, _r, _s);
_modifyWhitelist(_investor, _fromTime, _toTime, _expiryTime, _canBuyFromSTO);
_modifyWhitelist(_investor, _canSendAfter, _canReceiveAfter, _expiryTime, _canBuyFromSTO);
}

/**
* @notice Used to verify the signature
*/
function _checkSig(bytes32 _hash, uint8 _v, bytes32 _r, bytes32 _s) internal view {
//Check that the signature is valid
//sig should be signing - _investor, _fromTime, _toTime & _expiryTime and be signed by the issuer address
//sig should be signing - _investor, _canSendAfter, _canReceiveAfter & _expiryTime and be signed by the issuer address
address signer = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _hash)), _v, _r, _s);
require(signer == Ownable(securityToken).owner() || signer == signingAddress, "Incorrect signer");
}
Expand All @@ -322,16 +335,16 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, ITransferManag
/**
* @notice Internal function to adjust times using default values
*/
function _adjustTimes(uint64 _fromTime, uint64 _toTime) internal view returns(uint64, uint64) {
uint64 adjustedFromTime = _fromTime;
uint64 adjustedToTime = _toTime;
if (_fromTime == 0) {
adjustedFromTime = defaults.fromTime;
function _adjustTimes(uint64 _canSendAfter, uint64 _canReceiveAfter) internal view returns(uint64, uint64) {
uint64 adjustedCanSendAfter = _canSendAfter;
uint64 adjustedCanReceiveAfter = _canReceiveAfter;
if (_canSendAfter == 0) {
adjustedCanSendAfter = defaults.canSendAfter;
}
if (_toTime == 0) {
adjustedToTime = defaults.toTime;
if (_canReceiveAfter == 0) {
adjustedCanReceiveAfter = defaults.canReceiveAfter;
}
return (adjustedFromTime, adjustedToTime);
return (adjustedCanSendAfter, adjustedCanReceiveAfter);
}

/**
Expand All @@ -345,9 +358,9 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, ITransferManag
* @dev Returns list of all investors data
*/
function getAllInvestorsData() external view returns(address[], uint256[], uint256[], uint256[], bool[]) {
(uint256[] memory fromTimes, uint256[] memory toTimes, uint256[] memory expiryTimes, bool[] memory canBuyFromSTOs)
(uint256[] memory canSendAfters, uint256[] memory canReceiveAfters, uint256[] memory expiryTimes, bool[] memory canBuyFromSTOs)
= _investorsData(investors);
return (investors, fromTimes, toTimes, expiryTimes, canBuyFromSTOs);
return (investors, canSendAfters, canReceiveAfters, expiryTimes, canBuyFromSTOs);

}

Expand All @@ -359,21 +372,21 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, ITransferManag
}

function _investorsData(address[] _investors) internal view returns(uint256[], uint256[], uint256[], bool[]) {
uint256[] memory fromTimes = new uint256[](_investors.length);
uint256[] memory toTimes = new uint256[](_investors.length);
uint256[] memory canSendAfters = new uint256[](_investors.length);
uint256[] memory canReceiveAfters = new uint256[](_investors.length);
uint256[] memory expiryTimes = new uint256[](_investors.length);
bool[] memory canBuyFromSTOs = new bool[](_investors.length);
for (uint256 i = 0; i < _investors.length; i++) {
fromTimes[i] = whitelist[_investors[i]].fromTime;
toTimes[i] = whitelist[_investors[i]].toTime;
canSendAfters[i] = whitelist[_investors[i]].canSendAfter;
canReceiveAfters[i] = whitelist[_investors[i]].canReceiveAfter;
expiryTimes[i] = whitelist[_investors[i]].expiryTime;
if (whitelist[_investors[i]].canBuyFromSTO == 0) {
canBuyFromSTOs[i] = false;
} else {
canBuyFromSTOs[i] = true;
}
}
return (fromTimes, toTimes, expiryTimes, canBuyFromSTOs);
return (canSendAfters, canReceiveAfters, expiryTimes, canBuyFromSTOs);
}

/**
Expand Down
Loading