| 1 | [ |
| 2 | "autonomous agent", |
| 3 | { |
| 4 | "doc_url": "https://obyte.org/obby-forms-store-aa.json", |
| 5 | "init": "{ |
| 6 | $challenging_period = 30*24*3600; |
| 7 | $grace_period = 30*24*3600; |
| 8 | $overwhelming_multiplier = 5; |
| 9 | $min_amount = 1e8; |
| 10 | |
| 11 | $amount = trigger.output[[asset=base]]; |
| 12 | |
| 13 | $drawer = trigger.data.drawer OTHERWISE 0; |
| 14 | if (!is_integer($drawer)) |
| 15 | bounce("drawer must be integer"); |
| 16 | if ($drawer != 0 AND $drawer != 1 AND $drawer != 7 AND $drawer != 30 AND $drawer != 90 AND $drawer != 180 AND $drawer != 360) |
| 17 | bounce("bad drawer: " || $drawer); |
| 18 | |
| 19 | $symbol = trigger.data.symbol; |
| 20 | if ($symbol){ |
| 21 | if (typeof($symbol) != 'string') |
| 22 | bounce("symbol must be string"); |
| 23 | if ($symbol != to_lower($symbol)) |
| 24 | bounce("symbol must be lowercase"); |
| 25 | if (length($symbol) > 20) |
| 26 | bounce("symbol must be max 20 characters long"); |
| 27 | if ($symbol == 'store' OR $symbol == 'editor' OR $symbol == 'docs' OR $symbol == 'agent') |
| 28 | bounce("bad symbol"); |
| 29 | if (contains($symbol, "/")) |
| 30 | bounce("url must not be followed by a slash"); |
| 31 | if (!has_only($symbol, "a-z0-9")) |
| 32 | bounce("url must not valid"); |
| 33 | |
| 34 | } |
| 35 | |
| 36 | $description = trigger.data.description; |
| 37 | if ($description){ |
| 38 | if (typeof($description) != 'string') |
| 39 | bounce("description must be string"); |
| 40 | if (length($description) > 140) |
| 41 | bounce("description must be max 140 characters long"); |
| 42 | } |
| 43 | |
| 44 | $url = trigger.data.url; |
| 45 | if (exists($url)){ |
| 46 | if ($url != to_lower($url)) |
| 47 | bounce("url must be lower"); |
| 48 | |
| 49 | if (contains($url, "/")) |
| 50 | bounce("url must not be followed by a slash"); |
| 51 | } |
| 52 | |
| 53 | $address = trigger.data.address; |
| 54 | if ($address){ |
| 55 | if (!is_aa($address)) |
| 56 | bounce("address is not AA"); |
| 57 | } |
| 58 | }", |
| 59 | "messages": { |
| 60 | "cases": [ |
| 61 | { |
| 62 | "if": "{ trigger.data.withdraw AND trigger.data.amount AND $address AND $symbol }", |
| 63 | "init": "{ |
| 64 | $drawer_key = trigger.address || '_' || $drawer || '_' || $symbol || '_' || $address; |
| 65 | if (var[$drawer_key] < trigger.data.amount) |
| 66 | bounce("not enough funds in this drawer"); |
| 67 | if ($drawer){ |
| 68 | $expiry_ts = var[$drawer_key || '_expiry_ts']; |
| 69 | if ($expiry_ts AND timestamp < $expiry_ts) |
| 70 | bounce("warm-up period has not expired yet"); |
| 71 | $allowed = !!$expiry_ts; |
| 72 | } |
| 73 | else |
| 74 | $allowed = true; |
| 75 | }", |
| 76 | "messages": [ |
| 77 | { |
| 78 | "if": "{$allowed}", |
| 79 | "app": "payment", |
| 80 | "payload": { |
| 81 | "outputs": [ |
| 82 | { |
| 83 | "address": "{trigger.address}", |
| 84 | "amount": "{trigger.data.amount}" |
| 85 | } |
| 86 | ] |
| 87 | } |
| 88 | }, |
| 89 | { |
| 90 | "app": "state", |
| 91 | "state": "{ |
| 92 | if ($allowed){ |
| 93 | var[$drawer_key] -= trigger.data.amount; |
| 94 | if ($drawer) |
| 95 | var[$drawer_key || '_expiry_ts'] = false; |
| 96 | var['support_' || $symbol || '_' || $address] -= trigger.data.amount; |
| 97 | var['balance_' || trigger.address || '_' || $address] -= trigger.data.amount; |
| 98 | $desc_hash = var['desc_choice_' || $address || '_' || trigger.address]; |
| 99 | if ($desc_hash) |
| 100 | var['desc_support_' || $address || '_' || $desc_hash] -= trigger.data.amount; |
| 101 | } |
| 102 | else if (!$expiry_ts) |
| 103 | var[$drawer_key || '_expiry_ts'] = timestamp + $drawer * 24 * 3600; |
| 104 | }" |
| 105 | } |
| 106 | ] |
| 107 | }, |
| 108 | { |
| 109 | "if": "{$description AND $url AND ($address OR $symbol) AND $amount < $min_amount}", |
| 110 | "init": "{ |
| 111 | if ($address){ |
| 112 | $voted_address = $address; |
| 113 | $voted_symbol = var['a2s_' || $voted_address]; |
| 114 | } |
| 115 | else { |
| 116 | $voted_address = var['s2a_' || $symbol]; |
| 117 | if (!$voted_address) |
| 118 | bounce("no address found by symbol " || $symbol); |
| 119 | $voted_symbol = $symbol; |
| 120 | } |
| 121 | $balance = var['balance_' || trigger.address || '_' || $voted_address]; |
| 122 | if (!$balance) |
| 123 | bounce("you have no balance in this address"); |
| 124 | |
| 125 | $desc_hash = sha256($description || $url); |
| 126 | $current_my_desc_hash = var['desc_choice_' || $voted_address || '_' || trigger.address]; |
| 127 | $current_desc_hash = var['current_desc_' || $voted_address]; |
| 128 | $is_initial_desc = !$current_desc_hash; |
| 129 | if ($is_initial_desc) |
| 130 | $current_desc_changed = true; |
| 131 | else if ($desc_hash != $current_desc_hash){ |
| 132 | $new_desc_support = var['desc_support_' || $voted_address || '_' || $desc_hash] + $balance; |
| 133 | $is_removed_support_from_current_desc = ($current_my_desc_hash AND $current_my_desc_hash == $current_desc_hash); |
| 134 | $current_desc_support = var['desc_support_' || $voted_address || '_' || $current_desc_hash] - $is_removed_support_from_current_desc * $balance; |
| 135 | $current_desc_changed = ($new_desc_support > $current_desc_support); |
| 136 | } |
| 137 | if ($current_desc_changed AND !$is_initial_desc){ |
| 138 | $current_url = var['url_' || $current_desc_hash]; |
| 139 | $url_changed = ($url != $current_url); |
| 140 | } |
| 141 | }", |
| 142 | "messages": [ |
| 143 | { |
| 144 | "if": "{ ($is_initial_desc OR $url_changed) AND $voted_symbol }", |
| 145 | "app": "data", |
| 146 | "payload": { |
| 147 | "address": "{$voted_address}", |
| 148 | "name": "{$voted_symbol}", |
| 149 | "url": "{$url}" |
| 150 | } |
| 151 | }, |
| 152 | { |
| 153 | "app": "state", |
| 154 | "state": "{ |
| 155 | if (!var['desc_' || $desc_hash]){ |
| 156 | var['desc_' || $desc_hash] = $description; |
| 157 | var['url_' || $desc_hash] = $url; |
| 158 | } |
| 159 | if ($current_my_desc_hash) |
| 160 | var['desc_support_' || $voted_address || '_' || $current_my_desc_hash] -= $balance; |
| 161 | var['desc_choice_' || $voted_address || '_' || trigger.address] = $desc_hash; |
| 162 | var['desc_support_' || $voted_address || '_' || $desc_hash] += $balance; |
| 163 | if ($current_desc_changed){ |
| 164 | var['current_desc_' || $voted_address] = $desc_hash; |
| 165 | response['updated_support'] = var['desc_support_' || $voted_address || '_' || $desc_hash]; |
| 166 | response['message'] = "Your description is now the current"; |
| 167 | } |
| 168 | }" |
| 169 | } |
| 170 | ] |
| 171 | }, |
| 172 | { |
| 173 | "if": "{trigger.data.move AND trigger.data.wallet_address AND $drawer AND $address AND $symbol}", |
| 174 | "init": "{ |
| 175 | $drawer_key = trigger.data.wallet_address || '_' || $drawer || '_' || $symbol || '_' || $address; |
| 176 | $balance = var[$drawer_key]; |
| 177 | if (!$balance) |
| 178 | bounce("nothing in this drawer"); |
| 179 | $expiry_ts = var[$drawer_key || '_expiry_ts']; |
| 180 | if (!$expiry_ts) |
| 181 | bounce("warm-up period has not started yet"); |
| 182 | if (timestamp < $expiry_ts) |
| 183 | bounce("warm-up period has not expired yet"); |
| 184 | $drawer_0_key = trigger.data.wallet_address || '_0_' || $symbol || '_' || $address; |
| 185 | }", |
| 186 | "messages": [ |
| 187 | { |
| 188 | "app": "state", |
| 189 | "state": "{ |
| 190 | var[$drawer_key] = false; |
| 191 | var[$drawer_0_key] += $balance; |
| 192 | var[$drawer_key || '_expiry_ts'] = false; |
| 193 | response['message'] = "Moved " || $balance || " to drawer 0"; |
| 194 | }" |
| 195 | } |
| 196 | ] |
| 197 | }, |
| 198 | { |
| 199 | "if": "{$amount >= $min_amount AND $symbol AND $address }", |
| 200 | "init": "{ |
| 201 | $support = var['support_' || $symbol || '_' || $address] + $amount; |
| 202 | |
| 203 | $current_address = var['s2a_' || $symbol]; |
| 204 | $current_symbol = var['a2s_' || $address]; |
| 205 | $current_address_with_largest_support = var['by_largest_s2a_' || $symbol]; |
| 206 | $current_symbol_with_largest_support = var['by_largest_a2s_' || $address]; |
| 207 | if ($current_address AND !$current_address_with_largest_support) |
| 208 | bounce("no current address by largest support?"); |
| 209 | if ($current_symbol AND !$current_symbol_with_largest_support) |
| 210 | bounce("no current symbol by largest support?"); |
| 211 | |
| 212 | |
| 213 | if (!$current_address_with_largest_support OR $current_address_with_largest_support != $address AND var['support_' || $symbol || '_' || $current_address_with_largest_support] < $support) |
| 214 | $update_by_largest_s2a = true; |
| 215 | $symbol_challenge_expiry_ts = var['expiry_ts_' || $symbol]; |
| 216 | if (!$current_address){ |
| 217 | $s2a_ready = true; |
| 218 | } |
| 219 | else if ($current_address != $address AND var['support_' || $symbol || '_' || $current_address] < $support){ |
| 220 | if (!$symbol_challenge_expiry_ts) |
| 221 | $schedule_symbol_expiry = true; |
| 222 | else if (timestamp > $symbol_challenge_expiry_ts AND var['by_largest_s2a_' || $symbol] == $address){ |
| 223 | $s2a_ready = true; |
| 224 | } |
| 225 | } |
| 226 | else if ($current_address == $address AND var['by_largest_s2a_' || $symbol] == $address AND $symbol_challenge_expiry_ts AND timestamp > $symbol_challenge_expiry_ts) |
| 227 | $end_symbol_expiry = true; |
| 228 | |
| 229 | |
| 230 | if (!$current_symbol_with_largest_support OR $current_symbol_with_largest_support != $symbol AND var['support_' || $current_symbol_with_largest_support || '_' || $address] < $support) |
| 231 | $update_by_largest_a2s = true; |
| 232 | $address_challenge_expiry_ts = var['expiry_ts_' || $address]; |
| 233 | $current_symbol_support = var['support_' || $current_symbol || '_' || $address]; |
| 234 | if (!$current_symbol){ |
| 235 | $a2s_ready = true; |
| 236 | } |
| 237 | else if ($current_symbol != $symbol AND $current_symbol_support < $support){ |
| 238 | $has_largest_support = ($current_symbol_with_largest_support == $symbol); |
| 239 | $will_have_largest_support = ($has_largest_support OR $update_by_largest_a2s); |
| 240 | $has_overwhelming_support = ($support > $overwhelming_multiplier * $current_symbol_support); |
| 241 | $immediate = $will_have_largest_support AND $has_overwhelming_support AND timestamp < var['grace_expiry_ts_' || $address]; |
| 242 | if (!$address_challenge_expiry_ts){ |
| 243 | if ($immediate){ |
| 244 | $a2s_ready = true; |
| 245 | } |
| 246 | else |
| 247 | $schedule_address_expiry = true; |
| 248 | } |
| 249 | else if ((timestamp > $address_challenge_expiry_ts OR $immediate) AND $has_largest_support){ |
| 250 | $a2s_ready = true; |
| 251 | } |
| 252 | } |
| 253 | else if ($current_symbol == $symbol AND $current_symbol_with_largest_support == $symbol AND $address_challenge_expiry_ts AND timestamp > $address_challenge_expiry_ts) |
| 254 | $end_address_expiry = true; |
| 255 | |
| 256 | if ($s2a_ready AND $a2s_ready){ |
| 257 | if (!$current_address AND !$current_symbol AND exists($url) AND $description){ |
| 258 | $initial_desc_hash = sha256($description || $url); |
| 259 | $current_url = $url; |
| 260 | } |
| 261 | else { |
| 262 | $current_desc_hash = var['current_desc_' || $address]; |
| 263 | if ($current_desc_hash) |
| 264 | $current_url = var['url_' || $current_desc_hash]; |
| 265 | } |
| 266 | } |
| 267 | }", |
| 268 | "messages": [ |
| 269 | { |
| 270 | "if": "{$s2a_ready AND $a2s_ready AND exists($current_url)}", |
| 271 | "app": "data", |
| 272 | "payload": { |
| 273 | "address": "{$address}", |
| 274 | "name": "{$symbol}", |
| 275 | "url": "{$current_url}" |
| 276 | } |
| 277 | }, |
| 278 | { |
| 279 | "app": "state", |
| 280 | "state": "{ |
| 281 | var['support_' || $symbol || '_' || $address] = $support; |
| 282 | |
| 283 | |
| 284 | if ($update_by_largest_s2a) |
| 285 | var['by_largest_s2a_' || $symbol] = $address; |
| 286 | if ($schedule_symbol_expiry) |
| 287 | var['expiry_ts_' || $symbol] = timestamp + $challenging_period; |
| 288 | if ($end_symbol_expiry) |
| 289 | var['expiry_ts_' || $symbol] = false; |
| 290 | |
| 291 | |
| 292 | if ($update_by_largest_a2s) |
| 293 | var['by_largest_a2s_' || $address] = $symbol; |
| 294 | if ($schedule_address_expiry) |
| 295 | var['expiry_ts_' || $address] = timestamp + $challenging_period; |
| 296 | if ($end_address_expiry) |
| 297 | var['expiry_ts_' || $address] = false; |
| 298 | |
| 299 | |
| 300 | if ($s2a_ready AND $a2s_ready){ |
| 301 | |
| 302 | if ($current_address) |
| 303 | var['a2s_' || $current_address] = false; |
| 304 | if ($current_symbol) |
| 305 | var['s2a_' || $current_symbol] = false; |
| 306 | |
| 307 | var['s2a_' || $symbol] = $address; |
| 308 | var['a2s_' || $address] = $symbol; |
| 309 | response[$symbol] = $address; |
| 310 | response[$address] = $symbol; |
| 311 | if ($symbol_challenge_expiry_ts) |
| 312 | var['expiry_ts_' || $symbol] = false; |
| 313 | if ($address_challenge_expiry_ts) |
| 314 | var['expiry_ts_' || $address] = false; |
| 315 | if (!var['grace_expiry_ts_' || $address]) |
| 316 | var['grace_expiry_ts_' || $address] = timestamp + $grace_period; |
| 317 | } |
| 318 | |
| 319 | $drawer_key = trigger.address || '_' || $drawer || '_' || $symbol || '_' || $address; |
| 320 | var[$drawer_key] += $amount; |
| 321 | response[$drawer_key] = $amount; |
| 322 | if ($drawer) |
| 323 | var[$drawer_key || '_expiry_ts'] = false; |
| 324 | var['balance_' || trigger.address || '_' || $address] += $amount; |
| 325 | |
| 326 | $desc_hash = var['desc_choice_' || $address || '_' || trigger.address]; |
| 327 | if ($desc_hash) |
| 328 | var['desc_support_' || $address || '_' || $desc_hash] += $amount; |
| 329 | |
| 330 | if ($initial_desc_hash){ |
| 331 | if (!var['desc_' || $initial_desc_hash]){ |
| 332 | var['desc_' || $initial_desc_hash] = $description; |
| 333 | var['url_' || $initial_desc_hash] = $url; |
| 334 | } |
| 335 | var['desc_choice_' || $address || '_' || trigger.address] = $initial_desc_hash; |
| 336 | var['desc_support_' || $address || '_' || $initial_desc_hash] = $amount; |
| 337 | var['current_desc_' || $address] = $initial_desc_hash; |
| 338 | response['message'] = "Your description is now the current"; |
| 339 | } |
| 340 | }" |
| 341 | } |
| 342 | ] |
| 343 | } |
| 344 | ] |
| 345 | } |
| 346 | } |
| 347 | ] |