Skip to content

Commit dede0da

Browse files
authored
Merge pull request #559 from PolymathNetwork/gtm-signed-transfer
GTM KYC during transfer
2 parents 84f01b0 + 5dcfe29 commit dede0da

File tree

6 files changed

+474
-30
lines changed

6 files changed

+474
-30
lines changed

contracts/modules/Experimental/TransferManager/SignedTransferManager.sol

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ contract SignedTransferManager is TransferManager {
4343
function checkSignatureValidity(bytes calldata _data) external view returns(bool) {
4444
address targetAddress;
4545
uint256 nonce;
46+
uint256 validFrom;
4647
uint256 expiry;
4748
bytes memory signature;
48-
(targetAddress, nonce, expiry, signature) = abi.decode(_data, (address, uint256, uint256, bytes));
49-
if (targetAddress != address(this) || expiry < now || signature.length == 0 || _checkSignatureIsInvalid(signature))
49+
(targetAddress, nonce, validFrom, expiry, signature) = abi.decode(_data, (address, uint256, uint256, uint256, bytes));
50+
if (targetAddress != address(this) || expiry < now || validFrom > now || signature.length == 0 || _checkSignatureIsInvalid(signature))
5051
return false;
5152
return true;
5253
}
@@ -66,9 +67,9 @@ contract SignedTransferManager is TransferManager {
6667
*/
6768
function executeTransfer(address _from, address _to, uint256 _amount, bytes calldata _data) external onlySecurityToken returns(Result) {
6869
(Result success, ) = verifyTransfer(_from, _to, _amount, _data);
69-
if (success == Result.VALID) {
70+
if (success == Result.VALID && _data.length > 32) {
7071
bytes memory signature;
71-
(,,,signature) = abi.decode(_data, (address, uint256, uint256, bytes));
72+
(,,,,signature) = abi.decode(_data, (address, uint256, uint256, uint256, bytes));
7273
_invalidateSignature(signature);
7374
}
7475
return success;
@@ -86,19 +87,20 @@ contract SignedTransferManager is TransferManager {
8687
function verifyTransfer(address _from, address _to, uint256 _amount, bytes memory _data) public view returns(Result, bytes32) {
8788
if (!paused) {
8889

89-
if (_data.length == 0)
90+
if (_data.length <= 32)
9091
return (Result.NA, bytes32(0));
9192

9293
address targetAddress;
9394
uint256 nonce;
95+
uint256 validFrom;
9496
uint256 expiry;
9597
bytes memory signature;
96-
(targetAddress, nonce, expiry, signature) = abi.decode(_data, (address, uint256, uint256, bytes));
98+
(targetAddress, nonce, validFrom, expiry, signature) = abi.decode(_data, (address, uint256, uint256, uint256, bytes));
9799

98-
if (address(this) != targetAddress || signature.length == 0 || _checkSignatureIsInvalid(signature) || expiry < now)
100+
if (address(this) != targetAddress || signature.length == 0 || _checkSignatureIsInvalid(signature) || expiry < now || validFrom > now)
99101
return (Result.NA, bytes32(0));
100102

101-
bytes32 hash = keccak256(abi.encodePacked(targetAddress, nonce, expiry, _from, _to, _amount));
103+
bytes32 hash = keccak256(abi.encodePacked(targetAddress, nonce, validFrom, expiry, _from, _to, _amount));
102104
address signer = hash.toEthSignedMessageHash().recover(signature);
103105

104106
if (!_checkSigner(signer))
@@ -122,14 +124,15 @@ contract SignedTransferManager is TransferManager {
122124

123125
address targetAddress;
124126
uint256 nonce;
127+
uint256 validFrom;
125128
uint256 expiry;
126129
bytes memory signature;
127-
(targetAddress, nonce, expiry, signature) = abi.decode(_data, (address, uint256, uint256, bytes));
130+
(targetAddress, nonce, validFrom, expiry, signature) = abi.decode(_data, (address, uint256, uint256, uint256, bytes));
128131

129132
require(!_checkSignatureIsInvalid(signature), "Signature already invalid");
130133
require(targetAddress == address(this), "Signature not for this module");
131134

132-
bytes32 hash = keccak256(abi.encodePacked(targetAddress, nonce, expiry, _from, _to, _amount));
135+
bytes32 hash = keccak256(abi.encodePacked(targetAddress, nonce, validFrom, expiry, _from, _to, _amount));
133136
require(hash.toEthSignedMessageHash().recover(signature) == msg.sender, "Incorrect Signer");
134137

135138
_invalidateSignature(signature);

contracts/modules/TransferManager/GTM/GeneralTransferManager.sol

Lines changed: 121 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,29 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, TransferManage
101101
uint256 _amount,
102102
bytes calldata _data
103103
) external returns(Result) {
104-
(Result success,) = verifyTransfer(_from, _to, _amount, _data);
105-
return success;
104+
if (_data.length > 32) {
105+
address target;
106+
uint256 nonce;
107+
uint256 validFrom;
108+
uint256 validTo;
109+
bytes memory data;
110+
(target, nonce, validFrom, validTo, data) = abi.decode(_data, (address, uint256, uint256, uint256, bytes));
111+
if (target == address(this))
112+
_processTransferSignature(nonce, validFrom, validTo, data);
113+
}
114+
(Result success,) = verifyTransfer(_from, _to, _amount, _data);
115+
return success;
116+
}
117+
118+
function _processTransferSignature(uint256 _nonce, uint256 _validFrom, uint256 _validTo, bytes memory _data) internal {
119+
address[] memory investor;
120+
uint256[] memory fromTime;
121+
uint256[] memory toTime;
122+
uint256[] memory expiryTime;
123+
bytes memory signature;
124+
(investor, fromTime, toTime, expiryTime, signature) =
125+
abi.decode(_data, (address[], uint256[], uint256[], uint256[], bytes));
126+
_modifyKYCDataSignedMulti(investor, fromTime, toTime, expiryTime, _validFrom, _validTo, _nonce, signature);
106127
}
107128

108129
/**
@@ -376,28 +397,117 @@ contract GeneralTransferManager is GeneralTransferManagerStorage, TransferManage
376397
bytes memory _signature
377398
)
378399
public
400+
{
401+
require(
402+
_modifyKYCDataSigned(_investor, _fromTime, _toTime, _expiryTime, _validFrom, _validTo, _nonce, _signature),
403+
"Invalid signature or data"
404+
);
405+
}
406+
407+
function _modifyKYCDataSigned(
408+
address _investor,
409+
uint256 _fromTime,
410+
uint256 _toTime,
411+
uint256 _expiryTime,
412+
uint256 _validFrom,
413+
uint256 _validTo,
414+
uint256 _nonce,
415+
bytes memory _signature
416+
)
417+
internal
418+
returns(bool)
379419
{
380420
/*solium-disable-next-line security/no-block-members*/
381-
require(_validFrom <= now, "ValidFrom is too early");
382-
/*solium-disable-next-line security/no-block-members*/
383-
require(_validTo >= now, "ValidTo is too late");
384-
require(!nonceMap[_investor][_nonce], "Already used signature");
385-
nonceMap[_investor][_nonce] = true;
421+
if(_validFrom > now || _validTo < now || _investor == address(0))
422+
return false;
386423
bytes32 hash = keccak256(
387424
abi.encodePacked(this, _investor, _fromTime, _toTime, _expiryTime, _validFrom, _validTo, _nonce)
388425
);
389-
_checkSig(hash, _signature);
390-
_modifyKYCData(_investor, _fromTime, _toTime, _expiryTime);
426+
if (_checkSig(hash, _signature, _nonce)) {
427+
_modifyKYCData(_investor, _fromTime, _toTime, _expiryTime);
428+
return true;
429+
}
430+
return false;
431+
}
432+
433+
/**
434+
* @notice Adds or removes addresses from the whitelist - can be called by anyone with a valid signature
435+
* @param _investor is the address to whitelist
436+
* @param _fromTime is the moment when the sale lockup period ends and the investor can freely sell his tokens
437+
* @param _toTime is the moment when the purchase lockup period ends and the investor can freely purchase tokens from others
438+
* @param _expiryTime is the moment till investors KYC will be validated. After that investor need to do re-KYC
439+
* @param _validFrom is the time that this signature is valid from
440+
* @param _validTo is the time that this signature is valid until
441+
* @param _nonce nonce of signature (avoid replay attack)
442+
* @param _signature issuer signature
443+
*/
444+
function modifyKYCDataSignedMulti(
445+
address[] memory _investor,
446+
uint256[] memory _fromTime,
447+
uint256[] memory _toTime,
448+
uint256[] memory _expiryTime,
449+
uint256 _validFrom,
450+
uint256 _validTo,
451+
uint256 _nonce,
452+
bytes memory _signature
453+
)
454+
public
455+
{
456+
require(
457+
_modifyKYCDataSignedMulti(_investor, _fromTime, _toTime, _expiryTime, _validFrom, _validTo, _nonce, _signature),
458+
"Invalid signature or data"
459+
);
460+
}
461+
462+
function _modifyKYCDataSignedMulti(
463+
address[] memory _investor,
464+
uint256[] memory _fromTime,
465+
uint256[] memory _toTime,
466+
uint256[] memory _expiryTime,
467+
uint256 _validFrom,
468+
uint256 _validTo,
469+
uint256 _nonce,
470+
bytes memory _signature
471+
)
472+
internal
473+
returns(bool)
474+
{
475+
if (_investor.length != _fromTime.length ||
476+
_fromTime.length != _toTime.length ||
477+
_toTime.length != _expiryTime.length
478+
) {
479+
return false;
480+
}
481+
482+
if (_validFrom > now || _validTo < now) {
483+
return false;
484+
}
485+
486+
bytes32 hash = keccak256(
487+
abi.encodePacked(this, _investor, _fromTime, _toTime, _expiryTime, _validFrom, _validTo, _nonce)
488+
);
489+
490+
if (_checkSig(hash, _signature, _nonce)) {
491+
for (uint256 i = 0; i < _investor.length; i++) {
492+
_modifyKYCData(_investor[i], _fromTime[i], _toTime[i], _expiryTime[i]);
493+
}
494+
return true;
495+
}
496+
return false;
391497
}
392498

393499
/**
394500
* @notice Used to verify the signature
395501
*/
396-
function _checkSig(bytes32 _hash, bytes memory _signature) internal view {
502+
function _checkSig(bytes32 _hash, bytes memory _signature, uint256 _nonce) internal returns(bool) {
397503
//Check that the signature is valid
398504
//sig should be signing - _investor, _fromTime, _toTime & _expiryTime and be signed by the issuer address
399505
address signer = _hash.toEthSignedMessageHash().recover(_signature);
400-
require(_checkPerm(OPERATOR, signer), "Incorrect signer");
506+
if (nonceMap[signer][_nonce] || !_checkPerm(OPERATOR, signer)) {
507+
return false;
508+
}
509+
nonceMap[signer][_nonce] = true;
510+
return true;
401511
}
402512

403513
/**

0 commit comments

Comments
 (0)