Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 225 lines (216 sloc) 12.5 KB
# This software (Augur) allows buying && selling event outcomes in ethereum
# Copyright (C) 2015 Forecast Foundation OU
# This program is free software; you can redistribute it &&/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is free software: you can redistribute it &&/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <>.
# Any questions please contact
import branches as BRANCHES
import info as INFO
import cash as CASH
import markets as MARKETS
import backstops as BACKSTOPS
import events as EVENTS
import expiringEvents as EXPEVENTS
macro POINTONETWOFIVE: 125000000000000000
macro POINTZEROSIX: 60000000000000000
macro COSTPERREPORTER: 3500000
event marketCreated(sender: indexed, branch: indexed, marketID, marketCreationFee, eventBond, timestamp)
event tradingFeeUpdated(sender: indexed, branch: indexed, marketID: indexed, tradingFee, timestamp)
def createSingleEventMarket(branch, description:str, expDate, minValue, maxValue, numOutcomes, resolution:str, tradingFee, tag1, tag2, tag3, makerFees, extraInfo:str):
eventID = self.createEvent(branch, description, expDate, minValue, maxValue, numOutcomes, resolution, call=delegate)
marketID = self.createMarket(branch, description, tradingFee, eventID, tag1, tag2, tag3, makerFees, extraInfo, call=delegate)
# Create an event. Events are the basic units / questions that are reported on in Augur by reporters
# @param numOutcomes is number of outcomes for this event, e.g. quarter mile times from 10.0 to 11.0 would be 11 outcomes (if incremented by 0.1)
# @param minValue should be 10**18 for a binary event, maxValue should be 2*10**18 for a binary event
# @param minValue for a scalar from 0 to 100 should be 0 and max should be 100*10**18, for -100 to 200, it should be -100*10**18 and 200*10**18 respectively
# @param description is a description of the event / the question itself
# @param resolution is the string source/link to resolve the event
# @param branch is the branch the event is created in
# @param expDate is the epoch time in which the event expires
# @return eventID if success
# error messages otherwise
# 0: not enough money to pay fees or event already exists
# -1: we're either already past that date, branch doesn't exist, or description is bad
# -2: max value < min value or range < 1
# -3: invalid number of outcomes
# -4: would expire during non-reporting fork period
# .025 eth to create
def createEvent(branch, description:str, expDate, minValue, maxValue, numOutcomes, resolution: str):
periodLength = BRANCHES.getPeriodLength(branch)
forkPeriod = BRANCHES.getForkPeriod(branch)
if(!periodLength or description == 0 or expDate < block.timestamp):
if(maxValue < minValue or (maxValue-minValue) < ONE):
if(numOutcomes < 2 || numOutcomes > 8):
if(forkPeriod && ((expDate / periodLength)==forkPeriod or (expDate / periodLength)==(forkPeriod+1))):
eventinfo = string(7*32 + len(description))
eventinfo[0] = branch #branchID
eventinfo[1] = expDate #expiration date
eventinfo[2] = msg.sender #creator address
eventinfo[3] = periodLength
eventinfo[4] = minValue #minimum outcome value
eventinfo[5] = maxValue #maximum outcome value
eventinfo[6] = numOutcomes #number of outcomes
mcopy(eventinfo + 7*32, description, len(description))
eventID = sha3(eventinfo, chars=len(eventinfo))
if(INFO.setInfo(eventID, description, msg.sender, 0) && EVENTS.initializeEvent(eventID, branch, expDate, minValue, maxValue, numOutcomes, resolution)):
# Create a market. Markets are the basic _tradable_ units / questions that are traded on in Augur
# @param branch is the branch of the market
# @param description is the description for a market
# @param tradingFee is percent in fixedPoint
# @param events array is the list of events in a market [up to 3]
# @params tag1, tag2, and tag3 are the tags describing a market
# @param makerFees are the percent of the trading fee a maker pays [0-50% in fixed point]
# @param extraInfo is a string of any extra info associated with a market
# @return marketID if success or 1 if on an oracle only branch creation we have success
# error messages otherwise
# -1: bad input or event doesn't exist
# -2: event already expired
# -3: would expire during non-reporting fork period
# -4: market already exists
# throws if not enough money to create the market and place event in the appropriate reporting period
# .05 eth to create
# need at least 1.2M gas @ gas price to cover resolution 500k per event to calc. num reports for it - this is passed as value to this function
# need to check that it's an actual subcurrency upon market creation (maybe check send/balance funs)
def createMarket(branch, description:str, tradingFee, event, tag1, tag2, tag3, makerFees, extraInfo:str):
periodLength = BRANCHES.getPeriodLength(branch)
creationFee = POINTZEROSIX * BRANCHES.getBaseReporters(branch) * ONE / (2*tradingFee)
# gives ether/cash amount in fixed point
minFee = COSTPERREPORTER*BRANCHES.getBaseReporters(branch)*tx.gasprice
if(creationFee < minFee):
creationFee = minFee
validity_bond = (creationFee * (1 + EVENTS.getPast24(block.timestamp / periodLength)) / (1 + EXPEVENTS.getNumberEvents(branch, BRANCHES.getVotePeriod(branch))))/2
numOutcomes = EVENTS.getNumOutcomes(event)
cumulativeScale = 0
# market's trading period is the same as the last expiring event in the market
expirationDate = EVENTS.getExpiration(event)
futurePeriod = expirationDate / periodLength
forkPeriod = BRANCHES.getForkPeriod(branch)
# will need to get equivalent value in usd or eth or w/e via etherex exchange for subcurrency markets
if(periodLength==0 or len(description)==0 || tradingFee < BRANCHES.getMinTradingFee(branch) or tradingFee > POINTONETWOFIVE or EVENTS.getEventBranch(event) != branch or !INFO.getCreator(event) or makerFees < 0 or makerFees > ONEHALF):
if(expirationDate < block.timestamp):
if(forkPeriod && (futurePeriod == forkPeriod or futurePeriod == (forkPeriod+1))):
maxValue = EVENTS.getMaxValue(event)
minValue = EVENTS.getMinValue(event)
# is a valid scalar
if((maxValue!=TWO || minValue !=ONE) && numOutcomes==2):
# cumulativeScale is the range of a scalar
cumulativeScale += maxValue - minValue
cumulativeScale = ONE
# formation of marketID (hash)
marketinfo = string(4*32 + len(description))
marketinfo[0] = futurePeriod
marketinfo[1] = tradingFee
marketinfo[2] = expirationDate
marketinfo[3] = len(description)
mcopy(marketinfo + 4*32, description, chars=len(description))
marketID = sha3(marketinfo, chars=len(marketinfo))
# if it's already been created return 0
events = array(1)
events[0] = event
# initialize market and send money to pay for resolution
if(CASH.sendFrom(branch, creationFee, msg.sender) && send(MARKETS, (msg.value-500000*tx.gasprice)) && INFO.setInfo(marketID, description, msg.sender, creationFee) && BRANCHES.addMarketToBranch(branch, marketID) && MARKETS.initializeMarket(marketID, events, futurePeriod, tradingFee, branch, tag1, tag2, tag3, makerFees, cumulativeScale, numOutcomes, extraInfo, msg.value-500000*tx.gasprice, creationFee, expirationDate)):
MARKETS.addToMarketsHash(branch, marketID)
EVENTS.addMarket(event, marketID)
# add events to the appropriate reporting period
if(EXPEVENTS.getEvent(branch, futurePeriod, EXPEVENTS.getEventIndex(futurePeriod, event))!=event):
EXPEVENTS.addEvent(branch, futurePeriod, event, 500000*tx.gasprice)
# cost for calculating num. of reports for an event
if(!send(EXPEVENTS, 500000*tx.gasprice)):
# pay validity / indeterminate protection bond
period = block.timestamp / TWENTYFOURHR
if(!CASH.sendFrom(event, validity_bond, msg.sender)):
EVENTS.setBond(event, validity_bond)
log(type=marketCreated, msg.sender, branch, marketID, 500000*tx.gasprice, validity_bond, block.timestamp)
# Updates a trading fee to be lower in a market
# Errors
# -1: not the market creator
# -2: invalid new trading fee
def updateTradingFee(branch, market, tradingFee, makerFees):
if(msg.sender != INFO.getCreator(market) || tx.origin != INFO.getCreator(market)):
oldFee = MARKETS.getTradingFee(market)
oldCreationFee = POINTZEROSIX * BRANCHES.getBaseReporters(branch) * ONE / oldFee
newCreationFee = POINTZEROSIX * BRANCHES.getBaseReporters(branch) * ONE / tradingFee
if(tradingFee < BRANCHES.getMinTradingFee(branch) or tradingFee > oldFee or makerFees < 0 or makerFees > ONEHALF):
if(!CASH.sendFrom(branch, newCreationFee-oldCreationFee, msg.sender) || !MARKETS.setTradingFee(market, tradingFee) || !MARKETS.setMakerFees(market, makerFees)):
log(type=tradingFeeUpdated, msg.sender, branch, market, tradingFee, block.timestamp)
# Anyone can post an "Early Resolution Bond"
# This bond is equal to 0.5 * Market_Fee * Market_Value
# This amount is the amount needed to pay the reporters in case this was frivolous.
# The market goes up for early resolution and reporters place claim to what is truth, however for early resolution, they have an additional option: 'Market is not ready to resolve'
# this addl option is just the normal indeterminate (except here it's a bit of a special case, see below)
# In the event 'Market is not ready to resolve' is found to be the consensus, the early resolution bond is paid to the reporters for their labor.
# and market remains with old expiration dateevent
# In the event any other option is found to be the consensus the early resolution bond is returned to the poster and then resolution is handled just like any other case.
def pushMarketForward(branch, market):
# can't do while forking
forkPeriod = BRANCHES.getForkPeriod(branch)
periodLength = BRANCHES.getPeriodLength(branch)
currentPeriod = block.timestamp / periodLength
if(forkPeriod == currentPeriod or currentPeriod == (forkPeriod+1)):
# if market closed or already pushed forward, return 0
if(MARKETS.getOneWinningOutcome(market, 0) or MARKETS.getPushedForward(market)):
if(CASH.sendFrom(market, MARKETS.getTradingFee(market)*MARKETS.getTotalSharesPurchased(market)/(2*ONE), msg.sender)==0):
event = MARKETS.getMarketEvent(market, 0)
expiration = EVENTS.getExpiration(event)
if(EVENTS.getRejectedPeriod(event) || expiration!=EVENTS.getOriginalExpiration(event) || EVENTS.getOutcome(event) || BACKSTOPS.getRoundTwo(event) || expiration/periodLength==block.timestamp/periodLength):
# push into next vote period
period = block.timestamp/periodLength
# figure out what's going on with subsidy system here todo
EXPEVENTS.addEvent(branch, period, event, 0)
# set event expiration date to be after the current reporting period ends
EVENTS.setExpiration(event, block.timestamp)
MARKETS.setTradingPeriod(market, period)
MARKETS.setPushedForward(market, 1, msg.sender)
EVENTS.setEventPushedUp(event, 1)