def test_token_fee_collection(localFixture, universe, market, categoricalMarket, scalarMarket, cash, reputationToken, reportingWindow): # We'll progress past the designated dispute phase and finalize all the markets localFixture.chain.head_state.timestamp = market.getEndTime( ) + localFixture.contracts[ "Constants"].DESIGNATED_REPORTING_DURATION_SECONDS() + 1 assert market.tryFinalize() assert categoricalMarket.tryFinalize() assert scalarMarket.tryFinalize() # We can't redeem the stake used to do the designated report for fees yet since the window is not yet over marketDesignatedStake = localFixture.getStakeToken( market, [0, market.getNumTicks()]) categoricalMarketDesignatedStake = localFixture.getStakeToken( categoricalMarket, [0, 0, categoricalMarket.getNumTicks()]) scalarMarketDesignatedStake = localFixture.getStakeToken( scalarMarket, [0, scalarMarket.getNumTicks()]) with raises(TransactionFailed): marketDesignatedStake.redeemWinningTokens(False) # If we forgo fees we can redeem however. We'll do this for the scalar market. Note that the market total stake isn't decreased. Market total stake only decreases once it is finalized at which point it can no longer migrate so the value doesn't matter scalarStake = scalarMarketDesignatedStake.balanceOf(tester.a0) with TokenDelta(reputationToken, scalarStake, tester.a0, "Forgoing fees resulting in an incorrect REP refund"): with EtherDelta(0, tester.a0, localFixture.chain, "Forgoing fees gave fees incorrectly"): with StakeDelta( 0, -scalarStake, -scalarStake, scalarMarket, reportingWindow, "Forgoing fees incorrectly updated stake accounting"): assert scalarMarketDesignatedStake.redeemWinningTokens(True) # We cannot purchase participation tokens yet since the window isn't active participationToken = localFixture.applySignature( "ParticipationToken", reportingWindow.getParticipationToken()) with raises(TransactionFailed): participationToken.buy(1) # We'll progress to the start of the window and purchase some participation tokens localFixture.chain.head_state.timestamp = reportingWindow.getStartTime( ) + 1 participationTokenAmount = 100 with TokenDelta(reputationToken, -participationTokenAmount, tester.a0, "Buying participation tokens didn't deduct REP correctly"): with TokenDelta( participationToken, participationTokenAmount, tester.a0, "Buying participation tokens didn't increase participation token balance correctly" ): with StakeDelta( 0, participationTokenAmount, participationTokenAmount, market, reportingWindow, "Buying participation tokens din't adjust stake accounting correctly" ): assert participationToken.buy(participationTokenAmount) # As other testers we'll buy some more with StakeDelta( 0, participationTokenAmount * 3, participationTokenAmount * 3, market, reportingWindow, "Buying participation tokens din't adjust stake accounting correctly" ): with TokenDelta( participationToken, participationTokenAmount, tester.a1, "Buying participation tokens didn't increase participation token balance correctly" ): assert participationToken.buy(participationTokenAmount, sender=tester.k1) with TokenDelta( participationToken, participationTokenAmount, tester.a2, "Buying participation tokens didn't increase participation token balance correctly" ): assert participationToken.buy(participationTokenAmount, sender=tester.k2) with TokenDelta( participationToken, participationTokenAmount, tester.a3, "Buying participation tokens didn't increase participation token balance correctly" ): assert participationToken.buy(participationTokenAmount, sender=tester.k3) # We can't redeem the participation tokens for fees yet since the window isn't over with raises(TransactionFailed): participationToken.redeem(False) # We can redeem them to just get back REP. We'll have tester 3 do this participationValue = participationToken.balanceOf(tester.a3) with TokenDelta(reputationToken, participationValue, tester.a3, "Forgoing fees resulting in an incorrect REP refund"): with TokenDelta( participationToken, -participationTokenAmount, tester.a3, "Redeeming participation tokens didn't decrease participation token balance correctly" ): with EtherDelta(0, tester.a0, localFixture.chain, "Forgoing fees gave fees incorrectly"): with StakeDelta( 0, -participationValue, -participationValue, market, reportingWindow, "Forgoing fees incorrectly updated stake accounting"): assert participationToken.redeem(True, sender=tester.k3) # Fast forward time until the window is over and we can redeem our winning stake and participation tokens and receive fees localFixture.chain.head_state.timestamp = reportingWindow.getEndTime() + 1 reporterFees = 1000 * market.getNumTicks( ) / universe.getReportingFeeDivisor() totalWinningStake = reportingWindow.getTotalWinningStake() assert cash.balanceOf(reportingWindow.address) == reporterFees expectedParticipationFees = reporterFees * participationTokenAmount / totalWinningStake # Cashing out Participation tokens or Stake tokens will awards fees proportional to the total winning stake in the window with TokenDelta( participationToken, -participationTokenAmount, tester.a0, "Redeeming participation tokens didn't decrease participation token balance correctly" ): with EtherDelta( expectedParticipationFees, tester.a0, localFixture.chain, "Redeeming participation tokens didn't increase ETH correctly" ): assert participationToken.redeem(False) with TokenDelta( participationToken, -participationTokenAmount, tester.a1, "Redeeming participation tokens didn't decrease participation token balance correctly" ): with EtherDelta( expectedParticipationFees, tester.a1, localFixture.chain, "Redeeming participation tokens didn't increase ETH correctly" ): assert participationToken.redeem(False, sender=tester.k1) with TokenDelta( participationToken, -participationTokenAmount, tester.a2, "Redeeming participation tokens didn't decrease participation token balance correctly" ): with EtherDelta( expectedParticipationFees, tester.a2, localFixture.chain, "Redeeming participation tokens didn't increase ETH correctly" ): assert participationToken.redeem(False, sender=tester.k2) logs = [] captureFilteredLogs(localFixture.chain.head_state, localFixture.contracts['Augur'], logs) marketStake = marketDesignatedStake.balanceOf(tester.a0) expectedFees = reporterFees * marketStake / totalWinningStake + 1 # Rounding error with EtherDelta(expectedFees, tester.a0, localFixture.chain, "Redeeming Stake tokens didn't increase ETH correctly"): with TokenDelta( marketDesignatedStake, -marketStake, tester.a0, "Redeeming Stake tokens didn't decrease Stake token balance correctly" ): assert marketDesignatedStake.redeemWinningTokens(False) # Confirm redeeming stake tokens logs appropriately assert len(logs) == 3 assert logs[2]['_event_type'] == 'WinningTokensRedeemed' assert logs[2]['reporter'] == bytesToHexString(tester.a0) assert logs[2]['reportingFeesReceived'] == expectedFees assert logs[2]['stakeToken'] == marketDesignatedStake.address assert logs[2]['market'] == market.address assert logs[2]['amountRedeemed'] == marketStake assert logs[2]['payoutNumerators'] == [0, market.getNumTicks()] categoricalMarketStake = categoricalMarketDesignatedStake.balanceOf( tester.a0) expectedFees = reporterFees * categoricalMarketStake / totalWinningStake + 1 # Rounding error with EtherDelta(expectedFees, tester.a0, localFixture.chain, "Redeeming Stake tokens didn't increase ETH correctly"): with TokenDelta( categoricalMarketDesignatedStake, -categoricalMarketStake, tester.a0, "Redeeming Stake tokens didn't decrease Stake token balance correctly" ): assert categoricalMarketDesignatedStake.redeemWinningTokens(False)
def test_multiple_round_crowdsourcer(localFixture, universe, market, cash, reputationToken): constants = localFixture.contracts["Constants"] # Initial Report disputed proceedToNextRound(localFixture, market, tester.k1, True) # Initial Report winning proceedToNextRound(localFixture, market, tester.k2, True) # Initial Report disputed proceedToNextRound(localFixture, market, tester.k1, True, randomPayoutNumerators=True) # Initial Report winning proceedToNextRound(localFixture, market, tester.k3, True) # Get all the winning Reporting Participants initialReporter = localFixture.applySignature( 'InitialReporter', market.getReportingParticipant(0)) winningDisputeCrowdsourcer1 = localFixture.applySignature( 'DisputeCrowdsourcer', market.getReportingParticipant(2)) winningDisputeCrowdsourcer2 = localFixture.applySignature( 'DisputeCrowdsourcer', market.getReportingParticipant(4)) # Get losing Reporting Participants losingDisputeCrowdsourcer1 = localFixture.applySignature( 'DisputeCrowdsourcer', market.getReportingParticipant(1)) losingDisputeCrowdsourcer2 = localFixture.applySignature( 'DisputeCrowdsourcer', market.getReportingParticipant(3)) # We can't redeem yet as the market isn't finalized with raises(TransactionFailed): initialReporter.redeem(tester.a0) with raises(TransactionFailed): winningDisputeCrowdsourcer1.redeem(tester.a2) # Fast forward time until the new dispute window is over disputeWindow = localFixture.applySignature("DisputeWindow", market.getDisputeWindow()) localFixture.contracts["Time"].setTimestamp(disputeWindow.getEndTime() + 1) assert market.finalize() expectedRep = initialReporter.getStake( ) + initialReporter.getStake() * 2 / 5 with TokenDelta(reputationToken, expectedRep, tester.a0, "Redeeming didn't refund REP"): assert initialReporter.redeem(tester.a0) expectedRep = winningDisputeCrowdsourcer1.getStake( ) + winningDisputeCrowdsourcer1.getStake() * 2 / 5 with TokenDelta(reputationToken, expectedRep, tester.a2, "Redeeming didn't refund REP"): assert winningDisputeCrowdsourcer1.redeem(tester.a2) expectedRep = winningDisputeCrowdsourcer2.getStake( ) + winningDisputeCrowdsourcer2.getStake() * 2 / 5 with TokenDelta(reputationToken, expectedRep, tester.a3, "Redeeming didn't refund REP"): assert winningDisputeCrowdsourcer2.redeem(tester.a3) # The losing reports get no REP with TokenDelta(reputationToken, 0, tester.a1, "Redeeming refunded REP"): assert losingDisputeCrowdsourcer1.redeem(tester.a1) with TokenDelta(reputationToken, 0, tester.a1, "Redeeming refunded REP"): assert losingDisputeCrowdsourcer2.redeem(tester.a1)
def test_failed_crowdsourcer(finalize, localFixture, universe, market, cash, reputationToken): disputeWindow = localFixture.applySignature('DisputeWindow', market.getDisputeWindow()) # We'll make the window active localFixture.contracts["Time"].setTimestamp(disputeWindow.getStartTime() + 1) # We'll have testers contribute to a dispute but not reach the target amount = market.getParticipantStake() # confirm we can contribute 0 assert market.contribute([0, 1, market.getNumTicks() - 1], 0, "", sender=tester.k1) with TokenDelta(reputationToken, -amount + 1, tester.a1, "Disputing did not reduce REP balance correctly"): assert market.contribute([0, 1, market.getNumTicks() - 1], amount - 1, "", sender=tester.k1) with TokenDelta(reputationToken, -amount + 1, tester.a2, "Disputing did not reduce REP balance correctly"): assert market.contribute([0, 1, market.getNumTicks() - 1], amount - 1, "", sender=tester.k2) assert market.getDisputeWindow() == disputeWindow.address payoutDistributionHash = market.derivePayoutDistributionHash( [0, 1, market.getNumTicks() - 1]) failedCrowdsourcer = localFixture.applySignature( "DisputeCrowdsourcer", market.getCrowdsourcer(payoutDistributionHash)) # confirm we cannot contribute directly to a crowdsourcer without going through the market with raises(TransactionFailed): failedCrowdsourcer.contribute(tester.a0, 1) if finalize: # Fast forward time until the dispute window is over and we can redeem to recieve the REP back localFixture.contracts["Time"].setTimestamp( disputeWindow.getEndTime() + 1) else: # Continue to the next round which will disavow failed crowdsourcers and let us redeem once the window is over market.contribute([0, 0, market.getNumTicks()], amount * 2, "") assert market.getDisputeWindow() != disputeWindow.address localFixture.contracts["Time"].setTimestamp( disputeWindow.getEndTime() + 1) with TokenDelta(reputationToken, amount - 1, tester.a1, "Redeeming did not refund REP"): assert failedCrowdsourcer.redeem(tester.a1) with TokenDelta(reputationToken, amount - 1, tester.a2, "Redeeming did not refund REP"): assert failedCrowdsourcer.redeem(tester.a2)
def test_redeem_participation_tokens(kitchenSinkFixture, universe, market, cash): reputationToken = kitchenSinkFixture.applySignature( "ReputationToken", universe.getReputationToken()) # proceed to the next round and buy some more fee window tokens proceedToNextRound(kitchenSinkFixture, market, doGenerateFees=True) feeWindow = kitchenSinkFixture.applySignature('FeeWindow', market.getFeeWindow()) # We'll make the window active then purchase some participation tokens kitchenSinkFixture.contracts["Time"].setTimestamp( feeWindow.getStartTime() + 1) feeWindowAmount = 100 # Distribute REP for testAccount in [tester.a1, tester.a2, tester.a3]: reputationToken.transfer(testAccount, 1 * 10**6 * 10**18) assert feeWindow.buy(feeWindowAmount, sender=tester.k1) assert feeWindow.buy(feeWindowAmount, sender=tester.k2) assert feeWindow.buy(feeWindowAmount, sender=tester.k3) # proceed to the next round and buy some more fee window tokens proceedToNextRound(kitchenSinkFixture, market, doGenerateFees=True) newFeeWindow = kitchenSinkFixture.applySignature('FeeWindow', market.getFeeWindow()) assert newFeeWindow.buy(feeWindowAmount, sender=tester.k1) assert newFeeWindow.buy(feeWindowAmount, sender=tester.k2) assert newFeeWindow.buy(feeWindowAmount, sender=tester.k3) # Now end the window kitchenSinkFixture.contracts["Time"].setTimestamp( newFeeWindow.getEndTime() + 1) reporterFees = 1000 * market.getNumTicks( ) / universe.getOrCacheReportingFeeDivisor() totalStake = feeWindow.getTotalFeeStake() + newFeeWindow.getTotalFeeStake() assert cash.balanceOf(feeWindow.address) == reporterFees assert cash.balanceOf(newFeeWindow.address) == reporterFees expectedParticipationFees = reporterFees * feeWindowAmount * 2 / totalStake # Cashing out Participation tokens will awards fees proportional to the total winning stake in the window with TokenDelta(reputationToken, feeWindowAmount * 2, tester.a3, "Redeeming participation tokens didn't refund REP"): with TokenDelta( feeWindow, -feeWindowAmount, tester.a3, "Redeeming participation tokens didn't decrease participation token balance correctly" ): with EtherDelta( expectedParticipationFees, tester.a3, kitchenSinkFixture.chain, "Redeeming participation tokens didn't increase ETH correctly" ): with PrintGasUsed(kitchenSinkFixture, "Universe Redeem:", 0): assert universe.redeemStake( [], [feeWindow.address, newFeeWindow.address], sender=tester.k3) with TokenDelta(reputationToken, feeWindowAmount * 2, tester.a1, "Redeeming participation tokens didn't refund REP"): with TokenDelta( feeWindow, -feeWindowAmount, tester.a1, "Redeeming participation tokens didn't decrease participation token balance correctly" ): with EtherDelta( expectedParticipationFees, tester.a1, kitchenSinkFixture.chain, "Redeeming participation tokens didn't increase ETH correctly" ): assert universe.redeemStake( [], [feeWindow.address, newFeeWindow.address], sender=tester.k1) with TokenDelta(reputationToken, feeWindowAmount * 2, tester.a2, "Redeeming participation tokens didn't refund REP"): with TokenDelta( feeWindow, -feeWindowAmount, tester.a2, "Redeeming participation tokens didn't decrease participation token balance correctly" ): with EtherDelta( expectedParticipationFees, tester.a2, kitchenSinkFixture.chain, "Redeeming participation tokens didn't increase ETH correctly" ): assert universe.redeemStake( [], [feeWindow.address, newFeeWindow.address], sender=tester.k2)
def finalizeFork(fixture, market, universe, finalizeByMigration=True): reputationToken = fixture.applySignature('ReputationToken', universe.getReputationToken()) # The universe forks and there is now a universe where NO and YES are the respective outcomes of each noPayoutNumerators = [0] * market.getNumberOfOutcomes() noPayoutNumerators[0] = market.getNumTicks() yesPayoutNumerators = noPayoutNumerators[::-1] noUniverse = fixture.applySignature( 'Universe', universe.createChildUniverse(noPayoutNumerators, False)) yesUniverse = fixture.applySignature( 'Universe', universe.createChildUniverse(yesPayoutNumerators, False)) noUniverseReputationToken = fixture.applySignature( 'ReputationToken', noUniverse.getReputationToken()) yesUniverseReputationToken = fixture.applySignature( 'ReputationToken', yesUniverse.getReputationToken()) assert noUniverse.address != universe.address assert yesUniverse.address != universe.address assert yesUniverse.address != noUniverse.address assert noUniverseReputationToken.address != yesUniverseReputationToken.address # Attempting to finalize the fork now will not succeed as no REP has been migrated and not enough time has passed with raises(TransactionFailed): market.finalizeFork() # A Tester moves some of their REP to the YES universe amount = 10**6 * 10**18 bonus = amount / fixture.contracts[ "Constants"].FORK_MIGRATION_PERCENTAGE_BONUS_DIVISOR() with TokenDelta(yesUniverseReputationToken, amount + bonus, tester.a0, "Yes REP token balance was no correct"): reputationToken.migrateOut(yesUniverseReputationToken.address, amount) # Attempting to finalize the fork now will not succeed as a majority or REP has not yet migrated and fork end time has not been reached with raises(TransactionFailed): market.finalizeFork() if (finalizeByMigration): # Tester 0 moves more than 50% of REP amount = reputationToken.balanceOf(tester.a0) - 20 bonus = amount / fixture.contracts[ "Constants"].FORK_MIGRATION_PERCENTAGE_BONUS_DIVISOR() with TokenDelta(noUniverseReputationToken, amount + bonus, tester.a0, "No REP token balance was no correct"): reputationToken.migrateOut(noUniverseReputationToken.address, amount) assert reputationToken.balanceOf(tester.a0) == 20 assert market.getWinningPayoutDistributionHash( ) == noUniverse.getParentPayoutDistributionHash() else: # Time marches on past the fork end time fixture.contracts["Time"].setTimestamp(universe.getForkEndTime() + 1) assert market.finalize() assert market.getWinningPayoutDistributionHash( ) == yesUniverse.getParentPayoutDistributionHash() # if the fork finalized by migration we're still in the 60 day fork window and can still get a bonus for migrating. If the fork is past the fork period we can no longer get the 5% bonus amount = 20 amountAdded = amount if finalizeByMigration: bonus = amount / fixture.contracts[ "Constants"].FORK_MIGRATION_PERCENTAGE_BONUS_DIVISOR() amountAdded += bonus with TokenDelta(yesUniverseReputationToken, amountAdded, tester.a0, "reputation migration bonus did not work correctly"): reputationToken.migrateOut(yesUniverseReputationToken.address, amount) # Finalize fork cannot be called again with raises(TransactionFailed): market.finalizeFork()
def test_multiple_round_crowdsourcer_fees(localFixture, universe, market, cash, reputationToken): constants = localFixture.contracts["Constants"] # Initial Report disputed proceedToNextRound(localFixture, market, tester.k1, True) # Initial Report winning proceedToNextRound(localFixture, market, tester.k2, True) # Initial Report disputed proceedToNextRound(localFixture, market, tester.k1, True, randomPayoutNumerators=True) # Initial Report winning proceedToNextRound(localFixture, market, tester.k3, True) # Get all the winning Reporting Participants initialReporter = localFixture.applySignature( 'InitialReporter', market.getReportingParticipant(0)) winningDisputeCrowdsourcer1 = localFixture.applySignature( 'DisputeCrowdsourcer', market.getReportingParticipant(2)) winningDisputeCrowdsourcer2 = localFixture.applySignature( 'DisputeCrowdsourcer', market.getReportingParticipant(4)) # Get losing Reporting Participants losingDisputeCrowdsourcer1 = localFixture.applySignature( 'DisputeCrowdsourcer', market.getReportingParticipant(1)) losingDisputeCrowdsourcer2 = localFixture.applySignature( 'DisputeCrowdsourcer', market.getReportingParticipant(3)) # We can't redeem yet as the market isn't finalized with raises(TransactionFailed): initialReporter.redeem(tester.a0) with raises(TransactionFailed): winningDisputeCrowdsourcer1.redeem(tester.a2) # Fast forward time until the new fee window is over and we can receive fees feeWindow = localFixture.applySignature("FeeWindow", market.getFeeWindow()) localFixture.contracts["Time"].setTimestamp(feeWindow.getEndTime() + 1) assert market.finalize() # The initial reporter locked in REP for 5 rounds. expectedInitialReporterFees = getExpectedFees(localFixture, cash, initialReporter, 5) expectedRep = long(initialReporter.getStake() + initialReporter.getStake() / 2) with TokenDelta(reputationToken, expectedRep, tester.a0, "Redeeming didn't refund REP"): with EtherDelta(expectedInitialReporterFees, tester.a0, localFixture.chain, "Redeeming didn't increase ETH correctly"): assert initialReporter.redeem(tester.a0) # The first winning dispute crowdsourcer will get fees for 4 rounds expectedWinningDisputeCrowdsourcer1Fees = getExpectedFees( localFixture, cash, winningDisputeCrowdsourcer1, 4) expectedRep = long(winningDisputeCrowdsourcer1.getStake() + winningDisputeCrowdsourcer1.getStake() / 2) with TokenDelta(reputationToken, expectedRep, tester.a2, "Redeeming didn't refund REP"): with EtherDelta(expectedWinningDisputeCrowdsourcer1Fees, tester.a2, localFixture.chain, "Redeeming didn't increase ETH correctly"): assert winningDisputeCrowdsourcer1.redeem(tester.a2) # The final winning dispute crowdsourcer will get fees for 2 rounds expectedWinningDisputeCrowdsourcer2Fees = getExpectedFees( localFixture, cash, winningDisputeCrowdsourcer2, 2) expectedRep = long(winningDisputeCrowdsourcer2.getStake() + winningDisputeCrowdsourcer2.getStake() / 2) with TokenDelta(reputationToken, expectedRep, tester.a3, "Redeeming didn't refund REP"): with EtherDelta(expectedWinningDisputeCrowdsourcer2Fees, tester.a3, localFixture.chain, "Redeeming didn't increase ETH correctly"): assert winningDisputeCrowdsourcer2.redeem(tester.a3) # The losing reports get fees as well but no REP # The first losing bond has fees from 5 rounds expectedLosingDisputeCrowdsourcer1Fees = getExpectedFees( localFixture, cash, losingDisputeCrowdsourcer1, 5) with TokenDelta(reputationToken, 0, tester.a1, "Redeeming refunded REP"): with EtherDelta(expectedLosingDisputeCrowdsourcer1Fees, tester.a1, localFixture.chain, "Redeeming didn't increase ETH correctly"): assert losingDisputeCrowdsourcer1.redeem(tester.a1) # The second losing bond has fees from 3 rounds expectedLosingDisputeCrowdsourcer2Fees = getExpectedFees( localFixture, cash, losingDisputeCrowdsourcer2, 3) with TokenDelta(reputationToken, 0, tester.a1, "Redeeming refunded REP"): with EtherDelta(expectedLosingDisputeCrowdsourcer2Fees, tester.a1, localFixture.chain, "Redeeming didn't increase ETH correctly"): assert losingDisputeCrowdsourcer2.redeem(tester.a1)
def test_initial_report_and_participation_fee_collection( localFixture, universe, market, categoricalMarket, scalarMarket, cash, reputationToken): feeWindow = localFixture.applySignature('FeeWindow', market.getFeeWindow()) constants = localFixture.contracts["Constants"] # We cannot purchase participation tokens yet since the window isn't active with raises(TransactionFailed): feeWindow.buy(1) # generate some fees generateFees(localFixture, universe, market) # We'll make the window active then purchase some participation tokens localFixture.contracts["Time"].setTimestamp(feeWindow.getStartTime() + 1) feeWindowAmount = 100 with TokenDelta(reputationToken, -feeWindowAmount, tester.a0, "Buying participation tokens didn't deduct REP correctly"): with TokenDelta( feeWindow, feeWindowAmount, tester.a0, "Buying participation tokens didn't increase participation token balance correctly" ): assert feeWindow.buy(feeWindowAmount) # As other testers we'll buy some more assert feeWindow.buy(feeWindowAmount, sender=tester.k1) assert feeWindow.buy(feeWindowAmount, sender=tester.k2) assert feeWindow.buy(feeWindowAmount, sender=tester.k3) # We can't redeem the participation tokens yet since the window isn't over with raises(TransactionFailed): feeWindow.redeem(False) # Now end the window and finalize localFixture.contracts["Time"].setTimestamp(feeWindow.getEndTime() + 1) assert market.finalize() assert categoricalMarket.finalize() assert scalarMarket.finalize() marketInitialReport = localFixture.applySignature( 'InitialReporter', market.getInitialReporter()) categoricalInitialReport = localFixture.applySignature( 'InitialReporter', categoricalMarket.getInitialReporter()) scalarInitialReport = localFixture.applySignature( 'InitialReporter', scalarMarket.getInitialReporter()) reporterFees = 1000 * market.getNumTicks( ) / universe.getOrCacheReportingFeeDivisor() totalStake = feeWindow.getTotalFeeStake() assert cash.balanceOf(feeWindow.address) == reporterFees expectedParticipationFees = reporterFees * feeWindowAmount / totalStake # Cashing out Participation tokens will awards fees proportional to the total winning stake in the window with TokenDelta(reputationToken, feeWindowAmount, tester.a0, "Redeeming participation tokens didn't refund REP"): with TokenDelta( feeWindow, -feeWindowAmount, tester.a0, "Redeeming participation tokens didn't decrease participation token balance correctly" ): with EtherDelta( expectedParticipationFees, tester.a0, localFixture.chain, "Redeeming participation tokens didn't increase ETH correctly" ): assert feeWindow.redeem(tester.a0) with TokenDelta(reputationToken, feeWindowAmount, tester.a1, "Redeeming participation tokens didn't refund REP"): with TokenDelta( feeWindow, -feeWindowAmount, tester.a1, "Redeeming participation tokens didn't decrease participation token balance correctly" ): with EtherDelta( expectedParticipationFees, tester.a1, localFixture.chain, "Redeeming participation tokens didn't increase ETH correctly" ): assert feeWindow.redeem(tester.a1) with TokenDelta(reputationToken, feeWindowAmount, tester.a2, "Redeeming participation tokens didn't refund REP"): with TokenDelta( feeWindow, -feeWindowAmount, tester.a2, "Redeeming participation tokens didn't decrease participation token balance correctly" ): with EtherDelta( expectedParticipationFees, tester.a2, localFixture.chain, "Redeeming participation tokens didn't increase ETH correctly" ): assert feeWindow.redeem(tester.a2) marketStake = marketInitialReport.getStake() expectedFees = reporterFees * marketStake / totalStake initialReporterRedeemedLog = { "reporter": bytesToHexString(tester.a0), "amountRedeemed": marketStake, "repReceived": marketStake, "reportingFeesReceived": expectedFees, "payoutNumerators": [market.getNumTicks(), 0], "universe": universe.address, "market": market.address } with AssertLog(localFixture, "InitialReporterRedeemed", initialReporterRedeemedLog): with TokenDelta(reputationToken, marketStake, tester.a0, "Redeeming didn't refund REP"): with EtherDelta(expectedFees, tester.a0, localFixture.chain, "Redeeming didn't increase ETH correctly"): assert marketInitialReport.redeem(tester.a0) categoricalMarketStake = categoricalInitialReport.getStake() expectedFees = reporterFees * categoricalMarketStake / totalStake with TokenDelta(reputationToken, categoricalMarketStake, tester.a0, "Redeeming didn't refund REP"): with EtherDelta(expectedFees, tester.a0, localFixture.chain, "Redeeming didn't increase ETH correctly"): assert categoricalInitialReport.redeem(tester.a0)
def test_basic_trading(contractsFixture, cash, market, universe): zeroXTradeToken = contractsFixture.contracts['ZeroXTradeToken'] expirationTime = contractsFixture.contracts['Time'].getTimestamp() + 10000 salt = 5 # First we'll create a signed order rawZeroXOrderData, orderHash = zeroXTradeToken.createZeroXOrder( BID, fix(2), 60, market.address, YES, nullAddress, expirationTime, salt) signature = signOrder(orderHash, contractsFixture.privateKeys[0]) zeroXExchange = contractsFixture.applySignature("ZeroXExchange", zeroXTradeToken.exchange()) assert zeroXExchange.isValidSignature(orderHash, contractsFixture.accounts[0], signature) fillAmount = fix(1) affiliateAddress = nullAddress tradeGroupId = longTo32Bytes(42) orders = [rawZeroXOrderData] signatures = [signature] yesShareToken = contractsFixture.applySignature("ShareToken", market.getShareToken(YES)) noShareToken = contractsFixture.applySignature("ShareToken", market.getShareToken(NO)) # Lets take the order as another user and confirm assets are traded assert cash.faucet(fix(1, 60)) assert cash.faucet(fix(1, 40), sender=contractsFixture.accounts[1]) with TokenDelta(yesShareToken, fix(1), contractsFixture.accounts[0], "Tester 0 Shares not received"): with TokenDelta(noShareToken, fix(1), contractsFixture.accounts[1], "Tester 1 Shares not received"): with TokenDelta(cash, -fix(1, 60), contractsFixture.accounts[0], "Tester 0 cash not taken"): with TokenDelta(cash, -fix(1, 40), contractsFixture.accounts[1], "Tester 1 cash not taken"): with PrintGasUsed(contractsFixture, "zeroXTradeToken.trade", 0): amountRemaining = zeroXTradeToken.trade( fillAmount, affiliateAddress, tradeGroupId, orders, signatures, sender=contractsFixture.accounts[1]) assert amountRemaining == 0 # Another user can fill the rest. We'll also ask to fill more than is available and see that we get back the remaining amount desired assert cash.faucet(fix(1, 60)) assert cash.faucet(fix(1, 40), sender=contractsFixture.accounts[2]) amountRemaining = zeroXTradeToken.trade( fillAmount + 1, affiliateAddress, tradeGroupId, orders, signatures, sender=contractsFixture.accounts[2]) assert amountRemaining == 1 # The order is completely filled so further attempts to take it will not actuall result in any trade occuring assert cash.faucet(fix(1, 60)) assert cash.faucet(fix(1, 40), sender=contractsFixture.accounts[1]) with TokenDelta(yesShareToken, 0, contractsFixture.accounts[0], "Tester 0 Shares not received"): with TokenDelta(noShareToken, 0, contractsFixture.accounts[1], "Tester 1 Shares not received"): with TokenDelta(cash, 0, contractsFixture.accounts[0], "Tester 0 cash not taken"): with TokenDelta(cash, 0, contractsFixture.accounts[1], "Tester 1 cash not taken"): zeroXTradeToken.trade(fillAmount, affiliateAddress, tradeGroupId, orders, signatures, sender=contractsFixture.accounts[1])
def test_one_ask_on_books_buy_partial_order(contractsFixture, cash, market, universe): zeroXTradeToken = contractsFixture.contracts['ZeroXTradeToken'] expirationTime = contractsFixture.contracts['Time'].getTimestamp() + 10000 salt = 5 tradeGroupID = longTo32Bytes(42) yesShareToken = contractsFixture.applySignature("ShareToken", market.getShareToken(YES)) noShareToken = contractsFixture.applySignature("ShareToken", market.getShareToken(NO)) # create signed order sender = contractsFixture.accounts[1] senderPrivateKey = contractsFixture.privateKeys[1] cash.faucet(fix('4', '40'), sender=sender) rawZeroXOrderData, orderHash = zeroXTradeToken.createZeroXOrder( ASK, fix(4), 60, market.address, YES, nullAddress, expirationTime, salt, sender=sender) signature = signOrder(orderHash, senderPrivateKey) # fill signed order orderEventLog = { "eventType": 2, "addressData": [ nullAddress, contractsFixture.accounts[1], contractsFixture.accounts[2] ], "uint256Data": [ 60, 0, YES, 0, 0, 0, fix(2), contractsFixture.contracts['Time'].getTimestamp(), 0, 0 ], } orders = [rawZeroXOrderData] signatures = [signature] assert cash.faucet(fix(2, 60), sender=contractsFixture.accounts[2]) with AssertLog(contractsFixture, "OrderEvent", orderEventLog): with TokenDelta(noShareToken, fix(2), sender, "Creator Shares not received"): with TokenDelta(yesShareToken, fix(2), contractsFixture.accounts[2], "Taker Shares not received"): with TokenDelta(cash, -fix(2, 40), sender, "Creator cash not taken"): with TokenDelta(cash, -fix(2, 60), contractsFixture.accounts[2], "Taker cash not taken"): assert zeroXTradeToken.trade( fix(2), nullAddress, tradeGroupID, orders, signatures, sender=contractsFixture.accounts[2]) == 0
def test_uniswap_router(contractsFixture, cash, reputationToken): weth = contractsFixture.contracts["WETH9"] uniswap = contractsFixture.contracts["UniswapV2Router02"] account = contractsFixture.accounts[0] deadline = contractsFixture.eth_tester.backend.chain.header.timestamp + 1000000 # We'll provide some liquidity to the REP/DAI exchange cashAmount = 100 * 10**18 repAmount = 10 * 10**18 cash.faucet(cashAmount) reputationToken.faucet(repAmount) cash.approve(uniswap.address, APPROVAL_AMOUNT) reputationToken.approve(uniswap.address, APPROVAL_AMOUNT) uniswap.addLiquidity(reputationToken.address, cash.address, repAmount, cashAmount, 0, 0, account, deadline) # We'll provide liquidity to the ETH/DAI exchange now cashAmount = 1000 * 10**18 ethAmount = 10 * 10**18 cash.faucet(cashAmount) uniswap.addLiquidityETH(cash.address, cashAmount, cashAmount, ethAmount, account, deadline, value=ethAmount) # Now lets do some swaps. We'll pay some DAI get an exact amount of REP first exactRep = 10**17 maxDAI = 1.1 * 10**18 cash.faucet(maxDAI) with TokenDelta(reputationToken, exactRep, account, "REP token balance wrong"): uniswap.swapTokensForExactTokens( exactRep, maxDAI, [cash.address, reputationToken.address], account, deadline) # Now we'll pay an exact amount of DAI to get some REP exactDAI = 10**18 minRep = .95 * 10**17 cash.faucet(exactDAI) with TokenDelta(cash, -exactDAI, account, "Cash token balance wrong"): uniswap.swapExactTokensForTokens( exactDAI, minRep, [cash.address, reputationToken.address], account, deadline) # Now lets pay some DAI to get an exact amount of ETH. We pay gas to execute this so we subtract a dust value to account for that exactETH = 10**16 cash.faucet(maxDAI) initialETHBalance = contractsFixture.eth_tester.get_balance(account) uniswap.swapTokensForExactETH(exactETH, maxDAI, [cash.address, weth.address], account, deadline) newETHBalance = contractsFixture.eth_tester.get_balance(account) dust = 10**7 assert newETHBalance - initialETHBalance > exactETH - dust # Now we pay an exact amount of DAI to get some ETH minETH = .95 * 10**16 cash.faucet(exactDAI) with TokenDelta(cash, -exactDAI, account, "Cash token balance wrong"): uniswap.swapExactTokensForETH(exactDAI, minETH, [cash.address, weth.address], account, deadline) # Now lets pay some ETH to get an exact amount of DAI. maxETH = 1.1 * 10**16 uniswap.swapETHForExactTokens(exactDAI, [weth.address, cash.address], account, deadline, value=maxETH) # Finally we pay an exact amount of ETH to get some DAI. minDAI = .95 * 10**18 uniswap.swapExactETHForTokens(minDAI, [weth.address, cash.address], account, deadline, value=exactETH)
def test_fee_pot_main(contractsFixture, universe, reputationToken, cash): if not contractsFixture.paraAugur: return feePot = contractsFixture.getFeePot(universe) bob = contractsFixture.accounts[0] alice = contractsFixture.accounts[1] reputationToken.faucet(100, sender=bob) reputationToken.faucet(100, sender=alice) cash.faucet(10000) cash.approve(feePot.address, 10000000000000000) reputationToken.approve(feePot.address, 10000000000000000, sender=bob) reputationToken.approve(feePot.address, 10000000000000000, sender=alice) # Put 10 Cash in feePot.depositFees(10) # Alice puts in 1 REP feePot.stake(1, sender=alice) # Nothing owed yet assert feePot.withdrawableFeesOf(alice) == 0 # Put 10 Cash in feePot.depositFees(10) # Alice owed all 20 assert feePot.withdrawableFeesOf(alice) == 20 # Put 10 Cash in feePot.depositFees(10) # Alice owed all 30 assert feePot.withdrawableFeesOf(alice) == 30 # Bob puts in 2 REP feePot.stake(2, sender=bob) # Alice still owed all 30 assert feePot.withdrawableFeesOf(alice) == 30 # Bob owed nothing assert feePot.withdrawableFeesOf(bob) == 0 # Put 30 Cash in feePot.depositFees(30) # Alice owed 40 assert feePot.withdrawableFeesOf(alice) == 40 # Bob owed 20 assert feePot.withdrawableFeesOf(bob) == 20 # Alice redeems and gets 40 Cash and 0 REP with TokenDelta(reputationToken, 0, alice, "Alice got REP back for redeeming incorrectly"): with TokenDelta(cash, 40, alice, "Alice didnt get fees"): feePot.redeem(sender=alice) # Alice owed 0 assert feePot.withdrawableFeesOf(alice) == 0 # Bob owed 20 assert feePot.withdrawableFeesOf(bob) == 20 # Bob transfers 1 REP to Alice feePot.transfer(alice, 1, sender=bob) # Alice owed 0 assert feePot.withdrawableFeesOf(alice) == 0 # Bob owed 20 assert feePot.withdrawableFeesOf(bob) == 20 # Put in 90 feePot.depositFees(90) # Alice owed 60 assert feePot.withdrawableFeesOf(alice) == 60 # Bob owed 50 assert feePot.withdrawableFeesOf(bob) == 50 # Bob exits and gets 30 Cash and 1 REP with TokenDelta(reputationToken, 1, bob, "Bob didn't get back REP"): with TokenDelta(cash, 50, bob, "Bob didnt get fees"): feePot.exit(1, sender=bob) # Alice owed 60 assert feePot.withdrawableFeesOf(alice) == 60 # Bob owed 0 assert feePot.withdrawableFeesOf(bob) == 0
def test_redeem_shares_affiliate(kitchenSinkFixture, universe, cash, market): affiliates = kitchenSinkFixture.contracts['Affiliates'] shareToken = kitchenSinkFixture.contracts['ShareToken'] expectedValue = 100 * market.getNumTicks() expectedReporterFees = expectedValue / universe.getOrCacheReportingFeeDivisor( ) expectedMarketCreatorFees = expectedValue / market.getMarketCreatorSettlementFeeDivisor( ) expectedSettlementFees = expectedReporterFees + expectedMarketCreatorFees expectedPayout = expectedValue - expectedSettlementFees expectedAffiliateFees = expectedMarketCreatorFees / market.affiliateFeeDivisor( ) expectedMarketCreatorFees = expectedMarketCreatorFees - expectedAffiliateFees sourceKickback = expectedAffiliateFees / 5 expectedAffiliateFees -= sourceKickback fingerprint = longTo32Bytes(11) assert universe.getOpenInterestInAttoCash() == 0 affiliateAddress = kitchenSinkFixture.accounts[5] affiliates.setReferrer(affiliateAddress, longTo32Bytes(0), sender=kitchenSinkFixture.accounts[1]) affiliates.setReferrer(affiliateAddress, longTo32Bytes(0), sender=kitchenSinkFixture.accounts[2]) # get YES shares with a1 acquireLongShares(kitchenSinkFixture, cash, market, YES, 100, shareToken.address, sender=kitchenSinkFixture.accounts[1]) # get NO shares with a2 acquireShortShareSet(kitchenSinkFixture, cash, market, YES, 100, shareToken.address, sender=kitchenSinkFixture.accounts[2]) finalizeMarket(kitchenSinkFixture, market, [0, 0, 10**2]) with TokenDelta(cash, expectedMarketCreatorFees, market.getOwner(), "market creator fees not paid"): with TokenDelta(cash, expectedAffiliateFees, affiliateAddress, "affiliate fees not paid"): # redeem shares with a1 shareToken.claimTradingProceeds(market.address, kitchenSinkFixture.accounts[1], fingerprint) # redeem shares with a2 shareToken.claimTradingProceeds(market.address, kitchenSinkFixture.accounts[2], fingerprint) # assert a1 ends up with cash (minus fees) and a2 does not assert cash.balanceOf( kitchenSinkFixture.accounts[1]) == expectedPayout + sourceKickback assert shareToken.balanceOfMarketOutcome( market.address, YES, kitchenSinkFixture.accounts[1]) == 0 assert shareToken.balanceOfMarketOutcome( market.address, YES, kitchenSinkFixture.accounts[2]) == 0 assert shareToken.balanceOfMarketOutcome( market.address, NO, kitchenSinkFixture.accounts[1]) == 0
def test_forking(finalizeByMigration, manuallyDisavow, localFixture, universe, market, cash, categoricalMarket, scalarMarket): claimTradingProceeds = localFixture.contracts["ClaimTradingProceeds"] # Let's go into the one dispute round for the categorical market proceedToNextRound(localFixture, categoricalMarket) proceedToNextRound(localFixture, categoricalMarket) # proceed to forking proceedToFork(localFixture, market, universe) with raises(TransactionFailed): universe.fork() with raises(TransactionFailed): categoricalMarket.migrateThroughOneFork( [0, 0, 0, categoricalMarket.getNumTicks()], "") with raises(TransactionFailed): time = localFixture.contracts["Time"].getTimestamp() localFixture.createYesNoMarket(universe, time + 1000, 1, 0, localFixture.accounts[0]) # confirm that we can manually create a child universe from an outcome no one asserted was true during dispute numTicks = market.getNumTicks() childUniverse = universe.createChildUniverse( [0, numTicks / 4, numTicks * 3 / 4]) # confirm that before the fork is finalized we can redeem stake in other markets crowdsourcers, which are disavowable categoricalDisputeCrowdsourcer = localFixture.applySignature( "DisputeCrowdsourcer", categoricalMarket.getReportingParticipant(1)) # confirm we cannot liquidate it with raises(TransactionFailed): categoricalDisputeCrowdsourcer.liquidateLosing() # confirm we cannot fork it with raises(TransactionFailed): categoricalDisputeCrowdsourcer.forkAndRedeem() if manuallyDisavow: marketParticipantsDisavowedLog = { "universe": universe.address, "market": categoricalMarket.address, } with AssertLog(localFixture, "MarketParticipantsDisavowed", marketParticipantsDisavowedLog): assert categoricalMarket.disavowCrowdsourcers() # We can redeem before the fork finalizes since disavowal has occured assert categoricalDisputeCrowdsourcer.redeem(localFixture.accounts[0]) # We cannot contribute to a crowdsourcer during a fork with raises(TransactionFailed): categoricalMarket.contribute( [0, 2, 2, categoricalMarket.getNumTicks() - 4], 1, "") # We cannot purchase new Participation Tokens during a fork disputeWindowAddress = universe.getOrCreateCurrentDisputeWindow(False) disputeWindow = localFixture.applySignature("DisputeWindow", disputeWindowAddress) # finalize the fork finalize(localFixture, market, universe, finalizeByMigration) # We cannot contribute to a crowdsourcer in a forked universe with raises(TransactionFailed): categoricalMarket.contribute( [0, 2, 2, categoricalMarket.getNumTicks() - 4], 1, "") newUniverseAddress = universe.getWinningChildUniverse() newUniverse = localFixture.applySignature("Universe", newUniverseAddress) # Let's make sure fork payouts work correctly for the forking market completeSets = localFixture.contracts['CompleteSets'] numSets = 10 cost = market.getNumTicks() * numSets with BuyWithCash(cash, cost, localFixture.accounts[0], "buy complete set"): assert completeSets.publicBuyCompleteSets(market.address, numSets) yesShare = localFixture.applySignature("ShareToken", market.getShareToken(YES)) noShare = localFixture.applySignature("ShareToken", market.getShareToken(NO)) noShare.transfer(localFixture.accounts[1], noShare.balanceOf(localFixture.accounts[0])) expectedYesOutcomePayout = newUniverse.payoutNumerators(YES) expectedNoOutcomePayout = newUniverse.payoutNumerators(NO) expectedYesPayout = expectedYesOutcomePayout * yesShare.balanceOf( localFixture.accounts[0] ) * .99 # to account for fees (creator fee goes to the claimer in this case) with TokenDelta(cash, expectedYesPayout, localFixture.accounts[0], "Payout for Yes Shares was wrong in forking market"): claimTradingProceeds.claimTradingProceeds(market.address, localFixture.accounts[0]) expectedNoPayout = expectedNoOutcomePayout * noShare.balanceOf( localFixture.accounts[1]) * .98 # to account for fees with TokenDelta(cash, expectedNoPayout, localFixture.accounts[1], "Payout for No Shares was wrong in forking market"): claimTradingProceeds.claimTradingProceeds(market.address, localFixture.accounts[1]) # buy some complete sets to change OI of the cat market numSets = 10 cost = categoricalMarket.getNumTicks() * numSets with BuyWithCash(cash, cost, localFixture.accounts[0], "buy complete set"): assert completeSets.publicBuyCompleteSets(categoricalMarket.address, numSets) assert universe.getOpenInterestInAttoCash() == cost marketMigratedLog = { "market": categoricalMarket.address, "newUniverse": newUniverseAddress, "originalUniverse": universe.address, } with AssertLog(localFixture, "MarketMigrated", marketMigratedLog): assert categoricalMarket.migrateThroughOneFork( [0, 0, 0, categoricalMarket.getNumTicks()], "") assert universe.getOpenInterestInAttoCash() == 0 # The dispute crowdsourcer has been disavowed newUniverse = localFixture.applySignature("Universe", categoricalMarket.getUniverse()) assert newUniverse.address != universe.address assert categoricalDisputeCrowdsourcer.isDisavowed() assert not universe.isContainerForReportingParticipant( categoricalDisputeCrowdsourcer.address) assert not newUniverse.isContainerForReportingParticipant( categoricalDisputeCrowdsourcer.address) assert newUniverse.getOpenInterestInAttoCash() == cost # The initial report is still present however categoricalInitialReport = localFixture.applySignature( "InitialReporter", categoricalMarket.getReportingParticipant(0)) assert categoricalMarket.getReportingParticipant( 0) == categoricalInitialReport.address assert not categoricalInitialReport.isDisavowed() assert not universe.isContainerForReportingParticipant( categoricalInitialReport.address) assert newUniverse.isContainerForReportingParticipant( categoricalInitialReport.address) # The categorical market has a new dispute window since it was initially reported on and may be disputed now categoricalMarketDisputeWindowAddress = categoricalMarket.getDisputeWindow( ) categoricalMarketDisputeWindow = localFixture.applySignature( "DisputeWindow", categoricalMarketDisputeWindowAddress) proceedToNextRound(localFixture, categoricalMarket) # We will finalize the categorical market in the new universe disputeWindow = localFixture.applySignature( 'DisputeWindow', categoricalMarket.getDisputeWindow()) localFixture.contracts["Time"].setTimestamp(disputeWindow.getEndTime() + 1) assert categoricalMarket.finalize() # We can migrate a market that has not had its initial reporting completed as well, and confirm that the report is now made in the new universe reputationToken = localFixture.applySignature( "ReputationToken", universe.getReputationToken()) previousREPBalance = reputationToken.balanceOf(scalarMarket.address) assert previousREPBalance > 0 newReputationToken = localFixture.applySignature( "ReputationToken", newUniverse.getReputationToken()) with TokenDelta(reputationToken, previousREPBalance, scalarMarket.repBondOwner(), "Market did not transfer rep balance to rep bond owner"): with TokenDelta(newReputationToken, -newUniverse.getOrCacheMarketRepBond(), localFixture.accounts[0], "Migrator did not pay new REP bond"): assert scalarMarket.migrateThroughOneFork( [0, 0, scalarMarket.getNumTicks()], "") newUniverseREP = localFixture.applySignature( "ReputationToken", newUniverse.getReputationToken()) initialReporter = localFixture.applySignature( 'InitialReporter', scalarMarket.getInitialReporter()) assert newUniverseREP.balanceOf( initialReporter.address ) == newUniverse.getOrCacheDesignatedReportNoShowBond() # We cannot migrate legacy REP to the new Universe REP legacyRepToken = localFixture.applySignature( 'LegacyReputationToken', newUniverseREP.getLegacyRepToken()) assert legacyRepToken.faucet(500) totalSupply = legacyRepToken.balanceOf(localFixture.accounts[0]) legacyRepToken.approve(newUniverseREP.address, totalSupply) with raises(TransactionFailed): newUniverseREP.migrateFromLegacyReputationToken() # We can finalize this market as well proceedToNextRound(localFixture, scalarMarket) disputeWindow = localFixture.applySignature( 'DisputeWindow', scalarMarket.getDisputeWindow()) localFixture.contracts["Time"].setTimestamp(disputeWindow.getEndTime() + 1) assert scalarMarket.finalize()
def test_initialReport_methods(localFixture, universe, market, constants): reputationToken = localFixture.applySignature( "ReputationToken", universe.getReputationToken()) # proceed to the initial reporting period proceedToInitialReporting(localFixture, market) # do an initial report as someone other than the designated reporter assert market.doInitialReport([0, 0, market.getNumTicks()], "", 0, sender=localFixture.accounts[1]) # the market is now assigned a dispute window newDisputeWindowAddress = market.getDisputeWindow() assert newDisputeWindowAddress disputeWindow = localFixture.applySignature('DisputeWindow', newDisputeWindowAddress) # time marches on and the market can be finalized localFixture.contracts["Time"].setTimestamp(disputeWindow.getEndTime() + 1) assert market.finalize() # We can see that the market reports the designated reporter did not show assert not market.designatedReporterShowed() # Let's get a reference to the Initial Reporter bond and transfer it to the original designated reporter account initialReporter = localFixture.applySignature("InitialReporter", market.getInitialReporter()) transferLog = { "universe": universe.address, "market": market.address, "from": localFixture.accounts[1], "to": initialReporter.getDesignatedReporter(), } with AssertLog(localFixture, "InitialReporterTransferred", transferLog): assert initialReporter.transferOwnership( initialReporter.getDesignatedReporter(), sender=localFixture.accounts[1]) # Transfering to the owner is a noop assert initialReporter.transferOwnership( initialReporter.getDesignatedReporter()) # The market still correctly indicates the designated reporter did not show up assert not market.designatedReporterShowed() # confirm we cannot call protected methods on the initial reporter which only the market may use with raises(TransactionFailed): initialReporter.report(localFixture.accounts[0], "", [], 1) with raises(TransactionFailed): initialReporter.returnRepFromDisavow() with raises(TransactionFailed): initialReporter.migrateToNewUniverse(localFixture.accounts[0]) # When we redeem the initialReporter it goes to the correct party as well expectedRep = initialReporter.getStake() owner = initialReporter.getOwner() with TokenDelta(reputationToken, expectedRep, owner, "Redeeming didn't refund REP"): assert initialReporter.redeem(owner)
def test_failed_crowdsourcer_fees(finalize, localFixture, universe, market, cash, reputationToken): feeWindow = localFixture.applySignature('FeeWindow', market.getFeeWindow()) # generate some fees generateFees(localFixture, universe, market) # We'll make the window active localFixture.contracts["Time"].setTimestamp(feeWindow.getStartTime() + 1) # generate some fees generateFees(localFixture, universe, market) # We'll have testers contribute to a dispute but not reach the target amount = market.getParticipantStake() # confirm we can contribute 0 assert market.contribute([1, market.getNumTicks() - 1], False, 0, sender=tester.k1) with TokenDelta(reputationToken, -amount + 1, tester.a1, "Disputing did not reduce REP balance correctly"): assert market.contribute([1, market.getNumTicks() - 1], False, amount - 1, sender=tester.k1) with TokenDelta(reputationToken, -amount + 1, tester.a2, "Disputing did not reduce REP balance correctly"): assert market.contribute([1, market.getNumTicks() - 1], False, amount - 1, sender=tester.k2) assert market.getFeeWindow() == feeWindow.address payoutDistributionHash = market.derivePayoutDistributionHash( [1, market.getNumTicks() - 1], False) failedCrowdsourcer = localFixture.applySignature( "DisputeCrowdsourcer", market.getCrowdsourcer(payoutDistributionHash)) # confirm we cannot contribute directly to a crowdsourcer without going through the market with raises(TransactionFailed): failedCrowdsourcer.contribute(tester.a0, 1) if finalize: # Fast forward time until the fee window is over and we can redeem to recieve the REP back and fees localFixture.contracts["Time"].setTimestamp(feeWindow.getEndTime() + 1) expectedTotalFees = getExpectedFees(localFixture, cash, failedCrowdsourcer, 1) else: # Continue to the next round which will disavow failed crowdsourcers and let us redeem once the window is over market.contribute([0, market.getNumTicks()], False, amount * 2) assert market.getFeeWindow() != feeWindow.address localFixture.contracts["Time"].setTimestamp(feeWindow.getEndTime() + 1) expectedTotalFees = getExpectedFees(localFixture, cash, failedCrowdsourcer, 1) with TokenDelta(reputationToken, amount - 1, tester.a1, "Redeeming did not refund REP"): with EtherDelta(expectedTotalFees / 2, tester.a1, localFixture.chain, "Redeeming didn't increase ETH correctly"): assert failedCrowdsourcer.redeem(tester.a1) with TokenDelta(reputationToken, amount - 1, tester.a2, "Redeeming did not refund REP"): with EtherDelta(cash.balanceOf(failedCrowdsourcer.address), tester.a2, localFixture.chain, "Redeeming didn't increase ETH correctly"): assert failedCrowdsourcer.redeem(tester.a2)
def test_two_asks_on_books_buy_full_and_partial(contractsFixture, cash, market): zeroXTradeToken = contractsFixture.contracts['ZeroXTradeToken'] expirationTime = contractsFixture.contracts['Time'].getTimestamp() + 10000 salt = 5 tradeGroupID = longTo32Bytes(42) yesShareToken = contractsFixture.applySignature("ShareToken", market.getShareToken(YES)) noShareToken = contractsFixture.applySignature("ShareToken", market.getShareToken(NO)) # create signed order 1 cash.faucet(fix('1', '40'), sender=contractsFixture.accounts[1]) rawZeroXOrderData1, orderHash1 = zeroXTradeToken.createZeroXOrder( ASK, fix(1), 60, market.address, YES, nullAddress, expirationTime, salt, sender=contractsFixture.accounts[1]) signature1 = signOrder(orderHash1, contractsFixture.privateKeys[1]) # create signed order 2 cash.faucet(fix('4', '40'), sender=contractsFixture.accounts[3]) rawZeroXOrderData2, orderHash2 = zeroXTradeToken.createZeroXOrder( ASK, fix(4), 60, market.address, YES, nullAddress, expirationTime, salt, sender=contractsFixture.accounts[3]) signature2 = signOrder(orderHash2, contractsFixture.privateKeys[3]) orders = [rawZeroXOrderData1, rawZeroXOrderData2] signatures = [signature1, signature2] # fill signed orders cash.faucet(fix('3', '60'), sender=contractsFixture.accounts[2]) with TokenDelta(noShareToken, fix(1), contractsFixture.accounts[1], "Creator Shares not received"): with TokenDelta(noShareToken, fix(2), contractsFixture.accounts[3], "Creator Shares not received"): with TokenDelta(yesShareToken, fix(3), contractsFixture.accounts[2], "Taker Shares not received"): with TokenDelta(cash, -fix(1, 40), contractsFixture.accounts[1], "Creator cash not taken"): with TokenDelta(cash, -fix(2, 40), contractsFixture.accounts[3], "Creator cash not taken"): with TokenDelta(cash, -fix(3, 60), contractsFixture.accounts[2], "Taker cash not taken"): assert zeroXTradeToken.trade( fix(3), nullAddress, tradeGroupID, orders, signatures, sender=contractsFixture.accounts[2]) == 0
def test_one_round_crowdsourcer_fees(localFixture, universe, market, cash, reputationToken): feeWindow = localFixture.applySignature('FeeWindow', market.getFeeWindow()) constants = localFixture.contracts["Constants"] # We'll make the window active localFixture.contracts["Time"].setTimestamp(feeWindow.getStartTime() + 1) # generate some fees generateFees(localFixture, universe, market) # We'll have testers push markets into the next round by funding dispute crowdsourcers amount = 2 * market.getParticipantStake() with TokenDelta(reputationToken, -amount, tester.a1, "Disputing did not reduce REP balance correctly"): assert market.contribute([0, market.getNumTicks()], False, amount, sender=tester.k1) newFeeWindowAddress = market.getFeeWindow() assert newFeeWindowAddress != feeWindow.address # fast forward time to the fee new window and generate additional fees feeWindow = localFixture.applySignature('FeeWindow', newFeeWindowAddress) localFixture.contracts["Time"].setTimestamp(feeWindow.getStartTime() + 1) # Fast forward time until the new fee window is over and we can redeem our winning stake, and dispute bond tokens and receive fees localFixture.contracts["Time"].setTimestamp(feeWindow.getEndTime() + 1) assert market.finalize() initialReporter = localFixture.applySignature( 'InitialReporter', market.getReportingParticipant(0)) marketDisputeCrowdsourcer = localFixture.applySignature( 'DisputeCrowdsourcer', market.getReportingParticipant(1)) # The dispute crowdsourcer contributor locked in REP for 2 rounds, as did the Initial Reporter expectedTotalFees = cash.balanceOf(feeWindow.address) + cash.balanceOf( universe.getOrCreateFeeWindowBefore(feeWindow.address)) expectedFees = expectedTotalFees * 2 / 3 expectedRep = market.getParticipantStake() assert expectedRep == long(marketDisputeCrowdsourcer.getStake() + marketDisputeCrowdsourcer.getStake() / 2) disputeCrowdsourcerRedeemedLog = { "reporter": bytesToHexString(tester.a1), "disputeCrowdsourcer": marketDisputeCrowdsourcer.address, "amountRedeemed": marketDisputeCrowdsourcer.getStake(), "repReceived": expectedRep, "reportingFeesReceived": expectedFees, "payoutNumerators": [0, market.getNumTicks()], "universe": universe.address, "market": market.address } with AssertLog(localFixture, "DisputeCrowdsourcerRedeemed", disputeCrowdsourcerRedeemedLog): with TokenDelta(reputationToken, expectedRep, tester.a1, "Redeeming didn't refund REP"): with EtherDelta(expectedFees, tester.a1, localFixture.chain, "Redeeming didn't increase ETH correctly"): assert marketDisputeCrowdsourcer.redeem(tester.a1, sender=tester.k1) # The initial reporter gets fees even though they were not correct. They do not get their REP back though expectedFees = cash.balanceOf(feeWindow.address) + cash.balanceOf( universe.getOrCreateFeeWindowBefore(feeWindow.address)) with TokenDelta(reputationToken, 0, tester.a0, "Redeeming didn't refund REP"): with EtherDelta(expectedFees, tester.a0, localFixture.chain, "Redeeming didn't increase ETH correctly"): assert initialReporter.redeem(tester.a0)
def test_take_best_order_with_shares_escrowed_buy_with_shares_categorical( contractsFixture, cash, categoricalMarket, universe): market = categoricalMarket zeroXTradeToken = contractsFixture.contracts['ZeroXTradeToken'] completeSets = contractsFixture.contracts['CompleteSets'] expirationTime = contractsFixture.contracts['Time'].getTimestamp() + 10000 salt = 5 tradeGroupID = longTo32Bytes(42) firstShareToken = contractsFixture.applySignature('ShareToken', market.getShareToken(0)) secondShareToken = contractsFixture.applySignature('ShareToken', market.getShareToken(1)) thirdShareToken = contractsFixture.applySignature('ShareToken', market.getShareToken(2)) # buy complete sets for both users numTicks = market.getNumTicks() with BuyWithCash(cash, fix('1', numTicks), contractsFixture.accounts[1], "buy complete set"): assert completeSets.publicBuyCompleteSets( market.address, fix(1), sender=contractsFixture.accounts[1]) with BuyWithCash(cash, fix('1', numTicks), contractsFixture.accounts[2], "buy complete set"): assert completeSets.publicBuyCompleteSets( market.address, fix(1), sender=contractsFixture.accounts[2]) assert firstShareToken.balanceOf( contractsFixture.accounts[1]) == firstShareToken.balanceOf( contractsFixture.accounts[2]) == fix(1) assert secondShareToken.balanceOf( contractsFixture.accounts[1]) == secondShareToken.balanceOf( contractsFixture.accounts[2]) == fix(1) assert thirdShareToken.balanceOf( contractsFixture.accounts[1]) == thirdShareToken.balanceOf( contractsFixture.accounts[2]) == fix(1) # create signed order rawZeroXOrderData, orderHash = zeroXTradeToken.createZeroXOrder( ASK, fix(1), 60, market.address, 0, nullAddress, expirationTime, salt, sender=contractsFixture.accounts[1]) signature = signOrder(orderHash, contractsFixture.privateKeys[1]) # fill order with shares and see payouts occur orders = [rawZeroXOrderData] signatures = [signature] totalProceeds = fix(1, numTicks) totalProceeds -= fix( 1, numTicks) / market.getMarketCreatorSettlementFeeDivisor() totalProceeds -= fix(1, numTicks) / universe.getOrCacheReportingFeeDivisor() expectedTester1Payout = totalProceeds * 60 / numTicks expectedTester2Payout = totalProceeds * (numTicks - 60) / numTicks with TokenDelta(cash, expectedTester1Payout, contractsFixture.accounts[1], "Tester 1 Cash delta wrong"): with TokenDelta(cash, expectedTester2Payout, contractsFixture.accounts[2], "Tester 2 Cash delta wrong"): assert zeroXTradeToken.trade( fix(1), nullAddress, tradeGroupID, orders, signatures, sender=contractsFixture.accounts[2]) == 0 assert firstShareToken.balanceOf(contractsFixture.accounts[1]) == 0 assert secondShareToken.balanceOf(contractsFixture.accounts[1]) == fix(1) assert thirdShareToken.balanceOf(contractsFixture.accounts[1]) == fix(1) assert firstShareToken.balanceOf(contractsFixture.accounts[2]) == fix(1) assert secondShareToken.balanceOf(contractsFixture.accounts[2]) == 0 assert thirdShareToken.balanceOf(contractsFixture.accounts[2]) == 0
def test_forkAndRedeem(localFixture, universe, market, categoricalMarket, cash, reputationToken): # Let's do some initial disputes for the categorical market proceedToNextRound(localFixture, categoricalMarket, tester.k1, moveTimeForward=False) # Get to a fork testers = [tester.k0, tester.k1, tester.k2, tester.k3] testerIndex = 1 while (market.getForkingMarket() == longToHexString(0)): proceedToNextRound(localFixture, market, testers[testerIndex], True) testerIndex += 1 testerIndex = testerIndex % len(testers) # Have the participants fork and create new child universes for i in range(market.getNumParticipants()): reportingParticipant = localFixture.applySignature( "DisputeCrowdsourcer", market.getReportingParticipant(i)) # Finalize the fork finalizeFork(localFixture, market, universe) categoricalDisputeCrowdsourcer = localFixture.applySignature( "DisputeCrowdsourcer", categoricalMarket.getReportingParticipant(1)) # Migrate the categorical market into the winning universe. This will disavow the dispute crowdsourcer on it, letting us redeem for original universe rep and eth assert categoricalMarket.migrateThroughOneFork() expectedRep = categoricalDisputeCrowdsourcer.getStake() expectedEth = getExpectedFees(localFixture, cash, categoricalDisputeCrowdsourcer, 2) with EtherDelta(expectedEth, tester.a1, localFixture.chain, "Redeeming didn't increase ETH correctly"): with TokenDelta(reputationToken, expectedRep, tester.a1, "Redeeming didn't increase REP correctly"): categoricalDisputeCrowdsourcer.redeem(tester.a1) noPayoutNumerators = [0] * market.getNumberOfOutcomes() noPayoutNumerators[0] = market.getNumTicks() yesPayoutNumerators = noPayoutNumerators[::-1] noUniverse = localFixture.applySignature( 'Universe', universe.createChildUniverse(noPayoutNumerators, False)) yesUniverse = localFixture.applySignature( 'Universe', universe.createChildUniverse(yesPayoutNumerators, False)) noUniverseReputationToken = localFixture.applySignature( 'ReputationToken', noUniverse.getReputationToken()) yesUniverseReputationToken = localFixture.applySignature( 'ReputationToken', yesUniverse.getReputationToken()) # Now we'll fork and redeem the reporting participants for i in range(market.getNumParticipants()): account = localFixture.testerAddress[i % 4] key = localFixture.testerKey[i % 4] reportingParticipant = localFixture.applySignature( "DisputeCrowdsourcer", market.getReportingParticipant(i)) expectedRep = reportingParticipant.getStake() expectedRep += expectedRep / localFixture.contracts[ "Constants"].FORK_MIGRATION_PERCENTAGE_BONUS_DIVISOR() expectedRep += reportingParticipant.getStake() / 2 repToken = noUniverseReputationToken if i % 2 == 0 else yesUniverseReputationToken with TokenDelta( repToken, expectedRep, account, "Redeeming didn't increase REP correctly for " + str(i)): assert reportingParticipant.forkAndRedeem(sender=key)
def test_fees_from_trades(finalized, invalid, contractsFixture, cash, market, universe): zeroXTradeToken = contractsFixture.contracts['ZeroXTradeToken'] completeSets = contractsFixture.contracts['CompleteSets'] expirationTime = contractsFixture.contracts['Time'].getTimestamp() + 10000 salt = 5 tradeGroupID = longTo32Bytes(42) completeSets = contractsFixture.contracts['CompleteSets'] firstShareToken = contractsFixture.applySignature('ShareToken', market.getShareToken(0)) secondShareToken = contractsFixture.applySignature('ShareToken', market.getShareToken(1)) if finalized: if invalid: contractsFixture.contracts["Time"].setTimestamp( market.getDesignatedReportingEndTime() + 1) market.doInitialReport([market.getNumTicks(), 0, 0], "", 0) else: proceedToNextRound(contractsFixture, market) disputeWindow = contractsFixture.applySignature( 'DisputeWindow', market.getDisputeWindow()) contractsFixture.contracts["Time"].setTimestamp( disputeWindow.getEndTime() + 1) assert market.finalize() # buy complete sets for both users numTicks = market.getNumTicks() with BuyWithCash(cash, fix('1', numTicks), contractsFixture.accounts[1], "buy complete set"): assert completeSets.publicBuyCompleteSets( market.address, fix(1), sender=contractsFixture.accounts[1]) with BuyWithCash(cash, fix('1', numTicks), contractsFixture.accounts[2], "buy complete set"): assert completeSets.publicBuyCompleteSets( market.address, fix(1), sender=contractsFixture.accounts[2]) assert firstShareToken.balanceOf( contractsFixture.accounts[1]) == firstShareToken.balanceOf( contractsFixture.accounts[2]) == fix(1) assert secondShareToken.balanceOf( contractsFixture.accounts[1]) == secondShareToken.balanceOf( contractsFixture.accounts[2]) == fix(1) # create order with shares rawZeroXOrderData, orderHash = zeroXTradeToken.createZeroXOrder( ASK, fix(1), 60, market.address, 0, nullAddress, expirationTime, salt, sender=contractsFixture.accounts[1]) signature = signOrder(orderHash, contractsFixture.privateKeys[1]) orders = [rawZeroXOrderData] signatures = [signature] expectedAffiliateFees = fix(100) / 400 cash.faucet(fix(60), sender=contractsFixture.accounts[2]) # Trade and specify an affiliate address. if finalized: if invalid: nextDisputeWindowAddress = universe.getOrCreateNextDisputeWindow( False) totalFees = fix(100) / 50 # Market fees + reporting fees with TokenDelta(cash, totalFees, nextDisputeWindowAddress, "Dispute Window did not recieve the correct fees"): assert zeroXTradeToken.trade( fix(1), contractsFixture.accounts[3], tradeGroupID, orders, signatures, sender=contractsFixture.accounts[2]) == 0 else: with TokenDelta(cash, expectedAffiliateFees, contractsFixture.accounts[3], "Affiliate did not recieve the correct fees"): assert zeroXTradeToken.trade( fix(1), contractsFixture.accounts[3], tradeGroupID, orders, signatures, sender=contractsFixture.accounts[2]) == 0 else: assert zeroXTradeToken.trade(fix(1), contractsFixture.accounts[3], tradeGroupID, orders, signatures, sender=contractsFixture.accounts[2]) == 0 assert firstShareToken.balanceOf(contractsFixture.accounts[1]) == 0 assert secondShareToken.balanceOf(contractsFixture.accounts[1]) == fix(1) # The second user sold the complete set they ended up holding from this transaction, which extracts fees assert firstShareToken.balanceOf(contractsFixture.accounts[2]) == fix(1) assert secondShareToken.balanceOf(contractsFixture.accounts[2]) == fix(0) if not finalized: # We can confirm that the 3rd test account has an affiliate fee balance of 25% of the market creator fee 1% taken from the 1 ETH order assert market.affiliateFeesAttoCash( contractsFixture.accounts[3]) == expectedAffiliateFees # The affiliate can withdraw their fees only after the market is finalized as valid with raises(TransactionFailed): market.withdrawAffiliateFees(contractsFixture.accounts[3]) if invalid: contractsFixture.contracts["Time"].setTimestamp( market.getDesignatedReportingEndTime() + 1) market.doInitialReport([market.getNumTicks(), 0, 0], "", 0) else: proceedToNextRound(contractsFixture, market) disputeWindow = contractsFixture.applySignature( 'DisputeWindow', market.getDisputeWindow()) contractsFixture.contracts["Time"].setTimestamp( disputeWindow.getEndTime() + 1) totalCollectedFees = market.marketCreatorFeesAttoCash( ) + market.totalAffiliateFeesAttoCash() + market.validityBondAttoCash( ) nextDisputeWindowAddress = universe.getOrCreateNextDisputeWindow(False) nextDisputeWindowBalanceBeforeFinalization = cash.balanceOf( universe.getOrCreateNextDisputeWindow(False)) assert market.finalize() if invalid: with raises(TransactionFailed): market.withdrawAffiliateFees(contractsFixture.accounts[3]) assert cash.balanceOf( universe.getOrCreateNextDisputeWindow(False) ) == nextDisputeWindowBalanceBeforeFinalization + totalCollectedFees else: with TokenDelta(cash, expectedAffiliateFees, contractsFixture.accounts[3], "Affiliate did not recieve the correct fees"): market.withdrawAffiliateFees(contractsFixture.accounts[3]) # No more fees can be withdrawn if not invalid: with TokenDelta(cash, 0, contractsFixture.accounts[3], "Affiliate double received fees"): market.withdrawAffiliateFees(contractsFixture.accounts[3])
def test_redeem_shares_in_binary_market(kitchenSinkFixture, universe, cash, market): claimTradingProceeds = kitchenSinkFixture.contracts['ClaimTradingProceeds'] yesShareToken = kitchenSinkFixture.applySignature( 'ShareToken', market.getShareToken(YES)) noShareToken = kitchenSinkFixture.applySignature('ShareToken', market.getShareToken(NO)) expectedValue = 1 * market.getNumTicks() expectedReporterFees = expectedValue / universe.getOrCacheReportingFeeDivisor( ) expectedMarketCreatorFees = expectedValue / market.getMarketCreatorSettlementFeeDivisor( ) expectedSettlementFees = expectedReporterFees + expectedMarketCreatorFees expectedPayout = long(expectedValue - expectedSettlementFees) assert universe.getOpenInterestInAttoEth() == 0 # get YES shares with a1 acquireLongShares(kitchenSinkFixture, cash, market, YES, 1, claimTradingProceeds.address, sender=tester.k1) assert universe.getOpenInterestInAttoEth() == 1 * market.getNumTicks() # get NO shares with a2 acquireShortShareSet(kitchenSinkFixture, cash, market, YES, 1, claimTradingProceeds.address, sender=tester.k2) assert universe.getOpenInterestInAttoEth() == 2 * market.getNumTicks() finalizeMarket(kitchenSinkFixture, market, [0, 10**4]) initialLongHolderETH = kitchenSinkFixture.chain.head_state.get_balance( tester.a1) initialShortHolderETH = kitchenSinkFixture.chain.head_state.get_balance( tester.a2) tradingProceedsClaimedLog = { 'market': market.address, 'shareToken': yesShareToken.address, 'numPayoutTokens': expectedPayout, 'numShares': 1, 'sender': bytesToHexString(tester.a1), 'finalTokenBalance': initialLongHolderETH + expectedPayout, } with TokenDelta(cash, expectedMarketCreatorFees, market.getMarketCreatorMailbox(), "Market creator fees not paid"): with TokenDelta(cash, expectedReporterFees, universe.getOrCreateNextFeeWindow(), "Reporter fees not paid"): # redeem shares with a1 with AssertLog(kitchenSinkFixture, "TradingProceedsClaimed", tradingProceedsClaimedLog): claimTradingProceeds.claimTradingProceeds( market.address, tester.a1) # redeem shares with a2 claimTradingProceeds.claimTradingProceeds(market.address, tester.a2) # assert a1 ends up with cash (minus fees) and a2 does not assert kitchenSinkFixture.chain.head_state.get_balance( tester.a1) == initialLongHolderETH + expectedPayout assert kitchenSinkFixture.chain.head_state.get_balance( tester.a2) == initialShortHolderETH assert yesShareToken.balanceOf(tester.a1) == 0 assert yesShareToken.balanceOf(tester.a2) == 0 assert noShareToken.balanceOf(tester.a1) == 0 assert noShareToken.balanceOf(tester.a2) == 0 assert universe.getOpenInterestInAttoEth() == 1 * market.getNumTicks( ) # The corresponding share for tester2's complete set has not been redeemed
def test_redeem_shares_in_binary_market(kitchenSinkFixture, universe, cash, market): claimTradingProceeds = kitchenSinkFixture.contracts['ClaimTradingProceeds'] yesShareToken = kitchenSinkFixture.applySignature( 'ShareToken', market.getShareToken(YES)) noShareToken = kitchenSinkFixture.applySignature('ShareToken', market.getShareToken(NO)) expectedValue = 1 * market.getNumTicks() expectedReporterFees = expectedValue / universe.getOrCacheReportingFeeDivisor( ) expectedMarketCreatorFees = expectedValue / market.getMarketCreatorSettlementFeeDivisor( ) expectedSettlementFees = expectedReporterFees + expectedMarketCreatorFees expectedPayout = long(expectedValue - expectedSettlementFees) assert universe.getOpenInterestInAttoEth() == 0 # get YES shares with a1 acquireLongShares(kitchenSinkFixture, cash, market, YES, 1, claimTradingProceeds.address, sender=tester.k1) assert universe.getOpenInterestInAttoEth() == 1 * market.getNumTicks() # get NO shares with a2 acquireShortShareSet(kitchenSinkFixture, cash, market, YES, 1, claimTradingProceeds.address, sender=tester.k2) assert universe.getOpenInterestInAttoEth() == 2 * market.getNumTicks() finalizeMarket(kitchenSinkFixture, market, [0, 10**4]) logs = [] captureFilteredLogs(kitchenSinkFixture.chain.head_state, kitchenSinkFixture.contracts['Augur'], logs) with TokenDelta(cash, expectedMarketCreatorFees, market.getMarketCreatorMailbox(), "Market creator fees not paid"): with TokenDelta(cash, expectedReporterFees, universe.getOrCreateNextFeeWindow(), "Reporter fees not paid"): # redeem shares with a1 initialLongHolderETH = kitchenSinkFixture.chain.head_state.get_balance( tester.a1) claimTradingProceeds.claimTradingProceeds(market.address, tester.a1) # redeem shares with a2 initialShortHolderETH = kitchenSinkFixture.chain.head_state.get_balance( tester.a2) claimTradingProceeds.claimTradingProceeds(market.address, tester.a2) # Confirm claim proceeds logging works correctly assert len(logs) == 6 assert logs[3]['_event_type'] == 'TradingProceedsClaimed' assert logs[3]['market'] == market.address assert logs[3]['shareToken'] == yesShareToken.address assert logs[3]['numPayoutTokens'] == expectedPayout assert logs[3]['numShares'] == 1 assert logs[3]['sender'] == bytesToHexString(tester.a1) assert logs[3][ 'finalTokenBalance'] == initialLongHolderETH + expectedPayout # assert a1 ends up with cash (minus fees) and a2 does not assert kitchenSinkFixture.chain.head_state.get_balance( tester.a1) == initialLongHolderETH + expectedPayout assert kitchenSinkFixture.chain.head_state.get_balance( tester.a2) == initialShortHolderETH assert yesShareToken.balanceOf(tester.a1) == 0 assert yesShareToken.balanceOf(tester.a2) == 0 assert noShareToken.balanceOf(tester.a1) == 0 assert noShareToken.balanceOf(tester.a2) == 0 assert universe.getOpenInterestInAttoEth() == 1 * market.getNumTicks( ) # The corresponding share for tester2's complete set has not been redeemed
def test_bootstrap(localFixture, universe, reputationToken, auction, time, cash): # Lets confirm the auction is in the dormant state initially and also in bootstrap mode assert auction.getRoundType() == 0 assert auction.bootstrapMode() assert not auction.isActive() # If we move time forward to the next auction start time we can see that the auction is now active. startTime = auction.getAuctionStartTime() assert time.setTimestamp(startTime) assert auction.getRoundType() == 2 assert auction.bootstrapMode() assert auction.isActive() # We can get the price of ETH in REP assert auction.getRepSalePriceInAttoEth() == auction.initialRepSalePrice() # However since we're in bootstrap mode we cannot yet sell REP for ETH. with raises(TransactionFailed): auction.getEthSalePriceInAttoRep() # If we move time forward but stay in the auction the sale price of the REP will drop accordingly. We'll move forward an hour and confirm the price is 1/24th less repSalePrice = auction.initialRepSalePrice() * 23 / 24 assert time.incrementTimestamp(60 * 60) assert auction.getRepSalePriceInAttoEth() == repSalePrice # Before we do any trading lets confirm the contract balances are as expected repAuctionToken = localFixture.applySignature("AuctionToken", auction.repAuctionToken()) assert auction.initialAttoRepBalance() == reputationToken.balanceOf( repAuctionToken.address) assert auction.initialAttoRepBalance() == 11 * 10**6 * 10**18 / 400 assert localFixture.chain.head_state.get_balance(auction.address) == 0 # We can purchase some of the REP now. We'll send some extra ETH to confirm it just gets returned too repAmount = 10**18 cost = repAmount * repSalePrice / 10**18 with TokenDelta(cash, cost, auction.address, "ETH was not transfered to auction correctly"): with TokenDelta( repAuctionToken, cost, tester.a0, "REP auction token was not transferred to the user correctly"): assert auction.tradeEthForRep(repAmount, value=cost + 20) # Lets purchase the remaining REP in the auction repAmount = auction.getCurrentAttoRepBalance() cost = repAmount * repSalePrice / 10**18 with TokenDelta(cash, cost, auction.address, "ETH was not transfered to auction correctly"): with TokenDelta( repAuctionToken, cost, tester.a0, "REP auction token was not transferred to the user correctly"): assert auction.tradeEthForRep(repAmount, value=cost) # If we try to purchase any more the transaction will fail with raises(TransactionFailed): auction.tradeEthForRep(repAmount, value=cost) # Lets end this auction then move time to the next auction endTime = auction.getAuctionEndTime() assert time.setTimestamp(endTime + 1) assert auction.getRoundType() == 3 assert auction.bootstrapMode() assert not auction.isActive() # Now we can redeem the tokens we received for the amount of REP we purchased expectedREP = reputationToken.balanceOf(repAuctionToken.address) with TokenDelta( reputationToken, expectedREP, tester.a0, "REP was not distributed correctly from auction token redemption"): repAuctionToken.redeem() startTime = auction.getAuctionStartTime() assert time.setTimestamp(startTime) # We can see that the ETH and REP auctions are active assert auction.getRoundType() == 6 assert auction.isActive() assert auction.getRepSalePriceInAttoEth() == auction.initialRepSalePrice() assert auction.getEthSalePriceInAttoRep() == auction.initialEthSalePrice() assert not auction.bootstrapMode() ethAuctionToken = localFixture.applySignature("AuctionToken", auction.ethAuctionToken()) ethSalePrice = auction.initialEthSalePrice() ethAmount = 10**18 cost = ethAmount * ethSalePrice / 10**18 with TokenDelta( ethAuctionToken, cost, tester.a0, "ETH auction token was not transferred to the user correctly"): with TokenDelta(reputationToken, cost, auction.address, "REP was not transferred to the auction correctly"): assert auction.tradeRepForEth(ethAmount) endTime = auction.getAuctionEndTime() assert time.setTimestamp(endTime + 1) # We can redeem the eth auction tokens for ETH. Since the auction ended with no other bids we get all the ETH with EtherDelta( cash.balanceOf(ethAuctionToken.address), tester.a0, localFixture.chain, "ETH redemption from eth auction token did not work correctly"): assert ethAuctionToken.redeem()
def test_warp_sync(contractsFixture, augur, universe, reputationToken, warpSync, cash): account = contractsFixture.accounts[0] time = contractsFixture.contracts["Time"] # See that warp sync market does not exist initially assert warpSync.markets(universe.address) == nullAddress # We can initialize the warp sync market for a universe and be rewarded with REP based on how long since the universe was created expectedCreationReward = warpSync.getCreationReward(universe.address) with PrintGasUsed(contractsFixture, "WS Market Finalization Cost", 0): with TokenDelta(reputationToken, expectedCreationReward, account, "REP reward not minted for initializing universe"): warpSync.initializeUniverse(universe.address) # The market now exists market = contractsFixture.applySignature( "Market", warpSync.markets(universe.address)) # Initially there is no warp sync data for this universe assert warpSync.data(universe.address) == [0, 0] # Finalize the warp sync market with some value proceedToDesignatedReporting(contractsFixture, market) numTicks = market.getNumTicks() assert warpSync.doInitialReport(universe.address, [0, 0, numTicks], "") disputeWindow = contractsFixture.applySignature("DisputeWindow", market.getDisputeWindow()) time.setTimestamp(disputeWindow.getEndTime()) # Finalizing the warp sync market will award the finalizer REP based on time since it became finalizable # This will also trigger a sweep of accumulated interest expectedFinalizationReward = warpSync.getFinalizationReward(market.address) WarpSyncDataUpdatedLog = { "universe": universe.address, "warpSyncHash": numTicks, "marketEndTime": market.getEndTime() } nextDisputeWindow = universe.getOrCreateNextDisputeWindow(False) initialFeesBalance = cash.balanceOf(nextDisputeWindow) with AssertLog(contractsFixture, "WarpSyncDataUpdated", WarpSyncDataUpdatedLog): with PrintGasUsed(contractsFixture, "WS Market Finalization Cost", 0): with TokenDelta(reputationToken, expectedFinalizationReward, account, "REP reward not minted for finalizer"): assert market.finalize() assert cash.balanceOf(nextDisputeWindow) > initialFeesBalance # Check Warp Sync contract for universe and see existing value assert warpSync.data(universe.address) == [numTicks, market.getEndTime()] # See new warp sync market newWarpSyncMarket = contractsFixture.applySignature( "Market", warpSync.markets(universe.address)) assert newWarpSyncMarket.address != market.address # Finalize it proceedToInitialReporting(contractsFixture, newWarpSyncMarket) numTicks = newWarpSyncMarket.getNumTicks() assert newWarpSyncMarket.doInitialReport([0, 1, numTicks - 1], "", 0) disputeWindow = contractsFixture.applySignature( "DisputeWindow", newWarpSyncMarket.getDisputeWindow()) time.setTimestamp(disputeWindow.getEndTime()) assert newWarpSyncMarket.finalize() # See new warp sync value assert warpSync.data( universe.address) == [numTicks - 1, newWarpSyncMarket.getEndTime()] # See another new market assert newWarpSyncMarket.address != warpSync.markets(universe.address)
def finalize(fixture, market, universe, finalizeByMigration = True): account0 = fixture.accounts[0] reputationToken = fixture.applySignature('ReputationToken', universe.getReputationToken()) # The universe forks and there is now a universe where NO and YES are the respective outcomes of each noPayoutNumerators = [0] * market.getNumberOfOutcomes() noPayoutNumerators[1] = market.getNumTicks() yesPayoutNumerators = [0] * market.getNumberOfOutcomes() yesPayoutNumerators[2] = market.getNumTicks() noUniverse = fixture.applySignature('Universe', universe.createChildUniverse(noPayoutNumerators)) yesUniverse = fixture.applySignature('Universe', universe.createChildUniverse(yesPayoutNumerators)) noUniverseReputationToken = fixture.applySignature('ReputationToken', noUniverse.getReputationToken()) yesUniverseReputationToken = fixture.applySignature('ReputationToken', yesUniverse.getReputationToken()) assert noUniverse.address != universe.address assert yesUniverse.address != universe.address assert yesUniverse.address != noUniverse.address assert noUniverseReputationToken.address != yesUniverseReputationToken.address # Attempting to finalize the fork now will not succeed as no REP has been migrated and not enough time has passed with raises(TransactionFailed): market.finalize() # A Tester moves some of their REP to the YES universe amount = 10 ** 6 * 10 ** 18 with raises(TransactionFailed): reputationToken.migrateOutByPayout(yesPayoutNumerators, 0) with TokenDelta(yesUniverseReputationToken, amount, account0, "Yes REP token balance was no correct"): reputationToken.migrateOutByPayout(yesPayoutNumerators, amount) # Attempting to finalize the fork now will not succeed as a majority or REP has not yet migrated and fork end time has not been reached with raises(TransactionFailed): market.finalize() if (finalizeByMigration): # Tester 0 moves more than 50% of REP amount = reputationToken.balanceOf(account0) - 20 marketFinalizedLog = { "universe": universe.address, "market": market.address, } with AssertLog(fixture, "MarketFinalized", marketFinalizedLog): with TokenDelta(noUniverseReputationToken, amount, account0, "No REP token balance was no correct"): reputationToken.migrateOutByPayout(noPayoutNumerators, amount) assert reputationToken.balanceOf(account0) == 20 assert market.getWinningPayoutDistributionHash() == noUniverse.getParentPayoutDistributionHash() else: # Time marches on past the fork end time fixture.contracts["Time"].setTimestamp(universe.getForkEndTime() + 1) marketFinalizedLog = { "universe": universe.address, "market": market.address, } with AssertLog(fixture, "MarketFinalized", marketFinalizedLog): assert market.finalize() assert market.getWinningPayoutDistributionHash() == yesUniverse.getParentPayoutDistributionHash() # If the fork is past the fork period we can not migrate with raises(TransactionFailed): reputationToken.migrateOutByPayout(noPayoutNumerators, 1) # Finalize fork cannot be called again with raises(TransactionFailed): market.finalize()
def test_affiliate_validator(kitchenSinkFixture, universe, cash): affiliates = kitchenSinkFixture.contracts['Affiliates'] affiliateValidator = kitchenSinkFixture.applySignature( "AffiliateValidator", affiliates.createAffiliateValidator()) shareToken = kitchenSinkFixture.getShareToken() market = kitchenSinkFixture.createReasonableYesNoMarket( universe, affiliateValidator=affiliateValidator.address) accountFingerprint = longTo32Bytes(11) affiliateFingerprint = longTo32Bytes(12) account = kitchenSinkFixture.accounts[0] affiliate = kitchenSinkFixture.accounts[1] affiliateValidatorOperator = kitchenSinkFixture.accounts[5] affiliateValidatorOperatorPrivKey = kitchenSinkFixture.privateKeys[5] affiliates.setFingerprint(accountFingerprint, sender=account) affiliates.setFingerprint(affiliateFingerprint, sender=affiliate) affiliates.setReferrer(affiliate) accountKey = longTo32Bytes(21) salt = 0 accountHash = affiliateValidator.getKeyHash(accountKey, account, salt) # A bad signature will be rejected with raises(TransactionFailed): affiliateValidator.addKey(accountKey, salt, longTo32Bytes(0), longTo32Bytes(0), 8, sender=account) # This includes being signed by a non operator. So the same sig will fail initially but work once the signer is approved as an operator r, s, v = signHash(accountHash, affiliateValidatorOperatorPrivKey) with raises(TransactionFailed): affiliateValidator.addKey(accountKey, salt, r, s, v, sender=account) # Succesfully add the key for the trader account affiliateValidator.addOperator(affiliateValidatorOperator) affiliateValidator.addKey(accountKey, salt, r, s, v, sender=account) # Re-using a salt will not work with raises(TransactionFailed): affiliateValidator.addKey(accountKey, salt, r, s, v, sender=account) affiliateKey = longTo32Bytes(22) salt += 1 affiliateHash = affiliateValidator.getKeyHash(affiliateKey, affiliate, salt) r, s, v = signHash(affiliateHash, affiliateValidatorOperatorPrivKey) affiliateValidator.addKey(affiliateKey, salt, r, s, v, sender=affiliate) numSets = 10 cost = numSets * market.getNumTicks() expectedAffiliateFees = cost * .0025 expectedAffiliateFees *= .8 cash.faucet(cost) shareToken.buyCompleteSets(market.address, account, numSets) with TokenDelta(cash, expectedAffiliateFees, affiliate): shareToken.sellCompleteSets(market.address, account, account, numSets, accountFingerprint) # If we try to use an account that has registered an affiliate key which is the same as the referrer the affiliate fees do not apply and will remain what they were before complete set sale dupeAccount = kitchenSinkFixture.accounts[2] affiliates.setReferrer(affiliate, sender=dupeAccount) salt += 1 affiliateHash = affiliateValidator.getKeyHash(affiliateKey, dupeAccount, salt) r, s, v = signHash(affiliateHash, affiliateValidatorOperatorPrivKey) affiliateValidator.addKey(affiliateKey, salt, r, s, v, sender=dupeAccount) cash.faucet(cost, sender=dupeAccount) shareToken.buyCompleteSets(market.address, dupeAccount, numSets, sender=dupeAccount) expectedAffiliateAmount = 20 if kitchenSinkFixture.paraAugur else 0 with TokenDelta(cash, expectedAffiliateAmount, affiliate): shareToken.sellCompleteSets(market.address, dupeAccount, dupeAccount, numSets, accountFingerprint, sender=dupeAccount) # It will also not work if the account or the referrer does not have a key registered with the validator noKeyAccount = kitchenSinkFixture.accounts[3] affiliates.setReferrer(affiliate, sender=noKeyAccount) cash.faucet(cost, sender=noKeyAccount) shareToken.buyCompleteSets(market.address, noKeyAccount, numSets, sender=noKeyAccount) with TokenDelta(cash, expectedAffiliateAmount, affiliate): shareToken.sellCompleteSets(market.address, noKeyAccount, noKeyAccount, numSets, accountFingerprint, sender=noKeyAccount)
def test_forkAndRedeem(localFixture, universe, market, categoricalMarket, cash, reputationToken): # Let's do some initial disputes for the categorical market proceedToNextRound(localFixture, categoricalMarket, tester.k1, moveTimeForward=False) # Get to a fork testers = [tester.k0, tester.k1, tester.k2, tester.k3] testerIndex = 1 while (market.getForkingMarket() == longToHexString(0)): proceedToNextRound(localFixture, market, testers[testerIndex], True) testerIndex += 1 testerIndex = testerIndex % len(testers) # Have the participants fork and create new child universes for i in range(market.getNumParticipants()): reportingParticipant = localFixture.applySignature( "DisputeCrowdsourcer", market.getReportingParticipant(i)) # Finalize the fork finalizeFork(localFixture, market, universe) categoricalDisputeCrowdsourcer = localFixture.applySignature( "DisputeCrowdsourcer", categoricalMarket.getReportingParticipant(1)) # Migrate the categorical market into the winning universe. This will disavow the dispute crowdsourcer on it, letting us redeem for original universe rep assert categoricalMarket.migrateThroughOneFork( [0, 0, 0, categoricalMarket.getNumTicks()], "") expectedRep = categoricalDisputeCrowdsourcer.getStake() with TokenDelta(reputationToken, expectedRep, tester.a1, "Redeeming didn't increase REP correctly"): categoricalDisputeCrowdsourcer.redeem(tester.a1) noPayoutNumerators = [0] * market.getNumberOfOutcomes() noPayoutNumerators[1] = market.getNumTicks() yesPayoutNumerators = [0] * market.getNumberOfOutcomes() yesPayoutNumerators[2] = market.getNumTicks() noUniverse = localFixture.applySignature( 'Universe', universe.createChildUniverse(noPayoutNumerators)) yesUniverse = localFixture.applySignature( 'Universe', universe.createChildUniverse(yesPayoutNumerators)) noUniverseReputationToken = localFixture.applySignature( 'ReputationToken', noUniverse.getReputationToken()) yesUniverseReputationToken = localFixture.applySignature( 'ReputationToken', yesUniverse.getReputationToken()) # Now we'll fork and redeem the reporting participants for i in range(market.getNumParticipants()): account = localFixture.testerAddress[i % 4] key = localFixture.testerKey[i % 4] reportingParticipant = localFixture.applySignature( "DisputeCrowdsourcer", market.getReportingParticipant(i)) expectedRep = reputationToken.balanceOf( reportingParticipant.address ) * 7 / 5 # * 1.4 to account for the minting reward of 40% repToken = noUniverseReputationToken if i % 2 == 0 else yesUniverseReputationToken with TokenDelta( repToken, expectedRep, account, "Redeeming didn't increase REP correctly for " + str(i)): assert reportingParticipant.forkAndRedeem(sender=key)
def test_initialReport_methods(localFixture, universe, market, cash, constants): reputationToken = localFixture.applySignature( "ReputationToken", universe.getReputationToken()) # proceed to the initial reporting period proceedToInitialReporting(localFixture, market) # do an initial report as someone other than the designated reporter assert market.doInitialReport([0, market.getNumTicks()], False, sender=tester.k1) # the market is now assigned a fee window newFeeWindowAddress = market.getFeeWindow() assert newFeeWindowAddress feeWindow = localFixture.applySignature('FeeWindow', newFeeWindowAddress) # time marches on and the market can be finalized localFixture.contracts["Time"].setTimestamp(feeWindow.getEndTime() + 1) assert market.finalize() # We can see that the market reports the designated reporter did not show assert not market.designatedReporterShowed() # Let's get a reference to the Initial Reporter bond and transfer it to the original designated reporter account initialReporter = localFixture.applySignature("InitialReporter", market.getInitialReporter()) transferLog = { "universe": universe.address, "market": market.address, "from": bytesToHexString(tester.a1), "to": initialReporter.getDesignatedReporter(), } with AssertLog(localFixture, "InitialReporterTransferred", transferLog): assert initialReporter.transferOwnership( initialReporter.getDesignatedReporter(), sender=tester.k1) # Transfering to the owner is a noop assert initialReporter.transferOwnership( initialReporter.getDesignatedReporter()) # The market still correctly indicates the designated reporter did not show up assert not market.designatedReporterShowed() # confirm we cannot call protected methods on the initial reporter which only the market may use with raises(TransactionFailed): initialReporter.report(tester.a0, "", [], False) with raises(TransactionFailed): initialReporter.resetReportTimestamp() with raises(TransactionFailed): initialReporter.migrateREP() # When we redeem the initialReporter it goes to the correct party as well expectedRep = initialReporter.getStake() owner = initialReporter.getOwner() expectedGasBond = 2 * constants.GAS_TO_REPORT( ) * constants.DEFAULT_REPORTING_GAS_PRICE() with EtherDelta( expectedGasBond, owner, localFixture.chain, "Initial reporter did not get the reporting gas cost bond"): with TokenDelta(reputationToken, expectedRep, owner, "Redeeming didn't refund REP"): assert initialReporter.redeem(owner)
def test_reporting_fee_from_auction(localFixture, universe, auction, reputationToken, time, cash): # We'll quickly do the bootstrap auction and seed it with some CASH startTime = auction.getAuctionStartTime() assert time.setTimestamp(startTime) # Buy 1000 REP repSalePrice = auction.getRepSalePriceInAttoCash() repAuctionToken = localFixture.applySignature("AuctionToken", auction.repAuctionToken()) repAmount = 1000 * 10**18 cost = repAmount * repSalePrice / 10**18 with BuyWithCash(cash, cost, localFixture.accounts[0], "trade cash for rep"): with TokenDelta(cash, cost, auction.address, "CASH was not transferred to auction correctly"): with TokenDelta(repAuctionToken, cost, localFixture.accounts[0], "REP was not transferred to the user correctly"): assert auction.tradeCashForRep(repAmount) # Now we'll go to the first real auction, which will be a reported auction, meaning the result affects the reported REP price endTime = auction.getAuctionEndTime() assert time.setTimestamp(endTime + 1) # Now we can redeem the tokens we received for the amount of REP we purchased expectedREP = reputationToken.balanceOf(repAuctionToken.address) with TokenDelta( reputationToken, expectedREP, localFixture.accounts[0], "REP was not distributed correctly from auction token redemption"): repAuctionToken.redeem() startTime = auction.getAuctionStartTime() assert time.setTimestamp(startTime) # Initially the REP price of the auction will simply be what was provided as the constant initialized value assert auction.getRepPriceInAttoCash( ) == auction.initialRepPriceInAttoCash() repSalePrice = auction.getRepSalePriceInAttoCash() repAuctionToken = localFixture.applySignature("AuctionToken", auction.repAuctionToken()) cashAuctionToken = localFixture.applySignature("AuctionToken", auction.cashAuctionToken()) # Purchasing REP or CASH will update the current auctions derived price, though until the auction ends it will be very innacurate so we dont bother checking here. We'll purchase 1/4 of the available supply of each at the initial price repAmount = auction.getCurrentAttoRepBalance() / 4 cost = int(Decimal(repAmount) * Decimal(repSalePrice) / Decimal(10**18)) with BuyWithCash(cash, cost, localFixture.accounts[0], "trade cash for rep"): assert auction.tradeCashForRep(repAmount) cashSalePrice = auction.getCashSalePriceInAttoRep() cashAmount = auction.getCurrentAttoCashBalance() / 4 assert auction.tradeRepForCash(cashAmount) # We'll let some time pass and buy the rest of the REP and CASH and the halfpoint prices assert time.incrementTimestamp(12 * 60 * 60) newRepSalePrice = auction.getRepSalePriceInAttoCash() repAmount = auction.getCurrentAttoRepBalance() cost = int(Decimal(repAmount) * Decimal(newRepSalePrice) / Decimal(10**18)) with BuyWithCash(cash, cost, localFixture.accounts[0], "trade cash for rep"): assert auction.tradeCashForRep(repAmount) # Now we'll purchase 2 CASH newCashSalePrice = auction.getCashSalePriceInAttoRep() cashAmount = auction.getCurrentAttoCashBalance() assert auction.tradeRepForCash(cashAmount) # We can observe that the recorded lower bound weighs this purchase more since more CASH was purchased lowerBoundRepPrice = int( Decimal(auction.initialAttoCashBalance()) * Decimal(10**18) / Decimal(cashAuctionToken.maxSupply())) upperBoundRepPrice = int( Decimal(repAuctionToken.maxSupply()) * Decimal(10**18) / Decimal(auction.initialAttoRepBalance())) derivedRepPrice = int(Decimal(lowerBoundRepPrice + upperBoundRepPrice) / 2) assert auction.getDerivedRepPriceInAttoCash() == derivedRepPrice # Lets turn on auction price reporting and move time so that this auction is considered over assert time.setTimestamp(auction.getAuctionEndTime() + 1) # We can see now that the auction will use the derived rep price when we request the price of rep for reporting fee purposes assert auction.getRepPriceInAttoCash() == derivedRepPrice # If we move time forward to the next auction we can confirm the price is still the derived price assert time.setTimestamp(auction.getAuctionStartTime()) assert auction.getRepPriceInAttoCash() == derivedRepPrice # Lets purchase REP and CASH in this auction and confirm that it does not change the reported rep price, but is recorded for use internally to set auction pricing repSalePrice = auction.getRepSalePriceInAttoCash() # Note that the repSalePrice now starts at 4 x the previous auctions derived price assert auction.initialRepSalePrice() == 4 * derivedRepPrice repAmount = auction.getCurrentAttoRepBalance() cost = int(Decimal(repAmount) * Decimal(repSalePrice) / Decimal(10**18)) with BuyWithCash(cash, cost, localFixture.accounts[0], "trade cash for rep"): assert auction.tradeCashForRep(repAmount) # Now we'll purchase 1 CASH cashSalePrice = auction.getCashSalePriceInAttoRep() # Note that the cashSalePrice is now 4 x the previous auctions derived price in terms of CASH assert auction.initialCashSalePrice() == int(4 * Decimal(10**36) / Decimal(derivedRepPrice)) cashAmount = auction.getCurrentAttoCashBalance() assert auction.tradeRepForCash(cashAmount) # And as before the recorded REP price is the mean of the two bounds repAuctionToken = localFixture.applySignature("AuctionToken", auction.repAuctionToken()) cashAuctionToken = localFixture.applySignature("AuctionToken", auction.cashAuctionToken()) lowerBoundRepPrice = int( Decimal(auction.initialAttoCashBalance()) * Decimal(10**18) / Decimal(cashAuctionToken.maxSupply())) upperBoundRepPrice = int( Decimal(repAuctionToken.maxSupply()) * Decimal(10**18) / Decimal(auction.initialAttoRepBalance())) newDerivedRepPrice = int( Decimal(lowerBoundRepPrice + upperBoundRepPrice) / 2) assert auction.getDerivedRepPriceInAttoCash() == newDerivedRepPrice # Now lets go to the dormant state and confirm that the reported rep price is still the previous recorded auctions derived REP price assert time.setTimestamp(auction.getAuctionEndTime() + 1) assert auction.getRepPriceInAttoCash() == derivedRepPrice # In the next auction we will see the newly derived REP price used as the basis for auction pricing but NOT used as the reported rep price for fees assert time.setTimestamp(auction.getAuctionStartTime()) assert auction.initializeNewAuction() assert auction.getRepPriceInAttoCash() == derivedRepPrice assert auction.lastRepPrice() == newDerivedRepPrice assert auction.initialRepSalePrice() == 4 * newDerivedRepPrice assert auction.initialCashSalePrice() == int(4 * Decimal(10**36) / Decimal(newDerivedRepPrice))
def test_forking(localFixture, universe, market, categoricalMarket, scalarMarket, cash, reputationToken, reportingWindow): # We'll have testers put up dispute bonds against the designated reports and place stake in other outcomes otherOutcomeStake = 10**18 assert market.disputeDesignatedReport([market.getNumTicks(), 0], otherOutcomeStake, False, sender=tester.k1) assert categoricalMarket.disputeDesignatedReport( [categoricalMarket.getNumTicks(), 0, 0], otherOutcomeStake, False, sender=tester.k2) assert scalarMarket.disputeDesignatedReport( [scalarMarket.getNumTicks(), 0], otherOutcomeStake, False, sender=tester.k3) reporterFees = 1000 * market.getNumTicks( ) / universe.getReportingFeeDivisor() totalWinningStake = reportingWindow.getTotalWinningStake() assert cash.balanceOf(reportingWindow.address) == reporterFees # Progress to the Limited dispute phase and dispute one of the markets. This should migrate fees to the reporting window the market migrates to proportional to its stake localFixture.chain.head_state.timestamp = reportingWindow.getDisputeStartTime( ) + 1 assert market.disputeFirstReporters([market.getNumTicks() - 1, 1], otherOutcomeStake, False, sender=tester.k4, startgas=long(6.7 * 10**6)) assert categoricalMarket.disputeFirstReporters( [categoricalMarket.getNumTicks() - 1, 1, 0], otherOutcomeStake, False, sender=tester.k4, startgas=long(6.7 * 10**6)) assert scalarMarket.disputeFirstReporters( [scalarMarket.getNumTicks() - 1, 1], otherOutcomeStake, False, sender=tester.k4, startgas=long(6.7 * 10**6)) # Progress into last dispute and cause a fork reportingWindow = localFixture.applySignature("ReportingWindow", market.getReportingWindow()) localFixture.chain.head_state.timestamp = reportingWindow.getDisputeStartTime( ) + 1 forkDuration = lastDisputeCost = localFixture.contracts[ "Constants"].FORK_DURATION_SECONDS() nextReportingWindow = localFixture.applySignature( "ReportingWindow", universe.getReportingWindowByTimestamp( localFixture.chain.head_state.timestamp + forkDuration)) scalarMarketStake = scalarMarket.getTotalStake() lastDisputeCost = localFixture.contracts[ "Constants"].LAST_REPORTERS_DISPUTE_BOND_AMOUNT() totalScalarMarketStakeMoved = scalarMarketStake + lastDisputeCost migratedFees = reporterFees * (scalarMarketStake + lastDisputeCost) / ( reportingWindow.getTotalStake() + lastDisputeCost) with TokenDelta(cash, -migratedFees, reportingWindow.address, "Disputing in last didn't migrate ETH out correctly"): with TokenDelta(cash, migratedFees, nextReportingWindow.address, "Disputing in last didn't migrate ETH in correctly"): with StakeDelta( lastDisputeCost, -scalarMarketStake, 0, scalarMarket, reportingWindow, "Disputing in last is not migrating stake out correctly"): with StakeDelta( lastDisputeCost, totalScalarMarketStakeMoved, 0, scalarMarket, nextReportingWindow, "Disputing in last is not migrating stake in correctly" ): assert scalarMarket.disputeLastReporters(sender=tester.k5) # We migrate REP to a new universe and finalize the forking market newUniverse = localFixture.getOrCreateChildUniverse( universe, scalarMarket, [0, scalarMarket.getNumTicks()]) newUniverseReputationToken = localFixture.applySignature( 'ReputationToken', newUniverse.getReputationToken()) # Testers all move their REP to the new universe for i in range(0, 5): reputationToken.migrateOut(newUniverseReputationToken.address, localFixture.testerAddress[i], reputationToken.balanceOf( localFixture.testerAddress[i]), sender=localFixture.testerKey[i]) # Finalize the forking market assert scalarMarket.tryFinalize() # migrate one of the markets to the winning universe and confirm fees went with it oldReportingWindowAddress = market.getReportingWindow() designatedReportingDuration = localFixture.contracts[ "Constants"].DESIGNATED_REPORTING_DURATION_SECONDS() newReportingWindowAddress = newUniverse.getReportingWindowByMarketEndTime( localFixture.chain.head_state.timestamp - designatedReportingDuration) migratedFees = cash.balanceOf(oldReportingWindowAddress) with TokenDelta( cash, -migratedFees, oldReportingWindowAddress, "Migrating to a new universe didn't migrate ETH out correctly"): with TokenDelta( cash, migratedFees, newReportingWindowAddress, "Migrating to a new universe didn't migrate ETH in correctly"): market.migrateThroughAllForks()