diff --git a/packages/packages.json b/packages/packages.json index 2e84a33e..926c54f5 100644 --- a/packages/packages.json +++ b/packages/packages.json @@ -3,16 +3,16 @@ "contract/valory/keep3r_v1_library/0.1.0": "bafybeiguyavczsaebbh5docth3o6e36b24s46jynhvysewnk3hqim3a4qe", "contract/valory/keep3r_test_job/0.1.0": "bafybeigmugia7f4rkeyvaoagbwo5i5nboqxkogyirzayj6wynmm5yp4t7a", "contract/valory/keep3r_v1/0.1.0": "bafybeibtmwyixk5h6ochkeuvecazzyj7qznoe3yoqwazypqdxovey2ar2e", - "skill/valory/keep3r_job_abci/0.1.0": "bafybeid6jnfjq4qnw4tf2dqymtoibaml27r66w3svsve6u3fovuap2u6ju", - "skill/valory/keep3r_abci/0.1.0": "bafybeienl2gseiubqwqz57jb2qb7knr4elwq3tqvaaypc3k6w7yok4yo3i", - "agent/valory/keep3r_bot/0.1.0": "bafybeib76phj7aue3uwimbqejfp372vbq7cvyibuhnk5zqthbokjuv74ba", + "skill/valory/keep3r_job_abci/0.1.0": "bafybeieyztb2uyl4igmtbegkrizcnyxhnfweuygd2tdlkqr6mq5r7sji5e", + "skill/valory/keep3r_abci/0.1.0": "bafybeihm6enxuojuqjf2mdw7xqnyjgupi6mwtsapa4ry4khvc4vci7zoxi", + "agent/valory/keep3r_bot/0.1.0": "bafybeid7zpebkekceqjl6ofse5tot3tpp3lw4zl3tqvjjy3754hf474jlm", "contract/valory/keep3r_job/0.1.0": "bafybeiajy32pvqdzbecg2obmlnzdg756srtsmfzn4ujl5ybclx4hfvceli", - "contract/valory/keep3r_v2/0.1.0": "bafybeifllwgx5mmh4qjnviex2hmgznxzde4vlm7bw5fgnwl3z2cgbgdot4", + "contract/valory/keep3r_v2/0.1.0": "bafybeicqvpeo7czhkf5m3qstzdqnxhhht6dko5yjjuzf2dt2fie2lzjzii", "contract/valory/deposit_manager_job/0.1.0": "bafybeigiqzpzmxmaq5qf64smhhqabrg2txhq23gnha6bi4fe2gurisjduy", "contract/valory/phuture_harvesting_job/0.1.0": "bafybeidmd36fvitwuy7khfbtpvckopu4moldxntgnsmwxelegoyv3v4rr4", "contract/valory/keep3r_my_job/0.1.0": "bafybeicxuf5l5qgzdzoqf3jo7pkwlfnw33guad26lkmx3splgeavto5zj4", - "service/valory/keep3r_bot/0.1.0": "bafybeif63656jsjcj3yd4yheufbvh4d45ux7d2f7lornxy4otj7msmgoyy", - "service/valory/keep3r_bot_goerli/0.1.0": "bafybeib6u6cstqacruo2cf32tiztf2qvzjcclfaatvnoqouo3jjbnnmhxi", + "service/valory/keep3r_bot/0.1.0": "bafybeibbqvijadlls4y35wow2qnkkom5vf4fs427d2624kx3glum7nfa6u", + "service/valory/keep3r_bot_goerli/0.1.0": "bafybeidodcrbduryopdw533g2xglo7jn4gsm4dpkji44jnlxv5lxfp4qg4", "contract/valory/yearn_factory_harvest_job/0.1.0": "bafybeietc56fkz4frnblanaeejm24ctsu3w6ojz56cyeqlktsh57hv3fqm", "protocol/valory/ledger_api/1.0.0": "bafybeigpn6ysm53qkcllkzgdwc5xxpxz32xn2zoux3phdm2i3yty2i3thu", "connection/valory/ledger/0.19.0": "bafybeigvml36q4ic2tstc25xli5qw7hacykyudkuywfmc7qjb5kwfzhkka", @@ -21,7 +21,8 @@ "skill/valory/reset_pause_abci/0.1.0": "bafybeifgatypd7xp7ng3zcszpintfxaqgf6blapgsjejy42nr5u4dfseg4", "skill/valory/transaction_settlement_abci/0.1.0": "bafybeie3pn44j5mqg6r4zxdsr3fhxsy2imk64nxhnsw33ac6wyg2g2f2sy", "skill/valory/termination_abci/0.1.0": "bafybeiasehtl7rj62trkulyxtqna37nrrwvhwhthdkri2dihw53lky2txi", - "contract/valory/connext_propagate_job/0.1.0": "bafybeigmdxw6jiizdemubntcs6bgqtt6mmdljb2d2bc3cyaggmqexz4m5u" + "contract/valory/connext_propagate_job/0.1.0": "bafybeigmdxw6jiizdemubntcs6bgqtt6mmdljb2d2bc3cyaggmqexz4m5u", + "contract/valory/curve_pool/0.1.0": "bafybeia6e44qj5xmqdgewsnpa3k24ov25qf4rnvmbxpuukn2dbrbqv73ma" }, "third_party": { "protocol/valory/abci/0.1.0": "bafybeig3dj5jhsowlvg3t73kgobf6xn4nka7rkttakdb2gwsg5bp7rt7q4", diff --git a/packages/valory/agents/keep3r_bot/aea-config.yaml b/packages/valory/agents/keep3r_bot/aea-config.yaml index 90374bd7..7487da79 100644 --- a/packages/valory/agents/keep3r_bot/aea-config.yaml +++ b/packages/valory/agents/keep3r_bot/aea-config.yaml @@ -134,11 +134,12 @@ connections: - valory/ledger:0.19.0:bafybeigvml36q4ic2tstc25xli5qw7hacykyudkuywfmc7qjb5kwfzhkka - valory/p2p_libp2p_client:0.1.0:bafybeidwcobzb7ut3efegoedad7jfckvt2n6prcmd4g7xnkm6hp6aafrva contracts: +- valory/curve_pool:0.1.0:bafybeia6e44qj5xmqdgewsnpa3k24ov25qf4rnvmbxpuukn2dbrbqv73ma - valory/gnosis_safe:0.1.0:bafybeig2dobzlupi4twn3lv2avfajslgjukkmkdd4qzf37cbfv7ojupv54 - valory/gnosis_safe_proxy_factory:0.1.0:bafybeifydgooxpzav7b7blpxj4p5arytmjqphdcyl46egs3htnj2fszora - valory/keep3r_v1:0.1.0:bafybeibtmwyixk5h6ochkeuvecazzyj7qznoe3yoqwazypqdxovey2ar2e - valory/keep3r_v1_library:0.1.0:bafybeiguyavczsaebbh5docth3o6e36b24s46jynhvysewnk3hqim3a4qe -- valory/keep3r_v2:0.1.0:bafybeifllwgx5mmh4qjnviex2hmgznxzde4vlm7bw5fgnwl3z2cgbgdot4 +- valory/keep3r_v2:0.1.0:bafybeicqvpeo7czhkf5m3qstzdqnxhhht6dko5yjjuzf2dt2fie2lzjzii - valory/multisend:0.1.0:bafybeigjywkl7hydjsrkogob3xebj2ifhqwmfhhxoeyrndzhhxi5u6amey - valory/service_registry:0.1.0:bafybeics2gdksww76emw5b7hyph75t7l72dpnls6qua5yfzwpeeezolnhq protocols: @@ -153,8 +154,8 @@ protocols: skills: - valory/abstract_abci:0.1.0:bafybeigqr6dzr23r6oxbnpxqyae7g5ndjy75oatjk6liyrvmpb2jxehirq - valory/abstract_round_abci:0.1.0:bafybeiaf3twqpqguqmqmqhl4sjd2i2nh5jspzghs3cmhp2co7lb5hkr5yy -- valory/keep3r_abci:0.1.0:bafybeienl2gseiubqwqz57jb2qb7knr4elwq3tqvaaypc3k6w7yok4yo3i -- valory/keep3r_job_abci:0.1.0:bafybeid6jnfjq4qnw4tf2dqymtoibaml27r66w3svsve6u3fovuap2u6ju +- valory/keep3r_abci:0.1.0:bafybeihm6enxuojuqjf2mdw7xqnyjgupi6mwtsapa4ry4khvc4vci7zoxi +- valory/keep3r_job_abci:0.1.0:bafybeieyztb2uyl4igmtbegkrizcnyxhnfweuygd2tdlkqr6mq5r7sji5e - valory/registration_abci:0.1.0:bafybeic5uibumxihx7qvx2457sqo6d2dzj6u73tywffgamrb54dzvpzraq - valory/reset_pause_abci:0.1.0:bafybeifgatypd7xp7ng3zcszpintfxaqgf6blapgsjejy42nr5u4dfseg4 - valory/termination_abci:0.1.0:bafybeiasehtl7rj62trkulyxtqna37nrrwvhwhthdkri2dihw53lky2txi @@ -239,6 +240,7 @@ models: manual_gas_limit: ${int:5000000} multisend_address: ${str:0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761} raise_on_failed_simulation: ${bool:false} + curve_pool_contract_address: ${str:0x21410232B484136404911780bC32756D5d1a9Fa9} use_flashbots: ${bool:false} termination_sleep: 900 k3pr_address: ${str:0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44} diff --git a/packages/valory/contracts/curve_pool/__init__.py b/packages/valory/contracts/curve_pool/__init__.py new file mode 100644 index 00000000..7dbbcb61 --- /dev/null +++ b/packages/valory/contracts/curve_pool/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2022-2023 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + +"""This module contains the support resources for the Keep3r V1 library contract.""" +from pathlib import Path + + +PACKAGE_DIR = Path(__file__).parent diff --git a/packages/valory/contracts/curve_pool/build/CurvePool.json b/packages/valory/contracts/curve_pool/build/CurvePool.json new file mode 100644 index 00000000..ac9aaef5 --- /dev/null +++ b/packages/valory/contracts/curve_pool/build/CurvePool.json @@ -0,0 +1,1376 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "Keep3rV1Library", + "sourceName": "contracts/Keep3rV1.sol", + "abi": [ + { + "name": "TokenExchange", + "inputs": [ + { + "name": "buyer", + "type": "address", + "indexed": true + }, + { + "name": "sold_id", + "type": "uint256", + "indexed": false + }, + { + "name": "tokens_sold", + "type": "uint256", + "indexed": false + }, + { + "name": "bought_id", + "type": "uint256", + "indexed": false + }, + { + "name": "tokens_bought", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "AddLiquidity", + "inputs": [ + { + "name": "provider", + "type": "address", + "indexed": true + }, + { + "name": "token_amounts", + "type": "uint256[2]", + "indexed": false + }, + { + "name": "fee", + "type": "uint256", + "indexed": false + }, + { + "name": "token_supply", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "RemoveLiquidity", + "inputs": [ + { + "name": "provider", + "type": "address", + "indexed": true + }, + { + "name": "token_amounts", + "type": "uint256[2]", + "indexed": false + }, + { + "name": "token_supply", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "RemoveLiquidityOne", + "inputs": [ + { + "name": "provider", + "type": "address", + "indexed": true + }, + { + "name": "token_amount", + "type": "uint256", + "indexed": false + }, + { + "name": "coin_index", + "type": "uint256", + "indexed": false + }, + { + "name": "coin_amount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "CommitNewParameters", + "inputs": [ + { + "name": "deadline", + "type": "uint256", + "indexed": true + }, + { + "name": "admin_fee", + "type": "uint256", + "indexed": false + }, + { + "name": "mid_fee", + "type": "uint256", + "indexed": false + }, + { + "name": "out_fee", + "type": "uint256", + "indexed": false + }, + { + "name": "fee_gamma", + "type": "uint256", + "indexed": false + }, + { + "name": "allowed_extra_profit", + "type": "uint256", + "indexed": false + }, + { + "name": "adjustment_step", + "type": "uint256", + "indexed": false + }, + { + "name": "ma_half_time", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "NewParameters", + "inputs": [ + { + "name": "admin_fee", + "type": "uint256", + "indexed": false + }, + { + "name": "mid_fee", + "type": "uint256", + "indexed": false + }, + { + "name": "out_fee", + "type": "uint256", + "indexed": false + }, + { + "name": "fee_gamma", + "type": "uint256", + "indexed": false + }, + { + "name": "allowed_extra_profit", + "type": "uint256", + "indexed": false + }, + { + "name": "adjustment_step", + "type": "uint256", + "indexed": false + }, + { + "name": "ma_half_time", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "RampAgamma", + "inputs": [ + { + "name": "initial_A", + "type": "uint256", + "indexed": false + }, + { + "name": "future_A", + "type": "uint256", + "indexed": false + }, + { + "name": "initial_gamma", + "type": "uint256", + "indexed": false + }, + { + "name": "future_gamma", + "type": "uint256", + "indexed": false + }, + { + "name": "initial_time", + "type": "uint256", + "indexed": false + }, + { + "name": "future_time", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "StopRampA", + "inputs": [ + { + "name": "current_A", + "type": "uint256", + "indexed": false + }, + { + "name": "current_gamma", + "type": "uint256", + "indexed": false + }, + { + "name": "time", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "ClaimAdminFee", + "inputs": [ + { + "name": "admin", + "type": "address", + "indexed": true + }, + { + "name": "tokens", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "stateMutability": "nonpayable", + "type": "constructor", + "inputs": [ + { + "name": "_weth", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "stateMutability": "payable", + "type": "function", + "name": "exchange", + "inputs": [ + { + "name": "i", + "type": "uint256" + }, + { + "name": "j", + "type": "uint256" + }, + { + "name": "dx", + "type": "uint256" + }, + { + "name": "min_dy", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "payable", + "type": "function", + "name": "exchange", + "inputs": [ + { + "name": "i", + "type": "uint256" + }, + { + "name": "j", + "type": "uint256" + }, + { + "name": "dx", + "type": "uint256" + }, + { + "name": "min_dy", + "type": "uint256" + }, + { + "name": "use_eth", + "type": "bool" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "payable", + "type": "function", + "name": "exchange", + "inputs": [ + { + "name": "i", + "type": "uint256" + }, + { + "name": "j", + "type": "uint256" + }, + { + "name": "dx", + "type": "uint256" + }, + { + "name": "min_dy", + "type": "uint256" + }, + { + "name": "use_eth", + "type": "bool" + }, + { + "name": "receiver", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "payable", + "type": "function", + "name": "exchange_underlying", + "inputs": [ + { + "name": "i", + "type": "uint256" + }, + { + "name": "j", + "type": "uint256" + }, + { + "name": "dx", + "type": "uint256" + }, + { + "name": "min_dy", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "payable", + "type": "function", + "name": "exchange_underlying", + "inputs": [ + { + "name": "i", + "type": "uint256" + }, + { + "name": "j", + "type": "uint256" + }, + { + "name": "dx", + "type": "uint256" + }, + { + "name": "min_dy", + "type": "uint256" + }, + { + "name": "receiver", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "payable", + "type": "function", + "name": "exchange_extended", + "inputs": [ + { + "name": "i", + "type": "uint256" + }, + { + "name": "j", + "type": "uint256" + }, + { + "name": "dx", + "type": "uint256" + }, + { + "name": "min_dy", + "type": "uint256" + }, + { + "name": "use_eth", + "type": "bool" + }, + { + "name": "sender", + "type": "address" + }, + { + "name": "receiver", + "type": "address" + }, + { + "name": "cb", + "type": "bytes32" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "payable", + "type": "function", + "name": "add_liquidity", + "inputs": [ + { + "name": "amounts", + "type": "uint256[2]" + }, + { + "name": "min_mint_amount", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "payable", + "type": "function", + "name": "add_liquidity", + "inputs": [ + { + "name": "amounts", + "type": "uint256[2]" + }, + { + "name": "min_mint_amount", + "type": "uint256" + }, + { + "name": "use_eth", + "type": "bool" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "payable", + "type": "function", + "name": "add_liquidity", + "inputs": [ + { + "name": "amounts", + "type": "uint256[2]" + }, + { + "name": "min_mint_amount", + "type": "uint256" + }, + { + "name": "use_eth", + "type": "bool" + }, + { + "name": "receiver", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "remove_liquidity", + "inputs": [ + { + "name": "_amount", + "type": "uint256" + }, + { + "name": "min_amounts", + "type": "uint256[2]" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "remove_liquidity", + "inputs": [ + { + "name": "_amount", + "type": "uint256" + }, + { + "name": "min_amounts", + "type": "uint256[2]" + }, + { + "name": "use_eth", + "type": "bool" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "remove_liquidity", + "inputs": [ + { + "name": "_amount", + "type": "uint256" + }, + { + "name": "min_amounts", + "type": "uint256[2]" + }, + { + "name": "use_eth", + "type": "bool" + }, + { + "name": "receiver", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "remove_liquidity_one_coin", + "inputs": [ + { + "name": "token_amount", + "type": "uint256" + }, + { + "name": "i", + "type": "uint256" + }, + { + "name": "min_amount", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "remove_liquidity_one_coin", + "inputs": [ + { + "name": "token_amount", + "type": "uint256" + }, + { + "name": "i", + "type": "uint256" + }, + { + "name": "min_amount", + "type": "uint256" + }, + { + "name": "use_eth", + "type": "bool" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "remove_liquidity_one_coin", + "inputs": [ + { + "name": "token_amount", + "type": "uint256" + }, + { + "name": "i", + "type": "uint256" + }, + { + "name": "min_amount", + "type": "uint256" + }, + { + "name": "use_eth", + "type": "bool" + }, + { + "name": "receiver", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "claim_admin_fees", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "ramp_A_gamma", + "inputs": [ + { + "name": "future_A", + "type": "uint256" + }, + { + "name": "future_gamma", + "type": "uint256" + }, + { + "name": "future_time", + "type": "uint256" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "stop_ramp_A_gamma", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "commit_new_parameters", + "inputs": [ + { + "name": "_new_mid_fee", + "type": "uint256" + }, + { + "name": "_new_out_fee", + "type": "uint256" + }, + { + "name": "_new_admin_fee", + "type": "uint256" + }, + { + "name": "_new_fee_gamma", + "type": "uint256" + }, + { + "name": "_new_allowed_extra_profit", + "type": "uint256" + }, + { + "name": "_new_adjustment_step", + "type": "uint256" + }, + { + "name": "_new_ma_half_time", + "type": "uint256" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "apply_new_parameters", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "revert_new_parameters", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_dy", + "inputs": [ + { + "name": "i", + "type": "uint256" + }, + { + "name": "j", + "type": "uint256" + }, + { + "name": "dx", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "calc_token_amount", + "inputs": [ + { + "name": "amounts", + "type": "uint256[2]" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "calc_withdraw_one_coin", + "inputs": [ + { + "name": "token_amount", + "type": "uint256" + }, + { + "name": "i", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "lp_price", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "A", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "gamma", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "fee", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_virtual_price", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "price_oracle", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "initialize", + "inputs": [ + { + "name": "A", + "type": "uint256" + }, + { + "name": "gamma", + "type": "uint256" + }, + { + "name": "mid_fee", + "type": "uint256" + }, + { + "name": "out_fee", + "type": "uint256" + }, + { + "name": "allowed_extra_profit", + "type": "uint256" + }, + { + "name": "fee_gamma", + "type": "uint256" + }, + { + "name": "adjustment_step", + "type": "uint256" + }, + { + "name": "admin_fee", + "type": "uint256" + }, + { + "name": "ma_half_time", + "type": "uint256" + }, + { + "name": "initial_price", + "type": "uint256" + }, + { + "name": "_token", + "type": "address" + }, + { + "name": "_coins", + "type": "address[2]" + }, + { + "name": "_precisions", + "type": "uint256" + } + ], + "outputs": [] + }, + { + "stateMutability": "view", + "type": "function", + "name": "token", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "coins", + "inputs": [ + { + "name": "arg0", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "price_scale", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "last_prices", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "last_prices_timestamp", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "initial_A_gamma", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "future_A_gamma", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "initial_A_gamma_time", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "future_A_gamma_time", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "allowed_extra_profit", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "future_allowed_extra_profit", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "fee_gamma", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "future_fee_gamma", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "adjustment_step", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "future_adjustment_step", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "ma_half_time", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "future_ma_half_time", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "mid_fee", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "out_fee", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "admin_fee", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "future_mid_fee", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "future_out_fee", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "future_admin_fee", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "balances", + "inputs": [ + { + "name": "arg0", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "D", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "factory", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "xcp_profit", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "xcp_profit_a", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "virtual_price", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "admin_actions_deadline", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + } + ], + "bytecode": "0x6020615d406080396080518060a01c615d3b5760e05260e051610100526301500786601455615d1356600436101561000d57612687565b60046000601c37600051635b41b9088118610032576000610ee05233610f0052610080565b63394747c58118610056576084358060011c615ce557610ee05233610f0052610080565b63ce7d650381186100d9576084358060011c615ce557610ee05260a4358060a01c615ce557610f00525b600054615ce557600160005533610960523461098052608060046109a037610ee051610a2052610f0051610a40526000610a60526000610a80526100c5610f20614a9d565b610f2051610f40526020610f406000600055f35b6365b2489b81186100ee5733610ee052610109565b63e2ad025a8118610160576084358060a01c615ce557610ee0525b600054615ce557600160005533610960523461098052608060046109a0376001610a2052610ee051610a40526000610a60526000610a805261014c610f00614a9d565b610f0051610f20526020610f206000600055f35b63dd96994f81186101fe576084358060011c615ce557610ee05260a4358060a01c615ce557610f005260c4358060a01c615ce557610f2052600054615ce5576001600055600060e43514615ce557610f0051610960523461098052608060046109a037610ee051610a2052610f2051610a405233610a605260e435610a80526101ea610f40614a9d565b610f4051610f60526020610f606000600055f35b630b4c7e4d8118610219576000610960523361098052610267565b63ee22be23811861023d576064358060011c615ce557610960523361098052610267565b637328333b8118610bc8576064358060011c615ce557610960526084358060a01c615ce557610980525b600054615ce55760016000556000600435116102885760006024351161028b565b60015b15615ce55761029b6109e061275a565b6109e080516109a05280602001516109c05250601a546109e052601b54610a005260e036610a20376109e051610b0052610a0051610b2052610b4060006002818352015b6109e0610b40516002811015615ce55760200201516020610b405102600401358181830110615ce55780820190509050610b6052610b60516109e0610b40516002811015615ce5576020020152610b60516001610b40516002811015615ce55702601a015581516001018083528114156102df5750506109e051610a6052610a0051610a8052610370610b80612689565b610b808051610b40528060200151610b605250600454610b6051808202821582848304141715615ce55790509050610b80526109e051610b4051808202821582848304141715615ce557905090506109e052610a0051610b8051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610a0052610b0051610b4051808202821582848304141715615ce55790509050610b0052610b2051610b8051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610b2052610960516104505734615ce5575b610ba060006002818352015b6001610ba0516002811015615ce5570260020154610bc05261096051610483576000610495565b602060203803608039608051610bc051145b156104ac576020610ba05102600401353418615ce5575b60006020610ba051026004013511156106425761096051156104df57602060203803608039608051610bc05114156104e2565b60015b156105f4576323b872dd610c2452600433610c445230610c64526020610ba0510260040135610c8452606001610c2052610c20506020610ce0610c2051610c406000610bc0515af1610539573d600060003e3d6000fd5b610cc060203d80821161054c578161054e565b805b905090508152805160200180610be0828460045afa905050506000610be0511461058d57610c0051610be05181816020036008021c9050905015615ce5575b602060203803608039608051610bc051186105f457632e1a7d4d610c20526020610ba0510260040135610c40526020602038036080396080513b15615ce557600060006024610c3c60006020602038036080396080515af16105f4573d600060003e3d6000fd5b6109e0610ba0516002811015615ce5576020020151610b00610ba0516002811015615ce5576020020151808210615ce55780820390509050610a20610ba0516002811015615ce55760200201525b815160010180835281141561045c575050600b54610ba0526000610ba0511161067157601c54610ae0526106b4565b6109a051610200526109c05161022052610b005161024052610b20516102605261069c610bc0612be3565b610bc051610ae052610ba05142106106b4576001600b555b6109a051610200526109c051610220526109e05161024052610a0051610260526106df610be0612be3565b610be051610bc052600154610be0526318160ddd610c20526020610c206004610c3c610be0515afa610716573d600060003e3d6000fd5b601f3d1115615ce557610c2051610c00526000610ae0511161075257610bc05161020052610745610c20613d70565b610c2051610aa052610796565b610c0051610bc051808202821582848304141715615ce55790509050610ae051808015615ce557820490509050610c0051808210615ce55780820390509050610aa0525b6000610aa0511115615ce5576000610ae0511161081857610bc051601c55670de0b6b3a7640000602055670de0b6b3a7640000601e556340c10f19610c205261098051610c4052610aa051610c60526020610c206044610c3c6000610be0515af1610806573d600060003e3d6000fd5b601f3d1115615ce557610c2050610ae7565b610a205161016052610a4051610180526109e0516101a052610a00516101c052610843610c20615556565b610c2051610aa051808202821582848304141715615ce557905090506402540be4008082049050905060018181830110615ce55780820190509050610ac052610aa08051610ac051808210615ce55780820390509050815250610c008051610aa0518181830110615ce557808201905090508152506340c10f19610c205261098051610c4052610aa051610c60526020610c206044610c3c6000610be0515af16108f2573d600060003e3d6000fd5b601f3d1115615ce557610c20506000610c2052620186a0610aa0511115610aaf57600435156109245760243515610927565b60015b15610aaf57606036610c40376004351561096857610a8051610b6051808202821582848304141715615ce55790509050610c4052610b4051610c6052610997565b610a6051610b4051808202821582848304141715615ce55790509050610c4052610b6051610c60526001610c80525b610c4051610aa051808202821582848304141715615ce55790509050610c0051808015615ce557820490509050610c4052610c4051670de0b6b3a7640000808202821582848304141715615ce557905090506020610c80510260040135610c6051808202821582848304141715615ce55790509050610aa051610a60610c80516002811015615ce5576020020151808202821582848304141715615ce55790509050610c6051808202821582848304141715615ce55790509050610c0051808015615ce557820490509050808210615ce55780820390509050808015615ce557820490509050610c2052610c8051610aaf576ec097ce7bc90715b34b9f1000000000610c2051808015615ce557820490509050610c20525b6109a051610660526109c051610680526109e0516106a052610a00516106c052610c20516106e052610bc05161070052610ae7614298565b604435610aa0511015610b6b576008610c20527f536c697070616765000000000000000000000000000000000000000000000000610c4052610c2050610c205180610c4001818260206001820306601f82010390500336823750506308c379a0610be0526020610c0052610c205160206001820306601f8201039050604401610bfcfd5b610980517f540ab385f9b5d450a27404172caade516b3ba3f4be88239ac56a2ad1de2a1f5a600435610c2052602435610c4052610ac051610c6052610c0051610c80526080610c20a2610aa051610c20526020610c206000600055f35b635b36389c8118610be257600060e0523361010052610c2e565b63269b55818118610c05576064358060011c615ce55760e0523361010052610c2e565b631808e84a8118610fb4576064358060011c615ce55760e0526084358060a01c615ce557610100525b34615ce557600054615ce5576001600055600154610120526318160ddd610160526020610160600461017c610120515afa610c6e573d600060003e3d6000fd5b601f3d1115615ce55761016051610140526379cc67906101605233610180526004356101a0526020610160604461017c6000610120515af1610cb5573d600060003e3d6000fd5b601f3d1115615ce55761016050601a5461016052601b54610180526004356001808210615ce557808203905090506101a0526101c060006002818352015b6101606101c0516002811015615ce55760200201516101a051808202821582848304141715615ce5579050905061014051808015615ce5578204905090506101e05260206101c05102602401356101e05110615ce5576101606101c0516002811015615ce55760200201516101e051808210615ce5578082039050905060016101c0516002811015615ce55702601a01556101e0516101606101c0516002811015615ce557602002015260016101c0516002811015615ce55702600201546102005260e051610dc3576000610dd5565b60206020380360803960805161020051145b610ed4576020602038036080396080516102005118610e335763d0e30db0610220526020602038036080396080513b15615ce55760006000600461023c6101e0516020602038036080396080515af1610e33573d600060003e3d6000fd5b63a9059cbb61026452600461010051610284526101e0516102a45260400161026052610260506020610300610260516102806000610200515af1610e7c573d600060003e3d6000fd5b6102e060203d808211610e8f5781610e91565b805b905090508152805160200180610220828460045afa9050505060006102205114610f0257610240516102205181816020036008021c9050905015615ce557610f02565b6000610220526102205060006000610220516102406101e051610100515af1610f02573d600060003e3d6000fd5b8151600101808352811415610cf3575050601c546101c0526101c0516101c0516101a051808202821582848304141715615ce5579050905061014051808015615ce557820490509050808210615ce55780820390509050601c55337fdd3c0336a16f1b64f172b7bb0dad5b2b3c7c76f91e8c4aafd6aae60dce800153610160516101e052610180516102005261014051600435808210615ce557808203905090506102205260606101e0a26000600055005b63f1dc3cc98118610fcf57600061096052336109805261101d565b638f15b6b58118610ff3576064358060011c615ce55761096052336109805261101d565b6307329bcd81186113a6576064358060011c615ce557610960526084358060a01c615ce557610980525b34615ce557600054615ce55760016000556110396109e061275a565b6109e080516109a05280602001516109c0525060a0366109e037600b54610a80526109a0516104a0526109c0516104c052604060046104e0376000610a8051116105205260016105405261108e610aa06156df565b610aa080516109e0526020810151610a20526040810151610a0052606081018051610a40528060200151610a605250506044356109e0511015611142576008610aa0527f536c697070616765000000000000000000000000000000000000000000000000610ac052610aa050610aa05180610ac001818260206001820306601f82010390500336823750506308c379a0610a60526020610a8052610aa05160206001820306601f8201039050604401610a7cfd5b610a80514210611152576001600b555b60016024356002811015615ce55702601a0180546109e051808210615ce557808203905090508155506379cc6790610aa05233610ac052600435610ae0526020610aa06044610abc60006001545af16111b0573d600060003e3d6000fd5b601f3d1115615ce557610aa05060016024356002811015615ce5570260020154610aa052610960516111e35760006111f5565b602060203803608039608051610aa051145b6112f457602060203803608039608051610aa051186112535763d0e30db0610ac0526020602038036080396080513b15615ce557600060006004610adc6109e0516020602038036080396080515af1611253573d600060003e3d6000fd5b63a9059cbb610b0452600461098051610b24526109e051610b4452604001610b0052610b00506020610ba0610b0051610b206000610aa0515af161129c573d600060003e3d6000fd5b610b8060203d8082116112af57816112b1565b805b905090508152805160200180610ac0828460045afa905050506000610ac0511461132257610ae051610ac05181816020036008021c9050905015615ce557611322565b6000610ac052610ac05060006000610ac051610ae06109e051610980515af1611322573d600060003e3d6000fd5b6109a051610660526109c05161068052610a40516106a052610a60516106c052610a20516106e052610a00516107005261135a614298565b337f5ad056f2e28a8cec232015406b843668c1e36cda598127ec3b8c59b8c72773a060406004610ac0376109e051610b00526060610ac0a26109e051610ac0526020610ac06000600055f35b63c93f49e881186113d15734615ce557600054615ce55760016000556113ca613df5565b6000600055005b635e24807281186115fe5734615ce55763f851a4406101c05260206101c060046101dc601d545afa611408573d600060003e3d6000fd5b601f3d1115615ce5576101c0513318615ce557600a546201517f8181830110615ce55780820190509050421115615ce557426201517f8181830110615ce557808201905090506044351115615ce55761146261020061275a565b61020080516101c05280602001516101e052506101c05160801b610200526101e051610200511761020052610f9f6004351115615ce55763ee6b28016004351015615ce5576402540be3ff6024351115615ce55766470de4df8200016024351015615ce557670de0b6b3a7640000600435808202821582848304141715615ce557905090506101c051808015615ce55782049050905061022052678ac7230489e80001610220511015615ce55767016345785d89ffff610220511115615ce557670de0b6b3a7640000602435808202821582848304141715615ce557905090506101e051808015615ce55782049050905061022052678ac7230489e80001610220511015615ce55767016345785d89ffff610220511115615ce5576102005160085542600a5560043560801b61024052602435610240511761024052604435600b55610240516009557fe35f0559b0642164e286b30df2077ec3a05426617a25db7578fd20ba39a6cd056101c05161026052600435610280526101e0516102a0526024356102c052426102e0526044356103005260c0610260a1005b63244c7c2e81186116d25734615ce55763f851a4406101c05260206101c060046101dc601d545afa611635573d600060003e3d6000fd5b601f3d1115615ce5576101c0513318615ce55761165361020061275a565b61020080516101c05280602001516101e052506101c05160801b610200526101e051610200511761020052610200516008556102005160095542600a5542600b557f5f0e7fba3d100c9e19446e1c92fe436f0a9a22fe99669360e4fdd6d3de2fc2846101c051610220526101e0516102405242610260526060610220a1005b63a43c335181186118c55734615ce55763f851a44060e052602060e0600460fc601d545afa611706573d600060003e3d6000fd5b601f3d1115615ce55760e0513318615ce557602254615ce55760e0600460e0376402540be40161010051106117415760155461010052611750565b6207a11f610100511115615ce5575b6402540be40060e05111156117665760145460e0525b6101005160e05111615ce5576402540be40061012051111561178a57601654610120525b670de0b6b3a764000061014051106117a857600e54610140526117b5565b6000610140511115615ce5575b670de0b6b3a76400006101605111156117d057600c54610160525b670de0b6b3a76400006101805111156117eb57601054610180525b62093a806101a05110611804576012546101a052611811565b60006101a0511115615ce5575b426203f4808181830110615ce557808201905090506101c0526101c0516022556101205160195560e0516017556101005160185561014051600f5561016051600d55610180516011556101a0516013556101c0517f913fde9a37e1f8ab67876a4d0ce80790d764fcfc5692f4529526df9c6bdde553610120516101e05260e0516102005261010051610220526101405161024052610160516102605261018051610280526101a0516102a05260e06101e0a2005b632a7dd7cd8118611a105734615ce557600054615ce557600160005563f851a440610660526020610660600461067c601d545afa611908573d600060003e3d6000fd5b601f3d1115615ce557610660513318615ce5576022544210615ce557600060225414615ce55760006022556019546106605261066051601654146119565761194e613df5565b610660516016555b60175461068052610680516014556018546106a0526106a051601555600f546106c0526106c051600e55600d546106e0526106e051600c55601154610700526107005160105560135461072052610720516012557f1c65bbdc939f346e5d6f0bde1f072819947438d4fc7b182cc59c2f6dc5504087610660516107405261068051610760526106a051610780526106c0516107a0526106e0516107c052610700516107e052610720516108005260e0610740a16000600055005b63226840fb8118611a5d5734615ce55763f851a44060e052602060e0600460fc601d545afa611a44573d600060003e3d6000fd5b601f3d1115615ce55760e0513318615ce5576000602255005b63556d6e9f8118611d405734615ce55760243560043514615ce55760026004351015615ce55760026024351015615ce557611a996104e0612689565b6104e080516104a05280602001516104c052506004546104c051808202821582848304141715615ce557905090506104e052601a5461050052601b5461052052611ae461058061275a565b61058080516105405280602001516105605250601c54610580526000600b541115611b6b5761054051610600526105605161062052611b246105a06126d5565b6105a0805161064052806020015161066052506106005161020052610620516102205261064051610240526106605161026052611b626105e0612be3565b6105e051610580525b6105006004356002811015615ce5576020020180516044358181830110615ce55780820190509050815250610500516104a051808202821582848304141715615ce5579050905061050052610520516104e051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610520526105405160e052610560516101005261050051610120526105205161014052610580516101605260243561018052611c1f6105c0613372565b6105c0516105a0526105006024356002811015615ce55760200201516105a051808210615ce557808203905090506001808210615ce557808203905090506105c0526105a0516105006024356002811015615ce5576020020152600060243511611ca1576105c080516104a051808015615ce557820490509050815250611cd8565b6105c051670de0b6b3a7640000808202821582848304141715615ce557905090506104e051808015615ce5578204905090506105c0525b6105c080516105005160e0526105205161010052611cf76105e06128cd565b6105e0516105c051808202821582848304141715615ce557905090506402540be40080820490509050808210615ce557808203905090508152506105c0516105e05260206105e0f35b638d8ea7278118611fce5734615ce5576318160ddd6104c05260206104c060046104dc6001545afa611d77573d600060003e3d6000fd5b601f3d1115615ce5576104c0516104a052611d93610500612689565b61050080516104c05280602001516104e052506004546104e051808202821582848304141715615ce5579050905061050052611dd061056061275a565b61056080516105205280602001516105405250611dee6105a06126d5565b6105a0805161056052806020015161058052506004356104c051808202821582848304141715615ce557905090506105a05260243561050051808202821582848304141715615ce55790509050670de0b6b3a7640000808204905090506105c052601c546105e0526000600b541115611e95576105205161020052610540516102205261056051610240526105805161026052611e8c610600612be3565b610600516105e0525b61056080516105a0518181830110615ce5578082019050905081525061058080516105c0518181830110615ce557808201905090508152506105205161020052610540516102205261056051610240526105805161026052611ef8610620612be3565b61062051610600526104a05161060051808202821582848304141715615ce557905090506105e051808015615ce5578204905090506104a051808210615ce557808203905090506106205261062080516105a051610160526105c05161018052610560516101a052610580516101c052611f73610640615556565b6106405161062051808202821582848304141715615ce557905090506402540be4008082049050905060018181830110615ce55780820190509050808210615ce5578082039050905081525061062051610640526020610640f35b634fb08c5e811861205a5734615ce557611fe961078061275a565b61078080516108605280602001516108805250604060046108a03760016108e052600061090052610860516104a052610880516104c0526108a0516104e0526108c051610500526108e05161052052610900516105405261204b6107c06156df565b6107c051610920526020610920f35b6354f0f7d581186120db5734615ce5576002602054808202821582848304141715615ce5579050905061208e610300614192565b61030051610340526103405160e0526120a8610320615b98565b61032051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610360526020610360f35b63f446c1d081186121055734615ce5576120f66101c061275a565b6101c051610200526020610200f35b63b137392981186121325734615ce5576121206101c061275a565b6101c060200151610200526020610200f35b63ddca3f4381186121895734615ce55761214d6101a06126d5565b6101a0805161020052806020015161022052506102005160e052610220516101005261217a6101e06128cd565b6101e051610240526020610240f35b63bb7b8b8081186122185734615ce557670de0b6b3a7640000601c54610200526121b4610280613d70565b61028051808202821582848304141715615ce557905090506318160ddd6102a05260206102a060046102bc6001545afa6121f3573d600060003e3d6000fd5b601f3d1115615ce5576102a051808015615ce5578204905090506102e05260206102e0f35b6386fc88d381186122425734615ce557612233610300614192565b61030051610320526020610320f35b63a39e95c5811861231d57610144358060a01c615ce55760e052610164358060a01c615ce55761010052610184358060a01c615ce5576101205234615ce557601454615ce55733601d5560043560801b610140526024356101405117610140526101405160085561014051600955604435601455606435601555608435600c5560a435600e5560c43560105560e4356016556101243560045561012435600555610124356006554260075561010435601255670de0b6b3a7640000601f5560e05160015561010051600255610120516003556101a435602355005b63fc0c546a81186123395734615ce55760015460e052602060e0f35b63c661065781186123655734615ce55760016004356002811015615ce557026002015460e052602060e0f35b63b9e8c9fd81186123815734615ce55760045460e052602060e0f35b63c146bf94811861239d5734615ce55760065460e052602060e0f35b636112c74781186123b95734615ce55760075460e052602060e0f35b63204fe3d581186123d55734615ce55760085460e052602060e0f35b63f30cfad581186123f15734615ce55760095460e052602060e0f35b63e89876ff811861240d5734615ce557600a5460e052602060e0f35b63f9ed959781186124295734615ce557600b5460e052602060e0f35b6349fe9e7781186124455734615ce557600c5460e052602060e0f35b63727ced5781186124615734615ce557600d5460e052602060e0f35b6372d4f0e2811861247d5734615ce557600e5460e052602060e0f35b63d7c3dcbe81186124995734615ce557600f5460e052602060e0f35b63083812e581186124b55734615ce55760105460e052602060e0f35b634ea12c7d81186124d15734615ce55760115460e052602060e0f35b63662b627481186124ed5734615ce55760125460e052602060e0f35b630c5e23d481186125095734615ce55760135460e052602060e0f35b6392526c0c81186125255734615ce55760145460e052602060e0f35b63ee8de67581186125415734615ce55760155460e052602060e0f35b63fee3f7f9811861255d5734615ce55760165460e052602060e0f35b637cf9aedc81186125795734615ce55760175460e052602060e0f35b637d1b060c81186125955734615ce55760185460e052602060e0f35b63e382446281186125b15734615ce55760195460e052602060e0f35b634903b0d181186125dd5734615ce55760016004356002811015615ce55702601a015460e052602060e0f35b630f529ba281186125f95734615ce557601c5460e052602060e0f35b63c45a015581186126155734615ce557601d5460e052602060e0f35b637ba1a74d81186126315734615ce557601e5460e052602060e0f35b630b7b594b811861264d5734615ce557601f5460e052602060e0f35b630c46b72a81186126695734615ce55760205460e052602060e0f35b63405e28f881186126855734615ce55760225460e052602060e0f35b505b005b60235460e052604e60e05160081c1015615ce55760e05160081c600a0a61010052604e60ff60e051161015615ce55760ff60e05116600a0a60e05260e051815261010051816020015250565b6126e0610160612689565b61016080516101205280602001516101405250601a5461012051808202821582848304141715615ce557905090508152601b5461014051808202821582848304141715615ce55790509050600454808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050816020015250565b600b5460e052600954610100526fffffffffffffffffffffffffffffffff6101005116610120526101005160801c6101405260e0514210156128bb5760085461016052600a546101805260e0805161018051808210615ce557808203905090508152504261018051808210615ce557808203905090506101805260e05161018051808210615ce557808203905090506101a0526101605160801c6101a051808202821582848304141715615ce557905090506101405161018051808202821582848304141715615ce557905090508181830110615ce5578082019050905060e051808015615ce557820490509050610140526fffffffffffffffffffffffffffffffff61016051166101a051808202821582848304141715615ce557905090506101205161018051808202821582848304141715615ce557905090508181830110615ce5578082019050905060e051808015615ce557820490509050610120525b61014051815261012051816020015250565b600e546101205260e051610100518181830110615ce557808201905090506101405261012051670de0b6b3a7640000808202821582848304141715615ce5579050905061012051670de0b6b3a76400008181830110615ce55780820190509050673782dace9d90000060e051808202821582848304141715615ce5579050905061014051808015615ce55782049050905061010051808202821582848304141715615ce5579050905061014051808015615ce557820490509050808210615ce55780820390509050808015615ce5578204905090506101405260145461014051808202821582848304141715615ce55790509050601554670de0b6b3a764000061014051808210615ce55780820390509050808202821582848304141715615ce557905090508181830110615ce55780820190509050670de0b6b3a764000080820490509050815250565b60e05161014052610100516101605261012051612a36576000612a40565b6101605161014051105b15612a5557610100516101405260e051610160525b610140516101805260006101a0526101c0600060ff818352015b610180516101e052610180516101405161016051808202821582848304141715615ce5579050905061018051808015615ce5578204905090508181830110615ce55780820190509050600280820490509050610180526101e0516101805111612af1576101e05161018051808210615ce557808203905090506101a052612b0c565b610180516101e051808210615ce557808203905090506101a0525b60016101a0511115612b4357610180516101a051670de0b6b3a7640000808202821582848304141715615ce5579050905010612b46565b60015b15612b5957505061018051815250612be1565b8151600101808352811415612a6f57505060106101c0527f446964206e6f7420636f6e7665726765000000000000000000000000000000006101e0526101c0506101c051806101e001818260206001820306601f82010390500336823750506308c379a06101805260206101a0526101c05160206001820306601f820103905060440161019cfd5b565b610f9f6102005111612bf6576000612c01565b63ee6b280161020051105b15615ce5576402540be3ff6102205111612c1c576000612c2a565b66470de4df82000161022051105b15615ce5576102405161028052610260516102a0526102a051610280511015612c5e576102605161028052610240516102a0525b633b9ac9ff6102805111612c73576000612c88565b6d314dc6448d9338c15b0a0000000161028051105b15615ce557655af3107a3fff6102a051670de0b6b3a7640000808202821582848304141715615ce5579050905061028051808015615ce5578204905090501115615ce55760026102805160e0526102a05161010052600061012052612cee6102e0612a18565b6102e051808202821582848304141715615ce557905090506102c052610280516102a0518181830110615ce557808201905090506102e052610300600060ff818352015b6102c05161032052673782dace9d90000061028051808202821582848304141715615ce557905090506102c051808015615ce5578204905090506102a051808202821582848304141715615ce557905090506102c051808015615ce5578204905090506103405261022051670de0b6b3a76400008181830110615ce5578082019050905061036052610340516103605111612df8576103405161036051808210615ce5578082039050905060018181830110615ce5578082019050905061036052612e25565b6103605161034051808210615ce5578082039050905060018181830110615ce55780820190509050610360525b670de0b6b3a76400006102c051808202821582848304141715615ce5579050905061022051808015615ce55782049050905061036051808202821582848304141715615ce5579050905061022051808015615ce55782049050905061036051808202821582848304141715615ce55790509050612710808202821582848304141715615ce5579050905061020051808015615ce55782049050905061038052673782dace9d90000061034051808202821582848304141715615ce5579050905061036051808015615ce5578204905090506103a0526102e0516102e0516103a051808202821582848304141715615ce55790509050670de0b6b3a7640000808204905090508181830110615ce55780820190509050610380516002808202821582848304141715615ce5579050905061034051808015615ce5578204905090508181830110615ce557808201905090506103a0516102c051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050808210615ce557808203905090506103c0526102c0516103c0516102e0518181830110615ce55780820190509050808202821582848304141715615ce557905090506103c051808015615ce5578204905090506103e0526102c0516102c051808202821582848304141715615ce557905090506103c051808015615ce5578204905090506104005261034051670de0b6b3a7640000116130d35761040080516102c051610380516103c051808015615ce557820490509050808202821582848304141715615ce55790509050670de0b6b3a76400008082049050905061034051670de0b6b3a7640000808210615ce55780820390509050808202821582848304141715615ce5579050905061034051808015615ce557820490509050808210615ce55780820390509050815250613169565b61040080516102c051610380516103c051808015615ce557820490509050808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050670de0b6b3a764000061034051808210615ce55780820390509050808202821582848304141715615ce5579050905061034051808015615ce5578204905090508181830110615ce557808201905090508152505b610400516103e0511161319e57610400516103e051808210615ce557808203905090506002808204905090506102c0526131b9565b6103e05161040051808210615ce557808203905090506102c0525b600061042052610320516102c051116131eb57610320516102c051808210615ce5578082039050905061042052613206565b6102c05161032051808210615ce55780820390509050610420525b662386f26fc100006102c05180821061321f5781613221565b805b9050905061042051655af3107a4000808202821582848304141715615ce5579050905010156132e85761046060006002818352015b6020610460510261028001516104405261044051670de0b6b3a7640000808202821582848304141715615ce557905090506102c051808015615ce55782049050905061048052662386f26fc0ffff61048051116132b45760006132c4565b68056bc75e2d6310000161048051105b15615ce557815160010180835281141561325657505050506102c051815250613370565b8151600101808352811415612d325750506010610300527f446964206e6f7420636f6e7665726765000000000000000000000000000000006103205261030050610300518061032001818260206001820306601f82010390500336823750506308c379a06102c05260206102e0526103005160206001820306601f82010390506044016102dcfd5b565b610f9f60e0511161338457600061338e565b63ee6b280160e051105b15615ce5576402540be3ff61010051116133a95760006133b7565b66470de4df82000161010051105b15615ce55767016345785d89ffff61016051116133d55760006133ea565b6d314dc6448d9338c15b0a0000000161016051105b15615ce557610120600161018051808210615ce557808203905090506002811015615ce55760200201516101a052700100000000000000000000000000000000610160511015615ce5576002610160510a6101a0516004808202821582848304141715615ce55790509050808015615ce5578204905090506101c052671bc16d674ec800006101a051808202821582848304141715615ce5579050905061016051808015615ce5578204905090506101e05266470de4df81ffff6101e051116134b45760006134c4565b680ad78ebc5ac62000016101e051105b15615ce5576101a051655af3107a40008082049050905061016051655af3107a4000808204905090508082106134fa57816134fc565b805b90509050606480821061350f5781613511565b805b9050905061020052610220600060ff818352015b6101c051610240526101e0516101c051808202821582848304141715615ce557905090506002808202821582848304141715615ce5579050905061016051808015615ce557820490509050610260526101a0516101c0518181830110615ce557808201905090506102805261010051670de0b6b3a76400008181830110615ce557808201905090506102a052610260516102a051116135ef57610260516102a051808210615ce5578082039050905060018181830110615ce557808201905090506102a05261361c565b6102a05161026051808210615ce5578082039050905060018181830110615ce557808201905090506102a0525b670de0b6b3a764000061016051808202821582848304141715615ce5579050905061010051808015615ce5578204905090506102a051808202821582848304141715615ce5579050905061010051808015615ce5578204905090506102a051808202821582848304141715615ce55790509050612710808202821582848304141715615ce5579050905060e051808015615ce5578204905090506102c052670de0b6b3a7640000671bc16d674ec8000061026051808202821582848304141715615ce557905090506102a051808015615ce5578204905090508181830110615ce557808201905090506102e052670de0b6b3a76400006101c051808202821582848304141715615ce55790509050610280516102e051808202821582848304141715615ce557905090508181830110615ce557808201905090506102c0518181830110615ce5578082019050905061030052610160516102e051808202821582848304141715615ce55790509050610320526103205161030051106137ba57610300805161032051808210615ce557808203905090508152506137d0565b610240516002808204905090506101c052613a07565b610300516101c051808015615ce557820490509050610340526102c05161034051808015615ce5578204905090506103605261030051670de0b6b3a764000061016051808202821582848304141715615ce557905090508181830110615ce5578082019050905061034051808015615ce55782049050905061036051670de0b6b3a7640000808202821582848304141715615ce5579050905061026051808015615ce5578204905090508181830110615ce55780820190509050610380526103608051670de0b6b3a764000061028051808202821582848304141715615ce5579050905061034051808015615ce5578204905090508181830110615ce55780820190509050815250610360516103805110613904576103805161036051808210615ce557808203905090506101c052613916565b610240516002808204905090506101c0525b60006103a052610240516101c0511161394857610240516101c051808210615ce557808203905090506103a052613963565b6101c05161024051808210615ce557808203905090506103a0525b610200516101c051655af3107a4000808204905090508082106139865781613988565b805b905090506103a0511015613a07576101c051670de0b6b3a7640000808202821582848304141715615ce5579050905061016051808015615ce5578204905090506103c052662386f26fc0ffff6103c051116139e45760006139f4565b68056bc75e2d631000016103c051105b15615ce55750506101c051815250613a8f565b81516001018083528114156135255750506010610220527f446964206e6f7420636f6e7665726765000000000000000000000000000000006102405261022050610220518061024001818260206001820306601f82010390500336823750506308c379a06101e0526020610200526102205160206001820306601f82010390506044016101fcfd5b565b60e051670de0b6b3a7640000808204905090506101005260e05161010051670de0b6b3a7640000808202821582848304141715615ce55790509050808210615ce5578082039050905061012052603b610100511115613af4576000815250613d6e565b670de0b6b3a7640000610100610100511015615ce5576101005160020a808015615ce5578204905090506101405261012051613b365761014051815250613d6e565b670de0b6b3a7640000610160526706f05b59d3b2000061018052670de0b6b3a76400006101a05260006101c0526101e0600160ff818352015b6101e051670de0b6b3a7640000808202821582848304141715615ce557905090506102005261020051670de0b6b3a7640000808210615ce5578082039050905061022052610220516101205111613bdf57610220805161012051808210615ce55780820390509050815250613c03565b6101205161022051808210615ce55780820390509050610220526101c051156101c0525b610160516102205161018051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050808202821582848304141715615ce5579050905061020051808015615ce557820490509050610160526101c051613c85576101a08051610160518181830110615ce55780820190509050815250613ca0565b6101a0805161016051808210615ce557808203905090508152505b6402540be400610160511015613ce6575050610140516101a051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050815250613d6e565b8151600101808352811415613b6f57505060106101e0527f446964206e6f7420636f6e766572676500000000000000000000000000000000610200526101e0506101e0518061020001818260206001820306601f82010390500336823750506308c379a06101a05260206101c0526101e05160206001820306601f82010390506044016101bcfd5b565b610200516002808204905090506102205261020051670de0b6b3a7640000808202821582848304141715615ce557905090506004546002808202821582848304141715615ce55790509050808015615ce557820490509050610240526102205160e0526102405161010052600161012052613dec610260612a18565b61026051815250565b613e006104e061275a565b6104e080516104a05280602001516104c05250601e546104e052601f546105005261052060006002818352015b6001610520516002811015615ce5570260020154610540526020602038036080396080516105405118613e7457476001610520516002811015615ce55702601a0155613ec3565b6370a082316105605230610580526020610560602461057c610540515afa613ea1573d600060003e3d6000fd5b601f3d1115615ce557610560516001610520516002811015615ce55702601a01555b8151600101808352811415613e2d57505060205461052052610500516104e0511115614092576104e05161050051808210615ce55780820390509050601654808202821582848304141715615ce557905090506404a817c800808204905090506105405260006105405111156140925763cab4d3db610580526020610580600461059c601d545afa613f5a573d600060003e3d6000fd5b601f3d1115615ce557610580518060a01c615ce55761056052600061056051146140925761052051670de0b6b3a7640000808202821582848304141715615ce557905090506105205161054051808210615ce55780820390509050808015615ce557820490509050670de0b6b3a7640000808210615ce5578082039050905061058052636962f8456105c052610560516105e052610580516106005260206105c060446105dc60006001545af1614016573d600060003e3d6000fd5b601f3d1115615ce5576105c0516105a0526104e08051610540516002808202821582848304141715615ce55790509050808210615ce557808203905090508152506104e051601e55610560517f6059a38198b1dc42b3791087d1ff0fbd72b3179553c25f678cd246f52ffaaf596105a0516105c05260206105c0a25b6318160ddd610560526020610560600461057c6001545afa6140b9573d600060003e3d6000fd5b601f3d1115615ce55761056051610540526104a0516105e0526104c051610600526140e56105806126d5565b610580805161062052806020015161064052506105e051610200526106005161022052610620516102405261064051610260526141236105c0612be3565b6105c0516105605261056051601c55670de0b6b3a7640000610560516102005261414e610580613d70565b61058051808202821582848304141715615ce5579050905061054051808015615ce557820490509050602055610500516104e0511115614190576104e051601f555b565b60055461024052600754610260524261026051106141ba576102405181525061429656614296565b601254610280526006546102a0524261026051808210615ce55780820390509050670de0b6b3a7640000808202821582848304141715615ce5579050905061028051808015615ce55782049050905060e0526142176102e0613a91565b6102e0516102c0526102a051670de0b6b3a76400006102c051808210615ce55780820390509050808202821582848304141715615ce55790509050610240516102c051808202821582848304141715615ce557905090508181830110615ce55780820190509050670de0b6b3a764000080820490509050815250614296565b565b6005546107205260065461074052600454610760526007546107805260006107a052426107805110156143a2576012546107c0524261078051808210615ce55780820390509050670de0b6b3a7640000808202821582848304141715615ce557905090506107c051808015615ce55782049050905060e05261431b610800613a91565b610800516107e05261074051670de0b6b3a76400006107e051808210615ce55780820390509050808202821582848304141715615ce55790509050610720516107e051808202821582848304141715615ce557905090508181830110615ce55780820190509050670de0b6b3a7640000808204905090506107205261072051600555426007555b610700516107c052610700516143e657610660516102005261068051610220526106a051610240526106c051610260526143dd6107e0612be3565b6107e0516107c0525b60006106e051116144b0576106a0516107e0526106c051610800526107e051620f424080820490509050610820526107e08051610820518181830110615ce557808201905090508152506107605161082051808202821582848304141715615ce557905090506106c0516106605160e05261068051610100526107e0516101205261080051610140526107c05161016052600161018052614488610840613372565b61084051808210615ce55780820390509050808015615ce557820490509050610740526144b9565b6106e051610740525b610740516006556318160ddd610800526020610800600461081c6001545afa6144e7573d600060003e3d6000fd5b601f3d1115615ce557610800516107e052601e5461080052602054610820526107c051600280820490509050610840526107c051670de0b6b3a7640000808202821582848304141715615ce55790509050600261076051808202821582848304141715615ce55790509050808015615ce55782049050905061086052670de0b6b3a764000061088052670de0b6b3a76400006108a05260006108205111156146c6576108405160e05261086051610100526001610120526145a96108e0612a18565b6108e0516108c052670de0b6b3a76400006108c051808202821582848304141715615ce557905090506107e051808015615ce5578204905090506108a052610800516108a051808202821582848304141715615ce5579050905061082051808015615ce55782049050905061088052600b546108e052610820516108a05110614633576000614639565b6108e051155b156146b5576004610900527f4c6f7373000000000000000000000000000000000000000000000000000000006109205261090050610900518061092001818260206001820306601f82010390500336823750506308c379a06108c05260206108e0526109005160206001820306601f82010390506044016108dcfd5b60016108e051186146c6576000600b555b61088051601e5561072051670de0b6b3a7640000808202821582848304141715615ce5579050905061076051808015615ce5578204905090506108c052670de0b6b3a76400006108c0511161473957670de0b6b3a76400006108c051808210615ce557808203905090506108c052614759565b6108c08051670de0b6b3a7640000808210615ce557808203905090508152505b6010546108c0516005808204905090508082106147765781614778565b805b905090506108e05260215461090052610900511561479757600061481d565b610880516002600c54808202821582848304141715615ce557905090508181830110615ce557808201905090506108a0516002808202821582848304141715615ce55790509050670de0b6b3a7640000808210615ce557808203905090501161480157600061481d565b6108e0516108c0511161481557600061481d565b600061082051115b1561482e5760016109005260016021555b6109005115614a77576108e0516108c0511161484b576000614853565b600061082051115b15614a7757610760516108c0516108e051808210615ce55780820390509050808202821582848304141715615ce557905090506108e05161072051808202821582848304141715615ce557905090508181830110615ce557808201905090506108c051808015615ce5578204905090506107a0526106a051610840526106c0516107a051808202821582848304141715615ce5579050905061076051808015615ce55782049050905061086052610660516102005261068051610220526108405161024052610860516102605261492b610940612be3565b6109405161092052610920516002808204905090506108405261092051670de0b6b3a7640000808202821582848304141715615ce5579050905060026107a051808202821582848304141715615ce55790509050808015615ce55782049050905061086052670de0b6b3a76400006108405160e05261086051610100526001610120526149b9610940612a18565b61094051808202821582848304141715615ce557905090506107e051808015615ce55782049050905061082052670de0b6b3a764000061082051116149ff576000614a36565b61088051600261082051808202821582848304141715615ce55790509050670de0b6b3a7640000808210615ce55780820390509050115b614a5d5760006021556107c051601c556108a051602055614a9b613df556614a9b56614a77565b6107a05160045561092051601c5561082051602055614a9b565b6107c051601c556108a0516020556109005115614a9b576000602155614a9b613df5565b565b6109c0516109a05114615ce55760026109a0511015615ce55760026109c0511015615ce55760006109e0511115615ce557614ad9610ae061275a565b610ae08051610aa0528060200151610ac05250601a54610ae052601b54610b0052604036610b203760016109a0516002811015615ce5570260020154610b605260016109c0516002811015615ce5570260020154610b8052610ae06109c0516002811015615ce5576020020151610ba052610ae06109a0516002811015615ce5576020020151610bc052610bc0516109e0518181830110615ce55780820190509050610ae06109a0516002811015615ce5576020020152610ae06109a0516002811015615ce557602002015160016109a0516002811015615ce55702601a0155600454610be052614bcb610c40612689565b610c408051610c00528060200151610c205250610ae051610c0051808202821582848304141715615ce55790509050610ae052610b0051610be051808202821582848304141715615ce55790509050610c2051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610b0052610c0051610c4052610c2051610c605260016109a05118614c7257610c2051610c4052610c0051610c60525b600b54610c80526000610c80511115614d6f57610bc08051610c4051808202821582848304141715615ce5579050905081525060006109a0511115614ce257610bc051610be051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610bc0525b610ae06109a0516002811015615ce5576020020151610ca052610bc051610ae06109a0516002811015615ce5576020020152610aa05161020052610ac05161022052610ae05161024052610b005161026052614d3f610cc0612be3565b610cc051601c55610ca051610ae06109a0516002811015615ce5576020020152610c80514210614d6f576001600b555b610ae06109c0516002811015615ce5576020020151610aa05160e052610ac05161010052610ae05161012052610b005161014052601c54610160526109c05161018052614dbd610ca0613372565b610ca051808210615ce55780820390509050610b4052610ae06109c0516002811015615ce557602002018051610b4051808210615ce55780820390509050815250610b4080516001808210615ce5578082039050905081525060006109c0511115614e5957610b4051670de0b6b3a7640000808202821582848304141715615ce55790509050610be051808015615ce557820490509050610b40525b610b408051610c6051808015615ce557820490509050815250610b408051610ae05160e052610b005161010052614e91610ca06128cd565b610ca051610b4051808202821582848304141715615ce557905090506402540be40080820490509050808210615ce55780820390509050815250610a0051610b40511015614f50576008610ca0527f536c697070616765000000000000000000000000000000000000000000000000610cc052610ca050610ca05180610cc001818260206001820306601f82010390500336823750506308c379a0610c60526020610c8052610ca05160206001820306601f8201039050604401610c7cfd5b610ba08051610b4051808210615ce55780820390509050815250610ba05160016109c0516002811015615ce55702601a0155610a2051614f91576000614fa3565b602060203803608039608051610b6051145b61521c5761098051615ce557610a805115615116576370a08231610cc05230610ce0526020610cc06024610cdc610b60515afa614fe5573d600060003e3d6000fd5b601f3d1115615ce557610cc051610ca05260006000600460208206610cc001602082840111615ce557602080610ce082610a8060045afa505081815290509050600480602084610de00101826020850160045afa50508051820191505061096051610d4052610a4051610d6052610b6051610d80526109e051610da052610b4051610dc05260a0610d2052610d2060a080602084610de00101826020850160045afa50508051820191505080610de052610de0505060006000610de051610e006000610a60515af16150bc573d600060003e3d6000fd5b6109e0516370a08231610cc05230610ce0526020610cc06024610cdc610b60515afa6150ed573d600060003e3d6000fd5b601f3d1115615ce557610cc051610ca051808210615ce5578082039050905018615ce5576151b8565b6323b872dd610ce452600461096051610d045230610d24526109e051610d4452606001610ce052610ce0506020610da0610ce051610d006000610b60515af1615164573d600060003e3d6000fd5b610d8060203d8082116151775781615179565b805b905090508152805160200180610ca0828460045afa905050506000610ca051146151b857610cc051610ca05181816020036008021c9050905015615ce5575b602060203803608039608051610b60511861522a57632e1a7d4d610ca0526109e051610cc0526020602038036080396080513b15615ce557600060006024610cbc60006020602038036080396080515af161522a573d600060003e3d6000fd61522a565b6109e0516109805118615ce5575b610a205161523957600061524b565b602060203803608039608051610b8051145b61534a57602060203803608039608051610b8051186152a95763d0e30db0610ca0526020602038036080396080513b15615ce557600060006004610cbc610b40516020602038036080396080515af16152a9573d600060003e3d6000fd5b63a9059cbb610ce4526004610a4051610d0452610b4051610d2452604001610ce052610ce0506020610d80610ce051610d006000610b80515af16152f2573d600060003e3d6000fd5b610d6060203d8082116153055781615307565b805b905090508152805160200180610ca0828460045afa905050506000610ca0511461537857610cc051610ca05181816020036008021c9050905015615ce557615378565b6000610ca052610ca05060006000610ca051610cc0610b4051610a40515af1615378573d600060003e3d6000fd5b610ba08051610c6051808202821582848304141715615ce5579050905081525060006109c05111156153d557610ba051610be051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610ba0525b610ba051610ae06109c0516002811015615ce5576020020152620186a06109e0511161540257600061540c565b620186a0610b4051115b156154cc576109e051610c4051808202821582848304141715615ce55790509050610ca052610b4051610c6051808202821582848304141715615ce55790509050610cc0526109a0511561549557610cc051670de0b6b3a7640000808202821582848304141715615ce55790509050610ca051808015615ce557820490509050610b20526154cc565b610ca051670de0b6b3a7640000808202821582848304141715615ce55790509050610cc051808015615ce557820490509050610b20525b610aa05161066052610ac05161068052610ae0516106a052610b00516106c052610b20516106e052600061070052615502614298565b610960517fb2e76ae99761dc136e598d4a629bb347eccb9532a5f8bbd72e18467c3c34cc986109a051610ca0526109e051610cc0526109c051610ce052610b4051610d00526080610ca0a2610b4051815250565b6101a05160e0526101c051610100526155706102006128cd565b610200516002808202821582848304141715615ce557905090506004808204905090506101e05260006102005261024060006002818352015b602061024051026101600151610220526102008051610220518181830110615ce5578082019050905081525081516001018083528114156155a9575050610200516002808204905090506102205260006102405261028060006002818352015b602061028051026101600151610260526102205161026051116156595761024080516102205161026051808210615ce557808203905090508181830110615ce55780820190509050815250615688565b61024080516102605161022051808210615ce557808203905090508181830110615ce557808201905090508152505b81516001018083528114156156095750506101e05161024051808202821582848304141715615ce5579050905061020051808015615ce557820490509050620186a08181830110615ce55780820190509050815250565b6318160ddd610580526020610580600461059c6001545afa615706573d600060003e3d6000fd5b601f3d1115615ce5576105805161056052610560516104e05111615ce5576002610500511015615ce557601a5461058052601b546105a05260006105c05261574f610620612689565b61062080516105e0528060200151610600525060045461060051808202821582848304141715615ce5579050905061062052610580516105e051808202821582848304141715615ce55790509050610640526105a05161062051808202821582848304141715615ce55790509050670de0b6b3a76400008082049050905061066052610500516157ff57670de0b6b3a76400006105e051808202821582848304141715615ce55790509050610620525b6105205161581357601c546105c052615847565b6104a051610200526104c051610220526106405161024052610660516102605261583e610680612be3565b610680516105c0525b6105c051610680526106405160e05261066051610100526158696106c06128cd565b6106c0516106a0526104e05161068051808202821582848304141715615ce5579050905061056051808015615ce5578204905090506106c05261068080516106c0516106a0516106c051808202821582848304141715615ce557905090506404a817c8008082049050905060018181830110615ce55780820190509050808210615ce55780820390509050808210615ce557808203905090508152506104a05160e0526104c05161010052610640516101205261066051610140526106805161016052610500516101805261593f610700613372565b610700516106e052610640610500516002811015615ce55760200201516106e051808210615ce55780820390509050670de0b6b3a7640000808202821582848304141715615ce5579050905061062051808015615ce557820490509050610700526106e051610640610500516002811015615ce5576020020152600061072052610540516159ce5760006159ec565b620186a061070051116159e25760006159ec565b620186a06104e051115b15615b69576000610740526105e0516107605260016105005118615a3757610580516105e051808202821582848304141715615ce55790509050610740526106005161076052615a58565b6105a05161060051808202821582848304141715615ce55790509050610740525b610740516106c051808202821582848304141715615ce557905090506105c051808015615ce5578204905090506107405261074051670de0b6b3a7640000808202821582848304141715615ce557905090506107005161076051808202821582848304141715615ce557905090506106c051610580610500516002811015615ce5576020020151808202821582848304141715615ce5579050905061076051808202821582848304141715615ce557905090506105c051808015615ce557820490509050808210615ce55780820390509050808015615ce5578204905090506107205261050051615b69576ec097ce7bc90715b34b9f100000000061072051808015615ce557820490509050610720525b610700518152610720516020820152610680516040820152606081016106405181526106605181602001525050565b60e051615ba9576000815250615ce3565b60e051670de0b6b3a76400008181830110615ce557808201905090506002808204905090506101005260e051610120526101406000610100818352015b610120516101005118615c0157505061012051815250615ce3565b610100516101205260e051670de0b6b3a7640000808202821582848304141715615ce5579050905061010051808015615ce557820490509050610100518181830110615ce55780820190509050600280820490509050610100528151600101808352811415615be65750506010610140527f446964206e6f7420636f6e7665726765000000000000000000000000000000006101605261014050610140518061016001818260206001820306601f82010390500336823750506308c379a0610100526020610120526101405160206001820306601f820103905060440161011cfd5b565b600080fd5b610029615d130361002961012039610029615d13036101005181610120015280602001610120f35b600080fd000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "deployedBytecode": "0x6020615d406080396080518060a01c615d3b5760e05260e051610100526301500786601455615d1356600436101561000d57612687565b60046000601c37600051635b41b9088118610032576000610ee05233610f0052610080565b63394747c58118610056576084358060011c615ce557610ee05233610f0052610080565b63ce7d650381186100d9576084358060011c615ce557610ee05260a4358060a01c615ce557610f00525b600054615ce557600160005533610960523461098052608060046109a037610ee051610a2052610f0051610a40526000610a60526000610a80526100c5610f20614a9d565b610f2051610f40526020610f406000600055f35b6365b2489b81186100ee5733610ee052610109565b63e2ad025a8118610160576084358060a01c615ce557610ee0525b600054615ce557600160005533610960523461098052608060046109a0376001610a2052610ee051610a40526000610a60526000610a805261014c610f00614a9d565b610f0051610f20526020610f206000600055f35b63dd96994f81186101fe576084358060011c615ce557610ee05260a4358060a01c615ce557610f005260c4358060a01c615ce557610f2052600054615ce5576001600055600060e43514615ce557610f0051610960523461098052608060046109a037610ee051610a2052610f2051610a405233610a605260e435610a80526101ea610f40614a9d565b610f4051610f60526020610f606000600055f35b630b4c7e4d8118610219576000610960523361098052610267565b63ee22be23811861023d576064358060011c615ce557610960523361098052610267565b637328333b8118610bc8576064358060011c615ce557610960526084358060a01c615ce557610980525b600054615ce55760016000556000600435116102885760006024351161028b565b60015b15615ce55761029b6109e061275a565b6109e080516109a05280602001516109c05250601a546109e052601b54610a005260e036610a20376109e051610b0052610a0051610b2052610b4060006002818352015b6109e0610b40516002811015615ce55760200201516020610b405102600401358181830110615ce55780820190509050610b6052610b60516109e0610b40516002811015615ce5576020020152610b60516001610b40516002811015615ce55702601a015581516001018083528114156102df5750506109e051610a6052610a0051610a8052610370610b80612689565b610b808051610b40528060200151610b605250600454610b6051808202821582848304141715615ce55790509050610b80526109e051610b4051808202821582848304141715615ce557905090506109e052610a0051610b8051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610a0052610b0051610b4051808202821582848304141715615ce55790509050610b0052610b2051610b8051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610b2052610960516104505734615ce5575b610ba060006002818352015b6001610ba0516002811015615ce5570260020154610bc05261096051610483576000610495565b602060203803608039608051610bc051145b156104ac576020610ba05102600401353418615ce5575b60006020610ba051026004013511156106425761096051156104df57602060203803608039608051610bc05114156104e2565b60015b156105f4576323b872dd610c2452600433610c445230610c64526020610ba0510260040135610c8452606001610c2052610c20506020610ce0610c2051610c406000610bc0515af1610539573d600060003e3d6000fd5b610cc060203d80821161054c578161054e565b805b905090508152805160200180610be0828460045afa905050506000610be0511461058d57610c0051610be05181816020036008021c9050905015615ce5575b602060203803608039608051610bc051186105f457632e1a7d4d610c20526020610ba0510260040135610c40526020602038036080396080513b15615ce557600060006024610c3c60006020602038036080396080515af16105f4573d600060003e3d6000fd5b6109e0610ba0516002811015615ce5576020020151610b00610ba0516002811015615ce5576020020151808210615ce55780820390509050610a20610ba0516002811015615ce55760200201525b815160010180835281141561045c575050600b54610ba0526000610ba0511161067157601c54610ae0526106b4565b6109a051610200526109c05161022052610b005161024052610b20516102605261069c610bc0612be3565b610bc051610ae052610ba05142106106b4576001600b555b6109a051610200526109c051610220526109e05161024052610a0051610260526106df610be0612be3565b610be051610bc052600154610be0526318160ddd610c20526020610c206004610c3c610be0515afa610716573d600060003e3d6000fd5b601f3d1115615ce557610c2051610c00526000610ae0511161075257610bc05161020052610745610c20613d70565b610c2051610aa052610796565b610c0051610bc051808202821582848304141715615ce55790509050610ae051808015615ce557820490509050610c0051808210615ce55780820390509050610aa0525b6000610aa0511115615ce5576000610ae0511161081857610bc051601c55670de0b6b3a7640000602055670de0b6b3a7640000601e556340c10f19610c205261098051610c4052610aa051610c60526020610c206044610c3c6000610be0515af1610806573d600060003e3d6000fd5b601f3d1115615ce557610c2050610ae7565b610a205161016052610a4051610180526109e0516101a052610a00516101c052610843610c20615556565b610c2051610aa051808202821582848304141715615ce557905090506402540be4008082049050905060018181830110615ce55780820190509050610ac052610aa08051610ac051808210615ce55780820390509050815250610c008051610aa0518181830110615ce557808201905090508152506340c10f19610c205261098051610c4052610aa051610c60526020610c206044610c3c6000610be0515af16108f2573d600060003e3d6000fd5b601f3d1115615ce557610c20506000610c2052620186a0610aa0511115610aaf57600435156109245760243515610927565b60015b15610aaf57606036610c40376004351561096857610a8051610b6051808202821582848304141715615ce55790509050610c4052610b4051610c6052610997565b610a6051610b4051808202821582848304141715615ce55790509050610c4052610b6051610c60526001610c80525b610c4051610aa051808202821582848304141715615ce55790509050610c0051808015615ce557820490509050610c4052610c4051670de0b6b3a7640000808202821582848304141715615ce557905090506020610c80510260040135610c6051808202821582848304141715615ce55790509050610aa051610a60610c80516002811015615ce5576020020151808202821582848304141715615ce55790509050610c6051808202821582848304141715615ce55790509050610c0051808015615ce557820490509050808210615ce55780820390509050808015615ce557820490509050610c2052610c8051610aaf576ec097ce7bc90715b34b9f1000000000610c2051808015615ce557820490509050610c20525b6109a051610660526109c051610680526109e0516106a052610a00516106c052610c20516106e052610bc05161070052610ae7614298565b604435610aa0511015610b6b576008610c20527f536c697070616765000000000000000000000000000000000000000000000000610c4052610c2050610c205180610c4001818260206001820306601f82010390500336823750506308c379a0610be0526020610c0052610c205160206001820306601f8201039050604401610bfcfd5b610980517f540ab385f9b5d450a27404172caade516b3ba3f4be88239ac56a2ad1de2a1f5a600435610c2052602435610c4052610ac051610c6052610c0051610c80526080610c20a2610aa051610c20526020610c206000600055f35b635b36389c8118610be257600060e0523361010052610c2e565b63269b55818118610c05576064358060011c615ce55760e0523361010052610c2e565b631808e84a8118610fb4576064358060011c615ce55760e0526084358060a01c615ce557610100525b34615ce557600054615ce5576001600055600154610120526318160ddd610160526020610160600461017c610120515afa610c6e573d600060003e3d6000fd5b601f3d1115615ce55761016051610140526379cc67906101605233610180526004356101a0526020610160604461017c6000610120515af1610cb5573d600060003e3d6000fd5b601f3d1115615ce55761016050601a5461016052601b54610180526004356001808210615ce557808203905090506101a0526101c060006002818352015b6101606101c0516002811015615ce55760200201516101a051808202821582848304141715615ce5579050905061014051808015615ce5578204905090506101e05260206101c05102602401356101e05110615ce5576101606101c0516002811015615ce55760200201516101e051808210615ce5578082039050905060016101c0516002811015615ce55702601a01556101e0516101606101c0516002811015615ce557602002015260016101c0516002811015615ce55702600201546102005260e051610dc3576000610dd5565b60206020380360803960805161020051145b610ed4576020602038036080396080516102005118610e335763d0e30db0610220526020602038036080396080513b15615ce55760006000600461023c6101e0516020602038036080396080515af1610e33573d600060003e3d6000fd5b63a9059cbb61026452600461010051610284526101e0516102a45260400161026052610260506020610300610260516102806000610200515af1610e7c573d600060003e3d6000fd5b6102e060203d808211610e8f5781610e91565b805b905090508152805160200180610220828460045afa9050505060006102205114610f0257610240516102205181816020036008021c9050905015615ce557610f02565b6000610220526102205060006000610220516102406101e051610100515af1610f02573d600060003e3d6000fd5b8151600101808352811415610cf3575050601c546101c0526101c0516101c0516101a051808202821582848304141715615ce5579050905061014051808015615ce557820490509050808210615ce55780820390509050601c55337fdd3c0336a16f1b64f172b7bb0dad5b2b3c7c76f91e8c4aafd6aae60dce800153610160516101e052610180516102005261014051600435808210615ce557808203905090506102205260606101e0a26000600055005b63f1dc3cc98118610fcf57600061096052336109805261101d565b638f15b6b58118610ff3576064358060011c615ce55761096052336109805261101d565b6307329bcd81186113a6576064358060011c615ce557610960526084358060a01c615ce557610980525b34615ce557600054615ce55760016000556110396109e061275a565b6109e080516109a05280602001516109c0525060a0366109e037600b54610a80526109a0516104a0526109c0516104c052604060046104e0376000610a8051116105205260016105405261108e610aa06156df565b610aa080516109e0526020810151610a20526040810151610a0052606081018051610a40528060200151610a605250506044356109e0511015611142576008610aa0527f536c697070616765000000000000000000000000000000000000000000000000610ac052610aa050610aa05180610ac001818260206001820306601f82010390500336823750506308c379a0610a60526020610a8052610aa05160206001820306601f8201039050604401610a7cfd5b610a80514210611152576001600b555b60016024356002811015615ce55702601a0180546109e051808210615ce557808203905090508155506379cc6790610aa05233610ac052600435610ae0526020610aa06044610abc60006001545af16111b0573d600060003e3d6000fd5b601f3d1115615ce557610aa05060016024356002811015615ce5570260020154610aa052610960516111e35760006111f5565b602060203803608039608051610aa051145b6112f457602060203803608039608051610aa051186112535763d0e30db0610ac0526020602038036080396080513b15615ce557600060006004610adc6109e0516020602038036080396080515af1611253573d600060003e3d6000fd5b63a9059cbb610b0452600461098051610b24526109e051610b4452604001610b0052610b00506020610ba0610b0051610b206000610aa0515af161129c573d600060003e3d6000fd5b610b8060203d8082116112af57816112b1565b805b905090508152805160200180610ac0828460045afa905050506000610ac0511461132257610ae051610ac05181816020036008021c9050905015615ce557611322565b6000610ac052610ac05060006000610ac051610ae06109e051610980515af1611322573d600060003e3d6000fd5b6109a051610660526109c05161068052610a40516106a052610a60516106c052610a20516106e052610a00516107005261135a614298565b337f5ad056f2e28a8cec232015406b843668c1e36cda598127ec3b8c59b8c72773a060406004610ac0376109e051610b00526060610ac0a26109e051610ac0526020610ac06000600055f35b63c93f49e881186113d15734615ce557600054615ce55760016000556113ca613df5565b6000600055005b635e24807281186115fe5734615ce55763f851a4406101c05260206101c060046101dc601d545afa611408573d600060003e3d6000fd5b601f3d1115615ce5576101c0513318615ce557600a546201517f8181830110615ce55780820190509050421115615ce557426201517f8181830110615ce557808201905090506044351115615ce55761146261020061275a565b61020080516101c05280602001516101e052506101c05160801b610200526101e051610200511761020052610f9f6004351115615ce55763ee6b28016004351015615ce5576402540be3ff6024351115615ce55766470de4df8200016024351015615ce557670de0b6b3a7640000600435808202821582848304141715615ce557905090506101c051808015615ce55782049050905061022052678ac7230489e80001610220511015615ce55767016345785d89ffff610220511115615ce557670de0b6b3a7640000602435808202821582848304141715615ce557905090506101e051808015615ce55782049050905061022052678ac7230489e80001610220511015615ce55767016345785d89ffff610220511115615ce5576102005160085542600a5560043560801b61024052602435610240511761024052604435600b55610240516009557fe35f0559b0642164e286b30df2077ec3a05426617a25db7578fd20ba39a6cd056101c05161026052600435610280526101e0516102a0526024356102c052426102e0526044356103005260c0610260a1005b63244c7c2e81186116d25734615ce55763f851a4406101c05260206101c060046101dc601d545afa611635573d600060003e3d6000fd5b601f3d1115615ce5576101c0513318615ce55761165361020061275a565b61020080516101c05280602001516101e052506101c05160801b610200526101e051610200511761020052610200516008556102005160095542600a5542600b557f5f0e7fba3d100c9e19446e1c92fe436f0a9a22fe99669360e4fdd6d3de2fc2846101c051610220526101e0516102405242610260526060610220a1005b63a43c335181186118c55734615ce55763f851a44060e052602060e0600460fc601d545afa611706573d600060003e3d6000fd5b601f3d1115615ce55760e0513318615ce557602254615ce55760e0600460e0376402540be40161010051106117415760155461010052611750565b6207a11f610100511115615ce5575b6402540be40060e05111156117665760145460e0525b6101005160e05111615ce5576402540be40061012051111561178a57601654610120525b670de0b6b3a764000061014051106117a857600e54610140526117b5565b6000610140511115615ce5575b670de0b6b3a76400006101605111156117d057600c54610160525b670de0b6b3a76400006101805111156117eb57601054610180525b62093a806101a05110611804576012546101a052611811565b60006101a0511115615ce5575b426203f4808181830110615ce557808201905090506101c0526101c0516022556101205160195560e0516017556101005160185561014051600f5561016051600d55610180516011556101a0516013556101c0517f913fde9a37e1f8ab67876a4d0ce80790d764fcfc5692f4529526df9c6bdde553610120516101e05260e0516102005261010051610220526101405161024052610160516102605261018051610280526101a0516102a05260e06101e0a2005b632a7dd7cd8118611a105734615ce557600054615ce557600160005563f851a440610660526020610660600461067c601d545afa611908573d600060003e3d6000fd5b601f3d1115615ce557610660513318615ce5576022544210615ce557600060225414615ce55760006022556019546106605261066051601654146119565761194e613df5565b610660516016555b60175461068052610680516014556018546106a0526106a051601555600f546106c0526106c051600e55600d546106e0526106e051600c55601154610700526107005160105560135461072052610720516012557f1c65bbdc939f346e5d6f0bde1f072819947438d4fc7b182cc59c2f6dc5504087610660516107405261068051610760526106a051610780526106c0516107a0526106e0516107c052610700516107e052610720516108005260e0610740a16000600055005b63226840fb8118611a5d5734615ce55763f851a44060e052602060e0600460fc601d545afa611a44573d600060003e3d6000fd5b601f3d1115615ce55760e0513318615ce5576000602255005b63556d6e9f8118611d405734615ce55760243560043514615ce55760026004351015615ce55760026024351015615ce557611a996104e0612689565b6104e080516104a05280602001516104c052506004546104c051808202821582848304141715615ce557905090506104e052601a5461050052601b5461052052611ae461058061275a565b61058080516105405280602001516105605250601c54610580526000600b541115611b6b5761054051610600526105605161062052611b246105a06126d5565b6105a0805161064052806020015161066052506106005161020052610620516102205261064051610240526106605161026052611b626105e0612be3565b6105e051610580525b6105006004356002811015615ce5576020020180516044358181830110615ce55780820190509050815250610500516104a051808202821582848304141715615ce5579050905061050052610520516104e051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610520526105405160e052610560516101005261050051610120526105205161014052610580516101605260243561018052611c1f6105c0613372565b6105c0516105a0526105006024356002811015615ce55760200201516105a051808210615ce557808203905090506001808210615ce557808203905090506105c0526105a0516105006024356002811015615ce5576020020152600060243511611ca1576105c080516104a051808015615ce557820490509050815250611cd8565b6105c051670de0b6b3a7640000808202821582848304141715615ce557905090506104e051808015615ce5578204905090506105c0525b6105c080516105005160e0526105205161010052611cf76105e06128cd565b6105e0516105c051808202821582848304141715615ce557905090506402540be40080820490509050808210615ce557808203905090508152506105c0516105e05260206105e0f35b638d8ea7278118611fce5734615ce5576318160ddd6104c05260206104c060046104dc6001545afa611d77573d600060003e3d6000fd5b601f3d1115615ce5576104c0516104a052611d93610500612689565b61050080516104c05280602001516104e052506004546104e051808202821582848304141715615ce5579050905061050052611dd061056061275a565b61056080516105205280602001516105405250611dee6105a06126d5565b6105a0805161056052806020015161058052506004356104c051808202821582848304141715615ce557905090506105a05260243561050051808202821582848304141715615ce55790509050670de0b6b3a7640000808204905090506105c052601c546105e0526000600b541115611e95576105205161020052610540516102205261056051610240526105805161026052611e8c610600612be3565b610600516105e0525b61056080516105a0518181830110615ce5578082019050905081525061058080516105c0518181830110615ce557808201905090508152506105205161020052610540516102205261056051610240526105805161026052611ef8610620612be3565b61062051610600526104a05161060051808202821582848304141715615ce557905090506105e051808015615ce5578204905090506104a051808210615ce557808203905090506106205261062080516105a051610160526105c05161018052610560516101a052610580516101c052611f73610640615556565b6106405161062051808202821582848304141715615ce557905090506402540be4008082049050905060018181830110615ce55780820190509050808210615ce5578082039050905081525061062051610640526020610640f35b634fb08c5e811861205a5734615ce557611fe961078061275a565b61078080516108605280602001516108805250604060046108a03760016108e052600061090052610860516104a052610880516104c0526108a0516104e0526108c051610500526108e05161052052610900516105405261204b6107c06156df565b6107c051610920526020610920f35b6354f0f7d581186120db5734615ce5576002602054808202821582848304141715615ce5579050905061208e610300614192565b61030051610340526103405160e0526120a8610320615b98565b61032051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610360526020610360f35b63f446c1d081186121055734615ce5576120f66101c061275a565b6101c051610200526020610200f35b63b137392981186121325734615ce5576121206101c061275a565b6101c060200151610200526020610200f35b63ddca3f4381186121895734615ce55761214d6101a06126d5565b6101a0805161020052806020015161022052506102005160e052610220516101005261217a6101e06128cd565b6101e051610240526020610240f35b63bb7b8b8081186122185734615ce557670de0b6b3a7640000601c54610200526121b4610280613d70565b61028051808202821582848304141715615ce557905090506318160ddd6102a05260206102a060046102bc6001545afa6121f3573d600060003e3d6000fd5b601f3d1115615ce5576102a051808015615ce5578204905090506102e05260206102e0f35b6386fc88d381186122425734615ce557612233610300614192565b61030051610320526020610320f35b63a39e95c5811861231d57610144358060a01c615ce55760e052610164358060a01c615ce55761010052610184358060a01c615ce5576101205234615ce557601454615ce55733601d5560043560801b610140526024356101405117610140526101405160085561014051600955604435601455606435601555608435600c5560a435600e5560c43560105560e4356016556101243560045561012435600555610124356006554260075561010435601255670de0b6b3a7640000601f5560e05160015561010051600255610120516003556101a435602355005b63fc0c546a81186123395734615ce55760015460e052602060e0f35b63c661065781186123655734615ce55760016004356002811015615ce557026002015460e052602060e0f35b63b9e8c9fd81186123815734615ce55760045460e052602060e0f35b63c146bf94811861239d5734615ce55760065460e052602060e0f35b636112c74781186123b95734615ce55760075460e052602060e0f35b63204fe3d581186123d55734615ce55760085460e052602060e0f35b63f30cfad581186123f15734615ce55760095460e052602060e0f35b63e89876ff811861240d5734615ce557600a5460e052602060e0f35b63f9ed959781186124295734615ce557600b5460e052602060e0f35b6349fe9e7781186124455734615ce557600c5460e052602060e0f35b63727ced5781186124615734615ce557600d5460e052602060e0f35b6372d4f0e2811861247d5734615ce557600e5460e052602060e0f35b63d7c3dcbe81186124995734615ce557600f5460e052602060e0f35b63083812e581186124b55734615ce55760105460e052602060e0f35b634ea12c7d81186124d15734615ce55760115460e052602060e0f35b63662b627481186124ed5734615ce55760125460e052602060e0f35b630c5e23d481186125095734615ce55760135460e052602060e0f35b6392526c0c81186125255734615ce55760145460e052602060e0f35b63ee8de67581186125415734615ce55760155460e052602060e0f35b63fee3f7f9811861255d5734615ce55760165460e052602060e0f35b637cf9aedc81186125795734615ce55760175460e052602060e0f35b637d1b060c81186125955734615ce55760185460e052602060e0f35b63e382446281186125b15734615ce55760195460e052602060e0f35b634903b0d181186125dd5734615ce55760016004356002811015615ce55702601a015460e052602060e0f35b630f529ba281186125f95734615ce557601c5460e052602060e0f35b63c45a015581186126155734615ce557601d5460e052602060e0f35b637ba1a74d81186126315734615ce557601e5460e052602060e0f35b630b7b594b811861264d5734615ce557601f5460e052602060e0f35b630c46b72a81186126695734615ce55760205460e052602060e0f35b63405e28f881186126855734615ce55760225460e052602060e0f35b505b005b60235460e052604e60e05160081c1015615ce55760e05160081c600a0a61010052604e60ff60e051161015615ce55760ff60e05116600a0a60e05260e051815261010051816020015250565b6126e0610160612689565b61016080516101205280602001516101405250601a5461012051808202821582848304141715615ce557905090508152601b5461014051808202821582848304141715615ce55790509050600454808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050816020015250565b600b5460e052600954610100526fffffffffffffffffffffffffffffffff6101005116610120526101005160801c6101405260e0514210156128bb5760085461016052600a546101805260e0805161018051808210615ce557808203905090508152504261018051808210615ce557808203905090506101805260e05161018051808210615ce557808203905090506101a0526101605160801c6101a051808202821582848304141715615ce557905090506101405161018051808202821582848304141715615ce557905090508181830110615ce5578082019050905060e051808015615ce557820490509050610140526fffffffffffffffffffffffffffffffff61016051166101a051808202821582848304141715615ce557905090506101205161018051808202821582848304141715615ce557905090508181830110615ce5578082019050905060e051808015615ce557820490509050610120525b61014051815261012051816020015250565b600e546101205260e051610100518181830110615ce557808201905090506101405261012051670de0b6b3a7640000808202821582848304141715615ce5579050905061012051670de0b6b3a76400008181830110615ce55780820190509050673782dace9d90000060e051808202821582848304141715615ce5579050905061014051808015615ce55782049050905061010051808202821582848304141715615ce5579050905061014051808015615ce557820490509050808210615ce55780820390509050808015615ce5578204905090506101405260145461014051808202821582848304141715615ce55790509050601554670de0b6b3a764000061014051808210615ce55780820390509050808202821582848304141715615ce557905090508181830110615ce55780820190509050670de0b6b3a764000080820490509050815250565b60e05161014052610100516101605261012051612a36576000612a40565b6101605161014051105b15612a5557610100516101405260e051610160525b610140516101805260006101a0526101c0600060ff818352015b610180516101e052610180516101405161016051808202821582848304141715615ce5579050905061018051808015615ce5578204905090508181830110615ce55780820190509050600280820490509050610180526101e0516101805111612af1576101e05161018051808210615ce557808203905090506101a052612b0c565b610180516101e051808210615ce557808203905090506101a0525b60016101a0511115612b4357610180516101a051670de0b6b3a7640000808202821582848304141715615ce5579050905010612b46565b60015b15612b5957505061018051815250612be1565b8151600101808352811415612a6f57505060106101c0527f446964206e6f7420636f6e7665726765000000000000000000000000000000006101e0526101c0506101c051806101e001818260206001820306601f82010390500336823750506308c379a06101805260206101a0526101c05160206001820306601f820103905060440161019cfd5b565b610f9f6102005111612bf6576000612c01565b63ee6b280161020051105b15615ce5576402540be3ff6102205111612c1c576000612c2a565b66470de4df82000161022051105b15615ce5576102405161028052610260516102a0526102a051610280511015612c5e576102605161028052610240516102a0525b633b9ac9ff6102805111612c73576000612c88565b6d314dc6448d9338c15b0a0000000161028051105b15615ce557655af3107a3fff6102a051670de0b6b3a7640000808202821582848304141715615ce5579050905061028051808015615ce5578204905090501115615ce55760026102805160e0526102a05161010052600061012052612cee6102e0612a18565b6102e051808202821582848304141715615ce557905090506102c052610280516102a0518181830110615ce557808201905090506102e052610300600060ff818352015b6102c05161032052673782dace9d90000061028051808202821582848304141715615ce557905090506102c051808015615ce5578204905090506102a051808202821582848304141715615ce557905090506102c051808015615ce5578204905090506103405261022051670de0b6b3a76400008181830110615ce5578082019050905061036052610340516103605111612df8576103405161036051808210615ce5578082039050905060018181830110615ce5578082019050905061036052612e25565b6103605161034051808210615ce5578082039050905060018181830110615ce55780820190509050610360525b670de0b6b3a76400006102c051808202821582848304141715615ce5579050905061022051808015615ce55782049050905061036051808202821582848304141715615ce5579050905061022051808015615ce55782049050905061036051808202821582848304141715615ce55790509050612710808202821582848304141715615ce5579050905061020051808015615ce55782049050905061038052673782dace9d90000061034051808202821582848304141715615ce5579050905061036051808015615ce5578204905090506103a0526102e0516102e0516103a051808202821582848304141715615ce55790509050670de0b6b3a7640000808204905090508181830110615ce55780820190509050610380516002808202821582848304141715615ce5579050905061034051808015615ce5578204905090508181830110615ce557808201905090506103a0516102c051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050808210615ce557808203905090506103c0526102c0516103c0516102e0518181830110615ce55780820190509050808202821582848304141715615ce557905090506103c051808015615ce5578204905090506103e0526102c0516102c051808202821582848304141715615ce557905090506103c051808015615ce5578204905090506104005261034051670de0b6b3a7640000116130d35761040080516102c051610380516103c051808015615ce557820490509050808202821582848304141715615ce55790509050670de0b6b3a76400008082049050905061034051670de0b6b3a7640000808210615ce55780820390509050808202821582848304141715615ce5579050905061034051808015615ce557820490509050808210615ce55780820390509050815250613169565b61040080516102c051610380516103c051808015615ce557820490509050808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050670de0b6b3a764000061034051808210615ce55780820390509050808202821582848304141715615ce5579050905061034051808015615ce5578204905090508181830110615ce557808201905090508152505b610400516103e0511161319e57610400516103e051808210615ce557808203905090506002808204905090506102c0526131b9565b6103e05161040051808210615ce557808203905090506102c0525b600061042052610320516102c051116131eb57610320516102c051808210615ce5578082039050905061042052613206565b6102c05161032051808210615ce55780820390509050610420525b662386f26fc100006102c05180821061321f5781613221565b805b9050905061042051655af3107a4000808202821582848304141715615ce5579050905010156132e85761046060006002818352015b6020610460510261028001516104405261044051670de0b6b3a7640000808202821582848304141715615ce557905090506102c051808015615ce55782049050905061048052662386f26fc0ffff61048051116132b45760006132c4565b68056bc75e2d6310000161048051105b15615ce557815160010180835281141561325657505050506102c051815250613370565b8151600101808352811415612d325750506010610300527f446964206e6f7420636f6e7665726765000000000000000000000000000000006103205261030050610300518061032001818260206001820306601f82010390500336823750506308c379a06102c05260206102e0526103005160206001820306601f82010390506044016102dcfd5b565b610f9f60e0511161338457600061338e565b63ee6b280160e051105b15615ce5576402540be3ff61010051116133a95760006133b7565b66470de4df82000161010051105b15615ce55767016345785d89ffff61016051116133d55760006133ea565b6d314dc6448d9338c15b0a0000000161016051105b15615ce557610120600161018051808210615ce557808203905090506002811015615ce55760200201516101a052700100000000000000000000000000000000610160511015615ce5576002610160510a6101a0516004808202821582848304141715615ce55790509050808015615ce5578204905090506101c052671bc16d674ec800006101a051808202821582848304141715615ce5579050905061016051808015615ce5578204905090506101e05266470de4df81ffff6101e051116134b45760006134c4565b680ad78ebc5ac62000016101e051105b15615ce5576101a051655af3107a40008082049050905061016051655af3107a4000808204905090508082106134fa57816134fc565b805b90509050606480821061350f5781613511565b805b9050905061020052610220600060ff818352015b6101c051610240526101e0516101c051808202821582848304141715615ce557905090506002808202821582848304141715615ce5579050905061016051808015615ce557820490509050610260526101a0516101c0518181830110615ce557808201905090506102805261010051670de0b6b3a76400008181830110615ce557808201905090506102a052610260516102a051116135ef57610260516102a051808210615ce5578082039050905060018181830110615ce557808201905090506102a05261361c565b6102a05161026051808210615ce5578082039050905060018181830110615ce557808201905090506102a0525b670de0b6b3a764000061016051808202821582848304141715615ce5579050905061010051808015615ce5578204905090506102a051808202821582848304141715615ce5579050905061010051808015615ce5578204905090506102a051808202821582848304141715615ce55790509050612710808202821582848304141715615ce5579050905060e051808015615ce5578204905090506102c052670de0b6b3a7640000671bc16d674ec8000061026051808202821582848304141715615ce557905090506102a051808015615ce5578204905090508181830110615ce557808201905090506102e052670de0b6b3a76400006101c051808202821582848304141715615ce55790509050610280516102e051808202821582848304141715615ce557905090508181830110615ce557808201905090506102c0518181830110615ce5578082019050905061030052610160516102e051808202821582848304141715615ce55790509050610320526103205161030051106137ba57610300805161032051808210615ce557808203905090508152506137d0565b610240516002808204905090506101c052613a07565b610300516101c051808015615ce557820490509050610340526102c05161034051808015615ce5578204905090506103605261030051670de0b6b3a764000061016051808202821582848304141715615ce557905090508181830110615ce5578082019050905061034051808015615ce55782049050905061036051670de0b6b3a7640000808202821582848304141715615ce5579050905061026051808015615ce5578204905090508181830110615ce55780820190509050610380526103608051670de0b6b3a764000061028051808202821582848304141715615ce5579050905061034051808015615ce5578204905090508181830110615ce55780820190509050815250610360516103805110613904576103805161036051808210615ce557808203905090506101c052613916565b610240516002808204905090506101c0525b60006103a052610240516101c0511161394857610240516101c051808210615ce557808203905090506103a052613963565b6101c05161024051808210615ce557808203905090506103a0525b610200516101c051655af3107a4000808204905090508082106139865781613988565b805b905090506103a0511015613a07576101c051670de0b6b3a7640000808202821582848304141715615ce5579050905061016051808015615ce5578204905090506103c052662386f26fc0ffff6103c051116139e45760006139f4565b68056bc75e2d631000016103c051105b15615ce55750506101c051815250613a8f565b81516001018083528114156135255750506010610220527f446964206e6f7420636f6e7665726765000000000000000000000000000000006102405261022050610220518061024001818260206001820306601f82010390500336823750506308c379a06101e0526020610200526102205160206001820306601f82010390506044016101fcfd5b565b60e051670de0b6b3a7640000808204905090506101005260e05161010051670de0b6b3a7640000808202821582848304141715615ce55790509050808210615ce5578082039050905061012052603b610100511115613af4576000815250613d6e565b670de0b6b3a7640000610100610100511015615ce5576101005160020a808015615ce5578204905090506101405261012051613b365761014051815250613d6e565b670de0b6b3a7640000610160526706f05b59d3b2000061018052670de0b6b3a76400006101a05260006101c0526101e0600160ff818352015b6101e051670de0b6b3a7640000808202821582848304141715615ce557905090506102005261020051670de0b6b3a7640000808210615ce5578082039050905061022052610220516101205111613bdf57610220805161012051808210615ce55780820390509050815250613c03565b6101205161022051808210615ce55780820390509050610220526101c051156101c0525b610160516102205161018051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050808202821582848304141715615ce5579050905061020051808015615ce557820490509050610160526101c051613c85576101a08051610160518181830110615ce55780820190509050815250613ca0565b6101a0805161016051808210615ce557808203905090508152505b6402540be400610160511015613ce6575050610140516101a051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050815250613d6e565b8151600101808352811415613b6f57505060106101e0527f446964206e6f7420636f6e766572676500000000000000000000000000000000610200526101e0506101e0518061020001818260206001820306601f82010390500336823750506308c379a06101a05260206101c0526101e05160206001820306601f82010390506044016101bcfd5b565b610200516002808204905090506102205261020051670de0b6b3a7640000808202821582848304141715615ce557905090506004546002808202821582848304141715615ce55790509050808015615ce557820490509050610240526102205160e0526102405161010052600161012052613dec610260612a18565b61026051815250565b613e006104e061275a565b6104e080516104a05280602001516104c05250601e546104e052601f546105005261052060006002818352015b6001610520516002811015615ce5570260020154610540526020602038036080396080516105405118613e7457476001610520516002811015615ce55702601a0155613ec3565b6370a082316105605230610580526020610560602461057c610540515afa613ea1573d600060003e3d6000fd5b601f3d1115615ce557610560516001610520516002811015615ce55702601a01555b8151600101808352811415613e2d57505060205461052052610500516104e0511115614092576104e05161050051808210615ce55780820390509050601654808202821582848304141715615ce557905090506404a817c800808204905090506105405260006105405111156140925763cab4d3db610580526020610580600461059c601d545afa613f5a573d600060003e3d6000fd5b601f3d1115615ce557610580518060a01c615ce55761056052600061056051146140925761052051670de0b6b3a7640000808202821582848304141715615ce557905090506105205161054051808210615ce55780820390509050808015615ce557820490509050670de0b6b3a7640000808210615ce5578082039050905061058052636962f8456105c052610560516105e052610580516106005260206105c060446105dc60006001545af1614016573d600060003e3d6000fd5b601f3d1115615ce5576105c0516105a0526104e08051610540516002808202821582848304141715615ce55790509050808210615ce557808203905090508152506104e051601e55610560517f6059a38198b1dc42b3791087d1ff0fbd72b3179553c25f678cd246f52ffaaf596105a0516105c05260206105c0a25b6318160ddd610560526020610560600461057c6001545afa6140b9573d600060003e3d6000fd5b601f3d1115615ce55761056051610540526104a0516105e0526104c051610600526140e56105806126d5565b610580805161062052806020015161064052506105e051610200526106005161022052610620516102405261064051610260526141236105c0612be3565b6105c0516105605261056051601c55670de0b6b3a7640000610560516102005261414e610580613d70565b61058051808202821582848304141715615ce5579050905061054051808015615ce557820490509050602055610500516104e0511115614190576104e051601f555b565b60055461024052600754610260524261026051106141ba576102405181525061429656614296565b601254610280526006546102a0524261026051808210615ce55780820390509050670de0b6b3a7640000808202821582848304141715615ce5579050905061028051808015615ce55782049050905060e0526142176102e0613a91565b6102e0516102c0526102a051670de0b6b3a76400006102c051808210615ce55780820390509050808202821582848304141715615ce55790509050610240516102c051808202821582848304141715615ce557905090508181830110615ce55780820190509050670de0b6b3a764000080820490509050815250614296565b565b6005546107205260065461074052600454610760526007546107805260006107a052426107805110156143a2576012546107c0524261078051808210615ce55780820390509050670de0b6b3a7640000808202821582848304141715615ce557905090506107c051808015615ce55782049050905060e05261431b610800613a91565b610800516107e05261074051670de0b6b3a76400006107e051808210615ce55780820390509050808202821582848304141715615ce55790509050610720516107e051808202821582848304141715615ce557905090508181830110615ce55780820190509050670de0b6b3a7640000808204905090506107205261072051600555426007555b610700516107c052610700516143e657610660516102005261068051610220526106a051610240526106c051610260526143dd6107e0612be3565b6107e0516107c0525b60006106e051116144b0576106a0516107e0526106c051610800526107e051620f424080820490509050610820526107e08051610820518181830110615ce557808201905090508152506107605161082051808202821582848304141715615ce557905090506106c0516106605160e05261068051610100526107e0516101205261080051610140526107c05161016052600161018052614488610840613372565b61084051808210615ce55780820390509050808015615ce557820490509050610740526144b9565b6106e051610740525b610740516006556318160ddd610800526020610800600461081c6001545afa6144e7573d600060003e3d6000fd5b601f3d1115615ce557610800516107e052601e5461080052602054610820526107c051600280820490509050610840526107c051670de0b6b3a7640000808202821582848304141715615ce55790509050600261076051808202821582848304141715615ce55790509050808015615ce55782049050905061086052670de0b6b3a764000061088052670de0b6b3a76400006108a05260006108205111156146c6576108405160e05261086051610100526001610120526145a96108e0612a18565b6108e0516108c052670de0b6b3a76400006108c051808202821582848304141715615ce557905090506107e051808015615ce5578204905090506108a052610800516108a051808202821582848304141715615ce5579050905061082051808015615ce55782049050905061088052600b546108e052610820516108a05110614633576000614639565b6108e051155b156146b5576004610900527f4c6f7373000000000000000000000000000000000000000000000000000000006109205261090050610900518061092001818260206001820306601f82010390500336823750506308c379a06108c05260206108e0526109005160206001820306601f82010390506044016108dcfd5b60016108e051186146c6576000600b555b61088051601e5561072051670de0b6b3a7640000808202821582848304141715615ce5579050905061076051808015615ce5578204905090506108c052670de0b6b3a76400006108c0511161473957670de0b6b3a76400006108c051808210615ce557808203905090506108c052614759565b6108c08051670de0b6b3a7640000808210615ce557808203905090508152505b6010546108c0516005808204905090508082106147765781614778565b805b905090506108e05260215461090052610900511561479757600061481d565b610880516002600c54808202821582848304141715615ce557905090508181830110615ce557808201905090506108a0516002808202821582848304141715615ce55790509050670de0b6b3a7640000808210615ce557808203905090501161480157600061481d565b6108e0516108c0511161481557600061481d565b600061082051115b1561482e5760016109005260016021555b6109005115614a77576108e0516108c0511161484b576000614853565b600061082051115b15614a7757610760516108c0516108e051808210615ce55780820390509050808202821582848304141715615ce557905090506108e05161072051808202821582848304141715615ce557905090508181830110615ce557808201905090506108c051808015615ce5578204905090506107a0526106a051610840526106c0516107a051808202821582848304141715615ce5579050905061076051808015615ce55782049050905061086052610660516102005261068051610220526108405161024052610860516102605261492b610940612be3565b6109405161092052610920516002808204905090506108405261092051670de0b6b3a7640000808202821582848304141715615ce5579050905060026107a051808202821582848304141715615ce55790509050808015615ce55782049050905061086052670de0b6b3a76400006108405160e05261086051610100526001610120526149b9610940612a18565b61094051808202821582848304141715615ce557905090506107e051808015615ce55782049050905061082052670de0b6b3a764000061082051116149ff576000614a36565b61088051600261082051808202821582848304141715615ce55790509050670de0b6b3a7640000808210615ce55780820390509050115b614a5d5760006021556107c051601c556108a051602055614a9b613df556614a9b56614a77565b6107a05160045561092051601c5561082051602055614a9b565b6107c051601c556108a0516020556109005115614a9b576000602155614a9b613df5565b565b6109c0516109a05114615ce55760026109a0511015615ce55760026109c0511015615ce55760006109e0511115615ce557614ad9610ae061275a565b610ae08051610aa0528060200151610ac05250601a54610ae052601b54610b0052604036610b203760016109a0516002811015615ce5570260020154610b605260016109c0516002811015615ce5570260020154610b8052610ae06109c0516002811015615ce5576020020151610ba052610ae06109a0516002811015615ce5576020020151610bc052610bc0516109e0518181830110615ce55780820190509050610ae06109a0516002811015615ce5576020020152610ae06109a0516002811015615ce557602002015160016109a0516002811015615ce55702601a0155600454610be052614bcb610c40612689565b610c408051610c00528060200151610c205250610ae051610c0051808202821582848304141715615ce55790509050610ae052610b0051610be051808202821582848304141715615ce55790509050610c2051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610b0052610c0051610c4052610c2051610c605260016109a05118614c7257610c2051610c4052610c0051610c60525b600b54610c80526000610c80511115614d6f57610bc08051610c4051808202821582848304141715615ce5579050905081525060006109a0511115614ce257610bc051610be051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610bc0525b610ae06109a0516002811015615ce5576020020151610ca052610bc051610ae06109a0516002811015615ce5576020020152610aa05161020052610ac05161022052610ae05161024052610b005161026052614d3f610cc0612be3565b610cc051601c55610ca051610ae06109a0516002811015615ce5576020020152610c80514210614d6f576001600b555b610ae06109c0516002811015615ce5576020020151610aa05160e052610ac05161010052610ae05161012052610b005161014052601c54610160526109c05161018052614dbd610ca0613372565b610ca051808210615ce55780820390509050610b4052610ae06109c0516002811015615ce557602002018051610b4051808210615ce55780820390509050815250610b4080516001808210615ce5578082039050905081525060006109c0511115614e5957610b4051670de0b6b3a7640000808202821582848304141715615ce55790509050610be051808015615ce557820490509050610b40525b610b408051610c6051808015615ce557820490509050815250610b408051610ae05160e052610b005161010052614e91610ca06128cd565b610ca051610b4051808202821582848304141715615ce557905090506402540be40080820490509050808210615ce55780820390509050815250610a0051610b40511015614f50576008610ca0527f536c697070616765000000000000000000000000000000000000000000000000610cc052610ca050610ca05180610cc001818260206001820306601f82010390500336823750506308c379a0610c60526020610c8052610ca05160206001820306601f8201039050604401610c7cfd5b610ba08051610b4051808210615ce55780820390509050815250610ba05160016109c0516002811015615ce55702601a0155610a2051614f91576000614fa3565b602060203803608039608051610b6051145b61521c5761098051615ce557610a805115615116576370a08231610cc05230610ce0526020610cc06024610cdc610b60515afa614fe5573d600060003e3d6000fd5b601f3d1115615ce557610cc051610ca05260006000600460208206610cc001602082840111615ce557602080610ce082610a8060045afa505081815290509050600480602084610de00101826020850160045afa50508051820191505061096051610d4052610a4051610d6052610b6051610d80526109e051610da052610b4051610dc05260a0610d2052610d2060a080602084610de00101826020850160045afa50508051820191505080610de052610de0505060006000610de051610e006000610a60515af16150bc573d600060003e3d6000fd5b6109e0516370a08231610cc05230610ce0526020610cc06024610cdc610b60515afa6150ed573d600060003e3d6000fd5b601f3d1115615ce557610cc051610ca051808210615ce5578082039050905018615ce5576151b8565b6323b872dd610ce452600461096051610d045230610d24526109e051610d4452606001610ce052610ce0506020610da0610ce051610d006000610b60515af1615164573d600060003e3d6000fd5b610d8060203d8082116151775781615179565b805b905090508152805160200180610ca0828460045afa905050506000610ca051146151b857610cc051610ca05181816020036008021c9050905015615ce5575b602060203803608039608051610b60511861522a57632e1a7d4d610ca0526109e051610cc0526020602038036080396080513b15615ce557600060006024610cbc60006020602038036080396080515af161522a573d600060003e3d6000fd61522a565b6109e0516109805118615ce5575b610a205161523957600061524b565b602060203803608039608051610b8051145b61534a57602060203803608039608051610b8051186152a95763d0e30db0610ca0526020602038036080396080513b15615ce557600060006004610cbc610b40516020602038036080396080515af16152a9573d600060003e3d6000fd5b63a9059cbb610ce4526004610a4051610d0452610b4051610d2452604001610ce052610ce0506020610d80610ce051610d006000610b80515af16152f2573d600060003e3d6000fd5b610d6060203d8082116153055781615307565b805b905090508152805160200180610ca0828460045afa905050506000610ca0511461537857610cc051610ca05181816020036008021c9050905015615ce557615378565b6000610ca052610ca05060006000610ca051610cc0610b4051610a40515af1615378573d600060003e3d6000fd5b610ba08051610c6051808202821582848304141715615ce5579050905081525060006109c05111156153d557610ba051610be051808202821582848304141715615ce55790509050670de0b6b3a764000080820490509050610ba0525b610ba051610ae06109c0516002811015615ce5576020020152620186a06109e0511161540257600061540c565b620186a0610b4051115b156154cc576109e051610c4051808202821582848304141715615ce55790509050610ca052610b4051610c6051808202821582848304141715615ce55790509050610cc0526109a0511561549557610cc051670de0b6b3a7640000808202821582848304141715615ce55790509050610ca051808015615ce557820490509050610b20526154cc565b610ca051670de0b6b3a7640000808202821582848304141715615ce55790509050610cc051808015615ce557820490509050610b20525b610aa05161066052610ac05161068052610ae0516106a052610b00516106c052610b20516106e052600061070052615502614298565b610960517fb2e76ae99761dc136e598d4a629bb347eccb9532a5f8bbd72e18467c3c34cc986109a051610ca0526109e051610cc0526109c051610ce052610b4051610d00526080610ca0a2610b4051815250565b6101a05160e0526101c051610100526155706102006128cd565b610200516002808202821582848304141715615ce557905090506004808204905090506101e05260006102005261024060006002818352015b602061024051026101600151610220526102008051610220518181830110615ce5578082019050905081525081516001018083528114156155a9575050610200516002808204905090506102205260006102405261028060006002818352015b602061028051026101600151610260526102205161026051116156595761024080516102205161026051808210615ce557808203905090508181830110615ce55780820190509050815250615688565b61024080516102605161022051808210615ce557808203905090508181830110615ce557808201905090508152505b81516001018083528114156156095750506101e05161024051808202821582848304141715615ce5579050905061020051808015615ce557820490509050620186a08181830110615ce55780820190509050815250565b6318160ddd610580526020610580600461059c6001545afa615706573d600060003e3d6000fd5b601f3d1115615ce5576105805161056052610560516104e05111615ce5576002610500511015615ce557601a5461058052601b546105a05260006105c05261574f610620612689565b61062080516105e0528060200151610600525060045461060051808202821582848304141715615ce5579050905061062052610580516105e051808202821582848304141715615ce55790509050610640526105a05161062051808202821582848304141715615ce55790509050670de0b6b3a76400008082049050905061066052610500516157ff57670de0b6b3a76400006105e051808202821582848304141715615ce55790509050610620525b6105205161581357601c546105c052615847565b6104a051610200526104c051610220526106405161024052610660516102605261583e610680612be3565b610680516105c0525b6105c051610680526106405160e05261066051610100526158696106c06128cd565b6106c0516106a0526104e05161068051808202821582848304141715615ce5579050905061056051808015615ce5578204905090506106c05261068080516106c0516106a0516106c051808202821582848304141715615ce557905090506404a817c8008082049050905060018181830110615ce55780820190509050808210615ce55780820390509050808210615ce557808203905090508152506104a05160e0526104c05161010052610640516101205261066051610140526106805161016052610500516101805261593f610700613372565b610700516106e052610640610500516002811015615ce55760200201516106e051808210615ce55780820390509050670de0b6b3a7640000808202821582848304141715615ce5579050905061062051808015615ce557820490509050610700526106e051610640610500516002811015615ce5576020020152600061072052610540516159ce5760006159ec565b620186a061070051116159e25760006159ec565b620186a06104e051115b15615b69576000610740526105e0516107605260016105005118615a3757610580516105e051808202821582848304141715615ce55790509050610740526106005161076052615a58565b6105a05161060051808202821582848304141715615ce55790509050610740525b610740516106c051808202821582848304141715615ce557905090506105c051808015615ce5578204905090506107405261074051670de0b6b3a7640000808202821582848304141715615ce557905090506107005161076051808202821582848304141715615ce557905090506106c051610580610500516002811015615ce5576020020151808202821582848304141715615ce5579050905061076051808202821582848304141715615ce557905090506105c051808015615ce557820490509050808210615ce55780820390509050808015615ce5578204905090506107205261050051615b69576ec097ce7bc90715b34b9f100000000061072051808015615ce557820490509050610720525b610700518152610720516020820152610680516040820152606081016106405181526106605181602001525050565b60e051615ba9576000815250615ce3565b60e051670de0b6b3a76400008181830110615ce557808201905090506002808204905090506101005260e051610120526101406000610100818352015b610120516101005118615c0157505061012051815250615ce3565b610100516101205260e051670de0b6b3a7640000808202821582848304141715615ce5579050905061010051808015615ce557820490509050610100518181830110615ce55780820190509050600280820490509050610100528151600101808352811415615be65750506010610140527f446964206e6f7420636f6e7665726765000000000000000000000000000000006101605261014050610140518061016001818260206001820306601f82010390500336823750506308c379a0610100526020610120526101405160206001820306601f820103905060440161011cfd5b565b600080fd5b610029615d130361002961012039610029615d13036101005181610120015280602001610120f35b600080fd000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/valory/contracts/curve_pool/contract.py b/packages/valory/contracts/curve_pool/contract.py new file mode 100644 index 00000000..7878d74c --- /dev/null +++ b/packages/valory/contracts/curve_pool/contract.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2023 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + +"""This module contains the curve pool contract definition.""" + +import logging + +from aea.common import JSONLike +from aea.configurations.base import PublicId +from aea.contracts.base import Contract +from aea_ledger_ethereum import EthereumApi + +ENCODING = "utf-8" +PUBLIC_ID = PublicId.from_str("valory/curve_pool:0.1.0") + +_logger = logging.getLogger( + f"aea.packages.{PUBLIC_ID.author}.contracts.{PUBLIC_ID.name}.contract" +) + + +class CurvePoolContract(Contract): + """The CurvePoolContract contract interface.""" + + contract_id: PublicId = PUBLIC_ID + + @classmethod + def get_dy( + cls, + ledger_api: EthereumApi, + contract_address: str, + i: int, + j: int, + dx: int, + ) -> JSONLike: + """Get the dy value from the contract.""" + + contract = cls.get_instance(ledger_api, contract_address) + dy = contract.functions.get_dy(i, j, dx).call() + return dict(data=dy) + + @classmethod + def build_exchange_tx( + cls, + ledger_api: EthereumApi, + contract_address: str, + i: int, + j: int, + dx: int, + min_dy: int, + use_eth: bool = True, + ) -> JSONLike: + """Build curve exchange tx.""" + contract = cls.get_instance(ledger_api, contract_address) + data = contract.encodeABI( + fn_name="exchange", + args=[ + i, + j, + dx, + min_dy, + use_eth, + ], + ) + return dict(data=data) diff --git a/packages/valory/contracts/curve_pool/contract.yaml b/packages/valory/contracts/curve_pool/contract.yaml new file mode 100644 index 00000000..7a3e4007 --- /dev/null +++ b/packages/valory/contracts/curve_pool/contract.yaml @@ -0,0 +1,19 @@ +name: curve_pool +author: valory +version: 0.1.0 +type: contract +description: CurvePool contract +license: Apache-2.0 +aea_version: '>=1.0.0, <2.0.0' +fingerprint: + __init__.py: bafybeictrqqkqaiis2ohyzuooddrezhmi5jmxmibvmw7w5o24qu2mue24i + build/CurvePool.json: bafybeigktqontoncikf2nzz5ruhgotbv7u7arih7jgzi6jrwflsiuxl3zi + contract.py: bafybeihxlb6pkeetyrg3k63j6tjwuuodtu7laph4f2bdefnfly43wwxlnq +fingerprint_ignore_patterns: [] +contracts: [] +class_name: CurvePoolContract +contract_interface_paths: + ethereum: build/CurvePool.json +dependencies: + open-aea-ledger-ethereum: + version: ==1.33.0 diff --git a/packages/valory/contracts/keep3r_v2/contract.py b/packages/valory/contracts/keep3r_v2/contract.py index 1e4280d4..4d4d564b 100644 --- a/packages/valory/contracts/keep3r_v2/contract.py +++ b/packages/valory/contracts/keep3r_v2/contract.py @@ -18,15 +18,16 @@ # ------------------------------------------------------------------------------ """This module contains the Keep3rV1 contract definition.""" - +import asyncio +import concurrent.futures import logging -from typing import Dict, Union +from typing import Dict, List, Union, cast from aea.common import JSONLike from aea.configurations.base import PublicId from aea.contracts.base import Contract from aea_ledger_ethereum import EthereumApi -from web3.types import Nonce, TxParams, Wei +from web3.types import BlockIdentifier, Nonce, TxParams, Wei ENCODING = "utf-8" @@ -98,6 +99,20 @@ def pending_bonds( bondings = contract.functions.pendingBonds(address, bonding_asset).call() return dict(data=bondings) + @classmethod + def pending_unbonds( + cls, + ledger_api: EthereumApi, + contract_address: str, + address: str, + bonding_asset: str, + ) -> JSONLike: + """Unbonds that are not yet active.""" + + contract = cls.get_instance(ledger_api, contract_address) + unbondings = contract.functions.pendingUnbonds(address, bonding_asset).call() + return dict(data=unbondings) + @classmethod def credits( cls, @@ -156,6 +171,22 @@ def can_activate_after( ).call() return dict(data=can_activate_after) + @classmethod + def can_withdraw_after( + cls, + ledger_api: EthereumApi, + contract_address: str, + address: str, + bonding_asset: str, + ) -> JSONLike: + """Check if address is a registered keeper.""" + + contract = cls.get_instance(ledger_api, contract_address) + can_withdraw_after = contract.functions.canWithdrawAfter( + address, bonding_asset + ).call() + return dict(data=can_withdraw_after) + @classmethod def build_add_job_tx( cls, @@ -240,6 +271,7 @@ def build_withdraw_tx( cls, ledger_api: EthereumApi, contract_address: str, + bonding_asset: str, ) -> RawTransaction: """Withdraw funds after unbonding has finished.""" @@ -247,7 +279,143 @@ def build_withdraw_tx( data = contract.encodeABI( fn_name="withdraw", args=[ - contract.address, + ledger_api.api.toChecksumAddress(bonding_asset), ], ) return dict(data=data) + + @classmethod + def get_unbonding_events( + cls, + ledger_api: EthereumApi, + contract_address: str, + address: str, + bonding_asset: str, + from_block: BlockIdentifier = "earliest", + to_block: BlockIdentifier = "latest", + ) -> JSONLike: + """ + Get all unbonding events for a given keeper. + + :param ledger_api: the ledger API object + :param contract_address: the keep3rV2 contract address + :param address: the keeper address + :param bonding_asset: the asset that was unbonded + :param from_block: from which block to search for events + :param to_block: to which block to search for events + :return: the unbonding events + """ + ledger_api = cast(EthereumApi, ledger_api) + contract = cls.get_instance(ledger_api, contract_address) + address = ledger_api.api.toChecksumAddress(address) + entries = contract.events.Unbonding.createFilter( + fromBlock=from_block, + toBlock=to_block, + argument_filters=dict(_keeperOrJob=address, _unbonding=bonding_asset), + ).get_all_entries() + unbonding_events = list( + dict( + tx_hash=entry.transactionHash.hex(), + block_number=entry.blockNumber, + keeper=address, + unbonding_asset=bonding_asset, + amount=entry["args"]["_amount"], + ) + for entry in entries + ) + sorted(unbonding_events, key=lambda x: x["block_number"]) + return dict( + data=unbonding_events, + ) + + @classmethod + def get_withdrawal_events( + cls, + ledger_api: EthereumApi, + contract_address: str, + address: str, + bonding_asset: str, + from_block: BlockIdentifier = "earliest", + to_block: BlockIdentifier = "latest", + ) -> JSONLike: + """ + Get all withdrawal events for a given keeper. + + :param ledger_api: the ledger API object + :param contract_address: the keep3rV2 contract address + :param address: keeper address + :param bonding_asset: the asset that was withdrawn + :param from_block: from which block to search for events + :param to_block: to which block to search for events + :return: the withdrawal events + """ + ledger_api = cast(EthereumApi, ledger_api) + contract = cls.get_instance(ledger_api, contract_address) + address = ledger_api.api.toChecksumAddress(address) + entries = contract.events.Withdrawal.createFilter( + fromBlock=from_block, + toBlock=to_block, + argument_filters=dict(_keeper=address, _bond=bonding_asset), + ).get_all_entries() + withdrawal_events = list( + dict( + tx_hash=entry.transactionHash.hex(), + block_number=entry.blockNumber, + keeper=address, + unbonding_asset=bonding_asset, + amount=entry["args"]["_amount"], + ) + for entry in entries + ) + sorted(withdrawal_events, key=lambda x: x["block_number"]) + return dict( + data=withdrawal_events, + ) + + @classmethod + def sender_to_amount_spent( + cls, + ledger_api: EthereumApi, + contract_address: str, + transaction_hashes: List[str], + ) -> JSONLike: + """ + Get the amount of gas spent by each sender of the transactions provided. + + :param ledger_api: the ledger API object + :param contract_address: the contract address + :param transaction_hashes: the transaction hashes + :return: the amount of gas spent by each owner (in wei) + """ + loop = asyncio.new_event_loop() + tasks = [] + num_threads = 5 + + def get_gas_spent(tx_hash: str) -> Dict[str, int]: + tx_receipt = ledger_api.get_transaction_receipt(tx_hash) + tx = ledger_api.get_transaction(tx_hash) + gas_price = int(tx["gasPrice"]) + gas_used = int(tx_receipt["gasUsed"]) + total_spent = gas_price * gas_used + sender = tx["from"] + return {sender: total_spent} + + with concurrent.futures.ThreadPoolExecutor(num_threads) as pool: + for transaction_hash in transaction_hashes: + task = loop.run_in_executor(pool, get_gas_spent, transaction_hash) + tasks.append(task) + + results = cast( + List[JSONLike], loop.run_until_complete(asyncio.gather(*tasks)) + ) + loop.close() + + sender_to_amount_spent = {} + for result in results: + sender = list(result.keys())[0] + total_spent = result[sender] + if sender not in sender_to_amount_spent: + sender_to_amount_spent[sender] = 0 + sender_to_amount_spent[sender] += total_spent + + return dict(data=sender_to_amount_spent) diff --git a/packages/valory/contracts/keep3r_v2/contract.yaml b/packages/valory/contracts/keep3r_v2/contract.yaml index a0e43cc6..fb2eefb0 100644 --- a/packages/valory/contracts/keep3r_v2/contract.yaml +++ b/packages/valory/contracts/keep3r_v2/contract.yaml @@ -8,7 +8,7 @@ aea_version: '>=1.0.0, <2.0.0' fingerprint: __init__.py: bafybeihbklrajavaowaugrzilvxpqajlf6yrb2ow2oyfizuxumpzav35xm build/Keep3rV2.json: bafybeih2ve5z3keyoa3waokjnqap4mpiuuwe4axfzgpg2loeveievtmoq4 - contract.py: bafybeihetyqzcfxmronw4r7zvfsz2y7nxkx3rcacbpt4dnrkz756w5b2ji + contract.py: bafybeieqi4hr32yxd6jml6uh5cqiynkw53o5tydgll4jrzqrpw7n5dbcba fingerprint_ignore_patterns: [] contracts: [] class_name: KeeperV2 diff --git a/packages/valory/services/keep3r_bot/service.yaml b/packages/valory/services/keep3r_bot/service.yaml index 7b81a4c2..c703e46b 100644 --- a/packages/valory/services/keep3r_bot/service.yaml +++ b/packages/valory/services/keep3r_bot/service.yaml @@ -7,7 +7,7 @@ license: Apache-2.0 fingerprint: README.md: bafybeig26ntff2vdtmum3crflwqrybmonwdxahvlrst2brnazbo3mjvtqu fingerprint_ignore_patterns: [] -agent: valory/keep3r_bot:0.1.0:bafybeib76phj7aue3uwimbqejfp372vbq7cvyibuhnk5zqthbokjuv74ba +agent: valory/keep3r_bot:0.1.0:bafybeid7zpebkekceqjl6ofse5tot3tpp3lw4zl3tqvjjy3754hf474jlm number_of_agents: 1 deployment: tendermint: diff --git a/packages/valory/services/keep3r_bot_goerli/service.yaml b/packages/valory/services/keep3r_bot_goerli/service.yaml index 95ae037b..926f5ef0 100644 --- a/packages/valory/services/keep3r_bot_goerli/service.yaml +++ b/packages/valory/services/keep3r_bot_goerli/service.yaml @@ -7,7 +7,7 @@ license: Apache-2.0 fingerprint: README.md: bafybeiblcg3qti2cyz4ytufdkmqzcm6svbo5cwgsu2srjovvljdi35iz6i fingerprint_ignore_patterns: [] -agent: valory/keep3r_bot:0.1.0:bafybeib76phj7aue3uwimbqejfp372vbq7cvyibuhnk5zqthbokjuv74ba +agent: valory/keep3r_bot:0.1.0:bafybeid7zpebkekceqjl6ofse5tot3tpp3lw4zl3tqvjjy3754hf474jlm number_of_agents: 4 deployment: tendermint: diff --git a/packages/valory/skills/keep3r_abci/skill.yaml b/packages/valory/skills/keep3r_abci/skill.yaml index bd24fb15..4833065e 100644 --- a/packages/valory/skills/keep3r_abci/skill.yaml +++ b/packages/valory/skills/keep3r_abci/skill.yaml @@ -21,7 +21,7 @@ contracts: [] protocols: [] skills: - valory/abstract_round_abci:0.1.0:bafybeiaf3twqpqguqmqmqhl4sjd2i2nh5jspzghs3cmhp2co7lb5hkr5yy -- valory/keep3r_job_abci:0.1.0:bafybeid6jnfjq4qnw4tf2dqymtoibaml27r66w3svsve6u3fovuap2u6ju +- valory/keep3r_job_abci:0.1.0:bafybeieyztb2uyl4igmtbegkrizcnyxhnfweuygd2tdlkqr6mq5r7sji5e - valory/registration_abci:0.1.0:bafybeic5uibumxihx7qvx2457sqo6d2dzj6u73tywffgamrb54dzvpzraq - valory/reset_pause_abci:0.1.0:bafybeifgatypd7xp7ng3zcszpintfxaqgf6blapgsjejy42nr5u4dfseg4 - valory/termination_abci:0.1.0:bafybeiasehtl7rj62trkulyxtqna37nrrwvhwhthdkri2dihw53lky2txi @@ -144,6 +144,8 @@ models: termination_sleep: 900 tx_timeout: 10.0 unbonding_threshold: 50 + curve_pool_contract_address: '0x21410232B484136404911780bC32756D5d1a9Fa9' + agent_surplus_share: 0.75 use_flashbots: false use_termination: false use_v2: false diff --git a/packages/valory/skills/keep3r_job_abci/behaviours.py b/packages/valory/skills/keep3r_job_abci/behaviours.py index 8e5cf9ce..81db3c53 100644 --- a/packages/valory/skills/keep3r_job_abci/behaviours.py +++ b/packages/valory/skills/keep3r_job_abci/behaviours.py @@ -20,9 +20,16 @@ """This module contains the behaviours for the 'keep3r_job_abci' skill.""" import json from abc import ABC -from typing import Any, Dict, Generator, Optional, Set, Tuple, Type, cast +from typing import Any, Dict, Generator, List, Optional, Set, Tuple, Type, cast from aea.configurations.data_types import PublicId +from hexbytes import HexBytes + +from packages.valory.contracts.curve_pool.contract import CurvePoolContract +from packages.valory.contracts.multisend.contract import ( + MultiSendContract, + MultiSendOperation, +) try: @@ -30,7 +37,10 @@ except ImportError: from mypy_extensions import TypedDict # <=py3.7 -from packages.valory.contracts.gnosis_safe.contract import GnosisSafeContract +from packages.valory.contracts.gnosis_safe.contract import ( + GnosisSafeContract, + SafeOperation, +) from packages.valory.contracts.keep3r_v1.contract import Keep3rV1Contract from packages.valory.contracts.keep3r_v2.contract import KeeperV2 from packages.valory.protocols.contract_api.message import ContractApiMessage @@ -47,8 +57,10 @@ ActivationTxPayload, ApproveBondTxPayload, BondingTxPayload, + CalculateSpentGasPayload, GetJobsPayload, PathSelectionPayload, + SwapAndDisburseRewardsPayload, TopUpPayload, UnbondingTxPayload, WaitingPayload, @@ -59,10 +71,12 @@ ApproveBondRound, AwaitTopUpRound, BondingRound, + CalculateSpentGasRound, GetJobsRound, Keep3rJobAbciApp, PathSelectionRound, PerformWorkRound, + SwapAndDisburseRewardsRound, SynchronizedData, UnbondingRound, WaitingRound, @@ -315,6 +329,35 @@ def amount_to_approve( allowance = cast(int, allowance_msg.state.body.get("data")) return bond_amount - allowance + def is_ready_to_withdraw( + self, keeper: str, bonding_asset: str + ) -> Generator[None, None, Optional[bool]]: + """Check if the bond is ready to be activated""" + + can_withdraw_after = yield from self.read_keep3r( + "can_withdraw_after", + address=keeper, + bonding_asset=bonding_asset, + ) + if can_withdraw_after is None: + # something went wrong + return None + ledger_api_response = yield from self.get_ledger_api_response( + performative=LedgerApiMessage.Performative.GET_STATE, + ledger_callable="get_block", + block_identifier="latest", + ) + if ledger_api_response.performative != LedgerApiMessage.Performative.STATE: + log_msg = "Failed ledger get_block call in has_bonded" + self.context.logger.error(f"{log_msg}: {ledger_api_response}") + return None + latest_block_timestamp = cast( + int, ledger_api_response.state.body.get("timestamp") + ) + remaining_time = can_withdraw_after - latest_block_timestamp + self.context.logger.info(f"Remaining withdraw time: {remaining_time}") + return remaining_time <= 0 + def has_pending_bond( self, address: str, bonding_asset: str ) -> Generator[None, None, Optional[bool]]: @@ -529,6 +572,23 @@ def build_safe_raw_tx( ) return payload_data + def get_pending_unbonds( + self, keeper_address: str, bonding_asset: str + ) -> Generator[None, None, Optional[int]]: + """Get the amount of K3PR we are already unbonding.""" + pending_unbonds = yield from self.read_keep3r( + "pending_unbonds", + address=keeper_address, + bonding_asset=bonding_asset, + ) + if pending_unbonds is None: + # something went wrong + return None + + # return the amount of K3PR to swap, + # which should be all the available K3PR + return pending_unbonds + def _load_contract_package( self, ipfs_hash: str ) -> Generator[None, None, Optional[PublicId]]: @@ -635,16 +695,33 @@ def select_path( # pylint: disable=R0911 return self.transitions["APPROVE_BOND"].name return self.transitions["NOT_BONDED"].name - should_unbond = yield from self.should_unbond_k3pr( + pending_unbonds = yield from self.get_pending_unbonds( safe_address, self.params.k3pr_address ) - if should_unbond is None: + if pending_unbonds is None: # something went wrong return None - # only if true we unbond, if false we continue with the rest of the logic - if should_unbond: + should_unbond_k3pr = yield from self.should_unbond_k3pr( + safe_address, self.params.k3pr_address + ) + if should_unbond_k3pr is None: + # something went wrong + return None + if pending_unbonds == 0 and should_unbond_k3pr: + # we only unbond if we have reached the unbond threshold and we have no pending unbonds + # no pending unbonds means we can unbond without pushing the withdrawal date for all the pending unbonds return self.transitions["UNBOND"].name + # if we reach this point we have a pending unbond, we check if we can withdraw + is_ready_to_withdraw = yield from self.is_ready_to_withdraw( + safe_address, self.params.k3pr_address + ) + if is_ready_to_withdraw is None: + # something went wrong + return None + if is_ready_to_withdraw and pending_unbonds > 0: + return self.transitions["WITHDRAW"].name + bonded_keeper = yield from self.is_ready_to_activate( safe_address, self.context.params.bonding_asset ) @@ -822,6 +899,452 @@ def _build_unbond_tx(self) -> Generator[None, None, Optional[SafeTx]]: return safe_tx +class CalculateSpentGasBehaviour(Keep3rJobBaseBehaviour): + """A behaviour to check the amount of gas spent per user.""" + + matching_round: Type[AbstractRound] = CalculateSpentGasRound + _NO_EVENT: Dict = {} + + def async_act(self) -> Generator: + """Do the act, supporting asynchronous execution.""" + + with self.context.benchmark_tool.measure(self.behaviour_id).local(): + address_to_gas_spent = yield from self._get_gas_spent() + payload = CalculateSpentGasPayload( + sender=self.context.agent_address, + address_to_gas_spent=address_to_gas_spent, + ) + + with self.context.benchmark_tool.measure(self.behaviour_id).consensus(): + yield from self.send_a2a_transaction(payload) + yield from self.wait_until_round_end() + + self.set_done() + + def _get_gas_spent(self) -> Generator[None, None, str]: + """Get the gas spent for the latest unbonding interval.""" + keeper_address = self.synchronized_data.safe_contract_address + bonding_asset = self.params.k3pr_address + latest_unbonding_event = yield from self._get_latest_unbonding_event( + keeper_address, bonding_asset + ) + if latest_unbonding_event is None or latest_unbonding_event == self._NO_EVENT: + # something went wrong, the keeper MUST have unbonded if we have reached this point + return CalculateSpentGasRound.ERROR_PAYLOAD + + latest_withdraw_event = yield from self._get_latest_withdrawal_event( + keeper_address, bonding_asset + ) + if latest_withdraw_event is None: + # something went wrong + return CalculateSpentGasRound.ERROR_PAYLOAD + + # we start from the block the block in which the last withdraw event happened, or from the first block if we have + # never withdrawn + from_block = ( + 0 + if latest_withdraw_event == self._NO_EVENT + else latest_withdraw_event["block_number"] + ) + # we end at the block in which the last unbonding event happened + to_block = latest_unbonding_event["block_number"] + + transactions = yield from self._get_safe_txs( + keeper_address, from_block, to_block + ) + if transactions is None: + # something went wrong + return CalculateSpentGasRound.ERROR_PAYLOAD + + transaction_hashes = [tx["tx_hash"] for tx in transactions] + tx_sender_to_gas_spent = yield from self._tx_sender_to_gas_spent( + transaction_hashes + ) + if tx_sender_to_gas_spent is None: + # something went wrong + return CalculateSpentGasRound.ERROR_PAYLOAD + tx_sender_to_gas_spent_str = json.dumps(tx_sender_to_gas_spent, sort_keys=True) + return tx_sender_to_gas_spent_str + + def _get_latest_withdrawal_event( + self, keeper_address: str, bonding_asset: str + ) -> Generator[None, None, Optional[Dict]]: + """Get withdrawal events""" + withdrawal_events = yield from self.read_keep3r( + "get_withdrawal_events", + address=keeper_address, + bonding_asset=bonding_asset, + ) + if withdrawal_events is None: + # something went wrong + return None + + if len(withdrawal_events) == 0: + # return the empty dict to indicate no withdraws + return self._NO_EVENT + + # return the latest withdraw event + # events are sorted by block number + return withdrawal_events[-1] + + def _get_latest_unbonding_event( + self, keeper_address: str, bonding_asset: str + ) -> Generator[None, None, Optional[Dict]]: + """Get unbonding events""" + unbonding_events = yield from self.read_keep3r( + "get_unbonding_events", + address=keeper_address, + bonding_asset=bonding_asset, + ) + if unbonding_events is None: + # something went wrong + return None + + if len(unbonding_events) == 0: + # return the empty dict to indicate no unbondings + return self._NO_EVENT + + # return the latest unbonding event + # events are sorted by block number + return unbonding_events[-1] + + def _get_safe_txs( + self, safe_address: str, from_block: int, to_block: int + ) -> Generator[None, None, Optional[List[Dict]]]: + """Get the safe txs.""" + contract_api_response = yield from self.get_contract_api_response( + performative=ContractApiMessage.Performative.GET_STATE, + contract_address=safe_address, + contract_id=str(GnosisSafeContract.contract_id), + contract_callable="get_safe_txs", + from_block=from_block, + to_block=to_block, + ) + if contract_api_response.performative != ContractApiMessage.Performative.STATE: + self.context.logger.error( + f"Failed to get safe txs: {contract_api_response}" + ) + return None + log_msg = f"`get_safe_txs` contract api response on {contract_api_response}" + self.context.logger.info(f"{log_msg}: {contract_api_response}") + return cast(List[Dict], contract_api_response.state.body.get("txs")) + + def _tx_sender_to_gas_spent( + self, transaction_hashes: List[str] + ) -> Generator[None, None, Optional[Dict[str, int]]]: + """Get a mapping of tx senders to the amount of eth they've spent on gas for the given tx hashes.""" + tx_sender_to_gas_spent = yield from self.read_keep3r( + "sender_to_amount_spent", + transaction_hashes=transaction_hashes, + ) + if tx_sender_to_gas_spent is None: + # something went wrong + return None + + return cast(Dict[str, int], tx_sender_to_gas_spent) + + +class SwapAndDisburseRewardsBehaviour(Keep3rJobBaseBehaviour): + """SwapAndDisburseRewardsBehaviour""" + + matching_round: Type[AbstractRound] = SwapAndDisburseRewardsRound + + _ETH_INDEX = 0 + _K3PR_INDEX = 1 + + def async_act(self) -> Generator: + """Do the act, supporting asynchronous execution.""" + + with self.context.benchmark_tool.measure(self.behaviour_id).local(): + swap_and_disburse_tx = yield from self.get_tx() + payload = SwapAndDisburseRewardsPayload( + self.context.agent_address, swap_and_disburse_tx + ) + + with self.context.benchmark_tool.measure(self.behaviour_id).consensus(): + yield from self.send_a2a_transaction(payload) + yield from self.wait_until_round_end() + + self.set_done() + + def get_tx(self) -> Generator[None, None, str]: + """ + Prepare the multisend transaction to execute. + + Required transactions to execute the swap and disburse: + 1. Withdraw the k3pr from the keep3r contract + 2. Approve the k3pr for the swap + 3. Swap the k3pr for eth + 4. Disburse the eth to the agents + + :returns: the multisend transaction, or error payload if something went wrong + :yields: None + """ + keeper_address = self.synchronized_data.safe_contract_address + bonding_asset = self.context.params.k3pr_address + k3pr_amount = yield from self.get_pending_unbonds(keeper_address, bonding_asset) + if k3pr_amount is None: + # something went wrong + return SwapAndDisburseRewardsRound.ERROR_PAYLOAD + + # get the minimum amount of eth we are willing to swap the k3pr for + min_eth_amount = yield from self._get_eth_amount(k3pr_amount) + if min_eth_amount is None: + # something went wrong + return SwapAndDisburseRewardsRound.ERROR_PAYLOAD + + multisend_txs: List[Dict[str, Any]] = [] + # 1. get the withdraw transaction + withdraw_tx = yield from self._get_withdraw_tx( + self.keep3r_v2_contract_address, bonding_asset + ) + if withdraw_tx is None: + # something went wrong + return SwapAndDisburseRewardsRound.ERROR_PAYLOAD + + # 2. get the approve transaction + approve_tx = yield from self._get_approve_tx( + bonding_asset, self.params.curve_pool_contract_address, k3pr_amount + ) + if approve_tx is None: + # something went wrong + return SwapAndDisburseRewardsRound.ERROR_PAYLOAD + + # 3. get the swap transaction + swap_tx = yield from self._get_swap_tx( + self.params.curve_pool_contract_address, k3pr_amount, min_eth_amount + ) + if swap_tx is None: + # something went wrong + return SwapAndDisburseRewardsRound.ERROR_PAYLOAD + + # 4. get the agent disburse transactions + address_to_gas_spent = self.synchronized_data.address_to_gas_spent + address_to_eth = self._get_transfer_amounts( + address_to_gas_spent, min_eth_amount + ) + disburse_txs = self._get_disburse_txs(address_to_eth, min_eth_amount) + if disburse_txs is None: + # something went wrong + return SwapAndDisburseRewardsRound.ERROR_PAYLOAD + + multisend_txs.extend(disburse_txs) + tx = yield from self._get_multisend_tx(multisend_txs) + if tx is None: + # something went wrong + return SwapAndDisburseRewardsRound.ERROR_PAYLOAD + return tx + + def _get_eth_amount(self, k3pr_amount: int) -> Generator[None, None, Optional[int]]: + """Get the amount of eth we expect for the provided K3PR amount.""" + contract_api_response = yield from self.get_contract_api_response( + performative=ContractApiMessage.Performative.GET_STATE, + contract_address=self.params.curve_pool_contract_address, + contract_id=str(CurvePoolContract.contract_id), + contract_callable="get_dy", + dx=k3pr_amount, + i=self._K3PR_INDEX, + j=self._ETH_INDEX, + ) + if contract_api_response.performative != ContractApiMessage.Performative.STATE: + self.context.logger.error(f"Failed get_dy: {contract_api_response}") + return None + log_msg = f"`get_dy` contract api response on {contract_api_response}" + self.context.logger.info(f"{log_msg}: {contract_api_response}") + return cast(int, contract_api_response.state.body.get("data", 0)) + + def _get_swap_tx( + self, pool_address: str, k3pr_amount: int, min_eth_amount: int + ) -> Generator[None, None, Optional[Dict[str, Any]]]: + """Swap tx.""" + contract_api_response = yield from self.get_contract_api_response( + performative=ContractApiMessage.Performative.GET_STATE, + contract_address=pool_address, + contract_id=str(CurvePoolContract.contract_id), + contract_callable="build_exchange_tx", + dx=k3pr_amount, + i=self._K3PR_INDEX, + j=self._ETH_INDEX, + min_dy=min_eth_amount, + ) + if contract_api_response.performative != ContractApiMessage.Performative.STATE: + self.context.logger.error( + f"Failed build_exchange_tx: {contract_api_response}" + ) + return None + log_msg = ( + f"`build_exchange_tx` contract api response on {contract_api_response}" + ) + self.context.logger.info(f"{log_msg}: {contract_api_response}") + data_str = cast( + Optional[str], contract_api_response.state.body.get("data", False) + ) + if data_str is None: + # something went wrong + return None + + data = bytes.fromhex(data_str[2:]) + # build a single tx + single_tx = { + "operation": MultiSendOperation.CALL, + "to": pool_address, + "value": ZERO_ETH, + "data": HexBytes(data), + } + return single_tx + + def _get_withdraw_tx( + self, keep3rV2_address: str, bonding_asset: str + ) -> Generator[None, None, Optional[Dict[str, Any]]]: + """Withdraw tx.""" + withdraw_tx = yield from self.read_keep3r( + "build_withdraw_tx", + bonding_asset=bonding_asset, + ) + if withdraw_tx is None: + # something went wrong + return None + data = bytes.fromhex(withdraw_tx[2:]) + # build a single tx + single_tx = { + "operation": MultiSendOperation.CALL, + "to": keep3rV2_address, + "value": ZERO_ETH, + "data": HexBytes(data), + } + return single_tx + + def _get_approve_tx( + self, bonding_asset: str, spender: str, amount: int + ) -> Generator[None, None, Optional[Dict[str, Any]]]: + """Approve tx.""" + approve_tx = yield from self.build_approve_raw_tx( + spender, bonding_asset, amount + ) + if approve_tx is None: + # something went wrong + return None + + # build a single tx + single_tx = { + "operation": MultiSendOperation.CALL, + "to": bonding_asset, + "value": ZERO_ETH, + "data": HexBytes(approve_tx["data"]), + } + return single_tx + + def _get_transfer_amounts( + self, address_to_gas: Dict[str, int], eth_amount: int + ) -> Dict[str, int]: + """Get the transfer amounts for each address.""" + total_gas_spent = sum(address_to_gas.values()) + surplus = eth_amount - total_gas_spent + if surplus <= 0: + # there is no surplus, we divide the eth_amount based on the gas spent by each address + self.context.logger.info( + "There is no surplus, we divide the eth_amount based on the gas spent by each address." + ) + return { + address: int(eth_amount * gas_spent / total_gas_spent) + for address, gas_spent in address_to_gas.items() + } + + # agents get their share, the rest sits in the safe + # agent_surplus_share defines the share of the surplus that goes to the agents + agent_surplus_share: float = self.params.agent_surplus_share + agent_surplus = int(surplus * agent_surplus_share) + + # there is a surplus, we divide the surplus equally among the agents + surplus_per_agent = int(agent_surplus / len(address_to_gas)) + return { + address: gas_spent + surplus_per_agent + for address, gas_spent in address_to_gas.items() + } + + def _get_disburse_txs( + self, address_to_eth: Dict[str, int], eth_amount: int + ) -> List[Dict[str, Any]]: + """Get the transfer txs for each address.""" + transfer_amounts = self._get_transfer_amounts(address_to_eth, eth_amount) + transfer_txs = [] + for address, amount in transfer_amounts.items(): + transfer_txs.append( + { + "operation": MultiSendOperation.CALL, + "to": address, + "value": amount, + "data": b"", + } + ) + return transfer_txs + + def _get_safe_tx_hash(self, data: bytes) -> Generator[None, None, Optional[str]]: + """Prepares and returns the safe tx hash.""" + response = yield from self.get_contract_api_response( + performative=ContractApiMessage.Performative.GET_STATE, # type: ignore + contract_address=self.synchronized_data.safe_contract_address, + contract_id=str(GnosisSafeContract.contract_id), + contract_callable="get_raw_safe_transaction_hash", + to_address=self.params.multisend_address, # we send the tx to the multisend address + value=ZERO_ETH, + data=data, + safe_tx_gas=SAFE_GAS, + operation=SafeOperation.DELEGATE_CALL.value, + ) + + if response.performative != ContractApiMessage.Performative.STATE: + self.context.logger.error( + f"Couldn't get safe hash. " + f"Expected response performative {ContractApiMessage.Performative.STATE.value}, " # type: ignore + f"received {response.performative.value}." + ) + return None + + # strip "0x" from the response hash + tx_hash = cast(str, response.state.body["tx_hash"])[2:] + return tx_hash + + def _get_multisend_tx( + self, + multi_send_txs: List[Dict[str, Any]], + ) -> Generator[None, None, Optional[str]]: + """Get the multisend tx.""" + response = yield from self.get_contract_api_response( + performative=ContractApiMessage.Performative.GET_RAW_TRANSACTION, # type: ignore + contract_address=self.params.multisend_address, + contract_id=str(MultiSendContract.contract_id), + contract_callable="get_tx_data", + multi_send_txs=multi_send_txs, + ) + if response.performative != ContractApiMessage.Performative.RAW_TRANSACTION: + self.context.logger.error( + f"Couldn't compile the multisend tx. " + f"Expected response performative {ContractApiMessage.Performative.RAW_TRANSACTION.value}, " # type: ignore + f"received {response.performative.value}." + ) + return None + + # strip "0x" from the response + multisend_data_str = cast(str, response.raw_transaction.body["data"])[2:] + tx_data = bytes.fromhex(multisend_data_str) + tx_hash = yield from self._get_safe_tx_hash(tx_data) + if tx_hash is None: + # something went wrong + return None + + payload_data = hash_payload_to_hex( + safe_tx_hash=tx_hash, + ether_value=ZERO_ETH, + safe_tx_gas=SAFE_GAS, + operation=SafeOperation.DELEGATE_CALL.value, + to_address=self.params.multisend_address, + data=tx_data, + use_flashbots=self.use_flashbots, + ) + return payload_data + + class WaitingBehaviour(Keep3rJobBaseBehaviour): """WaitingBehaviour""" @@ -1065,6 +1588,8 @@ class Keep3rJobRoundBehaviour(AbstractRoundBehaviour): PathSelectionBehaviour, # type: ignore BondingBehaviour, # type: ignore UnbondingBehaviour, # type: ignore + CalculateSpentGasBehaviour, # type: ignore + SwapAndDisburseRewardsBehaviour, # type: ignore WaitingBehaviour, # type: ignore ActivationBehaviour, # type: ignore GetJobsBehaviour, # type: ignore diff --git a/packages/valory/skills/keep3r_job_abci/models.py b/packages/valory/skills/keep3r_job_abci/models.py index 4addf84c..274cebdb 100644 --- a/packages/valory/skills/keep3r_job_abci/models.py +++ b/packages/valory/skills/keep3r_job_abci/models.py @@ -85,6 +85,14 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: ) self.k3pr_address = self._ensure("k3pr_address", kwargs, str) self.unbonding_threshold = self._ensure("unbonding_threshold", kwargs, int) + self.curve_pool_contract_address = self._ensure( + "curve_pool_contract_address", kwargs, str + ) + self.agent_surplus_share = self._ensure("agent_surplus_share", kwargs, float) + multisend_address = kwargs.get("multisend_address", None) + if multisend_address is None: + raise ValueError("Multisend address not specified!") + self.multisend_address = multisend_address super().__init__(*args, **kwargs) def _get_supported_jobs_to_package_hash(self, kwargs: Dict) -> Dict[str, str]: diff --git a/packages/valory/skills/keep3r_job_abci/payloads.py b/packages/valory/skills/keep3r_job_abci/payloads.py index fb5c3db3..75b702cd 100644 --- a/packages/valory/skills/keep3r_job_abci/payloads.py +++ b/packages/valory/skills/keep3r_job_abci/payloads.py @@ -85,3 +85,17 @@ class WorkTxPayload(BaseTxPayload): """Represent a transaction payload of type 'randomness'.""" work_tx: str + + +@dataclass(frozen=True) +class CalculateSpentGasPayload(BaseTxPayload): + """Represent a transaction payload of type 'CalculateSpentGas'.""" + + address_to_gas_spent: str + + +@dataclass(frozen=True) +class SwapAndDisburseRewardsPayload(BaseTxPayload): + """Represent a transaction payload of type 'SwapAndDisburseRewardsPayload'.""" + + swap_and_disburse_tx: str diff --git a/packages/valory/skills/keep3r_job_abci/rounds.py b/packages/valory/skills/keep3r_job_abci/rounds.py index aa8b2628..21da3ad5 100644 --- a/packages/valory/skills/keep3r_job_abci/rounds.py +++ b/packages/valory/skills/keep3r_job_abci/rounds.py @@ -38,8 +38,10 @@ ActivationTxPayload, ApproveBondTxPayload, BondingTxPayload, + CalculateSpentGasPayload, GetJobsPayload, PathSelectionPayload, + SwapAndDisburseRewardsPayload, TopUpPayload, UnbondingTxPayload, WaitingPayload, @@ -59,6 +61,7 @@ class Event(Enum): ACTIVATION_TX = "activation_tx" AWAITING_BONDING = "awaiting_bonding" BLACKLISTED = "blacklisted" + WITHDRAW = "withdraw" UNKNOWN_HEALTH_ISSUE = "unknown_health_issue" HEALTHY = "healthy" DONE = "done" @@ -73,6 +76,7 @@ class Event(Enum): NO_MAJORITY = "no_majority" ROUND_TIMEOUT = "round_timeout" SIMULATION_FAILED = "simulation_failed" + ERROR = "error" class SynchronizedData(BaseSynchronizedData): @@ -103,6 +107,11 @@ def tx_submitter(self) -> str: """Get the round that submitted a tx to transaction_settlement_abci.""" return cast(str, self.db.get_strict("tx_submitter")) + @property + def address_to_gas_spent(self) -> Dict[str, int]: + """Get the address_to_gas_spent.""" + return cast(Dict[str, int], self.db.get_strict("address_to_gas_spent")) + class Keep3rJobAbstractRound(CollectSameUntilThresholdRound, ABC): """Keep3rJobAbstractRound""" @@ -130,6 +139,7 @@ class PathSelectionRound(Keep3rJobAbstractRound): "HEALTHY": Event.HEALTHY, "INSUFFICIENT_FUNDS": Event.INSUFFICIENT_FUNDS, "BLACKLISTED": Event.BLACKLISTED, + "WITHDRAW": Event.WITHDRAW, "UNKNOWN_HEALTH_ISSUE": Event.UNKNOWN_HEALTH_ISSUE, } @@ -222,6 +232,69 @@ def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Event]]: return None +class CalculateSpentGasRound(Keep3rJobAbstractRound): + """CalculateSpentGasRound""" + + payload_class = CalculateSpentGasPayload + payload_attribute: str = "address_to_gas_spent" + + ERROR_PAYLOAD = "ERROR" + + def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Event]]: + """Process the end of the block.""" + + if self.threshold_reached: + address_to_gas_spent_str = self.most_voted_payload + if address_to_gas_spent_str == self.ERROR_PAYLOAD: + return self.synchronized_data, Event.ERROR + + address_to_gas_spent = json.loads(address_to_gas_spent_str) + state = self.synchronized_data.update( + synchronized_data_class=SynchronizedData, + **{ + get_name( + SynchronizedData.address_to_gas_spent + ): address_to_gas_spent, + }, + ) + return state, Event.DONE + if not self.is_majority_possible( + self.collection, self.synchronized_data.nb_participants + ): + return self.synchronized_data, Event.NO_MAJORITY + return None + + +class SwapAndDisburseRewardsRound(Keep3rJobAbstractRound): + """Round to swap and distribute the earned keeper.""" + + payload_class = SwapAndDisburseRewardsPayload + payload_attribute: str = "swap_and_disburse_tx" + + ERROR_PAYLOAD = "ERROR" + + def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Event]]: + """Process the end of the block.""" + + if self.threshold_reached: + multisend_tx = self.most_voted_payload + if multisend_tx == self.ERROR_PAYLOAD: + return self.synchronized_data, Event.ERROR + + state = self.synchronized_data.update( + **{ + get_name(SynchronizedData.most_voted_tx_hash): multisend_tx, + get_name(SynchronizedData.tx_submitter): self.auto_round_id(), + } + ) + return state, Event.DONE + if not self.is_majority_possible( + self.collection, self.synchronized_data.nb_participants + ): + return self.synchronized_data, Event.NO_MAJORITY + return None + + class WaitingRound(Keep3rJobAbstractRound): """WaitingRound""" @@ -449,6 +522,7 @@ class Keep3rJobAbciApp(AbciApp[Event]): Event.UNBOND: UnbondingRound, Event.BLACKLISTED: BlacklistedRound, Event.APPROVE_BOND: ApproveBondRound, + Event.WITHDRAW: CalculateSpentGasRound, Event.UNKNOWN_HEALTH_ISSUE: DegenerateRound, Event.NO_MAJORITY: PathSelectionRound, Event.ROUND_TIMEOUT: PathSelectionRound, @@ -468,6 +542,18 @@ class Keep3rJobAbciApp(AbciApp[Event]): Event.NO_MAJORITY: UnbondingRound, Event.ROUND_TIMEOUT: UnbondingRound, }, + CalculateSpentGasRound: { + Event.DONE: SwapAndDisburseRewardsRound, + Event.NO_MAJORITY: CalculateSpentGasRound, + Event.ROUND_TIMEOUT: CalculateSpentGasRound, + Event.ERROR: PathSelectionRound, + }, + SwapAndDisburseRewardsRound: { + Event.DONE: FinalizeWorkRound, + Event.NO_MAJORITY: SwapAndDisburseRewardsRound, + Event.ROUND_TIMEOUT: SwapAndDisburseRewardsRound, + Event.ERROR: PathSelectionRound, + }, WaitingRound: { Event.DONE: ActivationRound, Event.NO_MAJORITY: WaitingRound, diff --git a/packages/valory/skills/keep3r_job_abci/skill.yaml b/packages/valory/skills/keep3r_job_abci/skill.yaml index 2c32a05a..e891f716 100644 --- a/packages/valory/skills/keep3r_job_abci/skill.yaml +++ b/packages/valory/skills/keep3r_job_abci/skill.yaml @@ -8,28 +8,30 @@ aea_version: '>=1.0.0, <2.0.0' fingerprint: README.md: bafybeidq32yfua6bopvzlo7xwpfdiz4bwr7txkv4vo4vxmjmvdthkr2cwe __init__.py: bafybeifr6ekniqkhuvkyfw3xktsntjvjjye5vfyir2i5zrzc3bcud5vvqa - behaviours.py: bafybeibjz7amkg6mapnbqjutszhykwkpnmycajjnwdhq4cwppdboinhkyq + behaviours.py: bafybeibpesvx2rcxswmotx2jbog2l4wxgfc23vonvw64x2qfbx45pjzxse dialogues.py: bafybeidfvafboay732zd7ez4yblojbzohujfwtp3e5elit7ztenepk6q3a dynamic_package_loader.py: bafybeifdp6ym6jjjqbcu4qcg5vkh2kksvowryvrfcjbklvhvs36653troe fsm_specification.yaml: bafybeihjvacl6sclfyrntgqoxexfxjq2kfjeyc6whpo4wciynrs3plclxm handlers.py: bafybeiflkitcwl4b4glto7xaf7oykdsutbsyr62fwzh4ycy6grvblnypya io_/__init__.py: bafybeifxgmmwjqzezzn3e6keh2bfo4cyo7y5dq2ept3stfmgglbrzfl5rq io_/loader.py: bafybeidbnhostvbufwc4z2ulcgzw3weyps4obpnofkuglaehz2jpwstpbq - models.py: bafybeicmjfaokpvass5wv7vthhnyicjdwmirtdgn7ug5gpqgfyrwrllbua - payloads.py: bafybeiafefdwdmziqjbmbq6x2lutbbre6bs6hqek4p75rnqk7q65w3ymjm - rounds.py: bafybeigt74daiqwdyzbhpevqmqkrey5bsdbg2qtnpkrrc7rwhkmk6zx3im + models.py: bafybeidz7o7j5y3paczwsfabqd3xyg2xca2wi6milyeyq7mssrekddkcuu + payloads.py: bafybeih4nbp77gimv4h3bcg3e7mutpb6h64ptzc3f5zmvw7kfpib3r2rs4 + rounds.py: bafybeiausofail75f3ebjafjnibp6fhvmiangvk6bpm44yiee7p2knr4me tests/__init__.py: bafybeicw6vp5sxxwr5p3dns6of2px4qizw4q2s55ozf5cu5uamfh3tlrby tests/helpers.py: bafybeigwnsg3r4mqo2rrai56ju4yknd6tvi3edkterbvbnptst5uwz6oa4 - tests/test_behaviours.py: bafybeiampb666oqjzqskhnxn2digrmnmbxeq7nuh5u772irnagnamy64ey + tests/test_behaviours.py: bafybeigvl6znniure2qvwh635k4fjeo4ncn5aztolddhqtovo2ot6qzqoy tests/test_dialogues.py: bafybeia6fxfnwbuubvsz5722upwyliokikwtlizhujpfglxva43wxcyfsm - tests/test_payloads.py: bafybeiboerzkr7gzru6chwqi6qhvueoduq5ica5t7tuojp2zoi4git4w2y - tests/test_rounds.py: bafybeie4cjlnkakouxn4p6wjxfjllhec77l4rt3z44wurqmoulzf7rvwvq + tests/test_payloads.py: bafybeifm72ezuvavj7qfjepzi27qipkgkasolqcwbu4qhfgjkuy6c6vdd4 + tests/test_rounds.py: bafybeib5lzc6cjhygow7aqk3p5amy44rcpebsf3c6nexq72q7c367zstvy fingerprint_ignore_patterns: [] connections: [] contracts: - valory/gnosis_safe:0.1.0:bafybeig2dobzlupi4twn3lv2avfajslgjukkmkdd4qzf37cbfv7ojupv54 +- valory/multisend:0.1.0:bafybeigjywkl7hydjsrkogob3xebj2ifhqwmfhhxoeyrndzhhxi5u6amey +- valory/curve_pool:0.1.0:bafybeia6e44qj5xmqdgewsnpa3k24ov25qf4rnvmbxpuukn2dbrbqv73ma - valory/keep3r_v1:0.1.0:bafybeibtmwyixk5h6ochkeuvecazzyj7qznoe3yoqwazypqdxovey2ar2e -- valory/keep3r_v2:0.1.0:bafybeifllwgx5mmh4qjnviex2hmgznxzde4vlm7bw5fgnwl3z2cgbgdot4 +- valory/keep3r_v2:0.1.0:bafybeicqvpeo7czhkf5m3qstzdqnxhhht6dko5yjjuzf2dt2fie2lzjzii protocols: - valory/contract_api:1.0.0:bafybeidv6wxpjyb2sdyibnmmum45et4zcla6tl63bnol6ztyoqvpl4spmy - valory/ledger_api:1.0.0:bafybeigpn6ysm53qkcllkzgdwc5xxpxz32xn2zoux3phdm2i3yty2i3thu @@ -117,11 +119,14 @@ models: k3pr_address: '0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44' keep3r_v1_contract_address: '0x1cEB5cB57C4D4E2b2433641b95Dd330A33185A44' keep3r_v2_contract_address: '0x85063437C02Ba7F4f82F898859e4992380DEd3bb' + curve_pool_contract_address: '0x21410232B484136404911780bC32756D5d1a9Fa9' + agent_surplus_share: 0.75 keeper_allowed_retries: 3 keeper_timeout: 30.0 manual_gas_limit: 0 max_attempts: 10 max_healthcheck: 120 + multisend_address: '0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761' on_chain_service_id: null profitability_threshold: 0 raise_on_failed_simulation: false diff --git a/packages/valory/skills/keep3r_job_abci/tests/test_behaviours.py b/packages/valory/skills/keep3r_job_abci/tests/test_behaviours.py index e9dbae71..37a4c9d6 100644 --- a/packages/valory/skills/keep3r_job_abci/tests/test_behaviours.py +++ b/packages/valory/skills/keep3r_job_abci/tests/test_behaviours.py @@ -25,12 +25,18 @@ import pytest +from packages.valory.contracts.curve_pool.contract import ( + PUBLIC_ID as CURVE_POOL_CONTRACT_ID, +) from packages.valory.contracts.gnosis_safe.contract import ( PUBLIC_ID as GNOSIS_SAFE_CONTRACT_ID, ) from packages.valory.contracts.keep3r_v1.contract import ( PUBLIC_ID as KEEP3R_V1_CONTRACT_ID, ) +from packages.valory.contracts.multisend.contract import ( + PUBLIC_ID as MULTISEND_CONTRACT_ID, +) from packages.valory.protocols.contract_api.custom_types import State from packages.valory.protocols.contract_api.message import ContractApiMessage from packages.valory.protocols.ledger_api.message import LedgerApiMessage @@ -48,12 +54,14 @@ ApproveBondBehaviour, AwaitTopUpBehaviour, BondingBehaviour, + CalculateSpentGasBehaviour, GetJobsBehaviour, Keep3rJobRoundBehaviour, PathSelectionBehaviour, PerformWorkBehaviour, SAFE_GAS, SafeTx, + SwapAndDisburseRewardsBehaviour, TO_WEI, UnbondingBehaviour, WaitingBehaviour, @@ -71,6 +79,7 @@ AwaitTopUpRound, BlacklistedRound, BondingRound, + CalculateSpentGasRound, Event, FinalizeActivationRound, FinalizeApproveBondRound, @@ -106,6 +115,12 @@ } TEST_JOB_CONTRACT_ID = "test_job_contract_id" DUMMY_CONTRACT = "0xaed599aadfee8e32cedb59db2b1120d33a7bacfd" +DUMMY_ADDRESS_TO_GAS_SPENT = { + "0x0": 1, + "0x1": 2, + "0x2": 3, + "0x3": 4, +} class DummyRoundId: # pylint: disable=too-few-public-methods @@ -143,6 +158,7 @@ def setup(self, **kwargs: Any) -> None: # type: ignore safe_contract_address=SOME_CONTRACT_ADDRESS, job_list=[SOME_CONTRACT_ADDRESS], workable_job=SOME_CONTRACT_ADDRESS, + address_to_gas_spent=DUMMY_ADDRESS_TO_GAS_SPENT, ) self.fast_forward(data) @@ -184,6 +200,48 @@ def mock_read_keep3r_v1(self, contract_callable: str, data: Any) -> None: ), ) + def mock_read_safe( + self, contract_callable: str, data: Any, data_field: str = "data" + ) -> None: + """Mock safe contract call""" + + self.mock_contract_api_request( + request_kwargs=dict( + performative=ContractApiMessage.Performative.GET_STATE, + callable=contract_callable, + ), + contract_id=str(GNOSIS_SAFE_CONTRACT_ID), + response_kwargs=dict( + performative=ContractApiMessage.Performative.STATE, + callable=contract_callable, + state=ContractApiMessage.State( + ledger_id="ethereum", + body={data_field: data}, + ), + ), + ) + + def mock_read_curve( + self, contract_callable: str, data: Any, data_field: str = "data" + ) -> None: + """Mock curve contract call""" + + self.mock_contract_api_request( + request_kwargs=dict( + performative=ContractApiMessage.Performative.GET_STATE, + callable=contract_callable, + ), + contract_id=str(CURVE_POOL_CONTRACT_ID), + response_kwargs=dict( + performative=ContractApiMessage.Performative.STATE, + callable=contract_callable, + state=ContractApiMessage.State( + ledger_id="ethereum", + body={data_field: data}, + ), + ), + ) + def mock_keep3r_v1_raw_tx(self, contract_callable: str, data: Any) -> None: """Mock keep3r V1 raw transaction""" @@ -283,6 +341,26 @@ def mock_build_work_tx_call(self, data: str) -> None: ), ) + def mock_multisend_tx_call(self, data: str) -> None: + """Mock build work transaction""" + + contract_callable = "get_tx_data" + self.mock_contract_api_request( + request_kwargs=dict( + performative=ContractApiMessage.Performative.GET_RAW_TRANSACTION, + callable=contract_callable, + ), + contract_id=str(MULTISEND_CONTRACT_ID), + response_kwargs=dict( + performative=ContractApiMessage.Performative.RAW_TRANSACTION, + callable=contract_callable, + raw_transaction=ContractApiMessage.RawTransaction( + ledger_id="ethereum", + body={"data": data}, # type: ignore + ), + ), + ) + def mock_build_safe_raw_tx(self) -> None: """Mock build safe raw transaction""" @@ -397,7 +475,10 @@ def test_not_activated(self, *_: Any) -> None: self.mock_read_keep3r_v1("blacklist", False) self.mock_ethereum_get_balance(amount=0) self.mock_read_keep3r_v1("bondings", 1) + self.mock_read_keep3r_v1("pending_unbonds", 0) self.mock_read_keep3r_v1("bondings", 1) + self.mock_read_keep3r_v1("can_withdraw_after", 0) + self.mock_get_latest_block({"timestamp": 3 * SECONDS_PER_DAY + 1}) self.mock_read_keep3r_v1("bondings", 1) self.mock_read_keep3r_v1("bond", 3 * SECONDS_PER_DAY) self.mock_get_latest_block(block={"timestamp": 0}) @@ -416,7 +497,10 @@ def test_unbond(self, *_: Any) -> None: self.mock_read_keep3r_v1("blacklist", False) self.mock_ethereum_get_balance(amount=0) self.mock_read_keep3r_v1("bondings", 1) + self.mock_read_keep3r_v1("pending_unbonds", 0) self.mock_read_keep3r_v1("bondings", 1) + self.mock_read_keep3r_v1("can_withdraw_after", 0) + self.mock_get_latest_block({"timestamp": 3 * SECONDS_PER_DAY + 1}) self.mock_read_keep3r_v1("bondings", 51 * TO_WEI) self.mock_read_keep3r_v1("bond", 3 * SECONDS_PER_DAY) self.mock_get_latest_block(block={"timestamp": 0}) @@ -429,13 +513,34 @@ def test_unbond(self, *_: Any) -> None: == UnbondingRound.auto_round_id() ) + def test_withdraw(self, *_: Any) -> None: + """Test path_selection to unbond.""" + self.behaviour.act_wrapper() + self.mock_read_keep3r_v1("blacklist", False) + self.mock_ethereum_get_balance(amount=0) + self.mock_read_keep3r_v1("bondings", 1) + self.mock_read_keep3r_v1("pending_unbonds", 1) + self.mock_read_keep3r_v1("bondings", 1) + self.mock_read_keep3r_v1("can_withdraw_after", 0) + self.mock_get_latest_block({"timestamp": 3 * SECONDS_PER_DAY + 1}) + self.mock_a2a_transaction() + self._test_done_flag_set() + self.end_round(done_event=Event.WITHDRAW) + assert ( + self.current_behaviour.matching_round.auto_round_id() + == CalculateSpentGasRound.auto_round_id() + ) + def test_healthy(self, *_: Any) -> None: """Test path_selection to healthy.""" self.behaviour.act_wrapper() self.mock_read_keep3r_v1("blacklist", False) self.mock_ethereum_get_balance(amount=0) self.mock_read_keep3r_v1("bondings", 1) + self.mock_read_keep3r_v1("pending_unbonds", 0) self.mock_read_keep3r_v1("bondings", 1) + self.mock_read_keep3r_v1("can_withdraw_after", 0) + self.mock_get_latest_block({"timestamp": 3 * SECONDS_PER_DAY + 1}) self.mock_read_keep3r_v1("bondings", 1) self.mock_read_keep3r_v1("bond", 3 * SECONDS_PER_DAY) self.mock_get_latest_block({"timestamp": 3 * SECONDS_PER_DAY + 1}) @@ -490,6 +595,79 @@ def test_bonding_tx(self) -> None: ) +class TestCalculateSpentGasBehaviour(Keep3rJobFSMBehaviourBaseCase): + """Test CalculateSpentGasBehaviour""" + + behaviour_class: Type[BaseBehaviour] = CalculateSpentGasBehaviour + + _DUMMY_UNBOND_EVENTS = [ + {"block_number": 1}, + {"block_number": 2}, + {"block_number": 3}, + ] + _DUMMY_WITHDRAW_EVENTS = [ + {"block_number": 1}, + {"block_number": 2}, + {"block_number": 3}, + ] + _DUMMY_SAFE_TX_EVENTS = [{"tx_hash": "0x0"}, {"tx_hash": "0x1"}, {"tx_hash": "0x2"}] + + def test_no_unbond_event(self) -> None: + """Test bonding tx""" + self.behaviour.act_wrapper() + self.mock_read_keep3r_v1("get_unbonding_events", []) + self.mock_a2a_transaction() + self._test_done_flag_set() + self.end_round(done_event=Event.ERROR) + assert ( + self.current_behaviour.auto_behaviour_id() + == PathSelectionBehaviour.auto_behaviour_id() + ) + + def test_happy_path(self) -> None: + """Test bonding tx""" + self.behaviour.act_wrapper() + self.mock_read_keep3r_v1("get_unbonding_events", self._DUMMY_UNBOND_EVENTS) + self.mock_read_keep3r_v1("get_withdrawal_events", self._DUMMY_WITHDRAW_EVENTS) + self.mock_read_safe("get_safe_txs", self._DUMMY_SAFE_TX_EVENTS, "txs") + self.mock_read_keep3r_v1("sender_to_amount_spent", DUMMY_ADDRESS_TO_GAS_SPENT) + self.mock_a2a_transaction() + self._test_done_flag_set() + self.end_round(done_event=Event.DONE) + assert ( + self.current_behaviour.auto_behaviour_id() + == SwapAndDisburseRewardsBehaviour.auto_behaviour_id() + ) + + +class TestSwapAndDisburseRewardsBehaviour(Keep3rJobFSMBehaviourBaseCase): + """Test CalculateSpentGasBehaviour""" + + behaviour_class: Type[BaseBehaviour] = SwapAndDisburseRewardsBehaviour + + _DUMMY_K3PR_REWARD_AMOUNT = 100 + _DUMMY_K3PR_TO_ETH_AMOUNT = 10 # assumes a 0.1ETH k3pr price + + def test_happy_path(self) -> None: + """Test bonding tx""" + self.behaviour.act_wrapper() + self.mock_read_keep3r_v1("pending_unbonds", self._DUMMY_K3PR_REWARD_AMOUNT) + self.mock_read_curve("get_dy", self._DUMMY_K3PR_TO_ETH_AMOUNT) + self.mock_read_keep3r_v1("build_withdraw_tx", DUMMY_DATA) + self.mock_read_keep3r_v1("build_approve_tx", DUMMY_DATA) + self.mock_read_curve("build_exchange_tx", DUMMY_DATA) + self.mock_multisend_tx_call(DUMMY_DATA) + self.mock_build_safe_raw_tx() + self.mock_a2a_transaction() + self._test_done_flag_set() + self.end_round(done_event=Event.DONE) + degenerate_state = make_degenerate_behaviour(FinalizeWorkRound) + assert ( + self.current_behaviour.auto_behaviour_id() + == degenerate_state.auto_behaviour_id() + ) + + class TestWaitingBehaviour(Keep3rJobFSMBehaviourBaseCase): """Test BondingBehaviour""" diff --git a/packages/valory/skills/keep3r_job_abci/tests/test_payloads.py b/packages/valory/skills/keep3r_job_abci/tests/test_payloads.py index a3bce480..601d3b12 100644 --- a/packages/valory/skills/keep3r_job_abci/tests/test_payloads.py +++ b/packages/valory/skills/keep3r_job_abci/tests/test_payloads.py @@ -27,8 +27,10 @@ ActivationTxPayload, ApproveBondTxPayload, BondingTxPayload, + CalculateSpentGasPayload, GetJobsPayload, PathSelectionPayload, + SwapAndDisburseRewardsPayload, UnbondingTxPayload, WaitingPayload, WorkTxPayload, @@ -114,3 +116,27 @@ def test_work_tx_payload(work_tx: str) -> None: assert payload.sender == "sender" assert payload.work_tx == work_tx assert payload.from_json(payload.json) == payload + + +@pytest.mark.parametrize("address_to_gas_spent", ["{address: gas_spent}"]) +def test_calculate_spent_gas_payload(address_to_gas_spent: str) -> None: + """Test CalculateSpentGasPayload""" + + payload = CalculateSpentGasPayload( + sender="sender", address_to_gas_spent=address_to_gas_spent + ) + assert payload.sender == "sender" + assert payload.address_to_gas_spent == address_to_gas_spent + assert payload.from_json(payload.json) == payload + + +@pytest.mark.parametrize("swap_and_disburse_tx", ["tx_hash"]) +def test_swap_and_disburse_rewards_payload(swap_and_disburse_tx: str) -> None: + """Test SwapAndDisburseRewardsPayload""" + + payload = SwapAndDisburseRewardsPayload( + sender="sender", swap_and_disburse_tx=swap_and_disburse_tx + ) + assert payload.sender == "sender" + assert payload.swap_and_disburse_tx == swap_and_disburse_tx + assert payload.from_json(payload.json) == payload diff --git a/packages/valory/skills/keep3r_job_abci/tests/test_rounds.py b/packages/valory/skills/keep3r_job_abci/tests/test_rounds.py index 3d7c1731..0be45889 100644 --- a/packages/valory/skills/keep3r_job_abci/tests/test_rounds.py +++ b/packages/valory/skills/keep3r_job_abci/tests/test_rounds.py @@ -33,8 +33,10 @@ ActivationTxPayload, ApproveBondTxPayload, BondingTxPayload, + CalculateSpentGasPayload, GetJobsPayload, PathSelectionPayload, + SwapAndDisburseRewardsPayload, TopUpPayload, UnbondingTxPayload, WaitingPayload, @@ -45,11 +47,13 @@ ApproveBondRound, AwaitTopUpRound, BondingRound, + CalculateSpentGasRound, Event, GetJobsRound, Keep3rJobAbstractRound, PathSelectionRound, PerformWorkRound, + SwapAndDisburseRewardsRound, SynchronizedData, UnbondingRound, WaitingRound, @@ -219,6 +223,36 @@ def test_run(self, activation_tx: str) -> None: assert event == Event.ACTIVATION_TX +class TestCalculateSpentGasRound(BaseRoundTestClass): + """Tests for CalculateSpentGasRound.""" + + round_class = CalculateSpentGasRound + payload_class = CalculateSpentGasPayload + + @pytest.mark.parametrize("address_to_gas_spent", ['{"0x0": 1}']) + def test_run(self, address_to_gas_spent: str) -> None: + """Run tests.""" + + next_state = self.deliver_payloads(address_to_gas_spent=address_to_gas_spent) + event = self.complete_round(next_state) + assert event == Event.DONE + + +class TestSwapAndDisburseRewardsRound(BaseRoundTestClass): + """Tests for SwapAndDisburseRewards.""" + + round_class = SwapAndDisburseRewardsRound + payload_class = SwapAndDisburseRewardsPayload + + @pytest.mark.parametrize("swap_and_disburse_tx", ["some_raw_tx_hash"]) + def test_run(self, swap_and_disburse_tx: str) -> None: + """Run tests.""" + + next_state = self.deliver_payloads(swap_and_disburse_tx=swap_and_disburse_tx) + event = self.complete_round(next_state) + assert event == Event.DONE + + class TestGetJobsRound(BaseRoundTestClass): """Tests for GetJobsRound."""