Unit ID
mLr6p3/2jcSrKT6SkdjYFLE345aqtOGAUE9S8IhKZLU=
Received
21.12.2021 00:35:15
Confirmation delay (full node)
1 minute 44 seconds
Confirmation delay (light node)
2 minutes 29 seconds
Messages
Definition
Definition: [ "autonomous agent", { "getters": "{ $get_leverages = () => [2, 5, 10, 20, 50, 100]; $singularity_threshold = 0.1; $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); round($final_X) }; // along x means keeping x fully leveraged (y underleveraged) $get_final_x_along_x = ($X, $Y, $final_Y, $pool_props, $inverted) => { $b = $inverted ? $pool_props.alpha : $pool_props.beta; // beta round($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) => { $a = $inverted ? $pool_props.beta : $pool_props.alpha; // alpha round($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"); $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); round($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 round($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 round($Y * ($final_X/$X)^($a*$pool_props.Lambda/($a*$pool_props.Lambda - 1))) }; $add_net_balance_without_changing_price = ($balances, $side, $amount, $Lambda) => { require($Lambda > 1, "Lambda must be > 1"); $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){ // 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 = round($ratio * $X); $delta_Y = round($ratio * $Y); } else{ $delta_X = round($delta_Xn * $Lambda); $delta_Y = round($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; }; $charge_interest = ($balances, $l_balances, $profits, $x0, $y0, $last_ts, $i, $alpha, $Lambda) => { require($last_ts, "no last ts"); $beta = 1 - $alpha; $x = $balances.x; $y = $balances.y; $accrued_rate = (timestamp - $last_ts)/3600/24/360 * $i; if ($Lambda == -1){ $sumX = reduce($get_leverages(), 6, ($acc, $L) => $acc + ($L-1)*$l_balances[$L||'x'].balance, 0) / ($x + $x0); $sumY = reduce($get_leverages(), 6, ($acc, $L) => $acc + ($L-1)*$l_balances[-$L||'x'].balance, 0) / ($y + $y0); } else{ // we change x and y in such a way that the price does not change $sumX = 0; $sumY = 0; } $denom = 1 - $sumX - $sumY; $y2x = ($y + $y0) / ($x + $x0); $p = $alpha/$beta * $y2x; $n_deltas = {dxn:0, dyn:0}; $sums = {x:0, y:0, xd:0, yd:0}; foreach($get_leverages(), 6, ($L) => { $xL = $l_balances[$L||'x'].balance; $yL = $l_balances[-$L||'x'].balance; if ($xL){ $dxL = -min(round($xL * ($L-1) * $accrued_rate * (1 - 2 * $sumY) / $denom), $xL); $l_balances[$L||'x'].balance = $xL + $dxL; $sums.x = $sums.x + $dxL; $sums.xd = $sums.xd + $dxL * ($L-1)/$L; // $n_deltas.dxn = $n_deltas.dxn - $dxL; // $dxL * $p comes from swapping, minus the pool's earnings from interest $dxL * $p / $L // 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.y = $profits.y - $dxL * $p / $L; $profits.x = $profits.x - $dxL - $delta_yn / $y2x; } else $n_deltas.dxn = -$dxL; // > 0 } if ($yL){ $dyL = -min(round($yL * ($L-1) * $accrued_rate * (1 - 2 * $sumX) / $denom), $yL); $l_balances[-$L||'x'].balance = $yL + $dyL; $sums.y = $sums.y + $dyL; $sums.yd = $sums.yd + $dyL * ($L-1)/$L; // $n_deltas.dyn = $n_deltas.dyn - $dyL; // $dyL / $p comes from swapping, minus the pool's earnings from interest $dyL / $p / $L // 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.x = $profits.x - $dyL / $p / $L; $profits.y = $profits.y - $dyL - $delta_xn * $y2x; } else $n_deltas.dyn = -$dyL; // > 0 } }); // log('interest', $n_deltas); // $p = ($alpha * ($y + $y0) - $alpha * $sums.y - $beta * $sums.yd) / ($beta * ($x + $x0) - $beta * $sums.x - $alpha * $sums.xd); // $dxn = -$sums.x + $sums.yd / $p; // $dyn = -$sums.y + $sums.xd * $p; $dxn = round($n_deltas.dxn); $dyn = round($n_deltas.dyn); // $dxn = round($n_deltas.dxn - $n_deltas.dyn / $p); // $dyn = round($n_deltas.dyn - $n_deltas.dxn * $p); // require($dyn == round($p * $dxn), "dyn " || $dyn || " != calc " || round($p * $dxn)); if ($Lambda == 1){ $profits.x = round($profits.x); $profits.y = round($profits.y); $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, 'x', $dxn, $Lambda); $add_net_balance_without_changing_price($balances, 'y', $dyn, $Lambda); } }; $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 }; $update_leveraged_balances = ($x, $y, $final_x, $final_y, $x0, $y0, $l_balances, $alpha, $inverted) => { $beta = 1 - $alpha; $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; $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; $debt_ratio = ($L-1)/$L; if ($balance) { $delta_XL_balance = round($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 = round($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, $x0, $y0, $y_in, $delta_Yn, $in_final_P, $received_amount_Y, $min_amount_out, $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; } 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; } 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 = -round(($b*$Lambda-1)/$a*$delta_Yn); $final_Y = $Y + $delta_Y; $final_X = $get_final_x_along_x($X, $Y, $final_Y, $pool_props, $inverted); $final_Xn = round($final_X/$Lambda); // $leveraged_along = 'X'; } else if ($underleveraged){ $delta_Yn_inflection = round($Y * (( $Lambda/($Lambda-1) * ($b + ($a * $Lambda - 1) * $Xn/$X) )^($a * $Lambda/($a*$Lambda-1)) - 1) / $Lambda); $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 = round($final_Yn1 * $Lambda); $final_X1 = $get_final_x_along_y($X, $Y, $final_Y1, $pool_props, $inverted); $delta_X1 = $final_X1 - $X; $delta_Xn1 = -round($b/($a*$Lambda-1) * $delta_X1); $final_Xn1 = $Xn + $delta_Xn1; if ($inflected){ // then, along X $delta_Yn2 = $final_Yn - $final_Yn1; $delta_Y2 = -round(($b*$Lambda-1)/$a*$delta_Yn2); $final_Y = $final_Y1 + $delta_Y2; $final_X = $get_final_x_along_x($final_X1, $final_Y1, $final_Y, $pool_props, $inverted); $final_Xn = round($final_X/$Lambda); require($final_Xn <= $final_Xn1, "Xn didn't decrease"); // $leveraged_along = 'X'; } else{ $final_X = $final_X1; $final_Xn = $final_Xn1; $final_Y = $final_Y1; // $leveraged_along = 'Y'; } } 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; // $final_Yn = $res.Yn; // $final_Xn = $res.Xn; // $final_X = $res.X; // $final_Y = $res.Y; // if inverted, XL corresponds to y, YL to x $totals = $update_leveraged_balances($x, $y, $final_x, $final_y, $x0, $y0, $l_balances, $alpha, $inverted); $amount_X = floor(-($final_Xn - $X + $totals.delta_XL)); $amount_Y = ceil($final_Yn - $Yn + $totals.delta_YL); if ($received_amount_Y >= 0) require($received_amount_Y >= $amount_Y, "expected " || $amount_Y || ", received " || $received_amount_Y); require($amount_X >= 0, "to pay " || $amount_X); if ($min_amount_out) require($amount_X >= $min_amount_out, "output amount " || $amount_X || " would be less than the expected minimum " || $min_amount_out); $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 L', $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 $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); $arb_profit_in_Y = ($final_P - $P) * $amount_X / 2; // in Y $arb_profit_in_X = $arb_profit_in_Y / $final_P; $fee = ceil($arb_profit_in_X * $pool_props.arb_profit_tax + $amount_X * $pool_props.swap_fee); $net_amount_X = $amount_X - $fee; // add the fee to the pool without trading and affecting the price (Lambda>1) or to a separate profit accumulator (Lambda=1) // add the fee to the pool, this might affect the price amd make L-pools trade if ($Lambda == 1){ // some X has been added to the pool, therefore X got cheaper and L-pools need to trade // These calculations assume the fee is small compared with the pool size /* $sumX = reduce($leverages, 6, ($acc, $L) => $acc + ($L-1)*$l_balances[$L].balance, 0) / ($x + $x0); $sumY = reduce($leverages, 6, ($acc, $L) => $acc + ($L-1)*$l_balances[-$L].balance, 0) / ($y + $y0); $fee_delta_X_traded = $fee * ($sumX + $beta/$alpha * $sumY) / (1 - $sumX/$beta - $sumY/$alpha); $fee_delta_Xn = $fee + $fee_delta_X_traded; $fee_delta_Yn = -$p * $fee_delta_X_traded; $fee_delta_X = $fee_delta_Xn; $fee_delta_Y = $fee_delta_Yn;*/ } else{ $add_net_balance_without_changing_price($balances, $y_in ? 'x' : 'y', $fee, $Lambda); /* $fee_delta_Xn = $fee; $fee_delta_Yn = 0; // the price doesn't change as X and Y grow proportionally if ($res.leveraged_along == 'X'){ // Y is underleveraged, increase Y proportionally while keeping Yn intact $full_fee_delta_Y = $final_Y * $fee_delta_Xn/$final_Xn; if ($final_Y + $full_fee_delta_Y > $final_Yn * $Lambda){ // would overshoot and make Y overleveraged $ratio = $final_Yn * $Lambda / $final_Y - 1; $fee_delta_X = round($ratio * $final_X); $fee_delta_Y = round($ratio * $final_Y); } else{ $fee_delta_X = round($fee_delta_Xn * $Lambda); $fee_delta_Y = round($full_fee_delta_Y); } } else{ $fee_delta_X = 0; // only net X gets increased $fee_delta_Y = 0; }*/ } { net_amount_X: $net_amount_X, amount_Y: $amount_Y, fee: $fee, change: $change, // delta_Xn: $final_Xn - $Xn + $fee_delta_Xn, // delta_Yn: $delta_Yn + $fee_delta_Y, // delta_X: $final_X - $X + $fee_delta_X, // delta_Y: $final_Y - $Y + $fee_delta_Y, } }; $buy_shares = ($s, $balances, $x0, $y0, $received_amount_x, $received_amount_y, $profits, $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; 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 + round($received_amount_x * $Lambda); $balances.y = $balances.y + round($received_amount_y * $Lambda); return { shares_amount: $shares_amount, coef: 1, change_x: 0, change_y: 0, // delta_x: round($received_amount_x * $Lambda), // delta_y: round($received_amount_x * $Lambda), }; } $p = $alpha/$beta * ($y + $y0) / ($x + $x0); $share_price_in_y = ($yn + $p * $xn) / $s; $share_price_in_x = ($xn + 1/$p * $yn) / $s; if ($Lambda > 1){ $proportional_y = round($yn/$xn * $received_amount_x); if ($received_amount_y > $proportional_y){ // ok if x is underleveraged $target_xn = ceil($x/$Lambda); if ($xn > $target_xn){ // x is underleveraged $max_delta_yn = round(($xn/$target_xn-1)*$yn); $delta_yn1 = min($max_delta_yn, $received_amount_y); $shares1 = floor($delta_yn1/$share_price_in_y); } $delta_xn1 = 0; } else{ // received x >= proportional x $target_yn = ceil($y/$Lambda); if ($yn > $target_yn){ // y is underleveraged $max_delta_xn = round(($yn/$target_yn-1)*$xn); $delta_xn1 = min($max_delta_xn, $received_amount_x); $shares1 = floor($delta_xn1/$share_price_in_x); } $delta_yn1 = 0; } $balances.xn = $balances.xn + $delta_xn1; $balances.yn = $balances.yn + $delta_yn1; } 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); // 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 = round($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 = round($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 $moved_profit_x = min($profits.x, round($remaining.y / $y_to_x)); $moved_profit_y = min($profits.y, round($remaining.x * $y_to_x)); // $share_price_in_x = ($balances.xn + $profits.x + 1/$p * ($balances.yn + $profits.y)) / $s; // $share_price_in_y = (($balances.xn + $profits.x) * $p + $balances.yn + $profits.y) / $s; // $share_price_in_x = ($balances.xn + 1/$p * $balances.yn) / $s; // not including the profits: the profit pool just buys shares along with the user at the old price // $share_price_in_y = ($balances.xn * $p + $balances.yn) / $s; $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; // $balances.xn = $balances.xn + $moved_profit_x; // $balances.yn = $balances.yn + $moved_profit_y; // $balances.x = $balances.x + $moved_profit_x; // $balances.y = $balances.y + $moved_profit_y; } // part 2: proportional buying log('before proportional buying: remaining', $remaining, 'y_to_x', $y_to_x); $remaining_proportional_y = round($y_to_x * $remaining.x); if ($remaining.y > $remaining_proportional_y){ // excessive y $proportional_delta_xn = $remaining.x; $proportional_delta_yn = $remaining_proportional_y; $change_x = 0; $change_y = $remaining.y - $remaining_proportional_y; // $shares_proportional = floor($remaining.x / ($xn + $delta_xn1) * ($s + $shares1)); $shares_proportional = ($remaining.x / $xn * $s); } else{ // excessive x $remaining_proportional_x = round($remaining.y / $y_to_x); $proportional_delta_xn = $remaining_proportional_x; $proportional_delta_yn = $remaining.y; $change_x = $remaining.x - $remaining_proportional_x; log({proportional_delta_xn:$proportional_delta_xn, change_x:$change_x, remaining_x:$remaining.x, remaining_proportional_x:$remaining_proportional_x}); require($change_x >= 0, "received x " || $remaining.x || ", proportional " || $remaining_proportional_x); $change_y = 0; // $shares_proportional = floor($remaining.y / ($yn + $delta_yn1) * ($s + $shares1)); $shares_proportional = ($remaining.y / $yn * $s); } $gross_shares_amount = $shares1 + $symmetric_moved_profit_shares + $shares_proportional; $shares_amount = floor($gross_shares_amount - $moved_profit_shares); $coef = ($s + $gross_shares_amount) / ($s + $shares_amount); 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 + round($proportional_delta_xn * $Lambda); $balances.y = $balances.y + round($proportional_delta_yn * $Lambda); // $balances.x = $balances.x + round(($delta_xn1 + $proportional_delta_xn) * $Lambda); // $balances.y = $balances.y + round(($delta_yn1 + $proportional_delta_yn) * $Lambda); { shares_amount: $shares_amount, coef: $coef, change_x: $change_x, change_y: $change_y, // delta_x: ($delta_xn1 + $remaining.x - $change_x) * $Lambda, // delta_y: ($delta_yn1 + $remaining.y - $change_y) * $Lambda, } }; $redeem_shares = ($s, $balances, $l_balances, $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"); $asset_label = $asset == $x_asset ? 'x' : 'y'; $net_balance = $asset == $x_asset ? $xn : $yn; $effective_balance = $asset == $x_asset ? $x : $y; $target_net_balance = ceil($effective_balance / $Lambda); // require($target_net_balance < $net_balance, "the preferred asset is already fully leveraged"); $excess_net_balance = $net_balance - $target_net_balance; $p = $alpha/$beta * $y / $x; $share_price_in_asset = ($asset_label == 'y') ? ($yn + $p * $xn) / $s : ($xn + 1/$p * $yn) / $s; $max_asset = ceil($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 - ceil($excess_net_balance / $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 = floor($share_of_assets * $x * $net_of_exit_fee); $y_amount = floor($share_of_assets * $y * $net_of_exit_fee); $xn_amount = floor(($share_of_assets * ($xn - $xn_amount1) + $xn_amount1) * $net_of_exit_fee); $yn_amount = floor(($share_of_assets * ($yn - $yn_amount1) + $yn_amount1) * $net_of_exit_fee); $balances.x = $balances.x - $x_amount; $balances.y = $balances.y - $y_amount; $balances.xn = $balances.xn - $xn_amount; $balances.yn = $balances.yn - $yn_amount; $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); { xn_amount: $xn_amount, yn_amount: $yn_amount, x_amount: $x_amount, y_amount: $y_amount, } }; $update_other_l_balances_and_get_sums = ($l_balances, $P, $final_P, $Leverage, $inverted) => { // $P_powers = $precompute($P); // $final_P_powers = $precompute($final_P); $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, }; 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; // $PL1 = $pow($P_powers, $L) / $P; // $final_PL1 = $pow($final_P_powers, $L) / $final_P; $ratio_L1 = $pow($ratio_powers, $L) / $ratio; if ($balance){ $sums.initial = $sums.initial + $balance * ($L-1)/$L; if ($L != $Leverage){ $new_balance = ($balance * $ratio_L1); // $new_balance = round($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); // $new_obalance = round($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; // delta of the swap-pool's X: borrowed X changes by trading in the pool } }); // $sums.delta_XL = round($sums.delta_XL); $sums }; // delta_Xn < 0: buy L-tokens // delta_Xn > 0: sell L-tokens $trade_l_shares = ($balances, $l_balances, $x0, $y0, $Leverage, $asset, $delta_Xn, $entry_price, $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; } 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; } require($Xn + $delta_Xn > 0, "Xn balance would be negative"); $L_key = ($inverted ? -$Leverage : $Leverage) || 'x'; $l_bal_direction = $delta_Xn < 0 ? "grow" : "fall"; $pool = {X: $X, Y: $Y, Xn: $Xn, Yn: $Yn, XL: 0}; $move_unleveraged = ($dXn) => { $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 = this_address#1.$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) ) ); // $new_l_balance = round( $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 * $delta_Xn < 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 = ($dXn) => { $P = $a/$b * ($pool.Y + $Y0) / ($pool.X + $X0); $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 = -round($a/($b*$Lambda-1)*$delta_Y); $final_Yn = $pool.Yn + $delta_Yn; $final_P = $a/$b * ($final_Y + $Y0) / ($final_X + $X0); $sums = this_address#1.$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 = round( $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 * $delta_Xn < 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 = ($dXn) => { $P = $a/$b * ($pool.Y + $Y0) / ($pool.X + $X0); $final_Xn = $pool.Xn + $dXn; $delta_X = -round(($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 = round($final_Y/$Lambda); $final_P = $a/$b * ($final_Y + $Y0) / ($final_X + $X0); $sums = this_address#1.$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 = round( $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 * $delta_Xn < 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; }; $initial_l_balance = $l_balances[$L_key].balance; $initial_shares = $l_balances[$L_key].supply; 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) $move_unleveraged($delta_Xn); else { $underleveraged = $Xn > ceil($X/$Lambda); if (!$underleveraged){ // along X if ($delta_Xn > 0){ // selling L-shares and X $delta_Xn_inflection = round($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; $move_along_X($dXn1); if ($inflected) $move_along_Y($delta_Xn - $delta_Xn_inflection); } else{ // along Y if ($delta_Xn < 0){ // buying L-shares and X $delta_Xn_inflection = -round($b/($Lambda-1) * ($Lambda*$Xn - $X)); $inflected = abs($delta_Xn) > abs($delta_Xn_inflection); } $dXn1 = $inflected ? $delta_Xn_inflection : $delta_Xn; $move_along_Y($dXn1); if ($inflected) $move_along_X($delta_Xn - $delta_Xn_inflection); } } $final_l_balance = $l_balances[$L_key].balance; $delta_l_balance = $final_l_balance - $initial_l_balance; // the change in the total X balance of swap pool + all L-pools (positive when buying, negative when selling) $net_delta = round($delta_Xn + $pool.delta_XL + $delta_l_balance); $final_shares = $initial_l_balance ? round($final_l_balance/$initial_l_balance / $pool.PL1_ratio * $initial_shares) : $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; $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 $final_P = $a/$b * ($pool.Y + $Y0) / ($pool.X + $X0); $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 / $final_P; $trading_fee = ceil($arb_profit_in_X * $pool_props.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(ceil(($avg_share_price - $entry_price)*(-$shares)*$pool_props.leverage_profit_tax), 0); } else $l_tax = ceil($gross_asset_out * $pool_props.leverage_token_tax); } $total_fee = $trading_fee + $l_tax; if ($Lambda > 1) $add_net_balance_without_changing_price($balances, $inverted ? 'y' : 'x', $total_fee, $Lambda); // For buying, the fee is added on top. For selling (net_delta<0), the fees are subtracted $gross_delta = $net_delta + $total_fee; { shares: $shares, net_delta: $net_delta, gross_delta: $gross_delta, avg_share_price: $avg_share_price, l_tax: $l_tax, trading_fee: $trading_fee, total_fee: $total_fee, // delta_l_balance: $delta_l_balance, // delta_total_XL: $pool.delta_XL, } }; $handle_trade_l_shares_request = ($pool_aa, $balances, $l_balances, $x0, $y0, $trigger_data, $trigger_address, $trigger_outputs, $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, $x0, $y0, $L, $asset, $delta_Xn, $position.price, $pool_props); $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, $profits, $lp_shares, $pool_props, $locked_governance) => { $Lambda = $pool_props.Lambda; $alpha = $pool_props.alpha; $beta = $pool_props.beta; $gamma = $pool_props.gamma; $mid_price = $pool_props.mid_price; if ($locked_governance) require(!$locked_governance[$name], "governance is not allowed to change "||$name); if ($name == 'pool_leverage'){ require(!$profits.x AND !$profits.y, "profits must be added to the pool before changing pool_leverage"); require($alpha != 1/$value, "pool leverage = 1/alpha"); require($beta != 1/$value, "pool leverage = 1/beta"); if ($value > 1){ require(!$mid_price, "price range setting is incompatible with new pool leverage"); $balances.x = round($balances.x * $value/$Lambda); $balances.y = round($balances.y * $value/$Lambda); } else{ $balances.x = $balances.xn; $balances.y = $balances.yn; } // we modified balances } 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; } $s_curve = $lp_shares.linear * $lp_shares.coef; $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 modified lp_shares } else if ($name == 'alpha'){ require(!$mid_price, "can't change token weights while trading in limited range"); require($value != 1/$Lambda AND 1-$value != 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; // nothing modified } }; }", "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:
44,752 bytes
(452 headers, 44300 payload)
Level:2537588
Witnessed level:2537581
Main chain index:2515753
Latest included mc index:2515752
Status:stable/confirmed/final