Skip to content

Commit 95be076

Browse files
authored
Fix canTransfer spec (#709)
* Fix spec * Fix conflict * Fix merge conflict * Small fixes * Revert package.json change * Fix test case
1 parent 884d5a6 commit 95be076

File tree

7 files changed

+55
-45
lines changed

7 files changed

+55
-45
lines changed

contracts/interfaces/ISecurityToken.sol

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,19 @@ interface ISecurityToken {
1919
event Transfer(address indexed from, address indexed to, uint256 value);
2020
event Approval(address indexed owner, address indexed spender, uint256 value);
2121

22-
// Emit at the time when module get added
22+
/**
23+
* @notice Transfers of securities may fail for a number of reasons. So this function will used to understand the
24+
* cause of failure by getting the byte value. Which will be the ESC that follows the EIP 1066. ESC can be mapped
25+
* with a reson string to understand the failure cause, table of Ethereum status code will always reside off-chain
26+
* @param _to address The address which you want to transfer to
27+
* @param _value uint256 the amount of tokens to be transferred
28+
* @param _data The `bytes _data` allows arbitrary data to be submitted alongside the transfer.
29+
* @return byte Ethereum status code (ESC)
30+
* @return bytes32 Application specific reason code
31+
*/
32+
function canTransfer(address _to, uint256 _value, bytes calldata _data) external view returns (byte statusCode, bytes32 reasonCode);
33+
34+
// Emit at the time when module get added
2335
event ModuleAdded(
2436
uint8[] _types,
2537
bytes32 indexed _name,
@@ -114,19 +126,6 @@ interface ISecurityToken {
114126
*/
115127
function initialize(address _getterDelegate) external;
116128

117-
/**
118-
* @notice Transfers of securities may fail for a number of reasons. So this function will used to understand the
119-
* cause of failure by getting the byte value. Which will be the ESC that follows the EIP 1066. ESC can be mapped
120-
* with a reson string to understand the failure cause, table of Ethereum status code will always reside off-chain
121-
* @param _to address The address which you want to transfer to
122-
* @param _value uint256 the amount of tokens to be transferred
123-
* @param _data The `bytes _data` allows arbitrary data to be submitted alongside the transfer.
124-
* @return bool It signifies whether the transaction will be executed or not.
125-
* @return byte Ethereum status code (ESC)
126-
* @return bytes32 Application specific reason code
127-
*/
128-
function canTransfer(address _to, uint256 _value, bytes calldata _data) external view returns (bool isExecuted, byte statusCode, bytes32 reasonCode);
129-
130129
/**
131130
* @notice The standard provides an on-chain function to determine whether a transfer will succeed,
132131
* and return details indicating the reason if the transfer is not valid.
@@ -158,11 +157,10 @@ interface ISecurityToken {
158157
* @param _to address The address which you want to transfer to
159158
* @param _value uint256 the amount of tokens to be transferred
160159
* @param _data The `bytes _data` allows arbitrary data to be submitted alongside the transfer.
161-
* @return bool It signifies whether the transaction will be executed or not.
162160
* @return byte Ethereum status code (ESC)
163161
* @return bytes32 Application specific reason code
164162
*/
165-
function canTransferFrom(address _from, address _to, uint256 _value, bytes calldata _data) external view returns (bool isExecuted, byte statusCode, bytes32 reasonCode);
163+
function canTransferFrom(address _from, address _to, uint256 _value, bytes calldata _data) external view returns (byte statusCode, bytes32 reasonCode);
166164

167165
/**
168166
* @notice Used to attach a new document to the contract, or update the URI or hash of an existing attached document

contracts/interfaces/token/IERC1594.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ interface IERC1594 {
1919
function redeemFrom(address _tokenHolder, uint256 _value, bytes calldata _data) external;
2020

2121
// Transfer Validity
22-
function canTransfer(address _to, uint256 _value, bytes calldata _data) external view returns (bool, byte, bytes32);
23-
function canTransferFrom(address _from, address _to, uint256 _value, bytes calldata _data) external view returns (bool, byte, bytes32);
22+
function canTransfer(address _to, uint256 _value, bytes calldata _data) external view returns (byte, bytes32);
23+
function canTransferFrom(address _from, address _to, uint256 _value, bytes calldata _data) external view returns (byte, bytes32);
2424

2525
// Issuance / Redemption Events
2626
event Issued(address indexed _operator, address indexed _to, uint256 _value, bytes _data);

contracts/libraries/TokenLib.sol

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -463,22 +463,22 @@ library TokenLib {
463463
)
464464
external
465465
pure
466-
returns (bool, byte, bytes32)
466+
returns (byte, bytes32)
467467
{
468468
if (!success)
469-
return (false, StatusCodes.code(StatusCodes.Status.TransferFailure), appCode);
469+
return (StatusCodes.code(StatusCodes.Status.TransferFailure), appCode);
470470

471471
if (balanceOfFrom < value)
472-
return (false, StatusCodes.code(StatusCodes.Status.InsufficientBalance), bytes32(0));
472+
return (StatusCodes.code(StatusCodes.Status.InsufficientBalance), bytes32(0));
473473

474474
if (to == address(0))
475-
return (false, StatusCodes.code(StatusCodes.Status.InvalidReceiver), bytes32(0));
475+
return (StatusCodes.code(StatusCodes.Status.InvalidReceiver), bytes32(0));
476476

477477
// Balance overflow can never happen due to totalsupply being a uint256 as well
478478
// else if (!KindMath.checkAdd(balanceOf(_to), _value))
479-
// return (false, 0x50, bytes32(0));
479+
// return (0x50, bytes32(0));
480480

481-
return (true, StatusCodes.code(StatusCodes.Status.TransferSuccess), bytes32(0));
481+
return (StatusCodes.code(StatusCodes.Status.TransferSuccess), bytes32(0));
482482
}
483483

484484
function _getKey(bytes32 _key1, address _key2) internal pure returns(bytes32) {

contracts/modules/STO/USDTiered/USDTieredSTO.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ contract USDTieredSTO is USDTieredSTOStorage, STO {
720720
* @notice Return the permissions flag that are associated with STO
721721
*/
722722
function getPermissions() public view returns(bytes32[] memory allPermissions) {
723-
bytes32[] memory allPermissions = new bytes32[](2);
723+
allPermissions = new bytes32[](2);
724724
allPermissions[0] = OPERATOR;
725725
allPermissions[1] = ADMIN;
726726
return allPermissions;

contracts/tokens/SecurityToken.sol

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -911,11 +911,10 @@ contract SecurityToken is ERC20, ReentrancyGuard, SecurityTokenStorage, IERC1594
911911
* @param _to address The address which you want to transfer to
912912
* @param _value uint256 the amount of tokens to be transferred
913913
* @param _data The `bytes _data` allows arbitrary data to be submitted alongside the transfer.
914-
* @return bool It signifies whether the transaction will be executed or not.
915914
* @return byte Ethereum status code (ESC)
916915
* @return bytes32 Application specific reason code
917916
*/
918-
function canTransfer(address _to, uint256 _value, bytes calldata _data) external view returns (bool, byte, bytes32) {
917+
function canTransfer(address _to, uint256 _value, bytes calldata _data) external view returns (byte, bytes32) {
919918
return _canTransfer(msg.sender, _to, _value, _data);
920919
}
921920

@@ -927,22 +926,21 @@ contract SecurityToken is ERC20, ReentrancyGuard, SecurityTokenStorage, IERC1594
927926
* @param _to address The address which you want to transfer to
928927
* @param _value uint256 the amount of tokens to be transferred
929928
* @param _data The `bytes _data` allows arbitrary data to be submitted alongside the transfer.
930-
* @return bool It signifies whether the transaction will be executed or not.
931929
* @return byte Ethereum status code (ESC)
932930
* @return bytes32 Application specific reason code
933931
*/
934-
function canTransferFrom(address _from, address _to, uint256 _value, bytes calldata _data) external view returns (bool success, byte reasonCode, bytes32 appCode) {
935-
(success, reasonCode, appCode) = _canTransfer(_from, _to, _value, _data);
936-
if (success && _value > allowance(_from, msg.sender)) {
937-
return (false, StatusCodes.code(StatusCodes.Status.InsufficientAllowance), bytes32(0));
932+
function canTransferFrom(address _from, address _to, uint256 _value, bytes calldata _data) external view returns (byte reasonCode, bytes32 appCode) {
933+
(reasonCode, appCode) = _canTransfer(_from, _to, _value, _data);
934+
if (isSuccess(reasonCode) && _value > allowance(_from, msg.sender)) {
935+
return (StatusCodes.code(StatusCodes.Status.InsufficientAllowance), bytes32(0));
938936
}
939937
}
940938

941-
function _canTransfer(address _from, address _to, uint256 _value, bytes memory _data) internal view returns (bool, byte, bytes32) {
939+
function _canTransfer(address _from, address _to, uint256 _value, bytes memory _data) internal view returns (byte, bytes32) {
942940
bytes32 appCode;
943941
bool success;
944942
if (_value % granularity != 0) {
945-
return (false, StatusCodes.code(StatusCodes.Status.TransferFailure), bytes32(0));
943+
return (StatusCodes.code(StatusCodes.Status.TransferFailure), bytes32(0));
946944
}
947945
(success, appCode) = TokenLib.verifyTransfer(modules[TRANSFER_KEY], modulesToData, _from, _to, _value, _data, transfersFrozen);
948946
return TokenLib.canTransfer(success, appCode, _to, _value, balanceOf(_from));
@@ -969,17 +967,16 @@ contract SecurityToken is ERC20, ReentrancyGuard, SecurityTokenStorage, IERC1594
969967
)
970968
external
971969
view
972-
returns (byte esc, bytes32 appStatusCode, bytes32 toPartition)
970+
returns (byte reasonCode, bytes32 appStatusCode, bytes32 toPartition)
973971
{
974972
if (_partition == UNLOCKED) {
975-
bool success;
976-
(success, esc, appStatusCode) = _canTransfer(_from, _to, _value, _data);
977-
if (success) {
973+
(reasonCode, appStatusCode) = _canTransfer(_from, _to, _value, _data);
974+
if (isSuccess(reasonCode)) {
978975
uint256 beforeBalance = _balanceOfByPartition(LOCKED, _to, 0);
979976
uint256 afterbalance = _balanceOfByPartition(LOCKED, _to, _value);
980977
toPartition = _returnPartition(beforeBalance, afterbalance, _value);
981978
}
982-
return (esc, appStatusCode, toPartition);
979+
return (reasonCode, appStatusCode, toPartition);
983980
}
984981
return (StatusCodes.code(StatusCodes.Status.TransferFailure), bytes32(0), bytes32(0));
985982
}
@@ -1100,4 +1097,13 @@ contract SecurityToken is ERC20, ReentrancyGuard, SecurityTokenStorage, IERC1594
11001097
emit OwnershipTransferred(_owner, newOwner);
11011098
_owner = newOwner;
11021099
}
1100+
1101+
/**
1102+
* @dev Check if a status code represents success (ie: 0x*1)
1103+
* @param status Binary ERC-1066 status code
1104+
* @return successful A boolean representing if the status code represents success
1105+
*/
1106+
function isSuccess(byte status) public pure returns (bool successful) {
1107+
return (status & 0x0F) == 0x01;
1108+
}
11031109
}

test/j_manual_approval_transfer_manager.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ const Web3 = require("web3");
1616
let BN = Web3.utils.BN;
1717
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port
1818

19+
const SUCCESS_CODE = 0x51;
20+
const FAILURE_CODE = 0x50;
21+
22+
1923
contract("ManualApprovalTransferManager", accounts => {
2024
// Accounts Variable declaration
2125
let account_polymath;
@@ -396,18 +400,20 @@ contract("ManualApprovalTransferManager", accounts => {
396400
from: account_investor1
397401
}
398402
);
399-
console.log(JSON.stringify(verified[0]));
400-
assert.equal(verified[0], true);
403+
// console.log(JSON.stringify(verified[0]));
404+
assert.equal(verified[0], SUCCESS_CODE);
401405

402406
verified = await I_SecurityToken.canTransfer.call(account_investor4, web3.utils.toWei("4", "ether"), "0x0", {
403407
from: account_investor1
404408
});
405-
assert.equal(verified[0], false);
409+
// console.log(JSON.stringify(verified[0]));
410+
assert.equal(verified[0], FAILURE_CODE);
406411

407412
verified = await I_SecurityToken.canTransfer.call(account_investor4, web3.utils.toWei("1", "ether"), "0x0", {
408413
from: account_investor1
409414
});
410-
assert.equal(verified[0], true);
415+
// console.log(JSON.stringify(verified[0]));
416+
assert.equal(verified[0], SUCCESS_CODE);
411417
});
412418

413419
it("Should fail to sell the tokens more than the allowance", async() => {

test/o_security_token.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ contract("SecurityToken", async (accounts) => {
525525
let tx = await I_SecurityToken.removeModule(I_GeneralTransferManager.address, { from: token_owner });
526526
assert.equal(tx.logs[0].args._types[0], transferManagerKey);
527527
assert.equal(tx.logs[0].args._module, I_GeneralTransferManager.address);
528+
528529
await revertToSnapshot(key);
529530
});
530531

@@ -666,8 +667,7 @@ contract("SecurityToken", async (accounts) => {
666667
it("Should Fail in transferring the token from one whitelist investor 1 to non whitelist investor 2", async () => {
667668
let _canTransfer = await I_SecurityToken.canTransfer.call(account_investor2, new BN(10).mul(new BN(10).pow(new BN(18))), "0x0", {from: account_investor1});
668669

669-
assert.isFalse(_canTransfer[0]);
670-
assert.equal(_canTransfer[1], 0x50);
670+
assert.equal(_canTransfer[0], 0x50);
671671

672672
await catchRevert(I_SecurityToken.transfer(account_investor2, new BN(10).mul(new BN(10).pow(new BN(18))), { from: account_investor1 }));
673673
});

0 commit comments

Comments
 (0)