# Creates a decentralized market between any two subcurrencies

# Here, the first subcurrency is the base asset and the second
# subcurrency is the asset priced against the base asset. Hence,
# "buying" refers to trading the first for the second, and
# "selling" refers to trading the second for the first

# storage 0: buy orders
# storage 1: sell orders
# storage 1000: first subcurrency
# storage 1001: last first subcurrency txid
# storage 2000: second subcurrency
# storage 2001: last second subcurrency txid
# storage 3000: current epoch
# storage 4000: price
# storage 4001: volume

init:
    # Heap for buy orders
    contract.storage[0] = create('heap.se')
    # Heap for sell orders
    contract.storage[1] = create('heap.se')
code:
    # Initialize with [ first_subcurrency, second_subcurrency ]
    if !contract.storage[1000]:
        contract.storage[1000] = msg.data[0] # First subcurrency
        contract.storage[1001] = -1
        contract.storage[2000] = msg.data[1] # Second subcurrency
        contract.storage[2001] = -1
        contract.storage[3000] = block.number / 1000
        stop
    first_subcurrency = contract.storage[1000]
    second_subcurrency = contract.storage[2000]
    buy_heap = contract.storage[0]
    sell_heap = contract.storage[1]
    # This contract operates in "epochs" of 100 blocks
    # At the end of each epoch, we process all orders
    # simultaneously, independent of order. This algorithm
    # prevents front-running, and generates a profit from
    # the spread. The profit is permanently kept in the
    # market (ie. destroyed), making both subcurrencies
    # more valuable
    
    # Epoch transition code
    if contract.storage[3000] < block.number / 100:
        done = 0
        volume = 0
        while !done:
            # Grab the top buy and sell order from each heap
            topbuy = call(buy_heap, 1)
            topsell = call(sell_heap, 1)
            # An order is recorded in the heap as:
            # Buys: (2^48 - 1 - price) * 2^208 + units of first currency * 2^160 + from
            # Sells: price * 2^208 + units of second currency * 2^160 + from
            buyprice = -(topbuy / 2^208)
            buyfcvalue = (topbuy / 2^160) % 2^48
            buyer = topbuy % 2^160
            sellprice = topsell / 2^208
            sellscvalue = (topsell / 2^160) % 2^48
            seller = topsell % 2^160
            # Heap empty, or no more matching orders
            if not topbuy or not topsell or buyprice < sellprice:
                done = 1
            else:
                # Add to volume counter
                volume += buyfcvalue
                # Calculate how much of the second currency the buyer gets, and
                # how much of the first currency the seller gets
                sellfcvalue = sellscvalue / buyprice
                buyscvalue = buyfcvalue * sellprice
                # Send the currency units along
                call(second_subcurrency, [buyer, buyscvalue], 2)
                call(first_subcurrency, [seller, sellfcvalue], 2)
        if volume:
            contract.storage[4000] = (buyprice + sellprice) / 2
        contract.storage[4001] = volume
        contract.storage[3000] = block.number / 100
    # Make buy order [0, price]
    if msg.data[0] == 0:
        # We ask the first subcurrency contract what the last transaction was. The
        # way to make a buy order is to send the amount of first currency units that
        # you wish to buy with, and then immediately call this contract. For security
        # it makes sense to set up a tx which sends both messages in sequence atomically
        data = call(first_subcurrency, [], 0, 4)
        from = data[0]
        to = data[1]
        value = data[2]
        txid = data[3]
        price = msg.data[1]
        if txid > contract.storage[1001] and to == contract.address:
            contract.storage[1001] = txid
            # Adds the order to the heap
            call(buy_heap, [0, -price * 2^208 + (value % 2^48) * 2^160 + from], 2) 
    # Make sell order [1, price]
    elif msg.data[0] == 1:
        # Same mechanics as buying
        data = call(second_subcurrency, [], 0, 4)
        from = data[0]
        to = data[1]
        value = data[2]
        txid = data[3]
        price = msg.data[1]
        if txid > contract.storage[2001] and to == contract.address:
            contract.storage[2001] = txid
            call(sell_heap, [0, price * 2^208 + (value % 2^48) * 2^160 + from], 2) 
    # Ask for price
    elif msg.data[0] == 2:
        return(contract.storage[4000])
    # Ask for volume
    elif msg.data[0] == 3:
        return(contract.storage[1000])
    # Ask for first currency
    elif msg.data[0] == 4:
        return(contract.storage[2000])
    # Ask for second currency
    elif msg.data[0] == 5:
        return(contract.storage[4001])
