From 631d2b107eb85fc7535d210ae4809942528a6525 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 22 Nov 2018 11:38:47 +0000 Subject: [PATCH 1/2] Allow 0x transfers for MATM --- .../modules/ManualApprovalTransferManager.sol | 169 ++++++++++++++++++ test/j_manual_approval_transfer_manager.js | 47 ++--- 2 files changed, 188 insertions(+), 28 deletions(-) create mode 100644 contracts/modules/ManualApprovalTransferManager.sol diff --git a/contracts/modules/ManualApprovalTransferManager.sol b/contracts/modules/ManualApprovalTransferManager.sol new file mode 100644 index 000000000..d95241e9a --- /dev/null +++ b/contracts/modules/ManualApprovalTransferManager.sol @@ -0,0 +1,169 @@ +pragma solidity ^0.4.24; + +import "./ITransferManager.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; + +/** + * @title Transfer Manager module for manually approving or blocking transactions between accounts + */ +contract ManualApprovalTransferManager is ITransferManager { + using SafeMath for uint256; + + //Address from which issuances come + address public issuanceAddress = address(0); + + //Address which can sign whitelist changes + address public signingAddress = address(0); + + bytes32 public constant TRANSFER_APPROVAL = "TRANSFER_APPROVAL"; + + //Manual approval is an allowance (that has been approved) with an expiry time + struct ManualApproval { + uint256 allowance; + uint256 expiryTime; + } + + //Manual blocking allows you to specify a list of blocked address pairs with an associated expiry time for the block + struct ManualBlocking { + uint256 expiryTime; + } + + //Store mappings of address => address with ManualApprovals + mapping (address => mapping (address => ManualApproval)) public manualApprovals; + + //Store mappings of address => address with ManualBlockings + mapping (address => mapping (address => ManualBlocking)) public manualBlockings; + + event AddManualApproval( + address indexed _from, + address indexed _to, + uint256 _allowance, + uint256 _expiryTime, + address indexed _addedBy + ); + + event AddManualBlocking( + address indexed _from, + address indexed _to, + uint256 _expiryTime, + address indexed _addedBy + ); + + event RevokeManualApproval( + address indexed _from, + address indexed _to, + address indexed _addedBy + ); + + event RevokeManualBlocking( + address indexed _from, + address indexed _to, + address indexed _addedBy + ); + + /** + * @notice Constructor + * @param _securityToken Address of the security token + * @param _polyAddress Address of the polytoken + */ + constructor (address _securityToken, address _polyAddress) + public + Module(_securityToken, _polyAddress) + { + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() public pure returns (bytes4) { + return bytes4(0); + } + + /** @notice Used to verify the transfer transaction and allow a manually approved transqaction to bypass other restrictions + * @param _from Address of the sender + * @param _to Address of the receiver + * @param _amount The amount of tokens to transfer + * @param _isTransfer Whether or not this is an actual transfer or just a test to see if the tokens would be transferrable + */ + 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 the owner"); + // manual blocking takes precidence over manual approval + if (!paused) { + /*solium-disable-next-line security/no-block-members*/ + if (manualBlockings[_from][_to].expiryTime >= now) { + return Result.INVALID; + } + /*solium-disable-next-line security/no-block-members*/ + if ((manualApprovals[_from][_to].expiryTime >= now) && (manualApprovals[_from][_to].allowance >= _amount)) { + if (_isTransfer) { + manualApprovals[_from][_to].allowance = manualApprovals[_from][_to].allowance.sub(_amount); + } + return Result.VALID; + } + } + return Result.NA; + } + + /** + * @notice Adds a pair of addresses to manual approvals + * @param _from is the address from which transfers are approved + * @param _to is the address to which transfers are approved + * @param _allowance is the approved amount of tokens + * @param _expiryTime is the time until which the transfer is allowed + */ + function addManualApproval(address _from, address _to, uint256 _allowance, uint256 _expiryTime) public withPerm(TRANSFER_APPROVAL) { + require(_to != address(0), "Invalid to address"); + /*solium-disable-next-line security/no-block-members*/ + require(_expiryTime > now, "Invalid expiry time"); + require(manualApprovals[_from][_to].allowance == 0, "Approval already exists"); + manualApprovals[_from][_to] = ManualApproval(_allowance, _expiryTime); + emit AddManualApproval(_from, _to, _allowance, _expiryTime, msg.sender); + } + + /** + * @notice Adds a pair of addresses to manual blockings + * @param _from is the address from which transfers are blocked + * @param _to is the address to which transfers are blocked + * @param _expiryTime is the time until which the transfer is blocked + */ + function addManualBlocking(address _from, address _to, uint256 _expiryTime) public withPerm(TRANSFER_APPROVAL) { + require(_to != address(0), "Invalid to address"); + /*solium-disable-next-line security/no-block-members*/ + require(_expiryTime > now, "Invalid expiry time"); + require(manualBlockings[_from][_to].expiryTime == 0, "Blocking already exists"); + manualBlockings[_from][_to] = ManualBlocking(_expiryTime); + emit AddManualBlocking(_from, _to, _expiryTime, msg.sender); + } + + /** + * @notice Removes a pairs of addresses from manual approvals + * @param _from is the address from which transfers are approved + * @param _to is the address to which transfers are approved + */ + function revokeManualApproval(address _from, address _to) public withPerm(TRANSFER_APPROVAL) { + require(_to != address(0), "Invalid to address"); + delete manualApprovals[_from][_to]; + emit RevokeManualApproval(_from, _to, msg.sender); + } + + /** + * @notice Removes a pairs of addresses from manual approvals + * @param _from is the address from which transfers are approved + * @param _to is the address to which transfers are approved + */ + function revokeManualBlocking(address _from, address _to) public withPerm(TRANSFER_APPROVAL) { + require(_to != address(0), "Invalid to address"); + delete manualBlockings[_from][_to]; + emit RevokeManualBlocking(_from, _to, msg.sender); + } + + /** + * @notice Returns the permissions flag that are associated with ManualApproval transfer manager + */ + function getPermissions() public view returns(bytes32[]) { + bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = TRANSFER_APPROVAL; + return allPermissions; + } +} diff --git a/test/j_manual_approval_transfer_manager.js b/test/j_manual_approval_transfer_manager.js index 709991b92..0461f287a 100644 --- a/test/j_manual_approval_transfer_manager.js +++ b/test/j_manual_approval_transfer_manager.js @@ -321,18 +321,6 @@ contract("ManualApprovalTransferManager", accounts => { assert.equal((await I_SecurityToken.balanceOf(account_investor1)).toNumber(), web3.utils.toWei("5", "ether")); }); - it("Should fail to add a manual approval because invalid _from address", async () => { - await catchRevert( - I_ManualApprovalTransferManager.addManualApproval( - "", - account_investor4, - web3.utils.toWei("2", "ether"), - latestTime() + duration.days(1), - { from: token_owner } - ) - ); - }); - it("Should fail to add a manual approval because invalid _to address", async () => { await catchRevert( I_ManualApprovalTransferManager.addManualApproval( @@ -367,6 +355,16 @@ contract("ManualApprovalTransferManager", accounts => { ); }); + it("Add a manual approval for a 5th investor from issuance", async () => { + await I_ManualApprovalTransferManager.addManualApproval( + "", + account_investor5, + web3.utils.toWei("2", "ether"), + latestTime() + duration.days(1), + { from: token_owner } + ); + }); + it("Should fail to add a manual approval because allowance is laready exists", async () => { await catchRevert( I_ManualApprovalTransferManager.addManualApproval( @@ -379,10 +377,6 @@ contract("ManualApprovalTransferManager", accounts => { ); }); - it("Should fail to revoke manual approval because invalid _from address", async () => { - await catchRevert(I_ManualApprovalTransferManager.revokeManualApproval("", account_investor4, { from: token_owner })); - }); - it("Should fail to revoke manual approval because invalid _to address", async () => { await catchRevert(I_ManualApprovalTransferManager.revokeManualApproval(account_investor1, "", { from: token_owner })); }); @@ -409,6 +403,15 @@ contract("ManualApprovalTransferManager", accounts => { assert.equal((await I_SecurityToken.balanceOf(account_investor4)).toNumber(), web3.utils.toWei("1", "ether")); }); + it("Approval fails with wrong from to address", async () => { + await catchRevert(I_SecurityToken.transfer(account_investor5, web3.utils.toWei("1", "ether"), { from: account_investor1 })); + }); + + it("Use 100% of issuance approval", async () => { + await I_SecurityToken.mint(account_investor5, web3.utils.toWei("2", "ether"), { from: token_owner }); + assert.equal((await I_SecurityToken.balanceOf(account_investor5)).toNumber(), web3.utils.toWei("2", "ether")); + }); + it("Check verifyTransfer without actually transferring", async () => { let verified = await I_SecurityToken.verifyTransfer.call( account_investor1, @@ -439,14 +442,6 @@ contract("ManualApprovalTransferManager", accounts => { await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 }); }); - it("Should fail to add a manual block because invalid _from address", async () => { - await catchRevert( - I_ManualApprovalTransferManager.addManualBlocking("", account_investor2, latestTime() + duration.days(1), { - from: token_owner - }) - ); - }); - it("Should fail to add a manual block because invalid _to address", async () => { await catchRevert( I_ManualApprovalTransferManager.addManualBlocking(account_investor1, "", latestTime() + duration.days(1), { @@ -477,10 +472,6 @@ contract("ManualApprovalTransferManager", accounts => { await catchRevert(I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 })); }); - it("Should fail to revoke manual block because invalid _from address", async () => { - await catchRevert(I_ManualApprovalTransferManager.revokeManualBlocking("0x0", account_investor2, { from: token_owner })); - }); - it("Should fail to revoke manual block because invalid _to address", async () => { await catchRevert(I_ManualApprovalTransferManager.revokeManualBlocking(account_investor1, "0x0", { from: token_owner })); }); From 115c3cb807a4bc87cf8a8905d2ad685b9881e574 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 22 Nov 2018 11:55:03 +0000 Subject: [PATCH 2/2] Fix typo --- .../modules/ManualApprovalTransferManager.sol | 169 ------------------ .../ManualApprovalTransferManager.sol | 4 - 2 files changed, 173 deletions(-) delete mode 100644 contracts/modules/ManualApprovalTransferManager.sol diff --git a/contracts/modules/ManualApprovalTransferManager.sol b/contracts/modules/ManualApprovalTransferManager.sol deleted file mode 100644 index d95241e9a..000000000 --- a/contracts/modules/ManualApprovalTransferManager.sol +++ /dev/null @@ -1,169 +0,0 @@ -pragma solidity ^0.4.24; - -import "./ITransferManager.sol"; -import "openzeppelin-solidity/contracts/math/SafeMath.sol"; - -/** - * @title Transfer Manager module for manually approving or blocking transactions between accounts - */ -contract ManualApprovalTransferManager is ITransferManager { - using SafeMath for uint256; - - //Address from which issuances come - address public issuanceAddress = address(0); - - //Address which can sign whitelist changes - address public signingAddress = address(0); - - bytes32 public constant TRANSFER_APPROVAL = "TRANSFER_APPROVAL"; - - //Manual approval is an allowance (that has been approved) with an expiry time - struct ManualApproval { - uint256 allowance; - uint256 expiryTime; - } - - //Manual blocking allows you to specify a list of blocked address pairs with an associated expiry time for the block - struct ManualBlocking { - uint256 expiryTime; - } - - //Store mappings of address => address with ManualApprovals - mapping (address => mapping (address => ManualApproval)) public manualApprovals; - - //Store mappings of address => address with ManualBlockings - mapping (address => mapping (address => ManualBlocking)) public manualBlockings; - - event AddManualApproval( - address indexed _from, - address indexed _to, - uint256 _allowance, - uint256 _expiryTime, - address indexed _addedBy - ); - - event AddManualBlocking( - address indexed _from, - address indexed _to, - uint256 _expiryTime, - address indexed _addedBy - ); - - event RevokeManualApproval( - address indexed _from, - address indexed _to, - address indexed _addedBy - ); - - event RevokeManualBlocking( - address indexed _from, - address indexed _to, - address indexed _addedBy - ); - - /** - * @notice Constructor - * @param _securityToken Address of the security token - * @param _polyAddress Address of the polytoken - */ - constructor (address _securityToken, address _polyAddress) - public - Module(_securityToken, _polyAddress) - { - } - - /** - * @notice This function returns the signature of configure function - */ - function getInitFunction() public pure returns (bytes4) { - return bytes4(0); - } - - /** @notice Used to verify the transfer transaction and allow a manually approved transqaction to bypass other restrictions - * @param _from Address of the sender - * @param _to Address of the receiver - * @param _amount The amount of tokens to transfer - * @param _isTransfer Whether or not this is an actual transfer or just a test to see if the tokens would be transferrable - */ - 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 the owner"); - // manual blocking takes precidence over manual approval - if (!paused) { - /*solium-disable-next-line security/no-block-members*/ - if (manualBlockings[_from][_to].expiryTime >= now) { - return Result.INVALID; - } - /*solium-disable-next-line security/no-block-members*/ - if ((manualApprovals[_from][_to].expiryTime >= now) && (manualApprovals[_from][_to].allowance >= _amount)) { - if (_isTransfer) { - manualApprovals[_from][_to].allowance = manualApprovals[_from][_to].allowance.sub(_amount); - } - return Result.VALID; - } - } - return Result.NA; - } - - /** - * @notice Adds a pair of addresses to manual approvals - * @param _from is the address from which transfers are approved - * @param _to is the address to which transfers are approved - * @param _allowance is the approved amount of tokens - * @param _expiryTime is the time until which the transfer is allowed - */ - function addManualApproval(address _from, address _to, uint256 _allowance, uint256 _expiryTime) public withPerm(TRANSFER_APPROVAL) { - require(_to != address(0), "Invalid to address"); - /*solium-disable-next-line security/no-block-members*/ - require(_expiryTime > now, "Invalid expiry time"); - require(manualApprovals[_from][_to].allowance == 0, "Approval already exists"); - manualApprovals[_from][_to] = ManualApproval(_allowance, _expiryTime); - emit AddManualApproval(_from, _to, _allowance, _expiryTime, msg.sender); - } - - /** - * @notice Adds a pair of addresses to manual blockings - * @param _from is the address from which transfers are blocked - * @param _to is the address to which transfers are blocked - * @param _expiryTime is the time until which the transfer is blocked - */ - function addManualBlocking(address _from, address _to, uint256 _expiryTime) public withPerm(TRANSFER_APPROVAL) { - require(_to != address(0), "Invalid to address"); - /*solium-disable-next-line security/no-block-members*/ - require(_expiryTime > now, "Invalid expiry time"); - require(manualBlockings[_from][_to].expiryTime == 0, "Blocking already exists"); - manualBlockings[_from][_to] = ManualBlocking(_expiryTime); - emit AddManualBlocking(_from, _to, _expiryTime, msg.sender); - } - - /** - * @notice Removes a pairs of addresses from manual approvals - * @param _from is the address from which transfers are approved - * @param _to is the address to which transfers are approved - */ - function revokeManualApproval(address _from, address _to) public withPerm(TRANSFER_APPROVAL) { - require(_to != address(0), "Invalid to address"); - delete manualApprovals[_from][_to]; - emit RevokeManualApproval(_from, _to, msg.sender); - } - - /** - * @notice Removes a pairs of addresses from manual approvals - * @param _from is the address from which transfers are approved - * @param _to is the address to which transfers are approved - */ - function revokeManualBlocking(address _from, address _to) public withPerm(TRANSFER_APPROVAL) { - require(_to != address(0), "Invalid to address"); - delete manualBlockings[_from][_to]; - emit RevokeManualBlocking(_from, _to, msg.sender); - } - - /** - * @notice Returns the permissions flag that are associated with ManualApproval transfer manager - */ - function getPermissions() public view returns(bytes32[]) { - bytes32[] memory allPermissions = new bytes32[](1); - allPermissions[0] = TRANSFER_APPROVAL; - return allPermissions; - } -} diff --git a/contracts/modules/TransferManager/ManualApprovalTransferManager.sol b/contracts/modules/TransferManager/ManualApprovalTransferManager.sol index fd888680a..d95241e9a 100644 --- a/contracts/modules/TransferManager/ManualApprovalTransferManager.sol +++ b/contracts/modules/TransferManager/ManualApprovalTransferManager.sol @@ -113,7 +113,6 @@ contract ManualApprovalTransferManager is ITransferManager { * @param _expiryTime is the time until which the transfer is allowed */ function addManualApproval(address _from, address _to, uint256 _allowance, uint256 _expiryTime) public withPerm(TRANSFER_APPROVAL) { - require(_from != address(0), "Invalid from address"); require(_to != address(0), "Invalid to address"); /*solium-disable-next-line security/no-block-members*/ require(_expiryTime > now, "Invalid expiry time"); @@ -129,7 +128,6 @@ contract ManualApprovalTransferManager is ITransferManager { * @param _expiryTime is the time until which the transfer is blocked */ function addManualBlocking(address _from, address _to, uint256 _expiryTime) public withPerm(TRANSFER_APPROVAL) { - require(_from != address(0), "Invalid from address"); require(_to != address(0), "Invalid to address"); /*solium-disable-next-line security/no-block-members*/ require(_expiryTime > now, "Invalid expiry time"); @@ -144,7 +142,6 @@ contract ManualApprovalTransferManager is ITransferManager { * @param _to is the address to which transfers are approved */ function revokeManualApproval(address _from, address _to) public withPerm(TRANSFER_APPROVAL) { - require(_from != address(0), "Invalid from address"); require(_to != address(0), "Invalid to address"); delete manualApprovals[_from][_to]; emit RevokeManualApproval(_from, _to, msg.sender); @@ -156,7 +153,6 @@ contract ManualApprovalTransferManager is ITransferManager { * @param _to is the address to which transfers are approved */ function revokeManualBlocking(address _from, address _to) public withPerm(TRANSFER_APPROVAL) { - require(_from != address(0), "Invalid from address"); require(_to != address(0), "Invalid to address"); delete manualBlockings[_from][_to]; emit RevokeManualBlocking(_from, _to, msg.sender);