Function: spot_market_order


Registered at block: #48244250
Snippets: 2b8ccbd08a20...

Source Code


def _decimal(tick):
    dec, _ = get(tick, 'decimal', None)
    if dec is None:
        dec = 0
    return dec

def _insert_order(addr, pair, order_type, order_start, order_new, quote_value, base_value, quote_scale):
    assert order_type in ['buy', 'sell']
    order_id = order_start
    price = - quote_value * quote_scale // base_value
    while True:
        order, _ = get('spot', f'{pair}_{order_type}', None, str(order_id))

        if order is None:
            put(addr, 'spot', f'{pair}_{order_type}',
                [addr, base_value, quote_value, price, None, None], str(order_new))
            order_new += 1
            put(addr, 'spot', f'{pair}_{order_type}_new', order_new)
            break

        if order_type == 'buy':
            cond = price > order[3]
        else:
            cond = price < order[3]

        if cond:
            next_order_id = order[5]
            put(addr, 'spot', f'{pair}_{order_type}',
                [addr, base_value, quote_value, price, order_id, next_order_id], str(order_new))
            if next_order_id is None:
                order_start = order_new
                put(addr, 'spot', f'{pair}_{order_type}_start', order_new)
            order[5] = order_new
            order_new += 1
            put(addr, 'spot', f'{pair}_{order_type}_new', order_new)

            put(addr, 'spot', f'{pair}_{order_type}', order, str(order_id))
            if next_order_id is not None:
                next_order, _ = get('spot', f'{pair}_{order_type}', None, str(next_order_id))
                if next_order is not None:
                    next_order[4] = order[5]
                    put(addr, 'spot', f'{pair}_{order_type}', next_order, str(next_order_id))
            break

        if order[4] is None:
            put(addr, 'spot', f'{pair}_{order_type}',
                [addr, base_value, quote_value, price, None, order_id], str(order_new))
            put(addr, 'spot', f'{pair}_{order_type}',
                [order[0], order[1], order[2], order[3], order_new, order[5]], str(order_id))
            order_new += 1
            put(addr, 'spot', f'{pair}_{order_type}_new', order_new)
            break

        order_id = order[4]
    return order_start, order_new


def _remove_order(addr, pair, order, order_start, buy_or_sell):
    assert buy_or_sell in ['buy', 'sell']
    if order[4]:
        prev_order, _ = get('spot', f'{pair}_{buy_or_sell}', None, str(order[4]))
        prev_order[5] = order[5]
        put(prev_order[0], 'spot', f'{pair}_{buy_or_sell}', prev_order, str(order[4]))

    if order[5]:
        next_order, _ = get('spot', f'{pair}_{buy_or_sell}', None, str(order[5]))
        next_order[4] = order[4]
        put(next_order[0], 'spot', f'{pair}_{buy_or_sell}', next_order, str(order[5]))

    if order[4] is not None and order[5] is None:
        order_start = order[4]
        put(addr, 'spot', f'{pair}_{buy_or_sell}_start', order_start)

    elif order[4] is None and order[5] is None:
        order_new, _ = get('spot', f'{pair}_{buy_or_sell}_new', 1)
        order_start = order_new
        put(addr, 'spot', f'{pair}_{buy_or_sell}_start', order_start)

    return order_start


def spot_limit_order(info, args):
    assert args['f'] == 'spot_limit_order'
    sender = info['sender']
    addr = handle_lookup(sender)

    base_tick = args['a'][0]
    quote_tick = args['a'][2]
    assert set(base_tick) <= set(string.ascii_uppercase+'_')
    assert set(quote_tick) <= set(string.ascii_uppercase+'_')

    pair = '%s_%s' % tuple([base_tick, quote_tick])
    quote_scale = 10 ** _decimal(quote_tick)
    base_value = int(args['a'][1])
    quote_value = int(args['a'][3])
    assert base_value * quote_value < 0

    spot_buy_start, _ = get('spot', f'{pair}_buy_start', 1)
    spot_buy_new, _ = get('spot', f'{pair}_buy_new', 1)
    spot_sell_start, _ = get('spot', f'{pair}_sell_start', 1)
    spot_sell_new, _ = get('spot', f'{pair}_sell_new', 1)

    if base_value < 0 and quote_value > 0:
        buy_or_sell = 'sell'
        balance, _ = get(base_tick, 'balance', 0, addr)
        balance += base_value
        make_base = - base_value
        assert balance >= 0
        put(addr, base_tick, 'balance', balance, addr)

        order_id = spot_sell_new
        spot_sell_start, spot_sell_new = _insert_order(addr, pair, 'sell', spot_sell_start, spot_sell_new, quote_value, base_value, quote_scale)

    elif base_value > 0 and quote_value < 0:
        buy_or_sell = 'buy'
        balance, _ = get(quote_tick, 'balance', 0, addr)
        balance += quote_value
        make_base = base_value
        assert balance >= 0
        put(addr, quote_tick, 'balance', balance, addr)

        order_id = spot_buy_new
        spot_buy_start, spot_buy_new = _insert_order(addr, pair, 'buy', spot_buy_start, spot_buy_new, quote_value, base_value, quote_scale)

    spot_sell_id = spot_sell_start
    highest_buy_price = None

    # take_amount = 0
    take_base = 0
    take_quote = 0
    while True:
        sell, _ = get('spot', f'{pair}_sell', None, str(spot_sell_id))
        if not sell:
            break
        sell_price = sell[3]
        if highest_buy_price and sell_price > highest_buy_price:
            break

        spot_buy_id = spot_buy_start
        while True:
            buy, _ = get('spot', f'{pair}_buy', None, str(spot_buy_id))
            if not buy:
                break
            buy_price = buy[3]
            if highest_buy_price is None:
                highest_buy_price = buy_price
            if sell_price > buy_price:
                spot_buy_id = buy[4]
                continue

            matched_price = sell_price
            dx_base = min(-sell[1], buy[1])
            dx_quote = dx_base * matched_price // quote_scale
            sell[1] += dx_base
            sell[2] -= dx_quote
            buy[1] -= dx_base
            buy[2] += dx_quote
            take_base += dx_base
            take_quote += dx_quote
            # if buy_or_sell == 'buy':
            #     take_amount += dx_quote
            # else:
            #     take_amount += dx_base
            balance, _ = get(base_tick, 'balance', 0, buy[0])
            balance += dx_base
            assert balance >= 0
            put(buy[0], base_tick, 'balance', balance, buy[0])

            balance, _ = get(quote_tick, 'balance', 0, sell[0])
            balance += dx_quote
            assert balance >= 0
            put(sell[0], quote_tick, 'balance', balance, sell[0])

            if buy[1] == 0:
                spot_buy_start = _remove_order(addr, pair, buy, spot_buy_start, 'buy')

                if buy[2] < 0:
                    balance, _ = get(quote_tick, 'balance', 0, buy[0])
                    balance -= buy[2]
                    assert balance >= 0
                    put(buy[0], quote_tick, 'balance', balance, buy[0])
    
                put(buy[0], 'spot', f'{pair}_buy', None, str(spot_buy_id))
            else:
                put(buy[0], 'spot', f'{pair}_buy', buy, str(spot_buy_id))

            if sell[1] == 0:
                break
            if buy[4] is None:
                break
            spot_buy_id = buy[4]

        if sell[1] == 0:
            spot_sell_start = _remove_order(addr, pair, sell, spot_sell_start, 'sell')

            if sell[1] < 0:
                balance, _ = get(base_tick, 'balance', 0, sell[0])
                balance -= sell[1]
                assert balance >= 0
                put(sell[0], base_tick, 'balance', balance, sell[0])

            put(sell[0], 'spot', f'{pair}_sell', None, str(spot_sell_id))
        else:
            put(sell[0], 'spot', f'{pair}_sell', sell, str(spot_sell_id))

        if sell[4] is None:
            break
        spot_sell_id = sell[4]

    make_base -= take_base
    assert make_base >= 0
    make_price = - quote_value * quote_scale // base_value
    event('SpotLimitMake', [pair, buy_or_sell, addr, make_base, make_price, order_id])
    if take_base > 0:
        take_price = take_quote * quote_scale // take_base
        event('SpotLimitTake', [pair, buy_or_sell, addr, take_base, take_price, order_id])


def spot_market_order(info, args):
    assert args['f'] == 'spot_market_order'
    sender = info['sender']
    addr = handle_lookup(sender)

    base_tick = args['a'][0]
    quote_tick = args['a'][2]
    assert set(base_tick) <= set(string.ascii_uppercase+'_')
    assert set(quote_tick) <= set(string.ascii_uppercase+'_')
    pair = '%s_%s' % tuple([base_tick, quote_tick])

    base_value = args['a'][1]
    quote_value = args['a'][3]
    quote_scale = 10 ** _decimal(quote_tick)
    spot_sell_start, _ = get('spot', f'{pair}_sell_start', 1)
    spot_buy_start, _ = get('spot', f'{pair}_buy_start', 1)

    take_base = 0
    take_quote = 0
    if quote_value is None and int(base_value) < 0:
        buy_or_sell = 'sell'
        base_value = int(args['a'][1])
        base_balance, _ = get(base_tick, 'balance', 0, addr)
        # base_sum = 0

        spot_buy_id = spot_buy_start
        while True:
            buy, _ = get('spot', f'{pair}_buy', None, str(spot_buy_id))
            if buy is None:
                break

            price = buy[3]
            dx_base = min(buy[1], -buy[2] * quote_scale // price, -base_value)
            dx_quote = dx_base * price // quote_scale
            if dx_base == 0 or dx_quote == 0:
                break
            if base_balance - dx_base < 0:
                break
            buy[1] -= dx_base
            buy[2] += dx_quote
            take_base += dx_base
            take_quote += dx_quote
            base_balance -= dx_base
            # base_sum += dx_base

            if buy[1] == 0 or buy[1] // price == 0:
                spot_buy_start = _remove_order(addr, pair, buy, spot_buy_start, 'buy')

                if buy[2] < 0:
                    balance, _ = get(quote_tick, 'balance', 0, buy[0])
                    balance -= buy[2]
                    assert balance >= 0
                    put(buy[0], quote_tick, 'balance', balance, buy[0])
    
                put(buy[0], 'spot', f'{pair}_buy', None, str(spot_buy_id))
            else:
                put(buy[0], 'spot', f'{pair}_buy', buy, str(spot_buy_id))

            balance, _ = get(base_tick, 'balance', 0, buy[0])
            balance += dx_base
            assert balance >= 0
            put(addr, base_tick, 'balance', balance, buy[0])

            base_value += dx_base
            assert base_value <= 0
            balance, _ = get(quote_tick, 'balance', 0, addr)
            balance += dx_quote
            assert balance >= 0
            put(addr, quote_tick, 'balance', balance, addr)

            if buy[4] is None:
                break
            spot_buy_id = buy[4]

        balance, _ = get(base_tick, 'balance', 0, addr)
        balance -= take_base
        assert balance >= 0
        put(addr, base_tick, 'balance', balance, addr)

    elif quote_value is None and int(base_value) > 0:
        buy_or_sell = 'buy'
        base_value = int(args['a'][1])
        quote_balance, _ = get(quote_tick, 'balance', 0, addr)
        # quote_sum = 0

        spot_sell_id = spot_sell_start
        while True:
            sell, _ = get('spot', f'{pair}_sell', None, str(spot_sell_id))
            if sell is None:
                break

            price = sell[3]
            dx_base = min(-sell[1], quote_balance * quote_scale // price, base_value)
            dx_quote = dx_base * price // quote_scale
            if dx_base == 0 or dx_quote == 0:
                break
            if quote_balance - dx_quote < 0:
                break

            sell[1] += dx_base
            sell[2] -= dx_quote
            take_base += dx_base
            take_quote += dx_quote
            quote_balance -= dx_quote
            # quote_sum += dx_quote

            if sell[1] == 0 or sell[1] // price == 0:
                spot_sell_start = _remove_order(addr, pair, sell, spot_sell_start, 'sell')

                if sell[1] < 0:
                    balance, _ = get(base_tick, 'balance', 0, sell[0])
                    balance -= sell[1]
                    assert balance >= 0
                    put(sell[0], base_tick, 'balance', balance, sell[0])

                put(sell[0], 'spot', f'{pair}_sell', None, str(spot_sell_id))
            else:
                put(sell[0], 'spot', f'{pair}_sell', sell, str(spot_sell_id))

            balance, _ = get(quote_tick, 'balance', 0, sell[0])
            balance += dx_quote
            assert balance >= 0
            put(addr, quote_tick, 'balance', balance, sell[0])

            base_value -= dx_base
            assert base_value >= 0
            balance, _ = get(base_tick, 'balance', 0, addr)
            balance += dx_base
            assert balance >= 0
            put(addr, base_tick, 'balance', balance, addr)

            if sell[4] is None:
                break
            spot_sell_id = sell[4]

        balance, _ = get(quote_tick, 'balance', 0, addr)
        balance -= take_quote
        assert balance >= 0
        put(addr, quote_tick, 'balance', balance, addr)

    elif base_value is None and int(quote_value) < 0:
        buy_or_sell = 'buy'
        quote_value = int(args['a'][3])
        quote_balance, _ = get(quote_tick, 'balance', 0, addr)
        # quote_sum = 0

        spot_sell_id = spot_sell_start
        while True:
            sell, _ = get('spot', f'{pair}_sell', None, str(spot_sell_id))
            if sell is None:
                break

            price = sell[3]
            dx_base = min(-sell[1], -quote_value * quote_scale // price)
            dx_quote = dx_base * price // quote_scale
            if dx_base == 0 or  dx_quote == 0:
                break
            if quote_balance - dx_quote < 0:
                break

            sell[1] += dx_base
            sell[2] -= dx_quote
            take_base += dx_base
            take_quote += dx_quote
            quote_balance -= dx_quote
            # quote_sum += dx_quote

            if sell[1] == 0 or sell[1] // price == 0:
                spot_sell_start = _remove_order(addr, pair, sell, spot_sell_start, 'sell')

                if sell[1] < 0:
                    balance, _ = get(base_tick, 'balance', 0, sell[0])
                    balance -= sell[1]
                    assert balance >= 0
                    put(sell[0], base_tick, 'balance', balance, sell[0])

                put(sell[0], 'spot', f'{pair}_sell', None, str(spot_sell_id))
            else:
                put(sell[0], 'spot', f'{pair}_sell', sell, str(spot_sell_id))

            balance, _ = get(quote_tick, 'balance', 0, sell[0])
            balance += dx_quote
            assert balance >= 0
            put(addr, quote_tick, 'balance', balance, sell[0])

            quote_value += dx_quote
            assert quote_value <= 0
            balance, _ = get(base_tick, 'balance', 0, addr)
            balance += dx_base
            assert balance >= 0
            put(addr, base_tick, 'balance', balance, addr)

            if sell[4] is None:
                break
            spot_sell_id = sell[4]

        balance, _ = get(quote_tick, 'balance', 0, addr)
        balance -= take_quote
        assert balance >= 0
        put(addr, quote_tick, 'balance', balance, addr)

    elif base_value is None and int(quote_value) > 0:
        buy_or_sell = 'sell'
        quote_value = int(args['a'][3])
        base_balance, _ = get(base_tick, 'balance', 0, addr)
        # base_sum = 0

        spot_buy_id = spot_buy_start
        while True:
            buy, _ = get('spot', f'{pair}_buy', None, str(spot_buy_id))
            if buy is None:
                break

            price = buy[3]
            dx_base = min(buy[1], base_balance, quote_value * quote_scale // price)
            dx_quote = dx_base * price // quote_scale
            if dx_base == 0 or dx_quote == 0:
                break
            if base_balance - dx_base < 0:
                break

            buy[1] -= dx_base
            buy[2] += dx_quote
            take_base += dx_base
            take_quote += dx_quote
            base_balance -= dx_base
            # base_sum += dx_base

            if buy[1] == 0 or buy[1] // price == 0:
                spot_buy_start = _remove_order(addr, pair, buy, spot_buy_start, 'buy')

                if buy[2] < 0:
                    balance, _ = get(quote_tick, 'balance', 0, buy[0])
                    balance -= buy[2]
                    assert balance >= 0
                    put(buy[0], quote_tick, 'balance', balance, buy[0])
    
                put(buy[0], 'spot', f'{pair}_buy', None, str(spot_buy_id))
            else:
                put(buy[0], 'spot', f'{pair}_buy', buy, str(spot_buy_id))

            balance, _ = get(base_tick, 'balance', 0, buy[0])
            balance += dx_base
            assert balance >= 0
            put(addr, base_tick, 'balance', balance, buy[0])

            quote_value -= dx_quote
            assert quote_value >= 0
            balance, _ = get(quote_tick, 'balance', 0, addr)
            balance += dx_quote
            assert balance >= 0
            put(addr, quote_tick, 'balance', balance, addr)

            if buy[4] is None:
                break
            spot_buy_id = buy[4]

        balance, _ = get(base_tick, 'balance', 0, addr)
        balance -= take_base
        assert balance >= 0
        put(addr, base_tick, 'balance', balance, addr)

    if take_base > 0:
        price = take_quote * quote_scale // take_base
        event('SpotMarketTake', [pair, buy_or_sell, addr, take_base, price])


def spot_limit_order_cancel(info, args):
    assert args['f'] == 'spot_limit_order_cancel'
    sender = info['sender']
    addr = handle_lookup(sender)

    base_tick = args['a'][0]
    assert set(base_tick) <= set(string.ascii_uppercase + '_')
    quote_tick = args['a'][1]
    assert set(quote_tick) <= set(string.ascii_uppercase + '_')
    buy_or_sell = args['a'][2]
    assert buy_or_sell in ['buy', 'sell']
    spot_order_id = int(args['a'][3])

    pair = '%s_%s' % (base_tick, quote_tick)
    order_key = f'{pair}_{buy_or_sell}'
    order, _ = get('spot', order_key, None, str(spot_order_id))

    if order is None:
        return

    assert order[0] == addr, "Sender is not the owner of the order"
    prev_order_id = order[4]
    next_order_id = order[5]

    if prev_order_id is not None:
        prev_order, _ = get('spot', order_key, None, str(prev_order_id))
        if prev_order:
            prev_order[5] = next_order_id
            put(prev_order[0], 'spot', order_key, prev_order, str(prev_order_id))

    if next_order_id is not None:
        next_order, _ = get('spot', order_key, None, str(next_order_id))
        if next_order:
            next_order[4] = prev_order_id
            put(next_order[0], 'spot', order_key, next_order, str(next_order_id))

    start_key = f'{pair}_{buy_or_sell}_start'
    current_start, _ = get('spot', start_key, 1)
    if current_start == spot_order_id:
        if prev_order_id is not None:
            put(addr, 'spot', start_key, prev_order_id)
        else:
            new_start_key = f'{pair}_{buy_or_sell}_new'
            new_start_val, _ = get('spot', new_start_key, 1)
            put(addr, 'spot', start_key, new_start_val)

    if buy_or_sell == 'sell':
        if order[1] < 0:
            balance, _ = get(base_tick, 'balance', 0, addr)
            balance -= order[1]
            put(addr, base_tick, 'balance', balance, addr)
    elif buy_or_sell == 'buy':
        if order[2] < 0:
            balance, _ = get(quote_tick, 'balance', 0, addr)
            balance -= order[2]
            put(addr, quote_tick, 'balance', balance, addr)

    put(addr, 'spot', order_key, None, str(spot_order_id))
    event('SpotOrderCancel', [spot_order_id, buy_or_sell, pair])


def spot_pair_create(info, args):
    assert args['f'] == 'spot_pair_create'
    sender = info['sender']
    addr = handle_lookup(sender)

    base_tick = args['a'][0]
    quote_tick = args['a'][1]

    quote_tokens, _ = get('spot', 'quote_tokens', [])
    assert quote_tick in quote_tokens, f"{quote_tick} is not a designated quote token"

    owner, _ = get('asset', 'owner', None, base_tick)
    assert owner == addr, f"Sender must be the owner of the base token ({base_tick})"
    pair = f'{base_tick}_{quote_tick}'
    enable, _ = get('spot', f'{pair}_enable', None)
    assert enable is None, "Pair already exists"

    put(addr, 'spot', f'{pair}_enable', True)
    put(addr, 'spot', f'{pair}_buy_start', 1)
    put(addr, 'spot', f'{pair}_buy_new', 1)
    put(addr, 'spot', f'{pair}_sell_start', 1)
    put(addr, 'spot', f'{pair}_sell_new', 1)


def spot_pair_enable(info, args):
    assert args['f'] == 'spot_pair_enable'
    sender = info['sender']
    addr = handle_lookup(sender)

    base_tick = args['a'][0]
    quote_tick = args['a'][1]
    pair = f'{base_tick}_{quote_tick}'

    owner, _ = get('asset', 'owner', None, base_tick)
    assert owner == addr, f"Sender must be the owner of the base token ({base_tick})"

    put(addr, 'spot', f'{pair}_enable', True)


def spot_pair_disable(info, args):
    assert args['f'] == 'spot_pair_disable'
    sender = info['sender']
    addr = handle_lookup(sender)

    base_tick = args['a'][0]
    quote_tick = args['a'][1]
    pair = f'{base_tick}_{quote_tick}'

    owner, _ = get('asset', 'owner', None, base_tick)
    assert owner == addr, f"Sender must be the owner of the base token ({base_tick})"

    put(addr, 'spot', f'{pair}_enable', False)


def spot_set_quote_tokens(info, args):
    assert args['f'] == 'spot_set_quote_tokens'
    sender = info['sender']
    addr = handle_lookup(sender)

    manager, _ = get('spot', 'manager', None)
    assert manager is not None, "Manager not set"
    assert addr == manager, "Only the manager can add quote tokens"

    new_tokens = args['a'][0]
    assert isinstance(new_tokens, list), "Quote tokens must be a list"

    quote_tokens, _ = get('spot', 'quote_tokens', [])

    for token in new_tokens:
        assert isinstance(token, str), "Token ticker must be a string"
        assert set(token) <= set(string.ascii_uppercase+'_'), "Invalid characters in token ticker"
        if token not in quote_tokens:
            quote_tokens.append(token)

    put(addr, 'spot', 'quote_tokens', quote_tokens)


def spot_vote_manager(info, args):
    assert args['f'] == 'spot_vote_manager'
    sender = info['sender']
    addr = handle_lookup(sender)

    committee_members, _ = get('committee', 'members', [])
    committee_members = set(committee_members)
    assert addr in committee_members, "Only committee members can vote"

    user = args['a'][0]
    assert isinstance(user, str), "User address must be a string"

    proposal_key = f'spot_manager:{user}'
    votes, _ = get('committee', 'proposal', [], proposal_key)
    votes = set(votes)
    votes.add(addr)

    if len(votes) >= len(committee_members) * 2 // 3:
        put(addr, 'spot', 'manager', user)
        put(addr, 'committee', 'proposal', [], proposal_key)
    else:
        put(addr, 'committee', 'proposal', list(votes), proposal_key)
← Back to Functions