Unit ID
GC4cJLmNRpFNQxQ8wPQ5Wc9deIWooqf7aJSZsMGHvpI=
Received
12.01.2022 00:15:18
Confirmation delay (full node)
3 minutes 10 seconds
Confirmation delay (light node)
4 minutes 19 seconds
Messages
Definition
Definition: [ "autonomous agent", { "getters": "{ $get_leverages = () => [2, 5, 10, 20, 50, 100]; $singularity_threshold = 0.1; $trade_merge_period = 1; $update_recent_data = ($recent, $p, $final_p, $trigger_initial_address, $tax_token, $traded_amount, $paid_tax, $period_length) => { $period_start_ts = floor(timestamp / $period_length) * $period_length; $pmin = min($p, $final_p); $pmax = max($p, $final_p); if (+$recent.current.start_ts < $period_start_ts){ $recent.prev = $recent.current; $recent.current = {start_ts: $period_start_ts, pmin: $pmin, pmax: $pmax}; } else{ $recent.current.pmin = min($recent.current.pmin, $pmin); $recent.current.pmax = max($recent.current.pmax, $pmax); } if ($recent.last_trade AND $recent.last_trade.address == $trigger_initial_address AND $recent.last_ts >= timestamp - $trade_merge_period){ // closely following trades are merged into one trade $recent.last_trade.pmin = min($recent.last_trade.pmin, $pmin); $recent.last_trade.pmax = max($recent.last_trade.pmax, $pmax); $recent.last_trade.amounts[$tax_token] = $recent.last_trade.amounts[$tax_token] + $traded_amount; $recent.last_trade.paid_taxes[$tax_token] = $recent.last_trade.paid_taxes[$tax_token] + $paid_tax; } else{ $amounts = {x:0, y:0}; $paid_taxes = {x:0, y:0}; $amounts[$tax_token] = $traded_amount; $paid_taxes[$tax_token] = $paid_tax; $recent.last_trade = { address: $trigger_initial_address, pmin: $pmin, pmax: $pmax, amounts: $amounts, paid_taxes: $paid_taxes, }; } $recent.last_ts = timestamp; }; $get_utilization_ratio = ($balances, $l_balances, $x0, $y0, $alpha) => { $beta = 1 - $alpha; $ratio = reduce($get_leverages(), 6, ($acc, $L) => $acc + $l_balances[$L||'x'].balance/($balances.x+$x0)*($L-1)/$beta + $l_balances[-$L||'x'].balance/($balances.y+$y0)*($L-1)/$alpha, 0); $ratio }; // X through Y: // without LP leverage (Lambda) $get_final_x = ($X, $Y, $final_Y, $X0, $Y0, $pool_props, $inverted) => { require($final_Y >= $Y, "not selling Y"); $a = $inverted ? $pool_props.beta : $pool_props.alpha; // alpha $b = 1 - $a; // beta $final_X = ($X + $X0) * (($Y + $Y0)/($final_Y + $Y0))^($b/$a) - $X0; require($final_X >= 0, "bad final_X " || $final_X); $deltaX = $X - $final_X; require($deltaX >= 0, "bad deltaX " || $deltaX); $final_X }; // along x means keeping x fully leveraged (y underleveraged) $get_final_x_along_x = ($X, $Y, $final_Y, $pool_props, $inverted) => { log('get_final_x_along_x', $X, $Y, $final_Y); $b = $inverted ? $pool_props.alpha : $pool_props.beta; // beta $X * ($final_Y/$Y)^($b * $pool_props.Lambda/($b * $pool_props.Lambda - 1)) }; // along y means keeping y fully leveraged (x underleveraged) $get_final_x_along_y = ($X, $Y, $final_Y, $pool_props, $inverted) => { log('get_final_x_along_y', $X, $Y, $final_Y); $a = $inverted ? $pool_props.beta : $pool_props.alpha; // alpha $X * ($final_Y/$Y)^(1-1/$a/$pool_props.Lambda) }; // Y through X: // without LP leverage (Lambda) $get_final_y = ($X, $Y, $final_X, $X0, $Y0, $pool_props, $inverted) => { // require($final_X <= $X, "not buying X"); // selling when redeeming L-tokens $a = $inverted ? $pool_props.beta : $pool_props.alpha; // alpha $b = 1 - $a; // beta $final_Y = ($Y + $Y0) * (($X + $X0)/($final_X + $X0))^($a/$b) - $Y0; require($final_Y >= 0, "bad final_Y " || $final_Y); // $deltaY = $final_Y - $Y; // require($deltaY >= 0, "bad deltaY " || $deltaY); $final_Y }; // along x means keeping x fully leveraged (y underleveraged) $get_final_y_along_x = ($X, $Y, $final_X, $pool_props, $inverted) => { $b = $inverted ? $pool_props.alpha : $pool_props.beta; // beta $Y * ($final_X/$X)^(1 - 1/$b/$pool_props.Lambda) }; // along y means keeping y fully leveraged (x underleveraged) $get_final_y_along_y = ($X, $Y, $final_X, $pool_props, $inverted) => { $a = $inverted ? $pool_props.beta : $pool_props.alpha; // alpha $Y * ($final_X/$X)^($a*$pool_props.Lambda/($a*$pool_props.Lambda - 1)) }; $add_net_balance_without_changing_price = ($balances, $profits, $side, $amount, $Lambda) => { if (!$amount) return; // bounce('add_net_balance_without_changing_price ' || $side || ' ' || $amount); if ($Lambda == 1){ $profits[$side] = $profits[$side] + $amount; return; } $opposite = $side == 'x' ? 'y' : 'x'; $side_n = $side || 'n'; $opposite_n = $opposite || 'n'; $Xn = $balances[$side_n]; $Yn = $balances[$opposite_n]; $X = $balances[$side]; $Y = $balances[$opposite]; $underleveraged = $Xn > ceil($X/$Lambda); $delta_Xn = $amount; // $delta_Yn = 0; // the price doesn't change as X and Y grow proportionally if (!$underleveraged){ // along X // Y is underleveraged, increase Y proportionally while keeping Yn intact $full_delta_Y = $Y * $delta_Xn/$Xn; if ($Y + $full_delta_Y > $Yn * $Lambda){ // would overshoot and make Y overleveraged $ratio = $Yn * $Lambda / $Y - 1; $delta_X = $ratio * $X; $delta_Y = $ratio * $Y; } else{ $delta_X = $delta_Xn * $Lambda; $delta_Y = $full_delta_Y; } } // else{ // $delta_X = 0; // only net X gets increased // $delta_Y = 0; // } $balances[$side_n] = $balances[$side_n] + $delta_Xn; // $balances[$opposite_n] = $balances[$opposite_n] + $delta_Yn; $balances[$side] = $balances[$side] + $delta_X; $balances[$opposite] = $balances[$opposite] + $delta_Y; }; $pow = ($precomputed, $power) => { require($precomputed[$power], "no precomputed power " || $power); $precomputed[$power] }; $precompute = $v => { $pre = {}; $pre['2'] = $v * $v; $pre['5'] = $pre['2'] * $pre['2'] * $v; $pre['10'] = $pre['5'] * $pre['5']; $pre['20'] = $pre['10'] * $pre['10']; $pre['50'] = $pre['20'] * $pre['20'] * $pre['10']; $pre['100'] = $pre['50'] * $pre['50']; $pre }; $charge_interest = ($balances, $l_balances, $profits, $x0, $y0, $last_ts, $i, $alpha, $Lambda) => { require($last_ts, "no last ts"); if (length($l_balances) == 0 OR $i == 0 OR timestamp == $last_ts) return; $beta = 1 - $alpha; $x = $balances.x; $y = $balances.y; $accrued_rate = (timestamp - $last_ts)/3600/24/360 * $i; $factor = max(1 - $accrued_rate, 0); // how much is left $factor_powers = $precompute($factor); $y2x = ($y + $y0) / ($x + $x0); $p = $alpha/$beta * $y2x; $n_deltas = {dxn:0, dyn:0}; // we change x and y in such a way that the price does not change foreach($get_leverages(), 6, ($L) => { $xL = $l_balances[$L||'x'].balance; $yL = $l_balances[-$L||'x'].balance; if (!$xL AND !$yL) return; $factorL1 = $pow($factor_powers, $L) / $factor; // $factor^($L-1) if ($xL){ $dxL = -$xL * (1 - $factorL1); $l_balances[$L||'x'].balance = $xL + $dxL; // change in the amount lent out by the swap pool (swap pool's assets) $delta_yn = $dxL * $p * ($L-1)/$L; // < 0 $n_deltas.dyn = $n_deltas.dyn + $delta_yn; if ($Lambda == 1){ $n_deltas.dxn = $n_deltas.dxn + $delta_yn / $y2x; // proportional - no price change $profits.x = $profits.x - $dxL - $delta_yn / $y2x; // income from interest + transferred from the pool to keep the price } else $n_deltas.dxn = $n_deltas.dxn - $dxL; // > 0 } if ($yL){ $dyL = -$yL * (1 - $factorL1); $l_balances[-$L||'x'].balance = $yL + $dyL; // change in the amount lent out by the swap pool (swap pool's assets) $delta_xn = $dyL / $p * ($L-1)/$L; // < 0 $n_deltas.dxn = $n_deltas.dxn + $delta_xn; if ($Lambda == 1){ $n_deltas.dyn = $n_deltas.dyn + $delta_xn * $y2x; // proportional - no price change $profits.y = $profits.y - $dyL - $delta_xn * $y2x; // income from interest + transferred from the pool to keep the price } else $n_deltas.dyn = $n_deltas.dyn - $dyL; // > 0 } }); // log('interest', $n_deltas); $dxn = $n_deltas.dxn; $dyn = $n_deltas.dyn; if ($Lambda == 1){ $balances.xn = $balances.xn + $dxn; $balances.yn = $balances.yn + $dyn; $balances.x = $balances.x + $dxn; $balances.y = $balances.y + $dyn; } else{ $add_net_balance_without_changing_price($balances, $profits, 'x', $dxn, $Lambda); $add_net_balance_without_changing_price($balances, $profits, 'y', $dyn, $Lambda); } }; $update_leveraged_balances = ($l_balances, $P, $final_P, $inverted) => { $ratio = $final_P/$P; $ratio_powers = $precompute($ratio); $totals = { delta_XL: 0, // (L>0) X added to the L-pools (bought from the swap pool) minus (L<0) new X borrowed by the L-pools (sent to the swap pool for buying Y) delta_YL: 0, // (L>0) Y added to the L-pools (bought from the swap pool) minus (L<0) new Y borrowed by the L-pools (sent to the swap pool for buying X) XL_denom: 0, YL_denom: 0, }; // if inverted, XL corresponds to y, YL to x foreach($get_leverages(), 6, ($L) => { $allyL = $inverted ? -$L : $L; $balance = $l_balances[$allyL||'x'].balance; $obalance = $l_balances[-$allyL||'x'].balance; if (!$balance AND !$obalance) return; $ratio_L1 = $pow($ratio_powers, $L) / $ratio; // $ratio^($L-1) $debt_ratio = ($L-1)/$L; if ($balance) { $delta_XL_balance = $balance * ($ratio_L1 - 1); $new_XL_balance = $balance + $delta_XL_balance; $l_balances[$allyL||'x'].balance = $new_XL_balance; $delta_YL_balance = -(($new_XL_balance * $final_P - $balance * $P) * $debt_ratio); // borrowed $totals.delta_XL = $totals.delta_XL + $delta_XL_balance; $totals.delta_YL = $totals.delta_YL + $delta_YL_balance; $totals.XL_denom = $totals.XL_denom + $new_XL_balance * ($L-1); } if ($obalance) { // e.g. L=-2 $delta_YL_obalance = $obalance * (1/$ratio_L1 - 1); $new_YL_obalance = $obalance + $delta_YL_obalance; $l_balances[-$allyL||'x'].balance = $new_YL_obalance; $delta_XL_obalance = -(($new_YL_obalance / $final_P - $obalance / $P) * $debt_ratio); // borrowed $totals.delta_YL = $totals.delta_YL + $delta_YL_obalance; $totals.delta_XL = $totals.delta_XL + $delta_XL_obalance; $totals.YL_denom = $totals.YL_denom + $new_YL_obalance * ($L-1); } }); $totals }; $swap = ($balances, $l_balances, $profits, $recent, $x0, $y0, $y_in, $delta_Yn, $in_final_P, $received_amount_Y, $min_amount_out, $trigger_initial_address, $pool_props) => { require(!$in_final_P, "no final price please, this is swap by Y"); $alpha = $pool_props.alpha; $beta = $pool_props.beta; $Lambda = $pool_props.Lambda; $xn = $balances.xn; $yn = $balances.yn; $x = $balances.x; $y = $balances.y; if ($y_in){ $inverted = false; $X = $x; $Y = $y; $Xn = $xn; $Yn = $yn; $X0 = $x0; $Y0 = $y0; $a = $alpha; $b = $beta; $in_token = 'y'; $out_token = 'x'; } else{ // x <-> y swap their roles. Uppercase X, Y, and P refer to invertable values $inverted = true; $X = $y; $Y = $x; $Xn = $yn; $Yn = $xn; $X0 = $y0; $Y0 = $x0; $a = $beta; $b = $alpha; $in_token = 'x'; $out_token = 'y'; } require($delta_Yn > 0 AND is_integer($delta_Yn), "bad delta " || $delta_Yn); if ($Lambda > 1){ $underleveraged = $Xn > ceil($X/$Lambda); } $final_Yn = $Yn + $delta_Yn; if ($Lambda == 1){ $final_Xn = $get_final_x($X, $Y, $final_Yn, $X0, $Y0, $pool_props, $inverted); $final_X = $final_Xn; $final_Y = $final_Yn; } else if (!$underleveraged){ // along X $delta_Y = -($b*$Lambda-1)/$a*$delta_Yn; $final_Y = $Y + $delta_Y; require($final_Y > 0, "fully leveraged: negative final_Y="||$final_Y); $final_X = $get_final_x_along_x($X, $Y, $final_Y, $pool_props, $inverted); $final_Xn = $final_X/$Lambda; } else if ($underleveraged){ $delta_Yn_inflection = $Y * (( $Lambda/($Lambda-1) * ($b + ($a * $Lambda - 1) * $Xn/$X) )^($a * $Lambda/($a*$Lambda-1)) - 1) / $Lambda; require($delta_Yn_inflection > 0, "negative delta_Yn_inflection="||$delta_Yn_inflection); $inflected = $delta_Yn > $delta_Yn_inflection; // along Y until the inflection point $inflection_Yn = $Yn + $delta_Yn_inflection; $final_Yn1 = $inflected ? $inflection_Yn : $final_Yn; $final_Y1 = $final_Yn1 * $Lambda; $final_X1 = $get_final_x_along_y($X, $Y, $final_Y1, $pool_props, $inverted); $delta_X1 = $final_X1 - $X; $delta_Xn1 = -$b/($a*$Lambda-1) * $delta_X1; $final_Xn1 = $Xn + $delta_Xn1; require($final_Xn1 > 0, "negative final_Xn1="||$final_Xn1); if ($inflected){ // then, along X log('inflected at ', $delta_Yn_inflection); $delta_Yn2 = $final_Yn - $final_Yn1; $delta_Y2 = -($b*$Lambda-1)/$a*$delta_Yn2; $final_Y = $final_Y1 + $delta_Y2; require($final_Y > 0, "inflected: negative final_Y="||$final_Y); $final_X = $get_final_x_along_x($final_X1, $final_Y1, $final_Y, $pool_props, $inverted); $final_Xn = $final_X/$Lambda; require($final_Xn <= $final_Xn1, "Xn didn't decrease"); } else{ $final_X = $final_X1; $final_Xn = $final_Xn1; $final_Y = $final_Y1; } } else bounce("???"); $balances.x = $y_in ? $final_X : $final_Y; $balances.y = $y_in ? $final_Y : $final_X; $balances.xn = $y_in ? $final_Xn : $final_Yn; $balances.yn = $y_in ? $final_Yn : $final_Xn; $final_y = $balances.y; $final_x = $balances.x; $p = $alpha/$beta * ($y + $y0) / ($x + $x0); // price of x in terms of y $P = $inverted ? 1/$p : $p; // price of X in terms of Y $final_p = $alpha/$beta * ($final_y + $y0) / ($final_x + $x0); $final_P = $inverted ? 1/$final_p : $final_p; require($final_P > $P, "price should have risen but hasn't, old " || $P || ", new " || $final_P); // if inverted, XL corresponds to y, YL to x $totals = $update_leveraged_balances($l_balances, $P, $final_P, $inverted); $amount_X_exact = -($final_Xn - $Xn + $totals.delta_XL); $amount_Y_exact = $final_Yn - $Yn + $totals.delta_YL; $amount_Y = ceil($amount_Y_exact); if ($received_amount_Y >= 0) require($received_amount_Y >= $amount_Y, "expected " || $amount_Y || ", received " || $received_amount_Y); require($amount_X_exact >= 0, "to pay " || $amount_X_exact); $change = $received_amount_Y - $amount_Y; $denom = 1 - $totals.XL_denom/$b/($final_X+$X0) - $totals.YL_denom/$a/($final_Y+$Y0); log('denom after swap by delta', $denom); require($denom >= $singularity_threshold, "too close to the singularity point, denom="||$denom||", need more liquidity in order to swap this amount"); // arb tax based on price difference if ($recent.last_trade AND $recent.last_trade.address == $trigger_initial_address AND $recent.last_ts >= timestamp - $trade_merge_period){ $min_P = min($P, $y_in ? $recent.last_trade.pmin : 1/$recent.last_trade.pmax); $max_P = max($final_P, $y_in ? $recent.last_trade.pmax : 1/$recent.last_trade.pmin); $recent_traded_amount = $recent.last_trade.amounts[$out_token]; $recent_paid_tax = $recent.last_trade.paid_taxes[$out_token]; } else{ $min_P = $P; $max_P = $final_P; } $arb_profit_in_Y = ($max_P - $min_P) * ($recent_traded_amount + $amount_X_exact) / 2; // in Y $arb_profit_in_X = $arb_profit_in_Y / $min_P; $arb_profit_tax = $arb_profit_in_X * $pool_props.arb_profit_tax - $recent_paid_tax; $fee = $arb_profit_tax + $amount_X_exact * $pool_props.swap_fee; $net_amount_X_exact = $amount_X_exact - $fee; $net_amount_X = floor($net_amount_X_exact); if ($min_amount_out) require($net_amount_X >= $min_amount_out, "output amount " || $net_amount_X || " would be less than the expected minimum " || $min_amount_out); // include rounding fees $fees = { X: $net_amount_X_exact - $net_amount_X + $fee, Y: $amount_Y - $amount_Y_exact, }; // add the fee to the pool without trading and affecting the price (Lambda>1) or to a separate profit accumulator (Lambda=1) $add_net_balance_without_changing_price($balances, $profits, $out_token, $fees.X, $Lambda); $add_net_balance_without_changing_price($balances, $profits, $in_token, $fees.Y, $Lambda); log({fees: $fees, profits: $profits}); $update_recent_data($recent, $p, $final_p, $trigger_initial_address, $out_token, $amount_X_exact, $arb_profit_tax, $pool_props.period_length); { net_amount_X: $net_amount_X, amount_Y: $amount_Y, arb_profit_tax: $arb_profit_tax, fee: $fee, fees: $fees, change: $change, initial_price: $P, final_price: $final_P, } }; $buy_shares = ($s, $balances, $profits, $recent, $x0, $y0, $received_amount_x, $received_amount_y, $pool_props) => { $Lambda = $pool_props.Lambda; $alpha = $pool_props.alpha; $beta = $pool_props.beta; // $get_shares = ($x_balance, $y_balance) => round($x_balance^$alpha * $y_balance^$beta); $get_shares = ($x_balance, $y_balance) => round(($x_balance/$y_balance)^$alpha * $y_balance); $xn = $balances.xn; $yn = $balances.yn; $x = $balances.x; $y = $balances.y; $recent.last_ts = timestamp; if (!$s){ require($received_amount_x > 0 AND $received_amount_y > 0, "send both assets for the first issue"); $mid_price = $pool_props.mid_price; if ($mid_price){ // first issue must be at mid price require($received_amount_y == round($mid_price * $received_amount_x), "first issue must be at mid price "||$mid_price); $gamma = $pool_props.gamma; $shares_amount = round($received_amount_x * $pool_props.mid_price_beta * $gamma / ($gamma - 1)); } else{ // first issue determines the price $shares_amount = $get_shares($received_amount_x, $received_amount_y); } $balances.xn = $balances.xn + $received_amount_x; $balances.yn = $balances.yn + $received_amount_y; $balances.x = $balances.x + ($received_amount_x * $Lambda); $balances.y = $balances.y + ($received_amount_y * $Lambda); return { shares_amount: $shares_amount, coef: 1, change_x: 0, change_y: 0, }; } $p = $alpha/$beta * ($y + $y0) / ($x + $x0); if ($Lambda > 1 AND $recent.prev){ $target_xn = $x/$Lambda; if ($xn > ceil($target_xn)){ // x is underleveraged // require($recent.prev, "too early, prev price not known yet"); // use the worst (for the user) price that was seen recently $share_price_in_y = ($yn + max($recent.current.pmax, $recent.prev.pmax) * $xn) / $s; $max_delta_yn = ($xn/$target_xn-1)*$yn; $delta_yn1 = min($max_delta_yn, $received_amount_y); $delta_y1 = $delta_yn1 * $Lambda; $delta_x1 = $x * $delta_yn1/$yn; // proportional $shares1 = $delta_yn1/$share_price_in_y; } else{ $target_yn = $y/$Lambda; if ($yn > ceil($target_yn)){ // y is underleveraged // require($recent.prev, "too early, prev price not known yet"); // use the worst (for the user) price that was seen recently $share_price_in_x = ($xn + 1/min($recent.current.pmin, $recent.prev.pmin) * $yn) / $s; $max_delta_xn = ($yn/$target_yn-1)*$xn; $delta_xn1 = min($max_delta_xn, $received_amount_x); $delta_x1 = $delta_xn1 * $Lambda; $delta_y1 = $y * $delta_xn1/$xn; // proportional $shares1 = $delta_xn1/$share_price_in_x; log({delta_xn1:$delta_xn1, shares1:$shares1, share_price_in_x:$share_price_in_x, p:$p, recent:$recent}); } } $balances.xn = $balances.xn + $delta_xn1; $balances.yn = $balances.yn + $delta_yn1; $balances.x = $balances.x + $delta_x1; $balances.y = $balances.y + $delta_y1; } else{ $delta_xn1 = 0; $delta_yn1 = 0; $shares1 = 0; } $remaining = { x: $received_amount_x - $delta_xn1, y: $received_amount_y - $delta_yn1, }; $y_to_x = ($yn+$delta_yn1)/($xn+$delta_xn1); // $y_to_x = $balances.yn/$balances.xn; if ($profits.x OR $profits.y){ require($Lambda == 1, "have profits while Lambda is " || $Lambda); require($profits.x >= 0 AND $profits.y >= 0, "negative profits?"); // should never happen // latest prices, not recent min/max $share_price_in_y = ($yn + $p * $xn) / $s; $share_price_in_x = ($xn + 1/$p * $yn) / $s; // move proportional amounts of profit from both x and y. The shares to be issued for the moved profit belong to the pool and will not be actually issued $profits_proportional_y = $y_to_x * $profits.x; if ($profits.y > $profits_proportional_y){ $delta_profit_x = $profits.x; $delta_profit_y = $profits_proportional_y; $symmetric_moved_profit_shares = $delta_profit_x/$xn * $s; } else{ $profits_proportional_x = $profits.y / $y_to_x; require($profits_proportional_x <= $profits.x, "profits x " || $profits.x || ", proportional " || $profits_proportional_x); $delta_profit_x = $profits_proportional_x; $delta_profit_y = $profits.y; $symmetric_moved_profit_shares = $delta_profit_y/$yn * $s; } $profits.x = $profits.x - $delta_profit_x; $profits.y = $profits.y - $delta_profit_y; $balances.xn = $balances.xn + $delta_profit_x; $balances.yn = $balances.yn + $delta_profit_y; $balances.x = $balances.x + $delta_profit_x; $balances.y = $balances.y + $delta_profit_y; log('after proportional profits: delta_profit_x', $delta_profit_x, 'delta_profit_y', $delta_profit_y, 'remaining profits', $profits, 'symmetric_moved_profit_shares', $symmetric_moved_profit_shares); // calc the shares to be issued for moving the one-sided profits to the pool, these shares belong to the pool and will not be actually issued. The user contributes the other side and gets the shares cheaper than their fair price because profits are not included in the price calculation. $moved_profit_x = min($profits.x, $remaining.y / $y_to_x); $moved_profit_y = min($profits.y, $remaining.x * $y_to_x); $moved_profit_shares = $moved_profit_x/$share_price_in_x + $moved_profit_y/$share_price_in_y + $symmetric_moved_profit_shares; log('share_price_in_x', $share_price_in_x, 'share_price_in_y', $share_price_in_y, 'moved_profit_shares', $moved_profit_shares, 'moved_profit_x', $moved_profit_x, 'moved_profit_y', $moved_profit_y); $profits.x = $profits.x - $moved_profit_x; $profits.y = $profits.y - $moved_profit_y; $remaining.x = $remaining.x + $moved_profit_x; $remaining.y = $remaining.y + $moved_profit_y; } // part 2: proportional buying log('before proportional buying: remaining', $remaining, 'y_to_x', $y_to_x); $remaining_proportional_y = $y_to_x * $remaining.x; if ($remaining.y > $remaining_proportional_y){ // excessive y $proportional_delta_xn = $remaining.x; $proportional_delta_yn = $remaining_proportional_y; $exact_change_x = 0; $exact_change_y = $remaining.y - $remaining_proportional_y; log({proportional_delta_yn:$proportional_delta_yn, exact_change_y:$exact_change_y, change_y_exact: $remaining.y - $remaining_proportional_y, remaining_y:$remaining.y, remaining_proportional_y:$remaining_proportional_y}); $shares_proportional = $remaining.x / ($xn + $delta_xn1) * ($s + $shares1); } else{ // excessive x $remaining_proportional_x = $remaining.y / $y_to_x; $proportional_delta_xn = $remaining_proportional_x; $proportional_delta_yn = $remaining.y; $exact_change_x = $remaining.x - $remaining_proportional_x; log({proportional_delta_xn:$proportional_delta_xn, exact_change_x:$exact_change_x, remaining_x:$remaining.x, remaining_proportional_x:$remaining_proportional_x}); require($exact_change_x >= 0, "received x " || $remaining.x || ", proportional " || $remaining_proportional_x); $exact_change_y = 0; $shares_proportional = $remaining.y / ($yn + $delta_yn1) * ($s + $shares1); } $gross_shares_amount = $shares1 + $symmetric_moved_profit_shares + $shares_proportional; $shares_amount = floor($gross_shares_amount - $moved_profit_shares); $coef = $Lambda == 1 ? ($s + $gross_shares_amount) / ($s + $shares_amount) : 1; log({shares_proportional:$shares_proportional, moved_profit_shares:$moved_profit_shares, shares_amount:$shares_amount}); $balances.xn = $balances.xn + $proportional_delta_xn; $balances.yn = $balances.yn + $proportional_delta_yn; $balances.x = $balances.x + $proportional_delta_xn * $Lambda; $balances.y = $balances.y + $proportional_delta_yn * $Lambda; $change_x = floor($exact_change_x); $change_y = floor($exact_change_y); $rounding_fee_x = $exact_change_x - $change_x; $rounding_fee_y = $exact_change_y - $change_y; $add_net_balance_without_changing_price($balances, $profits, 'x', $rounding_fee_x, $Lambda); $add_net_balance_without_changing_price($balances, $profits, 'y', $rounding_fee_y, $Lambda); { shares_amount: $shares_amount, coef: $coef, change_x: $change_x, change_y: $change_y, } }; $redeem_shares = ($s, $balances, $l_balances, $profits, $recent, $x0, $y0, $received_shares_amount, $asset, $pool_props) => { $xn = $balances.xn; $yn = $balances.yn; $x = $balances.x; $y = $balances.y; $net_of_exit_fee = 1 - $pool_props.exit_fee; $x_asset = $pool_props.x_asset; $y_asset = $pool_props.y_asset; $Lambda = $pool_props.Lambda; $alpha = $pool_props.alpha; // $beta = $pool_props.beta; if ($asset){ // one-sided redemption first, then proportional require($asset == $x_asset OR $asset == $y_asset, "wrong preferred asset"); require($Lambda > 1, "only proportional withdrawals allowed"); $asset_label = $asset == $x_asset ? 'x' : 'y'; $net_balance = $asset == $x_asset ? $xn : $yn; $effective_balance = $asset == $x_asset ? $x : $y; $target_net_balance = $effective_balance / $Lambda; // require($target_net_balance < $net_balance, "the preferred asset is already fully leveraged"); $excess_net_balance = max($net_balance - $target_net_balance, 0); // $p = $alpha/$beta * $y / $x; require($recent.prev, "too early, prev price not known yet"); // use the worst (for the user) price that was seen recently $share_price_in_asset = (($asset_label == 'y') ? ($yn + min($recent.current.pmin, $recent.prev.pmin) * $xn) / $s : ($xn + 1/max($recent.current.pmax, $recent.prev.pmax) * $yn) / $s) * $net_of_exit_fee; $max_asset = $received_shares_amount * $share_price_in_asset; $one_sided_amount = min($max_asset, $excess_net_balance); if ($asset_label == 'y'){ $yn_amount1 = $one_sided_amount; $xn_amount1 = 0; } else{ $xn_amount1 = $one_sided_amount; $yn_amount1 = 0; } $remaining_received_shares = max($received_shares_amount - ($one_sided_amount / $share_price_in_asset), 0); } else{ $remaining_received_shares = $received_shares_amount; $xn_amount1 = 0; $yn_amount1 = 0; } $share_of_shares = $remaining_received_shares / $s; $remaining_share_of_shares = 1 - $share_of_shares; $remaining_share_of_assets = $remaining_share_of_shares; $share_of_assets = 1 - $remaining_share_of_assets; $x_amount = $share_of_assets * $x * $net_of_exit_fee; $y_amount = $share_of_assets * $y * $net_of_exit_fee; $xn_amount = $share_of_assets * ($xn - $xn_amount1) * $net_of_exit_fee + $xn_amount1; $yn_amount = $share_of_assets * ($yn - $yn_amount1) * $net_of_exit_fee + $yn_amount1; if ($asset) log({xn_amount1:$xn_amount1, yn_amount1:$yn_amount1, shares_for_excess: $one_sided_amount / $share_price_in_asset, remaining_received_shares:$remaining_received_shares, xn_amount:$xn_amount, yn_amount:$yn_amount, asset_label:$asset_label, share_price_in_asset:$share_price_in_asset, max_asset: $max_asset, excess_net_balance:$excess_net_balance, recent:$recent, pmin:min($recent.current.pmin, $recent.prev.pmin), pmax:max($recent.current.pmax, $recent.prev.pmax)}); log({ remaining_received_shares:$remaining_received_shares, xn_amount:$xn_amount, yn_amount:$yn_amount, x_amount:$x_amount, y_amount:$y_amount}); log('balances before', $balances); $balances.x = $balances.x - $x_amount; $balances.y = $balances.y - $y_amount; $balances.xn = $balances.xn - $xn_amount; $balances.yn = $balances.yn - $yn_amount; log('balances after', $balances); $coef = $Lambda == 1 AND $received_shares_amount < $s ? ($s - $received_shares_amount * $net_of_exit_fee) / ($s - $received_shares_amount) : 1; $new_x0 = $x0 * ($s-$received_shares_amount)/$s; $new_y0 = $y0 * ($s-$received_shares_amount)/$s; $denom = 1 - $get_utilization_ratio($balances, $l_balances, $new_x0, $new_y0, $alpha); require($denom >= $singularity_threshold, "redemption amount too large, it would bring us too close to the singularity point, denom="||$denom); $rounding_fee_x = $xn_amount - floor($xn_amount); $rounding_fee_y = $yn_amount - floor($yn_amount); $add_net_balance_without_changing_price($balances, $profits, 'x', $rounding_fee_x, $Lambda); $add_net_balance_without_changing_price($balances, $profits, 'y', $rounding_fee_y, $Lambda); log('balances after rounding fees', $balances); $recent.last_ts = timestamp; { xn_amount: floor($xn_amount), yn_amount: floor($yn_amount), coef: $coef, } }; $update_other_l_balances_and_get_sums = ($l_balances, $P, $final_P, $Leverage, $inverted) => { $ratio = $final_P/$P; $ratio_powers = $precompute($ratio); $sums = { initial: 0, final: 0, delta_XL: 0, XL_denom: 0, YL_denom: 0, PL1_ratio: $pow($ratio_powers, $Leverage) / $ratio, // $ratio^($Leverage-1) }; foreach($get_leverages(), 6, $L => { $allyL = $inverted ? -$L : $L; $balance = $l_balances[$allyL||'x'].balance; $obalance = $l_balances[-$allyL||'x'].balance; if (!$balance AND !$obalance) return; $ratio_L1 = $pow($ratio_powers, $L) / $ratio; // $ratio^($L-1) if ($balance){ $sums.initial = $sums.initial + $balance * ($L-1)/$L; if ($L != $Leverage){ $new_balance = $balance * $ratio_L1; $l_balances[$allyL||'x'].balance = $new_balance; $sums.final = $sums.final + $new_balance * ($L-1)/$L; $sums.XL_denom = $sums.XL_denom + $new_balance * ($L-1); $sums.delta_XL = $sums.delta_XL + $new_balance - $balance; } } if ($obalance){ $sums.initial = $sums.initial - $obalance/$P; $new_obalance = $obalance / $ratio_L1; $l_balances[-$allyL||'x'].balance = $new_obalance; $sums.final = $sums.final - $new_obalance/$final_P; $sums.YL_denom = $sums.YL_denom + $new_obalance * ($L-1); $sums.delta_XL = $sums.delta_XL - ($new_obalance / $final_P - $obalance / $P) * ($L-1)/$L; } }); $sums }; $move_unleveraged = ($pool, $l_balances, $X0, $Y0, $dXn, $Leverage, $pool_props, $inverted) => { $a = $inverted ? $pool_props.beta : $pool_props.alpha; $b = 1 - $a; $L_key = ($inverted ? -$Leverage : $Leverage) || 'x'; $l_bal_direction = $dXn < 0 ? "grow" : "fall"; $Xt = $pool.X + $X0; $P = $a/$b * ($pool.Y + $Y0) / $Xt; $final_Xn = $pool.Xn + $dXn; $final_X = $final_Xn; $final_Xt = $final_X + $X0; $final_Y = $get_final_y($pool.X, $pool.Y, $final_X, $X0, $Y0, $pool_props, $inverted); $delta_Y = $final_Y - $pool.Y; $delta_Yn = $delta_Y; $final_Yn = $pool.Yn + $delta_Yn; $final_P = $a/$b * ($final_Y + $Y0) / $final_Xt; // log('l balances before', $l_balances); $sums = $update_other_l_balances_and_get_sums($l_balances, $P, $final_P, $Leverage, $inverted); // log('sums', $sums); // log('l balances after', $l_balances); // update the final balance of our L-pool $b1 = $sums.initial + $b/($b-1)*$Xt; $new_l_balance = $Leverage/($Leverage-1) * ( -$sums.final - $b/($b-1)*$final_Xt + $b1 * ($final_Xt/$Xt)^(1/$b) ); // log('new_l_balance', $new_l_balance); $delta_l_balance = $new_l_balance - $l_balances[$L_key].balance; // log('delta_l_balance', $delta_l_balance); require($delta_l_balance * $dXn < 0, "unleveraged l-bal should "||$l_bal_direction||", got new " || $new_l_balance || ", delta " || $delta_l_balance); $l_balances[$L_key].balance = $new_l_balance; // log('l balances after 2', $l_balances); $pool.X = $final_X; $pool.Y = $final_Y; $pool.Xn = $final_Xn; $pool.Yn = $final_Yn; $pool.delta_XL = $pool.delta_XL + $sums.delta_XL; $pool.XL_denom = $sums.XL_denom + $new_l_balance * ($Leverage-1); $pool.YL_denom = $sums.YL_denom; $pool.PL1_ratio = $sums.PL1_ratio; }; $move_along_X = ($pool, $l_balances, $dXn, $Leverage, $pool_props, $inverted) => { $Lambda = $pool_props.Lambda; $a = $inverted ? $pool_props.beta : $pool_props.alpha; $b = 1 - $a; $L_key = ($inverted ? -$Leverage : $Leverage) || 'x'; $l_bal_direction = $dXn < 0 ? "grow" : "fall"; $P = $a/$b * $pool.Y / $pool.X; $final_Xn = $pool.Xn + $dXn; $final_X = $Lambda * $final_Xn; $final_Y = $get_final_y_along_x($pool.X, $pool.Y, $final_X, $pool_props, $inverted); $delta_Y = $final_Y - $pool.Y; $delta_Yn = -$a/($b*$Lambda-1)*$delta_Y; $final_Yn = $pool.Yn + $delta_Yn; $final_P = $a/$b * $final_Y / $final_X; $sums = $update_other_l_balances_and_get_sums($l_balances, $P, $final_P, $Leverage, $inverted); // update the final balance of our L-pool $b1 = $sums.initial + $b/($b*$Lambda-1)*$pool.X; $new_l_balance = $Leverage/($Leverage-1) * ( -$sums.final - $b/($b*$Lambda-1)*$final_X + $b1 * ($final_X/$pool.X)^(1/$b/$Lambda) ); $delta_l_balance = $new_l_balance - $l_balances[$L_key].balance; require($delta_l_balance * $dXn < 0, "along x l-bal should "||$l_bal_direction||", got new " || $new_l_balance || ", delta " || $delta_l_balance); $l_balances[$L_key].balance = $new_l_balance; $pool.X = $final_X; $pool.Y = $final_Y; $pool.Xn = $final_Xn; $pool.Yn = $final_Yn; $pool.delta_XL = $pool.delta_XL + $sums.delta_XL; $pool.XL_denom = $sums.XL_denom + $new_l_balance * ($Leverage-1); $pool.YL_denom = $sums.YL_denom; $pool.PL1_ratio = $sums.PL1_ratio; }; $move_along_Y = ($pool, $l_balances, $dXn, $Leverage, $pool_props, $inverted) => { $Lambda = $pool_props.Lambda; $a = $inverted ? $pool_props.beta : $pool_props.alpha; $b = 1 - $a; $L_key = ($inverted ? -$Leverage : $Leverage) || 'x'; $l_bal_direction = $dXn < 0 ? "grow" : "fall"; $P = $a/$b * $pool.Y / $pool.X; $final_Xn = $pool.Xn + $dXn; $delta_X = -($a*$Lambda-1)/$b * $dXn; $final_X = $pool.X + $delta_X; $final_Y = $get_final_y_along_y($pool.X, $pool.Y, $final_X, $pool_props, $inverted); $final_Yn = $final_Y/$Lambda; $final_P = $a/$b * $final_Y / $final_X; $sums = $update_other_l_balances_and_get_sums($l_balances, $P, $final_P, $Leverage, $inverted); // update the final balance of our L-pool $b2 = $sums.initial - $b/$a/$Lambda*$pool.X; $new_l_balance = $Leverage/($Leverage-1) * ( -$sums.final + $b/$a/$Lambda*$final_X + $b2 * ($final_X/$pool.X)^(-1/($a*$Lambda-1)) ); $delta_l_balance = $new_l_balance - $l_balances[$L_key].balance; require($delta_l_balance * $dXn < 0, "along y l-bal should "||$l_bal_direction||", got new " || $new_l_balance || ", delta " || $delta_l_balance); $l_balances[$L_key].balance = $new_l_balance; $pool.X = $final_X; $pool.Y = $final_Y; $pool.Xn = $final_Xn; $pool.Yn = $final_Yn; $pool.delta_XL = $pool.delta_XL + $sums.delta_XL; $pool.XL_denom = $sums.XL_denom + $new_l_balance * ($Leverage-1); $pool.YL_denom = $sums.YL_denom; $pool.PL1_ratio = $sums.PL1_ratio; }; // delta_Xn < 0: buy L-tokens // delta_Xn > 0: sell L-tokens $trade_l_shares = ($balances, $l_balances, $profits, $recent, $x0, $y0, $Leverage, $asset, $delta_Xn, $entry_price, $trigger_initial_address, $pool_props) => { require(is_integer($delta_Xn), "delta must be int"); require($asset == $pool_props.x_asset OR $asset == $pool_props.y_asset, "wrong asset"); require($Leverage == 2 OR $Leverage == 5 OR $Leverage == 10 OR $Leverage == 20 OR $Leverage == 50 OR $Leverage == 100, "bad L"); $Lambda = $pool_props.Lambda; if ($asset == $pool_props.x_asset){ $inverted = false; $X = $balances.x; $Y = $balances.y; $Xn = $balances.xn; $Yn = $balances.yn; $X0 = $x0; $Y0 = $y0; $a = $pool_props.alpha; $b = $pool_props.beta; $token = 'x'; } else{ // x <-> y swap their roles. Uppercase X, Y, and P refer to invertable values $inverted = true; $X = $balances.y; $Y = $balances.x; $Xn = $balances.yn; $Yn = $balances.xn; $X0 = $y0; $Y0 = $x0; $a = $pool_props.beta; $b = $pool_props.alpha; $token = 'y'; } $direct = !$inverted; require($Xn + $delta_Xn > 0, "Xn balance would be negative"); $L_key = ($inverted ? -$Leverage : $Leverage) || 'x'; $pool = {X: $X, Y: $Y, Xn: $Xn, Yn: $Yn}; $initial_l_balance = $l_balances[$L_key].balance; $initial_shares = $l_balances[$L_key].supply; // $initial_l_balance might be non-zero after full redemption of all l-shares -- due to rounding of share amounts // require(!$initial_l_balance == !$initial_shares, "l-balance "||$initial_l_balance||" while shares "||$initial_shares); $initial_P = $a/$b * ($pool.Y + $Y0) / ($pool.X + $X0); if ($Lambda == 1) this_address#3.$move_unleveraged($pool, $l_balances, $X0, $Y0, $delta_Xn, $Leverage, $pool_props, $inverted); else { $underleveraged = $Xn > ceil($X/$Lambda); if (!$underleveraged){ // along X if ($delta_Xn > 0){ // selling L-shares and X $delta_Xn_inflection = $X * (( $Lambda/($Lambda-1) * ($a + ($b * $Lambda - 1) * $Yn/$Y) )^($b * $Lambda/($b*$Lambda-1)) - 1) / $Lambda; $inflected = $delta_Xn > $delta_Xn_inflection; } $dXn1 = $inflected ? $delta_Xn_inflection : $delta_Xn; this_address#3.$move_along_X($pool, $l_balances, $dXn1, $Leverage, $pool_props, $inverted); if ($inflected) this_address#3.$move_along_Y($pool, $l_balances, $delta_Xn - $delta_Xn_inflection, $Leverage, $pool_props, $inverted); } else{ // along Y if ($delta_Xn < 0){ // buying L-shares and X $delta_Xn_inflection = -$b/($Lambda-1) * ($Lambda*$Xn - $X); $inflected = abs($delta_Xn) > abs($delta_Xn_inflection); } $dXn1 = $inflected ? $delta_Xn_inflection : $delta_Xn; this_address#3.$move_along_Y($pool, $l_balances, $dXn1, $Leverage, $pool_props, $inverted); if ($inflected) this_address#3.$move_along_X($pool, $l_balances, $delta_Xn - $delta_Xn_inflection, $Leverage, $pool_props, $inverted); } } $final_l_balance = $l_balances[$L_key].balance; $delta_l_balance = $final_l_balance - $initial_l_balance; $final_P = $a/$b * ($pool.Y + $Y0) / ($pool.X + $X0); // the change in the total X balance of swap pool + all L-pools (positive when buying, negative when selling) $net_delta = $delta_Xn + $pool.delta_XL + $delta_l_balance; $final_shares = $initial_shares ? floor($final_l_balance/$initial_l_balance / $pool.PL1_ratio * $initial_shares) : round($net_delta); // the initial units for shares are arbitrary $shares = $final_shares - $initial_shares; $l_balances[$L_key].supply = $final_shares; $avg_share_price = $net_delta/$shares; if ($final_shares == 0){ require($final_l_balance >= 0, "negative final l-balance after redeeming all shares: "||$final_l_balance); // any remaining l-balance will be a gift to those who buy the l-shares later // $remainder_fee_X = $final_l_balance; // $remainder_fee_Y = $final_l_balance * $final_P * ($Leverage-1)/$Leverage; // $l_balances[$L_key].balance = 0; } $denom = 1 - $pool.XL_denom/$b/($pool.X+$X0) - $pool.YL_denom/$a/($pool.Y+$Y0); // log('denom after L', $denom); require($denom >= $singularity_threshold, "too close to the singularity point, denom="||$denom||", need more liquidity in order to buy this amount of L-tokens"); $balances.x = $inverted ? $pool.Y : $pool.X; $balances.y = $inverted ? $pool.X : $pool.Y; $balances.xn = $inverted ? $pool.Yn : $pool.Xn; $balances.yn = $inverted ? $pool.Xn : $pool.Yn; // regular trading fee (%) and arb tax are paid on top if ($recent.last_trade AND $recent.last_trade.address == $trigger_initial_address AND $recent.last_ts >= timestamp - $trade_merge_period){ $min_P = min($initial_P, $final_P, $direct ? $recent.last_trade.pmin : 1/$recent.last_trade.pmax); $max_P = max($initial_P, $final_P, $direct ? $recent.last_trade.pmax : 1/$recent.last_trade.pmin); $recent_traded_amount = $recent.last_trade.amounts[$token]; $recent_paid_tax = $recent.last_trade.paid_taxes[$token]; } else{ $min_P = min($initial_P, $final_P); $max_P = max($initial_P, $final_P); } $arb_profit_in_Y = ($max_P - $min_P) * ($recent_traded_amount + abs($net_delta)) / 2; // in Y // $arb_profit_in_Y = ($final_P - $initial_P) * $net_delta / 2; // in Y // require($arb_profit_in_Y > 0, "arb profit "||$arb_profit_in_Y); $arb_profit_in_X = $arb_profit_in_Y / $min_P; $arb_profit_tax = $arb_profit_in_X * $pool_props.arb_profit_tax - $recent_paid_tax; $trading_fee = $arb_profit_tax + abs($net_delta) * $pool_props.swap_fee; if ($delta_Xn > 0){ // sell $gross_asset_out = -$net_delta; // gross means before tax and fees require($gross_asset_out > 0, "asset out must be positive, got " || $gross_asset_out); if ($entry_price){ // L>0 profit is accounted for in x tokens (in y tokens for L<0) meaning that only profits from the borrowed part of the L-pool are taxed $l_tax = max(($avg_share_price - $entry_price)*(-$shares)*$pool_props.leverage_profit_tax, 0); } else $l_tax = $gross_asset_out * $pool_props.leverage_token_tax; } // For buying, the fee is added on top. For selling (net_delta<0), the fees are subtracted $gross_delta_exact = $net_delta + $trading_fee + $l_tax; $gross_delta = ceil($gross_delta_exact); $rounding_fee = $gross_delta - $gross_delta_exact; $total_fee = $trading_fee + $l_tax + $rounding_fee; // log('balances before', $balances); $add_net_balance_without_changing_price($balances, $profits, $token, $total_fee, $Lambda); // log('balances after', $balances); $update_recent_data($recent, $inverted ? 1/$initial_P : $initial_P, $inverted ? 1/$final_P : $final_P, $trigger_initial_address, $token, abs($net_delta), $arb_profit_tax, $pool_props.period_length); { shares: $shares, net_delta: $net_delta, gross_delta: $gross_delta, avg_share_price: $avg_share_price, arb_profit_tax: $arb_profit_tax, l_tax: $l_tax, trading_fee: $trading_fee, total_fee: $total_fee, initial_price: $initial_P, final_price: $final_P, } }; $handle_trade_l_shares_request = ($pool_aa, $balances, $l_balances, $profits, $recent, $x0, $y0, $trigger_data, $trigger_address, $trigger_outputs, $trigger_initial_address, $pool_props) => { $x_asset = $pool_props.x_asset; $y_asset = $pool_props.y_asset; $asset = $trigger_data.asset == 'x' ? $x_asset : ($trigger_data.asset == 'y' ? $y_asset : $trigger_data.asset); $L = $trigger_data.L; $buy = $trigger_data.buy; $sell = $trigger_data.sell; $delta = $trigger_data.delta; $received_amount = $trigger_outputs[$asset]; $min_received_amount = $asset == 'base' ? 10000 : 0; $net_received_amount = $received_amount - $min_received_amount; require(!($buy AND $sell), "buy or sell?"); require($delta > 0, "delta must be positive"); if ($buy) require($net_received_amount > 0, "you forgot to pay"); else require($net_received_amount == 0, "don't send asset"); $delta_Xn = $buy ? -$delta : $delta; // Xn in the pool $asset_label = $asset == $x_asset ? 'x' : 'y'; $signedL = $asset_label == 'x' ? $L : -$L; if ($buy AND $trigger_data.tokens OR $sell AND !$trigger_data.position){ $l_shares_asset = var[$pool_aa]['leveraged_asset' || $signedL]; require($l_shares_asset, "please define an asset for the leveraged token first"); } if ($sell){ if ($trigger_data.position){ $position = var[$pool_aa][$trigger_data.position]; require($position, "no such position"); require($position.owner == $trigger_address, "you are not the owner of this position"); $parts = split($trigger_data.position, '_'); require(+$parts[1] == $signedL, "wrong L"); $shares_in = $position.shares; } else{ $shares_in = $trigger_outputs[$l_shares_asset]; require($shares_in > 0, "no leveraged tokens received"); } } $res = $trade_l_shares($balances, $l_balances, $profits, $recent, $x0, $y0, $L, $asset, $delta_Xn, $position.price, $trigger_initial_address, $pool_props); // log('balances', $balances, 'res', $res); $shares = $res.shares; $gross_delta = $res.gross_delta; if ($buy){ $asset_out = $received_amount - $gross_delta; // the change require($asset_out >= 0, "expected " || $gross_delta || ", received " || $received_amount); } else{ $shares_change = $shares_in + $shares; // shares < 0 require($shares_change >= 0, "expected " || (-$shares) || " shares, received " || $shares_in); $asset_out = -$gross_delta; } $res.signedL = $signedL; $res.asset_label = $asset_label; $res.asset = $asset; $res.l_shares_asset = $l_shares_asset; $res.position = $position; $res.shares_change = $shares_change; $res.asset_out = $asset_out; $res }; $validate_and_apply_new_governed_param_value = ($name, $value, $balances, $l_balances, $profits, $lp_shares, $pool_props, $locked_governance) => { if ($locked_governance) require(!$locked_governance[$name], "governance is not allowed to change "||$name); $Lambda = $pool_props.Lambda; $alpha = $pool_props.alpha; $beta = $pool_props.beta; $gamma = $pool_props.gamma; $mid_price = $pool_props.mid_price; $mid_price_beta = $pool_props.mid_price_beta; $s_curve = $lp_shares.linear * $lp_shares.coef; $x0 = $mid_price ? $s_curve / $mid_price_beta / $gamma : 0; $y0 = $x0 * $mid_price; if ($name == 'pool_leverage'){ require($profits.x < 1 AND $profits.y < 1, "profits must be added to the pool before changing pool_leverage"); // only rounding fees are allowed $profits.x = 0; $profits.y = 0; require($alpha != 1/$value, "pool leverage = 1/alpha"); require($beta != 1/$value, "pool leverage = 1/beta"); require($value != $Lambda, "same Lambda"); if ($value > 1) require(!$mid_price, "price range setting is incompatible with new pool leverage"); $balances.x = $balances.x * $value/$Lambda; $balances.y = $balances.y * $value/$Lambda; if ($value == 1){ // move the excessive balances to profits $profits.x = $profits.x + $balances.xn - $balances.x; $profits.y = $profits.y + $balances.yn - $balances.y; $balances.xn = $balances.x; $balances.yn = $balances.y; } // we have modified balances and profits } else if ($name == 'mid_price' OR $name == 'price_deviation'){ require($value AND $mid_price, $name||" must be nonzero"); require($alpha == 0.5, "equal weights only"); if ($name == 'price_deviation'){ require($value > 1, "price deviation must be > 1"); $new_p0 = $mid_price; $new_gamma = $value; } else{ $new_p0 = $value; $new_gamma = $gamma; } $sqp = $mid_price_beta; // sqrt(mid_price) $new_sqp = sqrt($new_p0); $x = $balances.x; $y = $balances.y; // 1. assume we keep all x and decrease y $new_s1 = 1 / (1/$s_curve + (1/$gamma/$sqp - 1/$new_gamma/$new_sqp) / $x); $new_y = $new_s1 * ($y/$s_curve + $sqp/$gamma - $new_sqp/$new_gamma); if ($new_y <= $y){ require($new_y > 0, "new y is negative"); require($new_s1 > 0, "new s1 is negative"); $profits.y = $profits.y + $y - $new_y; $balances.y = $new_y; $balances.yn = $new_y; $lp_shares.coef = $lp_shares.coef * $new_s1/$s_curve; $new_s = $new_s1; } else{ // 2. keep all y and decrease x $new_s2 = 1 / (1/$s_curve + ($sqp/$gamma - $new_sqp/$new_gamma) / $y); $new_x = $new_s2 * ($x/$s_curve + 1/$gamma/$sqp - 1/$new_gamma/$new_sqp); require($new_x <= $x, "can't adjust x and y to keep the price"); require($new_x > 0, "new x is negative"); require($new_s2 > 0, "new s2 is negative"); $profits.x = $profits.x + $x - $new_x; $balances.x = $new_x; $balances.xn = $new_x; $lp_shares.coef = $lp_shares.coef * $new_s2/$s_curve; $new_s = $new_s2; } $new_x0 = $new_s / $new_sqp / $new_gamma; $new_y0 = $new_s * $new_sqp / $new_gamma; /* $sqp = sqrt($new_p0); $b = ($balances.x/$s_curve*$sqp + $balances.y/$s_curve/$sqp)/$new_gamma; $a = $balances.x*$balances.y/$s_curve/$s_curve; $lp_shares.coef = $lp_shares.coef / (-$b + sqrt($b*$b - 4*$a*(1/$new_gamma/$new_gamma-1)))*2*$a; */ // we have modified lp_shares, balances, and profits } else if ($name == 'alpha'){ require(!$mid_price, "can't change token weights while trading in limited range"); $new_alpha = $value; $new_beta = 1 - $new_alpha; require($new_alpha != 1/$Lambda AND $new_beta != 1/$Lambda, "pool leverage = 1/alpha or 1/beta"); // s_coef is unused // var['s_coef'] *= $balances.xn^$value * $balances.yn^(1-$value) / $s_curve; // change the balances to preserve the price p = alpha/beta * y/x = const $new_y2x = $new_beta/$new_alpha * $alpha/$beta * $balances.y/$balances.x; if ($Lambda > 1){ if ($balances.xn * $Lambda * $new_y2x <= $balances.yn * $Lambda){ // x fully leveraged $balances.x = $balances.xn * $Lambda; $balances.y = $balances.x * $new_y2x; } else if ($balances.yn * $Lambda / $new_y2x <= $balances.xn * $Lambda){ // y fully leveraged $balances.y = $balances.yn * $Lambda; $balances.x = $balances.y / $new_y2x; } else bounce("can't preserve the price"); // should never happen } else { $new_y = $balances.xn * $new_y2x; // assuming x balance stays unchanged if ($new_y <= $balances.yn){ // excessive y $profits.y = $profits.y + $balances.yn - $new_y; $balances.yn = $new_y; $balances.y = $new_y; } else { // excessive x $new_x = $balances.yn / $new_y2x; // assuming y balance stays unchanged require($new_x <= $balances.xn, "neither excessive x nor excessive y"); // should never happen $profits.x = $profits.x + $balances.xn - $new_x; $balances.xn = $new_x; $balances.x = $new_x; } } // balances and profits modified } // under new balances, we might go over (or too close to) the singularity point $denom = 1 - $get_utilization_ratio($balances, $l_balances, $new_x0 OTHERWISE $x0, $new_y0 OTHERWISE $y0, $new_alpha OTHERWISE $alpha); require($denom >= $singularity_threshold, "new balances would bring us too close to the singularity point, denom="||$denom); }; }", "messages": [ { "app": "state", "state": "{ // $u = $get_utilization_ratio(); // $A = $swap(); // $b = $charge_interest(); // $c = $update_leveraged_balances(); // $d = $trade_l_shares(); $h = $handle_trade_l_shares_request(); // $e = $buy_shares(); // $f = $redeem_shares(); // $g = $validate_and_apply_new_governed_param_value(); // $t = $get_total_balances(); bounce("library only"); }" } ] } ]
Technical information
Fees:
52,361 bytes
(452 headers, 51909 payload)
Level:2545589
Witnessed level:2545582
Main chain index:2523751
Latest included mc index:2523750
Status:stable/confirmed/final