Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9274cde
remove all blocking functions
comeonbuddy Dec 3, 2018
9316ccc
Merge branch 'dev-2.1.0' into remove-blocking-MATM
satyamakgec Dec 4, 2018
c0cf9ae
remove fuzz test related to manual blocking
comeonbuddy Dec 5, 2018
9c508f3
Merge branch 'dev-2.1.0' into remove-blocking-MATM
satyamakgec Dec 5, 2018
d6e5490
Merge branch 'dev-2.1.0' into remove-blocking-MATM
pabloruiz55 Dec 5, 2018
762f7b0
Merge branch 'dev-2.1.0' into remove-blocking-MATM
VictorVicente Dec 8, 2018
c5e70c1
CLI - Removed blocking functions
VictorVicente Dec 8, 2018
b894a0c
Updated permission document
VictorVicente Dec 8, 2018
f03b5f7
finished MATM function changes
comeonbuddy Dec 9, 2018
d59e9b1
Merge branch 'remove-blocking-MATM' of https://github.com/PolymathNet…
comeonbuddy Dec 9, 2018
a59e50e
improve the function definitions
SatyamSB Dec 10, 2018
75a1bdd
Merge branch 'dev-2.1.0' into remove-blocking-MATM
satyamakgec Dec 10, 2018
5d7263a
add missing return statement
SatyamSB Dec 10, 2018
46c9cdd
minor fix
SatyamSB Dec 10, 2018
0369cf3
MATM contract and test fixes
SatyamSB Dec 11, 2018
4fefd85
Merge branch 'dev-2.1.0' into remove-blocking-MATM
satyamakgec Dec 11, 2018
552de1b
minor fix
SatyamSB Dec 11, 2018
4956441
[WIP] CLI advances
shuffledex Dec 12, 2018
5e2ec74
Improve MATM to allow allowances and expiryTimes to be changed for ex…
shuffledex Dec 13, 2018
c8832cb
Refactoring and improvements
shuffledex Dec 13, 2018
002951b
Minor CLI changes
VictorVicente Dec 14, 2018
a10e0c0
fix modify batch
shuffledex Dec 14, 2018
22b6d45
manual merge
shuffledex Dec 14, 2018
4d48187
Improve menu string
shuffledex Dec 14, 2018
b53d904
CLI minor changes
VictorVicente Dec 14, 2018
09e8e4e
Merge branch 'dev-2.1.0' into remove-blocking-MATM
VictorVicente Dec 14, 2018
5e99a3a
Merge branch 'dev-2.1.0' into remove-blocking-MATM
maxsam4 Dec 18, 2018
ba851bf
Merge branch 'dev-2.1.0' into remove-blocking-MATM
VictorVicente Dec 21, 2018
5c14bcb
CLI typo fix
VictorVicente Dec 21, 2018
6dcef1f
fixed functions getActiveApprovalsToUser and added getAllApprovals() …
comeonbuddy Jan 2, 2019
bb3cdef
edited function description
comeonbuddy Jan 2, 2019
cad4562
CLI update
VictorVicente Jan 2, 2019
8737567
Merge branch 'dev-2.1.0' into remove-blocking-MATM
maxsam4 Jan 2, 2019
ad29c1b
test case fixed
maxsam4 Jan 2, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 68 additions & 40 deletions contracts/modules/TransferManager/ManualApprovalTransferManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "./ITransferManager.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";

/**
* @title Transfer Manager module for manually approving or blocking transactions between accounts
* @title Transfer Manager module for manually approving transactions between accounts
*/
contract ManualApprovalTransferManager is ITransferManager {
using SafeMath for uint256;
Expand All @@ -19,20 +19,18 @@ contract ManualApprovalTransferManager is ITransferManager {

//Manual approval is an allowance (that has been approved) with an expiry time
struct ManualApproval {
address from;
address to;
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;
bytes32 description;
}

//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;
//An array to track all current active approvals
ManualApproval[] public activeManualApprovals;

event AddManualApproval(
address indexed _from,
Expand All @@ -42,11 +40,11 @@ contract ManualApprovalTransferManager is ITransferManager {
address indexed _addedBy
);

event AddManualBlocking(
event EditManualApproval(
address indexed _from,
address indexed _to,
uint256 _expiryTime,
address indexed _addedBy
address indexed _edittedBy
);

event RevokeManualApproval(
Expand All @@ -55,12 +53,6 @@ contract ManualApprovalTransferManager is ITransferManager {
address indexed _addedBy
);

event RevokeManualBlocking(
address indexed _from,
address indexed _to,
address indexed _addedBy
);

/**
* @notice Constructor
* @param _securityToken Address of the security token
Expand Down Expand Up @@ -88,13 +80,8 @@ contract ManualApprovalTransferManager is ITransferManager {
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);
Expand All @@ -112,28 +99,45 @@ contract ManualApprovalTransferManager is ITransferManager {
* @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) {
function addManualApproval(address _from, address _to, uint256 _allowance, uint256 _expiryTime, bytes32 _description) 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);
manualApprovals[_from][_to] = ManualApproval(_from, _to, _allowance, _expiryTime, _description);
activeManualApprovals.push(ManualApproval(_from, _to, _allowance, _expiryTime, _description));

emit AddManualApproval(_from, _to, _allowance, _expiryTime, msg.sender);
}


/**
* @notice Adds mutiple manual approvals in batch
* @param _from is the address array from which transfers are approved
* @param _to is the address array to which transfers are approved
* @param _allowance is the array of approved amounts
* @param _expiryTime is the array of the times until which eath transfer is allowed
* @param _description is the description array for these manual approvals
*/
function addManualApprovalMulti(address[] _from, address[] _to, uint256[] _allowance, uint256[] _expiryTime, bytes32[] _description) public withPerm(TRANSFER_APPROVAL) {
require(_from.length == _to.length && _to.length == _allowance.length && _allowance.length == _expiryTime.length && _expiryTime.length == _description.length, "input array numbers not matching");
for(uint8 i=0; i<_from.length; i++){
addManualApproval(_from[i], _to[i], _allowance[i], _expiryTime[i], _description[i]);
}
}

/**
* @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
* @notice Edit an existing manual approvals
* @param _from is the new address from which transfers are approved
* @param _to is the new address to which transfers are approved
* @param _expiryTime is the new time until which the transfer is allowed
*/
function addManualBlocking(address _from, address _to, uint256 _expiryTime) public withPerm(TRANSFER_APPROVAL) {
function updateManualApproval(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);
require(manualApprovals[_from][_to].allowance != 0, "Approval does not exists");
manualApprovals[_from][_to].expiryTime = _expiryTime;
emit EditManualApproval(_from, _to, _expiryTime, msg.sender);
}

/**
Expand All @@ -144,18 +148,42 @@ contract ManualApprovalTransferManager is ITransferManager {
function revokeManualApproval(address _from, address _to) public withPerm(TRANSFER_APPROVAL) {
require(_to != address(0), "Invalid to address");
delete manualApprovals[_from][_to];

//find the record in active approvals array & delete it
uint256 index;
for(uint256 i = 0; i < activeManualApprovals.length; i++){
if (activeManualApprovals[i].from == _from && activeManualApprovals[i].to == _to){
index = i;
}
}

for(uint256 j = index; j < activeManualApprovals.length-1; j++){
activeManualApprovals[j] = activeManualApprovals[j+1];
}

delete activeManualApprovals[activeManualApprovals.length-1];
activeManualApprovals.length--;

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
* @notice Removes mutiple pairs of addresses from manual approvals
* @param _from is the address array from which transfers are approved
* @param _to is the address array 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);
function revokeManualApprovalMulti(address[] _from, address[] _to) public withPerm(TRANSFER_APPROVAL) {
require(_from.length == _to.length, "input array numbers not matching");
for(uint8 i=0; i<_from.length; i++){
revokeManualApproval(_from[i], _to[i]);
}
}

/**
* @notice Returns the current number of active approvals
*/
function getActiveApprovalsLength() public view returns(uint256) {
return activeManualApprovals.length;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ contract ManualApprovalTransferManagerFactory is ModuleFactory {
version = "2.1.0";
name = "ManualApprovalTransferManager";
title = "Manual Approval Transfer Manager";
description = "Manage transfers using single approvals / blocking";
description = "Manage transfers using single approvals";
compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0));
compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0));
}
Expand Down Expand Up @@ -53,7 +53,7 @@ contract ManualApprovalTransferManagerFactory is ModuleFactory {
*/
function getInstructions() external view returns(string) {
/*solium-disable-next-line max-len*/
return "Allows an issuer to set manual approvals or blocks for specific pairs of addresses and amounts. Init function takes no parameters.";
return "Allows an issuer to set manual approvals for specific pairs of addresses and amounts. Init function takes no parameters.";
}

/**
Expand Down
98 changes: 45 additions & 53 deletions test/j_manual_approval_transfer_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ contract("ManualApprovalTransferManager", accounts => {
"",
web3.utils.toWei("2", "ether"),
latestTime() + duration.days(1),
"DESCRIPTION",
{ from: token_owner }
)
);
Expand All @@ -340,27 +341,66 @@ contract("ManualApprovalTransferManager", accounts => {
account_investor4,
web3.utils.toWei("2", "ether"),
99999,
"DESCRIPTION",
{ from: token_owner }
)
);
});

it("Add a manual approval for a 4th investor", async () => {
it("Add a manual approval for a 4th investor & return correct length", async () => {
await I_ManualApprovalTransferManager.addManualApproval(
account_investor1,
account_investor4,
web3.utils.toWei("2", "ether"),
latestTime() + duration.days(1),
"DESCRIPTION",
{ from: token_owner }
);

assert(await I_ManualApprovalTransferManager.getActiveApprovalsLength({ from: token_owner }), 1);
});

it("Should update the manual approval expiry time for 4th investor", async () => {
await I_ManualApprovalTransferManager.updateManualApproval(
account_investor1,
account_investor4,
latestTime() + duration.days(2),
{ from: token_owner }
);
});

it("Add multiple manual approvals", async () => {
await I_ManualApprovalTransferManager.addManualApprovalMulti(
[account_investor2,account_investor3],
[account_investor3,account_investor4],
[web3.utils.toWei("2", "ether"), web3.utils.toWei("2", "ether")],
[latestTime() + duration.days(1),latestTime() + duration.days(1)],
["DESCRIPTION", "DESCRIPTION"],
{ from: token_owner }
);

assert(await I_ManualApprovalTransferManager.getActiveApprovalsLength({ from: token_owner }), 3);
});

it("Revoke multiple manual approvals", async () => {
await I_ManualApprovalTransferManager.revokeManualApprovalMulti(
[account_investor2,account_investor3],
[account_investor3,account_investor4],
{ from: token_owner }
);

assert(await I_ManualApprovalTransferManager.getActiveApprovalsLength({ from: token_owner }), 1);
});



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),
"DESCRIPTION",
{ from: token_owner }
);
});
Expand All @@ -372,6 +412,7 @@ contract("ManualApprovalTransferManager", accounts => {
account_investor4,
web3.utils.toWei("2", "ether"),
latestTime() + duration.days(5),
"DESCRIPTION",
{ from: token_owner }
)
);
Expand All @@ -393,6 +434,7 @@ contract("ManualApprovalTransferManager", accounts => {
account_investor4,
web3.utils.toWei("2", "ether"),
latestTime() + duration.days(1),
"DESCRIPTION",
{ from: token_owner }
);
});
Expand Down Expand Up @@ -442,56 +484,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 _to address", async () => {
await catchRevert(
I_ManualApprovalTransferManager.addManualBlocking(account_investor1, "", latestTime() + duration.days(1), {
from: token_owner
})
);
});

it("Should fail to add a manual block because invalid expiry time", async () => {
await catchRevert(
I_ManualApprovalTransferManager.addManualBlocking(account_investor1, account_investor2, 99999, { from: token_owner })
);
});

it("Add a manual block for a 2nd investor", async () => {
await I_ManualApprovalTransferManager.addManualBlocking(account_investor1, account_investor2, latestTime() + duration.days(1), {
from: token_owner
});
});

it("Should fail to add a manual block because blocking already exist", async () => {
await catchRevert(
I_ManualApprovalTransferManager.addManualBlocking(account_investor1, account_investor2, latestTime() + duration.days(5), { from: token_owner })
);
});

it("Check manual block causes failure", async () => {
await catchRevert(I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 }));
});

it("Should fail to revoke manual block because invalid _to address", async () => {
await catchRevert(I_ManualApprovalTransferManager.revokeManualBlocking(account_investor1, "0x0", { from: token_owner }));
});

it("Revoke manual block and check transfer works", async () => {
await I_ManualApprovalTransferManager.revokeManualBlocking(account_investor1, account_investor2, { from: token_owner });
await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 });
assert.equal((await I_SecurityToken.balanceOf(account_investor2)).toNumber(), web3.utils.toWei("2", "ether"));
});

it("Check manual block ignored after expiry", async () => {
await I_ManualApprovalTransferManager.addManualBlocking(account_investor1, account_investor2, latestTime() + duration.days(1), {
from: token_owner
});

await catchRevert(I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 }));
await increaseTime(1 + 24 * 60 * 60);
await I_SecurityToken.transfer(account_investor2, web3.utils.toWei("1", "ether"), { from: account_investor1 });
});

it("Should successfully attach the CountTransferManager with the security token (count of 1)", async () => {
let bytesCountTM = web3.eth.abi.encodeFunctionCall(
{
Expand Down Expand Up @@ -532,13 +524,13 @@ contract("ManualApprovalTransferManager", accounts => {
let name = web3.utils.toUtf8(await I_ManualApprovalTransferManagerFactory.getName.call());
assert.equal(name, "ManualApprovalTransferManager", "Wrong Module added");
let desc = await I_ManualApprovalTransferManagerFactory.description.call();
assert.equal(desc, "Manage transfers using single approvals / blocking", "Wrong Module added");
assert.equal(desc, "Manage transfers using single approvals", "Wrong Module added");
let title = await I_ManualApprovalTransferManagerFactory.title.call();
assert.equal(title, "Manual Approval Transfer Manager", "Wrong Module added");
let inst = await I_ManualApprovalTransferManagerFactory.getInstructions.call();
assert.equal(
inst,
"Allows an issuer to set manual approvals or blocks for specific pairs of addresses and amounts. Init function takes no parameters.",
"Allows an issuer to set manual approvals for specific pairs of addresses and amounts. Init function takes no parameters.",
"Wrong Module added"
);
assert.equal(await I_ManualApprovalTransferManagerFactory.version.call(), "2.1.0");
Expand Down
Loading