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;
};
// 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: $final_X,
Y: $final_Y,
}
};
// along x means keeping x fully leveraged (y underleveraged)
$get_final_xy_along_x = ($X, $Y, $P, $final_P, $pool_props, $inverted) => {
require($final_P >= $P, "along X: not selling Y");
$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: $final_X,
Y: $final_Y,
}
};
// along y means keeping y fully leveraged (x underleveraged)
$get_final_xy_along_y = ($X, $Y, $P, $final_P, $pool_props, $inverted) => {
require($final_P >= $P, "along Y: not selling Y");
$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: $final_X,
Y: $final_Y,
}
};
$add_net_balance_without_changing_price = ($balances, $profits, $side, $amount, $Lambda) => {
if ($amount == 0)
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){
// 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
// bounce('overshoot');
$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;
}
// log('add_net_balance_without_changing_price', {side: $side, amount: $amount, delta_X: $delta_X, delta_Y: $delta_Y, Xn: $Xn, Y: $Y});
$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 = ($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;
$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, $in_delta_Yn, $final_P, $received_amount_Y, $min_amount_out, $trigger_initial_address, $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;
$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';
}
$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 = $final_X/$Lambda;
$delta_Y = $final_Y - $Y;
$delta_Yn = -$a/($b*$Lambda-1)*$delta_Y;
$final_Yn = $Yn + $delta_Yn;
require($final_Yn > 0, "fully leveraged: negative final_Yn="||$final_Yn);
}
else if ($underleveraged){
$inflection_P = $P * ( $Lambda/($Lambda-1) * ($b + ($a * $Lambda - 1) * $Xn/$X) )^(1/($a*$Lambda-1));
require($inflection_P > 0, "negative inflection_P="||$inflection_P);
$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 = $final_Y1 / $Lambda;
$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 price', $inflection_P);
$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 = $final_X/$Lambda;
$delta_Y2 = $final_Y - $final_Y1;
$delta_Yn2 = -$a/($b*$Lambda-1)*$delta_Y2;
$final_Yn = $final_Yn1 + $delta_Yn2;
require($final_Xn > 0, "negative final_Xn="||$final_Xn);
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);
// 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 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
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("balances after adding the fees", $balances);
$update_recent_data($recent, $inverted ? 1/$P : $P, $inverted ? 1/$final_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,
}
};
}",
"messages": [
{
"app": "state",
"state": "{
$A = $swap();
bounce("library only");
}"
}
]
}
]