[
"autonomous agent",
{
"getters": "{
$get_leverages = () => [2, 5, 10, 20, 50, 100];
$singularity_threshold = 0.1;
// X, Y through P:
// without LP leverage (Lambda)
$get_final_xy = ($X, $Y, $P, $final_P, $X0, $Y0, $pool_props, $inverted) => {
require($final_P >= $P, "not selling Y");
log('get_final_xy', $X, $Y, $P, $final_P, $X0, $Y0);
$a = $inverted ? $pool_props.beta : $pool_props.alpha; // alpha
$b = 1 - $a; // beta
$final_X = ($X + $X0) * ($P/$final_P)^$b - $X0;
$final_Y = $b/$a * $final_P * ($final_X + $X0) - $Y0;
$deltaX = $X - $final_X;
require($final_X >= 0, "bad final_X " || $final_X);
require($final_Y >= 0, "bad final_Y " || $final_Y);
require($deltaX >= 0, "bad deltaX " || $deltaX);
{
X: round($final_X),
Y: round($final_Y),
}
};
// along x means keeping x fully leveraged (y underleveraged)
$get_final_xy_along_x = ($X, $Y, $P, $final_P, $pool_props, $inverted) => {
$a = $inverted ? $pool_props.beta : $pool_props.alpha; // alpha
$b = 1 - $a; // beta
$final_X = $X * ($P/$final_P)^($b*$pool_props.Lambda);
$final_Y = $b/$a * $final_P * $final_X;
{
X: round($final_X),
Y: round($final_Y),
}
};
// along y means keeping y fully leveraged (x underleveraged)
$get_final_xy_along_y = ($X, $Y, $P, $final_P, $pool_props, $inverted) => {
$a = $inverted ? $pool_props.beta : $pool_props.alpha; // alpha
$b = 1 - $a; // beta
$final_Y = $Y * ($final_P/$P)^($a*$pool_props.Lambda);
$final_X = $a/$b * $final_Y / $final_P;
{
X: round($final_X),
Y: round($final_Y),
}
};
$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;
};
$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;
// $P_powers = $precompute($P);
// $final_P_powers = $precompute($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;
// $PL1 = $pow($P_powers, $L) / $P;
// $final_PL1 = $pow($final_P_powers, $L) / $final_P;
$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, $in_delta_Yn, $final_P, $received_amount_Y, $min_amount_out, $pool_props) => {
require(!$in_delta_Yn, "no delta Yn please, this is swap by P");
$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;
}
$P = $a/$b * ($Y + $Y0) / ($X + $X0); // price of X in terms of Y
require($final_P > $P, "price should increase, current " || $P || ", target " || $final_P);
if ($Lambda > 1){
$underleveraged = $Xn > ceil($X/$Lambda);
}
if ($Lambda == 1){
$final = $get_final_xy($X, $Y, $P, $final_P, $X0, $Y0, $pool_props, $inverted);
$final_X = $final.X;
$final_Y = $final.Y;
$final_Xn = $final_X;
$final_Yn = $final_Y;
}
else if (!$underleveraged){ // along X
$final = $get_final_xy_along_x($X, $Y, $P, $final_P, $pool_props, $inverted);
$final_X = $final.X;
$final_Y = $final.Y;
$final_Xn = round($final_X/$Lambda);
$delta_Y = $final_Y - $Y;
$delta_Yn = -round($a/($b*$Lambda-1)*$delta_Y);
$final_Yn = $Yn + $delta_Yn;
}
else if ($underleveraged){
$inflection_P = $P * ( $Lambda/($Lambda-1) * ($b + ($a * $Lambda - 1) * $Xn/$X) )^(1/($a*$Lambda-1));
$inflected = $final_P > $inflection_P;
// along Y until the inflection point
$final_P1 = $inflected ? $inflection_P : $final_P;
$final1 = $get_final_xy_along_y($X, $Y, $P, $final_P1, $pool_props, $inverted);
$final_X1 = $final1.X;
$final_Y1 = $final1.Y;
$final_Yn1 = round($final_Y1 / $Lambda);
$delta_X1 = $final_X1 - $X;
$delta_Xn1 = -round($b/($a*$Lambda-1) * $delta_X1);
$final_Xn1 = $Xn + $delta_Xn1;
if ($inflected){
// then, along X
$final = $get_final_xy_along_x($final_X1, $final_Y1, $final_P1, $final_P, $pool_props, $inverted);
$final_X = $final.X;
$final_Y = $final.Y;
$final_Xn = round($final_X/$Lambda);
$delta_Y2 = $final_Y - $final_Y1;
$delta_Yn2 = -round($a/($b*$Lambda-1)*$delta_Y2);
$final_Yn = $final_Yn1 + $delta_Yn2;
require($final_Xn <= $final_Xn1, "Xn didn't decrease");
}
else{
$final_X = $final_X1;
$final_Y = $final_Y1;
$final_Xn = $final_Xn1;
$final_Yn = $final_Yn1;
}
}
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;
log("balances after swap", $balances);
$final_y = $balances.y;
$final_x = $balances.x;
// 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 swap to price:', $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
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)
if ($Lambda > 1)
$add_net_balance_without_changing_price($balances, $y_in ? 'x' : 'y', $fee, $Lambda);
{
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,
}
};
}",
"messages": [
{
"app": "state",
"state": "{
$A = $swap();
bounce("library only");
}"
}
]
}
]