diff --git a/contracts/OpenSTUtility.sol b/contracts/OpenSTUtility.sol index 76335345..cfeb1201 100644 --- a/contracts/OpenSTUtility.sol +++ b/contracts/OpenSTUtility.sol @@ -341,7 +341,7 @@ contract OpenSTUtility is Hasher, OpsManaged { require(_stakingUnlockHeight > 0); require(_stakingIntentHash != ""); - expirationHeight = block.number + BLOCKS_TO_WAIT_SHORT; + expirationHeight = block.number + blocksToWaitShort(); nonces[_staker] = _stakerNonce; bytes32 stakingIntentHash = hashStakingIntent( @@ -459,7 +459,7 @@ contract OpenSTUtility is Hasher, OpsManaged { require(token.allowance(msg.sender, address(this)) >= _amountBT); require(token.transferFrom(msg.sender, address(this), _amountBT)); - unlockHeight = block.number + BLOCKS_TO_WAIT_LONG; + unlockHeight = block.number + blocksToWaitLong(); redemptionIntentHash = hashRedemptionIntent( _uuid, @@ -502,7 +502,7 @@ contract OpenSTUtility is Hasher, OpsManaged { nonces[msg.sender] = _nonce; amountSTP = msg.value; - unlockHeight = block.number + BLOCKS_TO_WAIT_LONG; + unlockHeight = block.number + blocksToWaitLong(); redemptionIntentHash = hashRedemptionIntent( uuidSTPrime, @@ -611,32 +611,40 @@ contract OpenSTUtility is Hasher, OpsManaged { registeredToken.registrar); } - /* - * Administrative functions - */ - function initiateProtocolTransfer( - ProtocolVersioned _token, - address _proposedProtocol) - public - onlyAdmin - returns (bool) - { - _token.initiateProtocolTransfer(_proposedProtocol); + /* + * Administrative functions + */ + function initiateProtocolTransfer( + ProtocolVersioned _token, + address _proposedProtocol) + public + onlyAdmin + returns (bool) + { + _token.initiateProtocolTransfer(_proposedProtocol); - return true; - } + return true; + } - // on the very first released version v0.9.1 there is no need - // to completeProtocolTransfer from a previous version + // on the very first released version v0.9.1 there is no need + // to completeProtocolTransfer from a previous version - function revokeProtocolTransfer( - ProtocolVersioned _token) - public - onlyAdmin - returns (bool) - { - _token.revokeProtocolTransfer(); + function revokeProtocolTransfer( + ProtocolVersioned _token) + public + onlyAdmin + returns (bool) + { + _token.revokeProtocolTransfer(); + + return true; + } + + function blocksToWaitLong() public pure returns (uint256) { + return BLOCKS_TO_WAIT_LONG; + } - return true; + function blocksToWaitShort() public pure returns (uint256) { + return BLOCKS_TO_WAIT_SHORT; } } \ No newline at end of file diff --git a/contracts/OpenSTUtilityMock.sol b/contracts/OpenSTUtilityMock.sol new file mode 100644 index 00000000..3895310b --- /dev/null +++ b/contracts/OpenSTUtilityMock.sol @@ -0,0 +1,49 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// Utility chain: OpenSTUtilityMock.sol +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +import "./OpenSTUtility.sol"; + +/// @title OpenSTUtilityMock +/// @dev Overrides certain durational constants and getters to ease testing OpenSTUtility +contract OpenSTUtilityMock is OpenSTUtility { + uint256 private constant BLOCKS_TO_WAIT_LONG = 8; + uint256 private constant BLOCKS_TO_WAIT_SHORT = 5; + + /* + * Public functions + */ + function OpenSTUtilityMock( + uint256 _chainIdValue, + uint256 _chainIdUtility, + address _registrar) + OpenSTUtility(_chainIdValue, _chainIdUtility, _registrar) + public { } + + function blocksToWaitLong() public pure returns (uint256) { + return BLOCKS_TO_WAIT_LONG; + } + + function blocksToWaitShort() public pure returns (uint256) { + return BLOCKS_TO_WAIT_SHORT; + } +} \ No newline at end of file diff --git a/contracts/OpenSTValue.sol b/contracts/OpenSTValue.sol index 8b28013b..ce9a5706 100644 --- a/contracts/OpenSTValue.sol +++ b/contracts/OpenSTValue.sol @@ -63,9 +63,9 @@ contract OpenSTValue is OpsManaged, Hasher { uint8 public constant TOKEN_DECIMALS = 18; uint256 public constant DECIMALSFACTOR = 10**uint256(TOKEN_DECIMALS); // ~2 weeks, assuming ~15s per block - uint256 public constant BLOCKS_TO_WAIT_LONG = 80667; + uint256 private constant BLOCKS_TO_WAIT_LONG = 80667; // ~1hour, assuming ~15s per block - uint256 public constant BLOCKS_TO_WAIT_SHORT = 240; + uint256 private constant BLOCKS_TO_WAIT_SHORT = 240; /* * Structures @@ -184,7 +184,7 @@ contract OpenSTValue is OpsManaged, Hasher { require(valueToken.transferFrom(tx.origin, address(this), _amountST)); amountUT = _amountST.mul(utilityToken.conversionRate); - unlockHeight = block.number + BLOCKS_TO_WAIT_LONG; + unlockHeight = block.number + blocksToWaitLong(); nonces[tx.origin]++; nonce = nonces[tx.origin]; @@ -308,7 +308,7 @@ contract OpenSTValue is OpsManaged, Hasher { require(_redemptionIntentHash == redemptionIntentHash); - expirationHeight = block.number + BLOCKS_TO_WAIT_SHORT; + expirationHeight = block.number + blocksToWaitShort(); UtilityToken storage utilityToken = utilityTokens[_uuid]; // minimal precision to unstake 1 STWei @@ -435,6 +435,14 @@ contract OpenSTValue is OpsManaged, Hasher { utilityToken.stakingAccount); } + function blocksToWaitLong() public pure returns (uint256) { + return BLOCKS_TO_WAIT_LONG; + } + + function blocksToWaitShort() public pure returns (uint256) { + return BLOCKS_TO_WAIT_SHORT; + } + /* * Registrar functions */ diff --git a/contracts/OpenSTValueMock.sol b/contracts/OpenSTValueMock.sol new file mode 100644 index 00000000..ea329833 --- /dev/null +++ b/contracts/OpenSTValueMock.sol @@ -0,0 +1,49 @@ +pragma solidity ^0.4.17; + +// Copyright 2017 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ---------------------------------------------------------------------------- +// Value chain: OpenSTValueMock.sol +// +// http://www.simpletoken.org/ +// +// ---------------------------------------------------------------------------- + +import "./OpenSTValue.sol"; + +/// @title OpenSTValueMock +/// @dev Overrides certain durational constants and getters to ease testing OpenSTValue +contract OpenSTValueMock is OpenSTValue { + uint256 private constant BLOCKS_TO_WAIT_LONG = 8; + uint256 private constant BLOCKS_TO_WAIT_SHORT = 5; + + /* + * Public functions + */ + function OpenSTValueMock( + uint256 _chainIdValue, + EIP20Interface _eip20token, + address _registrar) + OpenSTValue(_chainIdValue, _eip20token, _registrar) + public { } + + function blocksToWaitLong() public pure returns (uint256) { + return BLOCKS_TO_WAIT_LONG; + } + + function blocksToWaitShort() public pure returns (uint256) { + return BLOCKS_TO_WAIT_SHORT; + } +} \ No newline at end of file diff --git a/test/OpenSTUtility_utils.js b/test/OpenSTUtility_utils.js index 7f114171..3ad13502 100644 --- a/test/OpenSTUtility_utils.js +++ b/test/OpenSTUtility_utils.js @@ -197,9 +197,35 @@ module.exports.checkProcessedRedemptionEvent = (event, _uuid, _redemptionIntentH } assert.equal(event.event, "ProcessedRedemption"); - assert.equal(event.args._uuid, _uuid) - assert.equal(event.args._redemptionIntentHash, _redemptionIntentHash) - assert.equal(event.args._token, _token) - assert.equal(event.args._redeemer, _redeemer) - assert.equal(event.args._amount.toNumber(), _amount.toNumber()) + assert.equal(event.args._uuid, _uuid); + assert.equal(event.args._redemptionIntentHash, _redemptionIntentHash); + assert.equal(event.args._token, _token); + assert.equal(event.args._redeemer, _redeemer); + assert.equal(event.args._amount.toNumber(), _amount.toNumber()); +} + +module.exports.checkRevertedMintEvent = (event, _uuid, _stakingIntentHash, _staker, _beneficiary, _amount) => { + if (Number.isInteger(_amount)) { + _amount = new BigNumber(_amount); + } + + assert.equal(event.event, "RevertedMint"); + assert.equal(event.args._uuid, _uuid) + assert.equal(event.args._stakingIntentHash, _stakingIntentHash) + assert.equal(event.args._staker, _staker) + assert.equal(event.args._beneficiary, _beneficiary) + assert.equal(event.args._amountUT.toNumber(), _amount.toNumber()) +} + +module.exports.checkRevertedRedemption = (event, _uuid, _redemptionIntentHash, _redeemer, _amountUT) => { + if (Number.isInteger(_amountUT)) { + _amount = new BigNumber(_amountUT); + } + + assert.equal(event.event, "RevertedRedemption"); + assert.equal(event.args._uuid, _uuid); + assert.equal(event.args._redemptionIntentHash, _redemptionIntentHash); + assert.equal(event.args._redeemer, _redeemer); + assert.equal(event.args._amountUT.toNumber(), _amountUT.toNumber()); + } diff --git a/test/OpenSTValue_utils.js b/test/OpenSTValue_utils.js index a9782ae4..c2ddd8e3 100644 --- a/test/OpenSTValue_utils.js +++ b/test/OpenSTValue_utils.js @@ -118,38 +118,6 @@ module.exports.checkStakingIntentDeclaredEvent = (event, _uuid, _staker, _staker assert.equal(event.args._chainIdUtility, _chainIdUtility); } - -module.exports.checkStakingIntentDeclaredEventProtocol = (formattedDecodedEvents, _uuid, _staker, _stakerNonce, _beneficiary, - _amountST, _amountUT, _chainIdUtility) => { - - var event = formattedDecodedEvents['StakingIntentDeclared']; - - if (Number.isInteger(_stakerNonce)) { - _stakerNonce = new BigNumber(_stakerNonce); - } - - if (Number.isInteger(_amountST)) { - _amountST = new BigNumber(_amountST); - } - - if (Number.isInteger(_amountUT)) { - _amountUT = new BigNumber(_amountUT); - } - - if (Number.isInteger(_chainIdUtility)) { - _chainIdUtility = new BigNumber(_chainIdUtility); - } - - assert.equal(event.event, "StakingIntentDeclared"); - assert.equal(event._uuid, _uuid); - assert.equal(event._staker, _staker); - assert.equal(event._stakerNonce, _stakerNonce.toNumber()); - assert.equal(event._beneficiary, _beneficiary); - assert.equal(event._amountST, _amountST.toNumber()); - assert.equal(event._amountUT, _amountUT.toNumber()); - assert.equal(event._chainIdUtility, _chainIdUtility.toNumber()); -} - module.exports.checkStakingIntentDeclaredEventProtocol = (event, _uuid, _staker, _stakerNonce, _beneficiary, _amountST, _amountUT, _chainIdUtility) => { @@ -252,4 +220,36 @@ module.exports.checkProcessedUnstakeEvent = (event, _uuid, _redemptionIntentHash assert.equal(event.args.stake, stake); assert.equal(event.args._redeemer, _redeemer); assert.equal(event.args._amountST.toNumber(), _amountST.toNumber()); +} + +module.exports.checkRevertStakingEventProtocol = (event, _uuid, _stakingIntentHash, _staker, _amountST, _amountUT) => { + + if (Number.isInteger(_amountUT)) { + _amountUT = new BigNumber(_amountUT); + } + + if (Number.isInteger(_amountST)) { + _amountST = new BigNumber(_amountST); + } + + assert.equal(event.event, "RevertedStake"); + assert.equal(event.args._uuid, _uuid); + assert.equal(event.args._staker, _staker); + assert.equal(event.args._stakingIntentHash, _stakingIntentHash); + assert.equal(event.args._amountST, _amountST.toNumber()); + assert.equal(event.args._amountUT, _amountUT.toNumber()); +} + +module.exports.checkRevertedUnstake = (event, _uuid, _redemptionIntentHash, _redeemer, _amountST) => { + + if (Number.isInteger(_amountST)) { + _amountST = new BigNumber(_amountST); + } + + assert.equal(event.event, "RevertedUnstake"); + assert.equal(event.args._uuid, _uuid); + assert.equal(event.args._redemptionIntentHash, _redemptionIntentHash); + assert.equal(event.args._redeemer, _redeemer); + assert.equal(event.args._amountST, _amountST.toNumber()); + } \ No newline at end of file diff --git a/test/Protocol.js b/test/Protocol.js index 30cc9ed4..d601ac33 100644 --- a/test/Protocol.js +++ b/test/Protocol.js @@ -28,9 +28,10 @@ const web3EventsDecoder = require('./lib/event_decoder.js'); const ProtocolUtils = require('./Protocol_utils.js'); -const openSTUtilityArtifacts = artifacts.require("./OpenSTUtility.sol"); -const openSTValueArtifacts = artifacts.require("./OpenSTValue.sol"); +const openSTUtilityArtifacts = artifacts.require("./OpenSTUtilityMock.sol"); +const openSTValueArtifacts = artifacts.require("./OpenSTValueMock.sol"); const BrandedToken = artifacts.require("./BrandedToken.sol"); +const SimpleStake = artifacts.require("./SimpleStake.sol"); const CHAINID_VALUE = new BigNumber(2001); const CHAINID_UTILITY = new BigNumber(2002); @@ -76,9 +77,10 @@ contract('OpenST', function(accounts) { var openSTValue = null; var openSTUtility = null; var stPrime = null; + var btSimpleStake = null; - var btSimpleStakeContractAddress = null; - var stPrimeSimpleStakeContractAddress = null; + var btSimpleStakeContractAddress = null; + var stPrimeSimpleStakeContractAddress = null; var registeredBrandedTokenUuid = null; var registeredBrandedToken = null; var nonceBT = null; @@ -247,6 +249,8 @@ contract('OpenST', function(accounts) { btSimpleStakeContractAddress = event.stake; + btSimpleStake = SimpleStake.at(btSimpleStakeContractAddress); + utils.logResponse(result, "OpenSTValue.registerUtilityToken"); }); @@ -369,7 +373,6 @@ contract('OpenST', function(accounts) { }); - // unstake BT by Redeemer context('Redeem and Unstake Branded Token', function() { it("approve branded token", async() => { @@ -498,5 +501,520 @@ contract('OpenST', function(accounts) { }); + // Revert stake + context('Revert stake', function() { + + it("call stake ", async() => { + // transfer ST to requester account + Assert.ok(await simpleToken.transfer(requester, AMOUNT_ST, { from: deployMachine })); + // Check for requester simpleToken Balance + var balanceOfRequester = await simpleToken.balanceOf(requester); + Assert.equal(balanceOfRequester, AMOUNT_ST.toNumber()); + // requester sets allowance for OpenSTValue + Assert.ok(await simpleToken.approve(openSTValue.address, AMOUNT_ST, { from: requester })); + + nonceBT = await openSTValue.getNextNonce.call(requester); + // requester calls OpenSTValue.stake to initiate the staking for Branded Token with registeredBrandedTokenUuid + // with requester as the beneficiary + var stakeResult = await openSTValue.stake(registeredBrandedTokenUuid, AMOUNT_ST, requester, { from: requester }); + + openSTValueUtils.checkStakingIntentDeclaredEventProtocol(stakeResult.logs[0], registeredBrandedTokenUuid, requester, nonceBT, + requester, AMOUNT_ST, AMOUNT_BT, CHAINID_UTILITY); + + stakingIntentHash = stakeResult.logs[0].args._stakingIntentHash; + + }); + + // // Before wait time as passed + it('fails to revertStaking before waiting period ends', async () => { + var waitTime = await openSTValue.blocksToWaitLong.call(); + waitTime = waitTime.toNumber(); + // Wait time less 1 block for preceding test case and 1 block because condition is <= + for (var i = 0; i < waitTime/2 ; i++) { + await web3.eth.sendTransaction({ from: owner, to: admin, value: 0.000000000000000000001 }); + await web3.eth.sendTransaction({ from: admin, to: owner, value: 0.000000000000000000001 }); + } + }); + + it("revert staking after unlocking block height", async() => { + // Revert staking from staker user as it can called from any external user. + // If we put this as a contrain this test case will fail + var result = await openSTValue.revertStaking(stakingIntentHash, {from: staker}); + openSTValueUtils.checkRevertStakingEventProtocol(result.logs[0], registeredBrandedTokenUuid, stakingIntentHash, requester, + AMOUNT_ST, AMOUNT_BT) + + }); + + }); + + // // Revert minting + // // Revert staking should also work smoothly before calling revert minting + context('Revert minting', function() { + + it("call stake ", async() => { + // transfer ST to requester account + Assert.ok(await simpleToken.transfer(requester, AMOUNT_ST, { from: deployMachine })); + // requester sets allowance for OpenSTValue + Assert.ok(await simpleToken.approve(openSTValue.address, AMOUNT_ST, { from: requester })); + + nonceBT = await openSTValue.getNextNonce.call(requester); + // requester calls OpenSTValue.stake to initiate the staking for Branded Token with registeredBrandedTokenUuid + // with requester as the beneficiary + var stakeResult = await openSTValue.stake(registeredBrandedTokenUuid, AMOUNT_ST, requester, { from: requester }); + + openSTValueUtils.checkStakingIntentDeclaredEventProtocol(stakeResult.logs[0], registeredBrandedTokenUuid, requester, nonceBT, + requester, AMOUNT_ST, AMOUNT_BT, CHAINID_UTILITY); + + stakingIntentHash = stakeResult.logs[0].args._stakingIntentHash; + unlockHeight = stakeResult.logs[0].args._unlockHeight; + nonceBT = stakeResult.logs[0].args._stakerNonce; + + }); + + it("confirm staking intent for Branded Token", async() => { + + const result = await registrarUC.confirmStakingIntent(openSTUtility.address, registeredBrandedTokenUuid, + requester, nonceBT, requester, AMOUNT_ST, AMOUNT_BT, unlockHeight, stakingIntentHash, { from: intercommUC }); + + var formattedDecodedEvents = web3EventsDecoder.perform(result.receipt, openSTUtility.address, openSTUtilityArtifacts.abi); + + openSTUtilityUtils.checkStakingIntentConfirmedEventOnProtocol(formattedDecodedEvents, registeredBrandedTokenUuid, + stakingIntentHash, requester, requester, AMOUNT_ST, AMOUNT_BT); + + utils.logResponse(result, "OpenSTUtility.confirmStakingIntent"); + + }); + + // Before wait time as passed + // Revert minting should run only if revert staking can be done. Hence checking that first + it('fails to revertStaking before waiting period ends', async () => { + var waitTime = await openSTValue.blocksToWaitLong.call(); + waitTime = waitTime.toNumber(); + // Wait time less 1 block for preceding test case and 1 block because condition is <= + for (var i = 0; i < waitTime/2; i++) { + await web3.eth.sendTransaction({ from: owner, to: admin, value: 0.000000000000000000001 }); + await web3.eth.sendTransaction({ from: admin, to: owner, value: 0.000000000000000000001 }); + } + }); + + it("revert staking after unlocking block height", async() => { + // Revert staking from staker user as it can called from any external user. + // If we put this as a contraint this test case will fail + var result = await openSTValue.revertStaking(stakingIntentHash, {from: staker}); + openSTValueUtils.checkRevertStakingEventProtocol(result.logs[0], registeredBrandedTokenUuid, stakingIntentHash, requester, + AMOUNT_ST, AMOUNT_BT) + + }); + + // Before wait time as passed + it('fails to revertMinting before expiration block', async () => { + var waitTime = await openSTUtility.blocksToWaitShort.call(); + waitTime = waitTime.toNumber(); + // Wait time less 1 block for preceding test case and 1 block because condition is <= + for (var i = 0; i < waitTime/2; i++) { + await web3.eth.sendTransaction({ from: owner, to: admin, value: 0.000000000000000000001 }); + await web3.eth.sendTransaction({ from: admin, to: owner, value: 0.000000000000000000001 }); + } + }); + + it("revert minting after expiring block height", async() => { + // Revert minting from staker user as it can called from any external user. + // If we put this as a contraint this test case will fail + var result = await openSTUtility.revertMinting(stakingIntentHash, {from: staker}); + openSTUtilityUtils.checkRevertedMintEvent(result.logs[0], registeredBrandedTokenUuid, stakingIntentHash, + requester, requester, AMOUNT_BT) + + }); + + }); + // + // // After process staking, revertStake cannot be called but revertMinting can still be called. + // // As of now ST get stuck in SimpleStake Contract for the staker which should ideally be returned after revertMinting + // // Checking the ST balance in SimpleStake as non zero which will start failing once we fix the issue of + // // releasing ST after revertMinting as well + context('Revert minting after process Staking', function() { + + var previousSTBalance = null; + + it("previous balance on simple stake", async() => { + + previousSTBalance = await btSimpleStake.getTotalStake.call(); + + }); + + it("call stake ", async() => { + + // transfer ST to requester account + Assert.ok(await simpleToken.transfer(requester, AMOUNT_ST, { from: deployMachine })); + Assert.ok(await simpleToken.approve(openSTValue.address, AMOUNT_ST, { from: requester })); + + nonceBT = await openSTValue.getNextNonce.call(requester); + // requester calls OpenSTValue.stake to initiate the staking for Branded Token with registeredBrandedTokenUuid + // with requester as the beneficiary + var stakeResult = await openSTValue.stake(registeredBrandedTokenUuid, AMOUNT_ST, requester, { from: requester }); + + openSTValueUtils.checkStakingIntentDeclaredEventProtocol(stakeResult.logs[0], registeredBrandedTokenUuid, requester, nonceBT, + requester, AMOUNT_ST, AMOUNT_BT, CHAINID_UTILITY); + + stakingIntentHash = stakeResult.logs[0].args._stakingIntentHash; + unlockHeight = stakeResult.logs[0].args._unlockHeight; + nonceBT = stakeResult.logs[0].args._stakerNonce; + + }); + + it("confirm staking intent for Branded Token", async() => { + + const result = await registrarUC.confirmStakingIntent(openSTUtility.address, registeredBrandedTokenUuid, + requester, nonceBT, requester, AMOUNT_ST, AMOUNT_BT, unlockHeight, stakingIntentHash, { from: intercommUC }); + + var formattedDecodedEvents = web3EventsDecoder.perform(result.receipt, openSTUtility.address, openSTUtilityArtifacts.abi); + + openSTUtilityUtils.checkStakingIntentConfirmedEventOnProtocol(formattedDecodedEvents, registeredBrandedTokenUuid, + stakingIntentHash, requester, requester, AMOUNT_ST, AMOUNT_BT); + + utils.logResponse(result, "OpenSTUtility.confirmStakingIntent"); + + }); + + it("process staking", async () => { + const o = await openSTValue.processStaking(stakingIntentHash, { from: requester }); + utils.logResponse(o, "OpenSTValue.processStaking"); + }); + + // Before wait time as passed + it('fails to revertMinting before expiration block', async () => { + var waitTime = await openSTUtility.blocksToWaitShort.call(); + waitTime = waitTime.toNumber(); + // Wait time less 1 block for preceding test case and 1 block because condition is <= + for (var i = 0; i < waitTime/2; i++) { + await web3.eth.sendTransaction({ from: owner, to: admin, value: 0.000000000000000000001 }); + await web3.eth.sendTransaction({ from: admin, to: owner, value: 0.000000000000000000001 }); + } + }); + + // Before wait time as passed + // Revert staking called after unlock block + it('fails to revertStaking before waiting period ends', async () => { + var waitTime = await openSTValue.blocksToWaitLong.call(); + waitTime = waitTime.toNumber(); + // Wait time less 1 block for preceding test case and 1 block because condition is <= + for (var i = 0; i < waitTime/2; i++) { + await web3.eth.sendTransaction({ from: owner, to: admin, value: 0.000000000000000000001 }); + await web3.eth.sendTransaction({ from: admin, to: owner, value: 0.000000000000000000001 }); + } + }); + + it("revert staking should not be allowed after processStaking", async() => { + await utils.expectThrow(openSTValue.revertStaking(stakingIntentHash, {from: staker})); + }); + + it("revert minting after expiring block height", async() => { + // Revert minting from staker user as it can be called from any external user. + // If we put this as a contraint this test case will fail + var result = await openSTUtility.revertMinting(stakingIntentHash, {from: staker}); + openSTUtilityUtils.checkRevertedMintEvent(result.logs[0], registeredBrandedTokenUuid, stakingIntentHash, + requester, requester, AMOUNT_BT); + }); + + it ("validate if the ST is stuck in Simple Stake", async() => { + var currentStBalance = await btSimpleStake.getTotalStake.call() + var tobeBalance = previousSTBalance.toNumber() + AMOUNT_ST.toNumber(); + Assert.equal(currentStBalance.toNumber(), tobeBalance) + }); + + }); + + // SEQUENCE OF EVENTS + // Call Redeem => Call RevertRedemption + // + context('call redeem then revertRedemption', function() { + + // Redeemer should have some branded token + it("transfers branded token to redeemer", async() => { + + var result = await brandedToken.transfer(redeemer, REDEEM_AMOUNT_BT, {from: requester}); + Assert.ok(result); + var balanceOfRedeemer = await brandedToken.balanceOf(redeemer); + Assert.equal(balanceOfRedeemer, REDEEM_AMOUNT_BT.toNumber()); + + utils.logResponse(result, "OpenSTUtility.revertRedemption.transfer"); + + }); + + // Call redeem + it("gives allowance and calls redeem", async() => { + + var approveResult = await brandedToken.approve(openSTUtility.address, REDEEM_AMOUNT_BT, { from: redeemer }); + Assert.ok(approveResult); + + nonce = await openSTValue.getNextNonce.call(redeemer); + var redeemResult = await openSTUtility.redeem(registeredBrandedTokenUuid, REDEEM_AMOUNT_BT, nonce, { from: redeemer }); + redemptionIntentHash = redeemResult.logs[0].args._redemptionIntentHash; + unlockHeight = redeemResult.logs[0].args._unlockHeight; + openSTUtilityUtils.checkRedemptionIntentDeclaredEvent(redeemResult.logs[0], registeredBrandedTokenUuid, redemptionIntentHash, + brandedToken.address, redeemer, nonce, REDEEM_AMOUNT_BT, unlockHeight, CHAINID_VALUE); + + utils.logResponse(redeemResult, "OpenSTUtility.revertRedemption.redeem"); + + }); + + // Wait though fake transactions so that redeem is expired + it('waits till redeem is expired', async () => { + var waitTime = await openSTUtility.blocksToWaitLong.call(); + waitTime = waitTime.toNumber(); + var amountToTransfer = new BigNumber(0.000001).mul(DECIMALSFACTOR); + // Mock transactions so that block number increases + for (var i = 0; i < waitTime; i++) { + await web3.eth.sendTransaction({ from: owner, to: admin, value: amountToTransfer, gasPrice: '0x12A05F200' }); + } + }); + + // after redeem, revert redemption can be called + it("revert redemption", async() => { + + var revertResult = await openSTUtility.revertRedemption(redemptionIntentHash, { from: redeemer }); + openSTUtilityUtils.checkRevertedRedemption(revertResult.logs[0], registeredBrandedTokenUuid, redemptionIntentHash, redeemer, + REDEEM_AMOUNT_BT); + + utils.logResponse(revertResult, "OpenSTUtility.redeem.revertRedemption"); + + }); + + // Reports gas usage + it("report gas usage: revert redemption", async () => { + + utils.printGasStatistics(); + utils.clearReceipts(); + + }); + + }); + + // SEQUENCE OF EVENTS + // Redeem => confirmRedemptionIntent => revertRedemption => revertUnstaking + // + context('call redeem then confirmRedemptionIntent then revertRedemption then revertUnstaking', function() { + + // Since we reverted redemption in above case we don't need to transfer again as redeemer will already have REDEEM_AMOUNT_BT balance + it("check branded token balance of redeemer", async() => { + + var balanceOfRedeemer = await brandedToken.balanceOf(redeemer); + Assert.equal(balanceOfRedeemer, REDEEM_AMOUNT_BT.toNumber()); + + }); + + // Gives allawance and call redeem + it("gives allowance and calls redeem", async() => { + + var approveResult = await brandedToken.approve(openSTUtility.address, REDEEM_AMOUNT_BT, { from: redeemer }) + Assert.ok(approveResult); + + nonce = await openSTValue.getNextNonce.call(redeemer); + var redeemResult = await openSTUtility.redeem(registeredBrandedTokenUuid, REDEEM_AMOUNT_BT, nonce, { from: redeemer }); + redemptionIntentHash = redeemResult.logs[0].args._redemptionIntentHash; + unlockHeight = redeemResult.logs[0].args._unlockHeight; + openSTUtilityUtils.checkRedemptionIntentDeclaredEvent(redeemResult.logs[0], registeredBrandedTokenUuid, redemptionIntentHash, + brandedToken.address, redeemer, nonce, REDEEM_AMOUNT_BT, unlockHeight, CHAINID_VALUE); + + utils.logResponse(redeemResult, "OpenSTUtility.revertUnstake.redeem"); + + }); + + // Call confirmRedemptionIntent + it("calls confirmRedemptionIntent", async() => { + + var confirmRedemptionResult = await registrarVC.confirmRedemptionIntent( openSTValue.address, registeredBrandedTokenUuid, + redeemer, nonce, REDEEM_AMOUNT_BT, unlockHeight, redemptionIntentHash, { from: intercommVC }); + var formattedDecodedEvents = web3EventsDecoder.perform(confirmRedemptionResult.receipt, openSTValue.address, openSTValueArtifacts.abi); + redeemedAmountST = (REDEEM_AMOUNT_BT/conversionRate); + openSTValueUtils.checkRedemptionIntentConfirmedEventOnProtocol(formattedDecodedEvents, registeredBrandedTokenUuid, + redemptionIntentHash, redeemer, redeemedAmountST, REDEEM_AMOUNT_BT); + utils.logResponse(confirmRedemptionResult, "OpenSTUtility.revertUnstake.confirmRedemptionIntent"); + + }); + + // Fake transactions so that redeem is expired and revertRedemption can be called + it('waits till redeem is expired', async () => { + var waitTime = await openSTUtility.blocksToWaitLong.call(); + waitTime = waitTime.toNumber(); + var amountToTransfer = new BigNumber(0.000001).mul(DECIMALSFACTOR); + // Mock transactions so that block number increases + for (var i = 0; i < waitTime; i++) { + await web3.eth.sendTransaction({ from: owner, to: admin, value: amountToTransfer, gasPrice: '0x12A05F200' }); + } + }); + + // Revert Redemption + it("reverts redemption", async() => { + + var revertRedemptionResult = await openSTUtility.revertRedemption(redemptionIntentHash, { from: redeemer }); + openSTUtilityUtils.checkRevertedRedemption(revertRedemptionResult.logs[0], registeredBrandedTokenUuid, redemptionIntentHash, + redeemer, REDEEM_AMOUNT_BT); + + utils.logResponse(revertRedemptionResult, "OpenSTUtility.revertUnstake.revertRedemption"); + + }); + + // Fake transactions so that unstakes is expired and revertUnstakes can be called + it('waits till unstake is expired', async () => { + var waitTime = await openSTValue.blocksToWaitShort.call(); + waitTime = waitTime.toNumber(); + var amountToTransfer = new BigNumber(0.000001).mul(DECIMALSFACTOR); + // Mock transactions so that block number increases + for (var i = 0; i < waitTime; i++) { + await web3.eth.sendTransaction({ from: owner, to: admin, value: amountToTransfer, gasPrice: '0x12A05F200' }); + } + }); + + // Revert unstakes + it("reverts unstake", async() => { + + var revertUnstakingResult = await openSTValue.revertUnstaking(redemptionIntentHash, { from: redeemer }); + openSTValueUtils.checkRevertedUnstake(revertUnstakingResult.logs[0], registeredBrandedTokenUuid, redemptionIntentHash, + redeemer, redeemedAmountST); + utils.logResponse(revertUnstakingResult, "OpenSTUtility.revertUnstake.revertUnstaking"); + + }); + + // Report Gas usages + it("report gas usage: revert unstake", async () => { + + utils.printGasStatistics(); + utils.clearReceipts(); + + }); + + }); + + // SEQUENCE OF EVENTS + // Redeem => confirmRedemptionIntent => ProcessRedemption => revertRedemption => revertUnstaking + // + context('call redeem then confirmRedemptionIntent then ProcessRedemption then revertRedemption then revertUnstaking', function() { + + var previousSTBalance = null; + + it("previous balance on simple stake contract", async() => { + + previousSTBalance = await btSimpleStake.getTotalStake.call(); + + }); + + // Since we reverted redemption in above case we don't need to transfer again as redeemer will already have REDEEM_AMOUNT_BT balance + it("check branded token balance of redeemer", async() => { + + var balanceOfRedeemer = await brandedToken.balanceOf(redeemer); + Assert.equal(balanceOfRedeemer, REDEEM_AMOUNT_BT.toNumber()); + + }); + + // Gives allawance and call redeem + it("gives allowance and calls redeem", async() => { + + var approveResult = await brandedToken.approve(openSTUtility.address, REDEEM_AMOUNT_BT, { from: redeemer }) + Assert.ok(approveResult); + + nonce = await openSTValue.getNextNonce.call(redeemer); + var redeemResult = await openSTUtility.redeem(registeredBrandedTokenUuid, REDEEM_AMOUNT_BT, nonce, { from: redeemer }); + redemptionIntentHash = redeemResult.logs[0].args._redemptionIntentHash; + unlockHeight = redeemResult.logs[0].args._unlockHeight; + openSTUtilityUtils.checkRedemptionIntentDeclaredEvent(redeemResult.logs[0], registeredBrandedTokenUuid, redemptionIntentHash, + brandedToken.address, redeemer, nonce, REDEEM_AMOUNT_BT, unlockHeight, CHAINID_VALUE); + + utils.logResponse(redeemResult, "OpenSTUtility.revertUnstake.redeem"); + + }); + + // Call confirmRedemptionIntent + it("calls confirmRedemptionIntent", async() => { + + var confirmRedemptionResult = await registrarVC.confirmRedemptionIntent( openSTValue.address, registeredBrandedTokenUuid, + redeemer, nonce, REDEEM_AMOUNT_BT, unlockHeight, redemptionIntentHash, { from: intercommVC }); + var formattedDecodedEvents = web3EventsDecoder.perform(confirmRedemptionResult.receipt, openSTValue.address, openSTValueArtifacts.abi); + redeemedAmountST = (REDEEM_AMOUNT_BT/conversionRate); + openSTValueUtils.checkRedemptionIntentConfirmedEventOnProtocol(formattedDecodedEvents, registeredBrandedTokenUuid, + redemptionIntentHash, redeemer, redeemedAmountST, REDEEM_AMOUNT_BT); + utils.logResponse(confirmRedemptionResult, "OpenSTUtility.revertUnstake.confirmRedemptionIntent"); + + }); + + it("process redemption", async() => { + + var processRedeemingResult = await openSTUtility.processRedeeming(redemptionIntentHash, { from: redeemer }); + + openSTUtilityUtils.checkProcessedRedemptionEvent(processRedeemingResult.logs[0], registeredBrandedTokenUuid, redemptionIntentHash, + brandedToken.address, redeemer, REDEEM_AMOUNT_BT) + + utils.logResponse(processRedeemingResult, "openSTUtility.revertUnstake.processRedeeming"); + + }); + + // Fake transactions so that redeem is expired and revertRedemption can be called + it('waits till redeem is expired', async () => { + + var waitTime = await openSTUtility.blocksToWaitLong.call(); + waitTime = waitTime.toNumber(); + var amountToTransfer = new BigNumber(0.000001).mul(DECIMALSFACTOR); + + // Mock transactions so that block number increases + for (var i = 0; i < waitTime; i++) { + await web3.eth.sendTransaction({ from: owner, to: admin, value: amountToTransfer, gasPrice: '0x12A05F200' }); + } + + }); + + // Revert Redemption + it("reverts redemption", async() => { + + await utils.expectThrow(openSTUtility.revertRedemption(redemptionIntentHash, { from: redeemer })); + + }); + + // Fake transactions so that unstakes is expired and revertUnstakes can be called + it('waits till unstake is expired', async () => { + + var waitTime = await openSTValue.blocksToWaitShort.call(); + waitTime = waitTime.toNumber(); + var amountToTransfer = new BigNumber(0.000001).mul(DECIMALSFACTOR); + + // Mock transactions so that block number increases + for (var i = 0; i < waitTime; i++) { + await web3.eth.sendTransaction({ from: owner, to: admin, value: amountToTransfer, gasPrice: '0x12A05F200' }); + } + + }); + + // Revert unstakes + it("reverts unstake", async() => { + + var revertUnstakingResult = await openSTValue.revertUnstaking(redemptionIntentHash, { from: redeemer }); + openSTValueUtils.checkRevertedUnstake(revertUnstakingResult.logs[0], registeredBrandedTokenUuid, redemptionIntentHash, + redeemer, redeemedAmountST); + utils.logResponse(revertUnstakingResult, "OpenSTUtility.revertUnstake.revertUnstaking"); + + }); + + + it("checks that branded token has been burned but SimpleToken has not been releases to redeemer", async() => { + + // Branded token has burned so balance will be 0 + var balanceOfRedeemer = await brandedToken.balanceOf(redeemer); + Assert.equal(balanceOfRedeemer, 0); + + // Simple Token has not been released so previous and current balance is same + var currentStBalance = await btSimpleStake.getTotalStake.call(); + Assert.equal(previousSTBalance.toNumber(), currentStBalance.toNumber()); + + }); + + // Report Gas usages + it("report gas usage: revert unstake", async () => { + + utils.printGasStatistics(); + utils.clearReceipts(); + + }); + + }); + }); }); \ No newline at end of file diff --git a/test/Protocol_utils.js b/test/Protocol_utils.js index 68851596..43f28353 100644 --- a/test/Protocol_utils.js +++ b/test/Protocol_utils.js @@ -28,8 +28,8 @@ const openSTValueUtils = require("./OpenSTValue_utils.js"); var SimpleToken = artifacts.require("./SimpleToken/SimpleToken.sol"); var Registrar = artifacts.require("./Registrar.sol"); var Core = artifacts.require("./Core.sol"); -var OpenSTValue = artifacts.require("./OpenSTValue.sol"); -var OpenSTUtility = artifacts.require("./OpenSTUtility.sol"); +var OpenSTValue = artifacts.require("./OpenSTValueMock.sol"); +var OpenSTUtility = artifacts.require("./OpenSTUtilityMock.sol"); var STPrime = artifacts.require("./STPrime.sol"); const CHAINID_VALUE = 2001;