Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Freeze Minting #224

Merged
merged 6 commits into from Aug 30, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions contracts/ModuleRegistry.sol
Expand Up @@ -24,6 +24,9 @@ contract ModuleRegistry is IModuleRegistry, Pausable, RegistryUpdater, ReclaimTo
// Contains the list of the available tags corresponds to the module type
mapping (uint8 => bytes32[]) public availableTags;

// Indicate if minting can be frozen permanently by issuers
bool public freezeMintingAllowed;

// Emit when Module been used by the securityToken
event LogModuleUsed(address indexed _moduleFactory, address indexed _securityToken);
// Emit when the Module Factory get registered with the ModuleRegistry contract
Expand Down Expand Up @@ -118,6 +121,21 @@ contract ModuleRegistry is IModuleRegistry, Pausable, RegistryUpdater, ReclaimTo
}
}

/**
* @notice One-way feature switch to enable permanently freezing minting
thegostep marked this conversation as resolved.
Show resolved Hide resolved
*/
function allowFreezeMinting() external onlyOwner {
freezeMintingAllowed = true;
}

/**
* @notice Getter function for freezeMintingAllowed
* @return bool
*/
function freezeMintingAllowed() external view returns(bool) {
return freezeMintingAllowed;
}

/**
* @notice pause registration function
*/
Expand Down
6 changes: 6 additions & 0 deletions contracts/interfaces/IModuleRegistry.sol
Expand Up @@ -46,4 +46,10 @@ interface IModuleRegistry {
*/
function removeTagByModuleType(uint8 _moduleType, bytes32[] _removedTags) external;

/**
* @notice Indicate if minting can be frozen permanently by issuers
* @return bool
*/
function freezeMintingAllowed() external view returns(bool);

}
9 changes: 2 additions & 7 deletions contracts/interfaces/ISecurityToken.sol
Expand Up @@ -145,14 +145,9 @@ interface ISecurityToken {
function unfreezeTransfers() external;

/**
* @notice End token minting period permanently for Issuer
* @notice End token minting period permanently
*/
function finishMintingIssuer() external;

/**
* @notice End token minting period permanently for STOs
*/
function finishMintingSTO() external;
function freezeMinting() external;

/**
* @notice mints new tokens and assigns them to the target _investor.
Expand Down
76 changes: 31 additions & 45 deletions contracts/tokens/SecurityToken.sol
Expand Up @@ -49,8 +49,11 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
// Reference to token burner contract
ITokenBurner public tokenBurner;

// Use to halt all the transactions
bool public freeze = false;
// Use to temporarily halt all transactions
bool public transfersFrozen;
thegostep marked this conversation as resolved.
Show resolved Hide resolved

// Use to permanently halt all minting
bool public mintingFrozen;

struct ModuleData {
bytes32 name;
Expand All @@ -66,9 +69,6 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
mapping (address => Checkpoint[]) public checkpointBalances;
Checkpoint[] public checkpointTotalSupply;

bool public finishedIssuerMinting = false;
bool public finishedSTOMinting = false;

mapping (bytes4 => bool) transferFunctions;

// Module list should be order agnostic!
Expand Down Expand Up @@ -97,14 +97,12 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
event LogModuleRemoved(uint8 indexed _type, address _module, uint256 _timestamp);
// Emit when the budget allocated to a module is changed
event LogModuleBudgetChanged(uint8 indexed _moduleType, address _module, uint256 _budget);
// Emit when all the transfers get freeze
event LogFreezeTransfers(bool _freeze, uint256 _timestamp);
// Emit when transfers are frozen or unfrozen
event LogFreezeTransfers(bool _status, uint256 _timestamp);
// Emit when new checkpoint created
event LogCheckpointCreated(uint256 indexed _checkpointId, uint256 _timestamp);
// Emit when the minting get finished for the Issuer
event LogFinishMintingIssuer(uint256 _timestamp);
// Emit when the minting get finished for the STOs
event LogFinishMintingSTO(uint256 _timestamp);
// Emit when is permanently frozen by the issuer
event LogFreezeMinting(uint256 _timestamp);
// Change the STR address in the event of a upgrade
event LogChangeSTRAddress(address indexed _oldAddress, address indexed _newAddress);
// Events to log minting and burning
Expand Down Expand Up @@ -135,14 +133,8 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
_;
}

// Checks whether the minting is allowed or not, check for the owner if owner is no the msg.sender then check
// for the finishedSTOMinting flag because only STOs and owner are allowed for minting
modifier isMintingAllowed() {
if (msg.sender == owner) {
require(!finishedIssuerMinting, "Minting is finished for Issuer");
} else {
require(!finishedSTOMinting, "Minting is finished for STOs");
}
modifier mintingAllowed() {
require(!mintingFrozen, "Minting is permanently frozen");
_;
}

Expand Down Expand Up @@ -381,21 +373,21 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
}

/**
* @notice freeze all the transfers
* @notice freeze transfers
*/
function freezeTransfers() external onlyOwner {
require(!freeze);
freeze = true;
emit LogFreezeTransfers(freeze, now);
require(!transfersFrozen);
transfersFrozen = true;
emit LogFreezeTransfers(true, now);
}

/**
* @notice un-freeze all the transfers
* @notice unfreeze transfers
*/
function unfreezeTransfers() external onlyOwner {
require(freeze);
freeze = false;
emit LogFreezeTransfers(freeze, now);
require(transfersFrozen);
transfersFrozen = false;
emit LogFreezeTransfers(false, now);
}

/**
Expand Down Expand Up @@ -486,7 +478,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
* @return bool
*/
function verifyTransfer(address _from, address _to, uint256 _amount) public checkGranularity(_amount) returns (bool) {
if (!freeze) {
if (!transfersFrozen) {
bool isTransfer = false;
if (transferFunctions[getSig(msg.data)]) {
isTransfer = true;
Expand Down Expand Up @@ -515,29 +507,23 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr
}

/**
* @notice End token minting period permanently for Issuer
*/
function finishMintingIssuer() external onlyOwner {
finishedIssuerMinting = true;
emit LogFinishMintingIssuer(now);
}

/**
* @notice End token minting period permanently for STOs
* @notice Permanently freeze minting of this security token.
* @dev It MUST NOT be possible to increase `totalSuppy` after this function is called.
*/
function finishMintingSTO() external onlyOwner {
finishedSTOMinting = true;
emit LogFinishMintingSTO(now);
function freezeMinting() external mintingAllowed() onlyOwner {
require(IModuleRegistry(moduleRegistry).freezeMintingAllowed());
mintingFrozen = true;
emit LogFreezeMinting(now);
}

/**
* @notice mints new tokens and assigns them to the target _investor.
* @dev Can only be called by the STO attached to the token (Or by the ST owner if there's no STO attached yet)
* @param _investor Address to whom the minted tokens will be dilivered
* @param _amount Number of tokens get minted
* @dev Can only be called by the issuer or STO attached to the token
* @param _investor Address where the minted tokens will be delivered
* @param _amount Number of tokens be minted
* @return success
*/
function mint(address _investor, uint256 _amount) public onlyModule(STO_KEY, true) checkGranularity(_amount) isMintingAllowed() returns (bool success) {
function mint(address _investor, uint256 _amount) public onlyModule(STO_KEY, true) checkGranularity(_amount) mintingAllowed() returns (bool success) {
thegostep marked this conversation as resolved.
Show resolved Hide resolved
thegostep marked this conversation as resolved.
Show resolved Hide resolved
require(_investor != address(0), "Investor address should not be 0x");
adjustInvestorCount(address(0), _investor, _amount);
require(verifyTransfer(address(0), _investor, _amount), "Transfer is not valid");
Expand All @@ -552,7 +538,7 @@ contract SecurityToken is StandardToken, DetailedERC20, ReentrancyGuard, Registr

/**
* @notice mints new tokens and assigns them to the target _investor.
* Can only be called by the STO attached to the token (Or by the ST owner if there's no STO attached yet)
* @dev Can only be called by the issuer or STO attached to the token.
* @param _investors A list of addresses to whom the minted tokens will be dilivered
* @param _amounts A list of number of tokens get minted and transfer to corresponding address of the investor from _investor[] list
* @return success
Expand Down
69 changes: 33 additions & 36 deletions test/o_security_token.js
Expand Up @@ -421,45 +421,63 @@ contract('SecurityToken', accounts => {
assert.equal(balance2.dividedBy(new BigNumber(10).pow(18)).toNumber(), 110);
});

it("Should finish the minting -- fail because msg.sender is not the owner", async() => {
it("Should finish the minting -- fail because feature is not activated", async() => {
let errorThrown = false;
try {
await I_SecurityToken.finishMintingIssuer({from: account_temp});
await I_SecurityToken.freezeMinting({from: account_issuer});
} catch(error) {
console.log(` tx revert -> finishMintingIssuer only be called by the owner of the SecurityToken`.grey);
console.log(` tx revert -> freezeMinting cannot be called before activated by polymath`.grey);
errorThrown = true;
ensureException(error);
}
assert.ok(errorThrown, message);
});

it("Should finish minting & rstrict the further minting", async() => {
let id = await takeSnapshot();
await I_SecurityToken.finishMintingIssuer({from: account_issuer});
it("Should finish the minting -- fail to activate the feature because msg.sender is not polymath", async() => {
let errorThrown = false;
try {
await I_SecurityToken.mint(account_affiliate1, (100 * Math.pow(10, 18)), {from: token_owner, gas: 500000});
await I_ModuleRegistry.allowFreezeMinting({from: account_issuer});
} catch(error) {
console.log(` tx revert -> Minting is finished`.grey);
console.log(` tx revert -> allowFreezeMinting mus be called by polymath`.grey);
errorThrown = true;
ensureException(error);
}
assert.ok(errorThrown, message);
await revertToSnapshot(id);
});

it("Should finish the minting -- successfully activate the feature", async() => {
assert.equal(false, await I_ModuleRegistry.freezeMintingAllowed());
await I_ModuleRegistry.allowFreezeMinting({from: account_polymath});
assert.equal(true, await I_ModuleRegistry.freezeMintingAllowed());
});

it("Should finish the minting -- fail because msg.sender is not the owner", async() => {
let errorThrown = false;
try {
await I_SecurityToken.finishMintingSTO({from: account_temp});
await I_SecurityToken.freezeMinting({from: account_temp});
} catch(error) {
console.log(` tx revert -> finishMintingSTO only be called by the owner of the SecurityToken`.grey);
console.log(` tx revert -> finishMintingIssuer only be called by the owner of the SecurityToken`.grey);
errorThrown = true;
ensureException(error);
}
assert.ok(errorThrown, message);
});

it("Should finish minting & rstrict the further minting", async() => {
let id = await takeSnapshot();
await I_SecurityToken.freezeMinting({from: account_issuer});
let errorThrown = false;
try {
await I_SecurityToken.mint(account_affiliate1, (100 * Math.pow(10, 18)), {from: token_owner, gas: 500000});
} catch(error) {
console.log(` tx revert -> Minting is finished`.grey);
errorThrown = true;
ensureException(error);
}
assert.ok(errorThrown, message);
await revertToSnapshot(id);
});

it("Should fail to attach the STO factory because not enough poly in contract", async () => {
startTime = latestTime() + duration.seconds(5000);
endTime = startTime + duration.days(30);
Expand Down Expand Up @@ -677,27 +695,6 @@ contract('SecurityToken', accounts => {
);
});

it("Should finish minting & rstrict the further minting", async() => {
let id = await takeSnapshot();
await I_SecurityToken.finishMintingSTO({from: account_issuer});
let errorThrown = false;
try {
// Fallback transaction
await web3.eth.sendTransaction({
from: account_investor1,
to: I_CappedSTO.address,
gas: 2100000,
value: web3.utils.toWei('2', 'ether')
});
} catch(error) {
console.log(` tx revert -> Minting is finished`.grey);
errorThrown = true;
ensureException(error);
}
assert.ok(errorThrown, message);
await revertToSnapshot(id);
});

it("Should Fail in transferring the token from one whitelist investor 1 to non whitelist investor 2", async() => {
let errorThrown = false;
try {
Expand Down Expand Up @@ -986,10 +983,10 @@ contract('SecurityToken', accounts => {

it("Should freeze the transfers", async() => {
let tx = await I_SecurityToken.freezeTransfers({from: token_owner});
assert.isTrue(tx.logs[0].args._freeze);
assert.isTrue(tx.logs[0].args._status);
});

it("Should freeze the transfers", async() => {
it("Should fail to freeze the transfers", async() => {
let errorThrown = false;
try {
await I_SecurityToken.freezeTransfers({from: token_owner});
Expand Down Expand Up @@ -1047,9 +1044,9 @@ contract('SecurityToken', accounts => {
assert.ok(errorThrown, message);
});

it("Should un freeze all the transfers", async() => {
it("Should unfreeze all the transfers", async() => {
let tx = await I_SecurityToken.unfreezeTransfers({from: token_owner});
assert.isFalse(tx.logs[0].args._freeze);
assert.isFalse(tx.logs[0].args._status);
});

it("Should freeze the transfers", async() => {
Expand Down