| 1 | [ |
| 2 | "autonomous agent", |
| 3 | { |
| 4 | "getters": "{ |
| 5 | |
| 6 | $get_leverages = () => [2, 5, 10, 20, 50, 100]; |
| 7 | |
| 8 | $singularity_threshold = 0.1; |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | |
| 15 | $get_final_xy = ($X, $Y, $P, $final_P, $X0, $Y0, $pool_props, $inverted) => { |
| 16 | require($final_P >= $P, "not selling Y"); |
| 17 | log('get_final_xy', $X, $Y, $P, $final_P, $X0, $Y0); |
| 18 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 19 | $b = 1 - $a; |
| 20 | $final_X = ($X + $X0) * ($P/$final_P)^$b - $X0; |
| 21 | $final_Y = $b/$a * $final_P * ($final_X + $X0) - $Y0; |
| 22 | $deltaX = $X - $final_X; |
| 23 | require($final_X >= 0, "bad final_X " || $final_X); |
| 24 | require($final_Y >= 0, "bad final_Y " || $final_Y); |
| 25 | require($deltaX >= 0, "bad deltaX " || $deltaX); |
| 26 | { |
| 27 | X: round($final_X), |
| 28 | Y: round($final_Y), |
| 29 | } |
| 30 | }; |
| 31 | |
| 32 | |
| 33 | $get_final_xy_along_x = ($X, $Y, $P, $final_P, $pool_props, $inverted) => { |
| 34 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 35 | $b = 1 - $a; |
| 36 | $final_X = $X * ($P/$final_P)^($b*$pool_props.Lambda); |
| 37 | $final_Y = $b/$a * $final_P * $final_X; |
| 38 | { |
| 39 | X: round($final_X), |
| 40 | Y: round($final_Y), |
| 41 | } |
| 42 | }; |
| 43 | |
| 44 | |
| 45 | $get_final_xy_along_y = ($X, $Y, $P, $final_P, $pool_props, $inverted) => { |
| 46 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 47 | $b = 1 - $a; |
| 48 | $final_Y = $Y * ($final_P/$P)^($a*$pool_props.Lambda); |
| 49 | $final_X = $a/$b * $final_Y / $final_P; |
| 50 | { |
| 51 | X: round($final_X), |
| 52 | Y: round($final_Y), |
| 53 | } |
| 54 | }; |
| 55 | |
| 56 | |
| 57 | |
| 58 | |
| 59 | $add_net_balance_without_changing_price = ($balances, $side, $amount, $Lambda) => { |
| 60 | require($Lambda > 1, "Lambda must be > 1"); |
| 61 | |
| 62 | $opposite = $side == 'x' ? 'y' : 'x'; |
| 63 | $side_n = $side || 'n'; |
| 64 | $opposite_n = $opposite || 'n'; |
| 65 | |
| 66 | $Xn = $balances[$side_n]; |
| 67 | $Yn = $balances[$opposite_n]; |
| 68 | $X = $balances[$side]; |
| 69 | $Y = $balances[$opposite]; |
| 70 | |
| 71 | $underleveraged = $Xn > ceil($X/$Lambda); |
| 72 | $delta_Xn = $amount; |
| 73 | |
| 74 | |
| 75 | if (!$underleveraged){ |
| 76 | |
| 77 | $full_delta_Y = $Y * $delta_Xn/$Xn; |
| 78 | if ($Y + $full_delta_Y > $Yn * $Lambda){ |
| 79 | $ratio = $Yn * $Lambda / $Y - 1; |
| 80 | $delta_X = round($ratio * $X); |
| 81 | $delta_Y = round($ratio * $Y); |
| 82 | } |
| 83 | else{ |
| 84 | $delta_X = round($delta_Xn * $Lambda); |
| 85 | $delta_Y = round($full_delta_Y); |
| 86 | } |
| 87 | } |
| 88 | else{ |
| 89 | $delta_X = 0; |
| 90 | $delta_Y = 0; |
| 91 | } |
| 92 | |
| 93 | $balances[$side_n] = $balances[$side_n] + $delta_Xn; |
| 94 | |
| 95 | $balances[$side] = $balances[$side] + $delta_X; |
| 96 | $balances[$opposite] = $balances[$opposite] + $delta_Y; |
| 97 | }; |
| 98 | |
| 99 | |
| 100 | |
| 101 | |
| 102 | |
| 103 | $pow = ($precomputed, $power) => { |
| 104 | require($precomputed[$power], "no precomputed power " || $power); |
| 105 | $precomputed[$power] |
| 106 | }; |
| 107 | $precompute = $v => { |
| 108 | $pre = {}; |
| 109 | $pre['2'] = $v * $v; |
| 110 | $pre['5'] = $pre['2'] * $pre['2'] * $v; |
| 111 | $pre['10'] = $pre['5'] * $pre['5']; |
| 112 | $pre['20'] = $pre['10'] * $pre['10']; |
| 113 | $pre['50'] = $pre['20'] * $pre['20'] * $pre['10']; |
| 114 | $pre['100'] = $pre['50'] * $pre['50']; |
| 115 | $pre |
| 116 | }; |
| 117 | |
| 118 | $update_leveraged_balances = ($x, $y, $final_x, $final_y, $x0, $y0, $l_balances, $alpha, $inverted) => { |
| 119 | $beta = 1 - $alpha; |
| 120 | |
| 121 | $p = $alpha/$beta * ($y + $y0) / ($x + $x0); |
| 122 | $P = $inverted ? 1/$p : $p; |
| 123 | $final_p = $alpha/$beta * ($final_y + $y0) / ($final_x + $x0); |
| 124 | $final_P = $inverted ? 1/$final_p : $final_p; |
| 125 | |
| 126 | |
| 127 | |
| 128 | |
| 129 | $ratio = $final_P/$P; |
| 130 | $ratio_powers = $precompute($ratio); |
| 131 | |
| 132 | $totals = { |
| 133 | delta_XL: 0, |
| 134 | delta_YL: 0, |
| 135 | XL_denom: 0, |
| 136 | YL_denom: 0, |
| 137 | }; |
| 138 | foreach($get_leverages(), 6, ($L) => { |
| 139 | $allyL = $inverted ? -$L : $L; |
| 140 | $balance = $l_balances[$allyL||'x'].balance; |
| 141 | $obalance = $l_balances[-$allyL||'x'].balance; |
| 142 | if (!$balance AND !$obalance) |
| 143 | return; |
| 144 | |
| 145 | |
| 146 | $ratio_L1 = $pow($ratio_powers, $L) / $ratio; |
| 147 | $debt_ratio = ($L-1)/$L; |
| 148 | if ($balance) { |
| 149 | $delta_XL_balance = round($balance * ($ratio_L1 - 1)); |
| 150 | $new_XL_balance = $balance + $delta_XL_balance; |
| 151 | $l_balances[$allyL||'x'].balance = $new_XL_balance; |
| 152 | $delta_YL_balance = -(($new_XL_balance * $final_P - $balance * $P) * $debt_ratio); |
| 153 | $totals.delta_XL = $totals.delta_XL + $delta_XL_balance; |
| 154 | $totals.delta_YL = $totals.delta_YL + $delta_YL_balance; |
| 155 | $totals.XL_denom = $totals.XL_denom + $new_XL_balance * ($L-1); |
| 156 | } |
| 157 | if ($obalance) { |
| 158 | $delta_YL_obalance = round($obalance * (1/$ratio_L1 - 1)); |
| 159 | $new_YL_obalance = $obalance + $delta_YL_obalance; |
| 160 | $l_balances[-$allyL||'x'].balance = $new_YL_obalance; |
| 161 | $delta_XL_obalance = -(($new_YL_obalance / $final_P - $obalance / $P) * $debt_ratio); |
| 162 | $totals.delta_YL = $totals.delta_YL + $delta_YL_obalance; |
| 163 | $totals.delta_XL = $totals.delta_XL + $delta_XL_obalance; |
| 164 | $totals.YL_denom = $totals.YL_denom + $new_YL_obalance * ($L-1); |
| 165 | } |
| 166 | }); |
| 167 | $totals |
| 168 | }; |
| 169 | |
| 170 | |
| 171 | |
| 172 | $swap = ($balances, $l_balances, $x0, $y0, $y_in, $in_delta_Yn, $final_P, $received_amount_Y, $min_amount_out, $pool_props) => { |
| 173 | |
| 174 | require(!$in_delta_Yn, "no delta Yn please, this is swap by P"); |
| 175 | |
| 176 | $alpha = $pool_props.alpha; |
| 177 | $beta = $pool_props.beta; |
| 178 | $Lambda = $pool_props.Lambda; |
| 179 | |
| 180 | $xn = $balances.xn; |
| 181 | $yn = $balances.yn; |
| 182 | $x = $balances.x; |
| 183 | $y = $balances.y; |
| 184 | |
| 185 | if ($y_in){ |
| 186 | $inverted = false; |
| 187 | $X = $x; |
| 188 | $Y = $y; |
| 189 | $Xn = $xn; |
| 190 | $Yn = $yn; |
| 191 | $X0 = $x0; |
| 192 | $Y0 = $y0; |
| 193 | $a = $alpha; |
| 194 | $b = $beta; |
| 195 | } |
| 196 | else{ |
| 197 | $inverted = true; |
| 198 | $X = $y; |
| 199 | $Y = $x; |
| 200 | $Xn = $yn; |
| 201 | $Yn = $xn; |
| 202 | $X0 = $y0; |
| 203 | $Y0 = $x0; |
| 204 | $a = $beta; |
| 205 | $b = $alpha; |
| 206 | } |
| 207 | $P = $a/$b * ($Y + $Y0) / ($X + $X0); |
| 208 | require($final_P > $P, "price should increase, current " || $P || ", target " || $final_P); |
| 209 | |
| 210 | if ($Lambda > 1){ |
| 211 | $underleveraged = $Xn > ceil($X/$Lambda); |
| 212 | } |
| 213 | |
| 214 | if ($Lambda == 1){ |
| 215 | $final = $get_final_xy($X, $Y, $P, $final_P, $X0, $Y0, $pool_props, $inverted); |
| 216 | $final_X = $final.X; |
| 217 | $final_Y = $final.Y; |
| 218 | $final_Xn = $final_X; |
| 219 | $final_Yn = $final_Y; |
| 220 | } |
| 221 | else if (!$underleveraged){ |
| 222 | $final = $get_final_xy_along_x($X, $Y, $P, $final_P, $pool_props, $inverted); |
| 223 | $final_X = $final.X; |
| 224 | $final_Y = $final.Y; |
| 225 | $final_Xn = round($final_X/$Lambda); |
| 226 | $delta_Y = $final_Y - $Y; |
| 227 | $delta_Yn = -round($a/($b*$Lambda-1)*$delta_Y); |
| 228 | $final_Yn = $Yn + $delta_Yn; |
| 229 | } |
| 230 | else if ($underleveraged){ |
| 231 | $inflection_P = $P * ( $Lambda/($Lambda-1) * ($b + ($a * $Lambda - 1) * $Xn/$X) )^(1/($a*$Lambda-1)); |
| 232 | $inflected = $final_P > $inflection_P; |
| 233 | |
| 234 | $final_P1 = $inflected ? $inflection_P : $final_P; |
| 235 | $final1 = $get_final_xy_along_y($X, $Y, $P, $final_P1, $pool_props, $inverted); |
| 236 | $final_X1 = $final1.X; |
| 237 | $final_Y1 = $final1.Y; |
| 238 | $final_Yn1 = round($final_Y1 / $Lambda); |
| 239 | $delta_X1 = $final_X1 - $X; |
| 240 | $delta_Xn1 = -round($b/($a*$Lambda-1) * $delta_X1); |
| 241 | $final_Xn1 = $Xn + $delta_Xn1; |
| 242 | if ($inflected){ |
| 243 | |
| 244 | $final = $get_final_xy_along_x($final_X1, $final_Y1, $final_P1, $final_P, $pool_props, $inverted); |
| 245 | $final_X = $final.X; |
| 246 | $final_Y = $final.Y; |
| 247 | $final_Xn = round($final_X/$Lambda); |
| 248 | $delta_Y2 = $final_Y - $final_Y1; |
| 249 | $delta_Yn2 = -round($a/($b*$Lambda-1)*$delta_Y2); |
| 250 | $final_Yn = $final_Yn1 + $delta_Yn2; |
| 251 | require($final_Xn <= $final_Xn1, "Xn didn't decrease"); |
| 252 | } |
| 253 | else{ |
| 254 | $final_X = $final_X1; |
| 255 | $final_Y = $final_Y1; |
| 256 | $final_Xn = $final_Xn1; |
| 257 | $final_Yn = $final_Yn1; |
| 258 | } |
| 259 | } |
| 260 | else |
| 261 | bounce("???"); |
| 262 | |
| 263 | $balances.x = $y_in ? $final_X : $final_Y; |
| 264 | $balances.y = $y_in ? $final_Y : $final_X; |
| 265 | $balances.xn = $y_in ? $final_Xn : $final_Yn; |
| 266 | $balances.yn = $y_in ? $final_Yn : $final_Xn; |
| 267 | log("balances after swap", $balances); |
| 268 | |
| 269 | $final_y = $balances.y; |
| 270 | $final_x = $balances.x; |
| 271 | |
| 272 | |
| 273 | $totals = $update_leveraged_balances($x, $y, $final_x, $final_y, $x0, $y0, $l_balances, $alpha, $inverted); |
| 274 | |
| 275 | $amount_X = floor(-($final_Xn - $X + $totals.delta_XL)); |
| 276 | $amount_Y = ceil($final_Yn - $Yn + $totals.delta_YL); |
| 277 | if ($received_amount_Y >= 0) |
| 278 | require($received_amount_Y >= $amount_Y, "expected " || $amount_Y || ", received " || $received_amount_Y); |
| 279 | require($amount_X >= 0, "to pay " || $amount_X); |
| 280 | if ($min_amount_out) |
| 281 | require($amount_X >= $min_amount_out, "output amount " || $amount_X || " would be less than the expected minimum " || $min_amount_out); |
| 282 | $change = $received_amount_Y - $amount_Y; |
| 283 | |
| 284 | $denom = 1 - $totals.XL_denom/$b/($final_X+$X0) - $totals.YL_denom/$a/($final_Y+$Y0); |
| 285 | log('denom after swap to price:', $denom); |
| 286 | require($denom >= $singularity_threshold, "too close to the singularity point, denom="||$denom||", need more liquidity in order to swap this amount"); |
| 287 | |
| 288 | |
| 289 | require($final_P > $P, "price should have risen but hasn't, old " || $P || ", new " || $final_P); |
| 290 | |
| 291 | $arb_profit_in_Y = ($final_P - $P) * $amount_X / 2; |
| 292 | $arb_profit_in_X = $arb_profit_in_Y / $final_P; |
| 293 | $fee = ceil($arb_profit_in_X * $pool_props.arb_profit_tax + $amount_X * $pool_props.swap_fee); |
| 294 | $net_amount_X = $amount_X - $fee; |
| 295 | |
| 296 | |
| 297 | if ($Lambda > 1) |
| 298 | $add_net_balance_without_changing_price($balances, $y_in ? 'x' : 'y', $fee, $Lambda); |
| 299 | |
| 300 | { |
| 301 | net_amount_X: $net_amount_X, |
| 302 | amount_Y: $amount_Y, |
| 303 | fee: $fee, |
| 304 | change: $change, |
| 305 | |
| 306 | |
| 307 | |
| 308 | |
| 309 | } |
| 310 | }; |
| 311 | |
| 312 | |
| 313 | |
| 314 | |
| 315 | }", |
| 316 | "messages": [ |
| 317 | { |
| 318 | "app": "state", |
| 319 | "state": "{ |
| 320 | $A = $swap(); |
| 321 | bounce("library only"); |
| 322 | }" |
| 323 | } |
| 324 | ] |
| 325 | } |
| 326 | ] |