From 5b5e1fc88b4e98e657f9eb20f0593b41efd8649c Mon Sep 17 00:00:00 2001 From: Chris Cassano <1285652+glitch003@users.noreply.github.com> Date: Sat, 27 Oct 2018 01:17:52 +0100 Subject: [PATCH] removed feature of having multiple volume lockup restrictions --- .../LockupVolumeRestrictionTM.sol | 211 +++++------------- ...kup_volume_restriction_transfer_manager.js | 188 ++-------------- 2 files changed, 77 insertions(+), 322 deletions(-) diff --git a/contracts/modules/Experimental/TransferManager/LockupVolumeRestrictionTM.sol b/contracts/modules/Experimental/TransferManager/LockupVolumeRestrictionTM.sol index 80f44cdb6..199265b3e 100644 --- a/contracts/modules/Experimental/TransferManager/LockupVolumeRestrictionTM.sol +++ b/contracts/modules/Experimental/TransferManager/LockupVolumeRestrictionTM.sol @@ -16,38 +16,31 @@ contract LockupVolumeRestrictionTM is ITransferManager { uint lockUpPeriodSeconds; // total period of lockup (seconds) uint releaseFrequencySeconds; // how often to release a tranche of tokens (seconds) uint startTime; // when this lockup starts (seconds) - uint totalAmount; // total amount of locked up tokens uint alreadyWithdrawn; // amount already withdrawn for this lockup } // maps user addresses to an array of lockups for that user - mapping (address => LockUp[]) internal lockUps; + mapping (address => LockUp) internal lockUps; event AddNewLockUp( address indexed userAddress, uint lockUpPeriodSeconds, uint releaseFrequencySeconds, - uint startTime, - uint totalAmount, - uint indexed addedIndex + uint startTime ); event RemoveLockUp( address indexed userAddress, uint lockUpPeriodSeconds, uint releaseFrequencySeconds, - uint startTime, - uint totalAmount, - uint indexed removedIndex + uint startTime ); event ModifyLockUp( address indexed userAddress, uint lockUpPeriodSeconds, uint releaseFrequencySeconds, - uint startTime, - uint totalAmount, - uint indexed modifiedIndex + uint startTime ); /** @@ -69,7 +62,7 @@ contract LockupVolumeRestrictionTM is ITransferManager { */ function verifyTransfer(address _from, address /* _to*/, uint256 _amount, bytes /* _data */, bool _isTransfer) public returns(Result) { // only attempt to verify the transfer if the token is unpaused, this isn't a mint txn, and there exists a lockup for this user - if (!paused && _from != address(0) && lockUps[_from].length != 0) { + if (!paused && _from != address(0) && lockUps[_from].startTime != 0) { // check if this transfer is valid return _checkIfValidTransfer(_from, _amount, _isTransfer); } @@ -82,17 +75,15 @@ contract LockupVolumeRestrictionTM is ITransferManager { * @param _lockUpPeriodSeconds Total period of lockup (seconds) * @param _releaseFrequencySeconds How often to release a tranche of tokens (seconds) * @param _startTime When this lockup starts (seconds) - * @param _totalAmount Total amount of locked up tokens */ function addLockUp( address _userAddress, uint _lockUpPeriodSeconds, uint _releaseFrequencySeconds, - uint _startTime, - uint _totalAmount + uint _startTime ) public withPerm(ADMIN) { uint256 startTime = _startTime; - _checkLockUpParams(_lockUpPeriodSeconds, _releaseFrequencySeconds, _totalAmount); + _checkLockUpParams(_userAddress, _lockUpPeriodSeconds, _releaseFrequencySeconds, 0); // if a startTime of 0 is passed in, then start now. if (startTime == 0) { @@ -100,15 +91,13 @@ contract LockupVolumeRestrictionTM is ITransferManager { startTime = now; } - lockUps[_userAddress].push(LockUp(_lockUpPeriodSeconds, _releaseFrequencySeconds, startTime, _totalAmount, 0)); + lockUps[_userAddress] = LockUp(_lockUpPeriodSeconds, _releaseFrequencySeconds, startTime, 0); emit AddNewLockUp( _userAddress, _lockUpPeriodSeconds, _releaseFrequencySeconds, - startTime, - _totalAmount, - lockUps[_userAddress].length - 1 + startTime ); } @@ -118,25 +107,22 @@ contract LockupVolumeRestrictionTM is ITransferManager { * @param _lockUpPeriodsSeconds Array of total periods of lockup (seconds) * @param _releaseFrequenciesSeconds Array of how often to release a tranche of tokens (seconds) * @param _startTimes Array of When this lockup starts (seconds) - * @param _totalAmounts Array of total amount of locked up tokens */ function addLockUpMulti( address[] _userAddresses, uint[] _lockUpPeriodsSeconds, uint[] _releaseFrequenciesSeconds, - uint[] _startTimes, - uint[] _totalAmounts + uint[] _startTimes ) external withPerm(ADMIN) { require( _userAddresses.length == _lockUpPeriodsSeconds.length && /*solium-disable-line operator-whitespace*/ _userAddresses.length == _releaseFrequenciesSeconds.length && /*solium-disable-line operator-whitespace*/ - _userAddresses.length == _startTimes.length && - _userAddresses.length == _totalAmounts.length, + _userAddresses.length == _startTimes.length, "Input array length mismatch" ); for (uint i = 0; i < _userAddresses.length; i++) { - addLockUp(_userAddresses[i], _lockUpPeriodsSeconds[i], _releaseFrequenciesSeconds[i], _startTimes[i], _totalAmounts[i]); + addLockUp(_userAddresses[i], _lockUpPeriodsSeconds[i], _releaseFrequenciesSeconds[i], _startTimes[i]); } } @@ -144,49 +130,35 @@ contract LockupVolumeRestrictionTM is ITransferManager { /** * @notice Lets the admin remove a user's lock up * @param _userAddress Address of the user whose tokens are locked up - * @param _lockUpIndex The index of the LockUp to remove for the given userAddress */ - function removeLockUp(address _userAddress, uint _lockUpIndex) public withPerm(ADMIN) { - LockUp[] storage userLockUps = lockUps[_userAddress]; - require(_lockUpIndex < userLockUps.length, "Array out of bounds exception"); + function removeLockUp(address _userAddress) public withPerm(ADMIN) { - LockUp memory toRemove = userLockUps[_lockUpIndex]; + + LockUp memory toRemove = lockUps[_userAddress]; emit RemoveLockUp( _userAddress, toRemove.lockUpPeriodSeconds, toRemove.releaseFrequencySeconds, - toRemove.startTime, - toRemove.totalAmount, - _lockUpIndex + toRemove.startTime ); - if (_lockUpIndex < userLockUps.length - 1) { - // move the last element in the array into the index that is desired to be removed. - userLockUps[_lockUpIndex] = userLockUps[userLockUps.length - 1]; - } - // delete the last element - userLockUps.length--; + delete lockUps[_userAddress]; } /** * @notice Lets the admin modify a volume restriction lockup for a given address. * @param _userAddress Address of the user whose tokens should be locked up - * @param _lockUpIndex The index of the LockUp to edit for the given userAddress * @param _lockUpPeriodSeconds Total period of lockup (seconds) * @param _releaseFrequencySeconds How often to release a tranche of tokens (seconds) * @param _startTime When this lockup starts (seconds) - * @param _totalAmount Total amount of locked up tokens */ function modifyLockUp( address _userAddress, - uint _lockUpIndex, uint _lockUpPeriodSeconds, uint _releaseFrequencySeconds, - uint _startTime, - uint _totalAmount + uint _startTime ) public withPerm(ADMIN) { - require(_lockUpIndex < lockUps[_userAddress].length, "Array out of bounds exception"); uint256 startTime = _startTime; // if a startTime of 0 is passed in, then start now. @@ -195,58 +167,46 @@ contract LockupVolumeRestrictionTM is ITransferManager { startTime = now; } - _checkLockUpParams(_lockUpPeriodSeconds, _releaseFrequencySeconds, _totalAmount); + _checkLockUpParams( + _userAddress, + _lockUpPeriodSeconds, + _releaseFrequencySeconds, + lockUps[_userAddress].alreadyWithdrawn + ); // Get the lockup from the master list and edit it - lockUps[_userAddress][_lockUpIndex] = LockUp( + lockUps[_userAddress] = LockUp( _lockUpPeriodSeconds, _releaseFrequencySeconds, startTime, - _totalAmount, - lockUps[_userAddress][_lockUpIndex].alreadyWithdrawn + lockUps[_userAddress].alreadyWithdrawn ); emit ModifyLockUp( _userAddress, _lockUpPeriodSeconds, _releaseFrequencySeconds, - startTime, - _totalAmount, - _lockUpIndex + startTime ); } - /** - * @notice Get the length of the lockups array for a specific user address - * @param _userAddress Address of the user whose tokens should be locked up - */ - function getLockUpsLength(address _userAddress) public view returns (uint) { - return lockUps[_userAddress].length; - } /** * @notice Get a specific element in a user's lockups array given the user's address and the element index * @param _userAddress Address of the user whose tokens should be locked up - * @param _lockUpIndex The index of the LockUp to edit for the given userAddress */ - function getLockUp( - address _userAddress, - uint _lockUpIndex) + function getLockUp(address _userAddress) public view returns ( uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime, - uint totalAmount, uint alreadyWithdrawn ) { - require(_lockUpIndex < lockUps[_userAddress].length, "Array out of bounds exception"); - LockUp storage userLockUp = lockUps[_userAddress][_lockUpIndex]; return ( - userLockUp.lockUpPeriodSeconds, - userLockUp.releaseFrequencySeconds, - userLockUp.startTime, - userLockUp.totalAmount, - userLockUp.alreadyWithdrawn + lockUps[_userAddress].lockUpPeriodSeconds, + lockUps[_userAddress].releaseFrequencySeconds, + lockUps[_userAddress].startTime, + lockUps[_userAddress].alreadyWithdrawn ); } @@ -255,57 +215,27 @@ contract LockupVolumeRestrictionTM is ITransferManager { * @param userAddress Address of the user whose lock ups should be checked */ function _checkIfValidTransfer(address userAddress, uint amount, bool isTransfer) internal returns (Result) { - // get lock up array for this user - LockUp[] storage userLockUps = lockUps[userAddress]; - // maps the index of userLockUps to the amount allowed in this transfer - uint[] memory allowedAmountPerLockup = new uint[](userLockUps.length); - uint[3] memory tokenSums = [ - uint256(0), // allowed amount right now - uint256(0), // total locked up, ever - uint256(0) // already withdrawn, ever - ]; - - // loop over the user's lock ups - for (uint i = 0; i < userLockUps.length; i++) { - LockUp storage aLockUp = userLockUps[i]; - - uint allowedAmountForThisLockup = 0; - - // check if lockup has entirely passed - /*solium-disable-next-line security/no-block-members*/ - if (now >= aLockUp.startTime.add(aLockUp.lockUpPeriodSeconds)) { - // lockup has passed, or not started yet. allow all. - allowedAmountForThisLockup = aLockUp.totalAmount.sub(aLockUp.alreadyWithdrawn); - /*solium-disable-next-line security/no-block-members*/ - } else if (now >= aLockUp.startTime) { - // lockup is active. calculate how many to allow to be withdrawn right now - // calculate how many periods have elapsed already - /*solium-disable-next-line security/no-block-members*/ - uint elapsedPeriods = (now.sub(aLockUp.startTime)).div(aLockUp.releaseFrequencySeconds); - // calculate the total number of periods, overall - uint totalPeriods = aLockUp.lockUpPeriodSeconds.div(aLockUp.releaseFrequencySeconds); - // calculate how much should be released per period - uint amountPerPeriod = aLockUp.totalAmount.div(totalPeriods); - // calculate the number of tokens that should be released, - // multiplied by the number of periods that have elapsed already - // and add it to the total tokenSums[0] - allowedAmountForThisLockup = amountPerPeriod.mul(elapsedPeriods).sub(aLockUp.alreadyWithdrawn); + uint alreadyWithdrawn = lockUps[userAddress].alreadyWithdrawn; + uint userBalance = ISecurityToken(securityToken).balanceOf(userAddress); + uint totalAmount = alreadyWithdrawn.add(userBalance); + // lockup is active. calculate how many to allow to be withdrawn right now + // calculate how many periods have elapsed already + /*solium-disable-next-line security/no-block-members*/ + uint elapsedPeriods = (now.sub(lockUps[userAddress].startTime)).div(lockUps[userAddress].releaseFrequencySeconds); + // calculate the total number of periods, overall + uint totalPeriods = lockUps[userAddress].lockUpPeriodSeconds.div(lockUps[userAddress].releaseFrequencySeconds); + uint amountPerPeriod = totalAmount.div(totalPeriods); + // calculate the number of tokens that should be released, + // multiplied by the number of periods that have elapsed already + // and add it to the total tokenSums[0] + uint allowedAmount = amountPerPeriod.mul(elapsedPeriods).sub(lockUps[userAddress].alreadyWithdrawn); - } - // tokenSums[0] is allowed sum - tokenSums[0] = tokenSums[0].add(allowedAmountForThisLockup); - // tokenSums[1] is total locked up - tokenSums[1] = tokenSums[1].add(aLockUp.totalAmount); - // tokenSums[2] is total already withdrawn - tokenSums[2] = tokenSums[2].add(aLockUp.alreadyWithdrawn); - - allowedAmountPerLockup[i] = allowedAmountForThisLockup; - } - // tokenSums[0] is allowed sum - if (amount <= tokenSums[0]) { + if (amount <= allowedAmount || // transfer is allowed + now >= lockUps[userAddress].startTime.add(lockUps[userAddress].lockUpPeriodSeconds) || // lockup is expired + now < lockUps[userAddress].startTime) { // lockup hasn't started yet // transfer is valid and will succeed. if (!isTransfer) { // if this isn't a real transfer, don't subtract the withdrawn amounts from the lockups. it's a "read only" txn @@ -315,43 +245,10 @@ contract LockupVolumeRestrictionTM is ITransferManager { // we are going to write the withdrawn balances back to the lockups, so make sure that the person calling this function is the securityToken itself, since its public require(msg.sender == securityToken, "Sender is not securityToken"); - // subtract amounts so they are now known to be withdrawen - for (i = 0; i < userLockUps.length; i++) { - aLockUp = userLockUps[i]; - - // tokenSums[0] is allowed sum - if (allowedAmountPerLockup[i] >= tokenSums[0]) { - aLockUp.alreadyWithdrawn = aLockUp.alreadyWithdrawn.add(tokenSums[0]); - // we withdrew the entire tokenSums[0] from the lockup. We are done. - break; - } else { - // we have to split the tokenSums[0] across mutiple lockUps - aLockUp.alreadyWithdrawn = aLockUp.alreadyWithdrawn.add(allowedAmountPerLockup[i]); - // subtract the amount withdrawn from this lockup - tokenSums[0] = tokenSums[0].sub(allowedAmountPerLockup[i]); - } + lockUps[userAddress].alreadyWithdrawn = lockUps[userAddress].alreadyWithdrawn.add(amount); - } return Result.VALID; } - - return _checkIfUnlockedTokenTransferIsPossible(userAddress, amount, tokenSums[1], tokenSums[2]); - } - - function _checkIfUnlockedTokenTransferIsPossible( - address userAddress, - uint amount, - uint totalSum, - uint alreadyWithdrawnSum - ) internal view returns (Result) { - // the amount the user wants to withdraw is greater than their allowed amounts according to the lockups. however, if the user has like, 10 tokens, but only 4 are locked up, we should let the transfer go through for those 6 that aren't locked up - uint currentUserBalance = ISecurityToken(securityToken).balanceOf(userAddress); - uint stillLockedAmount = totalSum.sub(alreadyWithdrawnSum); - if (currentUserBalance >= stillLockedAmount && amount <= currentUserBalance.sub(stillLockedAmount)) { - // the user has more tokens in their balance than are actually locked up. they should be allowed to withdraw the difference - return Result.VALID; - } - return Result.INVALID; } @@ -359,12 +256,14 @@ contract LockupVolumeRestrictionTM is ITransferManager { * @notice Parameter checking function for creating or editing a lockup. This function will cause an exception if any of the parameters are bad. * @param lockUpPeriodSeconds Total period of lockup (seconds) * @param releaseFrequencySeconds How often to release a tranche of tokens (seconds) - * @param totalAmount Total amount of locked up tokens */ - function _checkLockUpParams(uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint totalAmount) internal view { + function _checkLockUpParams(address userAddress, uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint alreadyWithdrawn) internal view { require(lockUpPeriodSeconds != 0, "lockUpPeriodSeconds cannot be zero"); require(releaseFrequencySeconds != 0, "releaseFrequencySeconds cannot be zero"); - require(totalAmount != 0, "totalAmount cannot be zero"); + + uint userBalance = ISecurityToken(securityToken).balanceOf(userAddress); + uint totalAmount = alreadyWithdrawn.add(userBalance); + // check that the total amount to be released isn't too granular require( diff --git a/test/w_lockup_volume_restriction_transfer_manager.js b/test/w_lockup_volume_restriction_transfer_manager.js index fe06f3f5c..5af71a4ac 100644 --- a/test/w_lockup_volume_restriction_transfer_manager.js +++ b/test/w_lockup_volume_restriction_transfer_manager.js @@ -112,7 +112,7 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { STFactory: ${I_STFactory.address} GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} - LockupVolumeRestrictionTransferManagerFactory: + LockupVolumeRestrictionTransferManagerFactory: ${I_VolumeRestrictionTransferManagerFactory.address} ----------------------------------------------------------------------------- `); @@ -285,19 +285,11 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { await I_VolumeRestrictionTransferManager.unpause({from: token_owner}); }); - it("Should prevent the creation of a lockup with bad parameters where the totalAmount is zero", async() => { - // create a lockup - // this will generate an exception because the totalAmount is zero - await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 16, 4, 0, 0, { from: token_owner }) - ) - }); - it("Should prevent the creation of a lockup with bad parameters where the releaseFrequencySeconds is zero", async() => { // create a lockup // this will generate an exception because the releaseFrequencySeconds is zero await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 16, 0, 0, web3.utils.toWei('1', 'ether'), { from: token_owner }) + I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 16, 0, 0, { from: token_owner }) ); }); @@ -305,7 +297,7 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { // create a lockup // this will generate an exception because the lockUpPeriodSeconds is zero await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 0, 4, 0, web3.utils.toWei('1', 'ether'), { from: token_owner }) + I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 0, 4, 0, { from: token_owner }) ); }); @@ -314,7 +306,7 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { // create a lockup // this will generate an exception because we're locking up 5e17 tokens but the granularity is 5e18 tokens await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 16, 4, 0, web3.utils.toWei('0.5', 'ether'), { from: token_owner }) + I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 16, 4, 0, { from: token_owner }) ); }); @@ -328,7 +320,7 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { // over 17 seconds total, with 4 periods. // this will generate an exception because 17 is not evenly divisble by 4. await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 17, 4, 0, balance, { from: token_owner }) + I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 17, 4, 0, { from: token_owner }) ); }); @@ -338,7 +330,7 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { // over 16e18 seconds total, with 4e18 periods of 4 seconds each. // this will generate an exception because 16e18 / 4e18 = 4e18 but the token granularity is 1e18 and 1e18 % 4e18 != 0 await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, web3.utils.toWei('16', 'ether'), 4, 0, web3.utils.toWei('1', 'ether'), { from: token_owner }) + I_VolumeRestrictionTransferManager.addLockUp(account_investor2, web3.utils.toWei('16', 'ether'), 4, 0, { from: token_owner }) ); }); @@ -346,14 +338,14 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { // balance should be 9000000000000000000 here (9 eth) let balance = await I_SecurityToken.balanceOf(account_investor2) - + // create a lockup for their entire balance // over 16 seconds total, with 4 periods of 4 seconds each. // this will generate an exception because 9000000000000000000 / 4 = 2250000000000000000 but the token granularity is 1000000000000000000 await catchRevert( - I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 16, 4, 0, balance, { from: token_owner }) + I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 16, 4, 0, { from: token_owner }) ); - + }); it("Should prevent the transfer of tokens in a lockup", async() => { @@ -362,7 +354,7 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { console.log("balance", balance.dividedBy(new BigNumber(1).times(new BigNumber(10).pow(18))).toNumber()); // create a lockup for their entire balance // over 12 seconds total, with 3 periods of 4 seconds each. - await I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 12, 4, 0, balance, { from: token_owner }); + await I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 12, 4, 0, { from: token_owner }); // read only - check if transfer will pass. it should return INVALID let result = await I_VolumeRestrictionTransferManager.verifyTransfer.call(account_investor2, account_investor1, web3.utils.toWei('1', 'ether'), 0, false) @@ -415,7 +407,7 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { // create a lockup for their entire balance // over 16 seconds total, with 4 periods of 4 seconds each. - await I_VolumeRestrictionTransferManager.addLockUp(account_investor1, 16, 4, 0, balance, { from: token_owner }); + await I_VolumeRestrictionTransferManager.addLockUp(account_investor1, 16, 4, 0, { from: token_owner }); // let blockNumber = await web3.eth.getBlockNumber(); // console.log('blockNumber',blockNumber) @@ -425,20 +417,16 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }) ); - // check and get the lockup - let lockUpCount = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor1); - assert.equal(lockUpCount, 1) - let lockUp = await I_VolumeRestrictionTransferManager.getLockUp(account_investor1, 0); + let lockUp = await I_VolumeRestrictionTransferManager.getLockUp(account_investor1); // console.log(lockUp); - // elements in lockup array are uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime, uint totalAmount + // elements in lockup array are uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime assert.equal(lockUp[0].toString(), '16'); assert.equal(lockUp[1].toString(), '4'); assert.equal(lockUp[2].toNumber(), now); - assert.equal(lockUp[3].toString(), balance.toString()); // edit the lockup - await I_VolumeRestrictionTransferManager.modifyLockUp(account_investor1, 0, 8, 4, 0, balance, { from: token_owner }); + await I_VolumeRestrictionTransferManager.modifyLockUp(account_investor1, 8, 4, 0, { from: token_owner }); // attempt a transfer await catchRevert( @@ -453,20 +441,6 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { }); - it("Should succesfully modify the lockup - fail because array index out of bound", async() => { - // balance here should be 12000000000000000000 (12e18 or 12 eth) - let balance = await I_SecurityToken.balanceOf(account_investor1); - await catchRevert( - I_VolumeRestrictionTransferManager.modifyLockUp(account_investor1, 8, 8, 4, 0, balance, { from: token_owner }) - ); - }) - - it("Should succesfully get the lockup - fail because array index out of bound", async() => { - await catchRevert( - I_VolumeRestrictionTransferManager.getLockUp(account_investor1, 9) - ); - }) - it("Should be possible to remove a lockup -- couldn't transfer because of lock up", async() => { let acct1Balance = await I_SecurityToken.balanceOf(account_investor1) @@ -475,15 +449,8 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { I_SecurityToken.transfer(account_investor2, acct1Balance, { from: account_investor1 }) ); - // check and get the lockup - let lockUpCount = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor1); - assert.equal(lockUpCount, 1) - // remove the lockup - await I_VolumeRestrictionTransferManager.removeLockUp(account_investor1, 0, { from: token_owner }); - - lockUpCount = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor1); - assert.equal(lockUpCount, 0) + await I_VolumeRestrictionTransferManager.removeLockUp(account_investor1, { from: token_owner }); let acct2BalanceBefore = await I_SecurityToken.balanceOf(account_investor2) await I_SecurityToken.transfer(account_investor2, acct1Balance, { from: account_investor1 }); @@ -492,12 +459,6 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { assert.equal(acct2BalanceAfter.sub(acct2BalanceBefore).toString(), acct1Balance.toString()) }); - it("Should try to remove the lockup --failed because of index is out of bounds", async() => { - await catchRevert( - I_VolumeRestrictionTransferManager.removeLockUp(account_investor2, 7, { from: token_owner }) - ); - }) - it("Should be possible to create multiple lockups at once", async() => { let balancesBefore = {} @@ -510,23 +471,12 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { balancesBefore[account_investor3] = await I_SecurityToken.balanceOf(account_investor3) - let lockUpCountsBefore = {} - - // get lockups for acct 2 - lockUpCountsBefore[account_investor2] = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor2); - assert.equal(lockUpCountsBefore[account_investor2], 1) // there's one old, expired lockup on acct already - - // get lockups for acct 3 - lockUpCountsBefore[account_investor3] = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor3); - assert.equal(lockUpCountsBefore[account_investor3], 0) - // create lockups for their entire balances await I_VolumeRestrictionTransferManager.addLockUpMulti( [account_investor2, account_investor3], [24, 8], [4, 4], [0, 0], - [balancesBefore[account_investor2], balancesBefore[account_investor3]], { from: token_owner } ); @@ -545,16 +495,6 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { balancesAfter[account_investor3] = await I_SecurityToken.balanceOf(account_investor3) assert.equal(balancesBefore[account_investor3].toString(), balancesAfter[account_investor3].toString()) - let lockUpCountsAfter = {} - - // get lockups for acct 2 - lockUpCountsAfter[account_investor2] = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor2); - assert.equal(lockUpCountsAfter[account_investor2], 2); - - // get lockups for acct 3 - lockUpCountsAfter[account_investor3] = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor3); - assert.equal(lockUpCountsAfter[account_investor3], 1); - // wait 4 seconds await increaseTime(4000); @@ -572,7 +512,7 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { }); it("Should revert if the parameters are bad when creating multiple lockups", async() => { - + await catchRevert( // pass in the wrong number of params. txn should revert I_VolumeRestrictionTransferManager.addLockUpMulti( @@ -580,7 +520,6 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { [16, 8], [2], // this array should have 2 elements but it has 1, which should cause a revert [0, 0], - [web3.utils.toWei('1', 'ether'), web3.utils.toWei('1', 'ether')], { from: token_owner } ) ); @@ -588,20 +527,12 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { it("Should be possible to create a lockup with a specific start time in the future", async() => { - // remove all lockups for account 2 - let lockUpsLength = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor2); - assert.equal(lockUpsLength, 2); - await I_VolumeRestrictionTransferManager.removeLockUp(account_investor2, 0, { from: token_owner }); - await I_VolumeRestrictionTransferManager.removeLockUp(account_investor2, 0, { from: token_owner }); - lockUpsLength = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor2); - assert.equal(lockUpsLength, 0); - let now = latestTime(); // balance here should be 10000000000000000000 let balance = await I_SecurityToken.balanceOf(account_investor2) - await I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 100, 10, now + duration.seconds(4), balance, { from: token_owner }); + await I_VolumeRestrictionTransferManager.addLockUp(account_investor2, 100, 10, now + duration.seconds(4), { from: token_owner }); // wait 4 seconds for the lockup to begin await increaseTime(duration.seconds(4)); @@ -622,31 +553,23 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { let balance = await I_SecurityToken.balanceOf(account_investor2) // check and get the lockup - let lockUpCount = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor2); - assert.equal(lockUpCount, 1) - - let lockUp = await I_VolumeRestrictionTransferManager.getLockUp(account_investor2, 0); + let lockUp = await I_VolumeRestrictionTransferManager.getLockUp(account_investor2); // elements in lockup array are uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime, uint totalAmount assert.equal(lockUp[0].toString(), '100'); assert.equal(lockUp[1].toString(), '10'); assert.isAtMost(lockUp[2].toNumber(), now); - assert.equal(lockUp[3].toString(), balance.toString()); // edit the lockup - await I_VolumeRestrictionTransferManager.modifyLockUp(account_investor2, 0, 8, 4, now + duration.seconds(4), balance, { from: token_owner }); + await I_VolumeRestrictionTransferManager.modifyLockUp(account_investor2, 8, 4, now + duration.seconds(4), { from: token_owner }); // check and get the lockup again - lockUpCount = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor2); - assert.equal(lockUpCount, 1) - - lockUp = await I_VolumeRestrictionTransferManager.getLockUp(account_investor2, 0); + lockUp = await I_VolumeRestrictionTransferManager.getLockUp(account_investor2); // elements in lockup array are uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime, uint totalAmount assert.equal(lockUp[0].toString(), '8'); assert.equal(lockUp[1].toString(), '4'); assert.isAtMost(lockUp[2].toNumber(), now + 4); - assert.equal(lockUp[3].toString(), balance.toString()); // try a transfer. it should fail because again, the lockup hasn't started yet. await catchRevert( @@ -676,14 +599,12 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { // wait 4 seconds for the lockup's first period to elapse await increaseTime(duration.seconds(4)); - let lockUpBeforeVerify = await I_VolumeRestrictionTransferManager.getLockUp(account_investor2, 0); + let lockUpBeforeVerify = await I_VolumeRestrictionTransferManager.getLockUp(account_investor2); // check if transfer will pass in read-only operation let result = await I_VolumeRestrictionTransferManager.verifyTransfer.call(account_investor2, account_investor1, web3.utils.toWei('5', 'ether'), 0, false) // enum Result {INVALID, NA, VALID, FORCE_VALID} and we want VALID so it should be 2 assert.equal(result.toString(), '2') - let lockUpAfterVerify = await I_VolumeRestrictionTransferManager.getLockUp(account_investor2, 0); - - assert.equal(lockUpBeforeVerify[4].toString(), lockUpAfterVerify[4].toString()) + let lockUpAfterVerify = await I_VolumeRestrictionTransferManager.getLockUp(account_investor2); // try another transfer. it should pass await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor2 }); @@ -697,71 +618,6 @@ contract('LockupVolumeRestrictionTransferManager', accounts => { ); }); - it("Should be possible to stack lockups", async() => { - // should be 17000000000000000000 - let balance = await I_SecurityToken.balanceOf(account_investor1) - - // check and make sure that acct1 has no lockups so far - let lockUpCount = await I_VolumeRestrictionTransferManager.getLockUpsLength(account_investor1); - assert.equal(lockUpCount.toString(), 0) - - await I_VolumeRestrictionTransferManager.addLockUp(account_investor1, 12, 4, 0, web3.utils.toWei('6', 'ether'), { from: token_owner }); - - // try to transfer 11 tokens that aren't locked up yet be locked up. should succeed - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('11', 'ether'), { from: account_investor1 }); - - // try a transfer. it should fail because it's locked up from the first lockups - await catchRevert( - I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }) - ); - // wait 4 seconds for the lockup's first period to elapse. - await increaseTime(duration.seconds(4)); - - // should succeed - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('2', 'ether'), { from: account_investor1 }); - - // send 8 back to investor1 so that we can lock them up - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('8', 'ether'), { from: account_investor2 }); - - // let's add another lockup to stack them - await I_VolumeRestrictionTransferManager.addLockUp(account_investor1, 16, 4, 0, web3.utils.toWei('8', 'ether'), { from: token_owner }); - - // try a transfer. it should fail because it's locked up from both lockups - await catchRevert( - I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }) - ); - - // wait 4 seconds for the 1st lockup's second period to elapse, and the 2nd lockup's first period to elapse - await increaseTime(duration.seconds(4)); - - // should now be able to transfer 4, because of 2 allowed from the 1st lockup and 2 from the 2nd - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('4', 'ether'), { from: account_investor1 }); - - // try aother transfer. it should fail because it's locked up from both lockups again - await catchRevert( - I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }) - ); - - // wait 4 seconds for the 1st lockup's final period to elapse, and the 2nd lockup's second period to elapse - await increaseTime(duration.seconds(4)); - // should now be able to transfer 4, because of 2 allowed from the 1st lockup and 2 from the 2nd - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('4', 'ether'), { from: account_investor1 }); - - // wait 8 seconds for 2nd lockup's third and fourth periods to elapse - await increaseTime(duration.seconds(8)); - // should now be able to transfer 4, because there are 2 allowed per period in the 2nd lockup, and 2 periods have elapsed - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('4', 'ether'), { from: account_investor1 }); - - // send the 3 back from acct2 that we sent over in the beginning of this test - await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3', 'ether'), { from: account_investor2 }); - - // try another transfer. it should pass because both lockups have been entirely used - await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }); - - balance = await I_SecurityToken.balanceOf(account_investor1) - assert.equal(balance.toString(), web3.utils.toWei('2', 'ether')) - }); - it("Should get configuration function signature", async() => { let sig = await I_VolumeRestrictionTransferManager.getInitFunction.call();