| 1 | [ |
| 2 | "autonomous agent", |
| 3 | { |
| 4 | "getters": "{ |
| 5 | |
| 6 | $get_leverages = () => [2, 5, 10, 20, 50, 100]; |
| 7 | |
| 8 | $singularity_threshold = 0.01; |
| 9 | |
| 10 | $trade_merge_period = 1; |
| 11 | |
| 12 | $update_recent_data = ($recent, $p, $final_p, $trigger_initial_address, $tax_token, $traded_amount, $paid_tax, $period_length) => { |
| 13 | $period_start_ts = floor(timestamp / $period_length) * $period_length; |
| 14 | $pmin = min($p, $final_p); |
| 15 | $pmax = max($p, $final_p); |
| 16 | if (+$recent.current.start_ts < $period_start_ts){ |
| 17 | $recent.prev = $recent.current; |
| 18 | $recent.current = {start_ts: $period_start_ts, pmin: $pmin, pmax: $pmax}; |
| 19 | } |
| 20 | else{ |
| 21 | $recent.current.pmin = min($recent.current.pmin, $pmin); |
| 22 | $recent.current.pmax = max($recent.current.pmax, $pmax); |
| 23 | } |
| 24 | if ($recent.last_trade AND $recent.last_trade.address == $trigger_initial_address AND $recent.last_ts >= timestamp - $trade_merge_period){ |
| 25 | $recent.last_trade.pmin = min($recent.last_trade.pmin, $pmin); |
| 26 | $recent.last_trade.pmax = max($recent.last_trade.pmax, $pmax); |
| 27 | $recent.last_trade.amounts[$tax_token] = $recent.last_trade.amounts[$tax_token] + $traded_amount; |
| 28 | $recent.last_trade.paid_taxes[$tax_token] = $recent.last_trade.paid_taxes[$tax_token] + $paid_tax; |
| 29 | } |
| 30 | else{ |
| 31 | $amounts = {x:0, y:0}; |
| 32 | $paid_taxes = {x:0, y:0}; |
| 33 | $amounts[$tax_token] = $traded_amount; |
| 34 | $paid_taxes[$tax_token] = $paid_tax; |
| 35 | $recent.last_trade = { |
| 36 | address: $trigger_initial_address, |
| 37 | pmin: $pmin, |
| 38 | pmax: $pmax, |
| 39 | amounts: $amounts, |
| 40 | paid_taxes: $paid_taxes, |
| 41 | }; |
| 42 | } |
| 43 | $recent.last_ts = timestamp; |
| 44 | }; |
| 45 | |
| 46 | $get_utilization_ratio = ($balances, $l_balances, $x0, $y0, $alpha) => { |
| 47 | $beta = 1 - $alpha; |
| 48 | $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); |
| 49 | $ratio |
| 50 | }; |
| 51 | |
| 52 | |
| 53 | |
| 54 | |
| 55 | $get_final_x = ($X, $Y, $final_Y, $X0, $Y0, $pool_props, $inverted) => { |
| 56 | require($final_Y >= $Y, "not selling Y"); |
| 57 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 58 | $b = 1 - $a; |
| 59 | $final_X = ($X + $X0) * (($Y + $Y0)/($final_Y + $Y0))^($b/$a) - $X0; |
| 60 | require($final_X >= 0, "bad final_X " || $final_X); |
| 61 | $deltaX = $X - $final_X; |
| 62 | require($deltaX >= 0, "bad deltaX " || $deltaX); |
| 63 | $final_X |
| 64 | }; |
| 65 | |
| 66 | |
| 67 | $get_final_x_along_x = ($X, $Y, $final_Y, $pool_props, $inverted) => { |
| 68 | |
| 69 | $b = $inverted ? $pool_props.alpha : $pool_props.beta; |
| 70 | $X * ($final_Y/$Y)^($b * $pool_props.Lambda/($b * $pool_props.Lambda - 1)) |
| 71 | }; |
| 72 | |
| 73 | |
| 74 | $get_final_x_along_y = ($X, $Y, $final_Y, $pool_props, $inverted) => { |
| 75 | |
| 76 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 77 | $X * ($final_Y/$Y)^(1-1/$a/$pool_props.Lambda) |
| 78 | }; |
| 79 | |
| 80 | |
| 81 | |
| 82 | |
| 83 | |
| 84 | $get_final_y = ($X, $Y, $final_X, $X0, $Y0, $pool_props, $inverted) => { |
| 85 | |
| 86 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 87 | $b = 1 - $a; |
| 88 | $final_Y = ($Y + $Y0) * (($X + $X0)/($final_X + $X0))^($a/$b) - $Y0; |
| 89 | require($final_Y >= 0, "bad final_Y " || $final_Y); |
| 90 | |
| 91 | |
| 92 | $final_Y |
| 93 | }; |
| 94 | |
| 95 | |
| 96 | $get_final_y_along_x = ($X, $Y, $final_X, $pool_props, $inverted) => { |
| 97 | $b = $inverted ? $pool_props.alpha : $pool_props.beta; |
| 98 | $Y * ($final_X/$X)^(1 - 1/$b/$pool_props.Lambda) |
| 99 | }; |
| 100 | |
| 101 | |
| 102 | $get_final_y_along_y = ($X, $Y, $final_X, $pool_props, $inverted) => { |
| 103 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 104 | $Y * ($final_X/$X)^($a*$pool_props.Lambda/($a*$pool_props.Lambda - 1)) |
| 105 | }; |
| 106 | |
| 107 | |
| 108 | |
| 109 | $add_net_balance_without_changing_price = ($balances, $profits, $side, $amount, $Lambda) => { |
| 110 | if (!$amount) |
| 111 | return; |
| 112 | if ($Lambda == 1){ |
| 113 | $profits[$side] = $profits[$side] + $amount; |
| 114 | return; |
| 115 | } |
| 116 | |
| 117 | $opposite = $side == 'x' ? 'y' : 'x'; |
| 118 | $side_n = $side || 'n'; |
| 119 | $opposite_n = $opposite || 'n'; |
| 120 | |
| 121 | $Xn = $balances[$side_n]; |
| 122 | $Yn = $balances[$opposite_n]; |
| 123 | $X = $balances[$side]; |
| 124 | $Y = $balances[$opposite]; |
| 125 | |
| 126 | $underleveraged = $Xn > ceil($X/$Lambda); |
| 127 | $delta_Xn = $amount; |
| 128 | |
| 129 | |
| 130 | if (!$underleveraged){ |
| 131 | |
| 132 | $full_delta_Y = $Y * $delta_Xn/$Xn; |
| 133 | if ($Y + $full_delta_Y > $Yn * $Lambda){ |
| 134 | $ratio = $Yn * $Lambda / $Y - 1; |
| 135 | $delta_X = $ratio * $X; |
| 136 | $delta_Y = $ratio * $Y; |
| 137 | } |
| 138 | else{ |
| 139 | $delta_X = $delta_Xn * $Lambda; |
| 140 | $delta_Y = $full_delta_Y; |
| 141 | } |
| 142 | } |
| 143 | else{ |
| 144 | $delta_X = 0; |
| 145 | $delta_Y = 0; |
| 146 | } |
| 147 | |
| 148 | $balances[$side_n] = $balances[$side_n] + $delta_Xn; |
| 149 | |
| 150 | $balances[$side] = $balances[$side] + $delta_X; |
| 151 | $balances[$opposite] = $balances[$opposite] + $delta_Y; |
| 152 | }; |
| 153 | |
| 154 | |
| 155 | $pow = ($precomputed, $power) => { |
| 156 | require(typeof($precomputed[$power]) == 'number', "no precomputed power " || $power); |
| 157 | $precomputed[$power] |
| 158 | }; |
| 159 | $precompute = $v => { |
| 160 | $pre = {}; |
| 161 | $pre['2'] = $v * $v; |
| 162 | $pre['5'] = $pre['2'] * $pre['2'] * $v; |
| 163 | $pre['10'] = $pre['5'] * $pre['5']; |
| 164 | $pre['20'] = $pre['10'] * $pre['10']; |
| 165 | $pre['50'] = $pre['20'] * $pre['20'] * $pre['10']; |
| 166 | $pre['100'] = $pre['50'] * $pre['50']; |
| 167 | $pre |
| 168 | }; |
| 169 | |
| 170 | |
| 171 | $powL1 = ($v, $L) => { |
| 172 | if ($L == 2) |
| 173 | return $v; |
| 174 | $v2 = $v * $v; |
| 175 | $v5 = $v2 * $v2 * $v; |
| 176 | if ($L == 5) |
| 177 | return $v5/$v; |
| 178 | $v10 = $v5 * $v5; |
| 179 | if ($L == 10) |
| 180 | return $v10/$v; |
| 181 | $v20 = $v10 * $v10; |
| 182 | if ($L == 20) |
| 183 | return $v20/$v; |
| 184 | $v50 = $v20 * $v20 * $v10; |
| 185 | if ($L == 50) |
| 186 | return $v50/$v; |
| 187 | $v100 = $v50 * $v50; |
| 188 | if ($L == 100) |
| 189 | return $v100/$v; |
| 190 | bounce("unknown L=" || $L); |
| 191 | }; |
| 192 | |
| 193 | |
| 194 | $charge_interest = ($balances, $l_balances, $profits, $x0, $y0, $last_ts, $i, $alpha, $Lambda) => { |
| 195 | require($last_ts, "no last ts"); |
| 196 | if (length($l_balances) == 0 OR $i == 0 OR timestamp == $last_ts) |
| 197 | return; |
| 198 | $beta = 1 - $alpha; |
| 199 | $x = $balances.x; |
| 200 | $y = $balances.y; |
| 201 | |
| 202 | $accrued_rate = (timestamp - $last_ts)/3600/24/360 * $i; |
| 203 | $factor = max(1 - $accrued_rate, 0); |
| 204 | $factor_powers = $precompute($factor); |
| 205 | |
| 206 | $y2x = ($y + $y0) / ($x + $x0); |
| 207 | $p = $alpha/$beta * $y2x; |
| 208 | $n_deltas = {dxn:0, dyn:0}; |
| 209 | $charged_interest = {x:0, y:0}; |
| 210 | |
| 211 | foreach($get_leverages(), 6, ($L) => { |
| 212 | $xL = $l_balances[$L||'x'].balance; |
| 213 | $yL = $l_balances[-$L||'x'].balance; |
| 214 | if (!$xL AND !$yL) |
| 215 | return; |
| 216 | $factorL1 = $factor ? $pow($factor_powers, $L) / $factor : 0; |
| 217 | $factorL1L1 = $powL1($factorL1, $L); |
| 218 | if ($xL){ |
| 219 | $dxL = -$xL * (1 - $factorL1L1); |
| 220 | $l_balances[$L||'x'].balance = $xL + $dxL; |
| 221 | |
| 222 | $delta_yn = $dxL * $p * ($L-1)/$L; |
| 223 | $n_deltas.dyn = $n_deltas.dyn + $delta_yn; |
| 224 | if ($Lambda == 1){ |
| 225 | $n_deltas.dxn = $n_deltas.dxn + $delta_yn / $y2x; |
| 226 | $profits.x = $profits.x - $dxL - $delta_yn / $y2x; |
| 227 | } |
| 228 | else |
| 229 | $n_deltas.dxn = $n_deltas.dxn - $dxL; |
| 230 | $charged_interest.x = $charged_interest.x - $dxL + $delta_yn / $p; |
| 231 | } |
| 232 | if ($yL){ |
| 233 | $dyL = -$yL * (1 - $factorL1L1); |
| 234 | $l_balances[-$L||'x'].balance = $yL + $dyL; |
| 235 | |
| 236 | $delta_xn = $dyL / $p * ($L-1)/$L; |
| 237 | $n_deltas.dxn = $n_deltas.dxn + $delta_xn; |
| 238 | if ($Lambda == 1){ |
| 239 | $n_deltas.dyn = $n_deltas.dyn + $delta_xn * $y2x; |
| 240 | $profits.y = $profits.y - $dyL - $delta_xn * $y2x; |
| 241 | } |
| 242 | else |
| 243 | $n_deltas.dyn = $n_deltas.dyn - $dyL; |
| 244 | $charged_interest.y = $charged_interest.y - $dyL + $delta_xn * $p; |
| 245 | } |
| 246 | }); |
| 247 | |
| 248 | |
| 249 | $dxn = $n_deltas.dxn; |
| 250 | $dyn = $n_deltas.dyn; |
| 251 | |
| 252 | if ($Lambda == 1){ |
| 253 | $balances.xn = $balances.xn + $dxn; |
| 254 | $balances.yn = $balances.yn + $dyn; |
| 255 | $balances.x = $balances.x + $dxn; |
| 256 | $balances.y = $balances.y + $dyn; |
| 257 | } |
| 258 | else{ |
| 259 | $add_net_balance_without_changing_price($balances, $profits, 'x', $dxn, $Lambda); |
| 260 | $add_net_balance_without_changing_price($balances, $profits, 'y', $dyn, $Lambda); |
| 261 | } |
| 262 | $charged_interest |
| 263 | }; |
| 264 | |
| 265 | |
| 266 | $update_leveraged_balances = ($l_balances, $P, $final_P, $inverted) => { |
| 267 | $ratio = $final_P/$P; |
| 268 | $ratio_powers = $precompute($ratio); |
| 269 | |
| 270 | $totals = { |
| 271 | delta_XL: 0, |
| 272 | delta_YL: 0, |
| 273 | XL_denom: 0, |
| 274 | YL_denom: 0, |
| 275 | }; |
| 276 | foreach($get_leverages(), 6, ($L) => { |
| 277 | $allyL = $inverted ? -$L : $L; |
| 278 | $balance = $l_balances[$allyL||'x'].balance; |
| 279 | $obalance = $l_balances[-$allyL||'x'].balance; |
| 280 | if (!$balance AND !$obalance) |
| 281 | return; |
| 282 | $ratio_L1 = $pow($ratio_powers, $L) / $ratio; |
| 283 | $debt_ratio = ($L-1)/$L; |
| 284 | if ($balance) { |
| 285 | $delta_XL_balance = $balance * ($ratio_L1 - 1); |
| 286 | $new_XL_balance = $balance + $delta_XL_balance; |
| 287 | $l_balances[$allyL||'x'].balance = $new_XL_balance; |
| 288 | $delta_YL_balance = -(($new_XL_balance * $final_P - $balance * $P) * $debt_ratio); |
| 289 | $totals.delta_XL = $totals.delta_XL + $delta_XL_balance; |
| 290 | $totals.delta_YL = $totals.delta_YL + $delta_YL_balance; |
| 291 | $totals.XL_denom = $totals.XL_denom + $new_XL_balance * ($L-1); |
| 292 | } |
| 293 | if ($obalance) { |
| 294 | $delta_YL_obalance = $obalance * (1/$ratio_L1 - 1); |
| 295 | $new_YL_obalance = $obalance + $delta_YL_obalance; |
| 296 | $l_balances[-$allyL||'x'].balance = $new_YL_obalance; |
| 297 | $delta_XL_obalance = -(($new_YL_obalance / $final_P - $obalance / $P) * $debt_ratio); |
| 298 | $totals.delta_YL = $totals.delta_YL + $delta_YL_obalance; |
| 299 | $totals.delta_XL = $totals.delta_XL + $delta_XL_obalance; |
| 300 | $totals.YL_denom = $totals.YL_denom + $new_YL_obalance * ($L-1); |
| 301 | } |
| 302 | }); |
| 303 | $totals |
| 304 | }; |
| 305 | |
| 306 | |
| 307 | |
| 308 | $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) => { |
| 309 | |
| 310 | require(!$in_final_P, "no final price please, this is swap by Y"); |
| 311 | |
| 312 | $alpha = $pool_props.alpha; |
| 313 | $beta = $pool_props.beta; |
| 314 | $Lambda = $pool_props.Lambda; |
| 315 | |
| 316 | $xn = $balances.xn; |
| 317 | $yn = $balances.yn; |
| 318 | $x = $balances.x; |
| 319 | $y = $balances.y; |
| 320 | |
| 321 | if ($y_in){ |
| 322 | $inverted = false; |
| 323 | $X = $x; |
| 324 | $Y = $y; |
| 325 | $Xn = $xn; |
| 326 | $Yn = $yn; |
| 327 | $X0 = $x0; |
| 328 | $Y0 = $y0; |
| 329 | $a = $alpha; |
| 330 | $b = $beta; |
| 331 | $in_token = 'y'; |
| 332 | $out_token = 'x'; |
| 333 | } |
| 334 | else{ |
| 335 | $inverted = true; |
| 336 | $X = $y; |
| 337 | $Y = $x; |
| 338 | $Xn = $yn; |
| 339 | $Yn = $xn; |
| 340 | $X0 = $y0; |
| 341 | $Y0 = $x0; |
| 342 | $a = $beta; |
| 343 | $b = $alpha; |
| 344 | $in_token = 'x'; |
| 345 | $out_token = 'y'; |
| 346 | } |
| 347 | require($delta_Yn > 0, "bad delta " || $delta_Yn); |
| 348 | |
| 349 | if ($Lambda > 1){ |
| 350 | $underleveraged = $Xn > ceil($X/$Lambda); |
| 351 | } |
| 352 | |
| 353 | $final_Yn = $Yn + $delta_Yn; |
| 354 | |
| 355 | if ($Lambda == 1){ |
| 356 | $final_Xn = $get_final_x($X, $Y, $final_Yn, $X0, $Y0, $pool_props, $inverted); |
| 357 | $final_X = $final_Xn; |
| 358 | $final_Y = $final_Yn; |
| 359 | } |
| 360 | else if (!$underleveraged){ |
| 361 | $delta_Y = -($b*$Lambda-1)/$a*$delta_Yn; |
| 362 | $final_Y = $Y + $delta_Y; |
| 363 | require($final_Y > 0, "fully leveraged: negative final_Y="||$final_Y); |
| 364 | $final_X = $get_final_x_along_x($X, $Y, $final_Y, $pool_props, $inverted); |
| 365 | $final_Xn = $final_X/$Lambda; |
| 366 | } |
| 367 | else if ($underleveraged){ |
| 368 | $delta_Yn_inflection = $Y * (( $Lambda/($Lambda-1) * ($b + ($a * $Lambda - 1) * $Xn/$X) )^($a * $Lambda/($a*$Lambda-1)) - 1) / $Lambda; |
| 369 | require($delta_Yn_inflection > 0, "negative delta_Yn_inflection="||$delta_Yn_inflection); |
| 370 | $inflected = $delta_Yn > $delta_Yn_inflection; |
| 371 | |
| 372 | $inflection_Yn = $Yn + $delta_Yn_inflection; |
| 373 | $final_Yn1 = $inflected ? $inflection_Yn : $final_Yn; |
| 374 | $final_Y1 = $final_Yn1 * $Lambda; |
| 375 | $final_X1 = $get_final_x_along_y($X, $Y, $final_Y1, $pool_props, $inverted); |
| 376 | $delta_X1 = $final_X1 - $X; |
| 377 | $delta_Xn1 = -$b/($a*$Lambda-1) * $delta_X1; |
| 378 | $final_Xn1 = $Xn + $delta_Xn1; |
| 379 | require($final_Xn1 > 0, "negative final_Xn1="||$final_Xn1); |
| 380 | if ($inflected){ |
| 381 | |
| 382 | log('inflected at ', $delta_Yn_inflection); |
| 383 | $delta_Yn2 = $final_Yn - $final_Yn1; |
| 384 | $delta_Y2 = -($b*$Lambda-1)/$a*$delta_Yn2; |
| 385 | $final_Y = $final_Y1 + $delta_Y2; |
| 386 | require($final_Y > 0, "inflected: negative final_Y="||$final_Y); |
| 387 | $final_X = $get_final_x_along_x($final_X1, $final_Y1, $final_Y, $pool_props, $inverted); |
| 388 | $final_Xn = $final_X/$Lambda; |
| 389 | require($final_Xn <= $final_Xn1, "Xn didn't decrease"); |
| 390 | } |
| 391 | else{ |
| 392 | $final_X = $final_X1; |
| 393 | $final_Xn = $final_Xn1; |
| 394 | $final_Y = $final_Y1; |
| 395 | } |
| 396 | } |
| 397 | else |
| 398 | bounce("???"); |
| 399 | |
| 400 | $balances.x = $y_in ? $final_X : $final_Y; |
| 401 | $balances.y = $y_in ? $final_Y : $final_X; |
| 402 | $balances.xn = $y_in ? $final_Xn : $final_Yn; |
| 403 | $balances.yn = $y_in ? $final_Yn : $final_Xn; |
| 404 | |
| 405 | $final_y = $balances.y; |
| 406 | $final_x = $balances.x; |
| 407 | |
| 408 | $p = $alpha/$beta * ($y + $y0) / ($x + $x0); |
| 409 | $P = $inverted ? 1/$p : $p; |
| 410 | $final_p = $alpha/$beta * ($final_y + $y0) / ($final_x + $x0); |
| 411 | $final_P = $inverted ? 1/$final_p : $final_p; |
| 412 | require($final_P > $P, "price should have risen but hasn't, old " || $P || ", new " || $final_P); |
| 413 | |
| 414 | |
| 415 | $totals = $update_leveraged_balances($l_balances, $P, $final_P, $inverted); |
| 416 | |
| 417 | $amount_X_exact = -($final_Xn - $Xn + $totals.delta_XL); |
| 418 | $amount_Y_exact = $final_Yn - $Yn + $totals.delta_YL; |
| 419 | $amount_Y = ceil($amount_Y_exact); |
| 420 | if ($received_amount_Y >= 0) |
| 421 | require($received_amount_Y >= $amount_Y, "expected " || $amount_Y || ", received " || $received_amount_Y); |
| 422 | require($amount_X_exact >= 0, "to pay " || $amount_X_exact); |
| 423 | $change = $received_amount_Y - $amount_Y; |
| 424 | |
| 425 | $denom = 1 - $totals.XL_denom/$b/($final_X+$X0) - $totals.YL_denom/$a/($final_Y+$Y0); |
| 426 | |
| 427 | require($denom >= $singularity_threshold, "too close to the singularity point, denom="||$denom||", need more liquidity in order to swap this amount"); |
| 428 | |
| 429 | |
| 430 | if ($recent.last_trade AND $recent.last_trade.address == $trigger_initial_address AND $recent.last_ts >= timestamp - $trade_merge_period){ |
| 431 | $min_P = min($P, $y_in ? $recent.last_trade.pmin : 1/$recent.last_trade.pmax); |
| 432 | $max_P = max($final_P, $y_in ? $recent.last_trade.pmax : 1/$recent.last_trade.pmin); |
| 433 | $recent_traded_amount = $recent.last_trade.amounts[$out_token]; |
| 434 | $recent_paid_tax = $recent.last_trade.paid_taxes[$out_token]; |
| 435 | } |
| 436 | else{ |
| 437 | $min_P = $P; |
| 438 | $max_P = $final_P; |
| 439 | } |
| 440 | $arb_profit_in_Y = ($max_P - $min_P) * ($recent_traded_amount + $amount_X_exact) / 2; |
| 441 | $arb_profit_in_X = $arb_profit_in_Y / $min_P; |
| 442 | $arb_profit_tax = $arb_profit_in_X * $pool_props.arb_profit_tax - $recent_paid_tax; |
| 443 | require($arb_profit_tax >= 0, "negative arb profit tax"); |
| 444 | |
| 445 | $swap_fee = $amount_X_exact * $pool_props.swap_fee; |
| 446 | $fee = $arb_profit_tax + $swap_fee; |
| 447 | |
| 448 | $net_amount_X_exact = $amount_X_exact - $fee; |
| 449 | $net_amount_X = floor($net_amount_X_exact); |
| 450 | $rounding_fee_X = $net_amount_X_exact - $net_amount_X; |
| 451 | $rounding_fee_Y = $amount_Y - $amount_Y_exact; |
| 452 | $total_fee = $fee + $rounding_fee_X; |
| 453 | |
| 454 | $avg_price = $amount_Y / $net_amount_X; |
| 455 | require($avg_price > $P, "avg price "||$avg_price||" below initial price "||$P); |
| 456 | |
| 457 | if ($min_amount_out) |
| 458 | require($net_amount_X >= $min_amount_out, "output amount " || $net_amount_X || " would be less than the expected minimum " || $min_amount_out); |
| 459 | |
| 460 | |
| 461 | $fees = { |
| 462 | X: $total_fee, |
| 463 | Y: $rounding_fee_Y, |
| 464 | }; |
| 465 | |
| 466 | |
| 467 | $add_net_balance_without_changing_price($balances, $profits, $out_token, $fees.X, $Lambda); |
| 468 | $add_net_balance_without_changing_price($balances, $profits, $in_token, $fees.Y, $Lambda); |
| 469 | |
| 470 | |
| 471 | $update_recent_data($recent, $p, $final_p, $trigger_initial_address, $out_token, $amount_X_exact, $arb_profit_tax, $pool_props.period_length); |
| 472 | |
| 473 | $event = json_stringify({ |
| 474 | type: 'swap', |
| 475 | direction: $y_in ? 'y2x' : 'x2y', |
| 476 | in: $amount_Y, |
| 477 | out: $net_amount_X, |
| 478 | swap_fee: $swap_fee, |
| 479 | arb_profit_tax: $arb_profit_tax, |
| 480 | total_fee: $total_fee, |
| 481 | }); |
| 482 | |
| 483 | { |
| 484 | net_amount_X: $net_amount_X, |
| 485 | amount_Y: $amount_Y, |
| 486 | swap_fee: $swap_fee, |
| 487 | arb_profit_tax: $arb_profit_tax, |
| 488 | total_fee: $total_fee, |
| 489 | fees: $fees, |
| 490 | change: $change, |
| 491 | initial_price: $P, |
| 492 | final_price: $final_P, |
| 493 | event: $event, |
| 494 | } |
| 495 | }; |
| 496 | |
| 497 | |
| 498 | |
| 499 | $buy_shares = ($s, $balances, $profits, $recent, $x0, $y0, $received_amount_x, $received_amount_y, $pool_props) => { |
| 500 | |
| 501 | $Lambda = $pool_props.Lambda; |
| 502 | $alpha = $pool_props.alpha; |
| 503 | $beta = $pool_props.beta; |
| 504 | |
| 505 | |
| 506 | $get_shares = ($x_balance, $y_balance) => round(($x_balance/$y_balance)^$alpha * $y_balance); |
| 507 | |
| 508 | $xn = $balances.xn; |
| 509 | $yn = $balances.yn; |
| 510 | $x = $balances.x; |
| 511 | $y = $balances.y; |
| 512 | |
| 513 | $recent.last_ts = timestamp; |
| 514 | |
| 515 | if (!$s){ |
| 516 | require($received_amount_x > 0 AND $received_amount_y > 0, "send both assets for the first issue"); |
| 517 | $mid_price = $pool_props.mid_price; |
| 518 | if ($mid_price){ |
| 519 | |
| 520 | require($received_amount_y == round($mid_price * $received_amount_x), "first issue must be at mid price "||$mid_price); |
| 521 | $gamma = $pool_props.gamma; |
| 522 | $shares_amount = round($received_amount_x * $pool_props.mid_price_beta * $gamma / ($gamma - 1)); |
| 523 | } |
| 524 | else{ |
| 525 | |
| 526 | $shares_amount = $get_shares($received_amount_x, $received_amount_y); |
| 527 | } |
| 528 | $balances.xn = $balances.xn + $received_amount_x; |
| 529 | $balances.yn = $balances.yn + $received_amount_y; |
| 530 | $balances.x = $balances.x + $received_amount_x * $Lambda; |
| 531 | $balances.y = $balances.y + $received_amount_y * $Lambda; |
| 532 | |
| 533 | $event = json_stringify({ |
| 534 | type: 'add', |
| 535 | x: $received_amount_x, |
| 536 | y: $received_amount_y, |
| 537 | shares: $shares_amount, |
| 538 | }); |
| 539 | |
| 540 | return { |
| 541 | shares_amount: $shares_amount, |
| 542 | coef: 1, |
| 543 | change_x: 0, |
| 544 | change_y: 0, |
| 545 | event: $event, |
| 546 | }; |
| 547 | } |
| 548 | |
| 549 | $p = $alpha/$beta * ($y + $y0) / ($x + $x0); |
| 550 | if ($Lambda > 1 AND $recent.prev){ |
| 551 | $target_xn = $x/$Lambda; |
| 552 | if ($xn > ceil($target_xn)){ |
| 553 | |
| 554 | $share_price_in_y = ($yn + max($recent.current.pmax, $recent.prev.pmax) * $xn) / $s; |
| 555 | $max_delta_yn = ($xn/$target_xn-1)*$yn; |
| 556 | $delta_yn1 = min($max_delta_yn, $received_amount_y); |
| 557 | $delta_y1 = $delta_yn1 * $Lambda; |
| 558 | $delta_x1 = $x * $delta_yn1/$yn; |
| 559 | $shares1 = $delta_yn1/$share_price_in_y; |
| 560 | } |
| 561 | else{ |
| 562 | $target_yn = $y/$Lambda; |
| 563 | if ($yn > ceil($target_yn)){ |
| 564 | |
| 565 | $share_price_in_x = ($xn + 1/min($recent.current.pmin, $recent.prev.pmin) * $yn) / $s; |
| 566 | $max_delta_xn = ($yn/$target_yn-1)*$xn; |
| 567 | $delta_xn1 = min($max_delta_xn, $received_amount_x); |
| 568 | $delta_x1 = $delta_xn1 * $Lambda; |
| 569 | $delta_y1 = $y * $delta_xn1/$xn; |
| 570 | $shares1 = $delta_xn1/$share_price_in_x; |
| 571 | |
| 572 | } |
| 573 | } |
| 574 | $balances.xn = $balances.xn + $delta_xn1; |
| 575 | $balances.yn = $balances.yn + $delta_yn1; |
| 576 | $balances.x = $balances.x + $delta_x1; |
| 577 | $balances.y = $balances.y + $delta_y1; |
| 578 | } |
| 579 | else{ |
| 580 | $delta_xn1 = 0; |
| 581 | $delta_yn1 = 0; |
| 582 | $shares1 = 0; |
| 583 | } |
| 584 | $remaining = { |
| 585 | x: $received_amount_x - $delta_xn1, |
| 586 | y: $received_amount_y - $delta_yn1, |
| 587 | }; |
| 588 | |
| 589 | $y_to_x = ($yn+$delta_yn1)/($xn+$delta_xn1); |
| 590 | |
| 591 | |
| 592 | if ($profits.x OR $profits.y){ |
| 593 | require($Lambda == 1, "have profits while Lambda is " || $Lambda); |
| 594 | require($profits.x >= 0 AND $profits.y >= 0, "negative profits?"); |
| 595 | |
| 596 | |
| 597 | $share_price_in_y = ($yn + $p * $xn) / $s; |
| 598 | $share_price_in_x = ($xn + 1/$p * $yn) / $s; |
| 599 | |
| 600 | |
| 601 | $profits_proportional_y = $y_to_x * $profits.x; |
| 602 | if ($profits.y > $profits_proportional_y){ |
| 603 | $delta_profit_x = $profits.x; |
| 604 | $delta_profit_y = $profits_proportional_y; |
| 605 | $symmetric_moved_profit_shares = $delta_profit_x/$xn * $s; |
| 606 | } |
| 607 | else{ |
| 608 | $profits_proportional_x = $profits.y / $y_to_x; |
| 609 | require($profits_proportional_x <= $profits.x, "profits x " || $profits.x || ", proportional " || $profits_proportional_x); |
| 610 | $delta_profit_x = $profits_proportional_x; |
| 611 | $delta_profit_y = $profits.y; |
| 612 | $symmetric_moved_profit_shares = $delta_profit_y/$yn * $s; |
| 613 | } |
| 614 | $profits.x = $profits.x - $delta_profit_x; |
| 615 | $profits.y = $profits.y - $delta_profit_y; |
| 616 | $balances.xn = $balances.xn + $delta_profit_x; |
| 617 | $balances.yn = $balances.yn + $delta_profit_y; |
| 618 | $balances.x = $balances.x + $delta_profit_x; |
| 619 | $balances.y = $balances.y + $delta_profit_y; |
| 620 | |
| 621 | |
| 622 | |
| 623 | $moved_profit_x = min($profits.x, $remaining.y / $y_to_x); |
| 624 | $moved_profit_y = min($profits.y, $remaining.x * $y_to_x); |
| 625 | |
| 626 | $moved_profit_shares = $moved_profit_x/$share_price_in_x + $moved_profit_y/$share_price_in_y + $symmetric_moved_profit_shares; |
| 627 | |
| 628 | |
| 629 | $profits.x = $profits.x - $moved_profit_x; |
| 630 | $profits.y = $profits.y - $moved_profit_y; |
| 631 | |
| 632 | $remaining.x = $remaining.x + $moved_profit_x; |
| 633 | $remaining.y = $remaining.y + $moved_profit_y; |
| 634 | } |
| 635 | |
| 636 | |
| 637 | |
| 638 | $remaining_proportional_y = $y_to_x * $remaining.x; |
| 639 | if ($remaining.y > $remaining_proportional_y){ |
| 640 | $proportional_delta_xn = $remaining.x; |
| 641 | $proportional_delta_yn = $remaining_proportional_y; |
| 642 | $exact_change_x = 0; |
| 643 | $exact_change_y = $remaining.y - $remaining_proportional_y; |
| 644 | |
| 645 | $shares_proportional = $remaining.x / ($xn + $delta_xn1) * ($s + $shares1); |
| 646 | } |
| 647 | else{ |
| 648 | $remaining_proportional_x = $remaining.y / $y_to_x; |
| 649 | $proportional_delta_xn = $remaining_proportional_x; |
| 650 | $proportional_delta_yn = $remaining.y; |
| 651 | $exact_change_x = $remaining.x - $remaining_proportional_x; |
| 652 | |
| 653 | require($exact_change_x >= 0, "received x " || $remaining.x || ", proportional " || $remaining_proportional_x); |
| 654 | $exact_change_y = 0; |
| 655 | $shares_proportional = $remaining.y / ($yn + $delta_yn1) * ($s + $shares1); |
| 656 | } |
| 657 | |
| 658 | $gross_shares_amount = $shares1 + $symmetric_moved_profit_shares + $shares_proportional; |
| 659 | $shares_amount = floor($gross_shares_amount - $moved_profit_shares); |
| 660 | $coef = $Lambda == 1 ? ($s + $gross_shares_amount) / ($s + $shares_amount) : 1; |
| 661 | |
| 662 | |
| 663 | $balances.xn = $balances.xn + $proportional_delta_xn; |
| 664 | $balances.yn = $balances.yn + $proportional_delta_yn; |
| 665 | $balances.x = $balances.x + $proportional_delta_xn * $Lambda; |
| 666 | $balances.y = $balances.y + $proportional_delta_yn * $Lambda; |
| 667 | |
| 668 | $change_x = floor($exact_change_x); |
| 669 | $change_y = floor($exact_change_y); |
| 670 | $rounding_fee_x = $exact_change_x - $change_x; |
| 671 | $rounding_fee_y = $exact_change_y - $change_y; |
| 672 | |
| 673 | $add_net_balance_without_changing_price($balances, $profits, 'x', $rounding_fee_x, $Lambda); |
| 674 | $add_net_balance_without_changing_price($balances, $profits, 'y', $rounding_fee_y, $Lambda); |
| 675 | |
| 676 | $event = json_stringify({ |
| 677 | type: 'add', |
| 678 | x: $received_amount_x - $change_x, |
| 679 | y: $received_amount_y - $change_y, |
| 680 | shares: $shares_amount, |
| 681 | }); |
| 682 | |
| 683 | { |
| 684 | shares_amount: $shares_amount, |
| 685 | coef: $coef, |
| 686 | change_x: $change_x, |
| 687 | change_y: $change_y, |
| 688 | event: $event, |
| 689 | } |
| 690 | |
| 691 | }; |
| 692 | |
| 693 | |
| 694 | $redeem_shares = ($s, $balances, $l_balances, $profits, $recent, $x0, $y0, $received_shares_amount, $asset, $pool_props) => { |
| 695 | $xn = $balances.xn; |
| 696 | $yn = $balances.yn; |
| 697 | $x = $balances.x; |
| 698 | $y = $balances.y; |
| 699 | $exit_fee = ($received_shares_amount < $s) ? $pool_props.exit_fee : 0; |
| 700 | $net_of_exit_fee = 1 - $exit_fee; |
| 701 | $x_asset = $pool_props.x_asset; |
| 702 | $y_asset = $pool_props.y_asset; |
| 703 | $Lambda = $pool_props.Lambda; |
| 704 | $alpha = $pool_props.alpha; |
| 705 | if ($asset){ |
| 706 | require($asset == $x_asset OR $asset == $y_asset, "wrong preferred asset"); |
| 707 | require($Lambda > 1, "only proportional withdrawals allowed"); |
| 708 | $asset_label = $asset == $x_asset ? 'x' : 'y'; |
| 709 | $net_balance = $asset == $x_asset ? $xn : $yn; |
| 710 | $effective_balance = $asset == $x_asset ? $x : $y; |
| 711 | $target_net_balance = $effective_balance / $Lambda; |
| 712 | |
| 713 | |
| 714 | $excess_net_balance = max($net_balance - $target_net_balance, 0); |
| 715 | require($recent.prev, "too early, prev price not known yet"); |
| 716 | |
| 717 | $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; |
| 718 | $max_asset = $received_shares_amount * $share_price_in_asset; |
| 719 | $one_sided_amount = min($max_asset, $excess_net_balance); |
| 720 | $one_sided_fee = $one_sided_amount / $net_of_exit_fee * $exit_fee; |
| 721 | if ($asset_label == 'y'){ |
| 722 | $yn_amount1 = $one_sided_amount; |
| 723 | $xn_amount1 = 0; |
| 724 | $one_sided_y_fee = $one_sided_fee; |
| 725 | } |
| 726 | else{ |
| 727 | $xn_amount1 = $one_sided_amount; |
| 728 | $yn_amount1 = 0; |
| 729 | $one_sided_x_fee = $one_sided_fee; |
| 730 | } |
| 731 | $remaining_received_shares = max($received_shares_amount - ($one_sided_amount / $share_price_in_asset), 0); |
| 732 | } |
| 733 | else{ |
| 734 | $remaining_received_shares = $received_shares_amount; |
| 735 | $xn_amount1 = 0; |
| 736 | $yn_amount1 = 0; |
| 737 | } |
| 738 | $share_of_shares = $remaining_received_shares / $s; |
| 739 | $remaining_share_of_shares = 1 - $share_of_shares; |
| 740 | $remaining_share_of_assets = $remaining_share_of_shares; |
| 741 | $share_of_assets = 1 - $remaining_share_of_assets; |
| 742 | $x_amount = $share_of_assets * $x * $net_of_exit_fee; |
| 743 | $y_amount = $share_of_assets * $y * $net_of_exit_fee; |
| 744 | $xn_amount = $share_of_assets * ($xn - $xn_amount1) * $net_of_exit_fee + $xn_amount1; |
| 745 | $yn_amount = $share_of_assets * ($yn - $yn_amount1) * $net_of_exit_fee + $yn_amount1; |
| 746 | |
| 747 | |
| 748 | |
| 749 | |
| 750 | |
| 751 | $balances.x = $balances.x - $x_amount; |
| 752 | $balances.y = $balances.y - $y_amount; |
| 753 | $balances.xn = $balances.xn - $xn_amount; |
| 754 | $balances.yn = $balances.yn - $yn_amount; |
| 755 | |
| 756 | |
| 757 | $coef = $Lambda == 1 AND $received_shares_amount < $s ? ($s - $received_shares_amount * $net_of_exit_fee) / ($s - $received_shares_amount) : 1; |
| 758 | |
| 759 | $new_x0 = $x0 * ($s-$received_shares_amount)/$s; |
| 760 | $new_y0 = $y0 * ($s-$received_shares_amount)/$s; |
| 761 | $denom = 1 - $get_utilization_ratio($balances, $l_balances, $new_x0, $new_y0, $alpha); |
| 762 | require($denom >= $singularity_threshold, "redemption amount too large, it would bring us too close to the singularity point, denom="||$denom); |
| 763 | |
| 764 | $int_xn_amount = floor($xn_amount); |
| 765 | $int_yn_amount = floor($yn_amount); |
| 766 | $rounding_fee_x = $xn_amount - $int_xn_amount; |
| 767 | $rounding_fee_y = $yn_amount - $int_yn_amount; |
| 768 | |
| 769 | $add_net_balance_without_changing_price($balances, $profits, 'x', $rounding_fee_x, $Lambda); |
| 770 | $add_net_balance_without_changing_price($balances, $profits, 'y', $rounding_fee_y, $Lambda); |
| 771 | |
| 772 | |
| 773 | $recent.last_ts = timestamp; |
| 774 | |
| 775 | $event = json_stringify({ |
| 776 | type: 'remove', |
| 777 | shares: $received_shares_amount, |
| 778 | x: $int_xn_amount, |
| 779 | y: $int_yn_amount, |
| 780 | x_fee: $share_of_assets * ($xn - $xn_amount1) * $exit_fee + $one_sided_x_fee, |
| 781 | y_fee: $share_of_assets * ($yn - $yn_amount1) * $exit_fee + $one_sided_y_fee, |
| 782 | }); |
| 783 | |
| 784 | { |
| 785 | xn_amount: $int_xn_amount, |
| 786 | yn_amount: $int_yn_amount, |
| 787 | coef: $coef, |
| 788 | event: $event, |
| 789 | } |
| 790 | }; |
| 791 | |
| 792 | |
| 793 | $update_other_l_balances_and_get_sums = ($l_balances, $P, $final_P, $Leverage, $inverted) => { |
| 794 | $ratio = $final_P/$P; |
| 795 | $ratio_powers = $precompute($ratio); |
| 796 | $sums = { |
| 797 | initial: 0, |
| 798 | final: 0, |
| 799 | delta_XL: 0, |
| 800 | XL_denom: 0, |
| 801 | YL_denom: 0, |
| 802 | PL1_ratio: $pow($ratio_powers, $Leverage) / $ratio, |
| 803 | }; |
| 804 | foreach($get_leverages(), 6, $L => { |
| 805 | $allyL = $inverted ? -$L : $L; |
| 806 | $balance = $l_balances[$allyL||'x'].balance; |
| 807 | $obalance = $l_balances[-$allyL||'x'].balance; |
| 808 | if (!$balance AND !$obalance) |
| 809 | return; |
| 810 | $ratio_L1 = $pow($ratio_powers, $L) / $ratio; |
| 811 | if ($balance){ |
| 812 | $sums.initial = $sums.initial + $balance * ($L-1)/$L; |
| 813 | if ($L != $Leverage){ |
| 814 | $new_balance = $balance * $ratio_L1; |
| 815 | $l_balances[$allyL||'x'].balance = $new_balance; |
| 816 | $sums.final = $sums.final + $new_balance * ($L-1)/$L; |
| 817 | $sums.XL_denom = $sums.XL_denom + $new_balance * ($L-1); |
| 818 | $sums.delta_XL = $sums.delta_XL + $new_balance - $balance; |
| 819 | } |
| 820 | } |
| 821 | if ($obalance){ |
| 822 | $sums.initial = $sums.initial - $obalance/$P; |
| 823 | $new_obalance = $obalance / $ratio_L1; |
| 824 | $l_balances[-$allyL||'x'].balance = $new_obalance; |
| 825 | $sums.final = $sums.final - $new_obalance/$final_P; |
| 826 | $sums.YL_denom = $sums.YL_denom + $new_obalance * ($L-1); |
| 827 | $sums.delta_XL = $sums.delta_XL - ($new_obalance / $final_P - $obalance / $P) * ($L-1)/$L; |
| 828 | } |
| 829 | }); |
| 830 | $sums |
| 831 | }; |
| 832 | |
| 833 | |
| 834 | |
| 835 | $move_unleveraged = ($pool, $l_balances, $X0, $Y0, $dXn, $Leverage, $pool_props, $inverted) => { |
| 836 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 837 | $b = 1 - $a; |
| 838 | $L_key = ($inverted ? -$Leverage : $Leverage) || 'x'; |
| 839 | $l_bal_direction = $dXn < 0 ? "grow" : "fall"; |
| 840 | |
| 841 | $Xt = $pool.X + $X0; |
| 842 | $P = $a/$b * ($pool.Y + $Y0) / $Xt; |
| 843 | $final_Xn = $pool.Xn + $dXn; |
| 844 | require($final_Xn > 0, "unleveraged: final_Xn=" || $final_Xn); |
| 845 | $final_X = $final_Xn; |
| 846 | $final_Xt = $final_X + $X0; |
| 847 | $final_Y = $get_final_y($pool.X, $pool.Y, $final_X, $X0, $Y0, $pool_props, $inverted); |
| 848 | $delta_Y = $final_Y - $pool.Y; |
| 849 | $delta_Yn = $delta_Y; |
| 850 | $final_Yn = $pool.Yn + $delta_Yn; |
| 851 | require($final_Yn > 0, "unleveraged: final_Yn=" || $final_Yn); |
| 852 | $final_P = $a/$b * ($final_Y + $Y0) / $final_Xt; |
| 853 | |
| 854 | |
| 855 | $sums = $update_other_l_balances_and_get_sums($l_balances, $P, $final_P, $Leverage, $inverted); |
| 856 | |
| 857 | |
| 858 | |
| 859 | |
| 860 | $b1 = $sums.initial + $b/($b-1)*$Xt; |
| 861 | $new_l_balance = $Leverage/($Leverage-1) * ( -$sums.final - $b/($b-1)*$final_Xt + $b1 * ($final_Xt/$Xt)^(1/$b) ); |
| 862 | |
| 863 | require($new_l_balance >= 0, "unleveraged: new_l_balance=" || $new_l_balance); |
| 864 | $delta_l_balance = $new_l_balance - $l_balances[$L_key].balance; |
| 865 | |
| 866 | require($delta_l_balance * $dXn < 0, "unleveraged l-bal should "||$l_bal_direction||", got new " || $new_l_balance || ", delta " || $delta_l_balance); |
| 867 | $l_balances[$L_key].balance = $new_l_balance; |
| 868 | |
| 869 | |
| 870 | $pool.X = $final_X; |
| 871 | $pool.Y = $final_Y; |
| 872 | $pool.Xn = $final_Xn; |
| 873 | $pool.Yn = $final_Yn; |
| 874 | $pool.delta_XL = $pool.delta_XL + $sums.delta_XL; |
| 875 | $pool.XL_denom = $sums.XL_denom + $new_l_balance * ($Leverage-1); |
| 876 | $pool.YL_denom = $sums.YL_denom; |
| 877 | $pool.PL1_ratio = $pool.PL1_ratio * $sums.PL1_ratio; |
| 878 | }; |
| 879 | |
| 880 | |
| 881 | $move_along_X = ($pool, $l_balances, $dXn, $Leverage, $pool_props, $inverted) => { |
| 882 | $Lambda = $pool_props.Lambda; |
| 883 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 884 | $b = 1 - $a; |
| 885 | $L_key = ($inverted ? -$Leverage : $Leverage) || 'x'; |
| 886 | $l_bal_direction = $dXn < 0 ? "grow" : "fall"; |
| 887 | |
| 888 | $P = $a/$b * $pool.Y / $pool.X; |
| 889 | $final_Xn = $pool.Xn + $dXn; |
| 890 | require($final_Xn > 0, "along X: final_Xn=" || $final_Xn); |
| 891 | $final_X = $Lambda * $final_Xn; |
| 892 | $final_Y = $get_final_y_along_x($pool.X, $pool.Y, $final_X, $pool_props, $inverted); |
| 893 | $delta_Y = $final_Y - $pool.Y; |
| 894 | $delta_Yn = -$a/($b*$Lambda-1)*$delta_Y; |
| 895 | $final_Yn = $pool.Yn + $delta_Yn; |
| 896 | require($final_Yn > 0, "along X: final_Yn=" || $final_Yn); |
| 897 | $final_P = $a/$b * $final_Y / $final_X; |
| 898 | |
| 899 | $sums = $update_other_l_balances_and_get_sums($l_balances, $P, $final_P, $Leverage, $inverted); |
| 900 | |
| 901 | |
| 902 | $b1 = $sums.initial + $b/($b*$Lambda-1)*$pool.X; |
| 903 | $new_l_balance = $Leverage/($Leverage-1) * ( -$sums.final - $b/($b*$Lambda-1)*$final_X + $b1 * ($final_X/$pool.X)^(1/$b/$Lambda) ); |
| 904 | require($new_l_balance >= 0, "along X: new_l_balance=" || $new_l_balance); |
| 905 | $delta_l_balance = $new_l_balance - $l_balances[$L_key].balance; |
| 906 | require($delta_l_balance * $dXn < 0, "along x l-bal should "||$l_bal_direction||", got new " || $new_l_balance || ", delta " || $delta_l_balance); |
| 907 | $l_balances[$L_key].balance = $new_l_balance; |
| 908 | |
| 909 | $pool.X = $final_X; |
| 910 | $pool.Y = $final_Y; |
| 911 | $pool.Xn = $final_Xn; |
| 912 | $pool.Yn = $final_Yn; |
| 913 | $pool.delta_XL = $pool.delta_XL + $sums.delta_XL; |
| 914 | $pool.XL_denom = $sums.XL_denom + $new_l_balance * ($Leverage-1); |
| 915 | $pool.YL_denom = $sums.YL_denom; |
| 916 | $pool.PL1_ratio = $pool.PL1_ratio * $sums.PL1_ratio; |
| 917 | }; |
| 918 | |
| 919 | $move_along_Y = ($pool, $l_balances, $dXn, $Leverage, $pool_props, $inverted) => { |
| 920 | $Lambda = $pool_props.Lambda; |
| 921 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 922 | $b = 1 - $a; |
| 923 | $L_key = ($inverted ? -$Leverage : $Leverage) || 'x'; |
| 924 | $l_bal_direction = $dXn < 0 ? "grow" : "fall"; |
| 925 | |
| 926 | $P = $a/$b * $pool.Y / $pool.X; |
| 927 | $final_Xn = $pool.Xn + $dXn; |
| 928 | require($final_Xn > 0, "along Y: final_Xn=" || $final_Xn); |
| 929 | $delta_X = -($a*$Lambda-1)/$b * $dXn; |
| 930 | $final_X = $pool.X + $delta_X; |
| 931 | require($final_X > 0, "along Y: final_X=" || $final_X); |
| 932 | $final_Y = $get_final_y_along_y($pool.X, $pool.Y, $final_X, $pool_props, $inverted); |
| 933 | $final_Yn = $final_Y/$Lambda; |
| 934 | $final_P = $a/$b * $final_Y / $final_X; |
| 935 | |
| 936 | $sums = $update_other_l_balances_and_get_sums($l_balances, $P, $final_P, $Leverage, $inverted); |
| 937 | |
| 938 | |
| 939 | $b2 = $sums.initial - $b/$a/$Lambda*$pool.X; |
| 940 | $new_l_balance = $Leverage/($Leverage-1) * ( -$sums.final + $b/$a/$Lambda*$final_X + $b2 * ($final_X/$pool.X)^(-1/($a*$Lambda-1)) ); |
| 941 | require($new_l_balance >= 0, "along Y: new_l_balance=" || $new_l_balance); |
| 942 | $delta_l_balance = $new_l_balance - $l_balances[$L_key].balance; |
| 943 | require($delta_l_balance * $dXn < 0, "along y l-bal should "||$l_bal_direction||", got new " || $new_l_balance || ", delta " || $delta_l_balance); |
| 944 | $l_balances[$L_key].balance = $new_l_balance; |
| 945 | |
| 946 | $pool.X = $final_X; |
| 947 | $pool.Y = $final_Y; |
| 948 | $pool.Xn = $final_Xn; |
| 949 | $pool.Yn = $final_Yn; |
| 950 | $pool.delta_XL = $pool.delta_XL + $sums.delta_XL; |
| 951 | $pool.XL_denom = $sums.XL_denom + $new_l_balance * ($Leverage-1); |
| 952 | $pool.YL_denom = $sums.YL_denom; |
| 953 | $pool.PL1_ratio = $pool.PL1_ratio * $sums.PL1_ratio; |
| 954 | }; |
| 955 | |
| 956 | |
| 957 | |
| 958 | |
| 959 | |
| 960 | $trade_l_shares = ($balances, $l_balances, $profits, $recent, $x0, $y0, $Leverage, $asset, $delta_Xn, $entry_price, $trigger_initial_address, $pool_props) => { |
| 961 | |
| 962 | require($asset == $pool_props.x_asset OR $asset == $pool_props.y_asset, "wrong asset"); |
| 963 | require($Leverage == 2 OR $Leverage == 5 OR $Leverage == 10 OR $Leverage == 20 OR $Leverage == 50 OR $Leverage == 100, "bad L"); |
| 964 | $Lambda = $pool_props.Lambda; |
| 965 | |
| 966 | if ($asset == $pool_props.x_asset){ |
| 967 | $inverted = false; |
| 968 | $X = $balances.x; |
| 969 | $Y = $balances.y; |
| 970 | $Xn = $balances.xn; |
| 971 | $Yn = $balances.yn; |
| 972 | $X0 = $x0; |
| 973 | $Y0 = $y0; |
| 974 | $a = $pool_props.alpha; |
| 975 | $b = $pool_props.beta; |
| 976 | $token = 'x'; |
| 977 | } |
| 978 | else{ |
| 979 | $inverted = true; |
| 980 | $X = $balances.y; |
| 981 | $Y = $balances.x; |
| 982 | $Xn = $balances.yn; |
| 983 | $Yn = $balances.xn; |
| 984 | $X0 = $y0; |
| 985 | $Y0 = $x0; |
| 986 | $a = $pool_props.beta; |
| 987 | $b = $pool_props.alpha; |
| 988 | $token = 'y'; |
| 989 | } |
| 990 | $direct = !$inverted; |
| 991 | require($Xn + $delta_Xn > 0, "Xn balance would be negative"); |
| 992 | $L_key = ($inverted ? -$Leverage : $Leverage) || 'x'; |
| 993 | $pool = {X: $X, Y: $Y, Xn: $Xn, Yn: $Yn, delta_XL: 0, PL1_ratio: 1}; |
| 994 | |
| 995 | |
| 996 | $initial_l_balance = $l_balances[$L_key].balance; |
| 997 | $initial_shares = $l_balances[$L_key].supply; |
| 998 | |
| 999 | |
| 1000 | $initial_P = $a/$b * ($pool.Y + $Y0) / ($pool.X + $X0); |
| 1001 | |
| 1002 | if ($Lambda == 1) |
| 1003 | this_address#3.$move_unleveraged($pool, $l_balances, $X0, $Y0, $delta_Xn, $Leverage, $pool_props, $inverted); |
| 1004 | else { |
| 1005 | $underleveraged = $Xn > ceil($X/$Lambda); |
| 1006 | if (!$underleveraged){ |
| 1007 | if ($delta_Xn > 0){ |
| 1008 | $delta_Xn_inflection = $X * (( $Lambda/($Lambda-1) * ($a + ($b * $Lambda - 1) * $Yn/$Y) )^($b * $Lambda/($b*$Lambda-1)) - 1) / $Lambda; |
| 1009 | $inflected = $delta_Xn > $delta_Xn_inflection; |
| 1010 | } |
| 1011 | $dXn1 = $inflected ? $delta_Xn_inflection : $delta_Xn; |
| 1012 | this_address#3.$move_along_X($pool, $l_balances, $dXn1, $Leverage, $pool_props, $inverted); |
| 1013 | if ($inflected) |
| 1014 | this_address#3.$move_along_Y($pool, $l_balances, $delta_Xn - $delta_Xn_inflection, $Leverage, $pool_props, $inverted); |
| 1015 | } |
| 1016 | else{ |
| 1017 | if ($delta_Xn < 0){ |
| 1018 | $delta_Xn_inflection = -$b/($Lambda-1) * ($Lambda*$Xn - $X); |
| 1019 | $inflected = abs($delta_Xn) > abs($delta_Xn_inflection); |
| 1020 | } |
| 1021 | $dXn1 = $inflected ? $delta_Xn_inflection : $delta_Xn; |
| 1022 | this_address#3.$move_along_Y($pool, $l_balances, $dXn1, $Leverage, $pool_props, $inverted); |
| 1023 | if ($inflected) |
| 1024 | this_address#3.$move_along_X($pool, $l_balances, $delta_Xn - $delta_Xn_inflection, $Leverage, $pool_props, $inverted); |
| 1025 | } |
| 1026 | } |
| 1027 | |
| 1028 | $final_l_balance = $l_balances[$L_key].balance; |
| 1029 | $delta_l_balance = $final_l_balance - $initial_l_balance; |
| 1030 | $final_P = $a/$b * ($pool.Y + $Y0) / ($pool.X + $X0); |
| 1031 | |
| 1032 | |
| 1033 | $net_delta = $delta_Xn + $pool.delta_XL + $delta_l_balance; |
| 1034 | |
| 1035 | $final_shares = $initial_shares |
| 1036 | ? floor($final_l_balance/($initial_l_balance OTHERWISE 0.1*$final_l_balance) / $pool.PL1_ratio * $initial_shares) |
| 1037 | : round($net_delta); |
| 1038 | $shares = $final_shares - $initial_shares; |
| 1039 | $l_balances[$L_key].supply = $final_shares; |
| 1040 | $avg_share_price = $net_delta/$shares; |
| 1041 | |
| 1042 | if ($final_shares == 0){ |
| 1043 | require($final_l_balance >= 0, "negative final l-balance after redeeming all shares: "||$final_l_balance); |
| 1044 | |
| 1045 | |
| 1046 | |
| 1047 | |
| 1048 | } |
| 1049 | |
| 1050 | |
| 1051 | $denom = 1 - $pool.XL_denom/$b/($pool.X+$X0) - $pool.YL_denom/$a/($pool.Y+$Y0); |
| 1052 | |
| 1053 | 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"); |
| 1054 | |
| 1055 | $balances.x = $inverted ? $pool.Y : $pool.X; |
| 1056 | $balances.y = $inverted ? $pool.X : $pool.Y; |
| 1057 | $balances.xn = $inverted ? $pool.Yn : $pool.Xn; |
| 1058 | $balances.yn = $inverted ? $pool.Xn : $pool.Yn; |
| 1059 | |
| 1060 | |
| 1061 | if ($recent.last_trade AND $recent.last_trade.address == $trigger_initial_address AND $recent.last_ts >= timestamp - $trade_merge_period){ |
| 1062 | $min_P = min($initial_P, $final_P, $direct ? $recent.last_trade.pmin : 1/$recent.last_trade.pmax); |
| 1063 | $max_P = max($initial_P, $final_P, $direct ? $recent.last_trade.pmax : 1/$recent.last_trade.pmin); |
| 1064 | $recent_traded_amount = $recent.last_trade.amounts[$token]; |
| 1065 | $recent_paid_tax = $recent.last_trade.paid_taxes[$token]; |
| 1066 | } |
| 1067 | else{ |
| 1068 | $min_P = min($initial_P, $final_P); |
| 1069 | $max_P = max($initial_P, $final_P); |
| 1070 | } |
| 1071 | $arb_profit_in_Y = ($max_P - $min_P) * ($recent_traded_amount + abs($net_delta)) / 2; |
| 1072 | $arb_profit_in_X = $arb_profit_in_Y / $min_P; |
| 1073 | $arb_profit_tax = $arb_profit_in_X * $pool_props.arb_profit_tax - $recent_paid_tax; |
| 1074 | require($arb_profit_tax >= 0, "negative arb profit tax"); |
| 1075 | |
| 1076 | $swap_fee = abs($net_delta) * $pool_props.swap_fee; |
| 1077 | |
| 1078 | if ($delta_Xn > 0){ |
| 1079 | $gross_asset_out = -$net_delta; |
| 1080 | require($gross_asset_out > 0, "asset out must be positive, got " || $gross_asset_out); |
| 1081 | if ($entry_price){ |
| 1082 | |
| 1083 | $l_tax = max(($avg_share_price - $entry_price)*(-$shares)*$pool_props.leverage_profit_tax, 0); |
| 1084 | } |
| 1085 | else |
| 1086 | $l_tax = $gross_asset_out * $pool_props.leverage_token_tax; |
| 1087 | } |
| 1088 | |
| 1089 | |
| 1090 | $subtotal_fee = $arb_profit_tax + $swap_fee + $l_tax; |
| 1091 | $gross_delta_exact = $net_delta + $subtotal_fee; |
| 1092 | $gross_delta = ceil($gross_delta_exact); |
| 1093 | $rounding_fee = $gross_delta - $gross_delta_exact; |
| 1094 | $total_fee = $subtotal_fee + $rounding_fee; |
| 1095 | |
| 1096 | |
| 1097 | $add_net_balance_without_changing_price($balances, $profits, $token, $total_fee, $Lambda); |
| 1098 | |
| 1099 | |
| 1100 | $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); |
| 1101 | |
| 1102 | $event = json_stringify({ |
| 1103 | type: 'leverage', |
| 1104 | token: $token, |
| 1105 | L: $Leverage, |
| 1106 | shares: $shares, |
| 1107 | amount: $gross_delta, |
| 1108 | swap_fee: $swap_fee, |
| 1109 | arb_profit_tax: $arb_profit_tax, |
| 1110 | l_tax: $l_tax, |
| 1111 | total_fee: $total_fee, |
| 1112 | }); |
| 1113 | |
| 1114 | { |
| 1115 | shares: $shares, |
| 1116 | net_delta: $net_delta, |
| 1117 | gross_delta: $gross_delta, |
| 1118 | avg_share_price: $avg_share_price, |
| 1119 | arb_profit_tax: $arb_profit_tax, |
| 1120 | l_tax: $l_tax, |
| 1121 | swap_fee: $swap_fee, |
| 1122 | total_fee: $total_fee, |
| 1123 | initial_price: $initial_P, |
| 1124 | final_price: $final_P, |
| 1125 | event: $event, |
| 1126 | } |
| 1127 | }; |
| 1128 | |
| 1129 | |
| 1130 | $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) => { |
| 1131 | $x_asset = $pool_props.x_asset; |
| 1132 | $y_asset = $pool_props.y_asset; |
| 1133 | |
| 1134 | $asset = $trigger_data.asset == 'x' ? $x_asset : ($trigger_data.asset == 'y' ? $y_asset : $trigger_data.asset); |
| 1135 | $L = $trigger_data.L; |
| 1136 | $buy = $trigger_data.buy; |
| 1137 | $sell = $trigger_data.sell; |
| 1138 | $delta = $trigger_data.delta; |
| 1139 | $received_amount = $trigger_outputs[$asset]; |
| 1140 | $min_received_amount = $asset == 'base' ? 10000 : 0; |
| 1141 | $net_received_amount = $received_amount - $min_received_amount; |
| 1142 | |
| 1143 | require(!($buy AND $sell), "buy or sell?"); |
| 1144 | require($delta > 0, "delta must be positive"); |
| 1145 | if ($buy) |
| 1146 | require($net_received_amount > 0, "you forgot to pay"); |
| 1147 | else |
| 1148 | require($net_received_amount == 0, "don't send asset"); |
| 1149 | $delta_Xn = $buy ? -$delta : $delta; |
| 1150 | $asset_label = $asset == $x_asset ? 'x' : 'y'; |
| 1151 | |
| 1152 | $signedL = $asset_label == 'x' ? $L : -$L; |
| 1153 | if ($buy AND $trigger_data.tokens OR $sell AND !$trigger_data.position){ |
| 1154 | $l_shares_asset = var[$pool_aa]['leveraged_asset' || $signedL]; |
| 1155 | require($l_shares_asset, "please define an asset for the leveraged token first"); |
| 1156 | } |
| 1157 | if ($sell){ |
| 1158 | if ($trigger_data.position){ |
| 1159 | $position = var[$pool_aa][$trigger_data.position]; |
| 1160 | require($position, "no such position"); |
| 1161 | require($position.owner == $trigger_address, "you are not the owner of this position"); |
| 1162 | $parts = split($trigger_data.position, '_'); |
| 1163 | require(+$parts[1] == $signedL, "wrong L"); |
| 1164 | $shares_in = $position.shares; |
| 1165 | } |
| 1166 | else{ |
| 1167 | $shares_in = $trigger_outputs[$l_shares_asset]; |
| 1168 | require($shares_in > 0, "no leveraged tokens received"); |
| 1169 | } |
| 1170 | } |
| 1171 | |
| 1172 | $res = $trade_l_shares($balances, $l_balances, $profits, $recent, $x0, $y0, $L, $asset, $delta_Xn, $position.price, $trigger_initial_address, $pool_props); |
| 1173 | |
| 1174 | |
| 1175 | $shares = $res.shares; |
| 1176 | $gross_delta = $res.gross_delta; |
| 1177 | |
| 1178 | if ($buy){ |
| 1179 | $asset_out = $received_amount - $gross_delta; |
| 1180 | require($asset_out >= 0, "expected " || $gross_delta || ", received " || $received_amount); |
| 1181 | } |
| 1182 | else{ |
| 1183 | $shares_change = $shares_in + $shares; |
| 1184 | require($shares_change >= 0, "expected " || (-$shares) || " shares, received " || $shares_in); |
| 1185 | $asset_out = -$gross_delta; |
| 1186 | } |
| 1187 | |
| 1188 | $res.signedL = $signedL; |
| 1189 | $res.asset_label = $asset_label; |
| 1190 | $res.asset = $asset; |
| 1191 | $res.l_shares_asset = $l_shares_asset; |
| 1192 | $res.position = $position; |
| 1193 | $res.shares_change = $shares_change; |
| 1194 | $res.asset_out = $asset_out; |
| 1195 | $res |
| 1196 | }; |
| 1197 | |
| 1198 | |
| 1199 | |
| 1200 | $validate_and_apply_new_governed_param_value = ($name, $value, $balances, $l_balances, $profits, $lp_shares, $pool_props, $locked_governance) => { |
| 1201 | |
| 1202 | if ($locked_governance) |
| 1203 | require(!$locked_governance[$name], "governance is not allowed to change "||$name); |
| 1204 | |
| 1205 | $Lambda = $pool_props.Lambda; |
| 1206 | $alpha = $pool_props.alpha; |
| 1207 | $beta = $pool_props.beta; |
| 1208 | $gamma = $pool_props.gamma; |
| 1209 | $mid_price = $pool_props.mid_price; |
| 1210 | $mid_price_beta = $pool_props.mid_price_beta; |
| 1211 | |
| 1212 | $s_curve = $lp_shares.linear * $lp_shares.coef; |
| 1213 | $x0 = $mid_price ? $s_curve / $mid_price_beta / $gamma : 0; |
| 1214 | $y0 = $x0 * $mid_price; |
| 1215 | |
| 1216 | if ($name == 'pool_leverage'){ |
| 1217 | require($profits.x < 1 AND $profits.y < 1, "profits must be added to the pool before changing pool_leverage"); |
| 1218 | $profits.x = 0; |
| 1219 | $profits.y = 0; |
| 1220 | require($alpha != 1/$value, "pool leverage = 1/alpha"); |
| 1221 | require($beta != 1/$value, "pool leverage = 1/beta"); |
| 1222 | require($value != $Lambda, "same Lambda"); |
| 1223 | if ($value > 1) |
| 1224 | require(!$mid_price, "price range setting is incompatible with new pool leverage"); |
| 1225 | $balances.x = $balances.x * $value/$Lambda; |
| 1226 | $balances.y = $balances.y * $value/$Lambda; |
| 1227 | if ($value == 1){ |
| 1228 | |
| 1229 | $profits.x = $profits.x + $balances.xn - $balances.x; |
| 1230 | $profits.y = $profits.y + $balances.yn - $balances.y; |
| 1231 | $balances.xn = $balances.x; |
| 1232 | $balances.yn = $balances.y; |
| 1233 | } |
| 1234 | |
| 1235 | } |
| 1236 | else if ($name == 'mid_price' OR $name == 'price_deviation'){ |
| 1237 | require($value AND $mid_price, $name||" must be nonzero"); |
| 1238 | require($alpha == 0.5, "equal weights only"); |
| 1239 | if ($name == 'price_deviation'){ |
| 1240 | require($value > 1, "price deviation must be > 1"); |
| 1241 | $new_p0 = $mid_price; |
| 1242 | $new_gamma = $value; |
| 1243 | } |
| 1244 | else{ |
| 1245 | $new_p0 = $value; |
| 1246 | $new_gamma = $gamma; |
| 1247 | } |
| 1248 | $sqp = $mid_price_beta; |
| 1249 | $new_sqp = sqrt($new_p0); |
| 1250 | $x = $balances.x; |
| 1251 | $y = $balances.y; |
| 1252 | |
| 1253 | |
| 1254 | $new_s1 = 1 / (1/$s_curve + (1/$gamma/$sqp - 1/$new_gamma/$new_sqp) / $x); |
| 1255 | $new_y = $new_s1 * ($y/$s_curve + $sqp/$gamma - $new_sqp/$new_gamma); |
| 1256 | if ($new_y <= $y){ |
| 1257 | require($new_y > 0, "new y is negative"); |
| 1258 | require($new_s1 > 0, "new s1 is negative"); |
| 1259 | $profits.y = $profits.y + $y - $new_y; |
| 1260 | $balances.y = $new_y; |
| 1261 | $balances.yn = $new_y; |
| 1262 | $lp_shares.coef = $lp_shares.coef * $new_s1/$s_curve; |
| 1263 | $new_s = $new_s1; |
| 1264 | } |
| 1265 | else{ |
| 1266 | $new_s2 = 1 / (1/$s_curve + ($sqp/$gamma - $new_sqp/$new_gamma) / $y); |
| 1267 | $new_x = $new_s2 * ($x/$s_curve + 1/$gamma/$sqp - 1/$new_gamma/$new_sqp); |
| 1268 | require($new_x <= $x, "can't adjust x and y to keep the price"); |
| 1269 | require($new_x > 0, "new x is negative"); |
| 1270 | require($new_s2 > 0, "new s2 is negative"); |
| 1271 | $profits.x = $profits.x + $x - $new_x; |
| 1272 | $balances.x = $new_x; |
| 1273 | $balances.xn = $new_x; |
| 1274 | $lp_shares.coef = $lp_shares.coef * $new_s2/$s_curve; |
| 1275 | $new_s = $new_s2; |
| 1276 | } |
| 1277 | $new_x0 = $new_s / $new_sqp / $new_gamma; |
| 1278 | $new_y0 = $new_s * $new_sqp / $new_gamma; |
| 1279 | |
| 1280 | /* |
| 1281 | $sqp = sqrt($new_p0); |
| 1282 | $b = ($balances.x/$s_curve*$sqp + $balances.y/$s_curve/$sqp)/$new_gamma; |
| 1283 | $a = $balances.x*$balances.y/$s_curve/$s_curve; |
| 1284 | $lp_shares.coef = $lp_shares.coef / (-$b + sqrt($b*$b - 4*$a*(1/$new_gamma/$new_gamma-1)))*2*$a; |
| 1285 | */ |
| 1286 | |
| 1287 | |
| 1288 | } |
| 1289 | else if ($name == 'alpha'){ |
| 1290 | require(!$mid_price, "can't change token weights while trading in limited range"); |
| 1291 | $new_alpha = $value; |
| 1292 | $new_beta = 1 - $new_alpha; |
| 1293 | require($new_alpha != 1/$Lambda AND $new_beta != 1/$Lambda, "pool leverage = 1/alpha or 1/beta"); |
| 1294 | |
| 1295 | |
| 1296 | |
| 1297 | |
| 1298 | $new_y2x = $new_beta/$new_alpha * $alpha/$beta * $balances.y/$balances.x; |
| 1299 | if ($Lambda > 1){ |
| 1300 | if ($balances.xn * $Lambda * $new_y2x <= $balances.yn * $Lambda){ |
| 1301 | $balances.x = $balances.xn * $Lambda; |
| 1302 | $balances.y = $balances.x * $new_y2x; |
| 1303 | } |
| 1304 | else if ($balances.yn * $Lambda / $new_y2x <= $balances.xn * $Lambda){ |
| 1305 | $balances.y = $balances.yn * $Lambda; |
| 1306 | $balances.x = $balances.y / $new_y2x; |
| 1307 | } |
| 1308 | else |
| 1309 | bounce("can't preserve the price"); |
| 1310 | } |
| 1311 | else { |
| 1312 | $new_y = $balances.xn * $new_y2x; |
| 1313 | if ($new_y <= $balances.yn){ |
| 1314 | $profits.y = $profits.y + $balances.yn - $new_y; |
| 1315 | $balances.yn = $new_y; |
| 1316 | $balances.y = $new_y; |
| 1317 | } |
| 1318 | else { |
| 1319 | $new_x = $balances.yn / $new_y2x; |
| 1320 | require($new_x <= $balances.xn, "neither excessive x nor excessive y"); |
| 1321 | $profits.x = $profits.x + $balances.xn - $new_x; |
| 1322 | $balances.xn = $new_x; |
| 1323 | $balances.x = $new_x; |
| 1324 | } |
| 1325 | } |
| 1326 | |
| 1327 | } |
| 1328 | |
| 1329 | |
| 1330 | $denom = 1 - $get_utilization_ratio($balances, $l_balances, $new_x0 OTHERWISE $x0, $new_y0 OTHERWISE $y0, $new_alpha OTHERWISE $alpha); |
| 1331 | require($denom >= $singularity_threshold, "new balances would bring us too close to the singularity point, denom="||$denom); |
| 1332 | }; |
| 1333 | |
| 1334 | |
| 1335 | |
| 1336 | }", |
| 1337 | "messages": [ |
| 1338 | { |
| 1339 | "app": "state", |
| 1340 | "state": "{ |
| 1341 | |
| 1342 | |
| 1343 | |
| 1344 | |
| 1345 | |
| 1346 | $h = $handle_trade_l_shares_request(); |
| 1347 | |
| 1348 | |
| 1349 | |
| 1350 | |
| 1351 | bounce("library only"); |
| 1352 | }" |
| 1353 | } |
| 1354 | ] |
| 1355 | } |
| 1356 | ] |