Unit ID
EVNv1D9FYuy0HO+OI2hFgUZtShfH57reQC6Zuaktz04=
Received
30.07.2021 14:47:50
Confirmation delay (full node)
9 minutes 53 seconds
Confirmation delay (light node)
11 minutes 48 seconds
Messages
Definition
Definition: [ "autonomous agent", { "doc_url": "https://ostable.org/stable-oswap-oswap-arb.json", "init": "{ $min_reserve_delta = params.min_reserve_delta OTHERWISE 1e5; $stable_aa = params.stable_aa; $stable_params = definition[$stable_aa][1].params; $curve_aa = $stable_params.curve_aa; $curve_params = definition[$curve_aa][1].params; $m = $curve_params.m; $n = $curve_params.n; $decimals1 = $curve_params.decimals1; $decimals2 = $curve_params.decimals2; $reserve_asset_decimals = $curve_params.reserve_asset_decimals; // tokens $interest_asset = var[$curve_aa]['asset2']; $stable_asset = var[$stable_aa]['asset']; $reserve_asset = $curve_params.reserve_asset OTHERWISE 'base'; $reserve_oswap_aa = params.reserve_oswap_aa; $stable_oswap_aa = params.stable_oswap_aa; $stable_oswap_params = definition[$stable_oswap_aa][1].params; $reserve_oswap_params = definition[$reserve_oswap_aa][1].params; $stable_oswap_fee = $stable_oswap_params.swap_fee / 1e11; $reserve_oswap_fee = $reserve_oswap_params.swap_fee / 1e11; $oswap_net = (1-$stable_oswap_fee)*(1-$reserve_oswap_fee); $stable_imported_asset = $stable_oswap_params.asset0 == $stable_asset ? $stable_oswap_params.asset1 : $stable_oswap_params.asset0; $stable_imported_asset2 = $reserve_oswap_params.asset0 == $reserve_asset ? $reserve_oswap_params.asset1 : $reserve_oswap_params.asset0; if ($stable_imported_asset2 != $stable_imported_asset) bounce("stable imported asset mismatch " || $stable_imported_asset || ' != ' || $stable_imported_asset2); $get_oracles = () => { $oracles = var[$curve_aa]['oracles']; if ($oracles) return $oracles; $initial_oracles = []; if ($curve_params.oracle1 AND $curve_params.feed_name1) $initial_oracles[] = {oracle: $curve_params.oracle1, feed_name: $curve_params.feed_name1, op: $curve_params.op1 OTHERWISE '*'}; if ($curve_params.oracle2 AND $curve_params.feed_name2) $initial_oracles[] = {oracle: $curve_params.oracle2, feed_name: $curve_params.feed_name2, op: $curve_params.op2 OTHERWISE '*'}; if ($curve_params.oracle3 AND $curve_params.feed_name3) $initial_oracles[] = {oracle: $curve_params.oracle3, feed_name: $curve_params.feed_name3, op: $curve_params.op3 OTHERWISE '*'}; $initial_oracles }; $get_oracle_price = () => { $oracles = $get_oracles(); $oracle_price = reduce($oracles, 3, ($price, $oracle_info) => { if (!exists($price)) return false; $df = data_feed[[oracles=$oracle_info.oracle, feed_name=$oracle_info.feed_name, ifnone=false]]; if (!exists($df)) return false; ($oracle_info.op == '*') ? $price * $df : $price / $df }, 1); $oracle_price }; $get_leverage = () => $curve_params.leverage OTHERWISE 0; $get_growth_factor = () => { $interest_rate = var[$curve_aa]['interest_rate']; $term = (timestamp - var[$curve_aa]['rate_update_ts']) / (360 * 24 * 3600); // in years $growth_factor = var[$curve_aa]['growth_factor'] * (1 + $interest_rate)^$term; $growth_factor }; $g = $get_growth_factor(); $get_target_p2 = () => { $oracle_price = $get_oracle_price(); if (!exists($oracle_price)) return false; $target_p2 = $oracle_price^($get_leverage() - 1) * $g; $target_p2 }; $get_reserve = ($s1, $s2) => { $r = $s1^$m * $s2^$n; $r }; $get_p2 = ($s1, $s2) => { $p2 = $s1^$m * $n * $s2^($n-1); // derivative $p2 }; $get_slow_capacity_share = () => { $slow_capacity_share_var = var[$curve_aa]['slow_capacity_share']; if (exists($slow_capacity_share_var)) $slow_capacity_share = $slow_capacity_share_var; else if (exists($curve_params.slow_capacity_share)) $slow_capacity_share = $curve_params.slow_capacity_share; else $slow_capacity_share = 0.5; $slow_capacity_share }; $get_distance = ($p2, $target_p2) => (exists($p2) AND exists($target_p2)) ? abs($p2 - $target_p2) / min($p2, $target_p2) : 0; $fee_multiplier = var[$curve_aa]['fee_multiplier'] OTHERWISE $curve_params.fee_multiplier OTHERWISE 5; $get_fee = ($avg_reserve, $old_distance, $new_distance) => { $fee = ceil($fee_multiplier * $avg_reserve * ($new_distance - $old_distance) * ($new_distance + $old_distance)); $fee }; $get_reserve_needed = ($tokens1, $tokens2) => { $slow_capacity_share = $get_slow_capacity_share(); $fast_capacity_share = 1 - $slow_capacity_share; $initial_p2 = var[$curve_aa]['p2']; $target_p2 = $get_target_p2(); $distance = $get_distance($initial_p2, $target_p2); $reserve = var[$curve_aa]['reserve']; if (!$reserve AND ($tokens1 <= 0 OR $tokens2 <= 0)) bounce("initial mint must be with both tokens"); $new_supply1 = var[$curve_aa]['supply1'] + $tokens1; $new_supply2 = var[$curve_aa]['supply2'] + $tokens2; $s1 = $new_supply1 / 10^$decimals1; $s2 = $new_supply2 / 10^$decimals2; $r = $get_reserve($s1, $s2); $p2 = $get_p2($s1, $s2); $new_reserve = ceil($r * 10^$reserve_asset_decimals); $reserve_delta = $new_reserve - $reserve; // can be negative if ($tokens1 >= 0 AND $tokens2 >= 0 AND $reserve_delta < 0) bounce("issuing tokens while the reserve decreases?"); if ($tokens1 <= 0 AND $tokens2 <= 0 AND $reserve_delta > 0) bounce("burning tokens while the reserve increases?"); $new_distance = $get_distance($p2, $target_p2); $avg_reserve = ($reserve + $new_reserve) / 2; $fast_capacity = var[$curve_aa]['fast_capacity']; if ($distance == 0 AND $new_distance == 0){ $fee = 0; $reward = 0; $reserve_needed = $reserve_delta; } else if ($new_distance >= $distance){ // going away from the target price - pay a fee $reward = 0; $regular_fee = $get_fee($avg_reserve, $distance, $new_distance); $new_fast_capacity = $fast_capacity + $regular_fee * $fast_capacity_share; $distance_share = 1 - $distance/$new_distance; // reward that would be paid for returning the price back to $initial_p2 $reverse_reward = $distance_share * $new_fast_capacity; if ($regular_fee >= $reverse_reward) $fee = $regular_fee; else $fee = ceil($distance_share / (1 - $distance_share * $fast_capacity_share) * $fast_capacity); $reserve_needed = $reserve_delta + $fee; // negative for payouts } else { // going towards the target price - get a reward $fee = 0; $regular_reward = floor((1 - $new_distance/$distance) * $fast_capacity); if ($curve_params.capped_reward){ // if the reward would be greater than the fee for the reverse transaction, cap the reward by the fee $reverse_fee = $get_fee($avg_reserve, $new_distance, $distance); $reward = min($regular_reward, $reverse_fee); } else $reward = $regular_reward; $reserve_needed = $reserve_delta - $reward; // negative for payouts } $reserve_needed }; $aa2aa_bytes = 2000; $network_fee = ($reserve_asset == 'base') ? 4000 : 0; // for fees and pinging the DE $full_network_fee = $network_fee + ($reserve_asset == 'base' ? $aa2aa_bytes : 0); $get_amount_for_buying = ($tokens2) => { if ($tokens2 == 0) bounce("0 T2"); $reserve_needed = $get_reserve_needed(0, $tokens2); if ($reserve_needed < $min_reserve_delta) bounce("reserve amount too small " || $reserve_needed); $amount = $reserve_needed + $full_network_fee; $amount }; }", "messages": { "cases": [ { "if": "{ trigger.data.arb }", "init": "{ $s2 = var[$curve_aa]['supply2'] / 10^$decimals2; $p2 = var[$curve_aa]['p2']; $fc = var[$curve_aa]['fast_capacity'] / 10^$reserve_asset_decimals; $target_p2 = $get_target_p2(); $reward_share = ($n-1)*$fc/abs($target_p2-$p2)/$s2; $fee_share = 2 * $fee_multiplier * ($n-1)/$n * abs($target_p2-$p2)/$p2; $s = balance[$stable_oswap_aa][$stable_asset] / 10^$decimals2; $i1 = balance[$stable_oswap_aa][$stable_imported_asset]; $i2 = balance[$reserve_oswap_aa][$stable_imported_asset]; $ro = balance[$reserve_oswap_aa][$reserve_asset] / 10^$reserve_asset_decimals; $p_stable = $p2/$g; $p_oswap = ($i1/$s) / ($i2/$ro); // profitable to buy on ostable and sell on oswap if ($p2 < $target_p2 AND $reward_share < 1 AND $get_distance($p2, $target_p2) > 0.0001 AND $p_stable * (1-$reward_share) < $p_oswap * $oswap_net){ $delta_s = (sqrt($g*$ro*$i2/$i1*$s*$oswap_net / $p2 / (1 - ($n-1)*$fc/$s2/($target_p2-$p2))) - $i2/$i1*$s) / (1 + $i2/$i1); $delta_s2 = $delta_s/$g; if ($delta_s2 < 0) bounce("expected to buy T2, calc says should sell " || $delta_s2); $tokens2 = round($delta_s2 * 10^$decimals2); $amount = $get_amount_for_buying($tokens2); $from = 'curve'; } // profitable to buy on oswap and sell on ostable else if ($p_stable * (1-$fee_share) > $p_oswap / $oswap_net){ $delta_s = (sqrt($g*$ro*$i2/$i1*$s/$oswap_net / $p2 / (1 - 2*$fee_multiplier*($n-1)/$n^2*abs($target_p2-$p2)/$p2)) - $i2/$i1*$s) / (1 + $i2/$i1); $delta_s2 = $delta_s/$g; if ($delta_s2 > 0) bounce("expected to sell T2, calc says should buy " || $delta_s2); $delta_ro = $ro/(1+$i2/$i1+$i2/$i1*$s/$delta_s) / $oswap_net; if ($delta_ro > 0) bounce("delta_ro > 0: " || $delta_s2); $amount = round(-$delta_ro * 10^$reserve_asset_decimals); if ($amount < $min_reserve_delta) bounce("amount too small " || $amount); $from = 'oswap'; } else bounce("no arb opportunity exists"); // check if we have enough balance and scale down if necessary $max_amount = balance[$reserve_asset] - ($reserve_asset == 'base' ? 10000 : 0); if ($amount > $max_amount){ // not enough balance response['suboptimal'] = "optimal arb amount " || $amount || " but have only " || $max_amount; if ($tokens2){ $sent_tokens2 = round(0.7 * $tokens2 * $max_amount/$amount); // scale down $sent_amount = $get_amount_for_buying($sent_tokens2); if ($sent_amount > $max_amount) bounce("balance is too small for optimal arb and scaling down didn't help"); } else $sent_amount = $max_amount; } else { $sent_tokens2 = $tokens2; $sent_amount = $amount; } }", "messages": [ { "app": "payment", "payload": { "asset": "{$reserve_asset}", "outputs": [ { "address": "{$from == 'curve' ? $curve_aa : $reserve_oswap_aa}", "amount": "{ $sent_amount }" } ] } }, { "if": "{$from == 'curve'}", "app": "data", "payload": { "tokens2": "{$sent_tokens2}", "tokens2_to": "{$stable_aa}" } }, { "if": "{$from == 'oswap'}", "app": "data", "payload": { "to_aa": "{$stable_oswap_aa}", "to": "{this_address}" } }, { "app": "state", "state": "{ var['sent_amount'] = $sent_amount; response['sent_amount'] = $sent_amount; response['message'] = $from == 'curve' ? 'will arb by buying from the curve' : 'will arb by selling to the curve'; }" } ] }, { "if": "{ trigger.output[[asset=$stable_asset]] > 0 AND trigger.address == $stable_aa }", "init": "{ if (!var['sent_amount']) bounce('no sent amount when received from stable AA'); }", "messages": [ { "app": "payment", "payload": { "asset": "{$stable_asset}", "outputs": [ { "address": "{$stable_oswap_aa}", "amount": "{ trigger.output[[asset=$stable_asset]] }" } ] } }, { "app": "data", "payload": { "to_aa": "{$reserve_oswap_aa}", "to": "{this_address}" } } ] }, { "if": "{ trigger.output[[asset=$stable_asset]] > 0 AND trigger.address == $stable_oswap_aa }", "init": "{ if (!var['sent_amount']) bounce('no sent amount when received from oswap'); }", "messages": [ { "app": "payment", "payload": { "asset": "{$stable_asset}", "outputs": [ { "address": "{$stable_aa}", "amount": "{ trigger.output[[asset=$stable_asset]] }" } ] } }, { "app": "data", "payload": { "to": "{$curve_aa}" } } ] }, { "if": "{ trigger.output[[asset=$reserve_asset]] > 0 AND (trigger.address == $reserve_oswap_aa OR trigger.address == $curve_aa) }", "messages": [ { "app": "state", "state": "{ $sent_amount = var['sent_amount']; if (!$sent_amount) bounce('no sent amount'); $profit = trigger.output[[asset=$reserve_asset]] - $sent_amount; response['profit'] = $profit; // response['profit%'] = 100 * $profit/$sent_amount; if ($profit < 0) bounce('unprofitable ' || trigger.output[[asset=$reserve_asset]] || ' < ' || $sent_amount); var['sent_amount'] = false; }" } ] }, { "if": "{ trigger.data.withdraw AND trigger.address == params.owner }", "messages": [ { "app": "payment", "payload": { "asset": "{trigger.data.asset OTHERWISE $reserve_asset}", "outputs": [ { "address": "{params.owner}", "amount": "{ trigger.data.amount OTHERWISE '' }" } ] } } ] }, { "if": "{ trigger.output[[asset=$reserve_asset]] > 0 }", "messages": [ { "app": "state", "state": "{ response['message'] = 'added ' || trigger.output[[asset=$reserve_asset]]; }" } ] } ] } } ]
Technical information
Fees:
13,148 bytes
(451 headers, 12697 payload)
Level:7930768
Witnessed level:7930757
Main chain index:7615266
Latest included mc index:7615265
Status:stable/confirmed/final