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

GTM logic refactored #572

Merged
merged 13 commits into from
Mar 5, 2019
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ All notable changes to this project will be documented in this file.
* `modifyWhitelist()` function renamed to `modifyKYCData()`.
* Added functions to modify and get flags
* `canBuyFromSto` is now `canNotBuyFromSto` and it is the flag `1`
* GTM logic reworked. Now, instead of flags like allowAllTransfers, there is a matrix of transfer requirements that must be fulfilled based on type of transfer.

## Generalize
* Removed `_polyAddress` parameter from constructors of all modules and module factories.
Expand Down
209 changes: 116 additions & 93 deletions contracts/modules/TransferManager/GTM/GeneralTransferManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,7 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, TransferManage

// Emit when Issuance address get changed
event ChangeIssuanceAddress(address _issuanceAddress);
// Emit when there is change in the flag variable called allowAllTransfers
event AllowAllTransfers(bool _allowAllTransfers);
// Emit when there is change in the flag variable called allowAllWhitelistTransfers
event AllowAllWhitelistTransfers(bool _allowAllWhitelistTransfers);
// Emit when there is change in the flag variable called allowAllWhitelistIssuances
event AllowAllWhitelistIssuances(bool _allowAllWhitelistIssuances);
// Emit when there is change in the flag variable called allowAllBurnTransfers
event AllowAllBurnTransfers(bool _allowAllBurnTransfers);

// Emit when investor details get modified related to their whitelisting
event ChangeDefaults(uint64 _defaultFromTime, uint64 _defaultToTime);

Expand All @@ -46,6 +39,14 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, TransferManage
bool _value
);

event ModifyTransferRequirements(
uint256 indexed _transferType,
bool _fromValidKYC,
bool _toValidKYC,
bool _fromRestricted,
bool _toRestricted
);

/**
* @notice Constructor
* @param _securityToken Address of the security token
Expand Down Expand Up @@ -84,50 +85,6 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, TransferManage
emit ChangeIssuanceAddress(_issuanceAddress);
}

/**
* @notice Used to change the flag
true - It refers there are no transfer restrictions, for any addresses
false - It refers transfers are restricted for all addresses.
* @param _allowAllTransfers flag value
*/
function changeAllowAllTransfers(bool _allowAllTransfers) public withPerm(ADMIN) {
allowAllTransfers = _allowAllTransfers;
emit AllowAllTransfers(_allowAllTransfers);
}

/**
* @notice Used to change the flag
true - It refers that time lock is ignored for transfers (address must still be on whitelist)
false - It refers transfers are restricted for all addresses.
* @param _allowAllWhitelistTransfers flag value
*/
function changeAllowAllWhitelistTransfers(bool _allowAllWhitelistTransfers) public withPerm(ADMIN) {
allowAllWhitelistTransfers = _allowAllWhitelistTransfers;
emit AllowAllWhitelistTransfers(_allowAllWhitelistTransfers);
}

/**
* @notice Used to change the flag
true - It refers that time lock is ignored for issuances (address must still be on whitelist)
false - It refers transfers are restricted for all addresses.
* @param _allowAllWhitelistIssuances flag value
*/
function changeAllowAllWhitelistIssuances(bool _allowAllWhitelistIssuances) public withPerm(ADMIN) {
allowAllWhitelistIssuances = _allowAllWhitelistIssuances;
emit AllowAllWhitelistIssuances(_allowAllWhitelistIssuances);
}

/**
* @notice Used to change the flag
true - It allow to burn the tokens
false - It deactivate the burning mechanism.
* @param _allowAllBurnTransfers flag value
*/
function changeAllowAllBurnTransfers(bool _allowAllBurnTransfers) public withPerm(ADMIN) {
allowAllBurnTransfers = _allowAllBurnTransfers;
emit AllowAllBurnTransfers(_allowAllBurnTransfers);
}

/**
* @notice Default implementation of verifyTransfer used by SecurityToken
* If the transfer request comes from the STO, it only checks that the investor is in the whitelist
Expand All @@ -150,67 +107,133 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, TransferManage

/**
* @notice Default implementation of verifyTransfer used by SecurityToken
* If the transfer request comes from the STO, it only checks that the investor is in the whitelist
* If the transfer request comes from a token holder, it checks that:
* a) Both are on the whitelist
* b) Seller's sale lockup period is over
* c) Buyer's purchase lockup is over
* @param _from Address of the sender
* @param _to Address of the receiver
*/
function verifyTransfer(
address _from,
address _to,
uint256, /*_amount*/
uint256 /*_amount*/,
bytes memory /* _data */
)
public
view
returns(Result, bytes32)
{
Result success;
if (!paused) {
TransferRequirements memory txReq;
uint64 fromTime;
uint64 fromExpiry;
uint64 toExpiry;
uint64 toTime;
if (allowAllTransfers) {
//All transfers allowed, regardless of whitelist
return (Result.VALID, getAddressBytes32());
}
if (allowAllBurnTransfers && (_to == address(0))) {
return (Result.VALID, getAddressBytes32());

if (_from == issuanceAddress) {
txReq = transferRequirements[1]; //Issuance
} else if (_to == address(0)) {
txReq = transferRequirements[2]; //Redemption
} else {
txReq = transferRequirements[0]; //General Transfer
}

(fromTime, fromExpiry, toTime, toExpiry) = _getValuesForTransfer(_from, _to);

if (allowAllWhitelistTransfers) {
//Anyone on the whitelist can transfer, regardless of time
success = (_validExpiry(toExpiry) && _validExpiry(fromExpiry)) ? Result.VALID : Result.NA;
return (success, success == Result.VALID ? getAddressBytes32() : bytes32(0));
if ((txReq.fromValidKYC && !_validExpiry(fromExpiry)) || (txReq.toValidKYC && !_validExpiry(toExpiry))) {
return (Result.NA, bytes32(0));
}
// Using the local variables to avoid the stack too deep error

(fromTime, toTime) = _adjustTimes(fromTime, toTime);
if (_from == issuanceAddress) {
// if allowAllWhitelistIssuances is true, so time stamp ignored
if (allowAllWhitelistIssuances) {
success = _validExpiry(toExpiry) ? Result.VALID : Result.NA;
return (success, success == Result.VALID ? getAddressBytes32() : bytes32(0));
} else {
success = (_validExpiry(toExpiry) && _validLockTime(toTime)) ? Result.VALID : Result.NA;
return (success, success == Result.VALID ? getAddressBytes32() : bytes32(0));
}

if ((txReq.fromRestricted && !_validLockTime(fromTime)) || (txReq.toRestricted && !_validLockTime(toTime))) {
return (Result.NA, bytes32(0));
}

//Anyone on the whitelist can transfer provided the blocknumber is large enough
/*solium-disable-next-line security/no-block-members*/
success = (_validExpiry(fromExpiry) && _validLockTime(fromTime) && _validExpiry(toExpiry) &&
_validLockTime(toTime)) ? Result.VALID : Result.NA; /*solium-disable-line security/no-block-members*/
return (success, success == Result.VALID ? getAddressBytes32() : bytes32(0));
return (Result.VALID, getAddressBytes32());
}
return (Result.NA, bytes32(0));
}

/**
* @notice Modifies the successful checks required for a transfer to be deemed valid.
* @param _transferType Type of transfer (0 = General, 1 = Issuance, 2 = Redemption)
* @param _fromValidKYC Defines if KYC is required for the sender
* @param _toValidKYC Defines if KYC is required for the receiver
* @param _fromRestricted Defines if transfer time restriction is checked for the sender
* @param _toRestricted Defines if transfer time restriction is checked for the receiver
*/
function modifyTransferRequirements(
uint256 _transferType,
bool _fromValidKYC,
bool _toValidKYC,
bool _fromRestricted,
bool _toRestricted
) public withPerm(ADMIN) {
_modifyTransferRequirements(
_transferType,
_fromValidKYC,
_toValidKYC,
_fromRestricted,
_toRestricted
);
}

/**
* @notice Modifies the successful checks required for transfers.
* @param _transferType Type of transfer (0 = General, 1 = Issuance, 2 = Redemption)
* @param _fromValidKYC Defines if KYC is required for the sender
* @param _toValidKYC Defines if KYC is required for the receiver
* @param _fromRestricted Defines if transfer time restriction is checked for the sender
* @param _toRestricted Defines if transfer time restriction is checked for the receiver
*/
function modifyTransferRequirementsMulti(
uint256[] memory _transferType,
maxsam4 marked this conversation as resolved.
Show resolved Hide resolved
bool[] memory _fromValidKYC,
bool[] memory _toValidKYC,
bool[] memory _fromRestricted,
bool[] memory _toRestricted
) public withPerm(ADMIN) {
require(
_transferType.length == _fromValidKYC.length &&
_fromValidKYC.length == _toValidKYC.length &&
_toValidKYC.length == _fromRestricted.length &&
_fromRestricted.length == _toRestricted.length,
"Mismatched input lengths"
);

for (uint256 i = 0; i < _transferType.length; i++) {
_modifyTransferRequirements(
_transferType[i],
_fromValidKYC[i],
_toValidKYC[i],
_fromRestricted[i],
_toRestricted[i]
);
}
}

function _modifyTransferRequirements(
uint256 _transferType,
bool _fromValidKYC,
bool _toValidKYC,
bool _fromRestricted,
bool _toRestricted
) internal {
transferRequirements[_transferType] =
maxsam4 marked this conversation as resolved.
Show resolved Hide resolved
TransferRequirements(
_fromValidKYC,
_toValidKYC,
_fromRestricted,
_toRestricted
);

emit ModifyTransferRequirements(
_transferType,
_fromValidKYC,
_toValidKYC,
_fromRestricted,
_toRestricted
);
}


/**
* @notice Add or remove KYC info of an investor.
Expand Down Expand Up @@ -380,31 +403,31 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, TransferManage
* @notice Internal function used to check whether the KYC of investor is valid
* @param _expiryTime Expiry time of the investor
*/
function _validExpiry(uint64 _expiryTime) internal view returns(bool) {
return (_expiryTime >= uint64(now)); /*solium-disable-line security/no-block-members*/
function _validExpiry(uint64 _expiryTime) internal view returns(bool valid) {
if (_expiryTime >= uint64(now)) /*solium-disable-line security/no-block-members*/
valid = true;
}

/**
* @notice Internal function used to check whether the lock time of investor is valid
* @param _lockTime Lock time of the investor
*/
function _validLockTime(uint64 _lockTime) internal view returns(bool) {
return (_lockTime <= uint64(now)); /*solium-disable-line security/no-block-members*/
function _validLockTime(uint64 _lockTime) internal view returns(bool valid) {
if (_lockTime <= uint64(now)) /*solium-disable-line security/no-block-members*/
valid = true;
}

/**
* @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;
_fromTime = defaults.fromTime;
}
if (_toTime == 0) {
adjustedToTime = defaults.toTime;
_toTime = defaults.toTime;
}
return (adjustedFromTime, adjustedToTime);
return (_fromTime, _toTime);
}

function _getKey(bytes32 _key1, address _key2) internal pure returns(bytes32) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ contract GeneralTransferManagerProxy is GeneralTransferManagerStorage, ModuleSto
{
require(_implementation != address(0), "Implementation address should not be 0x");
_upgradeTo(_version, _implementation);
transferRequirements[0] = TransferRequirements(true, true, true, true);
transferRequirements[1] = TransferRequirements(false, true, false, false);
transferRequirements[2] = TransferRequirements(true, false, false, false);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,6 @@ contract GeneralTransferManagerStorage {
//Address from which issuances come
address public issuanceAddress;

// //from and to timestamps that an investor can send / receive tokens respectively
// // Now Stored in DataStore
// struct TimeRestriction {
// uint64 fromTime;
// uint64 toTime;
// uint64 expiryTime;
// uint8 added;
// }

// Allows all TimeRestrictions to be offset
struct Defaults {
uint64 fromTime;
Expand All @@ -34,13 +25,13 @@ contract GeneralTransferManagerStorage {
// Map of used nonces by customer
mapping(address => mapping(uint256 => bool)) public nonceMap;

//If true, there are no transfer restrictions, for any addresses
bool public allowAllTransfers = false;
//If true, time lock is ignored for transfers (address must still be on whitelist)
bool public allowAllWhitelistTransfers = false;
//If true, time lock is ignored for issuances (address must still be on whitelist)
bool public allowAllWhitelistIssuances = true;
//If true, time lock is ignored for burn transactions
bool public allowAllBurnTransfers = false;
struct TransferRequirements {
bool fromValidKYC;
bool toValidKYC;
bool fromRestricted;
bool toRestricted;
}

mapping(uint256 => TransferRequirements) public transferRequirements;
// General = 0, Issuance = 1, Redemption = 2
}
Loading