diff --git a/contracts/modules/STO/Dummy/DummySTO.sol b/contracts/mocks/Dummy/DummySTO.sol similarity index 93% rename from contracts/modules/STO/Dummy/DummySTO.sol rename to contracts/mocks/Dummy/DummySTO.sol index 5a8bb8fe1..59a3c2904 100644 --- a/contracts/modules/STO/Dummy/DummySTO.sol +++ b/contracts/mocks/Dummy/DummySTO.sol @@ -1,7 +1,7 @@ pragma solidity ^0.5.0; -import "../STO.sol"; -import "../../../interfaces/ISecurityToken.sol"; +import "../../modules/STO/STO.sol"; +import "../../interfaces/ISecurityToken.sol"; import "./DummySTOStorage.sol"; /** @@ -81,4 +81,8 @@ contract DummySTO is DummySTOStorage, STO { return allPermissions; } + function () external payable { + //Payable fallback function to allow us to test leaking ETH + } + } diff --git a/contracts/modules/STO/Dummy/DummySTOFactory.sol b/contracts/mocks/Dummy/DummySTOFactory.sol similarity index 93% rename from contracts/modules/STO/Dummy/DummySTOFactory.sol rename to contracts/mocks/Dummy/DummySTOFactory.sol index 1a2a99dab..f4b9c9a2d 100644 --- a/contracts/modules/STO/Dummy/DummySTOFactory.sol +++ b/contracts/mocks/Dummy/DummySTOFactory.sol @@ -1,9 +1,9 @@ pragma solidity ^0.5.0; -import "../../UpgradableModuleFactory.sol"; -import "../../../libraries/Util.sol"; +import "../../modules/UpgradableModuleFactory.sol"; +import "../../libraries/Util.sol"; import "./DummySTOProxy.sol"; -import "../../../interfaces/IBoot.sol"; +import "../../interfaces/IBoot.sol"; /** * @title Factory for deploying DummySTO module diff --git a/contracts/modules/STO/Dummy/DummySTOProxy.sol b/contracts/mocks/Dummy/DummySTOProxy.sol similarity index 82% rename from contracts/modules/STO/Dummy/DummySTOProxy.sol rename to contracts/mocks/Dummy/DummySTOProxy.sol index 9ba63f6f2..1f7d44620 100644 --- a/contracts/modules/STO/Dummy/DummySTOProxy.sol +++ b/contracts/mocks/Dummy/DummySTOProxy.sol @@ -1,10 +1,10 @@ pragma solidity ^0.5.0; -import "../../../proxy/OwnedUpgradeabilityProxy.sol"; -import "../../../Pausable.sol"; +import "../../proxy/OwnedUpgradeabilityProxy.sol"; +import "../../Pausable.sol"; import "openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol"; -import "../../../storage/modules/STO/STOStorage.sol"; -import "../../../storage/modules/ModuleStorage.sol"; +import "../../storage/modules/STO/STOStorage.sol"; +import "../../storage/modules/ModuleStorage.sol"; import "./DummySTOStorage.sol"; /** diff --git a/contracts/modules/STO/Dummy/DummySTOStorage.sol b/contracts/mocks/Dummy/DummySTOStorage.sol similarity index 100% rename from contracts/modules/STO/Dummy/DummySTOStorage.sol rename to contracts/mocks/Dummy/DummySTOStorage.sol diff --git a/contracts/mocks/MockFactory.sol b/contracts/mocks/MockFactory.sol index b634c2525..d86983cb5 100644 --- a/contracts/mocks/MockFactory.sol +++ b/contracts/mocks/MockFactory.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.0; -import "../modules/STO/Dummy/DummySTOFactory.sol"; +import "./Dummy/DummySTOFactory.sol"; /** * @title Mock Contract Not fit for production environment diff --git a/contracts/mocks/TestSTOFactory.sol b/contracts/mocks/TestSTOFactory.sol index cca07fbc0..ab3030c86 100644 --- a/contracts/mocks/TestSTOFactory.sol +++ b/contracts/mocks/TestSTOFactory.sol @@ -1,6 +1,6 @@ pragma solidity ^0.5.0; -import "../modules/STO/Dummy/DummySTOFactory.sol"; +import "./Dummy/DummySTOFactory.sol"; contract TestSTOFactory is DummySTOFactory { /** diff --git a/contracts/modules/Checkpoint/DividendCheckpoint.sol b/contracts/modules/Checkpoint/DividendCheckpoint.sol index 2e2770e14..591aed22a 100644 --- a/contracts/modules/Checkpoint/DividendCheckpoint.sol +++ b/contracts/modules/Checkpoint/DividendCheckpoint.sol @@ -24,6 +24,7 @@ contract DividendCheckpoint is DividendCheckpointStorage, ICheckpoint, Module { event SetWithholding(address[] _investors, uint256[] _withholding); event SetWithholdingFixed(address[] _investors, uint256 _withholding); event SetWallet(address indexed _oldWallet, address indexed _newWallet); + event UpdateDividendDates(uint256 indexed _dividendIndex, uint256 _maturity, uint256 _expiry); function _validDividendIndex(uint256 _dividendIndex) internal view { require(_dividendIndex < dividends.length, "Invalid dividend"); @@ -195,7 +196,7 @@ contract DividendCheckpoint is DividendCheckpointStorage, ICheckpoint, Module { * @notice Investors can pull their own dividends * @param _dividendIndex Dividend to pull */ - function pullDividendPayment(uint256 _dividendIndex) public { + function pullDividendPayment(uint256 _dividendIndex) public whenNotPaused { _validDividendIndex(_dividendIndex); Dividend storage dividend = dividends[_dividendIndex]; require(!dividend.claimed[msg.sender], "Dividend already claimed"); @@ -265,6 +266,26 @@ contract DividendCheckpoint is DividendCheckpointStorage, ICheckpoint, Module { */ function withdrawWithholding(uint256 _dividendIndex) external; + /** + * @notice Allows issuer to change maturity / expiry dates for dividends + * @dev NB - setting the maturity of a currently matured dividend to a future date + * @dev will effectively refreeze claims on that dividend until the new maturity date passes + * @ dev NB - setting the expiry date to a past date will mean no more payments can be pulled + * @dev or pushed out of a dividend + * @param _dividendIndex Dividend to withdraw from + * @param _maturity updated maturity date + * @param _expiry updated expiry date + */ + function updateDividendDates(uint256 _dividendIndex, uint256 _maturity, uint256 _expiry) external withPerm(ADMIN) { + require(_dividendIndex < dividends.length, "Invalid dividend"); + require(_expiry > _maturity, "Expiry before maturity"); + Dividend storage dividend = dividends[_dividendIndex]; + require(dividend.expiry > now, "Dividend already expired"); + dividend.expiry = _expiry; + dividend.maturity = _maturity; + emit UpdateDividendDates(_dividendIndex, _maturity, _expiry); + } + /** * @notice Get static dividend data * @return uint256[] timestamp of dividends creation diff --git a/contracts/modules/Checkpoint/ERC20/ERC20DividendCheckpoint.sol b/contracts/modules/Checkpoint/ERC20/ERC20DividendCheckpoint.sol index 93c952a01..b4120122d 100644 --- a/contracts/modules/Checkpoint/ERC20/ERC20DividendCheckpoint.sol +++ b/contracts/modules/Checkpoint/ERC20/ERC20DividendCheckpoint.sol @@ -238,12 +238,12 @@ contract ERC20DividendCheckpoint is ERC20DividendCheckpointStorage, DividendChec uint256 claimAfterWithheld = claim.sub(withheld); if (claimAfterWithheld > 0) { require(IERC20(dividendTokens[_dividendIndex]).transfer(_payee, claimAfterWithheld), "transfer failed"); - if (withheld > 0) { - _dividend.totalWithheld = _dividend.totalWithheld.add(withheld); - _dividend.withheld[_payee] = withheld; - } - emit ERC20DividendClaimed(_payee, _dividendIndex, dividendTokens[_dividendIndex], claim, withheld); } + if (withheld > 0) { + _dividend.totalWithheld = _dividend.totalWithheld.add(withheld); + _dividend.withheld[_payee] = withheld; + } + emit ERC20DividendClaimed(_payee, _dividendIndex, dividendTokens[_dividendIndex], claim, withheld); } /** diff --git a/contracts/modules/Checkpoint/Ether/EtherDividendCheckpoint.sol b/contracts/modules/Checkpoint/Ether/EtherDividendCheckpoint.sol index d550c6056..6478f3e55 100644 --- a/contracts/modules/Checkpoint/Ether/EtherDividendCheckpoint.sol +++ b/contracts/modules/Checkpoint/Ether/EtherDividendCheckpoint.sol @@ -170,19 +170,17 @@ contract EtherDividendCheckpoint is DividendCheckpoint { (uint256 claim, uint256 withheld) = calculateDividend(_dividendIndex, _payee); _dividend.claimed[_payee] = true; uint256 claimAfterWithheld = claim.sub(withheld); - if (claimAfterWithheld > 0) { - /*solium-disable-next-line security/no-send*/ - if (_payee.send(claimAfterWithheld)) { - _dividend.claimedAmount = _dividend.claimedAmount.add(claim); - if (withheld > 0) { - _dividend.totalWithheld = _dividend.totalWithheld.add(withheld); - _dividend.withheld[_payee] = withheld; - } - emit EtherDividendClaimed(_payee, _dividendIndex, claim, withheld); - } else { - _dividend.claimed[_payee] = false; - emit EtherDividendClaimFailed(_payee, _dividendIndex, claim, withheld); + /*solium-disable-next-line security/no-send*/ + if (_payee.send(claimAfterWithheld)) { + _dividend.claimedAmount = _dividend.claimedAmount.add(claim); + if (withheld > 0) { + _dividend.totalWithheld = _dividend.totalWithheld.add(withheld); + _dividend.withheld[_payee] = withheld; } + emit EtherDividendClaimed(_payee, _dividendIndex, claim, withheld); + } else { + _dividend.claimed[_payee] = false; + emit EtherDividendClaimFailed(_payee, _dividendIndex, claim, withheld); } } diff --git a/contracts/modules/Experimental/Wallet/VestingEscrowWallet.sol b/contracts/modules/Experimental/Wallet/VestingEscrowWallet.sol index 6742d8659..dfce2aad5 100644 --- a/contracts/modules/Experimental/Wallet/VestingEscrowWallet.sol +++ b/contracts/modules/Experimental/Wallet/VestingEscrowWallet.sol @@ -136,7 +136,7 @@ contract VestingEscrowWallet is VestingEscrowWalletStorage, Wallet { /** * @notice Used to withdraw available tokens by beneficiary */ - function pullAvailableTokens() external { + function pullAvailableTokens() external whenNotPaused { _sendTokens(msg.sender); } diff --git a/contracts/modules/Experimental/Wallet/Wallet.sol b/contracts/modules/Experimental/Wallet/Wallet.sol index 95bdf9367..41177ce81 100644 --- a/contracts/modules/Experimental/Wallet/Wallet.sol +++ b/contracts/modules/Experimental/Wallet/Wallet.sol @@ -1,21 +1,11 @@ pragma solidity ^0.5.0; -import "../../../Pausable.sol"; import "../../Module.sol"; /** * @title Interface to be implemented by all Wallet modules * @dev abstract contract */ -contract Wallet is Module, Pausable { +contract Wallet is Module { - function unpause() public { - _onlySecurityTokenOwner(); - super._unpause(); - } - - function pause() public { - _onlySecurityTokenOwner(); - super._pause(); - } } diff --git a/contracts/modules/Module.sol b/contracts/modules/Module.sol index 9cef037a0..21a33a7f6 100644 --- a/contracts/modules/Module.sol +++ b/contracts/modules/Module.sol @@ -1,6 +1,7 @@ pragma solidity ^0.5.0; import "../interfaces/IModule.sol"; +import "../Pausable.sol"; import "../interfaces/IModuleFactory.sol"; import "../interfaces/IDataStore.sol"; import "../interfaces/ISecurityToken.sol"; @@ -13,7 +14,7 @@ import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; * @title Interface that any module contract should implement * @notice Contract is abstract */ -contract Module is IModule, ModuleStorage { +contract Module is IModule, ModuleStorage, Pausable { /** * @notice Constructor * @param _securityToken Address of the security token @@ -54,6 +55,22 @@ contract Module is IModule, ModuleStorage { _; } + /** + * @notice Pause (overridden function) + */ + function pause() public { + _onlySecurityTokenOwner(); + super._pause(); + } + + /** + * @notice Unpause (overridden function) + */ + function unpause() public { + _onlySecurityTokenOwner(); + super._unpause(); + } + /** * @notice used to withdraw the fee by the factory owner */ @@ -68,4 +85,26 @@ contract Module is IModule, ModuleStorage { function getDataStore() public view returns(IDataStore) { return IDataStore(ISecurityToken(securityToken).dataStore()); } + + /** + * @notice Reclaims ERC20Basic compatible tokens + * @dev We duplicate here due to the overriden owner & onlyOwner + * @param _tokenContract The address of the token contract + */ + function reclaimERC20(address _tokenContract) external { + _onlySecurityTokenOwner(); + require(_tokenContract != address(0), "Invalid address"); + IERC20 token = IERC20(_tokenContract); + uint256 balance = token.balanceOf(address(this)); + require(token.transfer(msg.sender, balance), "Transfer failed"); + } + + /** + * @notice Reclaims ETH + * @dev We duplicate here due to the overriden owner & onlyOwner + */ + function reclaimETH() external { + _onlySecurityTokenOwner(); + msg.sender.transfer(address(this).balance); + } } diff --git a/contracts/modules/STO/Capped/CappedSTO.sol b/contracts/modules/STO/Capped/CappedSTO.sol index dbb40a76f..adc89d5a9 100644 --- a/contracts/modules/STO/Capped/CappedSTO.sol +++ b/contracts/modules/STO/Capped/CappedSTO.sol @@ -91,12 +91,11 @@ contract CappedSTO is CappedSTOStorage, STO, ReentrancyGuard { * @notice Low level token purchase ***DO NOT OVERRIDE*** * @param _beneficiary Address performing the token purchase */ - function buyTokens(address _beneficiary) public payable nonReentrant { + function buyTokens(address _beneficiary) public payable whenNotPaused nonReentrant { if (!allowBeneficialInvestments) { require(_beneficiary == msg.sender, "Beneficiary address does not match msg.sender"); } - require(!paused, "Should not be paused"); require(fundRaiseTypes[uint8(FundRaiseType.ETH)], "Mode of investment is not ETH"); uint256 weiAmount = msg.value; @@ -110,8 +109,7 @@ contract CappedSTO is CappedSTOStorage, STO, ReentrancyGuard { * @notice low level token purchase * @param _investedPOLY Amount of POLY invested */ - function buyTokensWithPoly(uint256 _investedPOLY) public nonReentrant { - require(!paused, "Should not be paused"); + function buyTokensWithPoly(uint256 _investedPOLY) public whenNotPaused nonReentrant { require(fundRaiseTypes[uint8(FundRaiseType.POLY)], "Mode of investment is not POLY"); uint256 refund = _processTx(msg.sender, _investedPOLY); _forwardPoly(msg.sender, wallet, _investedPOLY.sub(refund)); diff --git a/contracts/modules/STO/STO.sol b/contracts/modules/STO/STO.sol index b3f37680f..d80c9d139 100644 --- a/contracts/modules/STO/STO.sol +++ b/contracts/modules/STO/STO.sol @@ -1,6 +1,5 @@ pragma solidity ^0.5.0; -import "../../Pausable.sol"; import "../Module.sol"; import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; import "../../storage/modules/STO/STOStorage.sol"; @@ -10,7 +9,7 @@ import "../../interfaces/ISTO.sol"; /** * @title Base abstract contract to be extended by all STO modules */ -contract STO is ISTO, STOStorage, Module, Pausable { +contract STO is ISTO, STOStorage, Module { using SafeMath for uint256; enum FundRaiseType {ETH, POLY, SC} @@ -18,19 +17,6 @@ contract STO is ISTO, STOStorage, Module, Pausable { // Event event SetFundRaiseTypes(FundRaiseType[] _fundRaiseTypes); - /** - * @notice Reclaims ERC20Basic compatible tokens - * @dev We duplicate here due to the overriden owner & onlyOwner - * @param _tokenContract The address of the token contract - */ - function reclaimERC20(address _tokenContract) external { - _onlySecurityTokenOwner(); - require(_tokenContract != address(0), "Invalid address"); - IERC20 token = IERC20(_tokenContract); - uint256 balance = token.balanceOf(address(this)); - require(token.transfer(msg.sender, balance), "Transfer failed"); - } - /** * @notice Returns funds raised by the STO */ @@ -45,20 +31,12 @@ contract STO is ISTO, STOStorage, Module, Pausable { /** * @notice Pause (overridden function) + * @dev Only securityToken owner restriction applied on the super function */ function pause() public { - _onlySecurityTokenOwner(); /*solium-disable-next-line security/no-block-members*/ require(now < endTime, "STO has been finalized"); - super._pause(); - } - - /** - * @notice Unpause (overridden function) - */ - function unpause() public { - _onlySecurityTokenOwner(); - super._unpause(); + super.pause(); } function _setFundRaiseType(FundRaiseType[] memory _fundRaiseTypes) internal { diff --git a/contracts/modules/STO/USDTiered/USDTieredSTO.sol b/contracts/modules/STO/USDTiered/USDTieredSTO.sol index e2f49f6ae..6a724270d 100644 --- a/contracts/modules/STO/USDTiered/USDTieredSTO.sol +++ b/contracts/modules/STO/USDTiered/USDTieredSTO.sol @@ -260,7 +260,7 @@ contract USDTieredSTO is USDTieredSTOStorage, STO { */ function finalize() external { _onlySecurityTokenOwner(); - require(!isFinalized, "STO already finalized"); + require(!isFinalized, "STO is finalized"); isFinalized = true; uint256 tempReturned; uint256 tempSold; @@ -322,7 +322,7 @@ contract USDTieredSTO is USDTieredSTOStorage, STO { */ function changeAllowBeneficialInvestments(bool _allowBeneficialInvestments) external { _onlySecurityTokenOwner(); - require(_allowBeneficialInvestments != allowBeneficialInvestments, "Value unchanged"); + require(_allowBeneficialInvestments != allowBeneficialInvestments); allowBeneficialInvestments = _allowBeneficialInvestments; emit SetAllowBeneficialInvestments(allowBeneficialInvestments); } @@ -409,7 +409,7 @@ contract USDTieredSTO is USDTieredSTOStorage, STO { initialMinted = getTokensMinted(); rate = getRate(_fundRaiseType); (spentUSD, spentValue) = _buyTokens(_beneficiary, _amount, rate, _fundRaiseType); - require(getTokensMinted().sub(initialMinted) >= _minTokens, "Insufficient tokens minted"); + require(getTokensMinted().sub(initialMinted) >= _minTokens, "Insufficient minted"); } diff --git a/contracts/modules/TransferManager/TransferManager.sol b/contracts/modules/TransferManager/TransferManager.sol index 69ccf603f..77f3c2fa5 100644 --- a/contracts/modules/TransferManager/TransferManager.sol +++ b/contracts/modules/TransferManager/TransferManager.sol @@ -1,13 +1,12 @@ pragma solidity ^0.5.0; -import "../../Pausable.sol"; import "../Module.sol"; import "../../interfaces/ITransferManager.sol"; /** * @title Base abstract contract to be implemented by all Transfer Manager modules */ -contract TransferManager is ITransferManager, Module, Pausable { +contract TransferManager is ITransferManager, Module { bytes32 public constant LOCKED = "LOCKED"; bytes32 public constant UNLOCKED = "UNLOCKED"; @@ -17,13 +16,4 @@ contract TransferManager is ITransferManager, Module, Pausable { _; } - function unpause() public { - _onlySecurityTokenOwner(); - super._unpause(); - } - - function pause() public { - _onlySecurityTokenOwner(); - super._pause(); - } } diff --git a/contracts/storage/modules/ModuleStorage.sol b/contracts/storage/modules/ModuleStorage.sol index 5c7fc41c0..6acedde99 100644 --- a/contracts/storage/modules/ModuleStorage.sol +++ b/contracts/storage/modules/ModuleStorage.sol @@ -1,7 +1,6 @@ pragma solidity ^0.5.0; import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; - /** * @title Storage for Module contract * @notice Contract is abstract diff --git a/contracts/tokens/SecurityToken.sol b/contracts/tokens/SecurityToken.sol index 1600237d1..6517ed69e 100644 --- a/contracts/tokens/SecurityToken.sol +++ b/contracts/tokens/SecurityToken.sol @@ -544,7 +544,7 @@ contract SecurityToken is ERC20, ERC20Detailed, Ownable, ReentrancyGuard, Securi */ function redeem(uint256 _value, bytes calldata _data) external onlyModule(BURN_KEY) { // Add a function to validate the `_data` parameter - require(_checkAndBurn(msg.sender, _value, _data), "Invalid redeem"); + _validateRedeem(_checkAndBurn(msg.sender, _value, _data)); } /** @@ -574,11 +574,15 @@ contract SecurityToken is ERC20, ERC20Detailed, Ownable, ReentrancyGuard, Securi */ function redeemFrom(address _tokenHolder, uint256 _value, bytes calldata _data) external onlyModule(BURN_KEY) { // Add a function to validate the `_data` parameter - require(_updateTransfer(_tokenHolder, address(0), _value, _data), "Invalid redeem"); + _validateRedeem(_updateTransfer(_tokenHolder, address(0), _value, _data)); _burnFrom(_tokenHolder, _value); emit Redeemed(msg.sender, _tokenHolder, _value, _data); } + function _validateRedeem(bool _isRedeem) internal pure { + require(_isRedeem, "Invalid redeem"); + } + /** * @notice Creates a checkpoint that can be used to query historical balances / totalSuppy * @return uint256 diff --git a/scripts/compareStorageLayout.js b/scripts/compareStorageLayout.js index 382efdedf..9faf22692 100644 --- a/scripts/compareStorageLayout.js +++ b/scripts/compareStorageLayout.js @@ -6,7 +6,7 @@ const path = require("path"); const util = require("util"); const exec = util.promisify(require("child_process").exec); -console.log(`Mandatory: Solc cli tool should be installed globally`); +console.log(`Mandatory: Solc cli tool should be installed globally. Please put the contract name only`); prompt.start(); prompt.get(["LogicContract", "ProxyContract"], async (err, result) => { diff --git a/test/b_capped_sto.js b/test/b_capped_sto.js index e1e862844..fcafeff5b 100644 --- a/test/b_capped_sto.js +++ b/test/b_capped_sto.js @@ -2,11 +2,12 @@ import latestTime from "./helpers/latestTime"; import { duration, ensureException, latestBlock } from "./helpers/utils"; import { takeSnapshot, increaseTime, revertToSnapshot } from "./helpers/time"; import { encodeModuleCall } from "./helpers/encodeCall"; -import { setUpPolymathNetwork, deployGPMAndVerifyed, deployCappedSTOAndVerifyed } from "./helpers/createInstances"; +import { setUpPolymathNetwork, deployGPMAndVerifyed, deployCappedSTOAndVerifyed, deployDummySTOAndVerifyed } from "./helpers/createInstances"; import { catchRevert } from "./helpers/exceptions"; const CappedSTOFactory = artifacts.require("./CappedSTOFactory.sol"); const CappedSTO = artifacts.require("./CappedSTO.sol"); +const DummySTO = artifacts.require("./DummySTO.sol"); const SecurityToken = artifacts.require("./SecurityToken.sol"); const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); @@ -54,6 +55,7 @@ contract("CappedSTO", async (accounts) => { let I_STFactory; let I_SecurityToken_ETH; let I_SecurityToken_POLY; + let I_DummySTO; let I_CappedSTO_Array_ETH = []; let I_CappedSTO_Array_POLY = []; let I_PolyToken; @@ -837,6 +839,61 @@ contract("CappedSTO", async (accounts) => { }); }); + describe("Check that we can reclaim ETH and ERC20 tokens from an STO", async () => { + //xxx + it("should attach a dummy STO", async () => { + let I_DummySTOFactory; + [I_DummySTOFactory] = await deployDummySTOAndVerifyed(account_polymath, I_MRProxied, new BN(0)); + const DummySTOParameters = ["uint256", "uint256", "uint256", "string"]; + let startTime = await latestTime() + duration.days(1); + let endTime = startTime + duration.days(30); + const cap = web3.utils.toWei("10000"); + const dummyBytesSig = encodeModuleCall(DummySTOParameters, [startTime, endTime, cap, "Hello"]); + const tx = await I_SecurityToken_ETH.addModule(I_DummySTOFactory.address, dummyBytesSig, maxCost, new BN(0), { from: token_owner }); + console.log(tx.logs[2]); + assert.equal(tx.logs[2].args._types[0], stoKey, `Wrong module type added`); + assert.equal( + web3.utils.hexToString(tx.logs[2].args._name), + "DummySTO", + `Wrong STO module added` + ); + I_DummySTO = await DummySTO.at(tx.logs[2].args._module); + console.log(I_DummySTO.address); + }); + it("should send some funds and ERC20 to the DummySTO", async () => { + let tx = await web3.eth.sendTransaction({ + from: account_investor1, + to: web3.utils.toChecksumAddress(I_DummySTO.address), + gas: 2100000, + value: web3.utils.toWei("1") + }); + let dummyETH = await web3.eth.getBalance(I_DummySTO.address); + assert.equal(dummyETH.toString(), web3.utils.toWei("1")); + await I_PolyToken.getTokens(web3.utils.toWei("2"), I_DummySTO.address); + let dummyPOLY = await I_PolyToken.balanceOf(I_DummySTO.address); + assert.equal(dummyPOLY.toString(), web3.utils.toWei("2")); + }); + + it("should reclaim ETH and ERC20 from STO", async () => { + let initialIssuerETH = await web3.eth.getBalance(token_owner); + let initialIssuerPOLY = await I_PolyToken.balanceOf(token_owner); + await catchRevert(I_DummySTO.reclaimERC20(I_PolyToken.address, {from: account_polymath, gasPrice: 0})); + await catchRevert(I_DummySTO.reclaimETH( {from: account_polymath, gasPrice: 0})); + let tx = await I_DummySTO.reclaimERC20(I_PolyToken.address, {from: token_owner, gasPrice: 0}); + let tx2 = await I_DummySTO.reclaimETH({from: token_owner, gasPrice: 0}); + let finalIssuerETH = await web3.eth.getBalance(token_owner); + let finalIssuerPOLY = await I_PolyToken.balanceOf(token_owner); + let ethDifference = parseInt(web3.utils.fromWei(finalIssuerETH.toString())) - parseInt(web3.utils.fromWei(initialIssuerETH.toString())); + let polyDifference = parseInt(web3.utils.fromWei(finalIssuerPOLY.toString())) - parseInt(web3.utils.fromWei(initialIssuerPOLY.toString())); + assert.equal(ethDifference, 1); + assert.equal(polyDifference, 2); + let dummyETH = await web3.eth.getBalance(I_DummySTO.address); + assert.equal(dummyETH.toString(), 0); + let dummyPOLY = await I_PolyToken.balanceOf(I_DummySTO.address); + assert.equal(dummyPOLY.toString(), 0); + }); + }); + describe("Test cases for the CappedSTOFactory", async () => { it("should get the exact details of the factory", async () => { assert.equal((await I_CappedSTOFactory.setupCost.call()).toString(), cappedSTOSetupCost.toString()); diff --git a/test/e_erc20_dividends.js b/test/e_erc20_dividends.js index 0a1b7cc8f..eea6f61e5 100644 --- a/test/e_erc20_dividends.js +++ b/test/e_erc20_dividends.js @@ -675,10 +675,12 @@ contract("ERC20DividendCheckpoint", async (accounts) => { ); }); - it("Set withholding tax of 20% on account_temp and 10% on investor2", async () => { - await I_ERC20DividendCheckpoint.setWithholding([account_temp, account_investor2], [new BN(20).mul(new BN(10).pow(new BN(16))), new BN(10).mul(new BN(10).pow(new BN(16)))], { - from: token_owner - }); + it("Set withholding tax of 20% on account_temp and 100% on investor2", async () => { + await I_ERC20DividendCheckpoint.setWithholding( + [account_temp, account_investor2], + [new BN(20).mul(new BN(10).pow(new BN(16))), new BN(100).mul(new BN(10).pow(new BN(16)))], + { from: token_owner } + ); }); it("Should not allow mismatching input lengths", async () => { @@ -813,25 +815,41 @@ contract("ERC20DividendCheckpoint", async (accounts) => { assert.equal(dividendAmount3[0].toString(), new BN(web3.utils.toWei("7", "ether")).toString()); assert.equal(dividendAmount_temp[0].toString(), new BN(web3.utils.toWei("1", "ether")).toString()); assert.equal(dividendAmount1[1].toString(), new BN(web3.utils.toWei("0", "ether")).toString()); - assert.equal(dividendAmount2[1].toString(), new BN(web3.utils.toWei("0.2", "ether")).toString()); + assert.equal(dividendAmount2[1].toString(), new BN(web3.utils.toWei("2", "ether")).toString()); assert.equal(dividendAmount3[1].toString(), new BN(web3.utils.toWei("0", "ether")).toString()); assert.equal(dividendAmount_temp[1].toString(), new BN(web3.utils.toWei("0.2", "ether")).toString()); }); + it("Pause and unpause the dividend contract", async () => { + await catchRevert(I_ERC20DividendCheckpoint.pause({from: account_polymath})); + let tx = await I_ERC20DividendCheckpoint.pause({from: token_owner}); + await catchRevert(I_ERC20DividendCheckpoint.pullDividendPayment(3, { from: account_investor2, gasPrice: 0 })); + await catchRevert(I_ERC20DividendCheckpoint.unpause({from: account_polymath})); + tx = await I_ERC20DividendCheckpoint.unpause({from: token_owner}); + }); + it("Investor 2 claims dividend", async () => { let investor1Balance = new BN(await I_PolyToken.balanceOf(account_investor1)); let investor2Balance = new BN(await I_PolyToken.balanceOf(account_investor2)); let investor3Balance = new BN(await I_PolyToken.balanceOf(account_investor3)); let tempBalance = new BN(await web3.eth.getBalance(account_temp)); - await I_ERC20DividendCheckpoint.pullDividendPayment(3, { from: account_investor2, gasPrice: 0 }); + let _blockNo = latestBlock(); + let tx = await I_ERC20DividendCheckpoint.pullDividendPayment(3, { from: account_investor2, gasPrice: 0 }); let investor1BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor1)); let investor2BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor2)); let investor3BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor3)); let tempBalanceAfter1 = new BN(await web3.eth.getBalance(account_temp)); assert.equal(investor1BalanceAfter1.sub(investor1Balance).toNumber(), 0); - assert.equal(investor2BalanceAfter1.sub(investor2Balance).toString(), new BN(web3.utils.toWei("1.8", "ether")).toString()); + // Full amount is in withheld tax + assert.equal(investor2BalanceAfter1.sub(investor2Balance).toString(), 0); assert.equal(investor3BalanceAfter1.sub(investor3Balance).toNumber(), 0); assert.equal(tempBalanceAfter1.sub(tempBalance).toNumber(), 0); + //Check tx contains event... + const log = (await I_ERC20DividendCheckpoint.getPastEvents('ERC20DividendClaimed', {filter: {transactionHash: tx.transactionHash}}))[0]; + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._payee, account_investor2); + assert.equal(web3.utils.fromWei(log.args._withheld.toString()), 2); + assert.equal(web3.utils.fromWei(log.args._amount.toString()), 2); }); it("Should issuer pushes temp investor - investor1 excluded", async () => { @@ -839,7 +857,10 @@ contract("ERC20DividendCheckpoint", async (accounts) => { let investor2BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor2)); let investor3BalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_investor3)); let tempBalanceAfter1 = new BN(await I_PolyToken.balanceOf(account_temp)); + // Issuer can still push payments when contract is paused + let tx = await I_ERC20DividendCheckpoint.pause({from: token_owner}); await I_ERC20DividendCheckpoint.pushDividendPaymentToAddresses(3, [account_temp, account_investor1], { from: token_owner }); + tx = await I_ERC20DividendCheckpoint.unpause({from: token_owner}); let investor1BalanceAfter2 = new BN(await I_PolyToken.balanceOf(account_investor1)); let investor2BalanceAfter2 = new BN(await I_PolyToken.balanceOf(account_investor2)); let investor3BalanceAfter2 = new BN(await I_PolyToken.balanceOf(account_investor3)); @@ -908,26 +929,34 @@ contract("ERC20DividendCheckpoint", async (accounts) => { assert.equal(info[0][3], account_investor3, "account match"); assert.equal(info[3][0].toString(), 0, "withheld match"); - assert.equal(info[3][1].toString(), new BN(web3.utils.toWei("0.2", "ether")).toString(), "withheld match"); - assert.equal(info[3][2].toString(), new BN(web3.utils.toWei("0.2", "ether")).toString(), "withheld match"); + assert.equal(info[3][1].toString(), web3.utils.toWei("2", "ether"), "withheld match"); + assert.equal(info[3][2].toString(), web3.utils.toWei("0.2", "ether"), "withheld match"); assert.equal(info[3][3].toString(), 0, "withheld match"); assert.equal(info[4][0].toString(), 0, "excluded"); - assert.equal(info[4][1].toString(), new BN(web3.utils.toWei("1.8", "ether")).toString(), "claim match"); - assert.equal(info[4][2].toString(), new BN(web3.utils.toWei("0.8", "ether")).toString(), "claim match"); - assert.equal(info[4][3].toString(), new BN(web3.utils.toWei("7", "ether")).toString(), "claim match"); + assert.equal(info[4][1].toString(), web3.utils.toWei("0", "ether"), "claim match"); + assert.equal(info[4][2].toString(), web3.utils.toWei("0.8", "ether"), "claim match"); + assert.equal(info[4][3].toString(), web3.utils.toWei("7", "ether"), "claim match"); assert.equal(info[5][0].toString(), (await stGetter.balanceOfAt(account_investor1, new BN(4))).toString(), "balance match"); assert.equal(info[5][1].toString(), (await stGetter.balanceOfAt(account_investor2, new BN(4))).toString(), "balance match"); assert.equal(info[5][2].toString(), (await stGetter.balanceOfAt(account_temp, new BN(4))).toString(), "balance match"); assert.equal(info[5][3].toString(), (await stGetter.balanceOfAt(account_investor3, new BN(4))).toString(), "balance match"); - + let dividend = await I_ERC20DividendCheckpoint.dividends.call(3); + console.log("totalWithheld: " + dividend[8].toString()); + console.log("totalWithheldWithdrawn: " + dividend[9].toString()); + assert.equal(dividend[8].toString(), web3.utils.toWei("2.2", "ether")); + assert.equal(dividend[9].toString(), 0); let issuerBalance = new BN(await I_PolyToken.balanceOf(wallet)); await I_ERC20DividendCheckpoint.withdrawWithholding(new BN(3), { from: token_owner, gasPrice: 0 }); let issuerBalanceAfter = new BN(await I_PolyToken.balanceOf(wallet)); - assert.equal(issuerBalanceAfter.sub(issuerBalance).toString(), new BN(web3.utils.toWei("0.4", "ether")).toString()); - + assert.equal(issuerBalanceAfter.sub(issuerBalance).toString(), web3.utils.toWei("2.2", "ether")); + dividend = await I_ERC20DividendCheckpoint.dividends.call(3); + console.log("totalWithheld: " + dividend[8].toString()); + console.log("totalWithheldWithdrawn: " + dividend[9].toString()); + assert.equal(dividend[8].toString(), web3.utils.toWei("2.2", "ether")); + assert.equal(dividend[9].toString(), web3.utils.toWei("2.2", "ether")); }); it("Issuer changes wallet address", async () => { @@ -983,7 +1012,7 @@ contract("ERC20DividendCheckpoint", async (accounts) => { assert.equal(tx.length, 2); }); - it("should registr a delegate", async () => { + it("should register a delegate", async () => { [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); let tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x", new BN(0), new BN(0), { from: token_owner }); assert.equal(tx.logs[2].args._types[0].toNumber(), delegateManagerKey, "General Permission Manager doesn't get deployed"); @@ -1151,7 +1180,7 @@ contract("ERC20DividendCheckpoint", async (accounts) => { assert.equal(info[1][2].toString(), (await stGetter.balanceOfAt.call(account_temp, checkpointID)).toString(), "balance match"); assert.equal(info[1][3].toString(), (await stGetter.balanceOfAt.call(account_investor3, checkpointID)).toString(), "balance match"); assert.equal(info[2][0].toNumber(), 0, "withholding match"); - assert.equal(info[2][1].toString(), new BN((10 * 10 ** 16).toString()).toString(), "withholding match"); + assert.equal(info[2][1].toString(), new BN((100 * 10 ** 16).toString()).toString(), "withholding match"); assert.equal(info[2][2].toString(), new BN((20 * 10 ** 16).toString()).toString(), "withholding match"); assert.equal(info[2][3].toNumber(), 0, "withholding match"); assert.equal(tx.logs[0].args._checkpointId.toNumber(), checkpointID); @@ -1194,6 +1223,45 @@ contract("ERC20DividendCheckpoint", async (accounts) => { { from: account_manager } ); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 10); + console.log(tx.logs[0].args._dividendIndex); + }); + + it("Should fail to update the dividend dates because msg.sender is not authorised", async () => { + // failed because msg.sender is not the owner + await catchRevert( + I_ERC20DividendCheckpoint.updateDividendDates(7, 0, 1, {from: account_polymath}) + ); + }); + + it("Should fail to update the dates when the dividend get expired", async() => { + let id = await takeSnapshot(); + await increaseTime(duration.days(11)); + await catchRevert( + I_ERC20DividendCheckpoint.updateDividendDates(7, 0, 1, {from: token_owner}) + ); + await revertToSnapshot(id); + }); + + it("Should update the dividend dates", async() => { + let newMaturity = await latestTime() - duration.days(4); + let newExpiry = await latestTime() - duration.days(2); + let tx = await I_ERC20DividendCheckpoint.updateDividendDates(7, newMaturity, newExpiry, {from: token_owner}); + let info = await I_ERC20DividendCheckpoint.getDividendData.call(7); + assert.equal(info[1].toNumber(), newMaturity); + assert.equal(info[2].toNumber(), newExpiry); + // Can now reclaim the dividend + await I_ERC20DividendCheckpoint.reclaimDividend(7, {from: token_owner}); + }); + + it("Reclaim ERC20 tokens from the dividend contract", async () => { + let currentDividendBalance = await I_PolyToken.balanceOf.call(I_ERC20DividendCheckpoint.address); + let currentIssuerBalance = await I_PolyToken.balanceOf.call(token_owner); + await catchRevert(I_ERC20DividendCheckpoint.reclaimERC20(I_PolyToken.address, {from: account_polymath})); + let tx = await I_ERC20DividendCheckpoint.reclaimERC20(I_PolyToken.address, {from: token_owner}); + assert.equal(await I_PolyToken.balanceOf.call(I_ERC20DividendCheckpoint.address), 0); + let newIssuerBalance = await I_PolyToken.balanceOf.call(token_owner); + console.log("Reclaimed: " + currentDividendBalance.toString()); + assert.equal(newIssuerBalance.sub(currentIssuerBalance).toString(), currentDividendBalance.toString()); }); it("should allow manager with permission to create checkpoint", async () => { diff --git a/test/f_ether_dividends.js b/test/f_ether_dividends.js index 88fb28754..53c993da4 100644 --- a/test/f_ether_dividends.js +++ b/test/f_ether_dividends.js @@ -313,6 +313,7 @@ contract("EtherDividendCheckpoint", async (accounts) => { }); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 1, "Dividend should be created at checkpoint 1"); assert.equal(tx.logs[0].args._name.toString(), dividendName, "Dividend name incorrect in event"); + console.log("Dividend first :" + tx.logs[0].args._dividendIndex.toNumber()); }); it("Investor 1 transfers his token balance to investor 2", async () => { @@ -368,6 +369,10 @@ contract("EtherDividendCheckpoint", async (accounts) => { assert.equal(issuerBalanceAfter.sub(issuerBalance).toString(), new BN(web3.utils.toWei("0", "ether")).toString()); }); + it("Set withholding tax of 100% on investor 2", async () => { + await I_EtherDividendCheckpoint.setWithholdingFixed([account_investor2], new BN(100).mul(new BN(10).pow(new BN(16))), { from: token_owner }); + }); + it("Buy some tokens for account_temp (1 ETH)", async () => { // Add the Investor in to the whitelist @@ -398,6 +403,7 @@ contract("EtherDividendCheckpoint", async (accounts) => { value: new BN(web3.utils.toWei("1.5", "ether")) }); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 2, "Dividend should be created at checkpoint 2"); + console.log("Dividend second :" + tx.logs[0].args._dividendIndex.toNumber()); }); it("Issuer pushes dividends fails due to passed expiry", async () => { @@ -452,6 +458,7 @@ contract("EtherDividendCheckpoint", async (accounts) => { value: new BN(web3.utils.toWei("11", "ether")) }); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 3, "Dividend should be created at checkpoint 3"); + console.log("Dividend third :" + tx.logs[0].args._dividendIndex.toNumber()); }); it("should investor 3 claims dividend - fails bad index", async () => { @@ -489,22 +496,23 @@ contract("EtherDividendCheckpoint", async (accounts) => { let investor1BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor1)); let investor2BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor2)); let investor3BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor3)); - await I_EtherDividendCheckpoint.pushDividendPayment(2, new BN(0), 10, { from: token_owner }); + let _blockNo = latestBlock(); + let tx = await I_EtherDividendCheckpoint.pushDividendPayment(2, new BN(0), 10, { from: token_owner }); let investor1BalanceAfter2 = new BN(await web3.eth.getBalance(account_investor1)); let investor2BalanceAfter2 = new BN(await web3.eth.getBalance(account_investor2)); let investor3BalanceAfter2 = new BN(await web3.eth.getBalance(account_investor3)); - assert.equal(investor1BalanceAfter2.sub(investor1BalanceAfter1).toNumber(), 0); - assert.equal(investor2BalanceAfter2.sub(investor2BalanceAfter1).toString(), new BN(web3.utils.toWei("2.4", "ether")).toString()); - assert.equal(investor3BalanceAfter2.sub(investor3BalanceAfter1).toNumber(), 0); + assert.equal(investor1BalanceAfter2.sub(investor1BalanceAfter1).toString(), 0); + assert.equal(investor2BalanceAfter2.sub(investor2BalanceAfter1).toString(), 0); + assert.equal(investor3BalanceAfter2.sub(investor3BalanceAfter1).toString(), 0); //Check fully claimed - assert.equal((await I_EtherDividendCheckpoint.dividends(2))[5].toString(), new BN(web3.utils.toWei("11", "ether")).toString()); + assert.equal((await I_EtherDividendCheckpoint.dividends(2))[5].toString(), web3.utils.toWei("11", "ether")); }); it("Issuer withdraws new withholding tax", async () => { let issuerBalance = new BN(await web3.eth.getBalance(wallet)); await I_EtherDividendCheckpoint.withdrawWithholding(2, { from: token_owner, gasPrice: 0 }); let issuerBalanceAfter = new BN(await web3.eth.getBalance(wallet)); - assert.equal(issuerBalanceAfter.sub(issuerBalance).toString(), new BN(web3.utils.toWei("0.6", "ether")).toString()); + assert.equal(issuerBalanceAfter.sub(issuerBalance).toString(), new BN(web3.utils.toWei("3", "ether")).toString()); }); it("Investor 2 transfers 1 ETH of his token balance to investor 1", async () => { @@ -534,7 +542,7 @@ contract("EtherDividendCheckpoint", async (accounts) => { ); }); - it("Create another new dividend with bad expirty - fails", async () => { + it("Create another new dividend with bad expiry - fails", async () => { let maturity = await latestTime() - duration.days(5); let expiry = await latestTime() - duration.days(2); await catchRevert( @@ -590,6 +598,7 @@ contract("EtherDividendCheckpoint", async (accounts) => { { from: token_owner, value: new BN(web3.utils.toWei("10", "ether")) } ); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 4, "Dividend should be created at checkpoint 4"); + console.log("Dividend Fourth :" + tx.logs[0].args._dividendIndex.toNumber()); }); it("Should not create new dividend with duplicate exclusion", async () => { @@ -653,7 +662,7 @@ contract("EtherDividendCheckpoint", async (accounts) => { assert.equal(dividendAmount1[0].toString(), new BN(web3.utils.toWei("0", "ether")).toString()); assert.equal(dividendAmount1[1].toString(), new BN(web3.utils.toWei("0", "ether")).toString()); assert.equal(dividendAmount2[0].toString(), new BN(web3.utils.toWei("2", "ether")).toString()); - assert.equal(dividendAmount2[1].toString(), new BN(web3.utils.toWei("0.4", "ether")).toString()); + assert.equal(dividendAmount2[1].toString(), new BN(web3.utils.toWei("2", "ether")).toString()); assert.equal(dividendAmount3[0].toString(), new BN(web3.utils.toWei("7", "ether")).toString()); assert.equal(dividendAmount3[1].toString(), new BN(web3.utils.toWei("0", "ether")).toString()); assert.equal(dividendAmount_temp[0].toString(), new BN(web3.utils.toWei("1", "ether")).toString()); @@ -671,7 +680,7 @@ contract("EtherDividendCheckpoint", async (accounts) => { let investor3BalanceAfter1 = new BN(await web3.eth.getBalance(account_investor3)); let tempBalanceAfter1 = new BN(await web3.eth.getBalance(account_temp)); assert.equal(investor1BalanceAfter1.sub(investor1Balance).toNumber(), 0); - assert.equal(investor2BalanceAfter1.sub(investor2Balance).toString(), new BN(web3.utils.toWei("1.6", "ether")).toString()); + assert.equal(investor2BalanceAfter1.sub(investor2Balance).toString(), 0); assert.equal(investor3BalanceAfter1.sub(investor3Balance).toNumber(), 0); assert.equal(tempBalanceAfter1.sub(tempBalance).toNumber(), 0); }); @@ -755,6 +764,7 @@ contract("EtherDividendCheckpoint", async (accounts) => { value: new BN(web3.utils.toWei("12", "ether")) }); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 6, "Dividend should be created at checkpoint 6"); + console.log("Dividend Fifth :" + tx.logs[0].args._dividendIndex.toNumber()); }); it("Should issuer pushes all dividends", async () => { @@ -773,7 +783,7 @@ contract("EtherDividendCheckpoint", async (accounts) => { let tokenBalanceAfter = new BN(await web3.eth.getBalance(I_PolyToken.address)); assert.equal(investor1BalanceAfter.sub(investor1BalanceBefore).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); - assert.equal(investor2BalanceAfter.sub(investor2BalanceBefore).toString(), new BN(web3.utils.toWei("1.6", "ether")).toString()); + assert.equal(investor2BalanceAfter.sub(investor2BalanceBefore).toString(), 0); assert.equal(investor3BalanceAfter.sub(investor3BalanceBefore).toString(), new BN(web3.utils.toWei("7", "ether")).toString()); assert.equal(tempBalanceAfter.sub(tempBalanceBefore).toString(), new BN(web3.utils.toWei("1", "ether")).toString()); assert.equal(tokenBalanceAfter.sub(tokenBalanceBefore).toString(), new BN(web3.utils.toWei("0", "ether")).toString()); @@ -891,6 +901,7 @@ contract("EtherDividendCheckpoint", async (accounts) => { value: new BN(web3.utils.toWei("12", "ether")) }); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 9); + console.log("Dividend sixth :" + tx.logs[0].args._dividendIndex.toNumber()); }); it("should allow manager with permission to create dividend with checkpoint", async () => { @@ -903,6 +914,7 @@ contract("EtherDividendCheckpoint", async (accounts) => { value: new BN(web3.utils.toWei("12", "ether")) }); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 10); + console.log("Dividend seventh :" + tx.logs[0].args._dividendIndex.toNumber()); }); it("should allow manager with permission to create dividend with exclusion", async () => { @@ -913,7 +925,48 @@ contract("EtherDividendCheckpoint", async (accounts) => { from: account_manager, value: new BN(web3.utils.toWei("12", "ether")) }); + console.log("Dividend Eighth :" + tx.logs[0].args._dividendIndex.toNumber()); assert.equal(tx.logs[0].args._checkpointId.toNumber(), 11); + console.log(tx.logs[0].args._dividendIndex.toNumber()); + }); + + it("Should fail to update the dividend dates because msg.sender is not authorised", async () => { + // failed because msg.sender is not the owner + await catchRevert( + I_EtherDividendCheckpoint.updateDividendDates(new BN(7), new BN(0), new BN(1), {from: account_polymath}) + ); + }); + + it("Should fail to update the dates when the dividend get expired", async() => { + let id = await takeSnapshot(); + await increaseTime(duration.days(11)); + await catchRevert( + I_EtherDividendCheckpoint.updateDividendDates(new BN(7), new BN(0), new BN(1), {from: token_owner}) + ); + await revertToSnapshot(id); + }); + + it("Should update the dividend dates", async() => { + let newMaturity = await latestTime() - duration.days(4); + let newExpiry = await latestTime() - duration.days(2); + let tx = await I_EtherDividendCheckpoint.updateDividendDates(new BN(7), newMaturity, newExpiry, {from: token_owner}); + let info = await I_EtherDividendCheckpoint.getDividendData.call(7); + assert.equal(info[1].toNumber(), newMaturity); + assert.equal(info[2].toNumber(), newExpiry); + // Can now reclaim the dividend + await I_EtherDividendCheckpoint.reclaimDividend(new BN(7), {from: token_owner}); + }); + + it("Reclaim ETH from the dividend contract", async () => { + let currentDividendBalance = await web3.eth.getBalance(I_EtherDividendCheckpoint.address); + let currentIssuerBalance = await web3.eth.getBalance(token_owner); + await catchRevert(I_EtherDividendCheckpoint.reclaimETH({from: account_polymath, gasPrice: 0})); + let tx = await I_EtherDividendCheckpoint.reclaimETH({from: token_owner, gasPrice: 0}); + assert.equal(await web3.eth.getBalance(I_EtherDividendCheckpoint.address), 0); + let newIssuerBalance = await web3.eth.getBalance(token_owner); + console.log("Reclaimed: " + currentDividendBalance.toString()); + let balanceDiff = parseInt(web3.utils.fromWei(newIssuerBalance.toString())) - parseInt(web3.utils.fromWei(currentIssuerBalance.toString())) + assert.equal(balanceDiff, parseInt(web3.utils.fromWei(currentDividendBalance.toString()))); }); it("should allow manager with permission to create dividend with checkpoint and exclusion", async () => { diff --git a/test/z_vesting_escrow_wallet.js b/test/z_vesting_escrow_wallet.js index 678195f29..3b90a5d56 100644 --- a/test/z_vesting_escrow_wallet.js +++ b/test/z_vesting_escrow_wallet.js @@ -450,7 +450,11 @@ contract('VestingEscrowWallet', accounts => { await I_VestingEscrowWallet.depositTokens(numberOfTokens, {from: token_owner}); await I_VestingEscrowWallet.addSchedule(account_beneficiary3, templateName, numberOfTokens, duration, frequency, startTime, {from: wallet_admin}); await increaseTime(durationUtil.seconds(130)); - + await I_VestingEscrowWallet.pause({from: token_owner}); + await catchRevert( + I_VestingEscrowWallet.pullAvailableTokens({from: account_beneficiary3}) + ); + await I_VestingEscrowWallet.unpause({from: token_owner}); const tx = await I_VestingEscrowWallet.pullAvailableTokens({from: account_beneficiary3}); assert.equal(tx.logs[0].args._beneficiary, account_beneficiary3); assert.equal(tx.logs[0].args._numberOfTokens.toString(), numberOfTokens); @@ -466,6 +470,28 @@ contract('VestingEscrowWallet', accounts => { await I_VestingEscrowWallet.removeTemplate(templateName, {from: wallet_admin}); }); + it("Should fetch the unused tokens from the wallet", async() => { + await I_PolyToken.getTokens(new BN(20).mul(new BN(10).pow(new BN(18))), I_VestingEscrowWallet.address, {from: token_owner}); + let previousBalance = web3.utils.fromWei((await I_PolyToken.balanceOf.call(I_VestingEscrowWallet.address)).toString()); + await catchRevert( + I_VestingEscrowWallet.reclaimERC20(I_PolyToken.address, {from: account_beneficiary2}) + ) + await I_VestingEscrowWallet.reclaimERC20(I_PolyToken.address, {from: token_owner}); + let newBalance = web3.utils.fromWei((await I_PolyToken.balanceOf.call(I_VestingEscrowWallet.address)).toString()); + assert.equal(previousBalance - newBalance, 20); + }); + + it("Should fail to transfer the tokens to wallet", async() => { + await catchRevert ( + web3.eth.sendTransaction({ + from: token_owner, + to: I_VestingEscrowWallet.address, + gas: 2100000, + value: new BN(web3.utils.toWei("1", "ether")) + }) + ); + }); + it("Should withdraw available tokens 2 times by 3 schedules to the beneficiary address", async () => { currentTime = new BN(await latestTime()); let schedules = [