| 1 | [ |
| 2 | "autonomous agent", |
| 3 | { |
| 4 | "doc_url": "https://coop.obyte.org/coop.json", |
| 5 | "getters": "{ |
| 6 | |
| 7 | |
| 8 | |
| 9 | $year = 31536000; |
| 10 | |
| 11 | |
| 12 | $get_deposited_supply = () => { |
| 13 | $state = var['state']; |
| 14 | $state.total_locked |
| 15 | }; |
| 16 | |
| 17 | |
| 18 | $get_variables = () => { |
| 19 | var['variables'] OTHERWISE { |
| 20 | daily_locked_reward: 0.01, |
| 21 | daily_liquid_reward: 0.001, |
| 22 | bytes_reducer: 0.75, |
| 23 | by_votes_share: 0.5, |
| 24 | messaging_attestors: 'WMFLGI2GLAB2MDF2KQAH37VNRRMK7A5N:WVO7PWJUAIEGJM7HY25SX6UPXSTCN4VH:FSJVTTCHUIWALPN7Y6GYEKZACXMEXIG3:5KM36CFPBD2QJLVD65PHZG34WEM4RPY2', |
| 25 | real_name_attestors: 'WMFLGI2GLAB2MDF2KQAH37VNRRMK7A5N:WVO7PWJUAIEGJM7HY25SX6UPXSTCN4VH:FSJVTTCHUIWALPN7Y6GYEKZACXMEXIG3', |
| 26 | referrer_coop_deposit_reward_share: 0.02, |
| 27 | referrer_bytes_deposit_reward_share: 0.01, |
| 28 | referral_reward: 10e9, |
| 29 | min_balance_instead_of_real_name: 1e8, |
| 30 | } |
| 31 | }; |
| 32 | |
| 33 | |
| 34 | $get_ceiling_price = () => { |
| 35 | $constants = var['constants']; |
| 36 | 2^((timestamp - $constants.launch_ts)/$year) |
| 37 | }; |
| 38 | |
| 39 | |
| 40 | |
| 41 | }", |
| 42 | "init": "{ |
| 43 | |
| 44 | $vote_lifetime = 90 * 24 * 3600; |
| 45 | |
| 46 | $constants = var['constants'] OTHERWISE {}; |
| 47 | |
| 48 | |
| 49 | $asset = $constants.asset; |
| 50 | |
| 51 | $governance_aa = $constants.governance_aa; |
| 52 | |
| 53 | $variables = $get_variables(); |
| 54 | |
| 55 | $by_votes_share = $variables.by_votes_share; |
| 56 | $by_vb_share = 1 - $by_votes_share; |
| 57 | |
| 58 | $ceiling_price = 2^((timestamp - $constants.launch_ts)/$year); |
| 59 | |
| 60 | $state = var['state'] OTHERWISE { |
| 61 | total_locked: 0, |
| 62 | total_locked_bytes: 0, |
| 63 | locked_emissions: 0, |
| 64 | liquid_emissions: 0, |
| 65 | locked_emissions_per_vote: 0, |
| 66 | liquid_emissions_per_vote: 0, |
| 67 | locked_emissions_per_vb: 0, |
| 68 | liquid_emissions_per_vb: 0, |
| 69 | total_votes: 0, |
| 70 | total_votes_bal: 0, |
| 71 | ts: timestamp, |
| 72 | }; |
| 73 | |
| 74 | $update_emissions = () => { |
| 75 | $elapsed_days = (timestamp - $state.ts)/24/3600; |
| 76 | $s = $state.total_locked + $state.total_locked_bytes / $ceiling_price * $variables.bytes_reducer; |
| 77 | $new_locked_emissions = $s * $variables.daily_locked_reward * $elapsed_days; |
| 78 | $new_liquid_emissions = $s * $variables.daily_liquid_reward * $elapsed_days; |
| 79 | $state.locked_emissions = $state.locked_emissions + $new_locked_emissions; |
| 80 | $state.liquid_emissions = $state.liquid_emissions + $new_liquid_emissions; |
| 81 | if ($state.total_votes AND $state.total_votes_bal){ |
| 82 | $state.locked_emissions_per_vote = $state.locked_emissions_per_vote + $new_locked_emissions / $state.total_votes; |
| 83 | $state.liquid_emissions_per_vote = $state.liquid_emissions_per_vote + $new_liquid_emissions / $state.total_votes; |
| 84 | $state.locked_emissions_per_vb = $state.locked_emissions_per_vb + $new_locked_emissions / $state.total_votes_bal; |
| 85 | $state.liquid_emissions_per_vb = $state.liquid_emissions_per_vb + $new_liquid_emissions / $state.total_votes_bal; |
| 86 | } |
| 87 | $state.ts = timestamp; |
| 88 | }; |
| 89 | |
| 90 | $update_user = ($user) => { |
| 91 | $old_total_balance = $user.total_balance; |
| 92 | $new_locked_emissions_per_vote = $state.locked_emissions_per_vote - $user.last_locked_emissions_per_vote; |
| 93 | $new_liquid_emissions_per_vote = $state.liquid_emissions_per_vote - $user.last_liquid_emissions_per_vote; |
| 94 | $new_locked_emissions_per_vb = $state.locked_emissions_per_vb - $user.last_locked_emissions_per_vb; |
| 95 | $new_liquid_emissions_per_vb = $state.liquid_emissions_per_vb - $user.last_liquid_emissions_per_vb; |
| 96 | $user.last_locked_emissions_per_vote = $state.locked_emissions_per_vote; |
| 97 | $user.last_liquid_emissions_per_vote = $state.liquid_emissions_per_vote; |
| 98 | $user.last_locked_emissions_per_vb = $state.locked_emissions_per_vb; |
| 99 | $user.last_liquid_emissions_per_vb = $state.liquid_emissions_per_vb; |
| 100 | $v = $by_votes_share * $user.votes; |
| 101 | $vb = $by_vb_share * $user.votes * $old_total_balance; |
| 102 | $user_new_locked_emissions = $v * $new_locked_emissions_per_vote + $vb * $new_locked_emissions_per_vb; |
| 103 | $user_new_liquid_emissions = $v * $new_liquid_emissions_per_vote + $vb * $new_liquid_emissions_per_vb; |
| 104 | $user.balance = $user.balance + $user_new_locked_emissions; |
| 105 | $user.liquid_balance = $user.liquid_balance + $user_new_liquid_emissions; |
| 106 | $user.locked_rewards = $user.locked_rewards + $user_new_locked_emissions; |
| 107 | $user.liquid_rewards = $user.liquid_rewards + $user_new_liquid_emissions; |
| 108 | |
| 109 | |
| 110 | $user.total_balance = $user.balance + $user.bytes_balance/$ceiling_price * $variables.bytes_reducer; |
| 111 | $user.last_ts = timestamp; |
| 112 | |
| 113 | $state.total_locked = $state.total_locked + $user_new_locked_emissions; |
| 114 | $state.total_votes_bal = $state.total_votes_bal + ($user.total_balance - $old_total_balance) * $user.votes; |
| 115 | }; |
| 116 | |
| 117 | |
| 118 | if ($asset) |
| 119 | $received_amount = trigger.output[[asset=$asset]]; |
| 120 | $received_bytes_amount = max(trigger.output[[asset=base]] - 10000, 0); |
| 121 | |
| 122 | |
| 123 | $update_emissions(); |
| 124 | |
| 125 | $governance_base_aa = 'BB6CNTV65XREXNBP6NCRGEGODEKOBPSR'; |
| 126 | |
| 127 | }", |
| 128 | "messages": { |
| 129 | "cases": [ |
| 130 | { |
| 131 | "if": "{ trigger.data.define AND !$asset }", |
| 132 | "messages": [ |
| 133 | { |
| 134 | "app": "asset", |
| 135 | "payload": { |
| 136 | "is_private": false, |
| 137 | "is_transferrable": true, |
| 138 | "auto_destroy": false, |
| 139 | "fixed_denominations": false, |
| 140 | "issued_by_definer_only": true, |
| 141 | "cosigned_by_definer": false, |
| 142 | "spender_attested": false |
| 143 | } |
| 144 | }, |
| 145 | { |
| 146 | "app": "definition", |
| 147 | "payload": { |
| 148 | "definition": [ |
| 149 | "autonomous agent", |
| 150 | { |
| 151 | "base_aa": "{$governance_base_aa}", |
| 152 | "params": { |
| 153 | "coop_aa": "{this_address}" |
| 154 | } |
| 155 | } |
| 156 | ] |
| 157 | } |
| 158 | }, |
| 159 | { |
| 160 | "app": "state", |
| 161 | "state": "{ |
| 162 | $constants.asset = response_unit; |
| 163 | $constants.governance_aa = unit[response_unit].messages[[.app='definition']].payload.address; |
| 164 | $constants.launch_ts = timestamp; |
| 165 | var['constants'] = $constants; |
| 166 | response['asset'] = response_unit; |
| 167 | }" |
| 168 | } |
| 169 | ] |
| 170 | }, |
| 171 | { |
| 172 | "if": "{ trigger.address == $governance_aa AND trigger.data.name }", |
| 173 | "init": "{ |
| 174 | $name = trigger.data.name; |
| 175 | $value = trigger.data.value; |
| 176 | }", |
| 177 | "messages": [ |
| 178 | { |
| 179 | "app": "state", |
| 180 | "state": "{ |
| 181 | $variables[$name] = $value; |
| 182 | var['variables'] = $variables; |
| 183 | }" |
| 184 | } |
| 185 | ] |
| 186 | }, |
| 187 | { |
| 188 | "if": "{ |
| 189 | ($received_amount > 0 OR $received_bytes_amount > 0 OR trigger.data.term) AND trigger.data.deposit |
| 190 | }", |
| 191 | "init": "{ |
| 192 | $username = attestation[[attestors=$variables.messaging_attestors, address=trigger.address, ifnone=false]].username; |
| 193 | require($username, "your address must be attested on a messaging service"); |
| 194 | |
| 195 | $messagingUserId = attestation[[attestors=$variables.messaging_attestors, address=trigger.address, ifnone=false]].userId OTHERWISE $username; |
| 196 | $m_addr = var['m_address_'||$messagingUserId]; |
| 197 | require(!$m_addr OR $m_addr == trigger.address, "only one account per messaging user is allowed"); |
| 198 | |
| 199 | require(!is_aa(trigger.address), "AAs not allowed to participate"); |
| 200 | |
| 201 | $user = var['user_'||trigger.address] OTHERWISE { |
| 202 | balance: 0, |
| 203 | bytes_balance: 0, |
| 204 | total_balance: 0, |
| 205 | unlock_date: false, |
| 206 | reg_date: timestamp_to_string(timestamp, 'date'), |
| 207 | reg_ts: timestamp, |
| 208 | last_ts: timestamp, |
| 209 | last_locked_emissions_per_vote: $state.locked_emissions_per_vote, |
| 210 | last_liquid_emissions_per_vote: $state.liquid_emissions_per_vote, |
| 211 | last_locked_emissions_per_vb: $state.locked_emissions_per_vb, |
| 212 | last_liquid_emissions_per_vb: $state.liquid_emissions_per_vb, |
| 213 | }; |
| 214 | $bNewUser = !$user.unlock_date; |
| 215 | $update_user($user); |
| 216 | |
| 217 | $old_total_balance = $user.total_balance; |
| 218 | |
| 219 | $total_balance_sans_reducers = $user.balance + $received_amount + ($user.bytes_balance + $received_bytes_amount)/$ceiling_price; |
| 220 | $bBigDeposit = $total_balance_sans_reducers >= $variables.min_balance_instead_of_real_name; |
| 221 | if (!$bBigDeposit) |
| 222 | $user_id = attestation[[attestors=$variables.real_name_attestors, address=trigger.address, ifnone=false]].user_id; |
| 223 | require($user_id OR $bBigDeposit, "your address must be real-name attested or you should deposit at least "||($variables.min_balance_instead_of_real_name/1e9)||" COOP"); |
| 224 | if ($user_id){ |
| 225 | $addr = var['rn_address_'||$user_id]; |
| 226 | require(!$addr OR $addr == trigger.address, "only one account per user is allowed"); |
| 227 | } |
| 228 | |
| 229 | if (exists(trigger.data.ref)){ |
| 230 | require(is_valid_address(trigger.data.ref), "referrer address not valid"); |
| 231 | require(var['user_'||trigger.data.ref], "referrer doesn't exist"); |
| 232 | if ($bNewUser){ |
| 233 | $user.ref = trigger.data.ref; |
| 234 | if (trigger.data.no_referrer_deposit_reward) |
| 235 | $user.no_referrer_deposit_reward = true; |
| 236 | } |
| 237 | else |
| 238 | response['ref_ignored'] = "Referrer can be set only with the first deposit"; |
| 239 | } |
| 240 | |
| 241 | if ($user.ref){ |
| 242 | $referrer = var['user_'||$user.ref]; |
| 243 | $min_unlock_date = timestamp_to_string(timestamp + $year, 'date'); |
| 244 | $bReferrerEligible = $referrer.unlock_date >= $min_unlock_date; |
| 245 | if (!$bReferrerEligible) |
| 246 | response['warning'] = "Referrer's unlock date is less than 1 year in the future and they are not eligible to receive the deposit reward"; |
| 247 | } |
| 248 | |
| 249 | $term = trigger.data.term OTHERWISE 365; |
| 250 | require($term >= 365 AND $term <= 3650 OR trigger.data.test, "minimum term is 365 days and maximum term is 3650 days"); |
| 251 | $new_unlock_date = timestamp_to_string(timestamp + $term * 24 * 3600, 'date'); |
| 252 | if (!$bNewUser) |
| 253 | require($new_unlock_date >= $user.unlock_date, "new unlock date must not go back"); |
| 254 | |
| 255 | }", |
| 256 | "messages": [ |
| 257 | { |
| 258 | "if": "{$user.ref AND !$user.no_referrer_deposit_reward AND $bReferrerEligible}", |
| 259 | "app": "payment", |
| 260 | "payload": { |
| 261 | "asset": "{$asset}", |
| 262 | "outputs": [ |
| 263 | { |
| 264 | "address": "{$user.ref}", |
| 265 | "amount": "{floor($received_amount * $variables.referrer_coop_deposit_reward_share + $received_bytes_amount/$ceiling_price * $variables.referrer_bytes_deposit_reward_share)}" |
| 266 | } |
| 267 | ] |
| 268 | } |
| 269 | }, |
| 270 | { |
| 271 | "app": "state", |
| 272 | "state": "{ |
| 273 | $user.balance = $user.balance + $received_amount; |
| 274 | $user.bytes_balance = $user.bytes_balance + $received_bytes_amount; |
| 275 | $user.total_balance = $user.balance + $user.bytes_balance/$ceiling_price * $variables.bytes_reducer; |
| 276 | $user.unlock_date = $new_unlock_date; |
| 277 | if ($bNewUser AND $user_id) |
| 278 | var['rn_address_'||$user_id] = trigger.address; |
| 279 | var['m_address_'||$messagingUserId] = trigger.address; |
| 280 | $state.total_locked = $state.total_locked + $received_amount; |
| 281 | $state.total_locked_bytes = $state.total_locked_bytes + $received_bytes_amount; |
| 282 | $state.total_votes_bal = $state.total_votes_bal + ($user.total_balance - $old_total_balance) * $user.votes; |
| 283 | |
| 284 | |
| 285 | if ($bNewUser AND $user.ref AND $bReferrerEligible){ |
| 286 | $capped_referral_reward = floor(min($variables.referral_reward, $user.total_balance, $referrer.total_balance)); |
| 287 | $user.balance = $user.balance + $capped_referral_reward; |
| 288 | $user.total_balance = $user.total_balance + $capped_referral_reward; |
| 289 | $referrer.balance = $referrer.balance + $capped_referral_reward; |
| 290 | $referrer.total_balance = $referrer.total_balance + $capped_referral_reward; |
| 291 | $referrer.referral_rewards = $referrer.referral_rewards + $capped_referral_reward; |
| 292 | $referrer.referred_users = $referrer.referred_users + 1; |
| 293 | var['user_'||$user.ref] = $referrer; |
| 294 | $state.total_locked = $state.total_locked + 2 * $capped_referral_reward; |
| 295 | $state.total_votes_bal = $state.total_votes_bal + $capped_referral_reward * ($user.votes + $referrer.votes); |
| 296 | $state.total_referral_rewards = $state.total_referral_rewards + 2 * $capped_referral_reward; |
| 297 | } |
| 298 | var['user_'||trigger.address] = $user; |
| 299 | var['state'] = $state; |
| 300 | |
| 301 | if ($received_amount > 0 OR $received_bytes_amount > 0) |
| 302 | response['message'] = "Deposited"; |
| 303 | response['unlock_date'] = $new_unlock_date; |
| 304 | response['event'] = json_stringify({type: 'deposit', owner: trigger.address, amount: $received_amount, bytes_amount: $received_bytes_amount, referral_reward: $capped_referral_reward, total_balance: $user.total_balance}); |
| 305 | }" |
| 306 | } |
| 307 | ] |
| 308 | }, |
| 309 | { |
| 310 | "if": "{trigger.data.vote AND trigger.data.for AND exists(trigger.data.strength)}", |
| 311 | "init": "{ |
| 312 | $strength = trigger.data.strength; |
| 313 | require($strength >= 0 AND $strength <= 3, "vote strength must be between 0 and 3"); |
| 314 | |
| 315 | $for = trigger.data.for; |
| 316 | require($for != trigger.address, "voting for oneself is unnecessary, such a vote is automatically added whenever you vote for anyone else"); |
| 317 | }", |
| 318 | "messages": [ |
| 319 | { |
| 320 | "app": "state", |
| 321 | "state": "{ |
| 322 | if (trigger.data.delete_expired_votes){ |
| 323 | foreach(trigger.data.delete_expired_votes, 5, ($from_address, $to_address) => { |
| 324 | $vote = var['vote_'||$from_address||'_'||$to_address]; |
| 325 | if (!$vote) return; |
| 326 | if (timestamp - $vote.ts < $vote_lifetime) return; |
| 327 | $to_user = var['user_'||$to_address]; |
| 328 | $to_user.votes = $to_user.votes - $vote.votes; |
| 329 | $state.total_votes = $state.total_votes - $vote.votes; |
| 330 | $state.total_votes_bal = $state.total_votes_bal - $vote.votes * $to_user.total_balance; |
| 331 | var['vote_'||$from_address||'_'||$to_address] = false; |
| 332 | var['user_'||$to_address] = $to_user; |
| 333 | }); |
| 334 | } |
| 335 | |
| 336 | $user = var['user_'||trigger.address]; |
| 337 | require($user, "you are not a user"); |
| 338 | require($user.balance > 0 OR $user.bytes_balance > 0, "you have no balance"); |
| 339 | |
| 340 | $for_user = var['user_'||$for]; |
| 341 | require($for_user, "the user you are voting for does not exist"); |
| 342 | |
| 343 | $min_unlock_date = timestamp_to_string(timestamp + 365 * 24 * 3600, 'date'); |
| 344 | require($user.unlock_date >= $min_unlock_date, "your balance unlocks on "||$user.unlock_date||", you can vote only if it unlocks in at least 1 year"); |
| 345 | require($for_user.unlock_date >= $min_unlock_date, "the voted user's balance unlocks on "||$for_user.unlock_date||", you can vote for them only if it unlocks in at least 1 year"); |
| 346 | |
| 347 | $update_user($user); |
| 348 | $update_user($for_user); |
| 349 | |
| 350 | $sqrt_bal = sqrt($user.total_balance); |
| 351 | $votes = $sqrt_bal * $strength; |
| 352 | $self_votes = $sqrt_bal * 3; |
| 353 | |
| 354 | $add_vote = ($u, $address, $new_votes, $s) => { |
| 355 | $prev_vote = var['vote_'||trigger.address||'_'||$address]; |
| 356 | $delta_votes = $new_votes - $prev_vote.votes; |
| 357 | var['vote_'||trigger.address||'_'||$address] = $new_votes ? {votes: $new_votes, strength: $s, ts: timestamp} : false; |
| 358 | $u.votes = $u.votes + $delta_votes; |
| 359 | var['user_'||$address] = $u; |
| 360 | $state.total_votes = $state.total_votes + $delta_votes; |
| 361 | $state.total_votes_bal = $state.total_votes_bal + $delta_votes * $u.total_balance; |
| 362 | }; |
| 363 | |
| 364 | $add_vote($for_user, $for, $votes, $strength); |
| 365 | $add_vote($user, trigger.address, $self_votes, 3); |
| 366 | |
| 367 | response['message'] = 'Voted'; |
| 368 | response['event'] = json_stringify({type: 'vote', address: trigger.address, for: $for, strength: $strength, votes: $votes, total_balance: $user.total_balance, for_total_balance: $for_user.total_balance}); |
| 369 | var['state'] = $state; |
| 370 | }" |
| 371 | } |
| 372 | ] |
| 373 | }, |
| 374 | { |
| 375 | "if": "{trigger.data.claim}", |
| 376 | "init": "{ |
| 377 | $user = var['user_'||trigger.address]; |
| 378 | require($user, "you are not a user"); |
| 379 | |
| 380 | $restake_percent = trigger.data.restake_percent OTHERWISE 0; |
| 381 | require($restake_percent >= 0 AND $restake_percent <= 100, "invalid restake_percent"); |
| 382 | |
| 383 | $update_user($user); |
| 384 | |
| 385 | require($user.liquid_balance > 0, "you have no liquid balance to withdraw"); |
| 386 | $claimed_amount = floor($user.liquid_balance * (1 - $restake_percent/100)); |
| 387 | $restaked_amount = $user.liquid_balance * $restake_percent/100; |
| 388 | }", |
| 389 | "messages": [ |
| 390 | { |
| 391 | "app": "payment", |
| 392 | "payload": { |
| 393 | "asset": "{$asset}", |
| 394 | "outputs": [ |
| 395 | { |
| 396 | "address": "{trigger.address}", |
| 397 | "amount": "{$claimed_amount}" |
| 398 | } |
| 399 | ] |
| 400 | } |
| 401 | }, |
| 402 | { |
| 403 | "app": "state", |
| 404 | "state": "{ |
| 405 | if ($restaked_amount){ |
| 406 | $user.balance = $user.balance + $restaked_amount; |
| 407 | $user.total_balance = $user.total_balance + $restaked_amount; |
| 408 | $state.total_locked = $state.total_locked + $restaked_amount; |
| 409 | $state.total_votes_bal = $state.total_votes_bal + $restaked_amount * $user.votes; |
| 410 | |
| 411 | |
| 412 | $new_unlock_date = timestamp_to_string(timestamp + $year, 'date'); |
| 413 | if ($new_unlock_date > $user.unlock_date) |
| 414 | $user.unlock_date = $new_unlock_date; |
| 415 | } |
| 416 | $user.liquid_balance = 0; |
| 417 | var['user_'||trigger.address] = $user; |
| 418 | var['state'] = $state; |
| 419 | |
| 420 | response['message'] = 'Claimed'; |
| 421 | response['event'] = json_stringify({type: 'claim', address: trigger.address, claimed_amount: $claimed_amount, restaked_amount: $restaked_amount, restake_percent: $restake_percent, total_balance: $user.total_balance}); |
| 422 | }" |
| 423 | } |
| 424 | ] |
| 425 | }, |
| 426 | { |
| 427 | "if": "{trigger.data.withdraw}", |
| 428 | "init": "{ |
| 429 | $user = var['user_'||trigger.address]; |
| 430 | require($user, "you are not a user"); |
| 431 | require(timestamp_to_string(timestamp, 'date') >= $user.unlock_date, "your balance unlocks on "||$user.unlock_date); |
| 432 | require($user.balance > 0 OR $user.bytes_balance > 0, "you have no balance"); |
| 433 | }", |
| 434 | "messages": [ |
| 435 | { |
| 436 | "app": "payment", |
| 437 | "payload": { |
| 438 | "asset": "base", |
| 439 | "outputs": [ |
| 440 | { |
| 441 | "address": "{trigger.address}", |
| 442 | "amount": "{floor($user.bytes_balance)}" |
| 443 | }, |
| 444 | { |
| 445 | "address": "{$governance_aa}", |
| 446 | "amount": 1000 |
| 447 | } |
| 448 | ] |
| 449 | } |
| 450 | }, |
| 451 | { |
| 452 | "app": "payment", |
| 453 | "payload": { |
| 454 | "asset": "{$asset}", |
| 455 | "outputs": [ |
| 456 | { |
| 457 | "address": "{trigger.address}", |
| 458 | "amount": "{floor($user.balance + $user.liquid_balance)}" |
| 459 | } |
| 460 | ] |
| 461 | } |
| 462 | }, |
| 463 | { |
| 464 | "app": "data", |
| 465 | "payload": { |
| 466 | "update_user_balance": 1, |
| 467 | "address": "{trigger.address}" |
| 468 | } |
| 469 | }, |
| 470 | { |
| 471 | "app": "state", |
| 472 | "state": "{ |
| 473 | response['message'] = 'Withdrawn'; |
| 474 | response['event'] = json_stringify({type: 'withdrawal', address: trigger.address, balance: $user.balance, bytes_balance: $user.bytes_balance, total_balance: 0}); |
| 475 | $state.total_locked = $state.total_locked - $user.balance; |
| 476 | $state.total_locked_bytes = $state.total_locked_bytes - $user.bytes_balance; |
| 477 | $state.total_votes_bal = $state.total_votes_bal - $user.total_balance * $user.votes; |
| 478 | $user.balance = 0; |
| 479 | $user.bytes_balance = 0; |
| 480 | $user.liquid_balance = 0; |
| 481 | $user.total_balance = 0; |
| 482 | var['user_'||trigger.address] = $user; |
| 483 | var['state'] = $state; |
| 484 | }" |
| 485 | } |
| 486 | ] |
| 487 | }, |
| 488 | { |
| 489 | "if": "{trigger.data.replace AND $received_amount > 0}", |
| 490 | "init": "{ |
| 491 | require($received_bytes_amount == 0, "don't send bytes"); |
| 492 | $user = var['user_'||trigger.address]; |
| 493 | require($user, "you are not a user"); |
| 494 | $update_user($user); |
| 495 | $out_bytes_amount = floor($received_amount * $ceiling_price); |
| 496 | require($user.bytes_balance >= $out_bytes_amount, "not enough bytes locked"); |
| 497 | }", |
| 498 | "messages": [ |
| 499 | { |
| 500 | "app": "payment", |
| 501 | "payload": { |
| 502 | "asset": "base", |
| 503 | "outputs": [ |
| 504 | { |
| 505 | "address": "{trigger.address}", |
| 506 | "amount": "{$out_bytes_amount}" |
| 507 | } |
| 508 | ] |
| 509 | } |
| 510 | }, |
| 511 | { |
| 512 | "app": "state", |
| 513 | "state": "{ |
| 514 | $state.total_locked = $state.total_locked + $received_amount; |
| 515 | $state.total_locked_bytes = $state.total_locked_bytes - $out_bytes_amount; |
| 516 | |
| 517 | $user.balance = $user.balance + $received_amount; |
| 518 | $user.bytes_balance = $user.bytes_balance - $out_bytes_amount; |
| 519 | |
| 520 | |
| 521 | $new_total_balance = $user.balance + $user.bytes_balance/$ceiling_price * $variables.bytes_reducer; |
| 522 | $delta_total_balance = $new_total_balance - $user.total_balance; |
| 523 | $user.total_balance = $new_total_balance; |
| 524 | |
| 525 | $state.total_votes_bal = $state.total_votes_bal + $delta_total_balance * $user.votes; |
| 526 | |
| 527 | var['user_'||trigger.address] = $user; |
| 528 | var['state'] = $state; |
| 529 | |
| 530 | response['message'] = 'Replaced'; |
| 531 | response['event'] = json_stringify({type: 'replace', address: trigger.address, received_amount: $received_amount, out_bytes_amount: $out_bytes_amount, total_balance: $user.total_balance}); |
| 532 | }" |
| 533 | } |
| 534 | ] |
| 535 | } |
| 536 | ] |
| 537 | } |
| 538 | } |
| 539 | ] |