From 0a2a82666bc512ccb54be1058e01190b75860048 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 22 Nov 2018 11:38:47 +0000 Subject: [PATCH 1/5] 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 5f73f75e05428772b660951cf8375a461f0412f3 Mon Sep 17 00:00:00 2001 From: Adam Dossa Date: Thu, 22 Nov 2018 11:55:03 +0000 Subject: [PATCH 2/5] 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); From 7405d810cc2fa23488a33bed3dbfdc1b3325fa0c Mon Sep 17 00:00:00 2001 From: satyam Date: Mon, 26 Nov 2018 18:17:46 +0530 Subject: [PATCH 3/5] changelog updated and MATM version changed --- CHANGELOG.md | 14 ++++++++++++-- .../ManualApprovalTransferManagerFactory.sol | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c5796e34..78ed20bb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ # Changelog All notable changes to this project will be documented in this file. +# v2.1.0 - Release Candidate + +[__2.1.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __13-089-18__ + +## Fixed +* Change the version of `ManualApprovalTransferManagerFactory` from `1.0.0` to `2.0.1`. + +## Removed +* Remove `0x0` check for the `_from` address to `ManualApprovalTransferManager`. + # v1.5.0 - Release Candidate [__1.5.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __15-08-18__ @@ -39,13 +49,13 @@ All notable changes to this project will be documented in this file. * All permissions are denied if no permission manager is active. * Generalize the STO varaible names and added them in `ISTO.sol` to use the common standard in all STOs. * Generalize the event when any new token get registered with the polymath ecosystem. `LogNewSecurityToken` should emit _ticker, _name, _securityTokenAddress, _owner, _addedAt, _registrant respectively. #230 -* Change the function name of `withdraPoly` to `withdrawERC20` and make the function generalize to extract tokens from the ST contract. parmeters are contract address and the value need to extract from the securityToken. +* Change the function name of `withdraPoly` to `withdrawERC20` and make the function generalize to extract tokens from the ST contract. parmeters are contract address and the value need to extract from the securityToken. ## Removed * Removed investors list pruning * Remove `swarmHash` from the `registerTicker(), addCustomTicker(), generateSecurityToken(), addCustomSecurityToken()` functions of TickerRegistry.sol and SecurityTokenRegistry.sol. #230 * Remove `Log` prefix from all the event present in the ecosystem. -* Removed `addTagByModuleType` & `removeTagsByModuleType` from MR. +* Removed `addTagByModuleType` & `removeTagsByModuleType` from MR. ====== diff --git a/contracts/modules/TransferManager/ManualApprovalTransferManagerFactory.sol b/contracts/modules/TransferManager/ManualApprovalTransferManagerFactory.sol index f634b33e9..9c5513ee7 100644 --- a/contracts/modules/TransferManager/ManualApprovalTransferManagerFactory.sol +++ b/contracts/modules/TransferManager/ManualApprovalTransferManagerFactory.sol @@ -18,7 +18,7 @@ contract ManualApprovalTransferManagerFactory is ModuleFactory { constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) { - version = "1.0.0"; + version = "2.0.1"; name = "ManualApprovalTransferManager"; title = "Manual Approval Transfer Manager"; description = "Manage transfers using single approvals / blocking"; From a48511dcf9aa1f061636b92b80587adeddfb24e8 Mon Sep 17 00:00:00 2001 From: Pablo Ruiz Date: Mon, 26 Nov 2018 09:52:17 -0300 Subject: [PATCH 4/5] Update CHANGELOG.md --- CHANGELOG.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78ed20bb5..af9843e8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,9 @@ All notable changes to this project will be documented in this file. [__2.1.0__](https://www.npmjs.com/package/polymath-core?activeTab=readme) __13-089-18__ -## Fixed -* Change the version of `ManualApprovalTransferManagerFactory` from `1.0.0` to `2.0.1`. - -## Removed -* Remove `0x0` check for the `_from` address to `ManualApprovalTransferManager`. +## Manual Approval TransferManager +* Removed `0x0` check for the `_from` address to `ManualApprovalTransferManager`. This allows for the Issuer/Transfer Agent to approve a one-off mint of tokens that otherwise would not be possible. +* Changed the version of `ManualApprovalTransferManagerFactory` from `1.0.0` to `2.0.1`. # v1.5.0 - Release Candidate From bcec5b2c971fd57c38df12f86fa3314a8bb6eb7b Mon Sep 17 00:00:00 2001 From: Pablo Ruiz Date: Mon, 26 Nov 2018 10:14:49 -0300 Subject: [PATCH 5/5] fixed MATM test w/ version change --- test/j_manual_approval_transfer_manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/j_manual_approval_transfer_manager.js b/test/j_manual_approval_transfer_manager.js index 0461f287a..5779a1c13 100644 --- a/test/j_manual_approval_transfer_manager.js +++ b/test/j_manual_approval_transfer_manager.js @@ -541,7 +541,7 @@ contract("ManualApprovalTransferManager", accounts => { "Allows an issuer to set manual approvals or blocks for specific pairs of addresses and amounts. Init function takes no parameters.", "Wrong Module added" ); - assert.equal(await I_ManualApprovalTransferManagerFactory.version.call(), "1.0.0"); + assert.equal(await I_ManualApprovalTransferManagerFactory.version.call(), "2.0.1"); }); it("Should get the tags of the factory", async () => {