Definition: [
    "autonomous agent",
    {
        "doc_url": "https://odex.ooo/odex.json",
        "init": "{
            $CHAINDEX = var['chainDEX'];
        }",
        "messages": {
            "cases": [
                {
                    "if": "{!$CHAINDEX}",
                    "messages": [
                        {
                            "app": "definition",
                            "payload": {
                                "definition": [
                                    "autonomous agent",
                                    {
                                        "{'init'}": "{
        "{$ODEX = '"||this_address||"';"
    ||    "$ODEX_KEY = 'balance_'||this_address||'_base';"
    ||    "if(trigger.output[[asset!=base]].asset != 'none')"
    ||        "bounce('no assets here');"
    ||    "$total_bal = balance[base]-storage_size-trigger.output[[asset=base]];}"
    }",
                                        "messages": {
                                            "{'cases'}": [
                                                {
                                                    "{'if'}": "{"{!var['low_fee_mode'] AND $total_bal+var[$ODEX][$ODEX_KEY] > 10000000}"}",
                                                    "messages": [
                                                        {
                                                            "app": "data",
                                                            "payload": {
                                                                "withdraw": 1,
                                                                "asset": "base",
                                                                "amount": "all"
                                                            }
                                                        },
                                                        {
                                                            "app": "payment",
                                                            "payload": {
                                                                "asset": "base",
                                                                "outputs": [
                                                                    {
                                                                        "address": "{"{$ODEX}"}",
                                                                        "amount": 10000
                                                                    }
                                                                ]
                                                            }
                                                        },
                                                        {
                                                            "app": "state",
                                                            "{'state'}": "{
                            "{var['low_fee_mode'] = true;"
                         ||   "response['message'] = 'Low fee mode active. Until AA depletion all operations are subsidized';}"
                        }"
                                                        }
                                                    ]
                                                },
                                                {
                                                    "{'if'}": "{
                    "{trigger.data.match}"
                }",
                                                    "{'init'}": "{
                    "{$my_order = trigger.data.order.signed_message;"
                 ||   "$order_key = trigger.data.addr_to_match;"
                 ||   "$order_pkg = var[$order_key];"
                 ||   "if(!$order_pkg)"
                 ||       "bounce('no order to match');"
                 ||   "if(typeof($order_pkg) != 'string')"
                 ||       "bounce('order already matched');"
                 ||   "if ($my_order.matcher != this_address)"
                 ||       "bounce('wrong matcher in order');"
                 ||   "if(!exists($my_order.matcher_fee_asset) or $my_order.matcher_fee_asset != 'base')"
                 ||       "bounce('matcher_fee_asset must be base');"
                 ||   "if(!is_valid_address($my_order.matcher) OR $my_order.matcher != this_address)"
                 ||       "bounce('I am the matcher, fuck offchain matchers');"
                 ||   "if($my_order.matcher_fee != 0)"
                 ||       "bounce('matcher fees not needed');"
                 ||   "if ($my_order.affiliate)"
                 ||       "bounce('affiliates not needed');"
                 ||   "$order_to_match = json_parse($order_pkg);}"
                }",
                                                    "messages": [
                                                        {
                                                            "app": "data",
                                                            "payload": {
                                                                "order1": "{"{trigger.data.order}"}",
                                                                "order2": "{"{$order_to_match}"}"
                                                            }
                                                        },
                                                        {
                                                            "app": "payment",
                                                            "payload": {
                                                                "asset": "base",
                                                                "outputs": [
                                                                    {
                                                                        "address": "{"{$ODEX}"}",
                                                                        "amount": "{"{var['low_fee_mode']?1:9000}"}"
                                                                    }
                                                                ]
                                                            }
                                                        },
                                                        {
                                                            "{'if'}": "{"{var['low_fee_mode']}"}",
                                                            "app": "payment",
                                                            "payload": {
                                                                "asset": "base",
                                                                "outputs": [
                                                                    {
                                                                        "address": "{"{trigger.address}"}",
                                                                        "amount": 9500
                                                                    }
                                                                ]
                                                            }
                                                        },
                                                        {
                                                            "app": "state",
                                                            "{'state'}": "{
                            "$id1 = sha256($my_order.address || $my_order.sell_asset || $my_order.buy_asset || $my_order.sell_amount || $my_order.price || ($my_order.nonce otherwise '') || (trigger.data.order.last_ball_unit otherwise '-'));"
                       ||     "$order2 = $order_to_match.signed_message;"
                       ||     "$id2 = sha256($order2.address || $order2.sell_asset || $order2.buy_asset || $order2.sell_amount || $order2.price || ($order2.nonce otherwise '') || ($order_to_match.last_ball_unit otherwise '-'));"
                       ||     "$amount_left1 = var['amount_left_' || $id1] otherwise $order1.sell_amount;"
                       ||     "$amount_left2 = var['amount_left_' || $id2] otherwise $order2.sell_amount;"
                       ||     "$maker_price = $my_order.price;"
                       ||     "$buy_amount1 = round($amount_left1 * $my_order.price);"
                       ||     "if ($buy_amount1 > $amount_left2)"
                       ||         "var[$order_key] = false;"
                       ||     "if(var['low_fee_mode'] AND $total_bal < 30000)"
                       ||         "var['low_fee_mode'] = false;"
                        }"
                                                        }
                                                    ]
                                                },
                                                {
                                                    "{'if'}": "{
                    "{trigger.data.match_stored}"
                }",
                                                    "{'init'}": "{
                    "{$to_match = trigger.data.addr_to_match;"
                ||    "$str1 = var[trigger.address];"
                ||    "$str2 = var[$to_match];"
                ||    "if(!var[trigger.address])"
                ||        "bounce('you have not stored order');"
                ||    "if(!$to_match OR !is_valid_address($to_match) OR !$order2)"
                ||        "bounce('addr_to_match must be an address with stored order');"
                ||    "$order1 = json_parse($str1);"
                ||    "$order2 = json_parse($str2);}"
                }",
                                                    "messages": [
                                                        {
                                                            "app": "data",
                                                            "payload": {
                                                                "order1": "{"{$order1}"}",
                                                                "order2": "{"{$order2}"}"
                                                            }
                                                        },
                                                        {
                                                            "app": "payment",
                                                            "payload": {
                                                                "asset": "base",
                                                                "outputs": [
                                                                    {
                                                                        "address": "{"{$ODEX}"}",
                                                                        "amount": "{"{var['low_fee_mode']?1:9000}"}"
                                                                    }
                                                                ]
                                                            }
                                                        },
                                                        {
                                                            "app": "payment",
                                                            "payload": {
                                                                "asset": "base",
                                                                "outputs": [
                                                                    {
                                                                        "address": "{"{trigger.address}"}",
                                                                        "amount": 10000
                                                                    }
                                                                ]
                                                            }
                                                        },
                                                        {
                                                            "app": "state",
                                                            "{'state'}": "{
                            "{$order1s = $order1.signed_message;"
                ||            "$order2s = $order2.signed_message;"
                ||            "$id1 = sha256($order1s.address || $order1s.sell_asset || $order1s.buy_asset || $order1s.sell_amount || $order1s.price || ($order1s.nonce otherwise '') || ($order1.last_ball_unit otherwise '-'));"
                 ||           "$id2 = sha256($order2s.address || $order2s.sell_asset || $order2s.buy_asset || $order2s.sell_amount || $order2s.price || ($order2s.nonce otherwise '') || ($order2.last_ball_unit otherwise '-'));"
                 ||           "$amount_left1 = var['amount_left_' || $id1] otherwise $order1s.sell_amount;"
                 ||           "$amount_left2 = var['amount_left_' || $id2] otherwise $order2s.sell_amount;"
                 ||           "$maker_price = $order1s.price;"
                 ||           "$buy_amount1 = round($amount_left1 * $order1s.price);"
                  ||          "if ($buy_amount1 > $amount_left2){"
                  ||              "$id_smaller = $id2;"
                  ||              "$id_larger = $id1;"
                 ||               "$amount_left_larger = $amount_left1;"
                  ||              "$buy_amount2 = round($amount_left2 / $maker_price);"
                  ||              "$buy_amount_smaller = $buy_amount2;"
                  ||          "}"
                  ||          "else{"
                 ||               "$id_smaller = $id1;"
                  ||              "$id_larger = $id2;"
                  ||              "$amount_left_larger = $amount_left2;"
                  ||              "$buy_amount_smaller = $buy_amount1;"
                  ||          "}"
                 ||           "$new_amount_left_larger = $amount_left_larger - $buy_amount_smaller;"
                 ||           "if($id1 == $id_smaller || (!$new_amount_left_larger AND $id1 == $id_larger))"
                  ||              "var[trigger.address] = false;"
                 ||           "if($id2 == $id_smaller || (!$new_amount_left_larger AND $id2 == $id_larger))"  
                 ||               "var[$to_match] = false;}"
                        }"
                                                        }
                                                    ]
                                                },
                                                {
                                                    "{'if'}": "{
                    "{trigger.data.cancel}"
                }",
                                                    "{'init'}": "{
                    "{$str1 = var[trigger.address];"
                ||    "if(!$str1)"
                ||        "bounce('you have not stored order');"
                 ||   "$order1 = json_parse($str1);}"
                }",
                                                    "messages": [
                                                        {
                                                            "app": "data",
                                                            "payload": {
                                                                "cancel": 1,
                                                                "order": "{"{$order}"}"
                                                            }
                                                        },
                                                        {
                                                            "app": "payment",
                                                            "payload": {
                                                                "asset": "base",
                                                                "outputs": [
                                                                    {
                                                                        "address": "{"{$ODEX}"}",
                                                                        "amount": "{"{var['low_fee_mode']?1000:9000}"}"
                                                                    }
                                                                ]
                                                            }
                                                        },
                                                        {
                                                            "{'if'}": "{"{var['low_fee_mode']}"}",
                                                            "app": "payment",
                                                            "payload": {
                                                                "asset": "base",
                                                                "outputs": [
                                                                    {
                                                                        "address": "{"{trigger.address}"}",
                                                                        "amount": 8500
                                                                    }
                                                                ]
                                                            }
                                                        },
                                                        {
                                                            "app": "state",
                                                            "{'state'}": "{
                            "{var[trigger.address] = false;}"
                        }"
                                                        }
                                                    ]
                                                },
                                                {
                                                    "{'if'}": "{
                    "{trigger.data.store}"
                }",
                                                    "{'init'}": "{
                    "{if(var[trigger.address])"
  ||                      "bounce('already stored, must be matched or cancelled first');"
    ||                "$order = trigger.data.order.signed_message;"
      ||              "if (!$order.sell_amount OR $order.sell_amount <= 0 or !$order.price or $order.price <= 0)"
        ||                "bounce('wrong data in order');"
          ||          "if (!$order.sell_asset or !$order.buy_asset)"
            ||            "bounce('buy_asset or sell_asset missing');"
              ||      "if(($order.sell_asset != 'base' and !asset[$order.sell_asset].exists) or ($order.buy_asset != 'base' and !asset[$order.buy_asset].exists))"
                ||        "bounce('invalid asset');"
                  ||  "if ($order.sell_asset == $order.buy_asset)"
                    ||    "bounce('same asset');"
||                    "if ($order.matcher != this_address)"
  ||                      "bounce('wrong matcher in order, I am the matcher, fuck offchain matchers');"
    ||                "if ($order.aa != $ODEX )"
      ||                  "bounce('wrong aa in order');"
        ||            "if ($order.expiry_ts AND $order.expiry_ts <= timestamp)"
          ||              "bounce('order expired');"
            ||        "if(!exists($order.matcher_fee_asset) or $order.matcher_fee_asset != 'base')"
              ||          "bounce('matcher_fee_asset must be base');"
                ||    "$sell_key1 = 'balance_' || $order.address || '_' || $order.sell_asset;"
                  ||  "if($order.sell_amount <= 0)"
                    ||    "bounce('sell_amount must be greater than 0');"
||                    "$id1 = sha256($order.address || $order.sell_asset || $order.buy_asset || $order.sell_amount || $order.price || ($order.nonce otherwise '') || (trigger.data.order.last_ball_unit otherwise '-'));"
  ||                  "if (var[$ODEX]['executed_' || $id1])"
    ||                    "bounce('order already executed');"
      ||              "if (var[$ODEX]['cancelled_' || $id1])"
        ||                "bounce('order already cancelled');"
          ||          "$amount_left1 = var[$ODEX]['amount_left_' || $id1] otherwise $order.sell_amount;"
            ||        "if ($amount_left1 > var[$ODEX][$sell_key1] + 0)"
              ||          "bounce('not sufficient balance in sell asset to complete order');"
                ||    "if($order.matcher_fee != 0)"
                  ||      "bounce('matcher fees not needed');"
                    ||"if ($order.affiliate)"
                      ||  "bounce('affiliates not needed');"
||                    "$signer1 = trigger.data.order.authors[0].address;"
  ||                  "if($order.address != trigger.address OR $signer1 != $order.address)"
    ||                    "bounce('you have to store and sign your order');"
      ||              "if (!is_valid_signed_package(trigger.data.order, $signer1))"
        ||                "bounce('bad signature of order');"
          ||          "$base_key1 = 'balance_' || $order.address || '_base';"
            ||        "if (var[$ODEX][$base_key1] <= 1000)"
              ||          "bounce('not enough balance for AA fees');"
                }",
                                                    "messages": [
                                                        {
                                                            "{'if'}": "{"{var['low_fee_mode']}"}",
                                                            "app": "payment",
                                                            "payload": {
                                                                "asset": "base",
                                                                "outputs": [
                                                                    {
                                                                        "address": "{"{trigger.address}"}",
                                                                        "amount": 9500
                                                                    }
                                                                ]
                                                            }
                                                        },
                                                        {
                                                            "app": "state",
                                                            "{'state'}": "{
                            "{var[trigger.address] = json_stringify(trigger.data.order);"
                         ||   "if(var['low_fee_mode'] AND $total_bal < 30000)"
                         ||       "var['low_fee_mode'] = false;"
                         ||   "response['message'] = 'Stored order with id '||trigger.data.id;}"
                        }"
                                                        }
                                                    ]
                                                },
                                                {
                                                    "messages": [
                                                        {
                                                            "app": "state",
                                                            "{'state'}": "{
                            "{response['message'] = 'bytes received';}"
                        }"
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    }
                                ]
                            }
                        },
                        {
                            "app": "state",
                            "state": "{
                                var['chainDEX'] = unit[response_unit].messages[[.app='definition']].payload.address;
                            }"
                        }
                    ]
                },
                {
                    "if": "{ trigger.data.withdraw AND trigger.data.asset AND trigger.data.amount AND trigger.output[[asset=base]] >= 10000 }",
                    "init": "{
                    if (trigger.data.to){
                        if (!is_valid_address(trigger.data.to))
                            bounce("invalid withdrawal address: " || trigger.data.to);
                        $address = trigger.data.to;
                        // authorized address can initiate withdrawals in favor of the owner
                        $owner_address = var['grant_' || $address || '_to_' || trigger.address] ? $address : trigger.address;
                    }
                    else {
                        $address = trigger.address;
                        $owner_address = trigger.address;
                    }
                    $key = 'balance_' || $owner_address || '_' || trigger.data.asset;
                    $balance = var[$key] + 0;
                    $ord = json_parse(var[$CHAINDEX][trigger.address]);
                    if($ord){
                        $ords = $ord.signed_message;
                        if($ords.sell_asset == trigger.data.asset){
                            $id_ord = sha256($ords.address || $ords.sell_asset || $ords.buy_asset || $ords.sell_amount || $ords.price || ($ords.nonce otherwise '') || $ord.last_ball_unit);
                            $opened_order = var['amount_left_' || $id_ord] otherwise $ords.sell_amount;
                            if(trigger.data.amount == 'all' OR (trigger.data.amount+$opened_order) > $balance)
                                bounce('cannot withdraw, please cancel opened order on '||$CHAINDEX);
                            if ($ords.sell_asset == 'base' AND (trigger.data.amount == 'all' or var['balance_' || $owner_address || '_base' ]-trigger.data.amount <= 1000))
                                bounce('not enough balance for AA fees, leave 1000 at least');
                        }
                    }
                        
                    
                    if (trigger.data.amount == 'all')
                        $amount = $balance;
                    else if (trigger.data.amount > $balance)
                        bounce("withdrawal amount too large, balance: " || $balance);
                    else
                        $amount = trigger.data.amount;
                }",
                    "messages": [
                        {
                            "app": "payment",
                            "payload": {
                                "asset": "{trigger.data.asset}",
                                "outputs": [
                                    {
                                        "address": "{$address}",
                                        "amount": "{$amount}"
                                    }
                                ]
                            }
                        },
                        {
                            "app": "state",
                            "state": "{
                            var[$key] -= $amount;
                            response[$owner_address || '_' || trigger.data.asset] = -$amount;
                            response['event'] = 'withdrawal';
                        }"
                        }
                    ]
                },
                {
                    "if": "{
                    $order1 = trigger.data.order1.signed_message;
                    $order2 = trigger.data.order2.signed_message;
                    if (!$order1.sell_asset OR !$order2.sell_asset)
                        return false;
                    if ($order1.sell_asset != $order2.buy_asset OR $order1.buy_asset != $order2.sell_asset)
                        bounce('assets do not match');
                    if ($order1.sell_asset == $order1.buy_asset)
                        bounce('same asset');
                    if ($order1.matcher != trigger.address)
                        bounce('wrong matcher in order1');
                    if ($order2.matcher != trigger.address)
                        bounce('wrong matcher in order2');
                    
                    if ($order1.aa != this_address)
                        bounce('wrong aa in order1');
                    if ($order2.aa != this_address)
                        bounce('wrong aa in order2');
                    if ($order1.expiry_ts AND $order1.expiry_ts <= timestamp)
                        bounce("order1 expired");
                    if ($order2.expiry_ts AND $order2.expiry_ts <= timestamp)
                        bounce("order2 expired");
                    $sell_key1 = 'balance_' || $order1.address || '_' || $order1.sell_asset;
                    $sell_key2 = 'balance_' || $order2.address || '_' || $order2.sell_asset;
                    $id1 = sha256($order1.address || $order1.sell_asset || $order1.buy_asset || $order1.sell_amount || $order1.price || ($order1.nonce otherwise '') || (trigger.data.order1.last_ball_unit otherwise '-'));
                    $id2 = sha256($order2.address || $order2.sell_asset || $order2.buy_asset || $order2.sell_amount || $order2.price || ($order2.nonce otherwise '') || (trigger.data.order2.last_ball_unit otherwise '-'));
                    if (var['executed_' || $id1])
                        bounce('order1 already executed');
                    if (var['executed_' || $id2])
                        bounce('order2 already executed');
                    if (var['cancelled_' || $id1])
                        bounce('order1 already cancelled');
                    if (var['cancelled_' || $id2])
                        bounce('order2 already cancelled');
                    $amount_left1 = var['amount_left_' || $id1] otherwise $order1.sell_amount;
                    $amount_left2 = var['amount_left_' || $id2] otherwise $order2.sell_amount;
                    // check balances
                    if ($amount_left1 > var[$sell_key1] + 0)
                        bounce('not sufficient balance in sell asset to complete order1');
                    if ($amount_left2 > var[$sell_key2] + 0)
                        bounce('not sufficient balance in sell asset to complete order2');
                    // check if prices match
                    $maker_price = $order1.price;
                    $buy_amount1 = round($amount_left1 * $order1.price);
                    if ($buy_amount1 > $amount_left2){ // order2 is the smaller one
                        $order_smaller = $order2;
                        $order_larger = $order1;
                        $id_smaller = $id2;
                        $id_larger = $id1;
                        $amount_left_smaller = $amount_left2;
                        $amount_left_larger = $amount_left1;
                        $buy_amount2 = round($amount_left2 / $maker_price); // using maker price instead of our $order2.price
                        $buy_amount_smaller = $buy_amount2;
                        $amount_sold2 = $amount_left2;
                        $amount_sold1 = $buy_amount2;
                    }
                    else{ // order1 is the smaller one
                        $order_smaller = $order1;
                        $order_larger = $order2;
                        $id_smaller = $id1;
                        $id_larger = $id2;
                        $amount_left_smaller = $amount_left1;
                        $amount_left_larger = $amount_left2;
                        $buy_amount_smaller = $buy_amount1;
                        $amount_sold1 = $amount_left1;
                        $amount_sold2 = $buy_amount1;
                    }
                    // if $order1.price * $order2.price > 1 then you can make a profit by going through two opposite trades or being on both sides of a single trade. This is only possible for market-maker orders, which are not matched.
                    // order of multiplication is important as it can affect rounding errors
                    if (round($amount_left_smaller * ($order1.price * $order2.price)) > $amount_left_smaller)
                        bounce("price mismatch");
                    $expected_buy_amount_larger = round(($buy_amount_smaller-1) * $order_larger.price); // -1 to account for rounding errors
                    if ($expected_buy_amount_larger > $amount_left_smaller)
                        bounce("price mismatch: larger user " || $id_larger || " doesn't like the price, he gets less than expects: " || $amount_left_smaller || " < " || $expected_buy_amount_larger || ", amounts left: " || $amount_left1 || ", " || $amount_left2);
                    // matcher fees
                    $max_matcher_fee1 = round($order1.matcher_fee * $amount_sold1/$order1.sell_amount);
                    $max_matcher_fee2 = round($order2.matcher_fee * $amount_sold2/$order2.sell_amount);
                    $matcher_fee1 = (!exists(trigger.data.matcher_fee1) OR trigger.data.matcher_fee1 == 'default') ? $max_matcher_fee1 : trigger.data.matcher_fee1;
                    $matcher_fee2 = (!exists(trigger.data.matcher_fee2) OR trigger.data.matcher_fee2 == 'default') ? $max_matcher_fee2 : trigger.data.matcher_fee2;
                    // the formula will fail if matcher_fee1/matcher_fee2 is not a number
                    if ($matcher_fee1 > $max_matcher_fee1)
                        bounce('matcher_fee1 is too large');
                    if ($matcher_fee2 > $max_matcher_fee2)
                        bounce('matcher_fee2 is too large');
                    
                    // affiliates
                    if ($order1.affiliate){
                        if (!$order1.affiliate_fee_asset)
                            bounce('no affiliate_fee_asset in order1');
                        if ($order1.affiliate_fee < 0) // will error if none or not a number
                            bounce('affiliate_fee < 0 in order1');
                        $affiliate_fee1 = round($order1.affiliate_fee * $amount_sold1/$order1.sell_amount);
                    }
                    if ($order2.affiliate){
                        if (!$order2.affiliate_fee_asset)
                            bounce('no affiliate_fee_asset in order2');
                        if ($order2.affiliate_fee < 0) // will error if none or not a number
                            bounce('affiliate_fee < 0 in order2');
                        $affiliate_fee2 = round($order2.affiliate_fee * $amount_sold2/$order2.sell_amount);
                    }
                    $signer1 = trigger.data.order1.authors[0].address;
                    $signer2 = trigger.data.order2.authors[0].address;
                    if ($signer1 != $order1.address AND !var['grant_' || $order1.address || '_to_' || $signer1])
                        bounce("order1 signer was not authorized to sign");
                    if ($signer2 != $order2.address AND !var['grant_' || $order2.address || '_to_' || $signer2])
                        bounce("order2 signer was not authorized to sign");
                    if (!is_valid_signed_package(trigger.data.order1, $signer1))
                        bounce('bad signature of order1');
                    if (!is_valid_signed_package(trigger.data.order2, $signer2))
                        bounce('bad signature of order2');
                    true
                }",
                    "messages": [
                        {
                            "app": "state",
                            "state": "{
                        $buy_key1 = 'balance_' || $order1.address || '_' || $order1.buy_asset;
                        $buy_key2 = 'balance_' || $order2.address || '_' || $order2.buy_asset;
                        var[$sell_key1] -= $amount_sold1;
                        var[$sell_key2] -= $amount_sold2;
                        var[$buy_key1] += $amount_sold2;
                        var[$buy_key2] += $amount_sold1;
                        // matcher fees
                        $matcher_fee_user_key1 = 'balance_' || $order1.address || '_' || $order1.matcher_fee_asset;
                        $matcher_fee_user_key2 = 'balance_' || $order2.address || '_' || $order2.matcher_fee_asset;
                        $matcher_fee_matcher_key1 = 'balance_' || $order1.matcher || '_' || $order1.matcher_fee_asset;
                        $matcher_fee_matcher_key2 = 'balance_' || $order2.matcher || '_' || $order2.matcher_fee_asset;
                        var[$matcher_fee_matcher_key1] += $matcher_fee1;
                        var[$matcher_fee_matcher_key2] += $matcher_fee2;
                        var[$matcher_fee_user_key1] -= $matcher_fee1;
                        var[$matcher_fee_user_key2] -= $matcher_fee2;
                        if (var[$matcher_fee_user_key1] < 0)
                            bounce("not enough user1 balance for matcher fees");
                        if (var[$matcher_fee_user_key2] < 0)
                            bounce("not enough user2 balance for matcher fees");
                        // fees can be negative
                        if (var[$matcher_fee_matcher_key1] < 0)
                            bounce("not enough matcher order1.matcher_fee_asset balance for matcher fees");
                        if (var[$matcher_fee_matcher_key2] < 0)
                            bounce("not enough matcher order2.matcher_fee_asset balance for matcher fees");
                        // affiliate fees
                        if ($order1.affiliate AND $affiliate_fee1){
                            $affiliate_fee_user_key1 = 'balance_' || $order1.address || '_' || $order1.affiliate_fee_asset;
                            $affiliate_fee_affiliate_key1 = 'balance_' || $order1.affiliate || '_' || $order1.affiliate_fee_asset;
                            var[$affiliate_fee_user_key1] -= $affiliate_fee1;
                            var[$affiliate_fee_affiliate_key1] += $affiliate_fee1;
                            if (var[$affiliate_fee_user_key1] < 0)
                                bounce("not enough user1 balance for affiliate fees");
                        }
                        if ($order2.affiliate AND $affiliate_fee2){
                            $affiliate_fee_user_key2 = 'balance_' || $order2.address || '_' || $order2.affiliate_fee_asset;
                            $affiliate_fee_affiliate_key2 = 'balance_' || $order2.affiliate || '_' || $order2.affiliate_fee_asset;
                            var[$affiliate_fee_user_key2] -= $affiliate_fee2;
                            var[$affiliate_fee_affiliate_key2] += $affiliate_fee2;
                            if (var[$affiliate_fee_user_key2] < 0)
                                bounce("not enough user2 balance for affiliate fees");
                        }
                        // AA fees
                        $aa_fee = 1000;
                        $base_key1 = 'balance_' || $order1.address || '_base';
                        $base_key2 = 'balance_' || $order2.address || '_base';
                        var[$base_key1] -= $aa_fee;
                        var[$base_key2] -= $aa_fee;
                        if (var[$base_key1] < 0 OR var[$base_key2] < 0)
                            bounce('not enough balance for AA fees');
                        
                        // refund the matcher for bounce fees
                        var['balance_' || trigger.address || '_base'] += trigger.output[[asset=base]];
                        // update order states
                        var['executed_' || $id_smaller] = 1;
                        var['amount_left_' || $id_smaller] = false;
                        response['executed_' || $id_smaller] = 1;
                        $new_amount_left_larger = $amount_left_larger - $buy_amount_smaller;
                        if ($new_amount_left_larger < 0)
                            bounce("panic: new_amount_left_larger < 0");
                        if ($new_amount_left_larger){
                            var['amount_left_' || $id_larger] = $new_amount_left_larger;
                            response['amount_left_' || $id_larger] = $new_amount_left_larger;
                        }
                        else{
                            var['executed_' || $id_larger] = 1;
                            var['amount_left_' || $id_larger] = false;
                            response['executed_' || $id_larger] = 1;
                        }
                        // parsable response for transaction log
                        if ($order1.address != $order2.address){
                            response[$order1.address || '_' || $order1.sell_asset] = -$amount_sold1;
                            response[$order2.address || '_' || $order2.buy_asset] = $amount_sold1;
                            response[$order1.address || '_' || $order1.buy_asset] = $amount_sold2;
                            response[$order2.address || '_' || $order2.sell_asset] = -$amount_sold2;
                        }
                        else{ // self-match
                            response[$order1.address || '_' || $order1.sell_asset] = 0;
                            response[$order1.address || '_' || $order1.buy_asset] = 0;
                        }
                        response['amount_' || $order1.sell_asset] = $amount_sold1;
                        response['amount_' || $order1.buy_asset] = $amount_sold2;
                        response['event'] = 'trade';
                    }"
                        }
                    ]
                },
                {
                    "if": "{trigger.data.cancel AND trigger.data.order}",
                    "init": "{
                    $order = trigger.data.order.signed_message;
                    if (!$order.sell_asset OR !$order.buy_asset OR !$order.sell_amount OR !$order.price OR !$order.address OR !$order.matcher)
                        bounce("missing data in order");
                    if(trigger.address != $CHAINDEX){
                        if($order.matcher == $CHAINDEX)
                            bounce('please cancel order through '||$CHAINDEX);
                        if ($order.address != trigger.address AND !var['grant_' || $order.address || '_to_' || trigger.address])
                            bounce('not your order');
                    }
                    
                    
                    if ($order.aa != this_address)
                        bounce('wrong aa in order');
                    if ($order.expiry_ts AND $order.expiry_ts <= timestamp)
                        bounce("order expired");
                    $id = sha256($order.address || $order.sell_asset || $order.buy_asset || $order.sell_amount || $order.price || ($order.nonce otherwise '') || trigger.data.order.last_ball_unit);
                    if (var['executed_' || $id])
                        bounce('order already executed');
                    
                    $signer = trigger.data.order.authors[0].address;
                    if ($signer != $order.address AND !var['grant_' || $order.address || '_to_' || $signer])
                        bounce("order signer was not authorized to sign");
                    if (!is_valid_signed_package(trigger.data.order, $signer))
                        bounce('bad signature of order');
                }",
                    "messages": [
                        {
                            "app": "state",
                            "state": "{
                        var['cancelled_' || $id] = 1;
                        response['message'] = 'cancelled order ' || $id;
                        response['id'] = $id;
                        response['event'] = 'cancel';
                        $cancel_fee = 1000;
                        $key = 'balance_' || ($order.matcher == $CHAINDEX?$CHAINDEX:$order.address) || '_base';
                        var[$key] += trigger.output[[asset=base]] - $cancel_fee;
                        if (var[$key] < 0) // received funds can be less than bounce fees if received from an AA
                            bounce("balance would drop below 0");
                    }"
                        }
                    ]
                },
                {
                    "if": "{ trigger.data.revoke AND trigger.data.address AND trigger.output[[asset=base]] >= 10000 }",
                    "init": "{
                    if (!is_valid_address(trigger.data.address))
                        bounce("invalid address");
                    if (trigger.data.address == trigger.address)
                        bounce("same address");
                }",
                    "messages": [
                        {
                            "app": "state",
                            "state": "{
                        var['grant_' || trigger.address || '_to_' || trigger.data.address] = false;
                        response['message'] = 'revoked authorization from ' || trigger.data.address || ' to sign orders for you';
                        response['address'] = trigger.data.address;
                        response['event'] = 'revocation';
                    }"
                        }
                    ]
                },
                {
                    "if": "{ (!trigger.data OR trigger.data.to OR trigger.data.grant AND trigger.data.address) AND trigger.output[[asset=base]] >= 10000 }",
                    "init": "{
                    if (trigger.data.grant){
                        if (!is_valid_address(trigger.data.address))
                            bounce("invalid address");
                        if (trigger.data.address == trigger.address)
                            bounce("same address");
                        if (trigger.data.to)
                            bounce("grant and to at the same time");
                    }
                }",
                    "messages": [
                        {
                            "app": "state",
                            "state": "{
                        if (trigger.data.grant){
                            var['grant_' || trigger.address || '_to_' || trigger.data.address] = 1;
                            $response_grant = 'authorized ' || trigger.data.address || ' to sign orders for you, also ';
                            response['authorized_address'] = trigger.data.address;
                            response['event'] = 'grant';
                        }
                        $asset = trigger.output[[asset!=base]].asset;
                        if ($asset == 'ambiguous')
                            bounce('ambiguous asset');
                        if (trigger.data.to){
                            if (!is_valid_address(trigger.data.to))
                                bounce("invalid deposit address: " || trigger.data.to);
                            $address = trigger.data.to;
                        }
                        else
                            $address = trigger.address;
                        $base_key = 'balance_'||$address||'_'||'base';
                        var[$base_key] = var[$base_key] + trigger.output[[asset=base]];
                        $response_base = trigger.output[[asset=base]] || ' bytes';
                        response[$address || '_base'] = trigger.output[[asset=base]];
                        if ($asset != 'none'){
                            $asset_key = 'balance_'||$address||'_'||$asset;
                            var[$asset_key] = var[$asset_key] + trigger.output[[asset=$asset]];
                            $response_asset = ' and ' || trigger.output[[asset=$asset]] || ' of ' || $asset;
                            response[$address || '_' || $asset] = trigger.output[[asset=$asset]];
                        }
                        response['message'] = ($response_grant otherwise '') || 'accepted coins: ' || $response_base || ($response_asset otherwise '');
                        response[trigger.data.grant ? 'secondary_event' : 'event'] = 'deposit';
                    }"
                        }
                    ]
                }
            ]
        }
    }
]