| 1 | [ |
| 2 | "autonomous agent", |
| 3 | { |
| 4 | "getters": "{ |
| 5 | |
| 6 | $year = 31104000; |
| 7 | |
| 8 | |
| 9 | $increase_supply = ($state, $ds) => { |
| 10 | $s = $state.supply; |
| 11 | $s0 = $state.s0; |
| 12 | $new_s = $s + $ds; |
| 13 | $new_s0 = $s0 * $new_s * $new_s / ($s0 * $ds + $s * $s); |
| 14 | $coef_multiplier = $s * $s / $new_s / $new_s; |
| 15 | |
| 16 | $state.supply = $new_s; |
| 17 | $state.s0 = $new_s0; |
| 18 | $state.coef = $state.coef * $coef_multiplier; |
| 19 | }; |
| 20 | |
| 21 | $subtract_grant = ($state, $dr) => { |
| 22 | $r = $state.reserve; |
| 23 | $s = $state.supply; |
| 24 | $s0 = $state.s0; |
| 25 | |
| 26 | $ro = $dr/$r; |
| 27 | $ro1 = 1 - $ro; |
| 28 | |
| 29 | $new_s0 = 1 / ($ro/$s + $ro1/$s0); |
| 30 | $coef_multiplier = $ro1 * $ro1; |
| 31 | |
| 32 | $state.reserve = $r - $dr; |
| 33 | $state.s0 = $new_s0; |
| 34 | $state.coef = $state.coef * $coef_multiplier; |
| 35 | }; |
| 36 | |
| 37 | $update_total_emissions = ($state, $props) => { |
| 38 | $total_new_emissions = $state.total_normalized_vp |
| 39 | ? (timestamp - $state.last_emissions_ts)/$year * $props.inflation_rate * $state.supply |
| 40 | : 0; |
| 41 | $state.last_emissions_ts = timestamp; |
| 42 | $state.stakers_emissions = $state.stakers_emissions + $props.stakers_share * $total_new_emissions; |
| 43 | $state.lp_emissions = $state.lp_emissions + (1 - $props.stakers_share) * $total_new_emissions; |
| 44 | }; |
| 45 | |
| 46 | $distribute_stakers_emissions = ($state, $user, $props) => { |
| 47 | $update_total_emissions($state, $props); |
| 48 | |
| 49 | $new_emissions_since_prev_visit = $state.stakers_emissions - $user.last_stakers_emissions; |
| 50 | $user.last_stakers_emissions = $state.stakers_emissions; |
| 51 | |
| 52 | if ($user.normalized_vp AND $state.total_normalized_vp){ |
| 53 | $reward = $new_emissions_since_prev_visit * $user.normalized_vp/$state.total_normalized_vp; |
| 54 | $user.reward = $user.reward + $reward; |
| 55 | $increase_supply($state, $reward); |
| 56 | } |
| 57 | }; |
| 58 | |
| 59 | $distribute_lp_emissions = ($state, $lp, $pool, $pool_vps, $total_lp_balance, $props) => { |
| 60 | $update_total_emissions($state, $props); |
| 61 | |
| 62 | if (!$state.total_normalized_vp OR $pool.blacklisted) |
| 63 | return; |
| 64 | $pool_share = $pool_vps[$pool.asset_key]/$state.total_normalized_vp; |
| 65 | |
| 66 | $new_total_lp_emissions_since_prev_visit = $state.lp_emissions - $pool.last_lp_emissions; |
| 67 | $pool.last_lp_emissions = $state.lp_emissions; |
| 68 | $pool.received_emissions = $pool.received_emissions + $new_total_lp_emissions_since_prev_visit * $pool_share; |
| 69 | |
| 70 | $new_emissions_since_prev_visit = $pool.received_emissions - $lp.last_pool_emissions; |
| 71 | $lp.last_pool_emissions = $pool.received_emissions; |
| 72 | |
| 73 | if ($lp.last_distribution_ts AND $lp.balance AND $total_lp_balance){ |
| 74 | $reward = $new_emissions_since_prev_visit * $lp.balance/$total_lp_balance; |
| 75 | $lp.reward = $lp.reward + $reward; |
| 76 | $increase_supply($state, $reward); |
| 77 | } |
| 78 | $lp.last_distribution_ts = timestamp; |
| 79 | }; |
| 80 | |
| 81 | |
| 82 | $distribute_new_vp = ($votes, $pool_vps, $delta_normalized_vp, $percentages) => { |
| 83 | $totals = {total: 0}; |
| 84 | foreach($percentages, 20, ($pool_asset_key, $percentage) => { |
| 85 | require(+substring($pool_asset_key, 1) AND starts_with($pool_asset_key, 'a'), "invalid pool asset key "||$pool_asset_key); |
| 86 | require($percentage > 0, "invalid percentage "||$percentage); |
| 87 | require(exists($pool_vps[$pool_asset_key]), "pool asset key "||$pool_asset_key||" not found in this group"); |
| 88 | $totals.total = $totals.total + $percentage; |
| 89 | $added_vp = $percentage/100 * $delta_normalized_vp; |
| 90 | $votes[$pool_asset_key] = $votes[$pool_asset_key] + $added_vp; |
| 91 | $pool_vps[$pool_asset_key] = $pool_vps[$pool_asset_key] + $added_vp; |
| 92 | }); |
| 93 | require($totals.total == 100, "percentages sum to "||$totals.total); |
| 94 | require(length($votes) <= 100, "max total number of supported pools is 100"); |
| 95 | $pool_vps.total = $pool_vps.total + $delta_normalized_vp; |
| 96 | }; |
| 97 | |
| 98 | |
| 99 | $apply_vote = ($votes, $pool_vps, $group_key1, $group_key2, $group_vps, $changes) => { |
| 100 | $totals = {total: 0, max: 0}; |
| 101 | foreach($changes, 20, ($pool_asset_key, $delta_vp) => { |
| 102 | require(+substring($pool_asset_key, 1) AND starts_with($pool_asset_key, 'a'), "invalid pool asset key "||$pool_asset_key); |
| 103 | $votes[$pool_asset_key] = $votes[$pool_asset_key] + $delta_vp; |
| 104 | $p = $votes[$pool_asset_key]; |
| 105 | require($p >= 0, "would have negative votes for pool_asset_key "||$pool_asset_key); |
| 106 | if ($p == 0) |
| 107 | delete($votes, $pool_asset_key); |
| 108 | $totals.total = $totals.total + $delta_vp; |
| 109 | if (abs($delta_vp) > $totals.max) |
| 110 | $totals.max = abs($delta_vp); |
| 111 | if (exists($pool_vps[$group_key1][$pool_asset_key])) |
| 112 | $group_key = $group_key1; |
| 113 | else if ($group_key2 AND exists($pool_vps[$group_key2][$pool_asset_key])) |
| 114 | $group_key = $group_key2; |
| 115 | else |
| 116 | bounce("pool asset key "||$pool_asset_key||" not found in any of the two groups"); |
| 117 | $pool_vps[$group_key][$pool_asset_key] = $pool_vps[$group_key][$pool_asset_key] + $delta_vp; |
| 118 | $pool_vps[$group_key].total = $pool_vps[$group_key].total + $delta_vp; |
| 119 | $group_vps[$group_key] = $group_vps[$group_key] + $delta_vp; |
| 120 | }); |
| 121 | require(abs($totals.total) < $totals.max * 1e-15, "total votes changed by "||$totals.total); |
| 122 | require(length($votes) <= 100, "max total number of supported pools is 100"); |
| 123 | }; |
| 124 | |
| 125 | $remove_votes = ($votes, $pool_vps) => { |
| 126 | foreach($votes, 100, ($pool_asset_key, $vp) => { |
| 127 | require(exists($pool_vps[$pool_asset_key]), "pool asset key "||$pool_asset_key||" not found in the indicated group"); |
| 128 | $pool_vps[$pool_asset_key] = $pool_vps[$pool_asset_key] - $votes[$pool_asset_key]; |
| 129 | }); |
| 130 | }; |
| 131 | |
| 132 | |
| 133 | |
| 134 | |
| 135 | $pow2 = $x => $x*$x; |
| 136 | |
| 137 | |
| 138 | $get_reserve_by_state = ($s, $state) => $state.coef * $s * $state.s0 / ($state.s0 - $s); |
| 139 | $get_tokens_by_state = ($r, $state) => 1 / (1/$state.s0 + $state.coef/$r); |
| 140 | $get_price_by_state = ($s, $state) => $state.coef * $pow2($state.s0 / ($state.s0 - $s)); |
| 141 | |
| 142 | |
| 143 | $get_appreciation_result = ($state, $appreciation_rate) => { |
| 144 | $elapsed_time = timestamp - $state.last_ts; |
| 145 | |
| 146 | $r = $state.reserve; |
| 147 | $s = $state.supply; |
| 148 | $s0 = $state.s0; |
| 149 | if ($s == 0 OR $elapsed_time == 0) |
| 150 | return {new_s0: $s0, coef_multiplier: 1}; |
| 151 | $p = $get_price_by_state($s, $state); |
| 152 | $new_p = $p * (1 + $elapsed_time/$year * $appreciation_rate); |
| 153 | $new_s0 = $s + 1 / ($new_p/$r - 1/$s); |
| 154 | require($new_s0 > 0, "appreciation new s0 = "||$new_s0); |
| 155 | $coef_multiplier = $s0/$new_s0 * ($new_s0 - $s)/($s0 - $s); |
| 156 | { |
| 157 | new_s0: $new_s0, |
| 158 | coef_multiplier: $coef_multiplier, |
| 159 | } |
| 160 | }; |
| 161 | |
| 162 | $apply_appreciation = ($state, $appreciation_rate) => { |
| 163 | $appr_res = $get_appreciation_result($state, $appreciation_rate); |
| 164 | $state.s0 = $appr_res.new_s0; |
| 165 | $state.coef = $state.coef * $appr_res.coef_multiplier; |
| 166 | $state.last_ts = timestamp; |
| 167 | }; |
| 168 | |
| 169 | |
| 170 | |
| 171 | |
| 172 | $get_exchange_result_by_state = ($tokens, $delta_r, $state, $props) => { |
| 173 | require($tokens > 0 AND $delta_r == 0 OR $tokens == 0 AND $delta_r > 0, "invalid input"); |
| 174 | |
| 175 | $r = $state.reserve; |
| 176 | $s = $state.supply; |
| 177 | $p = $get_price_by_state($s, $state); |
| 178 | |
| 179 | |
| 180 | $swap_fee_rate = $props.swap_fee; |
| 181 | $arb_profit_tax_rate = $props.arb_profit_tax; |
| 182 | |
| 183 | if ($tokens) { |
| 184 | $new_s = $s - $tokens; |
| 185 | $net_new_r = $get_reserve_by_state($new_s, $state); |
| 186 | $swap_fee = $swap_fee_rate * ($r - $net_new_r); |
| 187 | } |
| 188 | else { |
| 189 | $gross_new_r = $r + $delta_r; |
| 190 | $swap_fee = $r ? $swap_fee_rate * $delta_r : 0; |
| 191 | $new_r1 = $r + $delta_r - $swap_fee; |
| 192 | $new_s1 = $get_tokens_by_state($new_r1, $state); |
| 193 | |
| 194 | $new_p1 = $get_price_by_state($new_s1, $state); |
| 195 | |
| 196 | $arb_profit_tax = $r ? $arb_profit_tax_rate * ($new_p1 - $p) * ($new_s1 - $s) / 2 : 0; |
| 197 | |
| 198 | $net_new_r = $new_r1 - $arb_profit_tax; |
| 199 | $new_s = $get_tokens_by_state($net_new_r, $state); |
| 200 | |
| 201 | $delta_s = $new_s - $s; |
| 202 | } |
| 203 | |
| 204 | $new_p = $get_price_by_state($new_s, $state); |
| 205 | |
| 206 | |
| 207 | if ($tokens) |
| 208 | $arb_profit_tax = $arb_profit_tax_rate * abs(($new_p - $p) * ($new_s - $s) / 2); |
| 209 | $total_fee = $swap_fee + $arb_profit_tax; |
| 210 | |
| 211 | if ($tokens){ |
| 212 | $gross_new_r = ceil($net_new_r + $total_fee); |
| 213 | $payout = $r - $gross_new_r; |
| 214 | $fee_percent = $total_fee / ($r - $net_new_r) * 100; |
| 215 | } |
| 216 | else |
| 217 | $fee_percent = $total_fee / $delta_r * 100; |
| 218 | |
| 219 | $coef_multiplier = $gross_new_r / $net_new_r; |
| 220 | |
| 221 | { |
| 222 | payout: $payout, |
| 223 | delta_s: $tokens ? -$tokens : $delta_s, |
| 224 | old_reserve: $r, |
| 225 | new_reserve: $gross_new_r, |
| 226 | delta_reserve: $gross_new_r - $r, |
| 227 | old_price: $p, |
| 228 | new_price: $new_p, |
| 229 | swap_fee: $swap_fee, |
| 230 | arb_profit_tax: $arb_profit_tax, |
| 231 | total_fee: $total_fee, |
| 232 | fee_percent: $fee_percent, |
| 233 | coef_multiplier: $coef_multiplier, |
| 234 | } |
| 235 | }; |
| 236 | |
| 237 | }", |
| 238 | "messages": [ |
| 239 | { |
| 240 | "app": "state", |
| 241 | "state": "{ |
| 242 | bounce("library only"); |
| 243 | }" |
| 244 | } |
| 245 | ] |
| 246 | } |
| 247 | ] |