Contract con_staking_rswp_rswp_interop_v2


Contract Code

1 # Imports
2
3 import con_rswp_lst001
4
5 I = importlib
6
7 # Setup Tokens
8
9 STAKING_TOKEN = con_rswp_lst001
10 YIELD_TOKEN = con_rswp_lst001
11
12 # State
13
14 Owner = Variable()
15 DevRewardWallet = Variable()
16 EmissionRatePerHour = Variable()
17 DevRewardPct = Variable()
18 StartTime = Variable()
19 EndTime = Variable()
20 OpenForBusiness = Variable() # If false, users will be unable to join the pool
21
22 Deposits = Hash(default_value=False)
23 Withdrawals = Hash(default_value=0)
24 CurrentEpochIndex = Variable()
25 Epochs = Hash(default_value=False)
26 StakedBalance = Variable() # The total amount of farming token in the vault.
27 WithdrawnBalance = Variable()
28 EpochMinTime = Variable() # The minimum amount of seconds in Epoch
29 EpochMaxRatioIncrease = (
30 Variable()
31 ) # The maximum ratio which the Epoch can increase by since last Epoch before incrementing.
32 meta = Hash(default_value=False)
33 decimal_converter_var = Variable()
34 TrustedExporters = Variable()
35
36 # Vtoken
37 balances = Hash(default_value=0)
38
39
40 @construct
41 def seed():
42 Owner.set(ctx.caller)
43 DevRewardWallet.set(ctx.caller)
44 CurrentEpochIndex.set(0)
45 StakedBalance.set(0)
46 WithdrawnBalance.set(0)
47 EpochMaxRatioIncrease.set(10)
48 EpochMinTime.set(86000)
49 TrustedExporters.set([])
50
51 Epochs[0] = {"time": now, "staked": 0, "amt_per_hr": 3506}
52
53 meta["version"] = "0.0.2"
54 meta[
55 "type"
56 ] = "staking_smart_epoch" # sta # staking || lp_farming || etcetera ...
57 meta["STAKING_TOKEN"] = "con_rswp_lst001"
58 meta["YIELD_TOKEN"] = "con_rswp_lst001"
59
60 EmissionRatePerHour.set(3506) # 1200000 RSWP per year = 10% of supply
61 DevRewardPct.set(1 / 10)
62
63 # The datetime from which you want to allow staking.
64 StartTime.set(datetime.datetime(year=2021, month=7, day=30, hour=22))
65 # The datetime at which you want staking to finish.
66 EndTime.set(datetime.datetime(year=2022, month=5, day=5, hour=22))
67
68 OpenForBusiness.set(True)
69
70
71 @export
72 def addStakingTokens(amount: float):
73 user = ctx.caller
74 deposit = Deposits[user]
75
76 if deposit is False:
77 return createNewDeposit(amount=amount, from_contract=False)
78 else:
79 return increaseDeposit(amount=amount, from_contract=False)
80
81
82 # This is called FROM the contract to which the yields will be staked.
83 # This contract name will need to be added to the "TrustedImporters" list on the foreign contract.
84 @export
85 def stakeFromContractProfits(contract: str):
86 # verify that the contract is calling it is trusted.
87 assert (
88 contract in TrustedExporters.get()
89 ), "The contract is not in the trusted exporters list."
90 # import staking contract
91 yield_contract = I.import_module(contract)
92 # call withdraw function to this contract, take return value
93 amount = yield_contract.exportYieldToForeignContract()
94 # stake this value
95 user = ctx.signer
96
97 deposit = Deposits[user]
98
99 if deposit is False:
100 return createNewDeposit(amount=amount, from_contract=True)
101 else:
102 return increaseDeposit(amount=amount, from_contract=True)
103
104
105 def createNewDeposit(
106 amount: float, from_contract: bool
107 ):
108 assert OpenForBusiness.get() == True, "This staking pool is not open right now."
109 assert amount > 0, "You must stake something."
110
111 user = ctx.caller
112
113 # Take the staking tokens from the user's wallet if the user has called this function via addStakingTokens
114 if from_contract is False:
115 STAKING_TOKEN.transfer_from(amount=amount, to=ctx.this, main_account=user)
116
117 # Update the Staked amount
118 staked = StakedBalance.get()
119 new_staked_amount = staked + amount
120 StakedBalance.set(new_staked_amount)
121
122 # Update the Epoch
123 epoch_index = decideIncrementEpoch(new_staked_amount=new_staked_amount)
124
125 # Create a record of the user's deposit
126
127 Deposits[user] = {
128 "starting_epoch": epoch_index,
129 "time": now,
130 "amount": amount
131 }
132 # mint vtoken equal to the deposit.
133 mintVToken(amount=amount)
134 return Deposits[user]
135
136
137 def increaseDeposit(
138 amount: float, from_contract: bool
139 ):
140
141 user = ctx.caller
142 assert OpenForBusiness.get() == True, "This staking pool is not open right now."
143 assert amount >= 0, "You cannot stake a negative balance."
144
145 deposit = Deposits[user]
146
147 assert deposit is not False, "This user has no deposit to add to."
148
149 # Take the staking tokens from the user's wallet
150 if amount > 0 and from_contract is False:
151 STAKING_TOKEN.transfer_from(amount=amount, to=ctx.this, main_account=user)
152
153 withdrawn_yield = Withdrawals[user]
154 existing_stake = deposit["amount"]
155 start_time = False
156
157 yield_to_harvest = 0
158 yield_to_harvest += calculateYield(deposit=deposit)
159 start_time = deposit["time"]
160 existing_stake = deposit["amount"]
161 user_yield_share = 0
162 yield_to_harvest -= withdrawn_yield
163
164 if yield_to_harvest > 0:
165
166 # Take % of Yield Tokens, send it to dev fund
167 dev_share = yield_to_harvest * DevRewardPct.get()
168 if dev_share > 0:
169 YIELD_TOKEN.transfer(to=DevRewardWallet.get(), amount=dev_share)
170
171 # Send remanding Yield Tokens to user
172 user_yield_share = yield_to_harvest - dev_share
173
174 total_deposit_amount = user_yield_share + existing_stake + amount
175 global_amount_staked = StakedBalance.get()
176 new_global_staked = global_amount_staked + user_yield_share + amount
177 StakedBalance.set(new_global_staked)
178 WithdrawnBalance.set(WithdrawnBalance.get() + yield_to_harvest)
179
180 mintVToken(amount=user_yield_share + amount)
181
182 Withdrawals[user] = 0
183 Deposits[user] = {
184 "starting_epoch": decideIncrementEpoch(new_staked_amount=new_global_staked),
185 "time": now,
186 "amount": total_deposit_amount,
187 }
188
189 return Deposits[user]
190
191
192 def sendYieldToTarget(amount: float, target: str, user: str):
193
194 deposit = Deposits[user]
195 assert deposit is not False, "You have no deposit to withdraw yield from."
196
197 # Calculate how much yield is due per deposit account
198 withdrawn_yield = Withdrawals[user]
199 harvestable_yield = 0
200
201 harvestable_yield += calculateYield(deposit=deposit)
202
203 # Determine maximum amount of yield user can withdraw
204 harvestable_yield -= withdrawn_yield
205
206 yield_to_harvest = amount if amount < harvestable_yield else harvestable_yield
207
208 assert yield_to_harvest > 0, "There is no yield to harvest right now :("
209
210 # Take % of Yield Tokens, send it to dev fund
211 dev_share = yield_to_harvest * DevRewardPct.get()
212
213 if dev_share > 0:
214 YIELD_TOKEN.transfer(to=DevRewardWallet.get(), amount=dev_share)
215
216 # Send remanding Yield Tokens to user
217 user_share = yield_to_harvest - dev_share
218 YIELD_TOKEN.transfer(to=target, amount=user_share)
219
220 Withdrawals[user] = withdrawn_yield + yield_to_harvest
221
222 new_withdrawn_amount = WithdrawnBalance.get() + yield_to_harvest
223 WithdrawnBalance.set(new_withdrawn_amount)
224
225 return user_share
226
227
228 @export
229 def withdrawYield(amount: float):
230 assert amount > 0, "You cannot harvest a negative balance"
231
232 user = ctx.caller
233 return sendYieldToTarget(amount=amount, target=user, user=user)
234
235
236 @export
237 def withdrawTokensAndYield():
238 user = ctx.caller
239 deposit = Deposits[user]
240
241 assert deposit is not False, "You have no deposit to withdraw"
242
243 # Calculate how much yield is due per deposit account
244 withdrawn_yield = Withdrawals[user]
245 stake_to_return = 0
246 yield_to_harvest = 0
247 user_share = 0
248
249 yield_to_harvest += calculateYield(deposit=deposit)
250 stake_to_return += deposit["amount"]
251
252 # Send Staking Tokens to user
253 STAKING_TOKEN.transfer(to=user, amount=stake_to_return)
254 returnAndBurnVToken(amount=stake_to_return)
255
256 # check that the user has yield left to harvest (this should never be negative, but let's check here just in case)
257 yield_to_harvest -= withdrawn_yield
258 if yield_to_harvest > 0:
259
260 # Take % of Yield Tokens, send it to dev fund
261 dev_share = yield_to_harvest * DevRewardPct.get()
262 if dev_share > 0:
263 YIELD_TOKEN.transfer(to=DevRewardWallet.get(), amount=dev_share)
264
265 # Send remanding Yield Tokens to user
266 user_share = yield_to_harvest - dev_share
267 YIELD_TOKEN.transfer(to=user, amount=user_share)
268
269 # Reset User's Deposits
270 Deposits[user] = False
271
272 # Reset User's Withdrawal
273 Withdrawals[user] = 0
274
275 # Remove token amount from Staked
276 new_staked_amount = StakedBalance.get() - stake_to_return
277 StakedBalance.set(new_staked_amount)
278 new_withdrawn_amount = WithdrawnBalance.get() + yield_to_harvest
279 WithdrawnBalance.set(new_withdrawn_amount)
280
281 # Increment Epoch
282 decideIncrementEpoch(new_staked_amount=new_staked_amount)
283
284 return user_share
285
286
287 # This runs over each of the items in the user's Deposit
288 def calculateYield(deposit):
289 starting_epoch_index = deposit.get("starting_epoch")
290 deposit_start_time = deposit.get("time")
291 amount = deposit.get("amount")# now - now // 0 delta
292
293 current_epoch_index = getCurrentEpochIndex()
294 this_epoch_index = starting_epoch_index
295
296 y = 0
297
298 while this_epoch_index <= current_epoch_index:
299 this_epoch = Epochs[this_epoch_index]
300 next_epoch = Epochs[this_epoch_index + 1]
301
302 delta = 0
303
304 if starting_epoch_index == current_epoch_index:
305 delta = fitTimeToRange(now) - fitTimeToRange(deposit_start_time)
306 elif this_epoch_index == starting_epoch_index:
307 delta = fitTimeToRange(next_epoch["time"]) - fitTimeToRange(
308 deposit_start_time
309 )
310 elif this_epoch_index == current_epoch_index:
311 delta = fitTimeToRange(now) - fitTimeToRange(this_epoch["time"])
312 else:
313 delta = fitTimeToRange(next_epoch["time"]) - fitTimeToRange(
314 this_epoch["time"]
315 )
316
317 pct_share_of_stake = 0
318 if amount is not 0 and this_epoch["staked"] is not 0:
319 pct_share_of_stake = amount / this_epoch["staked"]
320
321 # These two lines below were causing some problems, until I used the decimal method. get a python expert to review.
322 emission_rate_per_hour = this_epoch["amt_per_hr"]
323 global_yield_this_epoch = delta.seconds * getEmissionRatePerSecond(
324 emission_rate_per_hour
325 )
326 decimal_converter_var.set(pct_share_of_stake)
327 pct_share_of_stake = decimal_converter_var.get()
328 deposit_yield_this_epoch = (
329 global_yield_this_epoch * pct_share_of_stake
330 )
331 y += deposit_yield_this_epoch
332
333 this_epoch_index += 1
334
335 return y
336
337
338 def fitTimeToRange(time: Any):
339 if time < StartTime.get():
340 time = StartTime.get()
341 elif time > EndTime.get():
342 time = EndTime.get()
343 return time
344
345
346 def getCurrentEpochIndex():
347 current_epoch_index = CurrentEpochIndex.get()
348 return current_epoch_index
349
350
351 def decideIncrementEpoch(new_staked_amount: float):
352 epoch_index = getCurrentEpochIndex()
353 this_epoch = Epochs[epoch_index]
354 this_epoch_staked = this_epoch["staked"]
355 delta = now - this_epoch["time"]
356 delta_seconds = delta.seconds if delta.seconds > 0 else 0
357 if (
358 delta_seconds >= EpochMinTime.get()
359 or this_epoch_staked is 0
360 or maxStakedChangeRatioExceeded(
361 new_staked_amount=new_staked_amount, this_epoch_staked=this_epoch_staked
362 )
363 ):
364 epoch_index = incrementEpoch(new_staked_amount)
365 return epoch_index
366
367
368 def maxStakedChangeRatioExceeded(new_staked_amount: float, this_epoch_staked: float):
369 smaller = (
370 new_staked_amount
371 if new_staked_amount <= this_epoch_staked
372 else this_epoch_staked
373 )
374 bigger = (
375 new_staked_amount
376 if new_staked_amount >= this_epoch_staked
377 else this_epoch_staked
378 )
379 dif = bigger - smaller
380 if this_epoch_staked < 0.0001 :
381 return true
382 return (dif) / this_epoch_staked >= EpochMaxRatioIncrease.get()
383
384
385 def incrementEpoch(new_staked_amount: float):
386 current_epoch = CurrentEpochIndex.get()
387 new_epoch_idx = current_epoch + 1
388 CurrentEpochIndex.set(new_epoch_idx)
389 Epochs[new_epoch_idx] = {
390 "time": now,
391 "staked": new_staked_amount,
392 "amt_per_hr": Epochs[current_epoch]["amt_per_hr"],
393 }
394 return new_epoch_idx
395
396
397 @export
398 def changeAmountPerHour(amount_per_hour: float):
399 assertOwner()
400 current_epoch = getCurrentEpochIndex()
401 new_epoch_idx = current_epoch + 1
402 CurrentEpochIndex.set(new_epoch_idx)
403 setEmissionRatePerHour(amount=amount_per_hour)
404
405 Epochs[new_epoch_idx] = {
406 "time": now,
407 "staked": StakedBalance.get(),
408 "amt_per_hr": amount_per_hour,
409 }
410
411
412 @export
413 def setEpochMinTime(min_seconds: float):
414 assertOwner()
415 assert min_seconds >= 0, "you must choose a positive value."
416 EpochMinTime.set(min_seconds)
417
418
419 @export
420 def setEpochMaxRatioIncrease(ratio: float):
421 assertOwner()
422 assert ratio > 0, "must be a positive value"
423 EpochMaxRatioIncrease.set(ratio)
424
425
426 def getEmissionRatePerSecond(emission_rate_per_hour: float):
427 emission_rate_per_minute = emission_rate_per_hour / 60
428 emission_rate_per_second = emission_rate_per_minute / 60
429 return emission_rate_per_second
430
431
432 @export
433 def setOwner(vk: str):
434 assertOwner()
435 Owner.set(vk)
436
437
438 @export
439 def setDevWallet(vk: str):
440 assertOwner()
441 DevRewardWallet.set(vk)
442
443
444 @export
445 def setDevRewardPct(amount: float):
446 assertOwner()
447 assert amount < 1 and amount >= 0, "Amount must be a value between 0 and 1"
448 DevRewardPct.set(amount)
449
450
451 def setEmissionRatePerHour(amount: float):
452 assertOwner()
453 EmissionRatePerHour.set(amount)
454
455
456 @export
457 def addToTrustedExporters(contract: str):
458 assertOwner()
459 trusted_exporters = TrustedExporters.get()
460 if contract in trusted_exporters:
461 return
462 trusted_exporters.append(contract)
463 TrustedExporters.set(trusted_exporters)
464
465
466 @export
467 def removeFromTrustedExporters(contract: str):
468 assertOwner()
469 trusted_exporters = TrustedExporters.get()
470 trusted_exporters.remove(contract)
471 TrustedExporters.set(trusted_exporters)
472
473
474 @export
475 def recoverYieldToken():
476 assertOwner()
477 staked_balance = StakedBalance.get()
478 yield_balances = ForeignHash(
479 foreign_contract=meta["YIELD_TOKEN"], foreign_name="balances"
480 )
481 total_in_contract = yield_balances[ctx.this]
482 total_available = total_in_contract - staked_balance
483 YIELD_TOKEN.transfer(amount=total_available, to=Owner.get())
484
485
486 @export
487 def allowStaking(is_open: bool):
488 assertOwner()
489 OpenForBusiness.set(is_open)
490
491
492 @export
493 def setStartTime(year: int, month: int, day: int, hour: int):
494 assertOwner()
495 time = datetime.datetime(year, month, day, hour)
496 StartTime.set(time)
497
498
499 @export
500 def setEndTime(year: int, month: int, day: int, hour: int):
501 assertOwner()
502 time = datetime.datetime(year, month, day, hour)
503 EndTime.set(time)
504
505
506 def assertOwner():
507 assert Owner.get() == ctx.caller, "You must be the owner to call this function."
508
509
510 # This is only to be called in emergencies - the user will forgo their yield when calling this FN.
511
512
513 @export
514 def emergencyReturnStake():
515 user = ctx.caller
516 deposit = Deposits[user]
517
518 assert Deposits[user] is not False, "This account has no deposits to return."
519
520 stake_to_return = 0
521
522 stake_to_return += deposit["amount"]
523
524 STAKING_TOKEN.transfer(to=user, amount=stake_to_return)
525 returnAndBurnVToken(amount=stake_to_return)
526 Deposits[user] = False
527 Withdrawals[user] = 0
528
529 # Remove token amount from Staked
530 new_staked_amount = StakedBalance.get() - stake_to_return
531 StakedBalance.set(new_staked_amount)
532 decideIncrementEpoch(new_staked_amount=new_staked_amount)
533
534
535 @export
536 def toggleTimeRamp(on: bool):
537 assertOwner()
538 UseTimeRamp.set(on)
539
540
541 def findTimeRampStep(days: int):
542 time_ramps = TimeRampValues.get()
543 step = None
544 for s in time_ramps:
545 if s["lower"] <= days and s["upper"] > days:
546 step = s
547 if step is None:
548 return time_ramps[len(time_ramps) - 1]["multiplier"]
549 return step["multiplier"]
550
551
552 @export
553 def setTimeRampValues(data: list):
554 assertOwner()
555 TimeRampValues.set(data)
556
557
558 # VTOKEN METHODS
559 @export
560 def transfer(amount: float, to: str):
561 assert amount > 0, "Cannot send negative balances!"
562 assert balances[ctx.caller] >= amount, "Not enough VTOKENS to send!"
563 balances[ctx.caller] -= amount
564 balances[to] += amount
565
566
567 @export
568 def approve(amount: float, to: str):
569 assert amount > 0, "Cannot send negative balances!"
570 balances[ctx.caller, to] += amount
571
572
573 @export
574 def transfer_from(amount: float, to: str, main_account: str):
575 assert amount > 0, "Cannot send negative balances!"
576
577 assert (
578 balances[main_account, ctx.caller] >= amount
579 ), "Not enough coins approved to send! You have {} and are trying to spend {}".format(
580 balances[main_account, ctx.caller], amount
581 )
582 assert balances[main_account] >= amount, "Not enough coins to send!"
583 balances[main_account, ctx.caller] -= amount
584 balances[main_account] -= amount
585 balances[to] += amount
586
587
588 def returnAndBurnVToken(amount: float):
589 user = ctx.caller
590 assert (
591 balances[user] >= amount
592 ), "Your VTOKEN balance is too low to unstake, recover your VTOKENS and try again."
593 balances[user] -= amount
594
595
596 def mintVToken(amount: float):
597 user = ctx.signer
598 balances[user] += amount

Byte Code

e3000000000000000000000000060000004000000073be030000640064016c005a0065015a0265005a0365005a0465056402640364048d025a0665056402640564048d025a0765056402640664048d025a0865056402640764048d025a0965056402640864048d025a0a65056402640964048d025a0b65056402640a64048d025a0c650d640b6402640c640d8d035a0e650d64006402640e640d8d035a0f65056402640f64048d025a10650d640b64026410640d8d035a1165056402641164048d025a1265056402641264048d025a1365056402641364048d025a1465056402641464048d025a15650d640b64026415640d8d035a1665056402641664048d025a1765056402641764048d025a18650d640064026418640d8d035a196419641a84005a1a651b64028301651c641b9c01641c641d840483015a1d651b64028301651e641e9c01641f6420840483015a1f651c652064219c026422642384045a21651c652064219c026424642584045a22651c651e651e64269c036427642884045a23651b64028301651c641b9c016429642a840483015a24651b64028301642b642c840083015a25642d642e84005a266527642f9c016430643184045a286432643384005a29651c64349c016435643684045a2a651c651c64379c026438643984045a2b651c64349c01643a643b84045a2c651b64028301651c643c9c01643d643e840483015a2d651b64028301651c643f9c0164406441840483015a2e651b64028301651c64429c0164436444840483015a2f651c64459c016446644784045a30651b64028301651e64489c016449644a840483015a31651b64028301651e64489c01644b644c840483015a32651b64028301651c641b9c01644d644e840483015a33651c641b9c01644f645084045a34651b64028301651e641e9c0164516452840483015a35651b64028301651e641e9c0164536454840483015a36651b6402830164556456840083015a37651b64028301652064579c0164586459840483015a38651b640283016539653965396539645a9c04645b645c840483015a3a651b640283016539653965396539645a9c04645d645e840483015a3b645f646084005a3c651b6402830164616462840083015a3d651b64028301652064639c0164646465840483015a3e653964669c016467646884045a3f651b64028301654064699c01646a646b840483015a41651b64028301651c651e646c9c02646d646e840483015a42651b64028301651c651e646c9c02646f6470840483015a43651b64028301651c651e651e64719c0364726473840483015a44651c641b9c016474647584045a45651c641b9c016476647784045a46640153002978e9000000004eda20636f6e5f7374616b696e675f727377705f727377705f696e7465726f705f7632da054f776e65722902da08636f6e7472616374da046e616d65da0f44657652657761726457616c6c6574da13456d697373696f6e52617465506572486f7572da0c446576526577617264506374da09537461727454696d65da07456e6454696d65da0f4f70656e466f72427573696e65737346da084465706f736974732903da0d64656661756c745f76616c756572040000007205000000da0b5769746864726177616c73da1143757272656e7445706f6368496e646578da0645706f636873da0d5374616b656442616c616e6365da1057697468647261776e42616c616e6365da0c45706f63684d696e54696d65da1545706f63684d6178526174696f496e637265617365da046d657461da15646563696d616c5f636f6e7665727465725f766172da10547275737465644578706f7274657273da0862616c616e63657363000000000000000000000000070000004300000073d600000074006a0174026a038301010074046a0174026a038301010074056a0164018301010074066a0164018301010074076a0164018301010074086a0164028301010074096a01640383010100740a6a01670083010100740b6401640464059c03740c64013c006406740d64073c006408740d64093c00640a740d640b3c00640a740d640c3c00740e6a01640483010100740f6a0164168301010074106a0174116a11640e640f6410641164128d048301010074126a0174116a11641364146414641164128d048301010074136a016415830101006400530029174e7201000000e90a00000069f04f010069b20d00002903da0474696d65da067374616b6564da0a616d745f7065725f68727a05302e302e32da0776657273696f6eda137374616b696e675f736d6172745f65706f6368da0474797065da0f636f6e5f727377705f6c7374303031da0d5354414b494e475f544f4b454eda0b5949454c445f544f4b454ee90100000069e5070000e907000000e91e000000e9160000002904da0479656172da056d6f6e7468da03646179da04686f757269e6070000e90500000054679a9999999999b93f2914da075f5f4f776e6572da03736574da03637478da0663616c6c6572da115f5f44657652657761726457616c6c6574da135f5f43757272656e7445706f6368496e646578da0f5f5f5374616b656442616c616e6365da125f5f57697468647261776e42616c616e6365da175f5f45706f63684d6178526174696f496e637265617365da0e5f5f45706f63684d696e54696d65da125f5f547275737465644578706f7274657273da036e6f77da085f5f45706f636873da065f5f6d657461da155f5f456d697373696f6e52617465506572486f7572da0e5f5f446576526577617264506374da0b5f5f537461727454696d65da086461746574696d65da095f5f456e6454696d65da115f5f4f70656e466f72427573696e657373a90072400000007240000000da00da045f5f5f5f2c000000732400000000010c010c010a010a010a010a010a010a01100108010801080108010a010a011801180172420000002901da06616d6f756e74630100000000000000030000000400000043000000733200000074006a017d0174027c0119007d027c0264016b08722274037c00640164028d02530074047c00640164028d0253006400530029034e4629027243000000da0d66726f6d5f636f6e74726163742905722e000000722f000000da0a5f5f4465706f73697473da125f5f6372656174654e65774465706f736974da115f5f696e6372656173654465706f73697429037243000000da0475736572da076465706f736974724000000072400000007241000000da106164645374616b696e67546f6b656e7341000000730a00000000020601080108010c02724a0000002901720400000063010000000000000005000000040000004300000073580000007c0074006a0183006b067314740264018301820174036a047c0083017d017c016a0583007d0274066a077d0374087c0319007d047c0464026b08724874097c02640364048d025300740a7c02640364048d0253006400530029054e7a3254686520636f6e7472616374206973206e6f7420696e207468652074727573746564206578706f7274657273206c6973742e4654290272430000007244000000290b7236000000da03676574da0e417373657274696f6e4572726f72da0149da0d696d706f72745f6d6f64756c65da1c6578706f72745969656c64546f466f726569676e436f6e7472616374722e000000da067369676e657272450000007246000000724700000029057204000000da0e7969656c645f636f6e7472616374724300000072480000007249000000724000000072400000007241000000da187374616b6546726f6d436f6e747261637450726f666974734b000000731200000000020e0106010a0108010601080108010c027252000000290272430000007244000000630200000000000000060000000500000043000000738a00000074006a01830064016b02731474026402830182017c0064036b047324740264048301820174036a047d027c0164056b08724474056a067c0074036a077c0264068d03010074086a0183007d037c037c0017007d0474086a097c0483010100740a7c0464078d017d057c05740b7c0064089c03740c7c023c00740d7c0064098d010100740c7c0219005300290a4e547a2854686973207374616b696e6720706f6f6c206973206e6f74206f70656e207269676874206e6f772e72010000007a19596f75206d757374207374616b6520736f6d657468696e672e4629037243000000da02746fda0c6d61696e5f6163636f756e742901da116e65775f7374616b65645f616d6f756e742903da0e7374617274696e675f65706f6368721a000000724300000029017243000000290e723f000000724b000000724c000000722e000000722f0000007221000000da0d7472616e736665725f66726f6dda04746869737232000000722d000000da165f5f646563696465496e6372656d656e7445706f636872370000007245000000da0c5f5f6d696e7456546f6b656e2906724300000072440000007248000000721b0000007255000000da0b65706f63685f696e646578724000000072400000007241000000724600000059000000731e000000000106010e011001060108010a010801080108010a010a0104010c010a0172460000006302000000000000000d0000000500000043000000734c01000074006a017d0274026a03830064016b02731a74046402830182017c0064036b05732a740464048301820174057c0219007d037c0364056b09734274046406830182017c0064036b0472647c0164056b08726474066a077c0074006a087c0264078d03010074097c0219007d047c03640819007d0564057d0664037d077c07740a7c0364098d0137007d077c03640a19007d067c03640819007d0564037d087c077c0438007d077c0764036b0472dc7c07740b6a03830014007d097c0964036b0472d4740c6a0d740e6a0383007c09640b8d0201007c077c0918007d087c087c0517007c0017007d0a740f6a0383007d0b7c0b7c0817007c0017007d0c740f6a107c0c8301010074116a1074116a0383007c0717008301010074127c087c001700640c8d010100640374097c023c0074137c0c640d8d0174147c0a640e9c0374057c023c0074057c0219005300290f4e547a2854686973207374616b696e6720706f6f6c206973206e6f74206f70656e207269676874206e6f772e72010000007a24596f752063616e6e6f74207374616b652061206e656761746976652062616c616e63652e467a2354686973207573657220686173206e6f206465706f73697420746f2061646420746f2e2903724300000072530000007254000000724300000029017249000000721a000000290272530000007243000000290172430000002901725500000029037256000000721a00000072430000002915722e000000722f000000723f000000724b000000724c0000007245000000722100000072570000007258000000da0d5f5f5769746864726177616c73da105f5f63616c63756c6174655969656c64723b0000007222000000da087472616e7366657272300000007232000000722d0000007233000000725a00000072590000007237000000290d7243000000724400000072480000007249000000da0f77697468647261776e5f7969656c64da0e6578697374696e675f7374616b65da0a73746172745f74696d65da107969656c645f746f5f68617276657374da10757365725f7969656c645f7368617265da096465765f7368617265da14746f74616c5f6465706f7369745f616d6f756e74da14676c6f62616c5f616d6f756e745f7374616b6564da116e65775f676c6f62616c5f7374616b656472400000007240000000724100000072470000006b00000073440000000001060106010e0110010801100110010a01080108010801040104010e01080108010401080108010c010801120108010c0108010c010a0112010e010801020108010c01724700000029037243000000da0674617267657472480000006303000000000000000a000000040000004300000073bc00000074007c0219007d037c0364016b097318740164028301820174027c0219007d0464037d057c0574037c0364048d0137007d057c057c0438007d057c007c056b0072467c006e027c057d067c0664036b04735a74016405830182017c0674046a05830014007d077c0764036b04728074066a0774086a0583007c0764068d0201007c067c0718007d0874066a077c017c0864068d0201007c047c06170074027c023c0074096a0583007c0617007d0974096a0a7c09830101007c08530029074e467a2b596f752068617665206e6f206465706f73697420746f207769746864726177207969656c642066726f6d2e7201000000290172490000007a295468657265206973206e6f207969656c6420746f2068617276657374207269676874206e6f77203a28290272530000007243000000290b7245000000724c000000725c000000725d000000723b000000724b0000007222000000725e00000072300000007233000000722d000000290a7243000000726800000072480000007249000000725f000000da116861727665737461626c655f7969656c6472620000007264000000da0a757365725f7368617265da146e65775f77697468647261776e5f616d6f756e74724000000072400000007241000000da135f5f73656e645969656c64546f546172676574900000007324000000000108011001080104010e0108010c01040110010c010801120108010e010c010c010a01726c00000063010000000000000002000000050000004300000073240000007c0064016b047310740064028301820174016a027d0174037c007c017c0164038d03530029044e72010000007a25596f752063616e6e6f7420686172766573742061206e656761746976652062616c616e636529037243000000726800000072480000002904724c000000722e000000722f000000726c000000290272430000007248000000724000000072400000007241000000da0d77697468647261775969656c64a50000007306000000000210010601726d00000063000000000000000009000000040000004300000073fa00000074006a017d0074027c0019007d017c0164016b09731e740364028301820174047c0019007d0264037d0364037d0464037d057c0474057c0164048d0137007d047c037c016405190037007d0374066a077c007c0364068d02010074087c0364078d0101007c047c0238007d047c0464036b0472b07c0474096a0a830014007d067c0664036b04729a740b6a07740c6a0a83007c0664068d0201007c047c0618007d05740b6a077c007c0564068d020100640174027c003c00640374047c003c00740d6a0a83007c0318007d07740d6a0e7c0783010100740f6a0a83007c0417007d08740f6a0e7c088301010074107c0764088d0101007c05530029094e467a1f596f752068617665206e6f206465706f73697420746f207769746864726177720100000029017249000000724300000029027253000000724300000029017243000000290172550000002911722e000000722f0000007245000000724c000000725c000000725d0000007221000000725e000000da155f5f72657475726e416e644275726e56546f6b656e723b000000724b000000722200000072300000007232000000722d00000072330000007259000000290972480000007249000000725f000000da0f7374616b655f746f5f72657475726e7262000000726a00000072640000007255000000726b000000724000000072400000007241000000da167769746864726177546f6b656e73416e645969656c64ac0000007334000000000206010801100108010401040104010e010c010e010a01080108010c010801120108010e01080108010c010a010c010a010a0172700000006301000000000000000e0000000400000043000000732a0100007c006a00640183017d017c006a00640283017d027c006a00640383017d03740183007d047c017d0564047d0678f87c057c046b019001722474027c0519007d0774027c056405170019007d0864047d097c017c046b02726a74037404830174037c02830118007d096e547c057c016b02728874037c0864021900830174037c02830118007d096e367c057c046b0272a674037404830174037c0764021900830118007d096e1874037c0864021900830174037c0764021900830118007d0964047d0a7c0364046b0972e27c076406190064046b0972e27c037c07640619001b007d0a7c07640719007d0b7c096a0574067c0b830114007d0c74076a087c0a8301010074076a0083007d0a7c0c7c0a14007d0d7c067c0d37007d067c05640537007d05712e57007c06530029084e7256000000721a000000724300000072010000007223000000721b000000721c0000002909724b000000da165f5f67657443757272656e7445706f6368496e6465787238000000da105f5f66697454696d65546f52616e67657237000000da077365636f6e6473da1a5f5f676574456d697373696f6e526174655065725365636f6e64da175f5f646563696d616c5f636f6e7665727465725f766172722d000000290e7249000000da147374617274696e675f65706f63685f696e646578da126465706f7369745f73746172745f74696d657243000000da1363757272656e745f65706f63685f696e646578da10746869735f65706f63685f696e646578da0179da0a746869735f65706f6368da0a6e6578745f65706f6368da0564656c7461da127063745f73686172655f6f665f7374616b65da16656d697373696f6e5f726174655f7065725f686f7572da17676c6f62616c5f7969656c645f746869735f65706f6368da186465706f7369745f7969656c645f746869735f65706f6368724000000072400000007241000000725d000000ca000000733e00000000010a010a010a010601040104010c0108010c0104010801120208010c010a01080116030c010c01040114010c010801060108010a010801080108010c01725d0000002901721a000000630100000000000000010000000200000043000000732e0000007c0074006a0183006b00721674006a0183007d006e147c0074026a0183006b04722a74026a0183007d007c00530029014e2903723c000000724b000000723e0000002901721a0000007240000000724000000072410000007272000000ef000000730a00000000010c010a010c0108017272000000630000000000000000010000000100000043000000730c00000074006a0183007d007c00530029014e29027231000000724b000000290172780000007240000000724000000072410000007271000000f70000007304000000000108017271000000290172550000006301000000000000000600000004000000430000007362000000740083007d0174017c0119007d027c02640119007d0374027c026402190018007d047c046a0364036b0472327c046a036e0264037d057c0574046a0583006b0573567c0364036b08735674067c007c0364048d02725e74077c0083017d017c01530029054e721b000000721a000000720100000029027255000000da11746869735f65706f63685f7374616b6564290872710000007238000000723700000072730000007235000000724b000000da1e5f5f6d61785374616b65644368616e6765526174696f4578636565646564da105f5f696e6372656d656e7445706f636829067255000000725b000000727b0000007282000000727d000000da0d64656c74615f7365636f6e64737240000000724000000072410000007259000000fc000000731600000000010601080108010c0114010c010a01020108010801725900000029027255000000728200000063020000000000000005000000030000004300000073480000007c007c016b01720c7c006e027c017d027c007c016b05721c7c006e027c017d037c037c0218007d047c017400640183016b007238740153007c047c011b0074026a0383006b05530029024e7a06302e303030312904da07646563696d616cda04747275657234000000724b000000290572550000007282000000da07736d616c6c6572da06626967676572da0364696672400000007240000000724100000072830000000a010000731000000000020c0104010c01040108010c0104017283000000630100000000000000030000000400000043000000733600000074006a0183007d017c01640117007d0274006a027c028301010074037c0074047c0119006402190064039c0374047c023c007c02530029044e7223000000721c0000002903721a000000721b000000721c00000029057231000000724b000000722d0000007237000000723800000029037255000000da0d63757272656e745f65706f6368da0d6e65775f65706f63685f696478724000000072400000007241000000728400000016010000730c0000000001080108010a010401140172840000002901da0f616d6f756e745f7065725f686f75726301000000000000000300000004000000430000007340000000740083000100740183007d017c01640117007d0274026a037c028301010074047c0064028d010100740574066a0783007c0064039c0374087c023c006400530029044e7223000000290172430000002903721a000000721b000000721c0000002909da0d5f5f6173736572744f776e657272710000007231000000722d000000da185f5f736574456d697373696f6e52617465506572486f757272370000007232000000724b00000072380000002903728d000000728b000000728c000000724000000072400000007241000000da136368616e6765416d6f756e74506572486f75721f010000730e00000000020601060108010a010a01080172900000002901da0b6d696e5f7365636f6e647363010000000000000001000000020000004300000073240000007400830001007c0064016b057316740164028301820174026a037c00830101006400530029034e72010000007a21796f75206d7573742063686f6f7365206120706f7369746976652076616c75652e2904728e000000724c0000007235000000722d00000029017291000000724000000072400000007241000000da0f73657445706f63684d696e54696d652a010000730600000000020601100172920000002901da05726174696f63010000000000000001000000020000004300000073240000007400830001007c0064016b047316740164028301820174026a037c00830101006400530029034e72010000007a186d757374206265206120706f7369746976652076616c75652904728e000000724c0000007234000000722d00000029017293000000724000000072400000007241000000da1873657445706f63684d6178526174696f496e63726561736531010000730600000000020601100172940000002901727f00000063010000000000000003000000020000004300000073140000007c0064011b007d017c0164011b007d027c02530029024ee93c00000072400000002903727f000000da18656d697373696f6e5f726174655f7065725f6d696e757465da18656d697373696f6e5f726174655f7065725f7365636f6e64724000000072400000007241000000727400000038010000730600000000010801080172740000002901da02766b630100000000000000010000000200000043000000731400000074008300010074016a027c00830101006400530029014e2903728e000000722c000000722d00000029017298000000724000000072400000007241000000da087365744f776e65723e0100007304000000000206017299000000630100000000000000010000000200000043000000731400000074008300010074016a027c00830101006400530029014e2903728e0000007230000000722d00000029017298000000724000000072400000007241000000da0c73657444657657616c6c657444010000730400000000020601729a000000630100000000000000010000000200000043000000732c0000007400830001007c0064016b0072167c0064026b05731e740164038301820174026a037c00830101006400530029044e722300000072010000007a26416d6f756e74206d75737420626520612076616c7565206265747765656e203020616e6420312904728e000000724c000000723b000000722d00000029017243000000724000000072400000007241000000da0f7365744465765265776172645063744a0100007306000000000206011801729b000000630100000000000000010000000200000043000000731400000074008300010074016a027c00830101006400530029014e2903728e000000723a000000722d00000029017243000000724000000072400000007241000000728f00000051010000730400000000010601728f000000630100000000000000020000000200000043000000733200000074008300010074016a0283007d017c007c016b06721a640053007c016a037c008301010074016a047c01830101006400530029014e2905728e0000007236000000724b000000da06617070656e64722d00000029027204000000da11747275737465645f6578706f7274657273724000000072400000007241000000da15616464546f547275737465644578706f727465727356010000730c000000000206010801080104010a01729e000000630100000000000000020000000200000043000000732600000074008300010074016a0283007d017c016a037c008301010074016a047c01830101006400530029014e2905728e0000007236000000724b000000da0672656d6f7665722d00000029027204000000729d000000724000000072400000007241000000da1a72656d6f766546726f6d547275737465644578706f72746572736001000073080000000002060108010a0172a0000000630000000000000000040000000600000043000000734a00000074008300010074016a0283007d00740374046401190064026403640464058d047d017c0174056a0619007d027c027c0018007d0374076a087c0374096a02830064068d0201006400530029074e722200000072180000007202000000da0e7969656c645f62616c616e6365732904da10666f726569676e5f636f6e7472616374da0c666f726569676e5f6e616d6572040000007205000000290272430000007253000000290a728e0000007232000000724b000000da0b466f726569676e486173687239000000722e00000072580000007222000000725e000000722c0000002904da0e7374616b65645f62616c616e6365da105f5f7969656c645f62616c616e636573da11746f74616c5f696e5f636f6e7472616374da0f746f74616c5f617661696c61626c65724000000072400000007241000000da117265636f7665725969656c64546f6b656e680100007310000000000206010801080102010a010a01080172a90000002901da0769735f6f70656e630100000000000000010000000200000043000000731400000074008300010074016a027c00830101006400530029014e2903728e000000723f000000722d000000290172aa000000724000000072400000007241000000da0c616c6c6f775374616b696e677401000073040000000002060172ab0000002904722700000072280000007229000000722a000000630400000000000000050000000500000043000000732400000074008300010074016a017c007c017c027c0383047d0474026a037c04830101006400530029014e2904728e000000723d000000723c000000722d0000002905722700000072280000007229000000722a000000721a000000724000000072400000007241000000da0c736574537461727454696d657a010000730600000000020601100172ac000000630400000000000000050000000500000043000000732400000074008300010074016a017c007c017c027c0383047d0474026a037c04830101006400530029014e2904728e000000723d000000723e000000722d0000002905722700000072280000007229000000722a000000721a000000724000000072400000007241000000da0a736574456e6454696d6581010000730600000000020601100172ad000000630000000000000000000000000200000043000000731a00000074006a01830074026a036b02731674046401830182016400530029024e7a2c596f75206d75737420626520746865206f776e657220746f2063616c6c20746869732066756e6374696f6e2e2905722c000000724b000000722e000000722f000000724c0000007240000000724000000072400000007241000000728e00000088010000730400000000010601728e000000630000000000000000040000000400000043000000737e00000074006a017d0074027c0019007d0174027c00190064016b097322740364028301820164037d027c027c016404190037007d0274046a057c007c0264058d02010074067c0264068d010100640174027c003c00640374077c003c0074086a0983007c0218007d0374086a0a7c0383010100740b7c0364078d0101006400530029084e467a2754686973206163636f756e7420686173206e6f206465706f7369747320746f2072657475726e2e720100000072430000002902725300000072430000002901724300000029017255000000290c722e000000722f0000007245000000724c0000007221000000725e000000726e000000725c0000007232000000724b000000722d0000007259000000290472480000007249000000726f0000007255000000724000000072400000007241000000da14656d657267656e637952657475726e5374616b658d010000731a00000000020601080106010e0104010c010e010a01080108010c010a0172ae0000002901da026f6e630100000000000000010000000200000043000000731400000074008300010074016a027c00830101006400530029014e2903728e000000da0b55736554696d6552616d70722d000000290172af000000724000000072400000007241000000da0e746f67676c6554696d6552616d709e01000073040000000002060172b10000002901da0464617973630100000000000000040000000300000043000000735a00000074006a0183007d0164007d0278287c0144005d207d037c03640119007c006b0172127c03640219007c006b0472127c037d02711257007c0264006b0872527c0174027c0183016403180019006404190053007c0264041900530029054eda056c6f776572da0575707065727223000000da0a6d756c7469706c6965722903da0e54696d6552616d7056616c756573724b000000da036c656e290472b2000000da0a74696d655f72616d7073da0473746570da0173724000000072400000007241000000da125f5f66696e6454696d6552616d7053746570a401000073100000000001080104010a01180108010801140172bb0000002901da0464617461630100000000000000010000000200000043000000731400000074008300010074016a027c00830101006400530029014e2903728e00000072b6000000722d000000290172bc000000724000000072400000007241000000da1173657454696d6552616d7056616c756573af01000073040000000002060172bd000000290272430000007253000000630200000000000000020000000400000043000000734c0000007c0064016b0473107400640283018201740174026a0319007c006b0573267400640383018201740174026a03050019007c00380003003c0074017c01050019007c00370003003c006400530029044e72010000007a1e43616e6e6f742073656e64206e656761746976652062616c616e636573217a1b4e6f7420656e6f7567682056544f4b454e5320746f2073656e64212904724c000000da0a5f5f62616c616e636573722e000000722f000000290272430000007253000000724000000072400000007241000000725e000000b501000073080000000002100116011201725e000000630200000000000000020000000400000043000000732a0000007c0064016b0473107400640283018201740174026a037c016602050019007c00370003003c006400530029034e72010000007a1e43616e6e6f742073656e64206e656761746976652062616c616e636573212904724c00000072be000000722e000000722f000000290272430000007253000000724000000072400000007241000000da07617070726f7665bd01000073040000000002100172bf0000002903724300000072530000007254000000630300000000000000030000000500000043000000738a0000007c0064016b047310740064028301820174017c0274026a03660219007c006b05733c740064036a0474017c0274026a03660219007c0083028301820174017c0219007c006b057350740064048301820174017c0274026a036602050019007c00380003003c0074017c02050019007c00380003003c0074017c01050019007c00370003003c006400530029054e72010000007a1e43616e6e6f742073656e64206e656761746976652062616c616e636573217a494e6f7420656e6f75676820636f696e7320617070726f76656420746f2073656e642120596f752068617665207b7d20616e642061726520747279696e6720746f207370656e64207b7d7a194e6f7420656e6f75676820636f696e7320746f2073656e64212905724c00000072be000000722e000000722f000000da06666f726d617429037243000000725300000072540000007240000000724000000072410000007257000000c30100007310000000000210010c010c0114011401160110017257000000630100000000000000020000000400000043000000732e00000074006a017d0174027c0119007c006b05731a740364018301820174027c01050019007c00380003003c006400530029024e7a4e596f75722056544f4b454e2062616c616e636520697320746f6f206c6f7720746f20756e7374616b652c207265636f76657220796f75722056544f4b454e5320616e642074727920616761696e2e2904722e000000722f00000072be000000724c000000290272430000007248000000724000000072400000007241000000726e000000cf01000073080000000001060106010e01726e000000630100000000000000020000000400000043000000731a00000074006a017d0174027c01050019007c00370003003c006400530029014e2903722e000000725000000072be000000290272430000007248000000724000000072400000007241000000725a000000d6010000730400000000010601725a00000029477220000000da09696d706f72746c6962724d00000072210000007222000000da085661726961626c65722c0000007230000000723a000000723b000000723c000000723e000000723f000000da04486173687245000000725c00000072310000007238000000723200000072330000007235000000723400000072390000007275000000723600000072be0000007242000000da085f5f6578706f7274da05666c6f6174724a000000da037374727252000000da04626f6f6c72460000007247000000726c000000726d0000007270000000725d000000da03416e797272000000727100000072590000007283000000728400000072900000007292000000729400000072740000007299000000729a000000729b000000728f000000729e00000072a000000072a900000072ab000000da03696e7472ac00000072ad000000728e00000072ae00000072b100000072bb000000da046c69737472bd000000725e00000072bf0000007257000000726e000000725a0000007240000000724000000072400000007241000000da083c6d6f64756c653e0100000073c400000008010401040104010c010401080102010a010401080104010801040108010401080104010a0104010a010401080104010a0104010801040108010401080102010a0104010a0102010a010401080104010a030815060110090601100d10121025121506011006101e08250e0808050e0e02010e0b0e090601100a06011006060110060e060601100506011005060110060e050601100906011007100c06011005060116060601160608051011060110050e0b0601100506011207060112050601140b0e07