diff --git a/contracts/event/EventFactory.sol b/contracts/event/EventFactory.sol index 14b9d0f..e057a0a 100644 --- a/contracts/event/EventFactory.sol +++ b/contracts/event/EventFactory.sol @@ -16,7 +16,7 @@ contract EventFactory is NRC223Receiver { uint amount; } - uint16 private constant VERSION = 5; + uint16 private constant VERSION = 6; address private _configManager; address private _bodhiTokenAddress; @@ -92,14 +92,13 @@ contract EventFactory is NRC223Receiver { returns (MultipleResultsEvent) { (string memory eventName, bytes32[3] memory eventResults, - uint betStartTime, uint betEndTime, uint resultSetStartTime, - uint resultSetEndTime, address centralizedOracle, + uint betEndTime, uint resultSetStartTime, address centralizedOracle, uint8 arbitrationOptionIndex, uint arbitrationRewardPercentage) = - abi.decode(params, (string, bytes32[3], uint, uint, uint, uint, - address, uint8, uint)); + abi.decode(params, (string, bytes32[3], uint, uint, address, uint8, + uint)); return createMultipleResultsEvent(from, value, eventName, eventResults, - betStartTime, betEndTime, resultSetStartTime, resultSetEndTime, - centralizedOracle, arbitrationOptionIndex, arbitrationRewardPercentage); + betEndTime, resultSetStartTime, centralizedOracle, + arbitrationOptionIndex, arbitrationRewardPercentage); } /// @dev Creates a new MultipleResultsEvent. Only tokenFallback can call this. @@ -107,10 +106,8 @@ contract EventFactory is NRC223Receiver { /// @param escrowDeposited Amount of escrow deposited to create the event. /// @param eventName Question or statement prediction. /// @param eventResults Possible results. - /// @param betStartTime Unix time when betting will start. /// @param betEndTime Unix time when betting will end. /// @param resultSetStartTime Unix time when the CentralizedOracle can set the result. - /// @param resultSetEndTime Unix time when anyone can set the result. /// @param centralizedOracle Address of the user that will decide the result. /// @param arbitrationOptionIndex Index of the selected arbitration option. /// @param arbitrationRewardPercentage Percentage of loser's bets going to winning arbitrators. @@ -120,10 +117,8 @@ contract EventFactory is NRC223Receiver { uint escrowDeposited, string memory eventName, bytes32[3] memory eventResults, - uint betStartTime, uint betEndTime, uint resultSetStartTime, - uint resultSetEndTime, address centralizedOracle, uint8 arbitrationOptionIndex, uint arbitrationRewardPercentage) @@ -152,9 +147,9 @@ contract EventFactory is NRC223Receiver { // Create event MultipleResultsEvent mrEvent = new MultipleResultsEvent( - creator, eventName, results, numOfResults, betStartTime, - betEndTime, resultSetStartTime, resultSetEndTime, centralizedOracle, - arbitrationOptionIndex, arbitrationRewardPercentage, _configManager); + creator, eventName, results, numOfResults, betEndTime, + resultSetStartTime, centralizedOracle, arbitrationOptionIndex, + arbitrationRewardPercentage, _configManager); // Store escrow info _escrows[address(mrEvent)].depositer = creator; diff --git a/contracts/event/MultipleResultsEvent.sol b/contracts/event/MultipleResultsEvent.sol index 8eaec4a..42f84c1 100644 --- a/contracts/event/MultipleResultsEvent.sol +++ b/contracts/event/MultipleResultsEvent.sol @@ -22,8 +22,9 @@ contract MultipleResultsEvent is NRC223Receiver, Ownable { uint arbitrationEndTime; } - uint16 private constant VERSION = 5; + uint16 private constant VERSION = 6; uint8 private constant INVALID_RESULT_INDEX = 255; + uint256 private constant ORACLE_RESULT_SETTING_LENGTH = 48 * 60 * 60; // 48 hours uint8 private _numOfResults; uint8 private _currentRound = 0; @@ -101,10 +102,8 @@ contract MultipleResultsEvent is NRC223Receiver, Ownable { /// @param eventName Question or statement prediction. /// @param eventResults Possible results. /// @param numOfResults Number of results. - /// @param betStartTime Unix time when betting will start. /// @param betEndTime Unix time when betting will end. /// @param resultSetStartTime Unix time when the CentralizedOracle can set the result. - /// @param resultSetEndTime Unix time when anyone can set the result. /// @param centralizedOracle Address of the user that will decide the result. /// @param arbitrationOptionIndex Index of the selected arbitration option. /// @param arbitrationRewardPercentage Percentage of loser's bets going to winning arbitrators. @@ -114,10 +113,8 @@ contract MultipleResultsEvent is NRC223Receiver, Ownable { string memory eventName, bytes32[4] memory eventResults, uint8 numOfResults, - uint betStartTime, uint betEndTime, uint resultSetStartTime, - uint resultSetEndTime, address centralizedOracle, uint8 arbitrationOptionIndex, uint arbitrationRewardPercentage, @@ -131,13 +128,10 @@ contract MultipleResultsEvent is NRC223Receiver, Ownable { require(eventNameBytes.length > 0, "Event name cannot be empty"); require(!eventResults[1].isEmpty(), "First event result cannot be empty"); require(!eventResults[2].isEmpty(), "Second event result cannot be empty"); - require(betEndTime > betStartTime, "betEndTime should be > betStartTime"); + require(betEndTime > block.timestamp, "betEndTime should be > current time"); require( resultSetStartTime >= betEndTime, "resultSetStartTime should be >= betEndTime"); - require( - resultSetEndTime > resultSetStartTime, - "resultSetEndTime should be > resultSetStartTime"); require( arbitrationOptionIndex < 4, "arbitrationOptionIndex should be < 4"); @@ -148,10 +142,10 @@ contract MultipleResultsEvent is NRC223Receiver, Ownable { _eventName = eventName; _eventResults = eventResults; _numOfResults = numOfResults; - _betStartTime = betStartTime; + _betStartTime = block.timestamp; _betEndTime = betEndTime; _resultSetStartTime = resultSetStartTime; - _resultSetEndTime = resultSetEndTime; + _resultSetEndTime = resultSetStartTime.add(ORACLE_RESULT_SETTING_LENGTH); _centralizedOracle = centralizedOracle; _arbitrationRewardPercentage = arbitrationRewardPercentage; @@ -490,6 +484,7 @@ contract MultipleResultsEvent is NRC223Receiver, Ownable { private { // Calculate next consensus threshold + uint currThreshold = _eventRounds[_currentRound].consensusThreshold; uint nextThreshold = getNextThreshold(_eventRounds[_currentRound].consensusThreshold); uint8 previousRound = _currentRound; @@ -517,7 +512,7 @@ contract MultipleResultsEvent is NRC223Receiver, Ownable { } // Emit events - emit VoteResultSet(address(this), from, resultIndex, value, + emit VoteResultSet(address(this), from, resultIndex, currThreshold, previousRound, nextThreshold, arbitrationEndTime); } diff --git a/test/event/multiple-results-event.js b/test/event/multiple-results-event.js index 71596e1..064555f 100644 --- a/test/event/multiple-results-event.js +++ b/test/event/multiple-results-event.js @@ -5,9 +5,7 @@ const { isNumber } = require('lodash') const getConstants = require('../constants') const { toSatoshi, - bigNumberFloor, currentBlockTime, - paddedHexToAddress, constructTransfer223Data, decodeEvent, } = require('../util') @@ -25,7 +23,7 @@ const SET_RESULT_FUNC_SIG = 'a6b4218b' const VOTE_FUNC_SIG = '1e00eb7f' const RESULT_INVALID = 'Invalid' const RESULT_INDEX_INVALID = 255 -const TOKEN_DECIMALS = 8 +const ORACLE_RESULT_SETTING_LENGTH = 172800 const fundUsers = async ({ nbotMethods, accounts }) => { await nbotMethods.transfer(accounts[1], toSatoshi(10000).toString()) @@ -40,8 +38,7 @@ const fundUsers = async ({ nbotMethods, accounts }) => { .send({ from: accounts[0] }) } -const getEventParams = async (cOracle) => { - const currTime = await currentBlockTime() +const getEventParams = async (cOracle, currTime) => { return [ 'Test Event 1', [ @@ -49,10 +46,8 @@ const getEventParams = async (cOracle) => { web3.utils.fromAscii('B'), web3.utils.fromAscii('C'), ], - currTime + 1000, currTime + 3000, currTime + 4000, - currTime + 6000, cOracle, 0, 10, @@ -66,8 +61,7 @@ const createEvent = async ( // Construct data const data = constructTransfer223Data( CREATE_EVENT_FUNC_SIG, - ['string', 'bytes32[3]', 'uint256', 'uint256', 'uint256', 'uint256', - 'address', 'uint8', 'uint256'], + ['string', 'bytes32[3]', 'uint256', 'uint256', 'address', 'uint8', 'uint256'], eventParams, ) @@ -230,11 +224,11 @@ contract('MultipleResultsEvent', (accounts) => { configManagerMethods.setEventFactory(eventFactoryAddr).send({ from: OWNER }) // Setup event params - eventParams = await getEventParams(OWNER) - betStartTime = eventParams[2] - betEndTime = eventParams[3] - resultSetStartTime = eventParams[4] - resultSetEndTime = eventParams[5] + betStartTime = await currentBlockTime() + eventParams = await getEventParams(OWNER, betStartTime) + betEndTime = eventParams[2] + resultSetStartTime = eventParams[3] + resultSetEndTime = resultSetStartTime + ORACLE_RESULT_SETTING_LENGTH // NBOT.transfer() -> create event eventAddr = await createEvent({ @@ -254,7 +248,7 @@ contract('MultipleResultsEvent', (accounts) => { assert.equal(await eventMethods.owner().call(), OWNER) const eventMeta = await eventMethods.eventMetadata().call() - assert.equal(eventMeta[0], 5) + assert.equal(eventMeta[0], 6) assert.equal(eventMeta[1], 'Test Event 1') assert.equal(web3.utils.toUtf8(eventMeta[2][0]), RESULT_INVALID) assert.equal(web3.utils.toUtf8(eventMeta[2][1]), 'A') @@ -263,11 +257,13 @@ contract('MultipleResultsEvent', (accounts) => { assert.equal(eventMeta[3], 4) const centralizedMeta = await eventMethods.centralizedMetadata().call() - assert.equal(centralizedMeta[0], eventParams[6]) - assert.equal(centralizedMeta[1], eventParams[2]) - assert.equal(centralizedMeta[2], eventParams[3]) - assert.equal(centralizedMeta[3], eventParams[4]) - assert.equal(centralizedMeta[4], eventParams[5]) + assert.equal(centralizedMeta[0], eventParams[4]) + // Block time during testing isn't completely accurate so test within a range + assert.isTrue(centralizedMeta[1] >= betStartTime - 50 + && centralizedMeta[1] <= betStartTime + 50) + assert.equal(centralizedMeta[2], betEndTime) + assert.equal(centralizedMeta[3], resultSetStartTime) + assert.equal(centralizedMeta[4], resultSetEndTime) const configMeta = await eventMethods.configMetadata().call() assert.equal(configMeta[0], escrowAmt) @@ -279,12 +275,12 @@ contract('MultipleResultsEvent', (accounts) => { configMeta[2], await configManagerMethods.thresholdPercentIncrease().call(), ) - assert.equal(configMeta[3], eventParams[8]) + assert.equal(configMeta[3], eventParams[6]) }) it('throws if centralizedOracle address is invalid', async () => { try { - const params = await getEventParams(INVALID_ADDR) + const params = await getEventParams(INVALID_ADDR, await currentBlockTime()) params[0] = 'Test Event 2' await createEvent({ nbotMethods, @@ -301,7 +297,7 @@ contract('MultipleResultsEvent', (accounts) => { it('throws if eventName is empty', async () => { try { - const params = await getEventParams(OWNER) + const params = await getEventParams(OWNER, await currentBlockTime()) params[0] = '' await createEvent({ nbotMethods, @@ -318,7 +314,7 @@ contract('MultipleResultsEvent', (accounts) => { it('throws if eventResults 0 or 1 are empty', async () => { try { - const params = await getEventParams(OWNER) + const params = await getEventParams(OWNER, await currentBlockTime()) params[0] = 'Test Event 3' params[1] = [ web3.utils.fromAscii(''), @@ -338,7 +334,7 @@ contract('MultipleResultsEvent', (accounts) => { } try { - const params = await getEventParams(OWNER) + const params = await getEventParams(OWNER, await currentBlockTime()) params[0] = 'Test Event 4' params[1] = [ web3.utils.fromAscii('A'), @@ -358,10 +354,10 @@ contract('MultipleResultsEvent', (accounts) => { } }) - it('throws if betEndTime is <= betStartTime', async () => { + it('throws if resultSetStartTime is < betEndTime', async () => { try { - const params = await getEventParams(OWNER) - params[0] = 'Test Event 5' + const params = await getEventParams(OWNER, await currentBlockTime()) + params[0] = 'Test Event 6' params[3] = params[2] await createEvent({ nbotMethods, @@ -372,15 +368,15 @@ contract('MultipleResultsEvent', (accounts) => { gas: MAX_GAS, }) } catch (e) { - sassert.revert(e, 'betEndTime should be > betStartTime') + sassert.revert(e, 'resultSetStartTime should be >= betEndTime') } }) - it('throws if resultSetStartTime is < betEndTime', async () => { + it('throws if arbitrationOptionIndex is invalid', async () => { try { - const params = await getEventParams(OWNER) - params[0] = 'Test Event 6' - params[4] = params[3] + const params = await getEventParams(OWNER, await currentBlockTime()) + params[0] = 'Test Event 7' + params[5] = 4 await createEvent({ nbotMethods, eventParams: params, @@ -390,15 +386,15 @@ contract('MultipleResultsEvent', (accounts) => { gas: MAX_GAS, }) } catch (e) { - sassert.revert(e, 'resultSetStartTime should be >= betEndTime') + sassert.revert(e, 'arbitrationOptionIndex should be < 4') } }) - it('throws if resultSetEndTime is <= resultSetStartTime', async () => { + it('throws if arbitrationRewardPercentage is invalid', async () => { try { - const params = await getEventParams(OWNER) - params[0] = 'Test Event 7' - params[5] = params[4] + const params = await getEventParams(OWNER, await currentBlockTime()) + params[0] = 'Test Event 8' + params[6] = 100 await createEvent({ nbotMethods, eventParams: params, @@ -408,7 +404,7 @@ contract('MultipleResultsEvent', (accounts) => { gas: MAX_GAS, }) } catch (e) { - sassert.revert(e, 'resultSetEndTime should be > resultSetStartTime') + sassert.revert(e, 'arbitrationRewardPercentage should be < 100') } }) }) @@ -443,9 +439,9 @@ contract('MultipleResultsEvent', (accounts) => { describe('valid time', () => { beforeEach(async () => { const currTime = await currentBlockTime() - await timeMachine.increaseTime(Number(eventParams[2]) - currTime) - assert.isAtLeast(await currentBlockTime(), Number(eventParams[2])) - assert.isBelow(await currentBlockTime(), Number(eventParams[3])) + await timeMachine.increaseTime(betStartTime - currTime) + assert.isAtLeast(await currentBlockTime(), betStartTime) + assert.isBelow(await currentBlockTime(), betEndTime) }) it('allows users to bet', async () => { @@ -474,8 +470,8 @@ contract('MultipleResultsEvent', (accounts) => { it('throws if the currentRound is not 0', async () => { const currTime = await currentBlockTime() - await timeMachine.increaseTime(Number(eventParams[4]) - currTime) - assert.isAtLeast(await currentBlockTime(), Number(eventParams[4])) + await timeMachine.increaseTime(resultSetStartTime - currTime) + assert.isAtLeast(await currentBlockTime(), resultSetStartTime) const amt = await eventMethods.currentConsensusThreshold().call() await setResult({ @@ -530,26 +526,10 @@ contract('MultipleResultsEvent', (accounts) => { }) describe('invalid time', () => { - it('throws if the current time is < betStartTime', async () => { - assert.isBelow(await currentBlockTime(), Number(eventParams[2])) - - try { - await placeBet({ - nbotMethods, - eventAddr, - amtDecimals: 1, - resultIndex: 0, - from: OWNER, - }) - } catch (e) { - sassert.revert(e, 'Current time should be >= betStartTime') - } - }) - it('throws if the current time is > betEndTime', async () => { const currTime = await currentBlockTime() - await timeMachine.increaseTime(Number(eventParams[3]) - currTime) - assert.isAtLeast(await currentBlockTime(), Number(eventParams[3])) + await timeMachine.increaseTime(betEndTime - currTime) + assert.isAtLeast(await currentBlockTime(), betEndTime) try { await placeBet({ diff --git a/test/util/contract_helper.js b/test/util/contract_helper.js deleted file mode 100644 index 0711f9a..0000000 --- a/test/util/contract_helper.js +++ /dev/null @@ -1,102 +0,0 @@ -// const Web3Beta = require('web3'); -// const Qweb3Utils = require('qweb3').Utils; -// const Encoder = require('qweb3').Encoder; - -// const AddressManager = artifacts.require('./storage/AddressManager.sol'); -// const BodhiEthereum = artifacts.require('./token/BodhiEthereum.sol'); -// const EventFactory = artifacts.require('./event/EventFactory.sol'); -// const OracleFactory = artifacts.require('./oracle/OracleFactory.sol'); - -// const Utils = require('.'); -// const Abi = require('./abi'); - -// const web3 = new Web3Beta(global.web3.currentProvider); - -// const BOT_DECIMALS = 8; -// const BODHI_TOKENS_BALANCE = Utils.toDenomination(100000, BOT_DECIMALS); - -// module.exports = class ContractHelper { -// static async initBaseContracts(admin, accounts) { -// const addressManager = await AddressManager.deployed({ from: admin }); - -// const bodhiToken = await ContractHelper.mintBodhiTokens(admin, accounts); -// await addressManager.setBodhiTokenAddress(bodhiToken.address, { from: admin }); -// assert.equal(await addressManager.bodhiTokenAddress.call(), bodhiToken.address); - -// const eventFactory = await EventFactory.deployed(addressManager.address, { from: admin }); -// await addressManager.setEventFactoryAddress(eventFactory.address, { from: admin }); -// assert.equal(await addressManager.eventFactoryVersionToAddress.call(0), eventFactory.address); - -// const oracleFactory = await OracleFactory.deployed(addressManager.address, { from: admin }); -// await addressManager.setOracleFactoryAddress(oracleFactory.address, { from: admin }); -// assert.equal(await addressManager.oracleFactoryVersionToAddress.call(0), oracleFactory.address); - -// return { -// addressManager, -// bodhiToken, -// eventFactory, -// oracleFactory, -// }; -// } - -// static async mintBodhiTokens(admin, accounts) { -// const token = await BodhiEthereum.deployed({ from: admin }); -// const expectedBalance = BODHI_TOKENS_BALANCE.toString(); - -// await token.mint(accounts[0], BODHI_TOKENS_BALANCE, { from: admin }); -// assert.equal((await token.balanceOf(accounts[0])).toString(), expectedBalance); - -// await token.mint(accounts[1], BODHI_TOKENS_BALANCE, { from: admin }); -// assert.equal((await token.balanceOf(accounts[1])).toString(), expectedBalance); - -// await token.mint(accounts[2], BODHI_TOKENS_BALANCE, { from: admin }); -// assert.equal((await token.balanceOf(accounts[2])).toString(), expectedBalance); - -// await token.mint(accounts[3], BODHI_TOKENS_BALANCE, { from: admin }); -// assert.equal((await token.balanceOf(accounts[3])).toString(), expectedBalance); - -// await token.mint(accounts[4], BODHI_TOKENS_BALANCE, { from: admin }); -// assert.equal((await token.balanceOf(accounts[4])).toString(), expectedBalance); - -// await token.mint(accounts[5], BODHI_TOKENS_BALANCE, { from: admin }); -// assert.equal((await token.balanceOf(accounts[5])).toString(), expectedBalance); - -// await token.mint(accounts[6], BODHI_TOKENS_BALANCE, { from: admin }); -// assert.equal((await token.balanceOf(accounts[6])).toString(), expectedBalance); - -// await token.mint(accounts[7], BODHI_TOKENS_BALANCE, { from: admin }); -// assert.equal((await token.balanceOf(accounts[7])).toString(), expectedBalance); - -// await token.mint(accounts[8], BODHI_TOKENS_BALANCE, { from: admin }); -// assert.equal((await token.balanceOf(accounts[8])).toString(), expectedBalance); - -// return token; -// } - -// static async approve(tokenContract, sender, to, amount) { -// await tokenContract.approve(to, amount, { from: sender }); -// assert.equal((await tokenContract.allowance(sender, to)).toString(), amount.toString()); -// } - -// static async transferSetResult(token, event, cOracle, resultSetter, resultIndex, amount) { -// const data = '0x65f4ced1' -// + Qweb3Utils.trimHexPrefix(cOracle.address) -// + Qweb3Utils.trimHexPrefix(resultSetter) -// + Encoder.uintToHex(resultIndex); - -// const tokenWeb3Contract = new web3.eth.Contract(Abi.BodhiEthereum, token.address); -// return await tokenWeb3Contract.methods["transfer(address,uint256,bytes)"](event.address, amount, data) -// .send({ from: resultSetter, gas: 5000000 }); -// } - -// static async transferVote(token, event, dOracle, voter, resultIndex, amount) { -// const data = '0x6f02d1fb' -// + Qweb3Utils.trimHexPrefix(dOracle.address) -// + Qweb3Utils.trimHexPrefix(voter) -// + Encoder.uintToHex(resultIndex); - -// const tokenWeb3Contract = new web3.eth.Contract(Abi.BodhiEthereum, token.address); -// return await tokenWeb3Contract.methods["transfer(address,uint256,bytes)"](event.address, amount, data) -// .send({ from: voter, gas: 5000000 }); -// } -// }; diff --git a/test/util/index.js b/test/util/index.js index 85b0ecd..df79694 100644 --- a/test/util/index.js +++ b/test/util/index.js @@ -47,15 +47,6 @@ module.exports = class Utils { return Utils.toDenomination(amount, 8) } - /* - * Truncates the decimals off the BigNumber and returns a new BigNumber. - * @param number {BigNumber} The number to truncate. - * @retun {BigNumber} The truncated BigNumber. - */ - static bigNumberFloor(bigNumber) { - return web3.toBigNumber(bigNumber.toString().split('.')[0]) - } - // Gets the unix time in seconds of the current block static async currentBlockTime() { const blockNum = await web3.eth.getBlockNumber() @@ -63,18 +54,6 @@ module.exports = class Utils { return block.timestamp } - /* - * Removes the padded zeros in an address hex string. - * eg. 0x0000000000000000000000006b36fdf89d706035dc97b6aa4bc84b2418a452f1 -> 0x6b36fdf89d706035dc97b6aa4bc84b2418a452f1 - * @param hexString {string} The hex string to remove the padding from. - * @return {string} The hex string with the padded zeros removed. - */ - static paddedHexToAddress(hexString) { - const regex = new RegExp(/(0x)(0+)([a-fA-F0-9]{40})/) - const matches = regex.exec(hexString) - return matches && matches[1] + matches[3] - } - static constructTransfer223Data(funcSig, types, params) { const encoded = Utils.removeHexPrefix(web3.eth.abi.encodeParameters(types, params)) return Utils.addHexPrefix(`${funcSig}${encoded}`)