From 02cbdd9e5ecd6dec1527ad81aa6bcb641a74c15c Mon Sep 17 00:00:00 2001 From: Tan Pham Date: Tue, 16 Oct 2018 18:13:57 +0700 Subject: [PATCH 1/4] change verify function --- .../LockupVolumeTransferManager.sol | 145 ++++ .../LockupVolumeTransferManagerFactory.sol | 104 +++ test/helpers/createInstances.js | 15 + ..._volume_restriction_transfer_manager.1.js} | 0 test/w_lockup_volume_transfer_manager.js | 818 ++++++++++++++++++ 5 files changed, 1082 insertions(+) create mode 100644 contracts/modules/TransferManager/LockupVolumeTransferManager.sol create mode 100644 contracts/modules/TransferManager/LockupVolumeTransferManagerFactory.sol rename test/{w_lockup_volume_restriction_transfer_manager.js => w_lockup_volume_restriction_transfer_manager.1.js} (100%) create mode 100644 test/w_lockup_volume_transfer_manager.js diff --git a/contracts/modules/TransferManager/LockupVolumeTransferManager.sol b/contracts/modules/TransferManager/LockupVolumeTransferManager.sol new file mode 100644 index 000000000..ab5240aa0 --- /dev/null +++ b/contracts/modules/TransferManager/LockupVolumeTransferManager.sol @@ -0,0 +1,145 @@ +pragma solidity ^0.4.24; + +import "./ITransferManager.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; + + +contract LockupVolumeTransferManager is ITransferManager { + + using SafeMath for uint256; + + bytes32 public constant ADMIN = "ADMIN"; + + struct LockUp { + uint startTime; + uint lockUpPeriod; + uint releaseFrequency; + uint totalLockUp; + } + + event PrintOut ( + uint _number + ); + + event AddLockUp ( + address indexed investor, + uint startTime, + uint lockUpPeriod, + uint releaseFrequency, + uint totalLockUp + ); + + mapping (address => LockUp[]) internal investerToLockUps; + + /** + * @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 Used to verify the transfer transaction and prevent locked up tokens from being transferred + * @param _from Address of the sender + * @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) { + + if (!paused) { + + uint currentLockUp = _calculateCurrentLockUp(_from); + uint currentBalance = ISecurityToken(securityToken).balanceOf(_from); + uint allowTransfer = currentBalance.sub(currentLockUp); + + if ( allowTransfer >= _amount) { + + if(!_isTransfer){ + return Result.VALID; + } + + require(msg.sender == securityToken, "Sender is not securityToken"); + + return Result.VALID; + } + + return Result.INVALID; + } + + return Result.NA; + } + + + function _calculateCurrentLockUp(address _from) internal returns(uint) { + + uint totalCurrentLockUp = 0; + + for (uint i = 0; i < investerToLockUps[_from].length; i++) { + + LockUp storage aLockUp = investerToLockUps[_from][i]; + + // only active lockup + if (now >= aLockUp.startTime && now <= aLockUp.startTime.add(aLockUp.lockUpPeriod)) { + + // calculate current lockup + uint elepsedPeriods = now.sub(aLockUp.startTime).div(aLockUp.releaseFrequency); + uint totalPeriods = aLockUp.lockUpPeriod.div(aLockUp.releaseFrequency); + uint amountPerPeriod = aLockUp.totalLockUp.div(totalPeriods); + uint currentLockUp = totalPeriods.sub(elepsedPeriods).mul(amountPerPeriod); + + // accumulate current lockup + totalCurrentLockUp.add(currentLockUp); + } + } + + // emit PrintOut(currentTotalLockUp); + return totalCurrentLockUp; + } + + + function addLockUp( + address _investor, + uint _startTime, + uint _lockUpPeriod, + uint _releaseFrequency, + uint _totalLockUp + ) + public + withPerm(ADMIN) + { + if (_startTime == 0) { + _startTime = now; + } + + LockUp memory newLockUp = LockUp(_startTime, _lockUpPeriod, _releaseFrequency, _totalLockUp); + investerToLockUps[_investor].push(newLockUp); + + emit AddLockUp( + _investor, + _startTime, + _lockUpPeriod, + _releaseFrequency, + _totalLockUp + ); + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() public pure returns (bytes4) { + return bytes4(0); + } + + /** + * @notice Return the permissions flag that are associated with Percentage transfer Manager + */ + function getPermissions() public view returns(bytes32[]) { + bytes32[] memory allPermissions = new bytes32[](1); + allPermissions[0] = ADMIN; + return allPermissions; + } +} diff --git a/contracts/modules/TransferManager/LockupVolumeTransferManagerFactory.sol b/contracts/modules/TransferManager/LockupVolumeTransferManagerFactory.sol new file mode 100644 index 000000000..e58622ea2 --- /dev/null +++ b/contracts/modules/TransferManager/LockupVolumeTransferManagerFactory.sol @@ -0,0 +1,104 @@ +pragma solidity ^0.4.24; + +import "./LockupVolumeTransferManager.sol"; +import "../ModuleFactory.sol"; + +/** + * @title Factory for deploying LockupVolumeTransferManagerFactory module + */ +contract LockupVolumeTransferManagerFactory is ModuleFactory { + + /** + * @notice Constructor + * @param _polyAddress Address of the polytoken + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _subscriptionCost Subscription cost of the module + */ + constructor (address _polyAddress, uint256 _setupCost, uint256 _usageCost, uint256 _subscriptionCost) public + ModuleFactory(_polyAddress, _setupCost, _usageCost, _subscriptionCost) + { + version = "1.0.0"; + name = "LockupVolumeRestrictionTM"; + title = "Lockup Volume Restriction Transfer Manager"; + description = "Manage transfers using lock ups over time"; + compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + } + + /** + * @notice used to launch the Module with the help of factory + * @return address Contract address of the Module + */ + function deploy(bytes /* _data */) external returns(address) { + if (setupCost > 0) + require(polyToken.transferFrom(msg.sender, owner, setupCost), "Failed transferFrom because of sufficent Allowance is not provided"); + LockupVolumeTransferManager lockupVolumeTransferManager = new LockupVolumeTransferManager(msg.sender, address(polyToken)); + emit GenerateModuleFromFactory(address(lockupVolumeTransferManager), getName(), address(this), msg.sender, now); + return address(lockupVolumeTransferManager); + } + + /** + * @notice Type of the Module factory + * @return uint8 + */ + function getTypes() external view returns(uint8[]) { + uint8[] memory res = new uint8[](1); + res[0] = 2; + return res; + } + + /** + * @notice Get the name of the Module + */ + function getName() public view returns(bytes32) { + return name; + } + + /** + * @notice Get the description of the Module + */ + function getDescription() external view returns(string) { + return description; + } + + /** + * @notice Get the title of the Module + */ + function getTitle() external view returns(string) { + return title; + } + + /** + * @notice Get the version of the Module + */ + function getVersion() external view returns(string) { + return version; + } + + /** + * @notice Get the setup cost of the module + */ + function getSetupCost() external view returns (uint256) { + return setupCost; + } + + /** + * @notice Returns the instructions associated with the module + */ + function getInstructions() external view returns(string) { + return "Allows an issuer to set lockup periods for user addresses, with funds distributed over time. Init function takes no parameters."; + } + + /** + * @notice Get the tags related to the module factory + */ + function getTags() external view returns(bytes32[]) { + bytes32[] memory availableTags = new bytes32[](2); + availableTags[0] = "Volume"; + availableTags[1] = "Transfer Restriction"; + return availableTags; + } + + +} diff --git a/test/helpers/createInstances.js b/test/helpers/createInstances.js index 82a77e42a..c9572828b 100644 --- a/test/helpers/createInstances.js +++ b/test/helpers/createInstances.js @@ -23,6 +23,7 @@ const GeneralTransferManagerFactory = artifacts.require("./GeneralTransferManage const GeneralPermissionManagerFactory = artifacts.require("./GeneralPermissionManagerFactory.sol"); const CountTransferManagerFactory = artifacts.require("./CountTransferManagerFactory.sol"); const VolumeRestrictionTransferManagerFactory = artifacts.require("./LockupVolumeRestrictionTMFactory"); +const LockupVolumeTransferManagerFactory = artifacts.require("./LockupVolumeTransferManagerFactory"); const PreSaleSTOFactory = artifacts.require("./PreSaleSTOFactory.sol"); const PolyToken = artifacts.require("./PolyToken.sol"); const PolyTokenFaucet = artifacts.require("./PolyTokenFaucet.sol"); @@ -38,6 +39,7 @@ let I_TrackedRedemptionFactory; let I_SingleTradeVolumeRestrictionManagerFactory; let I_ManualApprovalTransferManagerFactory; let I_VolumeRestrictionTransferManagerFactory; +let I_LockupVolumeTransferManagerFactory; let I_PercentageTransferManagerFactory; let I_EtherDividendCheckpointFactory; let I_CountTransferManagerFactory; @@ -249,6 +251,19 @@ export async function deployLockupVolumeRTMAndVerified(accountPolymath, MRProxyI return new Array(I_VolumeRestrictionTransferManagerFactory); } +export async function deployLockupVolumeTMAndVerified(accountPolymath, MRProxyInstance, polyToken, setupCost) { + I_LockupVolumeTransferManagerFactory = await LockupVolumeTransferManagerFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); + assert.notEqual( + I_LockupVolumeTransferManagerFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "LockupVolumeTransferManagerFactory contract was not deployed" + ); + + await registerAndVerifyByMR(I_LockupVolumeTransferManagerFactory.address, accountPolymath, MRProxyInstance); + return new Array(I_LockupVolumeTransferManagerFactory); +} + + export async function deploySingleTradeVolumeRMAndVerified(accountPolymath, MRProxyInstance, polyToken, setupCost) { I_SingleTradeVolumeRestrictionManagerFactory = await SingleTradeVolumeRestrictionManagerFactory.new(polyToken, setupCost, 0, 0, { from: accountPolymath }); assert.notEqual( diff --git a/test/w_lockup_volume_restriction_transfer_manager.js b/test/w_lockup_volume_restriction_transfer_manager.1.js similarity index 100% rename from test/w_lockup_volume_restriction_transfer_manager.js rename to test/w_lockup_volume_restriction_transfer_manager.1.js diff --git a/test/w_lockup_volume_transfer_manager.js b/test/w_lockup_volume_transfer_manager.js new file mode 100644 index 000000000..2faae4b4a --- /dev/null +++ b/test/w_lockup_volume_transfer_manager.js @@ -0,0 +1,818 @@ +import latestTime from './helpers/latestTime'; +import { duration, promisifyLogWatch, latestBlock } from './helpers/utils'; +import takeSnapshot, { increaseTime, revertToSnapshot } from './helpers/time'; +import { encodeProxyCall } from './helpers/encodeCall'; +import { setUpPolymathNetwork, deployLockupVolumeTMAndVerified } from "./helpers/createInstances"; +import { catchRevert } from "./helpers/exceptions"; + +const SecurityToken = artifacts.require('./SecurityToken.sol'); +const GeneralTransferManager = artifacts.require('./GeneralTransferManager'); +const LockupVolumeTransferManager = artifacts.require('./LockupVolumeTransferManager'); +const GeneralPermissionManager = artifacts.require('./GeneralPermissionManager'); + +const Web3 = require('web3'); +const BigNumber = require('bignumber.js'); +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")) // Hardcoded development port + +contract('LockupVolumeTransferManager', accounts => { + + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + + // investor Details + let fromTime = latestTime(); + let toTime = latestTime(); + let expiryTime = toTime + duration.days(15); + + let message = "Transaction Should Fail!"; + + // Contract Instance Declaration + let P_LockupVolumeTransferManagerFactory; + let I_SecurityTokenRegistryProxy; + let P_LockupVolumeTransferManager; + let I_GeneralTransferManagerFactory; + let I_LockupVolumeTransferManagerFactory; + let I_GeneralPermissionManager; + let I_LockupVolumeTransferManager; + let I_GeneralTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_SecurityTokenRegistry; + let I_STRProxied; + let I_MRProxied; + let I_STFactory; + let I_SecurityToken; + let I_PolyToken; + let I_PolymathRegistry; + + // SecurityToken Details + const name = "Team"; + const symbol = "sap"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + const contact = "team@polymath.network"; + + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + + // Initial fee for ticker registry and security token registry + const initRegFee = web3.utils.toWei("250"); + + before(async() => { + // Accounts setup + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + + account_investor1 = accounts[7]; + account_investor2 = accounts[8]; + account_investor3 = accounts[9]; + + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied + ] = instances; + + // STEP 4(c): Deploy the LockupVolumeTransferManager + [I_LockupVolumeTransferManagerFactory] = await deployLockupVolumeTMAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, 0); + // STEP 4(d): Deploy the LockupVolumeTransferManager + [P_LockupVolumeTransferManagerFactory] = await deployLockupVolumeTMAndVerified(account_polymath, I_MRProxied, I_PolyToken.address, web3.utils.toWei("500")); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistry: ${I_ModuleRegistry.address} + ModuleRegistryProxy: ${I_ModuleRegistryProxy.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + + LockupLockupVolumeTransferManagerFactory: + ${I_LockupVolumeTransferManagerFactory.address} + ----------------------------------------------------------------------------- + `); + }); + + describe("Generate the SecurityToken", async() => { + + it("Should register the ticker before the generation of the security token", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, contact, { from : token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol.toUpperCase()); + }); + + it("Should generate the new security token with the same symbol as registered above", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let _blockNo = latestBlock(); + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, { from: token_owner }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[1].args._ticker, symbol.toUpperCase(), "SecurityToken doesn't get deployed"); + + I_SecurityToken = SecurityToken.at(tx.logs[1].args._securityTokenAddress); + + const log = await promisifyLogWatch(I_SecurityToken.ModuleAdded({from: _blockNo}), 1); + + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toNumber(), 2); + assert.equal( + web3.utils.toAscii(log.args._name) + .replace(/\u0000/g, ''), + "GeneralTransferManager" + ); + }); + + it("Should intialize the auto attached modules", async () => { + let moduleData = (await I_SecurityToken.getModulesByType(2))[0]; + I_GeneralTransferManager = GeneralTransferManager.at(moduleData); + }); + + }); + + describe("Buy tokens using on-chain whitelist and test locking them up and attempting to transfer", async() => { + + it("Should Buy the tokens", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyWhitelist( + account_investor1, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor1.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Jump time + await increaseTime(5000); + + // Mint some tokens + await I_SecurityToken.mint(account_investor1, web3.utils.toWei('2', 'ether'), { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), + web3.utils.toWei('2', 'ether') + ); + }); + + it("Should Buy some more tokens", async() => { + // Add the Investor in to the whitelist + + let tx = await I_GeneralTransferManager.modifyWhitelist( + account_investor2, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer + }); + + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor2.toLowerCase(), "Failed in adding the investor in whitelist"); + + // Mint some tokens + await I_SecurityToken.mint(account_investor2, web3.utils.toWei('10', 'ether'), { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor2)).toNumber(), + web3.utils.toWei('10', 'ether') + ); + }); + + it("Should unsuccessfully attach the LockupVolumeTransferManager factory with the security token -- failed because Token is not paid", async () => { + await I_PolyToken.getTokens(web3.utils.toWei("500", "ether"), token_owner); + await catchRevert( + I_SecurityToken.addModule(P_LockupVolumeTransferManagerFactory.address, 0, web3.utils.toWei("500", "ether"), 0, { from: token_owner }) + ) + }); + + it("Should successfully attach the LockupVolumeTransferManager factory with the security token", async () => { + let snapId = await takeSnapshot(); + await I_PolyToken.transfer(I_SecurityToken.address, web3.utils.toWei("500", "ether"), {from: token_owner}); + const tx = await I_SecurityToken.addModule(P_LockupVolumeTransferManagerFactory.address, 0, web3.utils.toWei("500", "ether"), 0, { from: token_owner }); + assert.equal(tx.logs[3].args._types[0].toNumber(), transferManagerKey, "LockupVolumeTransferManagerFactory doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[3].args._name) + .replace(/\u0000/g, ''), + "LockupVolumeRestrictionTM", + "LockupVolumeTransferManagerFactory module was not added" + ); + P_LockupVolumeTransferManager = LockupVolumeTransferManager.at(tx.logs[3].args._module); + await revertToSnapshot(snapId); + }); + + it("Should successfully attach the LockupVolumeTransferManager with the security token", async () => { + const tx = await I_SecurityToken.addModule(I_LockupVolumeTransferManagerFactory.address, 0, 0, 0, { from: token_owner }); + assert.equal(tx.logs[2].args._types[0].toNumber(), transferManagerKey, "LockupVolumeTransferManager doesn't get deployed"); + assert.equal( + web3.utils.toAscii(tx.logs[2].args._name) + .replace(/\u0000/g, ''), + "LockupVolumeRestrictionTM", + "LockupVolumeTransferManager module was not added" + ); + I_LockupVolumeTransferManager = LockupVolumeTransferManager.at(tx.logs[2].args._module); + + + const tx_add = await I_LockupVolumeTransferManager.addLockUp(account_investor1, + 0, + 3600, + 3, + 10000, {from: token_owner}); + + console.log(tx_add.logs); + + await new Promise(resolve => setTimeout(resolve, 4000)); + + const tx_verify = await I_LockupVolumeTransferManager.verifyTransfer(account_investor1, + account_investor2, + 200, + "", + true); + + // print still in lockup + console.log(tx_verify.logs[0].args._number.toNumber()); + }); + + // it("Add a new token holder", async() => { + + // let tx = await I_GeneralTransferManager.modifyWhitelist( + // account_investor3, + // latestTime(), + // latestTime(), + // latestTime() + duration.days(10), + // true, + // { + // from: account_issuer + // }); + + // assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor3.toLowerCase(), "Failed in adding the investor in whitelist"); + + // // Add the Investor in to the whitelist + // // Mint some tokens + // await I_SecurityToken.mint(account_investor3, web3.utils.toWei('10', 'ether'), { from: token_owner }); + + // assert.equal( + // (await I_SecurityToken.balanceOf(account_investor3)).toNumber(), + // web3.utils.toWei('10', 'ether') + // ); + // }); + + // it("Should pause the tranfers at transferManager level", async() => { + // let tx = await I_LockupVolumeTransferManager.pause({from: token_owner}); + // }); + + // it("Should still be able to transfer between existing token holders up to limit", async() => { + // // Add the Investor in to the whitelist + // // Mint some tokens + // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }); + + // assert.equal( + // (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), + // web3.utils.toWei('3', 'ether') + // ); + // }); + + // it("Should unpause the tranfers at transferManager level", async() => { + // await I_LockupVolumeTransferManager.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_LockupVolumeTransferManager.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_LockupVolumeTransferManager.addLockUp(account_investor2, 16, 0, 0, web3.utils.toWei('1', 'ether'), { from: token_owner }) + // ); + // }); + + // it("Should prevent the creation of a lockup with bad parameters where the lockUpPeriodSeconds is zero", async() => { + // // create a lockup + // // this will generate an exception because the lockUpPeriodSeconds is zero + // await catchRevert( + // I_LockupVolumeTransferManager.addLockUp(account_investor2, 0, 4, 0, web3.utils.toWei('1', 'ether'), { from: token_owner }) + // ); + // }); + + + // it("Should prevent the creation of a lockup with bad parameters where the total amount to be released is more granular than allowed by the token", async() => { + // // create a lockup + // // this will generate an exception because we're locking up 5e17 tokens but the granularity is 5e18 tokens + // await catchRevert( + // I_LockupVolumeTransferManager.addLockUp(account_investor2, 16, 4, 0, web3.utils.toWei('0.5', 'ether'), { from: token_owner }) + // ); + // }); + + // it("Should prevent the creation of a lockup with bad parameters where the lockUpPeriodSeconds is not evenly divisible by releaseFrequencySeconds", async() => { + + // // balance should be 9000000000000000000 here (9 eth) + // let balance = await I_SecurityToken.balanceOf(account_investor2) + + + // // create a lockup + // // over 17 seconds total, with 4 periods. + // // this will generate an exception because 17 is not evenly divisble by 4. + // await catchRevert( + // I_LockupVolumeTransferManager.addLockUp(account_investor2, 17, 4, 0, balance, { from: token_owner }) + // ); + // }); + + // it("Should prevent the creation of a lockup with bad parameters where the total amount being locked up isn't evenly divisible by the number of total periods", async() => { + + // // create a lockup for a balance of 1 eth + // // 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_LockupVolumeTransferManager.addLockUp(account_investor2, web3.utils.toWei('16', 'ether'), 4, 0, web3.utils.toWei('1', 'ether'), { from: token_owner }) + // ); + // }); + + // it("Should prevent the creation of a lockup with bad parameters where the amount to be released per period is too granular for the token", async() => { + + // // 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_LockupVolumeTransferManager.addLockUp(account_investor2, 16, 4, 0, balance, { from: token_owner }) + // ); + + // }); + + // it("Should prevent the transfer of tokens in a lockup", async() => { + + // let balance = await I_SecurityToken.balanceOf(account_investor2) + + // // create a lockup for their entire balance + // // over 12 seconds total, with 3 periods of 4 seconds each. + // await I_LockupVolumeTransferManager.addLockUp(account_investor2, 12, 4, 0, balance, { from: token_owner }); + + // // read only - check if transfer will pass. it should return INVALID + // let result = await I_LockupVolumeTransferManager.verifyTransfer.call(account_investor2, account_investor1, web3.utils.toWei('1', 'ether'), 0, false) + // // enum Result {INVALID, NA, VALID, FORCE_VALID} and we want VALID so it should be 2 + // assert.equal(result.toString(), '0') + + // await catchRevert( + // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) + // ); + // }); + + // it("Should allow the transfer of tokens in a lockup if a period has passed", async() => { + + // // wait 4 seconds + // await new Promise(resolve => setTimeout(resolve, 4000)); + + // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3', 'ether'), { from: account_investor2 }); + // }); + + // it("Should prevent the transfer of tokens if the amount is larger than the amount allowed by lockups", async() => { + + // await catchRevert( + // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('4', 'ether'), { from: account_investor2 }) + // ); + // }); + + // it("Should allow the transfer of more tokens in a lockup if another period has passed", async() => { + + // // wait 4 more seconds + // await new Promise(resolve => setTimeout(resolve, 4000)); + + // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3', 'ether'), { from: account_investor2 }); + // }); + + // it("Should allow the transfer of all tokens in a lockup if the entire lockup has passed", async() => { + + // let balance = await I_SecurityToken.balanceOf(account_investor2) + + // // wait 4 more seconds + // await new Promise(resolve => setTimeout(resolve, 4000)); + + // await I_SecurityToken.transfer(account_investor1, balance, { from: account_investor2 }); + // }); + + // it("Should prevent the transfer of tokens in an edited lockup", async() => { + + + // // balance here should be 12000000000000000000 (12e18 or 12 eth) + // let balance = await I_SecurityToken.balanceOf(account_investor1) + + // // create a lockup for their entire balance + // // over 16 seconds total, with 4 periods of 4 seconds each. + // await I_LockupVolumeTransferManager.addLockUp(account_investor1, 16, 4, 0, balance, { from: token_owner }); + + // // let blockNumber = await web3.eth.getBlockNumber(); + // // console.log('blockNumber',blockNumber) + // let now = (await web3.eth.getBlock('latest')).timestamp + + // await catchRevert( + // I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }) + // ); + + // // check and get the lockup + // let lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor1); + // assert.equal(lockUpCount, 1) + + // let lockUp = await I_LockupVolumeTransferManager.getLockUp(account_investor1, 0); + // // console.log(lockUp); + // // elements in lockup array are uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime, uint totalAmount + // 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_LockupVolumeTransferManager.modifyLockUp(account_investor1, 0, 8, 4, 0, balance, { from: token_owner }); + + // // attempt a transfer + // await catchRevert( + // I_SecurityToken.transfer(account_investor2, web3.utils.toWei('6', 'ether'), { from: account_investor1 }) + // ); + + // // wait 4 seconds + // await new Promise(resolve => setTimeout(resolve, 4000)); + + // // transfer should succeed + // await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('6', 'ether'), { from: account_investor1 }); + + // }); + + // it("Should be possible to remove a lockup -- couldn't transfer because of lock up", async() => { + + // let acct1Balance = await I_SecurityToken.balanceOf(account_investor1) + + // await catchRevert( + // I_SecurityToken.transfer(account_investor2, acct1Balance, { from: account_investor1 }) + // ); + + // // check and get the lockup + // let lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor1); + // assert.equal(lockUpCount, 1) + + // // remove the lockup + // await I_LockupVolumeTransferManager.removeLockUp(account_investor1, 0, { from: token_owner }); + + // lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor1); + // assert.equal(lockUpCount, 0) + + // let acct2BalanceBefore = await I_SecurityToken.balanceOf(account_investor2) + // await I_SecurityToken.transfer(account_investor2, acct1Balance, { from: account_investor1 }); + // let acct2BalanceAfter = await I_SecurityToken.balanceOf(account_investor2) + + // assert.equal(acct2BalanceAfter.sub(acct2BalanceBefore).toString(), acct1Balance.toString()) + // }); + + // it("Should be possible to create multiple lockups at once", async() => { + + // let balancesBefore = {} + + // // should be 12000000000000000000 + // balancesBefore[account_investor2] = await I_SecurityToken.balanceOf(account_investor2) + + + // // should be 10000000000000000000 + // balancesBefore[account_investor3] = await I_SecurityToken.balanceOf(account_investor3) + + + // let lockUpCountsBefore = {} + + // // get lockups for acct 2 + // lockUpCountsBefore[account_investor2] = await I_LockupVolumeTransferManager.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_LockupVolumeTransferManager.getLockUpsLength(account_investor3); + // assert.equal(lockUpCountsBefore[account_investor3], 0) + + // // create lockups for their entire balances + // await I_LockupVolumeTransferManager.addLockUpMulti( + // [account_investor2, account_investor3], + // [24, 8], + // [4, 4], + // [0, 0], + // [balancesBefore[account_investor2], balancesBefore[account_investor3]], + // { from: token_owner } + // ); + + // await catchRevert( + // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('2', 'ether'), { from: account_investor2 }) + // ); + + // await catchRevert( + // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor3 }) + // ); + + // let balancesAfter = {} + // balancesAfter[account_investor2] = await I_SecurityToken.balanceOf(account_investor2) + // assert.equal(balancesBefore[account_investor2].toString(), balancesAfter[account_investor2].toString()) + + // 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_LockupVolumeTransferManager.getLockUpsLength(account_investor2); + // assert.equal(lockUpCountsAfter[account_investor2], 2); + + // // get lockups for acct 3 + // lockUpCountsAfter[account_investor3] = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor3); + // assert.equal(lockUpCountsAfter[account_investor3], 1); + + // // wait 4 seconds + // await new Promise(resolve => setTimeout(resolve, 4000)); + + // // try transfers again + // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('2', 'ether'), { from: account_investor2 }); + // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor3 }); + + + // balancesAfter[account_investor2] = await I_SecurityToken.balanceOf(account_investor2) + // assert.equal(balancesBefore[account_investor2].sub(web3.utils.toWei('2', 'ether')).toString(), balancesAfter[account_investor2].toString()) + + // balancesAfter[account_investor3] = await I_SecurityToken.balanceOf(account_investor3) + // assert.equal(balancesBefore[account_investor3].sub(web3.utils.toWei('5', 'ether')).toString(), balancesAfter[account_investor3].toString()) + + // }); + + // 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_LockupVolumeTransferManager.addLockUpMulti( + // [account_investor2, account_investor3], + // [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 } + // ) + // ); + // }); + + // 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_LockupVolumeTransferManager.getLockUpsLength(account_investor2); + // assert.equal(lockUpsLength, 2); + // await I_LockupVolumeTransferManager.removeLockUp(account_investor2, 0, { from: token_owner }); + // await I_LockupVolumeTransferManager.removeLockUp(account_investor2, 0, { from: token_owner }); + // lockUpsLength = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor2); + // assert.equal(lockUpsLength, 0); + + // let now = (await web3.eth.getBlock('latest')).timestamp + + // // balance here should be 10000000000000000000 + // let balance = await I_SecurityToken.balanceOf(account_investor2) + + // await I_LockupVolumeTransferManager.addLockUp(account_investor2, 100, 10, now + 4, balance, { from: token_owner }); + + // // try a transfer. it should fail because the lockup hasn't started yet. + // await catchRevert( + // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) + // ); + // now = (await web3.eth.getBlock('latest')).timestamp + + // // wait 4 seconds for the lockup to begin + // await new Promise(resolve => setTimeout(resolve, 4000)); + + // // try another transfer. it should also fail because the lockup has just begun + // await catchRevert( + // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) + // ); + // now = (await web3.eth.getBlock('latest')).timestamp + + // }); + + // it("Should be possible to edit a lockup with a specific start time in the future", async() => { + + // // edit the lockup + // let now = (await web3.eth.getBlock('latest')).timestamp + + // // should be 10000000000000000000 + // let balance = await I_SecurityToken.balanceOf(account_investor2) + + // // check and get the lockup + // let lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor2); + // assert.equal(lockUpCount, 1) + + // let lockUp = await I_LockupVolumeTransferManager.getLockUp(account_investor2, 0); + + // // 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_LockupVolumeTransferManager.modifyLockUp(account_investor2, 0, 8, 4, now + 4, balance, { from: token_owner }); + + // // check and get the lockup again + // lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor2); + // assert.equal(lockUpCount, 1) + + // lockUp = await I_LockupVolumeTransferManager.getLockUp(account_investor2, 0); + + // // 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( + // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) + // ); + + // // wait 4 seconds for the lockup to begin + // await new Promise(resolve => setTimeout(resolve, 4000)); + + // // try another transfer. it should fail because the lockup has just begun + // await catchRevert( + // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) + // ); + + // // wait 4 seconds for the lockup's first period to elapse + // await new Promise(resolve => setTimeout(resolve, 4000)); + + // // try another transfer. it should pass + // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor2 }); + + + // // try another transfer without waiting for another period to pass. it should fail + // await catchRevert( + // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor2 }) + // ); + + // // wait 4 seconds for the lockup's first period to elapse + // await new Promise(resolve => setTimeout(resolve, 4000)); + + // let lockUpBeforeVerify = await I_LockupVolumeTransferManager.getLockUp(account_investor2, 0); + // // check if transfer will pass in read-only operation + // let result = await I_LockupVolumeTransferManager.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_LockupVolumeTransferManager.getLockUp(account_investor2, 0); + + // assert.equal(lockUpBeforeVerify[4].toString(), lockUpAfterVerify[4].toString()) + + // // try another transfer. it should pass + // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor2 }); + + // // wait 4 seconds for the lockup's first period to elapse. but, we are all out of periods. + // await new Promise(resolve => setTimeout(resolve, 4000)); + + // // try one final transfer. this should fail because the user has already withdrawn their entire balance + // await catchRevert( + // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) + // ); + // }); + + // 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_LockupVolumeTransferManager.getLockUpsLength(account_investor1); + // assert.equal(lockUpCount.toString(), 0) + + // await I_LockupVolumeTransferManager.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 new Promise(resolve => setTimeout(resolve, 4000)); + + + // // 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_LockupVolumeTransferManager.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 new Promise(resolve => setTimeout(resolve, 4000)); + + // // 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 new Promise(resolve => setTimeout(resolve, 4000)); + + // // 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 new Promise(resolve => setTimeout(resolve, 8000)); + + // // 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_LockupVolumeTransferManager.getInitFunction.call(); + // assert.equal(web3.utils.hexToNumber(sig), 0); + // }); + + + // it("Should get the permission", async() => { + // let perm = await I_LockupVolumeTransferManager.getPermissions.call(); + // assert.equal(perm.length, 1); + // // console.log(web3.utils.toAscii(perm[0]).replace(/\u0000/g, '')) + // assert.equal(web3.utils.toAscii(perm[0]).replace(/\u0000/g, ''), "ADMIN") + // }); + + }); + + // describe("VolumeRestriction Transfer Manager Factory test cases", async() => { + + // it("Should get the exact details of the factory", async() => { + // assert.equal(await I_LockupVolumeTransferManagerFactory.setupCost.call(),0); + // assert.equal((await I_LockupVolumeTransferManagerFactory.getTypes.call())[0],2); + // assert.equal(web3.utils.toAscii(await I_LockupVolumeTransferManagerFactory.getName.call()) + // .replace(/\u0000/g, ''), + // "LockupVolumeRestrictionTM", + // "Wrong Module added"); + // assert.equal(await I_LockupVolumeTransferManagerFactory.getDescription.call(), + // "Manage transfers using lock ups over time", + // "Wrong Module added"); + // assert.equal(await I_LockupVolumeTransferManagerFactory.getTitle.call(), + // "Lockup Volume Restriction Transfer Manager", + // "Wrong Module added"); + // assert.equal(await I_LockupVolumeTransferManagerFactory.getInstructions.call(), + // "Allows an issuer to set lockup periods for user addresses, with funds distributed over time. Init function takes no parameters.", + // "Wrong Module added"); + + // }); + + // it("Should get the tags of the factory", async() => { + // let tags = await I_LockupVolumeTransferManagerFactory.getTags.call(); + // assert.equal(web3.utils.toAscii(tags[0]).replace(/\u0000/g, ''), "Volume"); + // }); + // }); + +}); From 4c558fd7d6959245632918ac0800f5e88258d732 Mon Sep 17 00:00:00 2001 From: Tan Pham Date: Tue, 16 Oct 2018 21:15:17 +0700 Subject: [PATCH 2/4] better naming --- .../LockupVolumeTransferManager.sol | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/contracts/modules/TransferManager/LockupVolumeTransferManager.sol b/contracts/modules/TransferManager/LockupVolumeTransferManager.sol index ab5240aa0..f61662695 100644 --- a/contracts/modules/TransferManager/LockupVolumeTransferManager.sol +++ b/contracts/modules/TransferManager/LockupVolumeTransferManager.sol @@ -14,7 +14,7 @@ contract LockupVolumeTransferManager is ITransferManager { uint startTime; uint lockUpPeriod; uint releaseFrequency; - uint totalLockUp; + uint lockUpAmount; } event PrintOut ( @@ -26,10 +26,10 @@ contract LockupVolumeTransferManager is ITransferManager { uint startTime, uint lockUpPeriod, uint releaseFrequency, - uint totalLockUp + uint lockUpAmount ); - mapping (address => LockUp[]) internal investerToLockUps; + mapping (address => LockUp[]) internal investorToLockUps; /** * @notice Constructor @@ -52,11 +52,11 @@ contract LockupVolumeTransferManager is ITransferManager { if (!paused) { - uint currentLockUp = _calculateCurrentLockUp(_from); + uint currentLockUp = _getInvestorCurrentLockUpAmount(_from); uint currentBalance = ISecurityToken(securityToken).balanceOf(_from); - uint allowTransfer = currentBalance.sub(currentLockUp); + uint currentAllowTransfer = currentBalance.sub(currentLockUp); - if ( allowTransfer >= _amount) { + if ( currentAllowTransfer >= _amount) { if(!_isTransfer){ return Result.VALID; @@ -74,30 +74,37 @@ contract LockupVolumeTransferManager is ITransferManager { } - function _calculateCurrentLockUp(address _from) internal returns(uint) { + function _getInvestorCurrentLockUpAmount(address _from) internal returns(uint) { - uint totalCurrentLockUp = 0; + uint investorCurrentLockUpAmount = 0; - for (uint i = 0; i < investerToLockUps[_from].length; i++) { + for (uint i = 0; i < investorToLockUps[_from].length; i++) { - LockUp storage aLockUp = investerToLockUps[_from][i]; + LockUp storage aLockUp = investorToLockUps[_from][i]; + + // if already end of lockup + uint currentLockUpAmount = 0; + + // lockup not yet start + if (now <= aLockUp.startTime) { + currentLockUpAmount = aLockUp.lockUpAmount; + } - // only active lockup - if (now >= aLockUp.startTime && now <= aLockUp.startTime.add(aLockUp.lockUpPeriod)) { + // inside valid lockup time + if (now > aLockUp.startTime && now < aLockUp.startTime.add(aLockUp.lockUpPeriod)) { // calculate current lockup uint elepsedPeriods = now.sub(aLockUp.startTime).div(aLockUp.releaseFrequency); uint totalPeriods = aLockUp.lockUpPeriod.div(aLockUp.releaseFrequency); - uint amountPerPeriod = aLockUp.totalLockUp.div(totalPeriods); - uint currentLockUp = totalPeriods.sub(elepsedPeriods).mul(amountPerPeriod); - - // accumulate current lockup - totalCurrentLockUp.add(currentLockUp); + uint amountPerPeriod = aLockUp.lockUpAmount.div(totalPeriods); + uint currentLockUpAmount = totalPeriods.sub(elepsedPeriods).mul(amountPerPeriod); } + + investorCurrentLockUpAmount.add(currentLockUpAmount); } - // emit PrintOut(currentTotalLockUp); - return totalCurrentLockUp; + // emit PrintOut(currentlockUpAmount); + return investorCurrentLockUpAmount; } @@ -106,24 +113,29 @@ contract LockupVolumeTransferManager is ITransferManager { uint _startTime, uint _lockUpPeriod, uint _releaseFrequency, - uint _totalLockUp + uint _lockUpAmount ) public withPerm(ADMIN) { + + require(_lockUpPeriod != 0, "lock up period can not be 0"); + require(_releaseFrequency != 0, "release frequency can not be 0"); + require(_lockUpAmount != 0, "lockup amount can not be 0"); + if (_startTime == 0) { _startTime = now; } - LockUp memory newLockUp = LockUp(_startTime, _lockUpPeriod, _releaseFrequency, _totalLockUp); - investerToLockUps[_investor].push(newLockUp); + LockUp memory newLockUp = LockUp(_startTime, _lockUpPeriod, _releaseFrequency, _lockUpAmount); + investorToLockUps[_investor].push(newLockUp); emit AddLockUp( _investor, _startTime, _lockUpPeriod, _releaseFrequency, - _totalLockUp + _lockUpAmount ); } From 67900e4bb87f56a53cda3edb436fac30b3cb678e Mon Sep 17 00:00:00 2001 From: Tan Pham Date: Wed, 17 Oct 2018 22:33:56 +0700 Subject: [PATCH 3/4] modify naming and test for new name --- .../LockupVolumeTransferManager.sol | 268 +++++- test/w_lockup_volume_transfer_manager.js | 874 +++++++++--------- 2 files changed, 651 insertions(+), 491 deletions(-) diff --git a/contracts/modules/TransferManager/LockupVolumeTransferManager.sol b/contracts/modules/TransferManager/LockupVolumeTransferManager.sol index f61662695..8fec13ea1 100644 --- a/contracts/modules/TransferManager/LockupVolumeTransferManager.sol +++ b/contracts/modules/TransferManager/LockupVolumeTransferManager.sol @@ -3,7 +3,6 @@ pragma solidity ^0.4.24; import "./ITransferManager.sol"; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; - contract LockupVolumeTransferManager is ITransferManager { using SafeMath for uint256; @@ -16,17 +15,32 @@ contract LockupVolumeTransferManager is ITransferManager { uint releaseFrequency; uint lockUpAmount; } - - event PrintOut ( - uint _number - ); event AddLockUp ( address indexed investor, uint startTime, uint lockUpPeriod, uint releaseFrequency, - uint lockUpAmount + uint lockUpAmount, + uint indexed addedIndex + ); + + event ModifyLockUp( + address investor, + uint startTime, + uint lockUpPeriod, + uint releaseFrequency, + uint lockUpAmount, + uint indexed modifiedIndex + ); + + event RemoveLockUp( + address investor, + uint startTime, + uint lockUpPeriod, + uint releaseFrequency, + uint lockUpAmount, + uint indexed removedIndex ); mapping (address => LockUp[]) internal investorToLockUps; @@ -42,7 +56,6 @@ contract LockupVolumeTransferManager is ITransferManager { { } - /** @notice Used to verify the transfer transaction and prevent locked up tokens from being transferred * @param _from Address of the sender * @param _amount The amount of tokens to transfer @@ -50,64 +63,63 @@ contract LockupVolumeTransferManager is ITransferManager { */ function verifyTransfer(address _from, address /* _to*/, uint256 _amount, bytes /* _data */, bool _isTransfer) public returns(Result) { - if (!paused) { - + if (!paused && _from != address(0)) { uint currentLockUp = _getInvestorCurrentLockUpAmount(_from); uint currentBalance = ISecurityToken(securityToken).balanceOf(_from); uint currentAllowTransfer = currentBalance.sub(currentLockUp); - - if ( currentAllowTransfer >= _amount) { + if ( currentAllowTransfer >= _amount) { if(!_isTransfer){ return Result.VALID; - } - - require(msg.sender == securityToken, "Sender is not securityToken"); - + } + require(msg.sender == securityToken, "Sender is not securityToken"); return Result.VALID; - } - + } return Result.INVALID; - } - + } return Result.NA; } - - function _getInvestorCurrentLockUpAmount(address _from) internal returns(uint) { - + /** + * @notice get total lock for one specific investor + * @param _investor address of investor + */ + function _getInvestorCurrentLockUpAmount(address _investor) internal returns(uint) { uint investorCurrentLockUpAmount = 0; - for (uint i = 0; i < investorToLockUps[_from].length; i++) { - - LockUp storage aLockUp = investorToLockUps[_from][i]; - + for (uint i = 0; i < investorToLockUps[_investor].length; i++) { + LockUp storage aLockUp = investorToLockUps[_investor][i]; // if already end of lockup uint currentLockUpAmount = 0; // lockup not yet start - if (now <= aLockUp.startTime) { - currentLockUpAmount = aLockUp.lockUpAmount; + if (now <= aLockUp.startTime) { + currentLockUpAmount = aLockUp.lockUpAmount; } // inside valid lockup time if (now > aLockUp.startTime && now < aLockUp.startTime.add(aLockUp.lockUpPeriod)) { - - // calculate current lockup + // calculate current amount is locked from this lockup uint elepsedPeriods = now.sub(aLockUp.startTime).div(aLockUp.releaseFrequency); uint totalPeriods = aLockUp.lockUpPeriod.div(aLockUp.releaseFrequency); uint amountPerPeriod = aLockUp.lockUpAmount.div(totalPeriods); - uint currentLockUpAmount = totalPeriods.sub(elepsedPeriods).mul(amountPerPeriod); + currentLockUpAmount = totalPeriods.sub(elepsedPeriods).mul(amountPerPeriod); } - - investorCurrentLockUpAmount.add(currentLockUpAmount); - } - // emit PrintOut(currentlockUpAmount); + // calculate current total lock for specific investor + investorCurrentLockUpAmount = investorCurrentLockUpAmount.add(currentLockUpAmount); + } return investorCurrentLockUpAmount; } - + /** + * @notice Lets the admin create a volume restriction lockup for a given address. + * @param _investor Address of the user whose tokens should be locked up + * @param _startTime When this lockup starts (seconds) + * @param _lockUpPeriod Total period of lockup (seconds) + * @param _releaseFrequency How often to release a tranche of tokens (seconds) + * @param _lockUpAmount Total amount of locked up tokens + */ function addLockUp( address _investor, uint _startTime, @@ -117,26 +129,192 @@ contract LockupVolumeTransferManager is ITransferManager { ) public withPerm(ADMIN) - { - - require(_lockUpPeriod != 0, "lock up period can not be 0"); - require(_releaseFrequency != 0, "release frequency can not be 0"); - require(_lockUpAmount != 0, "lockup amount can not be 0"); + { + _checkLockUpParams(_lockUpPeriod, _releaseFrequency, _lockUpAmount); + uint256 startTime = _startTime; if (_startTime == 0) { - _startTime = now; + startTime = now; } - LockUp memory newLockUp = LockUp(_startTime, _lockUpPeriod, _releaseFrequency, _lockUpAmount); + LockUp memory newLockUp = LockUp(startTime, _lockUpPeriod, _releaseFrequency, _lockUpAmount); investorToLockUps[_investor].push(newLockUp); emit AddLockUp( _investor, - _startTime, + startTime, _lockUpPeriod, _releaseFrequency, - _lockUpAmount + _lockUpAmount, + investorToLockUps[_investor].length - 1 + ); + } + + /** + * @notice Lets the admin modify a volume restriction lockup for a given address. + * @param _investor Address of the user whose tokens should be locked up + * @param _lockUpIndex The index of the LockUp to edit for the given userAddress + * @param _startTime When this lockup starts (seconds) + * @param _lockUpPeriod Total period of lockup (seconds) + * @param _releaseFrequency How often to release a tranche of tokens (seconds) + * @param _lockUpAmount Total amount of locked up tokens + */ + function modifyLockUp( + address _investor, + uint _lockUpIndex, + uint _startTime, + uint _lockUpPeriod, + uint _releaseFrequency, + uint _lockUpAmount + ) public withPerm(ADMIN) { + require(_lockUpIndex < investorToLockUps[_investor].length, "Array out of bounds exception"); + + uint256 startTime = _startTime; + // if a startTime of 0 is passed in, then start now. + if (startTime == 0) { + startTime = now; + } + + _checkLockUpParams(_lockUpPeriod, _releaseFrequency, _lockUpAmount); + + // Get the lockup from the master list and edit it + investorToLockUps[_investor][_lockUpIndex] = LockUp(startTime, _lockUpPeriod, _releaseFrequency, _lockUpAmount); + + emit ModifyLockUp( + _investor, + startTime, + _lockUpPeriod, + _releaseFrequency, + _lockUpAmount, + _lockUpIndex + ); + } + + /** + * @notice Parameter checking function for creating or editing a lockup. This function will cause an exception if any of the parameters are bad. + * @param _lockupPeriod Total period of lockup (seconds) + * @param _releaseFrequency How often to release a tranche of tokens (seconds) + * @param _lockupAmount Total amount of locked up tokens + */ + function _checkLockUpParams(uint _lockupPeriod, uint _releaseFrequency, uint _lockupAmount) internal view { + + require(_lockupPeriod != 0, "_lockupPeriod cannot be zero"); + require(_releaseFrequency != 0, "_releaseFrequency cannot be zero"); + require(_lockupAmount != 0, "_lockupAmount cannot be zero"); + + // check that the total amount to be released isn't too granular + require( + _lockupAmount % ISecurityToken(securityToken).granularity() == 0, + "The total amount to be released is more granular than allowed by the token" + ); + + // check that _releaseFrequency evenly divides _lockupPeriod + require( + _lockupPeriod % _releaseFrequency == 0, + "_lockupPeriod must be evenly divisible by _releaseFrequency" + ); + + // check that totalPeriods evenly divides _lockupAmount + uint totalPeriods = _lockupPeriod.div(_releaseFrequency); + require( + _lockupAmount % totalPeriods == 0, + "The total amount being locked up must be evenly divisible by the number of total periods" + ); + + // make sure the amount to be released per period is not too granular for the token + uint amountPerPeriod = _lockupAmount.div(totalPeriods); + require( + amountPerPeriod % ISecurityToken(securityToken).granularity() == 0, + "The amount to be released per period is more granular than allowed by the token" + ); + } + + /** + * @notice Lets the admin create multiple volume restriction lockups for multiple given addresses. + * @param _investors Array of address of the user whose tokens should be locked up + * @param _lockUpPeriods Array of total periods of lockup (seconds) + * @param _releaseFrequencies Array of how often to release a tranche of tokens (seconds) + * @param _startTimes Array of When this lockup starts (seconds) + * @param _lockUpAmounts Array of total amount of locked up tokens + */ + function addLockUpMulti( + address[] _investors, + uint[] _startTimes, + uint[] _lockUpPeriods, + uint[] _releaseFrequencies, + uint[] _lockUpAmounts + ) external withPerm(ADMIN) { + require( + _investors.length == _lockUpPeriods.length && + _investors.length == _releaseFrequencies.length && + _investors.length == _startTimes.length && + _investors.length == _lockUpAmounts.length, + "Input array length mismatch" + ); + + for (uint i = 0; i < _investors.length; i++) { + addLockUp(_investors[i],_startTimes[i], _lockUpPeriods[i], _releaseFrequencies[i], _lockUpAmounts[i]); + } + } + + /** + * @notice Lets the admin remove a user's lock up + * @param _investor 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 _investor, uint _lockUpIndex) public withPerm(ADMIN) { + LockUp[] storage userLockUps = investorToLockUps[_investor]; + require(_lockUpIndex < userLockUps.length, "Array out of bounds exception"); + + LockUp memory toRemove = userLockUps[_lockUpIndex]; + + emit RemoveLockUp( + _investor, + toRemove.startTime, + toRemove.lockUpPeriod, + toRemove.releaseFrequency, + toRemove.lockUpAmount, + _lockUpIndex ); + + 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--; + } + + /** + * @notice Get a specific element in a user's investorToLockUps array given the user's address and the element index + * @param _investor 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 _investor, + uint _lockUpIndex) + public view returns ( + uint startTime, + uint lockUpPeriod, + uint releaseFrequency, + uint lockUpAmount) + { + require(_lockUpIndex < investorToLockUps[_investor].length, "Array out of bounds exception"); + LockUp storage userLockUp = investorToLockUps[_investor][_lockUpIndex]; + return ( + userLockUp.startTime, + userLockUp.lockUpPeriod, + userLockUp.releaseFrequency, + userLockUp.lockUpAmount + ); + } + + /** + * @notice Get the length of the lockups array for a specific user address + * @param _investor Address of the user whose tokens should be locked up + */ + function getLockUpsLength(address _investor) public view returns (uint) { + return investorToLockUps[_investor].length; } /** diff --git a/test/w_lockup_volume_transfer_manager.js b/test/w_lockup_volume_transfer_manager.js index 2faae4b4a..d15e95f01 100644 --- a/test/w_lockup_volume_transfer_manager.js +++ b/test/w_lockup_volume_transfer_manager.js @@ -239,580 +239,562 @@ contract('LockupVolumeTransferManager', accounts => { "LockupVolumeRestrictionTM", "LockupVolumeTransferManager module was not added" ); + I_LockupVolumeTransferManager = LockupVolumeTransferManager.at(tx.logs[2].args._module); + }); + it("Add a new token holder", async() => { - const tx_add = await I_LockupVolumeTransferManager.addLockUp(account_investor1, - 0, - 3600, - 3, - 10000, {from: token_owner}); - - console.log(tx_add.logs); + let tx = await I_GeneralTransferManager.modifyWhitelist( + account_investor3, + latestTime(), + latestTime(), + latestTime() + duration.days(10), + true, + { + from: account_issuer + }); - await new Promise(resolve => setTimeout(resolve, 4000)); + assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor3.toLowerCase(), "Failed in adding the investor in whitelist"); - const tx_verify = await I_LockupVolumeTransferManager.verifyTransfer(account_investor1, - account_investor2, - 200, - "", - true); - - // print still in lockup - console.log(tx_verify.logs[0].args._number.toNumber()); - }); - - // it("Add a new token holder", async() => { - - // let tx = await I_GeneralTransferManager.modifyWhitelist( - // account_investor3, - // latestTime(), - // latestTime(), - // latestTime() + duration.days(10), - // true, - // { - // from: account_issuer - // }); - - // assert.equal(tx.logs[0].args._investor.toLowerCase(), account_investor3.toLowerCase(), "Failed in adding the investor in whitelist"); - - // // Add the Investor in to the whitelist - // // Mint some tokens - // await I_SecurityToken.mint(account_investor3, web3.utils.toWei('10', 'ether'), { from: token_owner }); - - // assert.equal( - // (await I_SecurityToken.balanceOf(account_investor3)).toNumber(), - // web3.utils.toWei('10', 'ether') - // ); - // }); - - // it("Should pause the tranfers at transferManager level", async() => { - // let tx = await I_LockupVolumeTransferManager.pause({from: token_owner}); - // }); - - // it("Should still be able to transfer between existing token holders up to limit", async() => { - // // Add the Investor in to the whitelist - // // Mint some tokens - // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }); - - // assert.equal( - // (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), - // web3.utils.toWei('3', 'ether') - // ); - // }); - - // it("Should unpause the tranfers at transferManager level", async() => { - // await I_LockupVolumeTransferManager.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_LockupVolumeTransferManager.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_LockupVolumeTransferManager.addLockUp(account_investor2, 16, 0, 0, web3.utils.toWei('1', 'ether'), { from: token_owner }) - // ); - // }); - - // it("Should prevent the creation of a lockup with bad parameters where the lockUpPeriodSeconds is zero", async() => { - // // create a lockup - // // this will generate an exception because the lockUpPeriodSeconds is zero - // await catchRevert( - // I_LockupVolumeTransferManager.addLockUp(account_investor2, 0, 4, 0, web3.utils.toWei('1', 'ether'), { from: token_owner }) - // ); - // }); - - - // it("Should prevent the creation of a lockup with bad parameters where the total amount to be released is more granular than allowed by the token", async() => { - // // create a lockup - // // this will generate an exception because we're locking up 5e17 tokens but the granularity is 5e18 tokens - // await catchRevert( - // I_LockupVolumeTransferManager.addLockUp(account_investor2, 16, 4, 0, web3.utils.toWei('0.5', 'ether'), { from: token_owner }) - // ); - // }); - - // it("Should prevent the creation of a lockup with bad parameters where the lockUpPeriodSeconds is not evenly divisible by releaseFrequencySeconds", async() => { - - // // balance should be 9000000000000000000 here (9 eth) - // let balance = await I_SecurityToken.balanceOf(account_investor2) - - - // // create a lockup - // // over 17 seconds total, with 4 periods. - // // this will generate an exception because 17 is not evenly divisble by 4. - // await catchRevert( - // I_LockupVolumeTransferManager.addLockUp(account_investor2, 17, 4, 0, balance, { from: token_owner }) - // ); - // }); - - // it("Should prevent the creation of a lockup with bad parameters where the total amount being locked up isn't evenly divisible by the number of total periods", async() => { - - // // create a lockup for a balance of 1 eth - // // 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_LockupVolumeTransferManager.addLockUp(account_investor2, web3.utils.toWei('16', 'ether'), 4, 0, web3.utils.toWei('1', 'ether'), { from: token_owner }) - // ); - // }); - - // it("Should prevent the creation of a lockup with bad parameters where the amount to be released per period is too granular for the token", async() => { - - // // balance should be 9000000000000000000 here (9 eth) - // let balance = await I_SecurityToken.balanceOf(account_investor2) + // Add the Investor in to the whitelist + // Mint some tokens + await I_SecurityToken.mint(account_investor3, web3.utils.toWei('10', 'ether'), { from: token_owner }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor3)).toNumber(), + web3.utils.toWei('10', 'ether') + ); + }); + + it("Should pause the tranfers at transferManager level", async() => { + let tx = await I_LockupVolumeTransferManager.pause({from: token_owner}); + }); + + it("Should still be able to transfer between existing token holders up to limit", async() => { + // Add the Investor in to the whitelist + // Mint some tokens + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }); + + assert.equal( + (await I_SecurityToken.balanceOf(account_investor1)).toNumber(), + web3.utils.toWei('3', 'ether') + ); + }); + + it("Should unpause the tranfers at transferManager level", async() => { + await I_LockupVolumeTransferManager.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_LockupVolumeTransferManager.addLockUp(account_investor2, 16, 4, 2, 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_LockupVolumeTransferManager.addLockUp(account_investor2, 16, 4, 0, web3.utils.toWei('1', 'ether'), { from: token_owner }) + ); + }); + + it("Should prevent the creation of a lockup with bad parameters where the lockUpPeriodSeconds is zero", async() => { + // create a lockup + // this will generate an exception because the lockUpPeriodSeconds is zero + await catchRevert( + I_LockupVolumeTransferManager.addLockUp(account_investor2, 4, 0, 4, web3.utils.toWei('1', 'ether'), { from: token_owner }) + ); + }); + + it("Should prevent the creation of a lockup with bad parameters where the total amount to be released is more granular than allowed by the token", async() => { + // create a lockup + // this will generate an exception because we're locking up 5e17 tokens but the granularity is 5e18 tokens + await catchRevert( + I_LockupVolumeTransferManager.addLockUp(account_investor2, 0, 16, 4, web3.utils.toWei('0.5', 'ether'), { from: token_owner }) + ); + }); + + it("Should prevent the creation of a lockup with bad parameters where the lockUpPeriodSeconds is not evenly divisible by releaseFrequencySeconds", async() => { + + // balance should be 9000000000000000000 here (9 eth) + let balance = await I_SecurityToken.balanceOf(account_investor2) + + + // create a lockup + // over 17 seconds total, with 4 periods. + // this will generate an exception because 17 is not evenly divisble by 4. + await catchRevert( + I_LockupVolumeTransferManager.addLockUp(account_investor2, 0, 17, 4, balance, { from: token_owner }) + ); + }); + + it("Should prevent the creation of a lockup with bad parameters where the total amount being locked up isn't evenly divisible by the number of total periods", async() => { + + // create a lockup for a balance of 1 eth + // 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_LockupVolumeTransferManager.addLockUp(account_investor2, 0, web3.utils.toWei('16', 'ether'), 4, web3.utils.toWei('1', 'ether'), { from: token_owner }) + ); + }); + + it("Should prevent the creation of a lockup with bad parameters where the amount to be released per period is too granular for the token", async() => { + + // 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_LockupVolumeTransferManager.addLockUp(account_investor2, 16, 4, 0, balance, { from: token_owner }) - // ); + // 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_LockupVolumeTransferManager.addLockUp(account_investor2, 0, 16, 4, balance, { from: token_owner }) + ); - // }); + }); - // it("Should prevent the transfer of tokens in a lockup", async() => { + it("Should prevent the transfer of tokens in a lockup", async() => { - // let balance = await I_SecurityToken.balanceOf(account_investor2) + let balance = await I_SecurityToken.balanceOf(account_investor2) - // // create a lockup for their entire balance - // // over 12 seconds total, with 3 periods of 4 seconds each. - // await I_LockupVolumeTransferManager.addLockUp(account_investor2, 12, 4, 0, balance, { from: token_owner }); + // create a lockup for their entire balance + // over 12 seconds total, with 3 periods of 4 seconds each. + await I_LockupVolumeTransferManager.addLockUp(account_investor2, 0, 12, 4, balance, { from: token_owner }); - // // read only - check if transfer will pass. it should return INVALID - // let result = await I_LockupVolumeTransferManager.verifyTransfer.call(account_investor2, account_investor1, web3.utils.toWei('1', 'ether'), 0, false) - // // enum Result {INVALID, NA, VALID, FORCE_VALID} and we want VALID so it should be 2 - // assert.equal(result.toString(), '0') + // read only - check if transfer will pass. it should return INVALID + let result = await I_LockupVolumeTransferManager.verifyTransfer.call(account_investor2, account_investor1, web3.utils.toWei('1', 'ether'), 0, false) + // enum Result {INVALID, NA, VALID, FORCE_VALID} and we want VALID so it should be 2 + let tx = await I_LockupVolumeTransferManager.verifyTransfer(account_investor2, account_investor1, web3.utils.toWei('1', 'ether'), 0, true); + + assert.equal(result.toString(), '0') - // await catchRevert( - // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) - // ); - // }); + await catchRevert( + I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) + ); + }); - // it("Should allow the transfer of tokens in a lockup if a period has passed", async() => { + it("Should allow the transfer of tokens in a lockup if a period has passed", async() => { - // // wait 4 seconds - // await new Promise(resolve => setTimeout(resolve, 4000)); + // wait 4 seconds + await new Promise(resolve => setTimeout(resolve, 4000)); - // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3', 'ether'), { from: account_investor2 }); - // }); + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3', 'ether'), { from: account_investor2 }); + }); - // it("Should prevent the transfer of tokens if the amount is larger than the amount allowed by lockups", async() => { + it("Should prevent the transfer of tokens if the amount is larger than the amount allowed by lockups", async() => { - // await catchRevert( - // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('4', 'ether'), { from: account_investor2 }) - // ); - // }); + await catchRevert( + I_SecurityToken.transfer(account_investor1, web3.utils.toWei('4', 'ether'), { from: account_investor2 }) + ); + }); - // it("Should allow the transfer of more tokens in a lockup if another period has passed", async() => { + it("Should allow the transfer of more tokens in a lockup if another period has passed", async() => { - // // wait 4 more seconds - // await new Promise(resolve => setTimeout(resolve, 4000)); + // wait 4 more seconds + await new Promise(resolve => setTimeout(resolve, 4000)); - // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3', 'ether'), { from: account_investor2 }); - // }); + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('3', 'ether'), { from: account_investor2 }); + }); - // it("Should allow the transfer of all tokens in a lockup if the entire lockup has passed", async() => { + it("Should allow the transfer of all tokens in a lockup if the entire lockup has passed", async() => { - // let balance = await I_SecurityToken.balanceOf(account_investor2) + let balance = await I_SecurityToken.balanceOf(account_investor2) - // // wait 4 more seconds - // await new Promise(resolve => setTimeout(resolve, 4000)); + // wait 4 more seconds + await new Promise(resolve => setTimeout(resolve, 4000)); - // await I_SecurityToken.transfer(account_investor1, balance, { from: account_investor2 }); - // }); + await I_SecurityToken.transfer(account_investor1, balance, { from: account_investor2 }); + }); - // it("Should prevent the transfer of tokens in an edited lockup", async() => { + it("Should prevent the transfer of tokens in an edited lockup", async() => { - // // balance here should be 12000000000000000000 (12e18 or 12 eth) - // let balance = await I_SecurityToken.balanceOf(account_investor1) + // balance here should be 12000000000000000000 (12e18 or 12 eth) + let balance = await I_SecurityToken.balanceOf(account_investor1) - // // create a lockup for their entire balance - // // over 16 seconds total, with 4 periods of 4 seconds each. - // await I_LockupVolumeTransferManager.addLockUp(account_investor1, 16, 4, 0, balance, { from: token_owner }); + // create a lockup for their entire balance + // over 16 seconds total, with 4 periods of 4 seconds each. + await I_LockupVolumeTransferManager.addLockUp(account_investor1, 0, 16, 4,balance, { from: token_owner }); - // // let blockNumber = await web3.eth.getBlockNumber(); - // // console.log('blockNumber',blockNumber) - // let now = (await web3.eth.getBlock('latest')).timestamp + // let blockNumber = await web3.eth.getBlockNumber(); + // console.log('blockNumber',blockNumber) + let now = (await web3.eth.getBlock('latest')).timestamp - // await catchRevert( - // I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }) - // ); + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei('1', 'ether'), { from: account_investor1 }) + ); - // // check and get the lockup - // let lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor1); - // assert.equal(lockUpCount, 1) + // check and get the lockup + let lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor1); + assert.equal(lockUpCount, 1) - // let lockUp = await I_LockupVolumeTransferManager.getLockUp(account_investor1, 0); - // // console.log(lockUp); - // // elements in lockup array are uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime, uint totalAmount - // 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()); + let lockUp = await I_LockupVolumeTransferManager.getLockUp(account_investor1, 0); + // console.log(lockUp); + // elements in lockup array are uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime, uint totalAmount + assert.equal(lockUp[0].toNumber(), now); + assert.equal(lockUp[1].toString(), '16'); + assert.equal(lockUp[2].toString(), '4'); + assert.equal(lockUp[3].toString(), balance.toString()); - // // edit the lockup - // await I_LockupVolumeTransferManager.modifyLockUp(account_investor1, 0, 8, 4, 0, balance, { from: token_owner }); + // edit the lockup + await I_LockupVolumeTransferManager.modifyLockUp(account_investor1, 0, 0, 8, 4,balance, { from: token_owner }); - // // attempt a transfer - // await catchRevert( - // I_SecurityToken.transfer(account_investor2, web3.utils.toWei('6', 'ether'), { from: account_investor1 }) - // ); + // attempt a transfer + await catchRevert( + I_SecurityToken.transfer(account_investor2, web3.utils.toWei('6', 'ether'), { from: account_investor1 }) + ); - // // wait 4 seconds - // await new Promise(resolve => setTimeout(resolve, 4000)); + // wait 4 seconds + await new Promise(resolve => setTimeout(resolve, 4000)); - // // transfer should succeed - // await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('6', 'ether'), { from: account_investor1 }); + // transfer should succeed + await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('6', 'ether'), { from: account_investor1 }); - // }); + }); - // it("Should be possible to remove a lockup -- couldn't transfer because of lock up", async() => { + it("Should be possible to remove a lockup -- couldn't transfer because of lock up", async() => { - // let acct1Balance = await I_SecurityToken.balanceOf(account_investor1) + let acct1Balance = await I_SecurityToken.balanceOf(account_investor1) - // await catchRevert( - // I_SecurityToken.transfer(account_investor2, acct1Balance, { from: account_investor1 }) - // ); + await catchRevert( + I_SecurityToken.transfer(account_investor2, acct1Balance, { from: account_investor1 }) + ); - // // check and get the lockup - // let lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor1); - // assert.equal(lockUpCount, 1) + // check and get the lockup + let lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor1); + assert.equal(lockUpCount, 1) - // // remove the lockup - // await I_LockupVolumeTransferManager.removeLockUp(account_investor1, 0, { from: token_owner }); + // remove the lockup + await I_LockupVolumeTransferManager.removeLockUp(account_investor1, 0, { from: token_owner }); - // lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor1); - // assert.equal(lockUpCount, 0) + lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor1); + assert.equal(lockUpCount, 0) - // let acct2BalanceBefore = await I_SecurityToken.balanceOf(account_investor2) - // await I_SecurityToken.transfer(account_investor2, acct1Balance, { from: account_investor1 }); - // let acct2BalanceAfter = await I_SecurityToken.balanceOf(account_investor2) + let acct2BalanceBefore = await I_SecurityToken.balanceOf(account_investor2) + await I_SecurityToken.transfer(account_investor2, acct1Balance, { from: account_investor1 }); + let acct2BalanceAfter = await I_SecurityToken.balanceOf(account_investor2) - // assert.equal(acct2BalanceAfter.sub(acct2BalanceBefore).toString(), acct1Balance.toString()) - // }); + assert.equal(acct2BalanceAfter.sub(acct2BalanceBefore).toString(), acct1Balance.toString()) + }); - // it("Should be possible to create multiple lockups at once", async() => { + it("Should be possible to create multiple lockups at once", async() => { - // let balancesBefore = {} + let balancesBefore = {} - // // should be 12000000000000000000 - // balancesBefore[account_investor2] = await I_SecurityToken.balanceOf(account_investor2) + // should be 12000000000000000000 + balancesBefore[account_investor2] = await I_SecurityToken.balanceOf(account_investor2) - // // should be 10000000000000000000 - // balancesBefore[account_investor3] = await I_SecurityToken.balanceOf(account_investor3) + // should be 10000000000000000000 + balancesBefore[account_investor3] = await I_SecurityToken.balanceOf(account_investor3) - // let lockUpCountsBefore = {} + let lockUpCountsBefore = {} - // // get lockups for acct 2 - // lockUpCountsBefore[account_investor2] = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor2); - // assert.equal(lockUpCountsBefore[account_investor2], 1) // there's one old, expired lockup on acct already + // get lockups for acct 2 + lockUpCountsBefore[account_investor2] = await I_LockupVolumeTransferManager.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_LockupVolumeTransferManager.getLockUpsLength(account_investor3); - // assert.equal(lockUpCountsBefore[account_investor3], 0) + // get lockups for acct 3 + lockUpCountsBefore[account_investor3] = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor3); + assert.equal(lockUpCountsBefore[account_investor3], 0) - // // create lockups for their entire balances - // await I_LockupVolumeTransferManager.addLockUpMulti( - // [account_investor2, account_investor3], - // [24, 8], - // [4, 4], - // [0, 0], - // [balancesBefore[account_investor2], balancesBefore[account_investor3]], - // { from: token_owner } - // ); + // create lockups for their entire balances + await I_LockupVolumeTransferManager.addLockUpMulti( + [account_investor2, account_investor3], + [0, 0], + [24, 8], + [4, 4], + [balancesBefore[account_investor2], balancesBefore[account_investor3]], + { from: token_owner } + ); - // await catchRevert( - // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('2', 'ether'), { from: account_investor2 }) - // ); + await catchRevert( + I_SecurityToken.transfer(account_investor1, web3.utils.toWei('2', 'ether'), { from: account_investor2 }) + ); - // await catchRevert( - // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor3 }) - // ); + await catchRevert( + I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor3 }) + ); - // let balancesAfter = {} - // balancesAfter[account_investor2] = await I_SecurityToken.balanceOf(account_investor2) - // assert.equal(balancesBefore[account_investor2].toString(), balancesAfter[account_investor2].toString()) + let balancesAfter = {} + balancesAfter[account_investor2] = await I_SecurityToken.balanceOf(account_investor2) + assert.equal(balancesBefore[account_investor2].toString(), balancesAfter[account_investor2].toString()) - // balancesAfter[account_investor3] = await I_SecurityToken.balanceOf(account_investor3) - // assert.equal(balancesBefore[account_investor3].toString(), balancesAfter[account_investor3].toString()) + balancesAfter[account_investor3] = await I_SecurityToken.balanceOf(account_investor3) + assert.equal(balancesBefore[account_investor3].toString(), balancesAfter[account_investor3].toString()) - // let lockUpCountsAfter = {} + let lockUpCountsAfter = {} - // // get lockups for acct 2 - // lockUpCountsAfter[account_investor2] = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor2); - // assert.equal(lockUpCountsAfter[account_investor2], 2); + // get lockups for acct 2 + lockUpCountsAfter[account_investor2] = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor2); + assert.equal(lockUpCountsAfter[account_investor2], 2); - // // get lockups for acct 3 - // lockUpCountsAfter[account_investor3] = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor3); - // assert.equal(lockUpCountsAfter[account_investor3], 1); + // get lockups for acct 3 + lockUpCountsAfter[account_investor3] = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor3); + assert.equal(lockUpCountsAfter[account_investor3], 1); - // // wait 4 seconds - // await new Promise(resolve => setTimeout(resolve, 4000)); + // wait 4 seconds + await new Promise(resolve => setTimeout(resolve, 4000)); - // // try transfers again - // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('2', 'ether'), { from: account_investor2 }); - // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor3 }); + // try transfers again + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('2', 'ether'), { from: account_investor2 }); + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor3 }); - // balancesAfter[account_investor2] = await I_SecurityToken.balanceOf(account_investor2) - // assert.equal(balancesBefore[account_investor2].sub(web3.utils.toWei('2', 'ether')).toString(), balancesAfter[account_investor2].toString()) + balancesAfter[account_investor2] = await I_SecurityToken.balanceOf(account_investor2) + assert.equal(balancesBefore[account_investor2].sub(web3.utils.toWei('2', 'ether')).toString(), balancesAfter[account_investor2].toString()) - // balancesAfter[account_investor3] = await I_SecurityToken.balanceOf(account_investor3) - // assert.equal(balancesBefore[account_investor3].sub(web3.utils.toWei('5', 'ether')).toString(), balancesAfter[account_investor3].toString()) + balancesAfter[account_investor3] = await I_SecurityToken.balanceOf(account_investor3) + assert.equal(balancesBefore[account_investor3].sub(web3.utils.toWei('5', 'ether')).toString(), balancesAfter[account_investor3].toString()) - // }); + }); - // it("Should revert if the parameters are bad when creating multiple lockups", async() => { + 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_LockupVolumeTransferManager.addLockUpMulti( - // [account_investor2, account_investor3], - // [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 } - // ) - // ); - // }); + await catchRevert( + // pass in the wrong number of params. txn should revert + I_LockupVolumeTransferManager.addLockUpMulti( + [account_investor2, account_investor3], + [0, 0], + [16, 8], + [2], // this array should have 2 elements but it has 1, which should cause a revert + [web3.utils.toWei('1', 'ether'), web3.utils.toWei('1', 'ether')], + { from: token_owner } + ) + ); + }); - // it("Should be possible to create a lockup with a specific start time in the future", async() => { + 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_LockupVolumeTransferManager.getLockUpsLength(account_investor2); - // assert.equal(lockUpsLength, 2); - // await I_LockupVolumeTransferManager.removeLockUp(account_investor2, 0, { from: token_owner }); - // await I_LockupVolumeTransferManager.removeLockUp(account_investor2, 0, { from: token_owner }); - // lockUpsLength = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor2); - // assert.equal(lockUpsLength, 0); + // remove all lockups for account 2 + let lockUpsLength = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor2); + assert.equal(lockUpsLength, 2); + await I_LockupVolumeTransferManager.removeLockUp(account_investor2, 0, { from: token_owner }); + await I_LockupVolumeTransferManager.removeLockUp(account_investor2, 0, { from: token_owner }); + lockUpsLength = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor2); + assert.equal(lockUpsLength, 0); - // let now = (await web3.eth.getBlock('latest')).timestamp + let now = (await web3.eth.getBlock('latest')).timestamp - // // balance here should be 10000000000000000000 - // let balance = await I_SecurityToken.balanceOf(account_investor2) + // balance here should be 10000000000000000000 + let balance = await I_SecurityToken.balanceOf(account_investor2) - // await I_LockupVolumeTransferManager.addLockUp(account_investor2, 100, 10, now + 4, balance, { from: token_owner }); + await I_LockupVolumeTransferManager.addLockUp(account_investor2, now + 4, 100, 10, balance, { from: token_owner }); - // // try a transfer. it should fail because the lockup hasn't started yet. - // await catchRevert( - // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) - // ); - // now = (await web3.eth.getBlock('latest')).timestamp + // try a transfer. it should fail because the lockup hasn't started yet. + await catchRevert( + I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) + ); + now = (await web3.eth.getBlock('latest')).timestamp - // // wait 4 seconds for the lockup to begin - // await new Promise(resolve => setTimeout(resolve, 4000)); + // wait 4 seconds for the lockup to begin + await new Promise(resolve => setTimeout(resolve, 4000)); - // // try another transfer. it should also fail because the lockup has just begun - // await catchRevert( - // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) - // ); - // now = (await web3.eth.getBlock('latest')).timestamp + // try another transfer. it should also fail because the lockup has just begun + await catchRevert( + I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) + ); + now = (await web3.eth.getBlock('latest')).timestamp - // }); + }); - // it("Should be possible to edit a lockup with a specific start time in the future", async() => { + it("Should be possible to edit a lockup with a specific start time in the future", async() => { - // // edit the lockup - // let now = (await web3.eth.getBlock('latest')).timestamp + // edit the lockup + let now = (await web3.eth.getBlock('latest')).timestamp - // // should be 10000000000000000000 - // let balance = await I_SecurityToken.balanceOf(account_investor2) + // should be 10000000000000000000 + let balance = await I_SecurityToken.balanceOf(account_investor2) - // // check and get the lockup - // let lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor2); - // assert.equal(lockUpCount, 1) + // check and get the lockup + let lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor2); + assert.equal(lockUpCount, 1) - // let lockUp = await I_LockupVolumeTransferManager.getLockUp(account_investor2, 0); + let lockUp = await I_LockupVolumeTransferManager.getLockUp(account_investor2, 0); - // // 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()); + // elements in lockup array are uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime, uint totalAmount + assert.isAtMost(lockUp[0].toNumber(), now); + assert.equal(lockUp[1].toString(), '100'); + assert.equal(lockUp[2].toString(), '10'); + assert.equal(lockUp[3].toString(), balance.toString()); - // // edit the lockup - // await I_LockupVolumeTransferManager.modifyLockUp(account_investor2, 0, 8, 4, now + 4, balance, { from: token_owner }); + // edit the lockup + await I_LockupVolumeTransferManager.modifyLockUp(account_investor2, 0, now + 4, 8, 4, balance, { from: token_owner }); - // // check and get the lockup again - // lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor2); - // assert.equal(lockUpCount, 1) + // check and get the lockup again + lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor2); + assert.equal(lockUpCount, 1) - // lockUp = await I_LockupVolumeTransferManager.getLockUp(account_investor2, 0); + lockUp = await I_LockupVolumeTransferManager.getLockUp(account_investor2, 0); - // // 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()); + // elements in lockup array are uint lockUpPeriodSeconds, uint releaseFrequencySeconds, uint startTime, uint totalAmount + assert.isAtMost(lockUp[0].toNumber(), now + 4); + assert.equal(lockUp[1].toString(), '8'); + assert.equal(lockUp[2].toString(), '4'); + assert.equal(lockUp[3].toString(), balance.toString()); - // // try a transfer. it should fail because again, the lockup hasn't started yet. - // await catchRevert( - // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) - // ); + // try a transfer. it should fail because again, the lockup hasn't started yet. + await catchRevert( + I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) + ); - // // wait 4 seconds for the lockup to begin - // await new Promise(resolve => setTimeout(resolve, 4000)); + // wait 4 seconds for the lockup to begin + await new Promise(resolve => setTimeout(resolve, 4000)); - // // try another transfer. it should fail because the lockup has just begun - // await catchRevert( - // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) - // ); + // try another transfer. it should fail because the lockup has just begun + await catchRevert( + I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) + ); - // // wait 4 seconds for the lockup's first period to elapse - // await new Promise(resolve => setTimeout(resolve, 4000)); + // wait 4 seconds for the lockup's first period to elapse + await new Promise(resolve => setTimeout(resolve, 4000)); - // // try another transfer. it should pass - // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor2 }); + // try another transfer. it should pass + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor2 }); - // // try another transfer without waiting for another period to pass. it should fail - // await catchRevert( - // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor2 }) - // ); + // try another transfer without waiting for another period to pass. it should fail + await catchRevert( + I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor2 }) + ); - // // wait 4 seconds for the lockup's first period to elapse - // await new Promise(resolve => setTimeout(resolve, 4000)); + // wait 4 seconds for the lockup's first period to elapse + await new Promise(resolve => setTimeout(resolve, 4000)); - // let lockUpBeforeVerify = await I_LockupVolumeTransferManager.getLockUp(account_investor2, 0); - // // check if transfer will pass in read-only operation - // let result = await I_LockupVolumeTransferManager.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_LockupVolumeTransferManager.getLockUp(account_investor2, 0); + let lockUpBeforeVerify = await I_LockupVolumeTransferManager.getLockUp(account_investor2, 0); + // check if transfer will pass in read-only operation + let result = await I_LockupVolumeTransferManager.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_LockupVolumeTransferManager.getLockUp(account_investor2, 0); - // assert.equal(lockUpBeforeVerify[4].toString(), lockUpAfterVerify[4].toString()) + // assert.equal(lockUpBeforeVerify[4].toString(), lockUpAfterVerify[4].toString()) - // // try another transfer. it should pass - // await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor2 }); + // try another transfer. it should pass + await I_SecurityToken.transfer(account_investor1, web3.utils.toWei('5', 'ether'), { from: account_investor2 }); - // // wait 4 seconds for the lockup's first period to elapse. but, we are all out of periods. - // await new Promise(resolve => setTimeout(resolve, 4000)); + // wait 4 seconds for the lockup's first period to elapse. but, we are all out of periods. + await new Promise(resolve => setTimeout(resolve, 4000)); - // // try one final transfer. this should fail because the user has already withdrawn their entire balance - // await catchRevert( - // I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) - // ); - // }); + // try one final transfer. this should fail because the user has already withdrawn their entire balance + await catchRevert( + I_SecurityToken.transfer(account_investor1, web3.utils.toWei('1', 'ether'), { from: account_investor2 }) + ); + }); - // it("Should be possible to stack lockups", async() => { - // // should be 17000000000000000000 - // let balance = await I_SecurityToken.balanceOf(account_investor1) + 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_LockupVolumeTransferManager.getLockUpsLength(account_investor1); - // assert.equal(lockUpCount.toString(), 0) + // check and make sure that acct1 has no lockups so far + let lockUpCount = await I_LockupVolumeTransferManager.getLockUpsLength(account_investor1); + assert.equal(lockUpCount.toString(), 0) - // await I_LockupVolumeTransferManager.addLockUp(account_investor1, 12, 4, 0, web3.utils.toWei('6', 'ether'), { from: token_owner }); + await I_LockupVolumeTransferManager.addLockUp(account_investor1, 0, 12, 4, 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 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 new Promise(resolve => setTimeout(resolve, 4000)); + // 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 new Promise(resolve => setTimeout(resolve, 4000)); - // // should succeed - // await I_SecurityToken.transfer(account_investor2, web3.utils.toWei('2', 'ether'), { from: account_investor1 }); + // 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 }); + // 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_LockupVolumeTransferManager.addLockUp(account_investor1, 16, 4, 0, web3.utils.toWei('8', 'ether'), { from: token_owner }); + // let's add another lockup to stack them + await I_LockupVolumeTransferManager.addLockUp(account_investor1, 0, 16, 4, 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 }) - // ); + // 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 new Promise(resolve => setTimeout(resolve, 4000)); + // wait 4 seconds for the 1st lockup's second period to elapse, and the 2nd lockup's first period to elapse + await new Promise(resolve => setTimeout(resolve, 4000)); - // // 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 }); + // 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 }) - // ); + // 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 new Promise(resolve => setTimeout(resolve, 4000)); + // wait 4 seconds for the 1st lockup's final period to elapse, and the 2nd lockup's second period to elapse + await new Promise(resolve => setTimeout(resolve, 4000)); - // // 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 }); + // 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 new Promise(resolve => setTimeout(resolve, 8000)); + // wait 8 seconds for 2nd lockup's third and fourth periods to elapse + await new Promise(resolve => setTimeout(resolve, 8000)); - // // 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 }); + // 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 }); + // 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 }); + // 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')) - // }); + 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_LockupVolumeTransferManager.getInitFunction.call(); - // assert.equal(web3.utils.hexToNumber(sig), 0); - // }); + it("Should get configuration function signature", async() => { + let sig = await I_LockupVolumeTransferManager.getInitFunction.call(); + assert.equal(web3.utils.hexToNumber(sig), 0); + }); - // it("Should get the permission", async() => { - // let perm = await I_LockupVolumeTransferManager.getPermissions.call(); - // assert.equal(perm.length, 1); - // // console.log(web3.utils.toAscii(perm[0]).replace(/\u0000/g, '')) - // assert.equal(web3.utils.toAscii(perm[0]).replace(/\u0000/g, ''), "ADMIN") - // }); + it("Should get the permission", async() => { + let perm = await I_LockupVolumeTransferManager.getPermissions.call(); + assert.equal(perm.length, 1); + // console.log(web3.utils.toAscii(perm[0]).replace(/\u0000/g, '')) + assert.equal(web3.utils.toAscii(perm[0]).replace(/\u0000/g, ''), "ADMIN") + }); }); - // describe("VolumeRestriction Transfer Manager Factory test cases", async() => { - - // it("Should get the exact details of the factory", async() => { - // assert.equal(await I_LockupVolumeTransferManagerFactory.setupCost.call(),0); - // assert.equal((await I_LockupVolumeTransferManagerFactory.getTypes.call())[0],2); - // assert.equal(web3.utils.toAscii(await I_LockupVolumeTransferManagerFactory.getName.call()) - // .replace(/\u0000/g, ''), - // "LockupVolumeRestrictionTM", - // "Wrong Module added"); - // assert.equal(await I_LockupVolumeTransferManagerFactory.getDescription.call(), - // "Manage transfers using lock ups over time", - // "Wrong Module added"); - // assert.equal(await I_LockupVolumeTransferManagerFactory.getTitle.call(), - // "Lockup Volume Restriction Transfer Manager", - // "Wrong Module added"); - // assert.equal(await I_LockupVolumeTransferManagerFactory.getInstructions.call(), - // "Allows an issuer to set lockup periods for user addresses, with funds distributed over time. Init function takes no parameters.", - // "Wrong Module added"); - - // }); - - // it("Should get the tags of the factory", async() => { - // let tags = await I_LockupVolumeTransferManagerFactory.getTags.call(); - // assert.equal(web3.utils.toAscii(tags[0]).replace(/\u0000/g, ''), "Volume"); - // }); - // }); + describe("VolumeRestriction Transfer Manager Factory test cases", async() => { + + it("Should get the exact details of the factory", async() => { + assert.equal(await I_LockupVolumeTransferManagerFactory.setupCost.call(),0); + assert.equal((await I_LockupVolumeTransferManagerFactory.getTypes.call())[0],2); + assert.equal(web3.utils.toAscii(await I_LockupVolumeTransferManagerFactory.getName.call()) + .replace(/\u0000/g, ''), + "LockupVolumeRestrictionTM", + "Wrong Module added"); + assert.equal(await I_LockupVolumeTransferManagerFactory.getDescription.call(), + "Manage transfers using lock ups over time", + "Wrong Module added"); + assert.equal(await I_LockupVolumeTransferManagerFactory.getTitle.call(), + "Lockup Volume Restriction Transfer Manager", + "Wrong Module added"); + assert.equal(await I_LockupVolumeTransferManagerFactory.getInstructions.call(), + "Allows an issuer to set lockup periods for user addresses, with funds distributed over time. Init function takes no parameters.", + "Wrong Module added"); + + }); + + it("Should get the tags of the factory", async() => { + let tags = await I_LockupVolumeTransferManagerFactory.getTags.call(); + assert.equal(web3.utils.toAscii(tags[0]).replace(/\u0000/g, ''), "Volume"); + }); + }); }); From e9affe161c116f7ae4bcf49277e44b93146a6b8b Mon Sep 17 00:00:00 2001 From: Tan Pham Date: Wed, 17 Oct 2018 23:32:16 +0700 Subject: [PATCH 4/4] rename test file --- ...nager.1.js => w_lockup_volume_restriction_transfer_manager.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{w_lockup_volume_restriction_transfer_manager.1.js => w_lockup_volume_restriction_transfer_manager.js} (100%) diff --git a/test/w_lockup_volume_restriction_transfer_manager.1.js b/test/w_lockup_volume_restriction_transfer_manager.js similarity index 100% rename from test/w_lockup_volume_restriction_transfer_manager.1.js rename to test/w_lockup_volume_restriction_transfer_manager.js