UNPKG

@gnosis.pm/pm-contracts

Version:

Collection of smart contracts for the Gnosis prediction market platform

330 lines (274 loc) 15.1 kB
const utils = require('./utils') const { getBlock } = utils const { wait } = require('./digix_tempo')(web3) const WETH9 = artifacts.require('WETH9') const CentralizedOracle = artifacts.require('CentralizedOracle') const CentralizedOracleFactory = artifacts.require('CentralizedOracleFactory') const DifficultyOracle = artifacts.require('DifficultyOracle') const DifficultyOracleFactory = artifacts.require('DifficultyOracleFactory') const MajorityOracle = artifacts.require('MajorityOracle') const MajorityOracleFactory = artifacts.require('MajorityOracleFactory') const UltimateOracle = artifacts.require('UltimateOracle') const UltimateOracleFactory = artifacts.require('UltimateOracleFactory') const FutarchyOracle = artifacts.require('FutarchyOracle') const FutarchyOracleFactory = artifacts.require('FutarchyOracleFactory') const StandardMarketWithPriceLogger = artifacts.require('StandardMarketWithPriceLogger') const LMSRMarketMaker = artifacts.require('LMSRMarketMaker') const CategoricalEvent = artifacts.require('CategoricalEvent') const ScalarEvent = artifacts.require('ScalarEvent') const OutcomeToken = artifacts.require('OutcomeToken') contract('Oracle', function (accounts) { let centralizedOracleFactory let difficultyOracleFactory let majorityOracleFactory let ultimateOracleFactory let futarchyOracleFactory let lmsrMarketMaker let etherToken let ipfsHash, ipfsBytes let spreadMultiplier, challengePeriod, challengeAmount, frontRunnerPeriod let fee, deadline, funding, startDate beforeEach(async () => { // deployed factory contracts centralizedOracleFactory = await CentralizedOracleFactory.deployed() difficultyOracleFactory = await DifficultyOracleFactory.deployed() majorityOracleFactory = await MajorityOracleFactory.deployed() ultimateOracleFactory = await UltimateOracleFactory.deployed() futarchyOracleFactory = await FutarchyOracleFactory.deployed() lmsrMarketMaker = await LMSRMarketMaker.deployed.call() etherToken = await WETH9.deployed() // ipfs hashes ipfsHash = web3.utils.utf8ToHex('QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG') ipfsBytes = '0x516d597741504a7a7635435a736e4136323573335866326e656d7459675070486457457a37396f6a576e50626447' // Ultimate oracle stuff spreadMultiplier = 2 challengePeriod = 200 // 200s challengeAmount = 100 // 100wei frontRunnerPeriod = 50 // 50s // Futarchy oracle stuff fee = 500000 // 5% deadline = 100 // 100s funding = (10**18).toString() // 1 ETH startDate = 0 }) it('should test centralized oracle', async () => { // Create centralized oracle factory const owner1 = 0 const owner2 = 1 // create centralized oracle const centralizedOracle = await utils.getParamFromTxEvent( await centralizedOracleFactory.createCentralizedOracle(ipfsHash, { from: accounts[owner1] }), 'centralizedOracle', CentralizedOracle ) // Replace account resolving outcome assert.equal(await centralizedOracle.owner.call(), accounts[owner1]) await centralizedOracle.replaceOwner(accounts[owner2], {from: accounts[owner1]}) assert.equal(await centralizedOracle.owner.call(), accounts[owner2]) // Set outcome await utils.assertRejects(centralizedOracle.setOutcome(0, {from: accounts[owner1]}), "owner1 is not the centralized oracle owner") assert.equal(await centralizedOracle.isOutcomeSet.call(), false) await centralizedOracle.setOutcome(1, {from: accounts[owner2]}) assert.equal(await centralizedOracle.isOutcomeSet.call(), true) assert.equal(await centralizedOracle.getOutcome.call(), 1) assert.equal(await centralizedOracle.ipfsHash.call(), ipfsBytes) }) it('should test difficulty oracle', async () => { // Create difficulty oracle const targetBlock = (await getBlock('latest')).number + 100 const difficultyOracle = await utils.getParamFromTxEvent( await difficultyOracleFactory.createDifficultyOracle(targetBlock), 'difficultyOracle', DifficultyOracle ) // Set outcome await utils.assertRejects(difficultyOracle.setOutcome()) assert.equal(await difficultyOracle.isOutcomeSet.call(), false) // TODO: TestRPC difficulty is 0, so these tests won't pass there // // Wait until block 100 // await waitUntilBlock(20, targetBlock) // await difficultyOracle.setOutcome() // assert.equal(await difficultyOracle.isOutcomeSet.call(), true) // assert.isAbove(await difficultyOracle.getOutcome(), 0) }) it('can create futarchy oracles in the future, but not the past', async () => { // Create Oracles const centralizedOracle = await utils.getParamFromTxEvent( await centralizedOracleFactory.createCentralizedOracle(ipfsHash), 'centralizedOracle', CentralizedOracle ) let now = (await getBlock('pending')).timestamp utils.getParamFromTxEvent( await futarchyOracleFactory.createFutarchyOracle( etherToken.address, centralizedOracle.address, 2, -100, 100, lmsrMarketMaker.address, fee, deadline, now + 1000), 'futarchyOracle', FutarchyOracle ) now = (await getBlock('pending')).timestamp await utils.assertRejects( futarchyOracleFactory.createFutarchyOracle( etherToken.address, centralizedOracle.address, 2, -100, 100, lmsrMarketMaker.address, fee, deadline, now - 1000), 'forged FutarchyOracle with startDate in the past') }) it('should test futarchy oracle', async () => { // Create Oracles const centralizedOracle = await utils.getParamFromTxEvent( await centralizedOracleFactory.createCentralizedOracle(ipfsHash), 'centralizedOracle', CentralizedOracle ) const futarchyOracle = await utils.getParamFromTxEvent( await futarchyOracleFactory.createFutarchyOracle( etherToken.address, centralizedOracle.address, 2, -100, 100, lmsrMarketMaker.address, fee, deadline, startDate), 'futarchyOracle', FutarchyOracle ) const creator = 0 await etherToken.deposit({ value: funding, from: accounts[creator] }) assert.equal(await etherToken.balanceOf.call(accounts[creator]), funding) await etherToken.approve(futarchyOracle.address, funding, { from: accounts[creator] }) await futarchyOracle.fund(funding, { from: accounts[creator] }) const market = await StandardMarketWithPriceLogger.at(await futarchyOracle.markets(1)) const categoricalEvent = await CategoricalEvent.at(await futarchyOracle.categoricalEvent()) // Buy into market for outcome token 1 const buyer = 1 const outcome = 1 const tokenCount = 1e15.toString() let outcomeTokenAmounts = Array.from({length: 2}, (v, i) => i === outcome ? tokenCount : 0) const outcomeTokenCost = await lmsrMarketMaker.calcNetCost.call(market.address, outcomeTokenAmounts) let marketfee = await market.calcMarketFee.call(outcomeTokenCost) const cost = marketfee.add(outcomeTokenCost) // Buy all outcomes await etherToken.deposit({ value: cost, from: accounts[buyer] }) assert.equal(await etherToken.balanceOf.call(accounts[buyer]), cost.toString()) await etherToken.approve(categoricalEvent.address, cost, { from: accounts[buyer] }) await categoricalEvent.buyAllOutcomes(cost, { from: accounts[buyer] }) // Buy long tokens from market 1 const collateralToken = await OutcomeToken.at(await categoricalEvent.outcomeTokens(1)) await collateralToken.approve(market.address, cost, { from: accounts[buyer] }) assert.equal(await utils.getParamFromTxEvent( await market.trade(outcomeTokenAmounts, cost, { from: accounts[buyer] }), 'outcomeTokenNetCost' ), outcomeTokenCost.toString()) // Set outcome of futarchy oracle await utils.assertRejects( futarchyOracle.setOutcome(), 'set oracle outcome before deadline') await wait(deadline) await futarchyOracle.setOutcome() assert.equal(await futarchyOracle.isOutcomeSet.call(), true) assert.equal(await futarchyOracle.getOutcome.call(), 1) await categoricalEvent.setOutcome() // Set winning outcome for scalar events await utils.assertRejects( futarchyOracle.close(), 'Futarchy oracle cannot be closed if oracle for scalar market is not set') await centralizedOracle.setOutcome(-50) const scalarEvent = await ScalarEvent.at(await market.eventContract()) await scalarEvent.setOutcome() // Close winning market and transfer collateral tokens to creator await futarchyOracle.close({ from: accounts[creator] }) assert((await etherToken.balanceOf.call(accounts[creator])).gt(funding)) }) it('should test majority oracle', async () => { // create Oracles const owners = [0, 1, 2] const oracles = await Promise.all( (await Promise.all( owners.map((owner) => centralizedOracleFactory.createCentralizedOracle(ipfsHash, {from: accounts[owner]})) )).map((tx) => utils.getParamFromTxEvent(tx, 'centralizedOracle', CentralizedOracle)) ) const majorityOracle = await utils.getParamFromTxEvent( await majorityOracleFactory.createMajorityOracle(oracles.map((o) => o.address)), 'majorityOracle', MajorityOracle ) // Majority oracle cannot be resolved yet assert.equal(await majorityOracle.isOutcomeSet.call(), false) // Set outcome in first centralized oracle await oracles[0].setOutcome(1, { from: accounts[owners[0]] }) // Majority vote is not reached yet assert.equal(await majorityOracle.isOutcomeSet.call(), false) // Set outcome in second centralized oracle await oracles[1].setOutcome(1, { from: accounts[owners[1]] }) // // majority vote is reached assert.equal(await majorityOracle.isOutcomeSet.call(), true) assert.equal(await majorityOracle.getOutcome.call(), 1) }) // TODO: test signed message oracle it('should test ultimate oracle', async () => { // Create Oracles const centralizedOracle = await utils.getParamFromTxEvent( await centralizedOracleFactory.createCentralizedOracle(ipfsHash), 'centralizedOracle', CentralizedOracle ) const ultimateOracle = await utils.getParamFromTxEvent( await ultimateOracleFactory.createUltimateOracle( centralizedOracle.address, etherToken.address, spreadMultiplier, challengePeriod, challengeAmount, frontRunnerPeriod), 'ultimateOracle', UltimateOracle ) // Set outcome in central oracle await centralizedOracle.setOutcome(1) assert.equal(await centralizedOracle.getOutcome.call(), 1) // Set outcome in ultimate oracle await ultimateOracle.setForwardedOutcome() assert.equal(await ultimateOracle.forwardedOutcome.call(), 1) assert.equal(await ultimateOracle.isOutcomeSet.call(), false) // Challenge outcome const sender1 = 0 await etherToken.deposit({value: 100, from: accounts[sender1]}) await etherToken.approve(ultimateOracle.address, 100, { from: accounts[sender1] }) await ultimateOracle.challengeOutcome(2) // Sender 1 tries to increase their bid but can't await etherToken.deposit({value: 50, from: accounts[sender1]}) await etherToken.approve(ultimateOracle.address, 50, { from: accounts[sender1] }) await ultimateOracle.voteForOutcome(2, 50, { from: accounts[sender1] }) assert.equal(await ultimateOracle.outcomeAmounts.call(accounts[sender1], 2), 100) // Sender 2 overbids sender 1 const sender2 = 1 await etherToken.deposit({value: 150, from: accounts[sender2]}) await etherToken.approve(ultimateOracle.address, 150, { from: accounts[sender2] }) await ultimateOracle.voteForOutcome(3, 150, { from: accounts[sender2] }) assert.equal(await ultimateOracle.outcomeAmounts.call(accounts[sender2], 3), 150) // Sender 2 tries to increase his front runner position by 150 but can only increase it by 50 await etherToken.deposit({value: 150, from: accounts[sender2]}) await etherToken.approve(ultimateOracle.address, 150, { from: accounts[sender2] }) await ultimateOracle.voteForOutcome(3, 150, { from: accounts[sender2] }) assert.equal(await ultimateOracle.outcomeAmounts.call(accounts[sender2], 3), 200) // Trying to withdraw before front runner period ends fails await utils.assertRejects( ultimateOracle.withdraw({from: accounts[sender2]}), 'withdrew before front runner period') // Wait for front runner period to pass assert.equal(await ultimateOracle.isOutcomeSet.call(), false) await wait(frontRunnerPeriod + 1) assert.equal(await ultimateOracle.isOutcomeSet.call(), true) assert.equal(await ultimateOracle.getOutcome.call(), 3) // Withdraw winnings assert.equal(await utils.getParamFromTxEvent( await ultimateOracle.withdraw({from: accounts[sender2]}), 'amount' ).valueOf(), 300) }) it('should test ultimate oracle challenge period', async () => { // create Oracles const owner1 = 0 const centralizedOracle = await utils.getParamFromTxEvent( await centralizedOracleFactory.createCentralizedOracle(ipfsHash, {from: accounts[owner1]}), 'centralizedOracle', CentralizedOracle ) const ultimateOracle = await utils.getParamFromTxEvent( await ultimateOracleFactory.createUltimateOracle( centralizedOracle.address, etherToken.address, spreadMultiplier, challengePeriod, challengeAmount, frontRunnerPeriod), 'ultimateOracle', UltimateOracle ) // Set outcome in central oracle await centralizedOracle.setOutcome(1) assert.equal(await centralizedOracle.getOutcome.call(), 1) // Set outcome in ultimate oracle await ultimateOracle.setForwardedOutcome() assert.equal(await ultimateOracle.forwardedOutcome.call(), 1) assert.equal(await ultimateOracle.isOutcomeSet.call(), false) // Wait for challenge period to pass await wait(challengePeriod + 1) assert.equal(await ultimateOracle.isOutcomeSet.call(), true) assert.equal(await ultimateOracle.getOutcome.call(), 1) }) })