Contract con_dex_example_1


Contract Code


  
1 import currency
2 I = importlib
3
4 # Enforceable interface
5 token_interface = [
6 I.Func('transfer', args=('amount', 'to')),
7 I.Func('approve', args=('amount', 'to')),
8 I.Func('transfer_from', args=('amount', 'to', 'main_account'))
9 ]
10
11 pairs = Hash()
12 prices = Hash(default_value=0)
13
14 lp_points = Hash(default_value=0)
15 reserves = Hash(default_value=[0, 0])
16
17 staked_amount = Hash(default_value=0)
18 discount = Hash(default_value=1)
19
20 state = Hash()
21
22 @construct
23 def seed(): #These are supposed to be constants, but they are changable
24 state["FEE_PERCENTAGE"] = 0.3 / 100
25 state["TOKEN_CONTRACT"] = "con_dex_token"
26 state["TOKEN_DISCOUNT"] = 0.75
27 state["BURN_PERCENTAGE"] = 0.8
28 state["BURN_ADDRESS"] = "0x0" #Change this
29 state["LOG_ACCURACY"] = 1000000000.0 #The stamp difference for a higher number should be unnoticable
30 state["MULTIPLIER"] = 0.05
31 state["DISCOUNT_FLOOR"] = 0.0
32
33 state["OWNER"] = ctx.caller
34
35 @export
36 def create_market(contract: str, currency_amount: float=0, token_amount: float=0):
37 assert pairs[contract] is None, 'Market already exists!'
38 assert currency_amount > 0 and token_amount > 0, 'Must provide currency amount and token amount!'
39
40 token = I.import_module(contract)
41
42 assert I.enforce_interface(token, token_interface), 'Invalid token interface!'
43
44 currency.transfer_from(amount=currency_amount, to=ctx.this, main_account=ctx.caller)
45 token.transfer_from(amount=token_amount, to=ctx.this, main_account=ctx.caller)
46
47 prices[contract] = currency_amount / token_amount
48
49 pairs[contract] = True
50
51 # Mint 100 liquidity points
52 lp_points[contract, ctx.caller] = 100
53 lp_points[contract] = 100
54
55 reserves[contract] = [currency_amount, token_amount]
56
57 return True
58
59 @export
60 def liquidity_balance_of(contract: str, account: str):
61 return lp_points[contract, account]
62
63 @export
64 def add_liquidity(contract: str, currency_amount: float=0):
65 assert pairs[contract] is True, 'Market does not exist!'
66
67 assert currency_amount > 0
68
69 token = I.import_module(contract)
70
71 assert I.enforce_interface(token, token_interface), 'Invalid token interface!'
72
73 # Determine the number of tokens required
74 token_amount = currency_amount / prices[contract]
75
76 # Transfer both tokens
77 currency.transfer_from(amount=currency_amount, to=ctx.this, main_account=ctx.caller)
78 token.transfer_from(amount=token_amount, to=ctx.this, main_account=ctx.caller)
79
80 # Calculate the LP points to mint
81 total_lp_points = lp_points[contract]
82 currency_reserve, token_reserve = reserves[contract]
83
84 points_per_currency = total_lp_points / currency_reserve
85 lp_to_mint = points_per_currency * currency_amount
86
87 # Update the LP points
88 lp_points[contract, ctx.caller] += lp_to_mint
89 lp_points[contract] += lp_to_mint
90
91 # Update the reserves
92 reserves[contract] = [currency_reserve + currency_amount, token_reserve + token_amount]
93
94 #Return amount of LP minted
95 return lp_to_mint
96
97 @export
98 def remove_liquidity(contract: str, amount: float=0):
99 assert pairs[contract] is True, 'Market does not exist!'
100
101 assert amount > 0, 'Must be a positive LP point amount!'
102 assert lp_points[contract, ctx.caller] >= amount, 'Not enough LP points to remove!'
103
104 token = I.import_module(contract)
105
106 assert I.enforce_interface(token, token_interface), 'Invalid token interface!'
107
108 lp_percentage = amount / lp_points[contract]
109
110 currency_reserve, token_reserve = reserves[contract]
111
112 currency_amount = currency_reserve * (lp_percentage)
113 token_amount = token_reserve * (lp_percentage)
114
115 currency.transfer(to=ctx.caller, amount=currency_amount)
116 token.transfer(to=ctx.caller, amount=token_amount)
117
118 lp_points[contract, ctx.caller] -= amount
119 lp_points[contract] -= amount
120
121 assert lp_points[contract] > 1, 'Not enough remaining liquidity!'
122
123 new_currency_reserve = currency_reserve - currency_amount
124 new_token_reserve = token_reserve - token_amount
125
126 assert new_currency_reserve > 0 and new_token_reserve > 0, 'Not enough remaining liquidity!'
127
128 reserves[contract] = [new_currency_reserve, new_token_reserve]
129
130 return currency_amount, token_amount
131
132 @export
133 def transfer_liquidity(contract: str, to: str, amount: float):
134 assert amount > 0, 'Must be a positive LP point amount!'
135 assert lp_points[contract, ctx.caller] >= amount, 'Not enough LP points to transfer!'
136
137 lp_points[contract, ctx.caller] -= amount
138 lp_points[contract, to] += amount
139
140 @export
141 def approve_liquidity(contract: str, to: str, amount: float):
142 assert amount > 0, 'Cannot send negative balances!'
143 lp_points[contract, ctx.caller, to] += amount
144
145 @export
146 def transfer_liquidity_from(contract: str, to: str, main_account: str, amount: float):
147 assert amount > 0, 'Cannot send negative balances!'
148
149 assert lp_points[contract, main_account, ctx.caller] >= amount, 'Not enough coins approved to send! You have ' \
150 '{} and are trying to spend {}'.format(lp_points[main_account, ctx.caller], amount)
151
152 assert lp_points[contract, main_account] >= amount, 'Not enough coins to send!'
153
154 lp_points[contract, main_account, ctx.caller] -= amount
155 lp_points[contract, main_account] -= amount
156
157 lp_points[contract, to] += amount
158
159 # Buy takes fee from the crypto being transferred in
160 @export
161 def buy(contract: str, currency_amount: float, minimum_received: float=0, token_fees: bool=False):
162 assert pairs[contract] is True, 'Market does not exist!'
163 assert currency_amount > 0, 'Must provide currency amount!'
164
165 token = I.import_module(contract)
166 amm_token = I.import_module(state["TOKEN_CONTRACT"])
167
168 assert I.enforce_interface(token, token_interface), 'Invalid token interface!'
169
170 if contract == state["TOKEN_CONTRACT"]:
171 currency.transfer_from(amount=currency_amount, to=ctx.this, main_account=ctx.caller)
172 tokens_purchased = internal_buy(contract=state["TOKEN_CONTRACT"], currency_amount=currency_amount)
173 token.transfer(amount=tokens_purchased, to=ctx.caller)
174
175 return tokens_purchased
176
177 currency_reserve, token_reserve = reserves[contract]
178 k = currency_reserve * token_reserve
179
180 new_currency_reserve = currency_reserve + currency_amount
181 new_token_reserve = k / new_currency_reserve
182
183 tokens_purchased = token_reserve - new_token_reserve
184
185 fee_percent = state["FEE_PERCENTAGE"] * discount[ctx.caller] #Discount is applied here
186 fee = tokens_purchased * fee_percent
187
188 if token_fees is True:
189 fee = fee * state["TOKEN_DISCOUNT"]
190
191 rswp_k = currency_reserve * token_reserve
192
193 rswp_new_token_reserve = token_reserve + fee
194 rswp_new_currency_reserve = rswp_k / rswp_new_token_reserve
195
196 rswp_currency_purchased = currency_reserve - rswp_new_currency_reserve # MINUS FEE
197 rswp_currency_purchased += rswp_currency_purchased * fee_percent
198
199
200 rswp_currency_reserve_2, rswp_token_reserve_2 = reserves[state["TOKEN_CONTRACT"]] #This converts fees in TAU to fees in RSWP
201 rswp_k_2 = rswp_currency_reserve_2 * rswp_token_reserve_2
202
203 rswp_new_currency_reserve_2 = rswp_currency_reserve_2 + rswp_currency_purchased
204 rswp_new_currency_reserve_2 += rswp_currency_purchased * fee_percent #Not 100% accurate, uses output currency instead of input currency
205 rswp_new_token_reserve_2 = rswp_k_2 / rswp_new_currency_reserve_2
206
207 sell_amount = rswp_token_reserve_2 - rswp_new_token_reserve_2 #SEMI-VOODOO MATH, PLEASE DOUBLE CHECK
208 sell_amount_with_fee = sell_amount * state["BURN_PERCENTAGE"]
209
210 amm_token.transfer_from(amount=sell_amount, to=ctx.this, main_account=ctx.caller)
211
212 currency_received = internal_sell(contract=state["TOKEN_CONTRACT"], token_amount=sell_amount_with_fee)
213 amm_token.transfer(amount=sell_amount - sell_amount_with_fee, to=state["BURN_ADDRESS"])
214
215 token_received = internal_buy(contract=contract, currency_amount=currency_received)
216
217 new_currency_reserve += reserves[contract][0] - currency_reserve
218 new_token_reserve += reserves[contract][1] - token_reserve
219
220 new_token_reserve = (new_token_reserve) + token_received #This can probably be removed during production
221
222 else:
223 tokens_purchased = (tokens_purchased) - fee
224 burn_amount = internal_buy(contract=state["TOKEN_CONTRACT"], currency_amount=internal_sell(contract=contract, token_amount=fee - fee * state["BURN_PERCENTAGE"]))
225
226 new_currency_reserve += reserves[contract][0] - currency_reserve
227 new_token_reserve += reserves[contract][1] - token_reserve
228
229 new_token_reserve = (new_token_reserve) + fee * state["BURN_PERCENTAGE"]
230 amm_token.transfer(amount=burn_amount, to=state["BURN_ADDRESS"]) #Burn here
231
232 if minimum_received != None:
233 assert tokens_purchased >= minimum_received, "Only {} tokens can be purchased, which is less than your minimum, which is {} tokens.".format(tokens_purchased, minimum_received)
234
235 assert tokens_purchased > 0, 'Token reserve error!'
236
237 currency.transfer_from(amount=currency_amount, to=ctx.this, main_account=ctx.caller)
238 token.transfer(amount=tokens_purchased, to=ctx.caller)
239
240 reserves[contract] = [new_currency_reserve, new_token_reserve]
241 prices[contract] = new_currency_reserve / new_token_reserve
242
243 return tokens_purchased
244
245 # Sell takes fee from crypto being transferred out
246 @export
247 def sell(contract: str, token_amount: float, minimum_received: float=0, token_fees: bool=False):
248 assert pairs[contract] is True, 'Market does not exist!'
249 assert token_amount > 0, 'Must provide currency amount and token amount!'
250
251 token = I.import_module(contract)
252 amm_token = I.import_module(state["TOKEN_CONTRACT"])
253
254 assert I.enforce_interface(token, token_interface), 'Invalid token interface!'
255
256 if contract == state["TOKEN_CONTRACT"]:
257 token.transfer_from(amount=token_amount, to=ctx.this, main_account=ctx.caller)
258 currency_purchased = internal_sell(contract=state["TOKEN_CONTRACT"], token_amount=token_amount)
259 currency.transfer(amount=currency_purchased, to=ctx.caller)
260
261 return currency_purchased
262
263 currency_reserve, token_reserve = reserves[contract]
264 k = currency_reserve * token_reserve
265
266 new_token_reserve = token_reserve + token_amount
267
268 new_currency_reserve = k / new_token_reserve
269
270 currency_purchased = currency_reserve - new_currency_reserve # MINUS FEE
271
272 fee_percent = state["FEE_PERCENTAGE"] * discount[ctx.caller] #Discount is applied here
273 fee = currency_purchased * fee_percent
274
275 if token_fees is True:
276 fee = fee * state["TOKEN_DISCOUNT"]
277 rswp_currency_reserve, rswp_token_reserve = reserves[state["TOKEN_CONTRACT"]]
278 rswp_k = rswp_currency_reserve * rswp_token_reserve
279
280 rswp_new_currency_reserve = rswp_currency_reserve + fee
281 rswp_new_currency_reserve += fee * fee_percent #Not 100% accurate, uses output currency instead of input currency
282 rswp_new_token_reserve = rswp_k / rswp_new_currency_reserve
283
284 sell_amount = rswp_token_reserve - rswp_new_token_reserve #SEMI-VOODOO MATH, PLEASE DOUBLE CHECK
285 sell_amount_with_fee = sell_amount * state["BURN_PERCENTAGE"]
286
287 amm_token.transfer_from(amount=sell_amount, to=ctx.this, main_account=ctx.caller)
288
289 currency_received = internal_sell(contract=state["TOKEN_CONTRACT"], token_amount=sell_amount_with_fee)
290 amm_token.transfer(amount=sell_amount - sell_amount_with_fee, to=state["BURN_ADDRESS"])
291
292 new_currency_reserve = (new_currency_reserve) + currency_received
293
294 else:
295 currency_purchased = (currency_purchased) - fee
296 burn_amount = fee - fee * state["BURN_PERCENTAGE"]
297
298 new_currency_reserve = (new_currency_reserve) + fee * state["BURN_PERCENTAGE"]
299 token_received = internal_buy(contract=state["TOKEN_CONTRACT"], currency_amount=burn_amount)
300 amm_token.transfer(amount=token_received, to=state["BURN_ADDRESS"]) #Buy and burn here
301
302 if minimum_received != None: #!= because the type is not exact
303 assert currency_purchased >= minimum_received, "Only {} TAU can be purchased, which is less than your minimum, which is {} TAU.".format(currency_purchased, minimum_received)
304
305 assert currency_purchased > 0, 'Token reserve error!'
306
307 token.transfer_from(amount=token_amount, to=ctx.this, main_account=ctx.caller)
308 currency.transfer(amount=currency_purchased, to=ctx.caller)
309
310 reserves[contract] = [new_currency_reserve, new_token_reserve]
311 prices[contract] = new_currency_reserve / new_token_reserve
312
313 return currency_purchased
314
315 @export
316 def stake(amount: float, token_contract: str=None):
317 assert amount >= 0, 'Must be a positive stake amount!'
318 if token_contract == None:
319 token_contract = state["TOKEN_CONTRACT"]
320 amm_token = I.import_module(token_contract)
321
322 current_balance = staked_amount[ctx.caller, token_contract]
323
324 if amount < current_balance:
325 amm_token.transfer(current_balance - amount, ctx.caller)
326 staked_amount[ctx.caller, token_contract] = amount #Rest of this can be abstracted in another function
327 discount_amount = state["LOG_ACCURACY"] * (staked_amount[ctx.caller, state["TOKEN_CONTRACT"]] ** (1 / state["LOG_ACCURACY"]) - 1) * state["MULTIPLIER"] - state["DISCOUNT_FLOOR"] #Calculates discount percentage
328 if discount_amount > 0.99: #Probably unnecessary, but added to prevent floating point and division by zero issues
329 discount_amount = 0.99
330 if discount_amount < 0:
331 discount_amount = 0
332 discount[ctx.caller] = 1 - discount_amount
333
334 return discount_amount
335
336 elif amount > current_balance: #Can replace with else, but this probably closes up a few edge cases like `if amount == current_balance`
337 amm_token.transfer_from(amount - current_balance, ctx.this, ctx.caller)
338 staked_amount[ctx.caller, token_contract] = amount
339 discount_amount = state["LOG_ACCURACY"] * (staked_amount[ctx.caller, state["TOKEN_CONTRACT"]] ** (1 / state["LOG_ACCURACY"]) - 1) * state["MULTIPLIER"] - state["DISCOUNT_FLOOR"]
340 if discount_amount > 0.99:
341 discount_amount = 0.99
342 if discount_amount < 0:
343 discount_amount = 0
344 discount[ctx.caller] = 1 - discount_amount
345
346 return discount_amount
347
348 @export
349 def change_state(key: str, new_value: str, convert_to_decimal: bool=False):
350 assert state["OWNER"] == ctx.caller, "Not the owner!"
351 if convert_to_decimal:
352 new_value = decimal(new_value)
353 state[key] = new_value
354
355 return new_value
356
357 @export
358 def change_state_float(key: str, new_value: float, convert_to_int: bool=False):
359 assert state["OWNER"] == ctx.caller, "Not the owner!"
360
361 if convert_to_int:
362 new_value = int(new_value)
363 state[key] = new_value
364
365 return new_value
366
367 @export
368 def sync_reserves(contract: str):
369 assert state["SYNC_ENABLED"] is True, "Sync is not enabled!"
370
371 token = I.import_module(contract)
372
373 new_balance = token.balance_of(ctx.this)
374 assert new_balance > 0, "Cannot be a negative balance!"
375 reserves[contract][1] = new_balance
376
377 return new_balance
378 # Internal use only
379 def internal_buy(contract: str, currency_amount: float):
380 assert pairs[contract] is True, 'RSWP Market does not exist!'
381
382 if currency_amount <= 0:
383 return 0
384
385 token = I.import_module(contract)
386
387 assert I.enforce_interface(token, token_interface), 'Invalid token interface!'
388
389 currency_reserve, token_reserve = reserves[contract]
390 k = currency_reserve * token_reserve
391
392 new_currency_reserve = currency_reserve + currency_amount
393 new_token_reserve = k / new_currency_reserve
394
395 tokens_purchased = token_reserve - new_token_reserve
396
397 fee = tokens_purchased * state["FEE_PERCENTAGE"]
398
399 tokens_purchased -= fee
400 new_token_reserve += fee
401
402 assert tokens_purchased > 0, 'Token reserve error!'
403
404 reserves[contract] = [new_currency_reserve, new_token_reserve]
405 prices[contract] = new_currency_reserve / new_token_reserve
406
407 return tokens_purchased
408
409 # Internal use only
410 def internal_sell(contract: str, token_amount: float):
411 assert pairs[contract] is True, 'RSWP Market does not exist!'
412 if token_amount <= 0:
413 return 0
414
415 token = I.import_module(contract)
416
417 assert I.enforce_interface(token, token_interface), 'Invalid token interface!'
418
419 currency_reserve, token_reserve = reserves[contract]
420 k = currency_reserve * token_reserve
421
422 new_token_reserve = token_reserve + token_amount
423
424 new_currency_reserve = k / new_token_reserve
425
426 currency_purchased = currency_reserve - new_currency_reserve # MINUS FEE
427
428 fee = currency_purchased * state["FEE_PERCENTAGE"]
429
430 currency_purchased -= fee
431 new_currency_reserve += fee
432
433 assert currency_purchased > 0, 'Token reserve error!'
434
435 reserves[contract] = [new_currency_reserve, new_token_reserve]
436 prices[contract] = new_currency_reserve / new_token_reserve
437
438 return currency_purchased

Byte Code

e30000000000000000000000000700000040000000731e020000640064016c005a0065015a0265026a036402644264058d0265026a036406644364058d0265026a036407644464058d0267035a0465056409640a640b8d025a06650564006409640c640d8d035a07650564006409640e640d8d035a0865056400640067026409640f640d8d035a096505640064096410640d8d035a0a6505641164096412640d8d035a0b650564096413640b8d025a0c6414641584005a0d650e640983016445650f6510651064169c0364176418840583015a11650e64098301650f650f64199c02641a641b840483015a12650e640983016446650f6510641c9c02641d641e840583015a13650e640983016447650f6510641f9c0264206421840583015a14650e64098301650f650f651064229c0364236424840483015a15650e64098301650f650f651064229c0364256426840483015a16650e64098301650f650f650f651064279c0464286429840483015a17650e640983016448650f651065106518642b9c04642c642d840583015a19650e640983016449650f651065106518642e9c04642f6430840583015a1a650e64098301644a6510650f64319c0264326433840583015a1b650e64098301644b650f650f651864349c0364356436840583015a1c650e64098301644c650f6510651864379c0364386439840583015a1d650e64098301650f643a9c01643b643c840483015a1e650f6510641c9c02643d643e84045a1f650f6510643f9c026440644184045a2064015300294de9000000004eda087472616e73666572da06616d6f756e74da02746f2901da0461726773da07617070726f7665da0d7472616e736665725f66726f6dda0c6d61696e5f6163636f756e74da11636f6e5f6465785f6578616d706c655f31da0570616972732902da08636f6e7472616374da046e616d65da067072696365732903da0d64656661756c745f76616c7565720b000000720c000000da096c705f706f696e7473da087265736572766573da0d7374616b65645f616d6f756e74e901000000da08646973636f756e74da057374617465630000000000000000000000000300000043000000736a00000074006401830164021b00740164033c006404740164053c00740064068301740164073c00740064088301740164093c00640a7401640b3c007400640c83017401640d3c007400640e83017401640f3c00740064108301740164113c0074026a03740164123c006400530029134e7a03302e33e964000000da0e4645455f50455243454e54414745da0d636f6e5f6465785f746f6b656eda0e544f4b454e5f434f4e54524143547a04302e3735da0e544f4b454e5f444953434f554e547a03302e38da0f4255524e5f50455243454e54414745da03307830da0c4255524e5f414444524553537a0c313030303030303030302e30da0c4c4f475f41434355524143597a04302e3035da0a4d554c5449504c4945527a03302e30da0e444953434f554e545f464c4f4f52da054f574e45522904da07646563696d616cda075f5f7374617465da03637478da0663616c6c6572a90072250000007225000000da00da045f5f5f5f1300000073120000000001100108010c010c0108010c010c010c0172270000002903720b000000da0f63757272656e63795f616d6f756e74da0c746f6b656e5f616d6f756e7463030000000000000004000000050000004300000073ac00000074007c00190064006b08731474016401830182017c0164026b0472247c0264026b04732c740164038301820174026a037c0083017d0374026a047c0374058302734a740164048301820174066a077c0174086a0974086a0a64058d0301007c036a077c0274086a0974086a0a64058d0301007c017c021b00740b7c003c00640674007c003c006407740c7c0074086a0a66023c006407740c7c003c007c017c026702740d7c003c006406530029084e7a164d61726b657420616c7265616479206578697374732172010000007a2e4d7573742070726f766964652063757272656e637920616d6f756e7420616e6420746f6b656e20616d6f756e74217a18496e76616c696420746f6b656e20696e74657266616365212903720300000072040000007208000000547215000000290eda075f5f7061697273da0e417373657274696f6e4572726f72da0149da0d696d706f72745f6d6f64756c65da11656e666f7263655f696e74657266616365da0f746f6b656e5f696e74657266616365da0863757272656e637972070000007223000000da04746869737224000000da085f5f707269636573da0b5f5f6c705f706f696e7473da0a5f5f72657365727665732904720b00000072280000007229000000da05746f6b656e722500000072250000007226000000da0d6372656174655f6d61726b65741f000000731c0000000003140118010a010e0106010a010a0114020c0108010e0108010c0172360000002902720b000000da076163636f756e74630200000000000000020000000300000043000000730c00000074007c007c0166021900530029014e290172330000002902720b0000007237000000722500000072250000007226000000da146c69717569646974795f62616c616e63655f6f66330000007302000000000272380000002902720b000000722800000063020000000000000009000000050000004300000073d400000074007c00190064016b08731474016402830182017c0164036b0473207401820174026a037c0083017d0274026a047c0274058302733e74016404830182017c0174067c0019001b007d0374076a087c0174096a0a74096a0b64058d0301007c026a087c0374096a0a74096a0b64058d030100740c7c0019007d04740d7c0019005c027d057d067c047c051b007d077c077c0114007d08740c7c0074096a0b6602050019007c08370003003c00740c7c00050019007c08370003003c007c057c0117007c067c0317006702740d7c003c007c08530029064e547a164d61726b657420646f6573206e6f742065786973742172010000007a18496e76616c696420746f6b656e20696e74657266616365212903720300000072040000007208000000290e722a000000722b000000722c000000722d000000722e000000722f000000723200000072300000007207000000722300000072310000007224000000723300000072340000002909720b000000722800000072350000007229000000da0f746f74616c5f6c705f706f696e7473da1063757272656e63795f72657365727665da0d746f6b656e5f72657365727665da13706f696e74735f7065725f63757272656e6379da0a6c705f746f5f6d696e74722500000072250000007226000000da0d6164645f6c6971756964697479380000007324000000000214010c010a010e0106010c010a010a01140208010c01080108011601100106010e01723e0000002902720b00000072030000006302000000000000000a0000000400000043000000731c01000074007c00190064016b08731474016402830182017c0164036b047324740164048301820174027c0074036a04660219007c016b05733e740164058301820174056a067c0083017d0274056a077c0274088302735c74016406830182017c0174027c0019001b007d0374097c0019005c027d047d057c047c0314007d067c057c0314007d07740a6a0b74036a047c0664078d0201007c026a0b74036a047c0764078d02010074027c0074036a046602050019007c01380003003c0074027c00050019007c01380003003c0074027c00190064086b0473de74016409830182017c047c0618007d087c057c0718007d097c0864036b046ffc7c0964036b049001730874016409830182017c087c09670274097c003c007c067c0766025300290a4e547a164d61726b657420646f6573206e6f742065786973742172010000007a234d757374206265206120706f736974697665204c5020706f696e7420616d6f756e74217a1f4e6f7420656e6f756768204c5020706f696e747320746f2072656d6f7665217a18496e76616c696420746f6b656e20696e746572666163652129027204000000720300000072120000007a1f4e6f7420656e6f7567682072656d61696e696e67206c697175696469747921290c722a000000722b000000723300000072230000007224000000722c000000722d000000722e000000722f000000723400000072300000007202000000290a720b00000072030000007235000000da0d6c705f70657263656e74616765723a000000723b00000072280000007229000000da146e65775f63757272656e63795f72657365727665da116e65775f746f6b656e5f72657365727665722500000072250000007226000000da1072656d6f76655f6c69717569646974794f000000732a0000000002140110010c010e010a010e0106010c010c010801080110011001160110011401080108011a010c0172420000002903720b0000007204000000720300000063030000000000000003000000040000004300000073580000007c0264016b047310740064028301820174017c0074026a03660219007c026b05732a740064038301820174017c0074026a036602050019007c02380003003c0074017c007c016602050019007c02370003003c006400530029044e72010000007a234d757374206265206120706f736974697665204c5020706f696e7420616d6f756e74217a214e6f7420656e6f756768204c5020706f696e747320746f207472616e73666572212904722b0000007233000000722300000072240000002903720b00000072040000007203000000722500000072250000007226000000da127472616e736665725f6c697175696469747968000000730a000000000210010c010e0116017243000000630300000000000000030000000400000043000000732c0000007c0264016b047310740064028301820174017c0074026a037c016603050019007c02370003003c006400530029034e72010000007a1e43616e6e6f742073656e64206e656761746976652062616c616e636573212904722b0000007233000000722300000072240000002903720b00000072040000007203000000722500000072250000007226000000da11617070726f76655f6c69717569646974797100000073040000000002100172440000002904720b000000720400000072080000007203000000630400000000000000040000000500000043000000739a0000007c0364016b047310740064028301820174017c007c0274026a03660319007c036b05733e740064036a0474017c0274026a03660219007c0383028301820174017c007c02660219007c036b057356740064048301820174017c007c0274026a036603050019007c03380003003c0074017c007c026602050019007c03380003003c0074017c007c016602050019007c03370003003c006400530029054e72010000007a1e43616e6e6f742073656e64206e656761746976652062616c616e636573217a494e6f7420656e6f75676820636f696e7320617070726f76656420746f2073656e642120596f752068617665207b7d20616e642061726520747279696e6720746f207370656e64207b7d7a194e6f7420656e6f75676820636f696e7320746f2073656e64212905722b000000723300000072230000007224000000da06666f726d61742904720b000000720400000072080000007203000000722500000072250000007226000000da177472616e736665725f6c69717569646974795f66726f6d770000007312000000000310010e010c0114010a010e01180114017246000000462904720b0000007228000000da106d696e696d756d5f7265636569766564da0a746f6b656e5f666565736304000000000000001c000000080000004300000073c802000074007c00190064016b08731474016402830182017c0164036b047324740164048301820174026a037c0083017d0474026a0374046405190083017d0574026a057c0474068302735074016406830182017c007404640519006b02729474076a087c0174096a0a74096a0b64078d030100740c7404640519007c0164088d027d067c046a0d7c0674096a0b64098d0201007c065300740e7c0019005c027d077d087c077c0814007d097c077c0117007d0a7c097c0a1b007d0b7c087c0b18007d067404640a1900740f74096a0b190014007d0c7c067c0c14007d0d7c0364016b08900172dc7c0d7404640b190014007d0d7c077c0814007d0e7c087c0d17007d0f7c0e7c0f1b007d107c077c1018007d117c117c117c0c140037007d11740e74046405190019005c027d127d137c127c1314007d147c127c1117007d157c157c117c0c140037007d157c147c151b007d167c137c1618007d177c177404640c190014007d187c056a087c1774096a0a74096a0b64078d03010074107404640519007c18640d8d027d197c056a0d7c177c1818007404640e190064098d020100740c7c007c1964088d027d1a7c0a740e7c001900640319007c07180037007d0a7c0b740e7c001900640f19007c08180037007d0b7c0b7c1a17007d0b6e767c067c0d18007d06740c74046405190074107c007c0d7c0d7404640c190014001800640d8d0264088d027d1b7c0a740e7c001900640319007c07180037007d0a7c0b740e7c001900640f19007c08180037007d0b7c0b7c0d7404640c1900140017007d0b7c056a0d7c1b7404640e190064098d0201007c0264006b03900272767c067c026b0590027376740164106a117c067c028302830182017c0664036b0490027388740164118301820174076a087c0174096a0a74096a0b64078d0301007c046a0d7c0674096a0b64098d0201007c0a7c0b6702740e7c003c007c0a7c0b1b0074127c003c007c06530029124e547a164d61726b657420646f6573206e6f742065786973742172010000007a1d4d7573742070726f766964652063757272656e637920616d6f756e742172180000007a18496e76616c696420746f6b656e20696e746572666163652129037203000000720400000072080000002902720b000000722800000029027203000000720400000072160000007219000000721a0000002902720b0000007229000000721c00000072120000007a554f6e6c79207b7d20746f6b656e732063616e206265207075726368617365642c207768696368206973206c657373207468616e20796f7572206d696e696d756d2c207768696368206973207b7d20746f6b656e732e7a14546f6b656e2072657365727665206572726f72212913722a000000722b000000722c000000722d0000007222000000722e000000722f00000072300000007207000000722300000072310000007224000000da0e5f5f696e7465726e616c5f62757972020000007234000000da0a5f5f646973636f756e74da0f5f5f696e7465726e616c5f73656c6c72450000007232000000291c720b0000007228000000724700000072480000007235000000da09616d6d5f746f6b656eda10746f6b656e735f707572636861736564723a000000723b000000da016b72400000007241000000da0b6665655f70657263656e74da03666565da06727377705f6bda16727377705f6e65775f746f6b656e5f72657365727665da19727377705f6e65775f63757272656e63795f72657365727665da17727377705f63757272656e63795f707572636861736564da17727377705f63757272656e63795f726573657276655f32da14727377705f746f6b656e5f726573657276655f32da08727377705f6b5f32da1b727377705f6e65775f63757272656e63795f726573657276655f32da18727377705f6e65775f746f6b656e5f726573657276655f32da0b73656c6c5f616d6f756e74da1473656c6c5f616d6f756e745f776974685f666565da1163757272656e63795f7265636569766564da0e746f6b656e5f7265636569766564da0b6275726e5f616d6f756e74722500000072250000007226000000da036275798500000073820000000003140110010a010e010e0106010c010a010a0108010801100104010c010801080108010801120108010a010c0108010801080108010c0104010c010801020106010c01080108010c010a010a0104010c010a010c0104010801140114010a02080108010401180114011401100212010a0110010a0112010a010a0110010c010c01725f0000002904720b000000722900000072470000007248000000630400000000000000180000000500000043000000733c02000074007c00190064016b08731474016402830182017c0164036b047324740164048301820174026a037c0083017d0474026a0374046405190083017d0574026a057c0474068302735074016406830182017c007404640519006b0272947c046a077c0174086a0974086a0a64078d030100740b7404640519007c0164088d027d06740c6a0d7c0674086a0a64098d0201007c065300740e7c0019005c027d077d087c077c0814007d097c087c0117007d0a7c097c0a1b007d0b7c077c0b18007d067404640a1900740f74086a0a190014007d0c7c067c0c14007d0d7c0364016b089001727c7c0d7404640b190014007d0d740e74046405190019005c027d0e7d0f7c0e7c0f14007d107c0e7c0d17007d117c117c0d7c0c140037007d117c107c111b007d127c0f7c1218007d137c137404640c190014007d147c056a077c1374086a0974086a0a64078d030100740b7404640519007c1464088d027d157c056a0d7c137c1418007404640d190064098d0201007c0b7c1517007d0b6e4a7c067c0d18007d067c0d7c0d7404640c1900140018007d167c0b7c0d7404640c1900140017007d0b74107404640519007c16640e8d027d177c056a0d7c177404640d190064098d0201007c0264006b03900172ea7c067c026b05900173ea7401640f6a117c067c028302830182017c0664036b04900173fc74016410830182017c046a077c0174086a0974086a0a64078d030100740c6a0d7c0674086a0a64098d0201007c0b7c0a6702740e7c003c007c0b7c0a1b0074127c003c007c06530029114e547a164d61726b657420646f6573206e6f742065786973742172010000007a2e4d7573742070726f766964652063757272656e637920616d6f756e7420616e6420746f6b656e20616d6f756e742172180000007a18496e76616c696420746f6b656e20696e746572666163652129037203000000720400000072080000002902720b000000722900000029027203000000720400000072160000007219000000721a000000721c0000002902720b00000072280000007a4f4f6e6c79207b7d205441552063616e206265207075726368617365642c207768696368206973206c657373207468616e20796f7572206d696e696d756d2c207768696368206973207b7d205441552e7a14546f6b656e2072657365727665206572726f72212913722a000000722b000000722c000000722d0000007222000000722e000000722f0000007207000000722300000072310000007224000000724b000000723000000072020000007234000000724a0000007249000000724500000072320000002918720b0000007229000000724700000072480000007235000000724c000000da1263757272656e63795f707572636861736564723a000000723b000000724e00000072410000007240000000724f0000007250000000da15727377705f63757272656e63795f72657365727665da12727377705f746f6b656e5f72657365727665725100000072530000007252000000725a000000725b000000725c000000725e000000725d000000722500000072250000007226000000da0473656c6ccd000000736a0000000003140110010a010e010e0106010c010a010a0104010c01100104010c010801080108010801120108010a010c0104010c01080108010c01080108010c010a010a0104010c010a010c010a020801100106010a010801080112010a0110010a011201140210010c010c01726300000029027203000000da0e746f6b656e5f636f6e7472616374630200000000000000050000000500000043000000736e0100007c0064016b05731074006402830182017c0164006b0272207401640319007d0174026a037c0183017d02740474056a067c01660219007d037c007c036b0072cc7c026a077c037c00180074056a06830201007c00740474056a067c0166023c00740164041900740474056a067401640319006602190064057401640419001b001300640518001400740164061900140074016407190018007d047c047408640883016b0472ae7408640883017d047c0464016b0072ba64017d0464057c041800740974056a063c007c0453007c007c036b049001726a7c026a0a7c007c03180074056a0b74056a06830301007c00740474056a067c0166023c00740164041900740474056a067401640319006602190064057401640419001b001300640518001400740164061900140074016407190018007d047c047408640883016b049001724a7408640883017d047c0464016b009001725864017d0464057c041800740974056a063c007c0453006400530029094e72010000007a204d757374206265206120706f736974697665207374616b6520616d6f756e74217218000000721d0000007212000000721e000000721f0000007a04302e3939290c722b0000007222000000722c000000722d000000da0f5f5f7374616b65645f616d6f756e747223000000722400000072020000007221000000724a00000072070000007231000000290572030000007264000000724c000000da0f63757272656e745f62616c616e6365da0f646973636f756e745f616d6f756e74722500000072250000007226000000da057374616b6509010000733600000000021001080108010a010e01080112010e03320108010c010801080104010e0104010a0116010e03320108010e0108010a0104010e0172680000002903da036b6579da096e65775f76616c7565da12636f6e766572745f746f5f646563696d616c630300000000000000030000000300000043000000732e00000074006401190074016a026b02731674036402830182017c02722274047c0183017d017c0174007c003c007c01530029034e72200000007a0e4e6f7420746865206f776e6572212905722200000072230000007224000000722b000000722100000029037269000000726a000000726b000000722500000072250000007226000000da0c6368616e67655f73746174652c010000730a00000000021601040108010801726c00000029037269000000726a000000da0e636f6e766572745f746f5f696e74630300000000000000030000000300000043000000732e00000074006401190074016a026b02731674036402830182017c02722274047c0183017d017c0174007c003c007c01530029034e72200000007a0e4e6f7420746865206f776e6572212905722200000072230000007224000000722b000000da03696e7429037269000000726a000000726d000000722500000072250000007226000000da126368616e67655f73746174655f666c6f617435010000730a00000000021601040108010801726f0000002901720b000000630100000000000000030000000300000043000000734a00000074006401190064026b087314740164038301820174026a037c0083017d017c016a0474056a0683017d027c0264046b04733a74016405830182017c0274077c00190064063c007c02530029074eda0c53594e435f454e41424c4544547a1453796e63206973206e6f7420656e61626c65642172010000007a1d43616e6e6f742062652061206e656761746976652062616c616e636521721200000029087222000000722b000000722c000000722d000000da0a62616c616e63655f6f667223000000723100000072340000002903720b0000007235000000da0b6e65775f62616c616e6365722500000072250000007226000000da0d73796e635f72657365727665733e010000730c000000000214010a010c0110010c0172730000006302000000000000000a000000030000004300000073b200000074007c00190064016b08731474016402830182017c0164036b0172206403530074026a037c0083017d0274026a047c0274058302733e740164048301820174067c0019005c027d037d047c037c0414007d057c037c0117007d067c057c061b007d077c047c0718007d087c0874076405190014007d097c087c0938007d087c077c0937007d077c0864036b04739674016406830182017c067c07670274067c003c007c067c071b0074087c003c007c08530029074e547a1b52535750204d61726b657420646f6573206e6f742065786973742172010000007a18496e76616c696420746f6b656e20696e746572666163652172160000007a14546f6b656e2072657365727665206572726f72212909722a000000722b000000722c000000722d000000722e000000722f000000723400000072220000007232000000290a720b00000072280000007235000000723a000000723b000000724e00000072400000007241000000724d0000007250000000722500000072250000007226000000724900000048010000732400000000011401080104010a010e0106010c0108010801080108010c010801080110010c010c0172490000002902720b00000072290000006302000000000000000a000000030000004300000073b200000074007c00190064016b08731474016402830182017c0164036b0172206403530074026a037c0083017d0274026a047c0274058302733e740164048301820174067c0019005c027d037d047c037c0414007d057c047c0117007d067c057c061b007d077c037c0718007d087c0874076405190014007d097c087c0938007d087c077c0937007d077c0864036b04739674016406830182017c077c06670274067c003c007c077c061b0074087c003c007c08530029074e547a1b52535750204d61726b657420646f6573206e6f742065786973742172010000007a18496e76616c696420746f6b656e20696e746572666163652172160000007a14546f6b656e2072657365727665206572726f72212909722a000000722b000000722c000000722d000000722e000000722f000000723400000072220000007232000000290a720b00000072290000007235000000723a000000723b000000724e0000007241000000724000000072600000007250000000722500000072250000007226000000724b0000005d010000732400000000011401080104010a010e0106010c0108010801080108010c010801080110010c010c01724b000000290272030000007204000000290272030000007204000000290372030000007204000000720800000029027201000000720100000029017201000000290172010000002902720100000046290272010000004629014e29014629014629217230000000da09696d706f72746c6962722c000000da0446756e63722f000000da0448617368722a0000007232000000723300000072340000007265000000724a00000072220000007227000000da085f5f6578706f7274da03737472da05666c6f617472360000007238000000723e0000007242000000724300000072440000007246000000da04626f6f6c725f00000072630000007268000000726c000000726f00000072730000007249000000724b0000007225000000722500000072250000007226000000da083c6d6f64756c653e0100000073600000000801040110010e010a010c010e01060108010a01080106010801060108010c03080c060100011612060112040601141606011418060114080601140506010601100c06010001184606010001183a060114220601160806011608060110091015