diff --git a/contracts/AllMarketsV2.sol b/contracts/AllMarketsV2.sol new file mode 100644 index 000000000..3f7358181 --- /dev/null +++ b/contracts/AllMarketsV2.sol @@ -0,0 +1,103 @@ +/* Copyright (C) 2020 PlotX.io + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ */ + +pragma solidity 0.5.7; + +import "./AllMarkets.sol"; + +contract AllMarketsV2 is AllMarkets { + + /** + * @dev Changes the master address and update it's instance + */ + function setMasterAddress() public { + } + + /** + * @dev Start the initial market and set initial variables. + */ + function addInitialMarketTypesAndStart(address _marketCreationRewards,address _ethAddress, address _marketUtility, uint32 _marketStartTime, address _ethFeed, address _btcFeed) external { + } + + /** + * @dev Create the market. + * @param _marketCurrencyIndex The index of market currency feed + * @param _marketTypeIndex The time duration of market. + */ + function createMarket(uint32 _marketCurrencyIndex,uint32 _marketTypeIndex) public payable { + } + + /** + * @dev Create the market and settle the prenultimate market. + * @param _marketCurrencyIndex The index of market currency feed + * @param _marketTypeIndex The time duration of market. + * @param _roundId The chainlink round id of the penultimate market, with the round updated time closest to market settlement time + */ + function createMarketAndSettle(uint32 _marketCurrencyIndex,uint32 _marketTypeIndex, uint80 _roundId) public { + uint256 gasProvided = gasleft(); + require(!marketCreationPaused && !marketTypeArray[_marketTypeIndex].paused); + _closePreviousMarketWithRoundId( _marketTypeIndex, _marketCurrencyIndex, _roundId); + marketUtility.update(); + uint32 _startTime = calculateStartTimeForMarket(_marketCurrencyIndex, _marketTypeIndex); + (uint64 _minValue, uint64 _maxValue) = marketUtility.calculateOptionRange(marketTypeArray[_marketTypeIndex].optionRangePerc, marketCurrencies[_marketCurrencyIndex].decimals, marketCurrencies[_marketCurrencyIndex].roundOfToNearest, marketCurrencies[_marketCurrencyIndex].marketFeed); + uint64 _marketIndex = uint64(marketBasicData.length); + marketBasicData.push(MarketBasicData(_marketTypeIndex,_marketCurrencyIndex,_startTime, marketTypeArray[_marketTypeIndex].predictionTime,_minValue,_maxValue)); + marketDataExtended[_marketIndex].ethCommission = commissionPercGlobal.ethCommission; + marketDataExtended[_marketIndex].plotCommission = commissionPercGlobal.plotCommission; + (marketCreationData[_marketTypeIndex][_marketCurrencyIndex].penultimateMarket, marketCreationData[_marketTypeIndex][_marketCurrencyIndex].latestMarket) = + (marketCreationData[_marketTypeIndex][_marketCurrencyIndex].latestMarket, _marketIndex); + emit MarketQuestion(_marketIndex, marketCurrencies[_marketCurrencyIndex].currencyName, _marketTypeIndex, _startTime, marketTypeArray[_marketTypeIndex].predictionTime, _minValue, _maxValue); + marketCreationRewards.calculateMarketCreationIncentive(msg.sender, gasProvided - gasleft(), _marketIndex); + } + + /** + * @dev Internal function to settle the previous market + */ + function _closePreviousMarket(uint64 _marketTypeIndex, uint64 _marketCurrencyIndex) internal { + } + + /** + * @dev Internal function to settle the previous market + */ + function _closePreviousMarketWithRoundId(uint64 _marketTypeIndex, uint64 _marketCurrencyIndex, uint80 _roundId) internal { + uint64 currentMarket = marketCreationData[_marketTypeIndex][_marketCurrencyIndex].latestMarket; + if(currentMarket != 0) { + require(marketStatus(currentMarket) >= PredictionStatus.InSettlement); + uint64 penultimateMarket = marketCreationData[_marketTypeIndex][_marketCurrencyIndex].penultimateMarket; + if(penultimateMarket > 0 && now >= marketSettleTime(penultimateMarket)) { + settleMarketByRoundId(penultimateMarket, _roundId); + } + } + } + + /** + * @dev Settle the market, setting the winning option + */ + function settleMarket(uint256 _marketId) public { + } + + /** + * @dev Settle the market, setting the winning option + * @param _marketId Index of market. + * @param _roundId Index of the nearest price feed round from which the price to be taken from. + */ + function settleMarketByRoundId(uint256 _marketId, uint80 _roundId) public { + if(marketStatus(_marketId) == PredictionStatus.InSettlement) { + require(_roundId > 0); + (uint256 _value, uint256 _roundIdUsed) = marketUtility.getSettlemetPriceByRoundId(marketCurrencies[marketBasicData[_marketId].currency].marketFeed, marketSettleTime(_marketId), _roundId); + _postResult(_value, _roundIdUsed, _marketId); + } + } +} \ No newline at end of file diff --git a/contracts/MarketUtilityV2_1.sol b/contracts/MarketUtilityV2_1.sol new file mode 100644 index 000000000..94d9ed031 --- /dev/null +++ b/contracts/MarketUtilityV2_1.sol @@ -0,0 +1,51 @@ +/* Copyright (C) 2020 PlotX.io + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ */ + +pragma solidity 0.5.7; + +import "./MarketUtilityV2.sol"; + +contract MarketUtilityV2_1 is MarketUtilityV2 { + /** + * @dev Get price of provided feed address + * @param _currencyFeedAddress Feed Address of currency on which market options are based on + * @return Current price of the market currency + **/ + function getSettlemetPriceByRoundId( + address _currencyFeedAddress, + uint256 _settleTime, + uint80 _roundId + ) public view returns (uint256 latestAnswer, uint256 roundId) { + uint80 roundIdToCheck; + uint256 currentRoundTime; + int256 currentRoundAnswer; + (roundIdToCheck, currentRoundAnswer, , currentRoundTime, )= IChainLinkOracle(_currencyFeedAddress).latestRoundData(); + if(roundIdToCheck == _roundId) { + if(currentRoundTime <= _settleTime) { + return (uint256(currentRoundAnswer), roundIdToCheck); + } + } else { + (roundIdToCheck, currentRoundAnswer, , currentRoundTime, )= IChainLinkOracle(_currencyFeedAddress).getRoundData(_roundId + 1); + require(currentRoundTime > _settleTime); + roundIdToCheck = _roundId + 1; + } + while(currentRoundTime > _settleTime) { + roundIdToCheck--; + (roundIdToCheck, currentRoundAnswer, , currentRoundTime, )= IChainLinkOracle(_currencyFeedAddress).getRoundData(roundIdToCheck); + } + return + (uint256(currentRoundAnswer), roundIdToCheck); + } +} \ No newline at end of file diff --git a/contracts/bLOTTokenV2.sol b/contracts/bLOTTokenV2.sol new file mode 100644 index 000000000..066ad2417 --- /dev/null +++ b/contracts/bLOTTokenV2.sol @@ -0,0 +1,286 @@ +/* Copyright (C) 2020 PlotX.io + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ */ + +pragma solidity 0.5.7; + +import "./external/openzeppelin-solidity/token/ERC20/ERC20.sol"; +import "./external/openzeppelin-solidity/access/Roles.sol"; +import "./external/proxy/OwnedUpgradeabilityProxy.sol"; +import "./interfaces/IMaster.sol"; +import "./interfaces/Iupgradable.sol"; + +contract BLOTV2 is Iupgradable { + using SafeMath for uint256; + using Roles for Roles.Role; + + string public constant name = "PlotXBonusToken"; + string public constant symbol = "bPLOT"; + uint8 public constant decimals = 18; + + Roles.Role private _minters; + + address public operator; + address public plotToken; + address public constant authorized = 0x6f9f333de6eCFa67365916cF95873a4DC480217a; + + event MinterAdded(address indexed account); + event MinterRemoved(address indexed account); + + mapping (address => uint256) internal _balances; + + bool private initiated; + uint256 private _totalSupply; + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Migrate(address indexed from, address indexed to, uint256 value); + + /** + * @dev Checks if msg.sender is token operator address. + */ + modifier onlyOperator() { + require(msg.sender == operator, "Only operator"); + _; + } + + modifier onlyMinter() { + require( + isMinter(msg.sender), + "MinterRole: caller does not have the Minter role" + ); + _; + } + + /** + * @dev Initiates the BLOT with default minter address + */ + function initiatebLOT(address _defaultMinter) public { + require(!initiated); + initiated = true; + _addMinter(_defaultMinter); + } + + /** + * @dev Changes the master address and update it's instance + */ + function setMasterAddress() public { + OwnedUpgradeabilityProxy proxy = OwnedUpgradeabilityProxy( + address(uint160(address(this))) + ); + require(msg.sender == proxy.proxyOwner(), "Sender is not proxy owner."); + require(plotToken == address(0), "Already Initialized"); + IMaster ms = IMaster(msg.sender); + plotToken = ms.dAppToken(); + operator = ms.getLatestAddress("TC"); + } + + + /** + * @dev See `IERC20.transfer`. + * + * Requirements: + * + * - `recipient` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + * Transfer is restricted to minter only + */ + function transfer(address recipient, uint256 amount) + public + onlyMinter + returns (bool) + { + _transfer(msg.sender, recipient, amount); + return true; + } + + /** + * @dev Moves tokens `amount` from `sender` to `recipient`. + * + * This is internal function is equivalent to `transfer`, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a `Transfer` event. + * + * Requirements: + * + * - `sender` cannot be the zero address. + * - `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + */ + function _transfer( + address sender, + address recipient, + uint256 amount + ) internal { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _balances[sender] = _balances[sender].sub(amount); + _balances[recipient] = _balances[recipient].add(amount); + emit Transfer(sender, recipient, amount); + } + + /** + * @dev See `ERC20._mint`. + * + * Requirements: + * + * - the caller must have the `MinterRole`. + * - equivalant number of PLOT will be transferred from sender to this contract + */ + function mint(address account, uint256 amount) + public + onlyMinter + returns (bool) + { + require( + IERC20(plotToken).transferFrom(msg.sender, address(this), amount), + "Error in transfer" + ); + _mint(account, amount); + return true; + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a `Transfer` event with `from` set to the zero address. + * + * Requirements + * + * - `to` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal { + require(account != address(0), "ERC20: mint to the zero address"); + + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from the caller. + * + * See `ERC20._burn`. + */ + function convertToPLOT( + address _of, + address _to, + uint256 amount + ) public onlyOperator { + _burn(_of, amount); + require(IERC20(plotToken).transfer(_to, amount), "Error in transfer"); + } + + /** + * @dev Destroys all tokens from the caller. + * + * See `ERC20._burn`. + */ + function migrate( + address _to + ) public { + require(balanceOf(msg.sender) > 0, "User must have bPlots"); + require(_to != address(0),"To should not be an zero address"); + require(IERC20(plotToken).transfer(authorized, balanceOf(msg.sender)), "Error in transfer"); + + emit Migrate(msg.sender,_to,balanceOf(msg.sender)); + _burn(msg.sender,balanceOf(msg.sender)); + } + + + + /** + * @dev Destoys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a `Transfer` event with `to` set to the zero address. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 value) internal { + require(account != address(0), "ERC20: burn from the zero address"); + + _totalSupply = _totalSupply.sub(value); + _balances[account] = _balances[account].sub(value); + emit Transfer(account, address(0), value); + } + + + + /** + * @dev Check if `account` has minting rights + */ + function isMinter(address account) public view returns (bool) { + return _minters.has(account); + } + + /** + * @dev Add `account` as minter + */ + function addMinter(address account) public onlyMinter { + _addMinter(account); + } + + /** + * @dev Renounce self as minter + */ + function renounceMinter() public { + _removeMinter(msg.sender); + } + + /** + * @dev Add `account` as minter + */ + function _addMinter(address account) internal { + _minters.add(account); + emit MinterAdded(account); + } + + /** + * @dev Remove `account` from minter role + */ + function _removeMinter(address account) internal { + _minters.remove(account); + emit MinterRemoved(account); + } + + /** + * @dev See `IERC20.totalSupply`. + */ + function totalSupply() public view returns (uint256) { + return _totalSupply; + } + + /** + * @dev See `IERC20.balanceOf`. + */ + function balanceOf(address account) public view returns (uint256) { + return _balances[account]; + } + +} \ No newline at end of file diff --git a/contracts/interfaces/IMarketUtility.sol b/contracts/interfaces/IMarketUtility.sol index bb7bf8978..05d974a02 100644 --- a/contracts/interfaces/IMarketUtility.sol +++ b/contracts/interfaces/IMarketUtility.sol @@ -82,4 +82,10 @@ contract IMarketUtility { address _currencyFeedAddress, uint256 _settleTime ) public view returns (uint256 latestAnswer, uint256 roundId); + + function getSettlemetPriceByRoundId( + address _currencyFeedAddress, + uint256 _settleTime, + uint80 _roundId + ) public view returns (uint256 latestAnswer, uint256 roundId); } diff --git a/contracts/mock/MockConfig.sol b/contracts/mock/MockConfig.sol index 51c4c0071..8cdebeebe 100644 --- a/contracts/mock/MockConfig.sol +++ b/contracts/mock/MockConfig.sol @@ -1,8 +1,8 @@ pragma solidity 0.5.7; -import "../MarketUtilityV2.sol"; +import "../MarketUtilityV2_1.sol"; -contract MockConfig is MarketUtilityV2 { +contract MockConfig is MarketUtilityV2_1 { uint public priceOfToken; bool public mockFlag; diff --git a/test/26_settleMarketV2.test.js b/test/26_settleMarketV2.test.js new file mode 100644 index 000000000..299eb5c71 --- /dev/null +++ b/test/26_settleMarketV2.test.js @@ -0,0 +1,370 @@ +const { assert } = require("chai"); +const OwnedUpgradeabilityProxy = artifacts.require("OwnedUpgradeabilityProxy"); +const Market = artifacts.require("MockMarket"); +const Plotus = artifacts.require("MarketRegistry"); +const Master = artifacts.require("Master"); +const MemberRoles = artifacts.require("MemberRoles"); +const PlotusToken = artifacts.require("MockPLOT"); +const MockWeth = artifacts.require("MockWeth"); +const MockConfig = artifacts.require("MockConfig"); //mock +const Governance = artifacts.require("GovernanceV2"); +const AllMarkets = artifacts.require("MockAllMarkets"); +const AllMarketsV2 = artifacts.require("AllMarketsV2"); +const MockchainLink = artifacts.require('MockChainLinkAggregator'); +const MockUniswapRouter = artifacts.require("MockUniswapRouter"); +const MockUniswapV2Pair = artifacts.require("MockUniswapV2Pair"); +const MockUniswapFactory = artifacts.require("MockUniswapFactory"); +const TokenController = artifacts.require("MockTokenController"); +const DummyTokenMock2 = artifacts.require("SampleERC"); + +const web3 = Market.web3; +const ethAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; +const increaseTime = require("./utils/increaseTime.js").increaseTime; +const assertRevert = require("./utils/assertRevert").assertRevert; +const latestTime = require("./utils/latestTime").latestTime; +const encode = require("./utils/encoder.js").encode; +const encode1 = require("./utils/encoder.js").encode1; +const gvProposal = require("./utils/gvProposal.js").gvProposalWithIncentiveViaTokenHolder; +const { toHex, toWei, toChecksumAddress } = require("./utils/ethTools"); +const to8Power = (number) => String(parseFloat(number) * 1e8); + +// Multiplier Sheet + +contract("26_SettleMarketV2. AllMarket", async function([ + ab1, + ab2, + ab3, + ab4, + mem1, + mem2, + mem3, + mem4, + mem5, + mem6, + mem7, + mem8, + mem9, + mem10, + notMember, + dr1, + dr2, + dr3, + user11, + user12, + user13, +]) { + let masterInstance, + plotusToken, + mockMarketConfig, + MockUniswapRouterInstance, + tokenControllerAdd, + tokenController, + plotusNewAddress, + plotusNewInstance, + governance, + mockUniswapV2Pair, + mockUniswapFactory, + weth, + allMarkets, chainlinkAggregator; + before(async () => { + masterInstance = await OwnedUpgradeabilityProxy.deployed(); + masterInstance = await Master.at(masterInstance.address); + plotusToken = await PlotusToken.deployed(); + chainlinkAggregator = await MockchainLink.deployed(); + tokenControllerAdd = await masterInstance.getLatestAddress(web3.utils.toHex("TC")); + tokenController = await TokenController.at(tokenControllerAdd); + plotusNewAddress = await masterInstance.getLatestAddress(web3.utils.toHex("PL")); + let memberRoles = await masterInstance.getLatestAddress(web3.utils.toHex("MR")); + memberRoles = await MemberRoles.at(memberRoles); + governance = await masterInstance.getLatestAddress(web3.utils.toHex("GV")); + governance = await Governance.at(governance); + MockUniswapRouterInstance = await MockUniswapRouter.deployed(); + mockUniswapFactory = await MockUniswapFactory.deployed(); + plotusNewInstance = await Plotus.at(plotusNewAddress); + mockMarketConfig = await plotusNewInstance.marketUtility(); + mockMarketConfig = await MockConfig.at(mockMarketConfig); + weth = await MockWeth.deployed(); + await mockMarketConfig.setWeth(weth.address); + let newUtility = await MockConfig.new(); + let actionHash = encode( + "upgradeContractImplementation(address,address)", + mockMarketConfig.address, + newUtility.address + ); + await gvProposal( + 6, + actionHash, + await MemberRoles.at(await masterInstance.getLatestAddress(toHex("MR"))), + governance, + 2, + 0 + ); + await increaseTime(604800); + mockUniswapV2Pair = await MockUniswapV2Pair.new(); + await mockUniswapV2Pair.initialize(plotusToken.address, weth.address); + await weth.deposit({ from: user11, value: toWei(10) }); + await weth.transfer(mockUniswapV2Pair.address, toWei(10), { from: user11 }); + await plotusToken.transfer(mockUniswapV2Pair.address, toWei(1000)); + initialPLOTPrice = 1000 / 10; + initialEthPrice = 10 / 1000; + await mockUniswapFactory.setPair(mockUniswapV2Pair.address); + await mockUniswapV2Pair.sync(); + newUtility = await MockConfig.new(); + actionHash = encode( + "upgradeContractImplementation(address,address)", + mockMarketConfig.address, + newUtility.address + ); + await gvProposal( + 6, + actionHash, + await MemberRoles.at(await masterInstance.getLatestAddress(toHex("MR"))), + governance, + 2, + 0 + ); + await increaseTime(604800); + allMarkets = await AllMarkets.at(await masterInstance.getLatestAddress(web3.utils.toHex("AM"))); + let date = await latestTime(); + await increaseTime(3610); + date = Math.round(date); + await mockMarketConfig.setInitialCummulativePrice(); + await mockMarketConfig.setAuthorizedAddress(allMarkets.address); + let utility = await MockConfig.at("0xCBc7df3b8C870C5CDE675AaF5Fd823E4209546D2"); + await utility.setAuthorizedAddress(allMarkets.address); + await mockUniswapV2Pair.sync(); + }); + it("Create markets in AllMarketsV1", async ()=> { + await increaseTime(604800); + await allMarkets.createMarket(0,0); + await allMarkets.createMarket(0,1); + await allMarkets.createMarket(0,2); + await allMarkets.createMarket(1,0); + await allMarkets.createMarket(1,1); + await allMarkets.createMarket(1,2); + await increaseTime(2*604800); + + }); + + it("Should upgrade AllMarkets implementation to V2", async()=> { + let allMarketsV2Implementation = await AllMarketsV2.new(); + let actionHash = encode1( + ['bytes2[]', 'address[]'], + [ + [toHex("AM")], + [allMarketsV2Implementation.address] + ] + ); + await gvProposal( + 7, + actionHash, + await MemberRoles.at(await masterInstance.getLatestAddress(toHex("MR"))), + governance, + 2, + 0 + ); + await increaseTime(604800); + allMarkets = await AllMarketsV2.at(allMarkets.address); + }); + + it("Should be able to settle markets created in V2 AllMarkets", async() => { + let tx; + let currentRoundId; + let requiredRoundId = await chainlinkAggregator.currentRound(); + await chainlinkAggregator.setLatestAnswer(500000000); + currentRoundId = await chainlinkAggregator.currentRound(); + for(let i = 1;i<=12;i++) { + tx = await allMarkets.settleMarketByRoundId(i, currentRoundId/1); + } + assert.equal(tx.logs[0].args.closeValue/1, ((await chainlinkAggregator.getRoundData(requiredRoundId))[1])/1); + }) + it("Scenario 1: Latest Round Id, Required RoundId, Sent RoundId are same", async () => { + let tx = await allMarkets.createMarketAndSettle(0,0,0); + let id = tx.logs[0].args.marketIndex/1; + let settleTime = (await allMarkets.getMarketData(id))._expireTime/1 - (await latestTime())*1; + settleTime = settleTime + 14400; + await increaseTime(settleTime - 500); + let closingPrice = 1000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + let requiredRoundId = await chainlinkAggregator.currentRound(); + await increaseTime(600); + tx = await allMarkets.settleMarketByRoundId(tx.logs[0].args.marketIndex/1, requiredRoundId/1); + assert.equal(tx.logs[0].args.closeValue/1, ((await chainlinkAggregator.getRoundData(requiredRoundId))[1])/1); + }); + + it("Scenario 2: Required RoundId, Sent RoundId are same and 1 round behind the latest round", async () => { + let tx = await allMarkets.createMarketAndSettle(0,0,0); + let id = tx.logs[0].args.marketIndex/1; + let settleTime = (await allMarkets.getMarketData(id))._expireTime/1 - (await latestTime())*1; + settleTime = settleTime + 14400; + await increaseTime(settleTime - 500); + let closingPrice = 2000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + let requiredRoundId = await chainlinkAggregator.currentRound(); + await increaseTime(3600); + closingPrice = 3000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + tx = await allMarkets.settleMarketByRoundId(tx.logs[0].args.marketIndex/1, requiredRoundId/1); + assert.equal(tx.logs[0].args.closeValue/1, ((await chainlinkAggregator.getRoundData(requiredRoundId))[1])/1); + }); + + it("Scenario 3: Required RoundId < Sent RoundId < latest round", async () => { + let tx = await allMarkets.createMarketAndSettle(0,0,0); + + let id = tx.logs[0].args.marketIndex/1; + let settleTime = (await allMarkets.getMarketData(id))._expireTime/1 - (await latestTime())*1; + settleTime = settleTime + 14400; + await increaseTime(settleTime - 500); + let closingPrice = 2000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + let requiredRoundId = await chainlinkAggregator.currentRound(); + await increaseTime(3600); + closingPrice = 3000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + closingPrice = 4000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + tx = await allMarkets.settleMarketByRoundId(tx.logs[0].args.marketIndex/1, requiredRoundId); + assert.equal(tx.logs[0].args.closeValue/1, ((await chainlinkAggregator.getRoundData(requiredRoundId))[1])/1); + }); + + it("Scenario 4: Required RoundId is much older than Sent RoundId, which is less than latest round", async () => { + let tx = await allMarkets.createMarketAndSettle(0,0,0); + let id = tx.logs[0].args.marketIndex/1; + let settleTime = (await allMarkets.getMarketData(id))._expireTime/1 - (await latestTime())*1; + settleTime = settleTime + 14400; + await increaseTime(settleTime - 500); + let closingPrice = 2000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + let requiredRoundId = await chainlinkAggregator.currentRound(); + await increaseTime(3600); + closingPrice = 3000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + closingPrice = 4000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + tx = await allMarkets.settleMarketByRoundId(tx.logs[0].args.marketIndex/1, requiredRoundId); + assert.equal(tx.logs[0].args.closeValue/1, ((await chainlinkAggregator.getRoundData(requiredRoundId))[1])/1); + }); + + it("Scenario 5: Sent RoundId = latest round, required round id is much older", async () => { + let tx = await allMarkets.createMarketAndSettle(0,0,0); + let id = tx.logs[0].args.marketIndex/1; + let settleTime = (await allMarkets.getMarketData(id))._expireTime/1 - (await latestTime())*1; + settleTime = settleTime + 14400; + await increaseTime(settleTime - 500); + let closingPrice = 2000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + let requiredRoundId = await chainlinkAggregator.currentRound(); + await increaseTime(3600); + for(let i = 0;i<5;i++) { + closingPrice += 1000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + } + let currentRoundId = await chainlinkAggregator.currentRound(); + tx = await allMarkets.settleMarketByRoundId(tx.logs[0].args.marketIndex/1, currentRoundId); + assert.equal(tx.logs[0].args.closeValue/1, ((await chainlinkAggregator.getRoundData(requiredRoundId))[1])/1); + }); + + it("Scenario 6: Sent RoundId is less than latest round, required round id is much older", async () => { + let tx = await allMarkets.createMarketAndSettle(0,0,0); + let id = tx.logs[0].args.marketIndex/1; + let settleTime = (await allMarkets.getMarketData(id))._expireTime/1 - (await latestTime())*1; + settleTime = settleTime + 14400; + await increaseTime(settleTime - 500); + let closingPrice = 2000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + let requiredRoundId = await chainlinkAggregator.currentRound(); + await increaseTime(3600); + for(let i = 0;i<10;i++) { + closingPrice += 1000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + } + let currentRoundId = await chainlinkAggregator.currentRound(); + tx = await allMarkets.settleMarketByRoundId(tx.logs[0].args.marketIndex/1, currentRoundId/1-3); + assert.equal(tx.logs[0].args.closeValue/1, ((await chainlinkAggregator.getRoundData(requiredRoundId))[1])/1); + }); + + it("Scenario 7: Sent RoundId is less than the required round, both are less than latest round", async () => { + let tx = await allMarkets.createMarketAndSettle(0,0,0); + let id = tx.logs[0].args.marketIndex/1; + let settleTime = (await allMarkets.getMarketData(id))._expireTime/1 - (await latestTime())*1; + settleTime = settleTime + 14400; + await increaseTime(settleTime - 500); + let closingPrice = 2000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + let requiredRoundId = await chainlinkAggregator.currentRound(); + await increaseTime(3600); + for(let i = 0;i<10;i++) { + closingPrice += 1000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + } + tx = await assertRevert(allMarkets.settleMarketByRoundId(tx.logs[0].args.marketIndex/1, requiredRoundId/1-1)); + // assert.equal(tx.logs[0].args.closeValue/1, ((await chainlinkAggregator.getRoundData(requiredRoundId))[1])/1); + }); + + it("Scenario 8: Sent RoundId and required round are same, both are less than latest round", async () => { + let tx = await allMarkets.createMarketAndSettle(0,0,0); + let id = tx.logs[0].args.marketIndex/1; + let settleTime = (await allMarkets.getMarketData(id))._expireTime/1 - (await latestTime())*1; + settleTime = settleTime + 14400; + await increaseTime(settleTime - 500); + let closingPrice = 2000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + let requiredRoundId = await chainlinkAggregator.currentRound(); + await increaseTime(3600); + for(let i = 0;i<10;i++) { + closingPrice += 1000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + } + let currentRoundId = await chainlinkAggregator.currentRound(); + let tx2 = await allMarkets.createMarketAndSettle(0,0,currentRoundId/1); + await increaseTime(2*14400); + closingPrice += 1000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + //Will settle the required market + tx = await allMarkets.createMarketAndSettle(0,0,requiredRoundId); + // tx = await allMarkets.settleMarketByRoundId(tx.logs[0].args.marketIndex/1, requiredRoundId); + assert.equal(tx.logs[0].args.closeValue/1, ((await chainlinkAggregator.getRoundData(requiredRoundId))[1])/1); + await increaseTime(14400*2) + currentRoundId = await chainlinkAggregator.currentRound(); + await allMarkets.settleMarketByRoundId(tx.logs[1].args.marketIndex/1, currentRoundId/1); + await allMarkets.settleMarketByRoundId(tx2.logs[1].args.marketIndex/1, currentRoundId/1); + }); + + it("Scenario 9: Required RoundId = latest round, Sent round id is lesser", async () => { + let tx = await allMarkets.createMarketAndSettle(0,0,0); + let id = tx.logs[0].args.marketIndex/1; + let settleTime = (await allMarkets.getMarketData(id))._expireTime/1 - (await latestTime())*1; + settleTime = settleTime + 14400; + await increaseTime(settleTime - 500); + let closingPrice = 2000000000; + let requiredRoundId = await chainlinkAggregator.currentRound(); + await chainlinkAggregator.setLatestAnswer(closingPrice); + closingPrice += 1000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + let currentRoundId = await chainlinkAggregator.currentRound(); + await increaseTime(3600); + tx = await assertRevert(allMarkets.settleMarketByRoundId(tx.logs[0].args.marketIndex/1, requiredRoundId)); + // assert.equal(tx.logs[0].args.closeValue/1, ((await chainlinkAggregator.getRoundData(currentRoundId))[1])/1); + }); + + it("Scenario 10: Required RoundId = latest round, Sent round id is lesser", async () => { + let tx = await allMarkets.createMarketAndSettle(0,0,0); + let id = tx.logs[0].args.marketIndex/1; + let settleTime = (await allMarkets.getMarketData(id))._expireTime/1 - (await latestTime())*1; + settleTime = settleTime + 14400; + await increaseTime(settleTime - 500); + let closingPrice = 2000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + let requiredRoundId = await chainlinkAggregator.currentRound(); + for(let i = 0;i<10;i++) { + closingPrice += 1000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + } + closingPrice += 1000000000; + await chainlinkAggregator.setLatestAnswer(closingPrice); + let currentRoundId = await chainlinkAggregator.currentRound(); + await increaseTime(3600); + tx = await assertRevert(allMarkets.settleMarketByRoundId(tx.logs[0].args.marketIndex/1, requiredRoundId)); + // assert.equal(tx.logs[0].args.closeValue/1, ((await chainlinkAggregator.getRoundData(currentRoundId))[1])/1); + }); +}); \ No newline at end of file