[
"autonomous agent",
{
"getters": "{
$get_leverages = () => [2, 5, 10, 20, 50, 100];
$singularity_threshold = 0.01;
$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){
$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
};
$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;
$b = 1 - $a;
$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
};
$get_final_x_along_x = ($X, $Y, $final_Y, $pool_props, $inverted) => {
$b = $inverted ? $pool_props.alpha : $pool_props.beta;
$X * ($final_Y/$Y)^($b * $pool_props.Lambda/($b * $pool_props.Lambda - 1))
};
$get_final_x_along_y = ($X, $Y, $final_Y, $pool_props, $inverted) => {
$a = $inverted ? $pool_props.beta : $pool_props.alpha;
$X * ($final_Y/$Y)^(1-1/$a/$pool_props.Lambda)
};
$get_final_y = ($X, $Y, $final_X, $X0, $Y0, $pool_props, $inverted) => {
$a = $inverted ? $pool_props.beta : $pool_props.alpha;
$b = 1 - $a;
$final_Y = ($Y + $Y0) * (($X + $X0)/($final_X + $X0))^($a/$b) - $Y0;
require($final_Y >= 0, "bad final_Y " || $final_Y);
$final_Y
};
$get_final_y_along_x = ($X, $Y, $final_X, $pool_props, $inverted) => {
$b = $inverted ? $pool_props.alpha : $pool_props.beta;
$Y * ($final_X/$X)^(1 - 1/$b/$pool_props.Lambda)
};
$get_final_y_along_y = ($X, $Y, $final_X, $pool_props, $inverted) => {
$a = $inverted ? $pool_props.beta : $pool_props.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;
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;
if (!$underleveraged){
$full_delta_Y = $Y * $delta_Xn/$Xn;
if ($Y + $full_delta_Y > $Yn * $Lambda){
$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;
$delta_Y = 0;
}
$balances[$side_n] = $balances[$side_n] + $delta_Xn;
$balances[$side] = $balances[$side] + $delta_X;
$balances[$opposite] = $balances[$opposite] + $delta_Y;
};
$pow = ($precomputed, $power) => {
require(typeof($precomputed[$power]) == 'number', "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
};
$powL1 = ($v, $L) => {
if ($L == 2)
return $v;
$v2 = $v * $v;
$v5 = $v2 * $v2 * $v;
if ($L == 5)
return $v5/$v;
$v10 = $v5 * $v5;
if ($L == 10)
return $v10/$v;
$v20 = $v10 * $v10;
if ($L == 20)
return $v20/$v;
$v50 = $v20 * $v20 * $v10;
if ($L == 50)
return $v50/$v;
$v100 = $v50 * $v50;
if ($L == 100)
return $v100/$v;
bounce("unknown L=" || $L);
};
$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);
$factor_powers = $precompute($factor);
$y2x = ($y + $y0) / ($x + $x0);
$p = $alpha/$beta * $y2x;
$n_deltas = {dxn:0, dyn:0};
$charged_interest = {x:0, y:0};
foreach($get_leverages(), 6, ($L) => {
$xL = $l_balances[$L||'x'].balance;
$yL = $l_balances[-$L||'x'].balance;
if (!$xL AND !$yL)
return;
$factorL1 = $factor ? $pow($factor_powers, $L) / $factor : 0;
$factorL1L1 = $powL1($factorL1, $L);
if ($xL){
$dxL = -$xL * (1 - $factorL1L1);
$l_balances[$L||'x'].balance = $xL + $dxL;
$delta_yn = $dxL * $p * ($L-1)/$L;
$n_deltas.dyn = $n_deltas.dyn + $delta_yn;
if ($Lambda == 1){
$n_deltas.dxn = $n_deltas.dxn + $delta_yn / $y2x;
$profits.x = $profits.x - $dxL - $delta_yn / $y2x;
}
else
$n_deltas.dxn = $n_deltas.dxn - $dxL;
$charged_interest.x = $charged_interest.x - $dxL + $delta_yn / $p;
}
if ($yL){
$dyL = -$yL * (1 - $factorL1L1);
$l_balances[-$L||'x'].balance = $yL + $dyL;
$delta_xn = $dyL / $p * ($L-1)/$L;
$n_deltas.dxn = $n_deltas.dxn + $delta_xn;
if ($Lambda == 1){
$n_deltas.dyn = $n_deltas.dyn + $delta_xn * $y2x;
$profits.y = $profits.y - $dyL - $delta_xn * $y2x;
}
else
$n_deltas.dyn = $n_deltas.dyn - $dyL;
$charged_interest.y = $charged_interest.y - $dyL + $delta_xn * $p;
}
});
$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);
}
$charged_interest
};
$update_leveraged_balances = ($l_balances, $P, $final_P, $inverted) => {
$ratio = $final_P/$P;
$ratio_powers = $precompute($ratio);
$totals = {
delta_XL: 0,
delta_YL: 0,
XL_denom: 0,
YL_denom: 0,
};
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 = $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);
$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) {
$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);
$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{
$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, "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){
$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;
$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){
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);
$P = $inverted ? 1/$p : $p;
$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);
$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);
require($denom >= $singularity_threshold, "too close to the singularity point, denom="||$denom||", need more liquidity in order to swap this amount");
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;
$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;
require($arb_profit_tax >= 0, "negative arb profit tax");
$swap_fee = $amount_X_exact * $pool_props.swap_fee;
$fee = $arb_profit_tax + $swap_fee;
$net_amount_X_exact = $amount_X_exact - $fee;
$net_amount_X = floor($net_amount_X_exact);
$rounding_fee_X = $net_amount_X_exact - $net_amount_X;
$rounding_fee_Y = $amount_Y - $amount_Y_exact;
$total_fee = $fee + $rounding_fee_X;
$avg_price = $amount_Y / $net_amount_X;
require($avg_price > $P, "avg price "||$avg_price||" below initial price "||$P);
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);
$fees = {
X: $total_fee,
Y: $rounding_fee_Y,
};
$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);
$update_recent_data($recent, $p, $final_p, $trigger_initial_address, $out_token, $amount_X_exact, $arb_profit_tax, $pool_props.period_length);
$event = json_stringify({
type: 'swap',
direction: $y_in ? 'y2x' : 'x2y',
in: $amount_Y,
out: $net_amount_X,
swap_fee: $swap_fee,
arb_profit_tax: $arb_profit_tax,
total_fee: $total_fee,
});
{
net_amount_X: $net_amount_X,
amount_Y: $amount_Y,
swap_fee: $swap_fee,
arb_profit_tax: $arb_profit_tax,
total_fee: $total_fee,
fees: $fees,
change: $change,
initial_price: $P,
final_price: $final_P,
event: $event,
}
};
$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/$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){
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{
$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;
$event = json_stringify({
type: 'add',
x: $received_amount_x,
y: $received_amount_y,
shares: $shares_amount,
});
return {
shares_amount: $shares_amount,
coef: 1,
change_x: 0,
change_y: 0,
event: $event,
};
}
$p = $alpha/$beta * ($y + $y0) / ($x + $x0);
if ($Lambda > 1 AND $recent.prev){
$target_xn = $x/$Lambda;
if ($xn > ceil($target_xn)){
$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;
$shares1 = $delta_yn1/$share_price_in_y;
}
else{
$target_yn = $y/$Lambda;
if ($yn > ceil($target_yn)){
$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;
$shares1 = $delta_xn1/$share_price_in_x;
}
}
$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);
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?");
$share_price_in_y = ($yn + $p * $xn) / $s;
$share_price_in_x = ($xn + 1/$p * $yn) / $s;
$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;
$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;
$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;
}
$remaining_proportional_y = $y_to_x * $remaining.x;
if ($remaining.y > $remaining_proportional_y){
$proportional_delta_xn = $remaining.x;
$proportional_delta_yn = $remaining_proportional_y;
$exact_change_x = 0;
$exact_change_y = $remaining.y - $remaining_proportional_y;
$shares_proportional = $remaining.x / ($xn + $delta_xn1) * ($s + $shares1);
}
else{
$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;
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;
$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);
$event = json_stringify({
type: 'add',
x: $received_amount_x - $change_x,
y: $received_amount_y - $change_y,
shares: $shares_amount,
});
{
shares_amount: $shares_amount,
coef: $coef,
change_x: $change_x,
change_y: $change_y,
event: $event,
}
};
$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;
$exit_fee = ($received_shares_amount < $s) ? $pool_props.exit_fee : 0;
$net_of_exit_fee = 1 - $exit_fee;
$x_asset = $pool_props.x_asset;
$y_asset = $pool_props.y_asset;
$Lambda = $pool_props.Lambda;
$alpha = $pool_props.alpha;
if ($asset){
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;
$excess_net_balance = max($net_balance - $target_net_balance, 0);
require($recent.prev, "too early, prev price not known yet");
$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);
$one_sided_fee = $one_sided_amount / $net_of_exit_fee * $exit_fee;
if ($asset_label == 'y'){
$yn_amount1 = $one_sided_amount;
$xn_amount1 = 0;
$one_sided_y_fee = $one_sided_fee;
}
else{
$xn_amount1 = $one_sided_amount;
$yn_amount1 = 0;
$one_sided_x_fee = $one_sided_fee;
}
$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;
$balances.x = $balances.x - $x_amount;
$balances.y = $balances.y - $y_amount;
$balances.xn = $balances.xn - $xn_amount;
$balances.yn = $balances.yn - $yn_amount;
$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);
$int_xn_amount = floor($xn_amount);
$int_yn_amount = floor($yn_amount);
$rounding_fee_x = $xn_amount - $int_xn_amount;
$rounding_fee_y = $yn_amount - $int_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);
$recent.last_ts = timestamp;
$event = json_stringify({
type: 'remove',
shares: $received_shares_amount,
x: $int_xn_amount,
y: $int_yn_amount,
x_fee: $share_of_assets * ($xn - $xn_amount1) * $exit_fee + $one_sided_x_fee,
y_fee: $share_of_assets * ($yn - $yn_amount1) * $exit_fee + $one_sided_y_fee,
});
{
xn_amount: $int_xn_amount,
yn_amount: $int_yn_amount,
coef: $coef,
event: $event,
}
};
$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,
};
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;
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;
require($final_Xn > 0, "unleveraged: final_Xn=" || $final_Xn);
$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;
require($final_Yn > 0, "unleveraged: final_Yn=" || $final_Yn);
$final_P = $a/$b * ($final_Y + $Y0) / $final_Xt;
$sums = $update_other_l_balances_and_get_sums($l_balances, $P, $final_P, $Leverage, $inverted);
$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) );
require($new_l_balance >= 0, "unleveraged: new_l_balance=" || $new_l_balance);
$delta_l_balance = $new_l_balance - $l_balances[$L_key].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;
$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 = $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;
require($final_Xn > 0, "along X: final_Xn=" || $final_Xn);
$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;
require($final_Yn > 0, "along X: final_Yn=" || $final_Yn);
$final_P = $a/$b * $final_Y / $final_X;
$sums = $update_other_l_balances_and_get_sums($l_balances, $P, $final_P, $Leverage, $inverted);
$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) );
require($new_l_balance >= 0, "along X: new_l_balance=" || $new_l_balance);
$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 = $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;
require($final_Xn > 0, "along Y: final_Xn=" || $final_Xn);
$delta_X = -($a*$Lambda-1)/$b * $dXn;
$final_X = $pool.X + $delta_X;
require($final_X > 0, "along Y: final_X=" || $final_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);
$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)) );
require($new_l_balance >= 0, "along Y: new_l_balance=" || $new_l_balance);
$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 = $pool.PL1_ratio * $sums.PL1_ratio;
};
$trade_l_shares = ($balances, $l_balances, $profits, $recent, $x0, $y0, $Leverage, $asset, $delta_Xn, $entry_price, $trigger_initial_address, $pool_props) => {
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{
$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, delta_XL: 0, PL1_ratio: 1};
$initial_l_balance = $l_balances[$L_key].balance;
$initial_shares = $l_balances[$L_key].supply;
$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){
if ($delta_Xn > 0){
$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{
if ($delta_Xn < 0){
$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);
$net_delta = $delta_Xn + $pool.delta_XL + $delta_l_balance;
$final_shares = $initial_shares
? floor($final_l_balance/($initial_l_balance OTHERWISE 0.1*$final_l_balance) / $pool.PL1_ratio * $initial_shares)
: round($net_delta);
$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);
}
$denom = 1 - $pool.XL_denom/$b/($pool.X+$X0) - $pool.YL_denom/$a/($pool.Y+$Y0);
require($denom >= $singularity_threshold, "too close to the singularity point, denom="||$denom||", need more liquidity in order to buy/sell 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;
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;
$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;
require($arb_profit_tax >= 0, "negative arb profit tax");
$swap_fee = abs($net_delta) * $pool_props.swap_fee;
if ($delta_Xn > 0){
$gross_asset_out = -$net_delta;
require($gross_asset_out > 0, "asset out must be positive, got " || $gross_asset_out);
if ($entry_price){
$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;
}
$subtotal_fee = $arb_profit_tax + $swap_fee + $l_tax;
$gross_delta_exact = $net_delta + $subtotal_fee;
$gross_delta = ceil($gross_delta_exact);
$rounding_fee = $gross_delta - $gross_delta_exact;
$total_fee = $subtotal_fee + $rounding_fee;
$add_net_balance_without_changing_price($balances, $profits, $token, $total_fee, $Lambda);
$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);
$event = json_stringify({
type: 'leverage',
token: $token,
L: $Leverage,
shares: $shares,
amount: $gross_delta,
swap_fee: $swap_fee,
arb_profit_tax: $arb_profit_tax,
l_tax: $l_tax,
total_fee: $total_fee,
});
{
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,
swap_fee: $swap_fee,
total_fee: $total_fee,
initial_price: $initial_P,
final_price: $final_P,
event: $event,
}
};
$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;
$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);
$shares = $res.shares;
$gross_delta = $res.gross_delta;
if ($buy){
$asset_out = $received_amount - $gross_delta;
require($asset_out >= 0, "expected " || $gross_delta || ", received " || $received_amount);
}
else{
$shares_change = $shares_in + $shares;
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");
$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){
$profits.x = $profits.x + $balances.xn - $balances.x;
$profits.y = $profits.y + $balances.yn - $balances.y;
$balances.xn = $balances.x;
$balances.yn = $balances.y;
}
}
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;
$new_sqp = sqrt($new_p0);
$x = $balances.x;
$y = $balances.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{
$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;
*/
}
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");
$new_y2x = $new_beta/$new_alpha * $alpha/$beta * $balances.y/$balances.x;
if ($Lambda > 1){
if ($balances.xn * $Lambda * $new_y2x <= $balances.yn * $Lambda){
$balances.x = $balances.xn * $Lambda;
$balances.y = $balances.x * $new_y2x;
}
else if ($balances.yn * $Lambda / $new_y2x <= $balances.xn * $Lambda){
$balances.y = $balances.yn * $Lambda;
$balances.x = $balances.y / $new_y2x;
}
else
bounce("can't preserve the price");
}
else {
$new_y = $balances.xn * $new_y2x;
if ($new_y <= $balances.yn){
$profits.y = $profits.y + $balances.yn - $new_y;
$balances.yn = $new_y;
$balances.y = $new_y;
}
else {
$new_x = $balances.yn / $new_y2x;
require($new_x <= $balances.xn, "neither excessive x nor excessive y");
$profits.x = $profits.x + $balances.xn - $new_x;
$balances.xn = $new_x;
$balances.x = $new_x;
}
}
}
$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": "{
$h = $handle_trade_l_shares_request();
bounce("library only");
}"
}
]
}
]