Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 202 lines (186 sloc) 8.68 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
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Any questions please contact joey@augur.net
import branches as BRANCHES
import cash as CASH
import markets as MARKETS
import trades as TRADES
import completeSets as COMPLETESETS
inset('refund.se')
inset('logReturn.se')
event buyAndSellShares_logReturn(returnValue)
event log_add_tx(market:indexed, sender:indexed, type, price, amount, outcome, tradeid, isShortAsk, timestamp, tradeGroupID)
event log_cancel(market:indexed, sender:indexed, price, amount, tradeid, outcome, type, cashRefund, timestamp)
# Trade types
macro BID: 1
macro ASK: 2
# Field counts
macro TRADE_FIELDS: 8
# Boolean success/failure
macro SUCCESS: 1
macro FAILURE: 0
# Error codes
macro INSUFFICIENT_BALANCE: 10
macro TRADE_ALREADY_EXISTS: 21
macro TRADE_SAME_BLOCK_PROHIBITED: 22
macro fee_percent($market_fee, $price, $cumScale):
4 * $market_fee * $price * (ONE-$price*ONE/$cumScale) / ($cumScale*ONE)
macro save_trade($type, $amount, $price, $market, $outcome, $branch, $fee, $sender, $isShortAsk, $tradeGroupID):
trade = [$type, $market, $amount, $price, $sender, block.number, $outcome]
trade_id = sha3(trade, items=7)
cumScale = MARKETS.getCumScale($market)
# Save trade
if !TRADES.getID(trade_id):
TRADES.saveTrade(trade_id, $type, $market, $amount, $price, $sender, $outcome)
# Update market
last_id = MARKETS.getLastTrade($market)
MARKETS.addTrade($market, trade_id, last_id)
# Update available and trading amounts for asks
if $type == ASK:
MARKETS.modifyParticipantShares($market, msg.sender, $outcome, -$amount, 0)
if($fee):
CASH.sendFrom($market, ($amount * $price / ONE * $fee / ONE), $sender)
# Send / escrow cash for bids
if $type == BID:
CASH.sendFrom($market, $amount * $price / ONE, $sender)
if($fee):
CASH.sendFrom($market, ($amount * $price / ONE * $fee / ONE), $sender)
else:
logReturn(buyAndSellShares_logReturn, TRADE_ALREADY_EXISTS)
log(type=log_add_tx, $market, $sender, $type, $price, $amount, $outcome, trade_id, $isShortAsk, block.timestamp, $tradeGroupID)
logReturn(buyAndSellShares_logReturn, trade_id)
def shortAsk(amount, price, market, outcome, minimumTradeSize, tradeGroupID):
refund()
buyCompleteSetsResult = COMPLETESETS.buyCompleteSets(market, amount, call=delegate)
if buyCompleteSetsResult != amount:
return(buyCompleteSetsResult)
return(self.sell(amount, price, market, outcome, minimumTradeSize, 1, tradeGroupID, call=delegate))
#
# Cancellation: cancels a trade, if a bid refunds money, if an ask returns shares
# @returns 1 if success, 0 if failure
#
# 200k gas
def cancel(trade_id):
refund()
# user can cancel anytime
# Get trade
trade = array(TRADE_FIELDS)
trade = TRADES.get_trade(trade_id, outitems=TRADE_FIELDS)
if !trade:
logReturn(buyAndSellShares_logReturn, 0)
type = trade[1]
market = trade[2]
amount = trade[3]
price = trade[4]
owner = trade[5]
outcome = trade[7]
branch = MARKETS.getBranchID(market)
trading_fee = MARKETS.getTradingFee(market)
cumScale = MARKETS.getCumScale(market)
fee = fee_percent(trading_fee, price, cumScale) * MARKETS.getMakerFees(market) / ONE
# Check the owner
if msg.sender == owner:
# Clear the trade first
remove_trade(trade_id, market)
# Issue refunds
if type == BID:
# cash refund
cashRefund = amount * price / ONE * (fee + ONE) / ONE
CASH.subtractCash(market, cashRefund)
CASH.addCash(msg.sender, cashRefund)
elif type == ASK:
# shares refund
MARKETS.modifyParticipantShares(market, msg.sender, outcome, amount, 1)
cashRefund = (amount * price / ONE * fee / ONE)
CASH.subtractCash(market, cashRefund)
CASH.addCash(msg.sender, cashRefund)
# Log cancellation
log(type=log_cancel, market, msg.sender, price, amount, trade_id, outcome, type, cashRefund, block.timestamp)
logReturn(buyAndSellShares_logReturn, SUCCESS)
logReturn(buyAndSellShares_logReturn, FAILURE)
macro remove_trade($trade_id, $market):
TRADES.remove_trade($trade_id)
MARKETS.remove_trade_from_market($market, $trade_id)
#
# Buy / Sell actions: puts orders on the book
# Errors:
# 0: market doesn't exist
# -1: amount/price bad
# -2: oracle only branch
# -3: trader doesn't exist in market
# -4: not enough money or shares
# -5: bid price exceeds best ask
#
# costs 532k gas
# smallest trade value is 0.00000001
def buy(amount, price, market, outcome, minimumTradeSize, tradeGroupID):
refund()
# Check if there are any "crossed orders" (i.e., ask orders
# at a price less than or equal to the buy price).
bestAskID = TRADES.getBestAskID(market, outcome)
if bestAskID != 0 and price >= TRADES.get_price(bestAskID) and TRADES.get_amount(bestAskID) >= minimumTradeSize:
logReturn(buyAndSellShares_logReturn, -5)
trading_fee = MARKETS.getTradingFee(market)
cumScale = MARKETS.getCumScale(market)
fee = fee_percent(trading_fee, price, cumScale) * MARKETS.getMakerFees(market) / ONE
branch = MARKETS.getBranchID(market)
if(!MARKETS.getCreationTime(market)):
logReturn(buyAndSellShares_logReturn, 0)
# price cannot be greater than max share value, value traded can't be <.00000001, and value can't be > 2**126 in unfixed point so we don't get overflow issues
if(price>cumScale or amount*price < 3402823669209384705829531287552 or amount*price >= 2**190):
logReturn(buyAndSellShares_logReturn, -1)
if(BRANCHES.getOracleOnly(branch)):
logReturn(buyAndSellShares_logReturn, -2)
if(CASH.balance(msg.sender) < (amount * price / ONE * (fee + ONE) / ONE)):
logReturn(buyAndSellShares_logReturn, -4)
save_trade(BID, amount, price, market, outcome, branch, fee, msg.sender, 0, tradeGroupID)
logReturn(buyAndSellShares_logReturn, FAILURE)
# costs 532k gas
# smallest trade value is 0.00000001
# Errors:
# 0: market doesn't exist
# -1: amount/price bad
# -2: oracle only branch
# -3: trader doesn't exist in market
# -4: not enough money or shares
# -5: best bid exceeds ask price
def sell(amount, price, market, outcome, minimumTradeSize, isShortAsk, tradeGroupID):
refund()
# Check if there are any "crossed orders" (i.e., bid orders
# at a price greater than or equal to the sell price).
bestBidID = TRADES.getBestBidID(market, outcome)
if bestBidID != 0 and price <= TRADES.get_price(bestBidID) and TRADES.get_amount(bestBidID) >= minimumTradeSize:
logReturn(buyAndSellShares_logReturn, -5)
branch = MARKETS.getBranchID(market)
trading_fee = MARKETS.getTradingFee(market)
cumScale = MARKETS.getCumScale(market)
fee = fee_percent(trading_fee, price, cumScale) * MARKETS.getMakerFees(market) / ONE
# price cannot be greater than max share value, value traded can't be <.00000001, and value can't be > 2**126 in unfixed point so we don't get overflow issues
if(price>cumScale or amount*price < 3402823669209384705829531287552 or amount*price >= 2**190):
logReturn(buyAndSellShares_logReturn, -1)
if(BRANCHES.getOracleOnly(branch)):
logReturn(buyAndSellShares_logReturn, -2)
if(MARKETS.getParticipantSharesPurchased(market, msg.sender, outcome) < amount):
logReturn(buyAndSellShares_logReturn, -4)
# check for enough money for fees
if(CASH.balance(msg.sender) < (amount * price / ONE * fee / ONE)):
logReturn(buyAndSellShares_logReturn, -4)
save_trade(ASK, amount, price, market, outcome, branch, fee, msg.sender, isShortAsk, tradeGroupID)
logReturn(buyAndSellShares_logReturn, FAILURE)