From 7f7175c97f7c593924f1f50ba76ec0d823e94261 Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Thu, 3 Oct 2024 01:02:31 +0530 Subject: [PATCH 1/7] fix: add retry and fallback mechanism --- packages/packages.json | 8 +- .../valory/agents/optimus/aea-config.yaml | 6 +- packages/valory/services/optimus/service.yaml | 2 +- .../liquidity_trader_abci/behaviours.py | 467 ++++++++++-------- .../skills/liquidity_trader_abci/rounds.py | 53 +- .../skills/liquidity_trader_abci/skill.yaml | 6 +- .../valory/skills/optimus_abci/skill.yaml | 4 +- 7 files changed, 323 insertions(+), 223 deletions(-) diff --git a/packages/packages.json b/packages/packages.json index 6d82fa6..cfbf7fb 100644 --- a/packages/packages.json +++ b/packages/packages.json @@ -8,10 +8,10 @@ "contract/valory/merkl_distributor/0.1.0": "bafybeihaqsvmncuzmwv2r6iuzc5t7ur6ugdhephz7ydftypksjidpsylbq", "contract/valory/staking_token/0.1.0": "bafybeifrvtkofw5c26b3irm6izqfdpik6vpjhm6hqwcdzx333h6vhdanai", "contract/valory/staking_activity_checker/0.1.0": "bafybeibjzsi2r5b6xd4iwl4wbwldptnynryzsdpifym4mkv32ynswx22ou", - "skill/valory/liquidity_trader_abci/0.1.0": "bafybeich6pbchgdy7v56dwfdq6btb2blcagfquhytdhaemdfgmtgk4hbv4", - "skill/valory/optimus_abci/0.1.0": "bafybeigdgah43tm2byc5ilynpxkcjkt2lh5euwjbb36shxjyn57alfb77i", - "agent/valory/optimus/0.1.0": "bafybeihoajamcrdv3yxsgrmfaeyhspjc6vt4jbwn3egmzcdszihtpjdno4", - "service/valory/optimus/0.1.0": "bafybeiasbh4wha2soe62izwwwtgf2vxcjyxe5nxqen4t66amfwbyho4v2u" + "skill/valory/liquidity_trader_abci/0.1.0": "bafybeidxpwveipbrdhzlawtzvkhtuwqp42ys2vw25tu5iqiu6tndlz7aom", + "skill/valory/optimus_abci/0.1.0": "bafybeibzjuqil6ipnzhmljlorobbjnv3ai3uv4cave5sv43k336mvbj2ia", + "agent/valory/optimus/0.1.0": "bafybeiftg63fekqan2uzenh5ro2fnitfqyc77ehu74vxjmsq2lyx56xiby", + "service/valory/optimus/0.1.0": "bafybeigxj6z6pvibmya534hm2tmvtoultb5ymqfvj5gmweimmbkk7jf52m" }, "third_party": { "protocol/open_aea/signing/1.0.0": "bafybeihv62fim3wl2bayavfcg3u5e5cxu3b7brtu4cn5xoxd6lqwachasi", diff --git a/packages/valory/agents/optimus/aea-config.yaml b/packages/valory/agents/optimus/aea-config.yaml index 9b26e8b..c1c83e4 100644 --- a/packages/valory/agents/optimus/aea-config.yaml +++ b/packages/valory/agents/optimus/aea-config.yaml @@ -35,8 +35,8 @@ protocols: skills: - valory/abstract_abci:0.1.0:bafybeidz54kvxhbdmpruzguuzzq7bjg4pekjb5amqobkxoy4oqknnobopu - valory/abstract_round_abci:0.1.0:bafybeiajjzuh6vf23crp55humonknirvv2f4s3dmdlfzch6tc5ow52pcgm -- valory/liquidity_trader_abci:0.1.0:bafybeich6pbchgdy7v56dwfdq6btb2blcagfquhytdhaemdfgmtgk4hbv4 -- valory/optimus_abci:0.1.0:bafybeigdgah43tm2byc5ilynpxkcjkt2lh5euwjbb36shxjyn57alfb77i +- valory/liquidity_trader_abci:0.1.0:bafybeidxpwveipbrdhzlawtzvkhtuwqp42ys2vw25tu5iqiu6tndlz7aom +- valory/optimus_abci:0.1.0:bafybeibzjuqil6ipnzhmljlorobbjnv3ai3uv4cave5sv43k336mvbj2ia - valory/registration_abci:0.1.0:bafybeiffipsowrqrkhjoexem7ern5ob4fabgif7wa6gtlszcoaop2e3oey - valory/reset_pause_abci:0.1.0:bafybeif4lgvbzsmzljesxbphycdv52ka7qnihyjrjpfaseclxadcmm6yiq - valory/termination_abci:0.1.0:bafybeiekkpo5qef5zaeagm3si6v45qxcojvtjqe4a5ceccvk4q7k3xi3bi @@ -226,7 +226,7 @@ models: merkl_distributor_contract_addresses: ${str:{"optimism":"0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae","base":"0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae"}} intermediate_tokens: ${str:{"ethereum":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2":{"symbol":"WETH","liquidity_provider":"0xF04a5cC80B1E94C69B48f5ee68a08CD2F09A7c3E"},"0xdAC17F958D2ee523a2206206994597C13D831ec7":{"symbol":"USDT","liquidity_provider":"0xcEe284F754E854890e311e3280b767F80797180d"},"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48":{"symbol":"USDC","liquidity_provider":"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"},"0x6B175474E89094C44Da98b954EedeAC495271d0F":{"symbol":"DAI","liquidity_provider":"0x517F9dD285e75b599234F7221227339478d0FcC8"},"0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84":{"symbol":"stETH","liquidity_provider":"0x4028DAAC072e492d34a3Afdbef0ba7e35D8b55C4"},"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0":{"symbol":"wstETH","liquidity_provider":"0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa"},"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599":{"symbol":"WBTC","liquidity_provider":"0xCBCdF9626bC03E24f779434178A73a0B4bad62eD"},"0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984":{"symbol":"UNI","liquidity_provider":"0x1d42064Fc4Beb5F8aAF85F4617AE8b3b5B8Bd801"}},"optimism":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0x4200000000000000000000000000000000000006"},"0x7F5c764cBc14f9669B88837ca1490cCa17c31607":{"symbol":"USDC.e","liquidity_provider":"0xD1F1baD4c9E6c44DeC1e9bF3B94902205c5Cd6C3"},"0x4200000000000000000000000000000000000006":{"symbol":"WETH","liquidity_provider":"0xBA12222222228d8Ba445958a75a0704d566BF2C8"},"0x94b008aA00579c1307B0EF2c499aD98a8ce58e58":{"symbol":"USDT","liquidity_provider":"0xA73C628eaf6e283E26A7b1f8001CF186aa4c0E8E"},"0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1":{"symbol":"DAI","liquidity_provider":"0x03aF20bDAaFfB4cC0A521796a223f7D85e2aAc31"},"0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb":{"symbol":"wstETH","liquidity_provider":"0x04F6C85A1B00F6D9B75f91FD23835974Cc07E65c"},"0x68f180fcCe6836688e9084f035309E29Bf0A2095":{"symbol":"WBTC","liquidity_provider":"0x078f358208685046a11C85e8ad32895DED33A249"},"0x76FB31fb4af56892A25e32cFC43De717950c9278":{"symbol":"AAVE","liquidity_provider":"0xf329e36C7bF6E5E86ce2150875a84Ce77f477375"},"0x4200000000000000000000000000000000000042":{"symbol":"OP","liquidity_provider":"0x2A82Ae142b2e62Cb7D10b55E323ACB1Cab663a26"}},"base":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0xd0b53D9277642d899DF5C87A3966A349A798F224"},"0x4200000000000000000000000000000000000006":{"symbol":"WETH","liquidity_provider":"0xBA12222222228d8Ba445958a75a0704d566BF2C8"},"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913":{"symbol":"USDC","liquidity_provider":"0x0B0A5886664376F59C351ba3f598C8A8B4D0A6f3"},"0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA":{"symbol":"USDbC","liquidity_provider":"0x0B25c51637c43decd6CC1C1e3da4518D54ddb528"},"0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb":{"symbol":"DAI","liquidity_provider":"0x927860797d07b1C46fbBe7f6f73D45C7E1BFBb27"},"0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452":{"symbol":"wstETH","liquidity_provider":"0x99CBC45ea5bb7eF3a5BC08FB1B7E56bB2442Ef0D"},"0xB6fe221Fe9EeF5aBa221c348bA20A1Bf5e73624c":{"symbol":"rETH","liquidity_provider":"0x95Fa1ddc9a78273f795e67AbE8f1Cd2Cd39831fF"},"0x532f27101965dd16442E59d40670FaF5eBB142E4":{"symbol":"BRETT","liquidity_provider":"0xBA3F945812a83471d709BCe9C3CA699A19FB46f7"}}}} merkl_user_rewards_url: ${str:https://api.merkl.xyz/v3/userRewards} - tenderly_bundle_simulation_url: ${str:https://api.tenderly.co/api/v1/account/{tenderly_account_slug}/project/{tenderly_project_slug}/simulate-bundle} + tenderly_bundle_simulation_url: ${str:https://api.tenderly.co/api/v1/account/{tenderly_account_slug}/project/{tenderly_project_slug}/simulate} tenderly_access_key: ${str:access_key} tenderly_account_slug: ${str:account_slug} tenderly_project_slug: ${str:project_slug} diff --git a/packages/valory/services/optimus/service.yaml b/packages/valory/services/optimus/service.yaml index f0a4b6a..2be0ff4 100644 --- a/packages/valory/services/optimus/service.yaml +++ b/packages/valory/services/optimus/service.yaml @@ -6,7 +6,7 @@ aea_version: '>=1.0.0, <2.0.0' license: Apache-2.0 fingerprint: {} fingerprint_ignore_patterns: [] -agent: valory/optimus:0.1.0:bafybeihoajamcrdv3yxsgrmfaeyhspjc6vt4jbwn3egmzcdszihtpjdno4 +agent: valory/optimus:0.1.0:bafybeiftg63fekqan2uzenh5ro2fnitfqyc77ehu74vxjmsq2lyx56xiby number_of_agents: 1 deployment: {} --- diff --git a/packages/valory/skills/liquidity_trader_abci/behaviours.py b/packages/valory/skills/liquidity_trader_abci/behaviours.py index 287913d..9edda52 100644 --- a/packages/valory/skills/liquidity_trader_abci/behaviours.py +++ b/packages/valory/skills/liquidity_trader_abci/behaviours.py @@ -110,7 +110,8 @@ # A safety margin in case there is a delay between the moment the KPI condition is # satisfied, and the moment where the checkpoint is called. REQUIRED_REQUESTS_SAFETY_MARGIN = 1 -MAX_RETRIES = 3 +MAX_RETRIES_FOR_API_CALL = 3 +MAX_RETRIES_FOR_ROUTES = 3 HTTP_OK = [200, 201] UTF8 = "utf-8" STAKING_CHAIN = "optimism" @@ -119,7 +120,7 @@ WAITING_PERIOD_FOR_BALANCE_TO_REFLECT = 5 WaitableConditionType = Generator[None, None, Any] HTTP_NOT_FOUND = 404 -RETRIES = 5 + class DexTypes(Enum): @@ -137,6 +138,12 @@ class Action(Enum): ENTER_POOL = "EnterPool" BRIDGE_SWAP = "BridgeAndSwap" FIND_BRIDGE_ROUTE = "FindBridgeRoute" + EXECUTE_STEP = "execute_step" + ROUTES_FETCHED = "routes_fetched" + FIND_ROUTE = "find_route" + BRIDGE_SWAP_EXECUTED = "bridge_swap_executed" + STEP_EXECUTED = "step_executed" + SWITCH_ROUTE = "switch_route" class SwapStatus(Enum): @@ -1327,7 +1334,7 @@ def _get_top_tokens_by_value(self) -> Generator[None, None, Optional[List[Any]]] tokens = [] for token_data in token_balances: - if token_data["value"] > self.params.min_swap_amount_threshold: + if token_data["value"] >= self.params.min_swap_amount_threshold: tokens.append(token_data) if len(tokens) == 2: self.context.logger.info( @@ -1407,7 +1414,7 @@ def _request_with_retries( body: Optional[Any] = None, headers: Optional[Dict] = None, rate_limited_code: int = 429, - max_retries: int = MAX_RETRIES, + max_retries: int = MAX_RETRIES_FOR_API_CALL, retry_wait: int = 0, ) -> Generator[None, None, Tuple[bool, Dict]]: """Request wrapped around a retry mechanism""" @@ -1777,6 +1784,7 @@ class DecisionMakingBehaviour(LiquidityTraderBaseBehaviour): def async_act(self) -> Generator: """Async act""" with self.context.benchmark_tool.measure(self.behaviour_id).local(): + self.context.logger.info(f"ROUTES: {self.synchronized_data.routes}") sender = self.context.agent_address ( next_event, @@ -1820,12 +1828,9 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict -1 ].round_id - # check tx status if last action was bridge and swap and the last round was not DecisionMaking or EvaluateStrategy + # check tx status if last action was executing a step if ( - last_round_id != DecisionMakingRound.auto_round_id() - and last_round_id != EvaluateStrategyRound.auto_round_id() - and Action(actions[last_executed_action_index].get("action")) - == Action.BRIDGE_SWAP + self.synchronized_data.last_action == Action.EXECUTE_STEP.value ): self.context.logger.info("Checking the status of swap tx") # we wait for some time before checking the status of the tx because the tx may take time to reflect on the lifi endpoint @@ -1849,7 +1854,7 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict self.context.logger.error("Swap failed") return Event.DONE.value, {}, {} - # if swap was successful we update the list of assets + # if swap/bridge was successful we update the list of assets if decision == Decision.CONTINUE: action = actions[last_executed_action_index] self._add_token_to_assets( @@ -1862,12 +1867,16 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict action.get("to_token"), action.get("to_token_symbol"), ) + fee_details = { + "remaining_fee_allowance": action.get("remaining_fee_allowance") + "remaining_gas_allowance": action.get("remaining_gas_allowance") + } + return Event.UPDATE.value, {"last_executed_step_index": self.synchronized_data.last_executed_step_index + 1, "fee_details": fee_details, "last_action": Action.STEP_EXECUTED.value}, {} # If last action was Enter Pool and it was successful we update the current pool if ( last_executed_action_index is not None - and Action(actions[last_executed_action_index].get("action")) - == Action.ENTER_POOL + and self.synchronized_data.last_action == Action.ENTER_POOL.value ): action = actions[last_executed_action_index] current_pool = { @@ -1893,8 +1902,7 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict # If last action was Exit Pool and it was successful we remove the current pool if ( last_executed_action_index is not None - and Action(actions[last_executed_action_index]["action"]) - == Action.EXIT_POOL + and self.synchronized_data.last_action == Action.EXIT_POOL.value ): current_pool = {} self.current_pool = current_pool @@ -1907,8 +1915,7 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict if ( last_executed_action_index is not None and last_round_id != DecisionMakingRound.auto_round_id() - and Action(actions[last_executed_action_index]["action"]) - == Action.CLAIM_REWARDS + and self.synchronized_data.last_action == Action.CLAIM_REWARDS.value ): action = actions[last_executed_action_index] chain = action.get("chain") @@ -1923,10 +1930,90 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict return ( Event.UPDATE.value, - {"last_reward_claimed_timestamp": current_timestamp}, + {"last_reward_claimed_timestamp": current_timestamp, "last_action": Action.CLAIM_REWARDS.value}, {}, ) + + if ( + last_executed_action_index is not None + and (self.synchronized_data.last_action == Action.ROUTES_FETCHED.value + or self.synchronized_data.last_action == Action.STEP_EXECUTED.value + or self.synchronized_data.last_action == Action.SWITCH_ROUTE.value + ) + ): + routes = self.synchronized_data.routes + if not routes: + self.context.logger.error("No routes found!") + return Event.DONE.value, {}, {} + + last_executed_route_index = ( + -1 if self.synchronized_data.last_executed_route_index is None + else self.synchronized_data.last_executed_route_index + ) + to_execute_route_index = last_executed_route_index + 1 + + last_executed_step_index = ( + -1 if self.synchronized_data.last_executed_step_index is None + else self.synchronized_data.last_executed_step_index + ) + to_execute_step_index = last_executed_step_index + 1 + if to_execute_route_index >= len(routes): + self.context.logger.error("No more routes left to execute") + return Event.DONE.value, {}, {} + if to_execute_step_index >= len(routes[to_execute_route_index]): + self.context.logger.info("All steps executed successfully!") + return Event.UPDATE.value, {"last_executed_route_index": None, "last_executed_step_index": None, "fee_details": {}, "routes": {}, "max_allowed_steps_in_a_route": None, "routes_retry_attempt": 0, "last_action": Action.BRIDGE_SWAP_EXECUTED.value}, {} + + # if step index is zero, it indicates that we are executing this route for the first time, so we check for profitability of route before executing steps + total_fee = self.synchronized_data.fee_details.get("total_fee") + total_gas_cost = self.synchronized_data.fee_details.get("total_gas_cost") + remaining_fee_allowance = self.synchronized_data.fee_details.get("remaining_fee_allowance") + remaining_gas_allowance = self.synchronized_data.fee_details.get("remaining_gas_allowance") + # Check profitability if it's the first step + if to_execute_step_index == 0: + is_profitable, total_fee, total_gas_cost = yield from self.check_if_route_is_profitable(routes[to_execute_route_index]) + if not is_profitable: + self.context.logger.error("Route not profitable. Switching to next route..") + return Event.UPDATE.value, {"last_executed_route_index": to_execute_route_index, "last_executed_step_index": None, "last_action": Action.SWITCH_ROUTE.value}, {} + + remaining_fee_allowance = total_fee + remaining_gas_allowance = total_gas_cost + + steps = route[to_execute_route_index].get("steps") + step = steps[to_execute_step_index] + + # Check step costs + step_profitable, step_data = yield from self.check_step_costs(step, remaining_fee_allowance, remaining_gas_allowance, to_execute_step_index, len(steps)) + if not step_profitable: + return Event.DONE.value, {}, {} + + bridge_swap_action = yield from self.prepare_bridge_swap_action(step_data, remaining_fee_allowance, remaining_gas_allowance) + if not bridge_swap_action: + if to_execute_step_index == 0: + self.context.logger.error("First step failed. Switching to next route..") + return Event.UPDATE.value, {"last_executed_route_index": to_execute_route_index, "last_executed_step_index": None, "last_action": Action.SWITCH_ROUTE.value}, {} + else: + self.context.logger.error("Intermediate step failed. Fetching new routes..") + remaining_steps = len(steps) - to_execute_step_index + if self.synchronized_data.routes_retry_attempt > MAX_RETRIES_FOR_ROUTES: + self.context.logger.error("Exceeded retry limit") + return Event.DONE.value, {}, {} + + routes_retry_attempt = self.synchronized_data.routes_retry_attempt + 1 + find_route_action = { + "action": Action.FIND_BRIDGE_ROUTE.value, + "from_chain": step_data["from_chain"], + "to_chain": step_data["to_chain"], + "from_token": step_data["source_token"], + "from_token_symbol": step_data["source_token_symbol"], + "to_token": step_data["target_token"], + "to_token_symbol": step_data["target_token_symbol"] + } + + return Event.UPDATE.value, {"last_executed_step_index": None, "last_executed_route_index": None, "new_action": find_route_action, "last_executed_action_index": current_action_index, "routes_retry_attempt": routes_retry_attempt, "last_action": Action.FIND_ROUTE.value}, {} + + return Event.EXECUTE_STEP.value, {"new_action": bridge_and_swap_action, "last_action": Event.EXECUTE_STEP.value}, {} # if all actions have been executed we exit DecisionMaking if current_action_index >= len(self.synchronized_data.actions): self.context.logger.info("All actions have been executed") @@ -1948,26 +2035,28 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict tx_hash, chain_id, safe_address = yield from self.get_enter_pool_tx_hash( positions, next_action_details ) + last_action = Action.ENTER_POOL.value elif next_action == Action.EXIT_POOL: tx_hash, chain_id, safe_address = yield from self.get_exit_pool_tx_hash( next_action_details ) + last_action = Action.EXIT_POOL.value elif next_action == Action.FIND_BRIDGE_ROUTE: - bridge_and_swap_actions = yield from self.get_transaction_data_for_route( - positions, next_action_details - ) - if not bridge_and_swap_actions or not bridge_and_swap_actions.get( - "actions" - ): + routes = yield from self.fetch_routes(positions, next_action_details) + if self.synchronized_data.max_allowed_steps_in_a_route: + routes = [ + route for route in routes + if len(route.get("steps", [])) <= self.synchronized_data.max_allowed_steps_in_a_route + ] + + serialized_routes = json.dumps(routes) + if not routes: + self.context.logger.error("Error fetching routes") return Event.DONE.value, {}, {} - - return ( - Event.UPDATE.value, - {"last_executed_action_index": current_action_index}, - bridge_and_swap_actions, - ) + + return Event.UPDATE.value, {"routes": serialized_routes, "last_executed_action_index": current_action_index, "last_action": Action.ROUTES_FETCHED.value}, {} elif next_action == Action.BRIDGE_SWAP: # wait for sometime to get the balances reflected @@ -1975,16 +2064,19 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict tx_hash = next_action_details.get("payload") chain_id = next_action_details.get("from_chain") safe_address = next_action_details.get("safe_address") + last_action = Action.EXECUTE_STEP.value elif next_action == Action.CLAIM_REWARDS: tx_hash, chain_id, safe_address = yield from self.get_claim_rewards_tx_hash( next_action_details ) + last_action = Action.CLAIM_REWARDS.value else: tx_hash = None chain_id = None safe_address = None + last_action = None if not tx_hash: self.context.logger.error("There was an error preparing the next action") @@ -2000,6 +2092,7 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict "positions": positions, # TO-DO: Decide on the correct method/logic for maintaining the period number for the last transaction. "last_executed_action_index": current_action_index, + "last_action": last_action }, {}, ) @@ -2043,7 +2136,7 @@ def get_swap_status( url = f"{self.params.lifi_check_status_url}?txHash={tx_hash}" self.context.logger.info(f"checking status from endpoint {url}") - for _attempt in range(RETRIES): + for _attempt in range(MAX_RETRIES_FOR_API_CALL): response = yield from self.get_http_response( method="GET", url=url, @@ -2080,7 +2173,7 @@ def get_swap_status( return status, sub_status - self.context.logger.error(f"Failed to fetch status after {RETRIES} retries.") + self.context.logger.error(f"Failed to fetch status after {MAX_RETRIES_FOR_API_CALL} retries.") return None, None def get_enter_pool_tx_hash( @@ -2338,104 +2431,114 @@ def get_exit_pool_tx_hash( return payload_string, chain, safe_address - def get_transaction_data_for_route( - self, positions, action - ) -> Generator[None, None, Dict]: - """Prepares the bridge swap actions""" - bridge_and_swap_actions = {} - routes = yield from self._fetch_routes(positions, action) - if not routes: - return {} + def prepare_bridge_swap_action(self, tx_info: Dict[str,Any], remaining_fee_allowance: float, remaining_gas_allowance: float) -> Generator[None, None, Optional[Dict]]: + """Prepares the bridge swap action""" + multisend_tx_hash = yield from self._build_multisend_tx(tx_info) + if not multisend_tx_hash: + return None - for route in routes: - all_steps_successful = True - bridge_and_swap_actions["actions"] = [] - step_transactions = yield from self._get_step_transactions_data(route) + multisend_tx_data = bytes.fromhex(multisend_tx_hash[2:]) + from_chain = tx_info.get("from_chain") + multisend_address = self.params.multisend_contract_addresses[ + tx_info.get("from_chain") + ] - total_gas_cost = 0 - total_fee = 0 - total_fee += sum( - float(tx_info.get("fee", 0)) for tx_info in step_transactions - ) - total_gas_cost += sum( - float(tx_info.get("gas_cost", 0)) for tx_info in step_transactions - ) - from_amount_usd = float(route.get("fromAmountUSD", 0)) - to_amount_usd = float(route.get("toAmountUSD", 0)) - is_profitable = self._is_route_profitable( - from_amount_usd, to_amount_usd, total_fee, total_gas_cost + is_ok = yield from self._simulate_transaction( + to_address=multisend_address, + data=multisend_tx_data, + token=tx_info.get("source_token"), + amount=tx_info.get("amount"), + chain=tx_info.get("from_chain"), + ) + if not is_ok: + self.context.logger.info( + f"Simulation failed for bridge/swap tx: {tx_info.get('source_token_symbol')}({tx_info.get('from_chain')}) --> {tx_info.get('target_token_symbol')}({tx_info.get('to_chain')}). Tool used: {tx_info.get('tool')}" ) - if not is_profitable: - self.context.logger.info("Switching to next route.") - continue + return None - for tx_info in step_transactions: - multisend_tx_hash = yield from self._build_multisend_tx(tx_info) - if not multisend_tx_hash: - all_steps_successful = False - break + self.context.logger.info( + f"Simulation successful for bridge/swap tx: {tx_info.get('source_token_symbol')}({tx_info.get('from_chain')}) --> {tx_info.get('target_token_symbol')}({tx_info.get('to_chain')}). Tool used: {tx_info.get('tool')}" + ) - multisend_tx_data = bytes.fromhex(multisend_tx_hash[2:]) - from_chain = tx_info.get("from_chain") - multisend_address = self.params.multisend_contract_addresses[ - tx_info.get("from_chain") - ] - liquidity_provider = ( - self.params.intermediate_tokens.get(tx_info.get("from_chain"), {}) - .get(tx_info.get("source_token"), {}) - .get("liquidity_provider") - ) - # if we do not have an address from where we can perform a mock transfer we do not perform the simulation and proceed to the next steps - if liquidity_provider: - is_ok = yield from self._simulate_execution_bundle( - to_address=multisend_address, - data=multisend_tx_data, - token=tx_info.get("source_token"), - mock_transfer_from=liquidity_provider, - amount=tx_info.get("amount"), - chain=tx_info.get("from_chain"), - ) - if not is_ok: - self.context.logger.info( - f"Simulation failed for bridge/swap tx: {tx_info.get('source_token_symbol')}({tx_info.get('from_chain')}) --> {tx_info.get('target_token_symbol')}({tx_info.get('to_chain')}). Tool used: {tx_info.get('tool')}" - ) - all_steps_successful = False - break - self.context.logger.info( - f"Simulation successful for bridge/swap tx: {tx_info.get('source_token_symbol')}({tx_info.get('from_chain')}) --> {tx_info.get('target_token_symbol')}({tx_info.get('to_chain')}). Tool used: {tx_info.get('tool')}" - ) + payload_string = yield from self._build_safe_tx( + from_chain, multisend_tx_hash, multisend_address + ) + if not payload_string: + all_steps_successful = False + break + + bridge_and_swap_action = { + "action": Action.BRIDGE_SWAP.value, + "from_chain": tx_info.get("from_chain"), + "to_chain": tx_info.get("to_chain"), + "from_token": tx_info.get("source_token"), + "from_token_symbol": tx_info.get("source_token_symbol"), + "to_token": tx_info.get("target_token"), + "to_token_symbol": tx_info.get("target_token_symbol"), + "payload": payload_string, + "safe_address": self.params.safe_contract_addresses.get( + from_chain + ), + "remaining_gas_allowance": remaining_gas_allowance - tx_info.get("gas_cost"), + "remaining_fee_allowance": remaining_fee_allowance - tx_info.get("fee") + } + return bridge_and_swap_action + + def check_if_route_is_profitable(self, route: Dict[str, Any]) -> Generator[None, None, Optional[bool, float, float]]: + """Checks if the entire route is profitable""" + step_transactions = yield from self._get_step_transactions_data(route) + total_gas_cost = 0 + total_fee = 0 + total_fee += sum( + float(tx_info.get("fee", 0)) for tx_info in step_transactions + ) + total_gas_cost += sum( + float(tx_info.get("gas_cost", 0)) for tx_info in step_transactions + ) + from_amount_usd = float(route.get("fromAmountUSD", 0)) + to_amount_usd = float(route.get("toAmountUSD", 0)) - payload_string = yield from self._build_safe_tx( - from_chain, multisend_tx_hash, multisend_address - ) - if not payload_string: - all_steps_successful = False - break + if not from_amount_usd or not to_amount_usd: + return False, None, None - bridge_and_swap_actions["actions"].append( - { - "action": Action.BRIDGE_SWAP.value, - "from_chain": tx_info.get("from_chain"), - "to_chain": tx_info.get("to_chain"), - "from_token": tx_info.get("source_token"), - "from_token_symbol": tx_info.get("source_token_symbol"), - "to_token": tx_info.get("target_token"), - "to_token_symbol": tx_info.get("target_token_symbol"), - "payload": payload_string, - "safe_address": self.params.safe_contract_addresses.get( - from_chain - ), - } - ) + allowed_fee_percentage = self.params.max_fee_percentage * 100 + allowed_gas_percentage = self.params.max_gas_percentage * 100 - if all_steps_successful: - self.context.logger.info( - f"BRIDGE SWAP ACTIONS: {bridge_and_swap_actions}" - ) - return bridge_and_swap_actions + fee_percentage = (total_fee / from_amount_usd) * 100 + gas_percentage = (total_gas_cost / from_amount_usd) * 100 - self.context.logger.error("NONE OF THE ROUTES WERE SUCCESSFUL!") - return {} + self.context.logger.info( + f"Fee is {fee_percentage:.2f}% of total amount, allowed is {allowed_fee_percentage:.2f}% and gas is {gas_percentage:.2f}% of total amount, allowed is {allowed_gas_percentage:.2f}%." + f"Details: total_fee={total_fee}, total_gas_cost={total_gas_cost}, from_amount_usd={from_amount_usd}, to_amount_usd={to_amount_usd}" + ) + + if fee_percentage > allowed_fee_percentage or gas_percentage > allowed_gas_percentage: + self.context.logger.error("Route is not profitable!") + return False, None, None + + self.context.logger.info("Route is profitable!") + return True, total_fee, total_gas_cost + + def check_step_costs(self, step, remaining_fee_allowance, remaining_gas_allowance, step_index, total_steps) -> Generator[None, None, Tuple[Optional[bool], Optional[Dict[str,Any]]]]: + """Check if the step costs are within the allowed range.""" + step = self._set_step_addresses(step) + step_data = yield from self._get_step_transaction(step) + + step_fee = step_data.get("fee", 0) + step_gas_cost = step_data.get("gas_cost", 0) + + if step_index == total_steps - 1: + # For the last step, ensure it is not more than 50% of the remaining fee and gas allowance + if step_fee > 0.5 * remaining_fee_allowance or step_gas_cost > 0.5 * remaining_gas_allowance: + self.context.logger.error("Last step exceeds 50%% of the remaining fee or gas allowance. Dropping step.") + return False, None + + else: + if step_fee > remaining_fee_allowance or step_gas_cost > remaining_gas_allowance: + self.context.logger.error("First step exceeds remaining fee or gas allowance. Dropping step.") + return False, None + + return True, step_data def _build_safe_tx( self, from_chain, multisend_tx_hash, multisend_address @@ -2521,31 +2624,7 @@ def _get_step_transactions_data( step_transactions = [] steps = route.get("steps", []) for step in steps: - from_chain_id = step.get("action", {}).get("fromChainId") - from_chain = next( - ( - k - for k, v in self.params.chain_to_chain_id_mapping.items() - if v == from_chain_id - ), - None, - ) - to_chain_id = step.get("action", {}).get("toChainId") - to_chain = next( - ( - k - for k, v in self.params.chain_to_chain_id_mapping.items() - if v == to_chain_id - ), - None, - ) - # lifi response had mixed up address, temporary solution to fix it - step["action"]["fromAddress"] = self.params.safe_contract_addresses.get( - from_chain - ) - step["action"]["toAddress"] = self.params.safe_contract_addresses.get( - to_chain - ) + step = self._set_step_addresses(step) tx_info = yield from self._get_step_transaction(step) step_transactions.append(tx_info) @@ -2620,6 +2699,8 @@ def _get_step_transaction( fee += sum(float(fee_cost.get("amountUSD", 0)) for fee_cost in fee_costs) gas_cost += sum(float(gas_cost.get("amountUSD", 0)) for gas_cost in gas_costs) + from_amount_usd = float(response.get("fromAmountUSD", 0)) + to_amount_usd = float(response.get("toAmountUSD", 0)) return { "source_token": source_token, "source_token_symbol": source_token_symbol, @@ -2634,11 +2715,43 @@ def _get_step_transaction( "tx_hash": tx_hash, "fee": fee, "gas_cost": gas_cost, + "from_amount_usd": from_amount_usd, + "to_amount_usd": to_amount_usd } - def _fetch_routes( + def _set_step_addresses(self, step: Dict[str, Any]) -> Dict[str, Any]: + """Set the fromAddress and toAddress in the step action. Lifi response had mixed up address, temporary solution to fix it""" + from_chain_id = step.get("action", {}).get("fromChainId") + from_chain = next( + ( + k + for k, v in self.params.chain_to_chain_id_mapping.items() + if v == from_chain_id + ), + None, + ) + to_chain_id = step.get("action", {}).get("toChainId") + to_chain = next( + ( + k + for k, v in self.params.chain_to_chain_id_mapping.items() + if v == to_chain_id + ), + None, + ) + # lifi response had mixed up address, temporary solution to fix it + step["action"]["fromAddress"] = self.params.safe_contract_addresses.get( + from_chain + ) + step["action"]["toAddress"] = self.params.safe_contract_addresses.get( + to_chain + ) + + return step + + def fetch_routes( self, positions, action - ) -> Generator[None, None, Optional[Dict[str, Any]]]: + ) -> Generator[None, None, Optional[List[Any]]]: """Get transaction data for route from LiFi API""" from_chain = action.get("from_chain") to_chain = action.get("to_chain") @@ -2673,7 +2786,7 @@ def _fetch_routes( if any(value is None for key, value in params.items()): self.context.logger.error(f"Missing value in params: {params}") - return {} + return None self.context.logger.info( f"Finding route: {from_token_symbol}({from_chain}) --> {to_token_symbol}({to_chain})" @@ -2711,41 +2824,11 @@ def _fetch_routes( return routes - def _is_route_profitable( - self, - from_amount_usd: float, - to_amount_usd: float, - total_fee: float, - total_gas_cost: float, - ) -> bool: - """Check if the route is profitable""" - allowed_fee_percentage = self.params.max_fee_percentage * 100 - allowed_gas_percentage = self.params.max_gas_percentage * 100 - - fee_percentage = (total_fee / from_amount_usd) * 100 - gas_percentage = (total_gas_cost / from_amount_usd) * 100 - - self.context.logger.info( - f"Fee is {fee_percentage:.2f}% of total amount, allowed is {allowed_fee_percentage:.2f}% and gas is {gas_percentage:.2f}% of total amount, allowed is {allowed_gas_percentage:.2f}%." - f"Details: total_fee={total_fee}, total_gas_cost={total_gas_cost}, from_amount_usd={from_amount_usd}, to_amount_usd={to_amount_usd}" - ) - - if ( - fee_percentage > allowed_fee_percentage - or gas_percentage > allowed_gas_percentage - ): - self.context.logger.error("Route is not profitable!") - return False - - self.context.logger.info("Route is profitable!") - return True - - def _simulate_execution_bundle( + def _simulate_transaction( self, to_address: str, data: bytes, token: str, - mock_transfer_from: str, amount: int, chain: str, **kwargs: Any, @@ -2779,27 +2862,16 @@ def _simulate_execution_bundle( transfer_data = self._encode_transfer_data(token, safe_address, amount) body = { - "simulations": [ - { - "network_id": self.params.chain_to_chain_id_mapping.get(chain), - "save": True, - "save_if_fails": True, - "simulation_type": "quick", - "from": mock_transfer_from, - "to": token, - "input": transfer_data, - }, - { "network_id": self.params.chain_to_chain_id_mapping.get(chain), + "block_number": "latest", "save": True, "save_if_fails": True, + "gas": 0, "simulation_type": "quick", "from": self.context.agent_address, "to": safe_address, "input": tx_data, - }, - ] - } + } response = yield from self.get_http_response( "POST", @@ -2820,13 +2892,10 @@ def _simulate_execution_bundle( try: data = json.loads(response.body) if data: - simulation_results = data.get("simulation_results", []) + simulation = data.get("simulation", {}) status = False - if simulation_results: - for simulation_result in simulation_results: - simulation = simulation_result.get("simulation", {}) - if isinstance(simulation, Dict): - status = simulation.get("status", False) + if isinstance(simulation, Dict): + status = simulation.get("status", False) return status except (ValueError, TypeError) as e: diff --git a/packages/valory/skills/liquidity_trader_abci/rounds.py b/packages/valory/skills/liquidity_trader_abci/rounds.py index cc35b9f..cb1587a 100644 --- a/packages/valory/skills/liquidity_trader_abci/rounds.py +++ b/packages/valory/skills/liquidity_trader_abci/rounds.py @@ -108,8 +108,6 @@ def participant_to_positions_round(self) -> DeserializedCollection: def positions(self) -> List[Dict[str, Any]]: """Get the positions.""" serialized = self.db.get("positions", "[]") - if serialized is None: - serialized = "[]" positions = json.loads(serialized) return positions @@ -122,8 +120,6 @@ def participant_to_actions_round(self) -> DeserializedCollection: def actions(self) -> Optional[List[Dict[str, Any]]]: """Get the actions""" serialized = self.db.get("actions", "[]") - if serialized is None: - serialized = "[]" actions = json.loads(serialized) return actions @@ -176,8 +172,44 @@ def chain_id(self) -> Optional[str]: def period_number_at_last_cp(self) -> Optional[int]: """Get the period number at last cp.""" return cast(int, self.db.get("period_number_at_last_cp", 0)) + + @property + def last_executed_route_index(self) -> Optional[int]: + """Get the last executed route index.""" + return cast(int, self.db.get("last_executed_route_index", None)) + + @property + def last_executed_step_index(self) -> Optional[int]: + """Get the last executed step index.""" + return cast(int, self.db.get("last_executed_step_index", None)) + + @property + def routes_retry_attempt(self) -> Optional[int]: + """Get the routes retry attempt index.""" + return cast(int, self.db.get("routes_retry_attempt", 0)) + + @property + def routes(self) -> Optional[List[Dict[str, Any]]]: + """Get the routes""" + serialized = self.db.get("routes", "[]") + routes = json.loads(serialized) + return routes + @property + def fee_details(self) -> Optional[Dict[str, Any]]: + """Get fee details related to route""" + return self.db.get("fee_details", {}) + + @property + def max_allowed_steps_in_a_route(self) -> Optional[int]: + """Get the max allowed steps in a route index.""" + return cast(int, self.db.get("max_allowed_steps_in_a_route", None)) + @property + def last_action(self) -> Optional[str]: + """Get the last action.""" + return cast(str, self.db.get("last_action", None)) + class CallCheckpointRound(CollectSameUntilThresholdRound): """A round for the checkpoint call preparation.""" @@ -286,7 +318,6 @@ class EvaluateStrategyRound(CollectSameUntilThresholdRound): payload_class = EvaluateStrategyPayload synchronized_data_class = SynchronizedData - selection_key = get_name(SynchronizedData.actions) done_event = Event.DONE collection_key = get_name(SynchronizedData.participant_to_actions_round) selection_key = get_name(SynchronizedData.actions) @@ -332,14 +363,14 @@ def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Event]]: if positions and not isinstance(positions, str): payload["updates"]["positions"] = json.dumps(positions, sort_keys=True) - bridge_and_swap_actions = payload.get("bridge_and_swap_actions", {}) + new_action = payload.get("updates", {}).get("new_action", {}) updated_actions = self.synchronized_data.actions - if bridge_and_swap_actions and bridge_and_swap_actions.get("actions"): - if not self.synchronized_data.last_executed_action_index: - index = 1 + if new_action: + if self.synchronized_data.last_executed_action_index is None: + index = 0 else: - index = self.synchronized_data.last_executed_action_index + 2 - updated_actions[index:index] = bridge_and_swap_actions.get("actions") + index = self.synchronized_data.last_executed_action_index + 1 + updated_actions.insert(new_action) serialized_actions = json.dumps(updated_actions) synchronized_data = synchronized_data.update( diff --git a/packages/valory/skills/liquidity_trader_abci/skill.yaml b/packages/valory/skills/liquidity_trader_abci/skill.yaml index 2f462be..dffc9a1 100644 --- a/packages/valory/skills/liquidity_trader_abci/skill.yaml +++ b/packages/valory/skills/liquidity_trader_abci/skill.yaml @@ -7,7 +7,7 @@ license: Apache-2.0 aea_version: '>=1.0.0, <2.0.0' fingerprint: __init__.py: bafybeia7bn2ahqqwkf63ptje6rfnftuwrsp33sswgpcbh5osbesxxr6g4m - behaviours.py: bafybeifrmkalngpld57inxxtyvdpqllfxjfornafbtovxamkmpadh6zpk4 + behaviours.py: bafybeihttk6wduv3nrfplpzilvwrjknba457nacof72kasaiscwnyoqx3e dialogues.py: bafybeiay23otskx2go5xhtgdwfw2kd6rxd62sxxdu3njv7hageorl5zxzm fsm_specification.yaml: bafybeiabbiulb7k6xkjysulmy6o4ugnhxlpp5jiaeextvwj65q4ttadoeq handlers.py: bafybeidxw2lvgiifmo4siobpwuwbxscuifrdo3gnkjyn6bgexotj5f7zf4 @@ -16,7 +16,7 @@ fingerprint: pool_behaviour.py: bafybeiaheuesscgqzwjbpyrezgwpdbdfurlmfwbc462qv6rblwwxlx5dpm pools/balancer.py: bafybeigznhgv7ylo5dvlhxcqikhiuqlqtnx3ikv4tszyvkl2lpcuqgoa5u pools/uniswap.py: bafybeigmqptgmjaxscszohfusgxsexqyx4awuyw7p4g5l7k2qpeyq7vdcu - rounds.py: bafybeidorqqosfidfxqwwegjjifzuqapl65ndpdnakolhlwawf57moopli + rounds.py: bafybeiafgvijxvardoabgj2turhg7eebvfemkszlkurn6iaff56w5jdwby strategies/simple_strategy.py: bafybeiasu2nchowx6leksjllpuum4ckezxoj4o2m4sstavblplvvutmvzm strategy_behaviour.py: bafybeidk6sorg47kuuubamcccksi65x3txldyo7y2hm5opbye2ghmz2ljy fingerprint_ignore_patterns: [] @@ -170,7 +170,7 @@ models: intermediate_tokens: '{"ethereum":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2":{"symbol":"WETH","liquidity_provider":"0xF04a5cC80B1E94C69B48f5ee68a08CD2F09A7c3E"},"0xdAC17F958D2ee523a2206206994597C13D831ec7":{"symbol":"USDT","liquidity_provider":"0xcEe284F754E854890e311e3280b767F80797180d"},"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48":{"symbol":"USDC","liquidity_provider":"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"},"0x6B175474E89094C44Da98b954EedeAC495271d0F":{"symbol":"DAI","liquidity_provider":"0x517F9dD285e75b599234F7221227339478d0FcC8"},"0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84":{"symbol":"stETH","liquidity_provider":"0x4028DAAC072e492d34a3Afdbef0ba7e35D8b55C4"},"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0":{"symbol":"wstETH","liquidity_provider":"0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa"},"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599":{"symbol":"WBTC","liquidity_provider":"0xCBCdF9626bC03E24f779434178A73a0B4bad62eD"},"0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984":{"symbol":"UNI","liquidity_provider":"0x1d42064Fc4Beb5F8aAF85F4617AE8b3b5B8Bd801"}},"optimism":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0x4200000000000000000000000000000000000006"},"0x7F5c764cBc14f9669B88837ca1490cCa17c31607":{"symbol":"USDC.e","liquidity_provider":"0xD1F1baD4c9E6c44DeC1e9bF3B94902205c5Cd6C3"},"0x4200000000000000000000000000000000000006":{"symbol":"WETH","liquidity_provider":"0xBA12222222228d8Ba445958a75a0704d566BF2C8"},"0x94b008aA00579c1307B0EF2c499aD98a8ce58e58":{"symbol":"USDT","liquidity_provider":"0xA73C628eaf6e283E26A7b1f8001CF186aa4c0E8E"},"0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1":{"symbol":"DAI","liquidity_provider":"0x03aF20bDAaFfB4cC0A521796a223f7D85e2aAc31"},"0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb":{"symbol":"wstETH","liquidity_provider":"0x04F6C85A1B00F6D9B75f91FD23835974Cc07E65c"},"0x68f180fcCe6836688e9084f035309E29Bf0A2095":{"symbol":"WBTC","liquidity_provider":"0x078f358208685046a11C85e8ad32895DED33A249"},"0x76FB31fb4af56892A25e32cFC43De717950c9278":{"symbol":"AAVE","liquidity_provider":"0xf329e36C7bF6E5E86ce2150875a84Ce77f477375"},"0x4200000000000000000000000000000000000042":{"symbol":"OP","liquidity_provider":"0x2A82Ae142b2e62Cb7D10b55E323ACB1Cab663a26"}},"base":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0xd0b53D9277642d899DF5C87A3966A349A798F224"},"0x4200000000000000000000000000000000000006":{"symbol":"WETH","liquidity_provider":"0xBA12222222228d8Ba445958a75a0704d566BF2C8"},"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913":{"symbol":"USDC","liquidity_provider":"0x0B0A5886664376F59C351ba3f598C8A8B4D0A6f3"},"0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA":{"symbol":"USDbC","liquidity_provider":"0x0B25c51637c43decd6CC1C1e3da4518D54ddb528"},"0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb":{"symbol":"DAI","liquidity_provider":"0x927860797d07b1C46fbBe7f6f73D45C7E1BFBb27"},"0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452":{"symbol":"wstETH","liquidity_provider":"0x99CBC45ea5bb7eF3a5BC08FB1B7E56bB2442Ef0D"},"0xB6fe221Fe9EeF5aBa221c348bA20A1Bf5e73624c":{"symbol":"rETH","liquidity_provider":"0x95Fa1ddc9a78273f795e67AbE8f1Cd2Cd39831fF"},"0x532f27101965dd16442E59d40670FaF5eBB142E4":{"symbol":"BRETT","liquidity_provider":"0xBA3F945812a83471d709BCe9C3CA699A19FB46f7"}}}' lifi_fetch_tools_url: https://li.quest/v1/tools merkl_user_rewards_url: https://api.merkl.xyz/v3/userRewards - tenderly_bundle_simulation_url: https://api.tenderly.co/api/v1/account/{tenderly_account_slug}/project/{tenderly_project_slug}/simulate-bundle + tenderly_bundle_simulation_url: https://api.tenderly.co/api/v1/account/{tenderly_account_slug}/project/{tenderly_project_slug}/simulate tenderly_access_key: access_key tenderly_account_slug: account_slug tenderly_project_slug: project_slug diff --git a/packages/valory/skills/optimus_abci/skill.yaml b/packages/valory/skills/optimus_abci/skill.yaml index 270cbb8..298b249 100644 --- a/packages/valory/skills/optimus_abci/skill.yaml +++ b/packages/valory/skills/optimus_abci/skill.yaml @@ -22,7 +22,7 @@ skills: - valory/registration_abci:0.1.0:bafybeiffipsowrqrkhjoexem7ern5ob4fabgif7wa6gtlszcoaop2e3oey - valory/reset_pause_abci:0.1.0:bafybeif4lgvbzsmzljesxbphycdv52ka7qnihyjrjpfaseclxadcmm6yiq - valory/termination_abci:0.1.0:bafybeiekkpo5qef5zaeagm3si6v45qxcojvtjqe4a5ceccvk4q7k3xi3bi -- valory/liquidity_trader_abci:0.1.0:bafybeich6pbchgdy7v56dwfdq6btb2blcagfquhytdhaemdfgmtgk4hbv4 +- valory/liquidity_trader_abci:0.1.0:bafybeidxpwveipbrdhzlawtzvkhtuwqp42ys2vw25tu5iqiu6tndlz7aom - valory/transaction_settlement_abci:0.1.0:bafybeielv6eivt2z6nforq43xewl2vmpfwpdu2s2vfogobziljnwsclmlm behaviours: main: @@ -160,7 +160,7 @@ models: intermediate_tokens: '{"ethereum":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2":{"symbol":"WETH","liquidity_provider":"0xF04a5cC80B1E94C69B48f5ee68a08CD2F09A7c3E"},"0xdAC17F958D2ee523a2206206994597C13D831ec7":{"symbol":"USDT","liquidity_provider":"0xcEe284F754E854890e311e3280b767F80797180d"},"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48":{"symbol":"USDC","liquidity_provider":"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"},"0x6B175474E89094C44Da98b954EedeAC495271d0F":{"symbol":"DAI","liquidity_provider":"0x517F9dD285e75b599234F7221227339478d0FcC8"},"0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84":{"symbol":"stETH","liquidity_provider":"0x4028DAAC072e492d34a3Afdbef0ba7e35D8b55C4"},"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0":{"symbol":"wstETH","liquidity_provider":"0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa"},"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599":{"symbol":"WBTC","liquidity_provider":"0xCBCdF9626bC03E24f779434178A73a0B4bad62eD"},"0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984":{"symbol":"UNI","liquidity_provider":"0x1d42064Fc4Beb5F8aAF85F4617AE8b3b5B8Bd801"}},"optimism":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0x4200000000000000000000000000000000000006"},"0x7F5c764cBc14f9669B88837ca1490cCa17c31607":{"symbol":"USDC.e","liquidity_provider":"0xD1F1baD4c9E6c44DeC1e9bF3B94902205c5Cd6C3"},"0x4200000000000000000000000000000000000006":{"symbol":"WETH","liquidity_provider":"0xBA12222222228d8Ba445958a75a0704d566BF2C8"},"0x94b008aA00579c1307B0EF2c499aD98a8ce58e58":{"symbol":"USDT","liquidity_provider":"0xA73C628eaf6e283E26A7b1f8001CF186aa4c0E8E"},"0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1":{"symbol":"DAI","liquidity_provider":"0x03aF20bDAaFfB4cC0A521796a223f7D85e2aAc31"},"0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb":{"symbol":"wstETH","liquidity_provider":"0x04F6C85A1B00F6D9B75f91FD23835974Cc07E65c"},"0x68f180fcCe6836688e9084f035309E29Bf0A2095":{"symbol":"WBTC","liquidity_provider":"0x078f358208685046a11C85e8ad32895DED33A249"},"0x76FB31fb4af56892A25e32cFC43De717950c9278":{"symbol":"AAVE","liquidity_provider":"0xf329e36C7bF6E5E86ce2150875a84Ce77f477375"},"0x4200000000000000000000000000000000000042":{"symbol":"OP","liquidity_provider":"0x2A82Ae142b2e62Cb7D10b55E323ACB1Cab663a26"}},"base":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0xd0b53D9277642d899DF5C87A3966A349A798F224"},"0x4200000000000000000000000000000000000006":{"symbol":"WETH","liquidity_provider":"0xBA12222222228d8Ba445958a75a0704d566BF2C8"},"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913":{"symbol":"USDC","liquidity_provider":"0x0B0A5886664376F59C351ba3f598C8A8B4D0A6f3"},"0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA":{"symbol":"USDbC","liquidity_provider":"0x0B25c51637c43decd6CC1C1e3da4518D54ddb528"},"0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb":{"symbol":"DAI","liquidity_provider":"0x927860797d07b1C46fbBe7f6f73D45C7E1BFBb27"},"0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452":{"symbol":"wstETH","liquidity_provider":"0x99CBC45ea5bb7eF3a5BC08FB1B7E56bB2442Ef0D"},"0xB6fe221Fe9EeF5aBa221c348bA20A1Bf5e73624c":{"symbol":"rETH","liquidity_provider":"0x95Fa1ddc9a78273f795e67AbE8f1Cd2Cd39831fF"},"0x532f27101965dd16442E59d40670FaF5eBB142E4":{"symbol":"BRETT","liquidity_provider":"0xBA3F945812a83471d709BCe9C3CA699A19FB46f7"}}}' lifi_fetch_tools_url: https://li.quest/v1/tools merkl_user_rewards_url: https://api.merkl.xyz/v3/userRewards - tenderly_bundle_simulation_url: https://api.tenderly.co/api/v1/account/{tenderly_account_slug}/project/{tenderly_project_slug}/simulate-bundle + tenderly_bundle_simulation_url: https://api.tenderly.co/api/v1/account/{tenderly_account_slug}/project/{tenderly_project_slug}/simulate tenderly_access_key: access_key tenderly_account_slug: account_slug tenderly_project_slug: project_slug From a23dba37c9529fbd9973188f9098624ba1850405 Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Thu, 3 Oct 2024 01:21:13 +0530 Subject: [PATCH 2/7] fix: refactor code --- .../liquidity_trader_abci/behaviours.py | 456 ++++++++++-------- 1 file changed, 244 insertions(+), 212 deletions(-) diff --git a/packages/valory/skills/liquidity_trader_abci/behaviours.py b/packages/valory/skills/liquidity_trader_abci/behaviours.py index 9edda52..492b2e1 100644 --- a/packages/valory/skills/liquidity_trader_abci/behaviours.py +++ b/packages/valory/skills/liquidity_trader_abci/behaviours.py @@ -1798,7 +1798,6 @@ def async_act(self) -> Generator: { "event": next_event, "updates": updates, - "bridge_and_swap_actions": bridge_and_swap_actions, }, sort_keys=True, ), @@ -1810,11 +1809,9 @@ def async_act(self) -> Generator: self.set_done() - def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict]]]: + def get_next_event(self) -> Generator[None, None, Tuple[str, Dict]]: """Get next event""" - actions = self.synchronized_data.actions - # If there are no actions, we return if not actions: self.context.logger.info("No actions to prepare") return Event.DONE.value, {}, {} @@ -1824,213 +1821,252 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict 0 if last_executed_action_index is None else last_executed_action_index + 1 ) - last_round_id = self.context.state.round_sequence._abci_app._previous_rounds[ - -1 - ].round_id + last_round_id = self.context.state.round_sequence._abci_app._previous_rounds[-1].round_id - # check tx status if last action was executing a step - if ( - self.synchronized_data.last_action == Action.EXECUTE_STEP.value - ): - self.context.logger.info("Checking the status of swap tx") - # we wait for some time before checking the status of the tx because the tx may take time to reflect on the lifi endpoint + if self.synchronized_data.last_action == Action.EXECUTE_STEP.value: + return yield from self._post_execute_step(actions, last_executed_action_index) + + if last_executed_action_index is not None: + if self.synchronized_data.last_action == Action.ENTER_POOL.value: + self._post_execute_enter_pool(actions, last_executed_action_index) + if self.synchronized_data.last_action == Action.EXIT_POOL.value: + self._post_execute_exit_pool() + if self.synchronized_data.last_action == Action.CLAIM_REWARDS.value and last_round_id != DecisionMakingRound.auto_round_id(): + return self._post_execute_claim_rewards(actions, last_executed_action_index) + + if last_executed_action_index is not None and self.synchronized_data.last_action in [ + Action.ROUTES_FETCHED.value, Action.STEP_EXECUTED.value, Action.SWITCH_ROUTE.value + ]: + return yield from self._post_execute_route_execution() + + if current_action_index >= len(self.synchronized_data.actions): + self.context.logger.info("All actions have been executed") + return Event.DONE.value, {}, {} + + return yield from self._prepare_next_action(actions, current_action_index, last_round_id) + + def _post_execute_step(self, actions, last_executed_action_index) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: + """Handle the execution of a step.""" + self.context.logger.info("Checking the status of swap tx") + yield from self.sleep(self.params.waiting_period_for_status_check) + decision = yield from self.get_decision_on_swap() + self.context.logger.info(f"Action to take {decision}") + + if decision == Decision.WAIT: + decision = yield from self._wait_for_swap_confirmation() + + if decision == Decision.EXIT: + self.context.logger.error("Swap failed") + return Event.DONE.value, {} + + if decision == Decision.CONTINUE: + return self._update_assets_after_swap(actions, last_executed_action_index) + + def _wait_for_swap_confirmation(self) -> Generator[None, None, Optional[Decision]]: + """Wait for swap confirmation.""" + self.context.logger.info("Waiting for tx to get executed") + while True: yield from self.sleep(self.params.waiting_period_for_status_check) decision = yield from self.get_decision_on_swap() self.context.logger.info(f"Action to take {decision}") + if decision != Decision.WAIT: + break + return decision + + def _update_assets_after_swap(self, actions, last_executed_action_index) -> Tuple[Optional[str], Optional[Dict]]: + """Update assets after a successful swap.""" + action = actions[last_executed_action_index] + self._add_token_to_assets( + action.get("from_chain"), + action.get("from_token"), + action.get("from_token_symbol"), + ) + self._add_token_to_assets( + action.get("to_chain"), + action.get("to_token"), + action.get("to_token_symbol"), + ) + fee_details = { + "remaining_fee_allowance": action.get("remaining_fee_allowance"), + "remaining_gas_allowance": action.get("remaining_gas_allowance"), + } + return Event.UPDATE.value, { + "last_executed_step_index": self.synchronized_data.last_executed_step_index + 1, + "fee_details": fee_details, + "last_action": Action.STEP_EXECUTED.value, + } - # If tx is pending then we wait until it gets confirmed or refunded - if decision == Decision.WAIT: - self.context.logger.info("Waiting for tx to get executed") - while decision == Decision.WAIT: - # Wait for given time between each status check - yield from self.sleep(self.params.waiting_period_for_status_check) - self.context.logger.info("Checking the status of swap tx again") - decision = ( - yield from self.get_decision_on_swap() - ) # Check the status again - self.context.logger.info(f"Action to take {decision}") - - if decision == Decision.EXIT: - self.context.logger.error("Swap failed") - return Event.DONE.value, {}, {} - - # if swap/bridge was successful we update the list of assets - if decision == Decision.CONTINUE: - action = actions[last_executed_action_index] - self._add_token_to_assets( - action.get("from_chain"), - action.get("from_token"), - action.get("from_token_symbol"), - ) - self._add_token_to_assets( - action.get("to_chain"), - action.get("to_token"), - action.get("to_token_symbol"), - ) - fee_details = { - "remaining_fee_allowance": action.get("remaining_fee_allowance") - "remaining_gas_allowance": action.get("remaining_gas_allowance") - } - return Event.UPDATE.value, {"last_executed_step_index": self.synchronized_data.last_executed_step_index + 1, "fee_details": fee_details, "last_action": Action.STEP_EXECUTED.value}, {} + def _post_execute_enter_pool(self, actions, last_executed_action_index): + """Handle entering a pool.""" + action = actions[last_executed_action_index] + current_pool = { + "chain": action["chain"], + "address": action["pool_address"], + "dex_type": action["dex_type"], + "assets": action["assets"], + "apr": action["apr"], + "pool_type": action["pool_type"], + } + if action.get("dex_type") == DexTypes.UNISWAP_V3.value: + token_id, liquidity = yield from self._get_data_from_mint_tx_receipt( + self.synchronized_data.final_tx_hash, action.get("chain") + ) + current_pool["token_id"] = token_id + current_pool["liquidity"] = liquidity + self.current_pool = current_pool + self.store_current_pool() + self.context.logger.info( + f"Enter pool was successful! Updating current pool to {current_pool}" + ) - # If last action was Enter Pool and it was successful we update the current pool - if ( - last_executed_action_index is not None - and self.synchronized_data.last_action == Action.ENTER_POOL.value + def _post_execute_exit_pool(self): + """Handle exiting a pool.""" + self.current_pool = {} + self.store_current_pool() + self.context.logger.info("Exit was successful! Removing current pool") + yield from self.sleep(WAITING_PERIOD_FOR_BALANCE_TO_REFLECT) + + + def _post_execute_claim_rewards(self, actions, last_executed_action_index) -> Tuple[Optional[str], Optional[Dict]]: + """Handle claiming rewards.""" + action = actions[last_executed_action_index] + chain = action.get("chain") + for token, token_symbol in zip( + action.get("tokens"), action.get("token_symbols") ): - action = actions[last_executed_action_index] - current_pool = { - "chain": action["chain"], - "address": action["pool_address"], - "dex_type": action["dex_type"], - "assets": action["assets"], - "apr": action["apr"], - "pool_type": action["pool_type"], + self._add_token_to_assets(chain, token, token_symbol) + + current_timestamp = cast( + SharedState, self.context.state + ).round_sequence.last_round_transition_timestamp.timestamp() + + return Event.UPDATE.value, { + "last_reward_claimed_timestamp": current_timestamp, + "last_action": Action.CLAIM_REWARDS.value, + } + + def _post_execute_route_execution(self) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: + """Handle route execution.""" + routes = self.synchronized_data.routes + if not routes: + self.context.logger.error("No routes found!") + return Event.DONE.value, {} + + last_executed_route_index = ( + -1 if self.synchronized_data.last_executed_route_index is None + else self.synchronized_data.last_executed_route_index + ) + to_execute_route_index = last_executed_route_index + 1 + + last_executed_step_index = ( + -1 if self.synchronized_data.last_executed_step_index is None + else self.synchronized_data.last_executed_step_index + ) + to_execute_step_index = last_executed_step_index + 1 + + if to_execute_route_index >= len(routes): + self.context.logger.error("No more routes left to execute") + return Event.DONE.value, {} + if to_execute_step_index >= len(routes[to_execute_route_index]): + self.context.logger.info("All steps executed successfully!") + return Event.UPDATE.value, { + "last_executed_route_index": None, + "last_executed_step_index": None, + "fee_details": {}, + "routes": {}, + "max_allowed_steps_in_a_route": None, + "routes_retry_attempt": 0, + "last_action": Action.BRIDGE_SWAP_EXECUTED.value, } - if action.get("dex_type") == DexTypes.UNISWAP_V3.value: - token_id, liquidity = yield from self._get_data_from_mint_tx_receipt( - self.synchronized_data.final_tx_hash, action.get("chain") - ) - current_pool["token_id"] = token_id - current_pool["liquidity"] = liquidity - self.current_pool = current_pool - self.store_current_pool() - self.context.logger.info( - f"Enter pool was successful! Updating current pool to {current_pool}" - ) - # If last action was Exit Pool and it was successful we remove the current pool - if ( - last_executed_action_index is not None - and self.synchronized_data.last_action == Action.EXIT_POOL.value - ): - current_pool = {} - self.current_pool = current_pool - self.store_current_pool() - self.context.logger.info("Exit was successful! Removing current pool") - # when we exit the pool, it may take time to reflect the balance of our assets - yield from self.sleep(WAITING_PERIOD_FOR_BALANCE_TO_REFLECT) - - # If last action was Claim Rewards and it was successful we update the list of assets and the last_reward_claimed_timestamp - if ( - last_executed_action_index is not None - and last_round_id != DecisionMakingRound.auto_round_id() - and self.synchronized_data.last_action == Action.CLAIM_REWARDS.value - ): - action = actions[last_executed_action_index] - chain = action.get("chain") - for token, token_symbol in zip( - action.get("tokens"), action.get("token_symbols") - ): - self._add_token_to_assets(chain, token, token_symbol) + return yield from self._execute_route_step(routes, to_execute_route_index, to_execute_step_index) + + def _execute_route_step(self, routes, to_execute_route_index, to_execute_step_index) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: + """Execute a step in the route.""" + steps = routes[to_execute_route_index].get("steps") + step = steps[to_execute_step_index] + + total_fee = self.synchronized_data.fee_details.get("total_fee") + total_gas_cost = self.synchronized_data.fee_details.get("total_gas_cost") + remaining_fee_allowance = self.synchronized_data.fee_details.get("remaining_fee_allowance") + remaining_gas_allowance = self.synchronized_data.fee_details.get("remaining_gas_allowance") + + if to_execute_step_index == 0: + is_profitable, total_fee, total_gas_cost = yield from self.check_if_route_is_profitable(routes[to_execute_route_index]) + if not is_profitable: + self.context.logger.error("Route not profitable. Switching to next route..") + return Event.UPDATE.value, { + "last_executed_route_index": to_execute_route_index, + "last_executed_step_index": None, + "last_action": Action.SWITCH_ROUTE.value, + } - current_timestamp = cast( - SharedState, self.context.state - ).round_sequence.last_round_transition_timestamp.timestamp() + remaining_fee_allowance = total_fee + remaining_gas_allowance = total_gas_cost - return ( - Event.UPDATE.value, - {"last_reward_claimed_timestamp": current_timestamp, "last_action": Action.CLAIM_REWARDS.value}, - {}, - ) - - if ( - last_executed_action_index is not None - and (self.synchronized_data.last_action == Action.ROUTES_FETCHED.value - or self.synchronized_data.last_action == Action.STEP_EXECUTED.value - or self.synchronized_data.last_action == Action.SWITCH_ROUTE.value - ) - ): - routes = self.synchronized_data.routes - if not routes: - self.context.logger.error("No routes found!") - return Event.DONE.value, {}, {} - - last_executed_route_index = ( - -1 if self.synchronized_data.last_executed_route_index is None - else self.synchronized_data.last_executed_route_index - ) - to_execute_route_index = last_executed_route_index + 1 - - last_executed_step_index = ( - -1 if self.synchronized_data.last_executed_step_index is None - else self.synchronized_data.last_executed_step_index - ) - to_execute_step_index = last_executed_step_index + 1 - - if to_execute_route_index >= len(routes): - self.context.logger.error("No more routes left to execute") - return Event.DONE.value, {}, {} - if to_execute_step_index >= len(routes[to_execute_route_index]): - self.context.logger.info("All steps executed successfully!") - return Event.UPDATE.value, {"last_executed_route_index": None, "last_executed_step_index": None, "fee_details": {}, "routes": {}, "max_allowed_steps_in_a_route": None, "routes_retry_attempt": 0, "last_action": Action.BRIDGE_SWAP_EXECUTED.value}, {} - - # if step index is zero, it indicates that we are executing this route for the first time, so we check for profitability of route before executing steps - total_fee = self.synchronized_data.fee_details.get("total_fee") - total_gas_cost = self.synchronized_data.fee_details.get("total_gas_cost") - remaining_fee_allowance = self.synchronized_data.fee_details.get("remaining_fee_allowance") - remaining_gas_allowance = self.synchronized_data.fee_details.get("remaining_gas_allowance") - # Check profitability if it's the first step - if to_execute_step_index == 0: - is_profitable, total_fee, total_gas_cost = yield from self.check_if_route_is_profitable(routes[to_execute_route_index]) - if not is_profitable: - self.context.logger.error("Route not profitable. Switching to next route..") - return Event.UPDATE.value, {"last_executed_route_index": to_execute_route_index, "last_executed_step_index": None, "last_action": Action.SWITCH_ROUTE.value}, {} - - remaining_fee_allowance = total_fee - remaining_gas_allowance = total_gas_cost - - steps = route[to_execute_route_index].get("steps") - step = steps[to_execute_step_index] - - # Check step costs - step_profitable, step_data = yield from self.check_step_costs(step, remaining_fee_allowance, remaining_gas_allowance, to_execute_step_index, len(steps)) - if not step_profitable: - return Event.DONE.value, {}, {} - - bridge_swap_action = yield from self.prepare_bridge_swap_action(step_data, remaining_fee_allowance, remaining_gas_allowance) - if not bridge_swap_action: - if to_execute_step_index == 0: - self.context.logger.error("First step failed. Switching to next route..") - return Event.UPDATE.value, {"last_executed_route_index": to_execute_route_index, "last_executed_step_index": None, "last_action": Action.SWITCH_ROUTE.value}, {} - else: - self.context.logger.error("Intermediate step failed. Fetching new routes..") - remaining_steps = len(steps) - to_execute_step_index - if self.synchronized_data.routes_retry_attempt > MAX_RETRIES_FOR_ROUTES: - self.context.logger.error("Exceeded retry limit") - return Event.DONE.value, {}, {} - - routes_retry_attempt = self.synchronized_data.routes_retry_attempt + 1 - find_route_action = { - "action": Action.FIND_BRIDGE_ROUTE.value, - "from_chain": step_data["from_chain"], - "to_chain": step_data["to_chain"], - "from_token": step_data["source_token"], - "from_token_symbol": step_data["source_token_symbol"], - "to_token": step_data["target_token"], - "to_token_symbol": step_data["target_token_symbol"] - } - - return Event.UPDATE.value, {"last_executed_step_index": None, "last_executed_route_index": None, "new_action": find_route_action, "last_executed_action_index": current_action_index, "routes_retry_attempt": routes_retry_attempt, "last_action": Action.FIND_ROUTE.value}, {} + step_profitable, step_data = yield from self.check_step_costs(step, remaining_fee_allowance, remaining_gas_allowance, to_execute_step_index, len(steps)) + if not step_profitable: + return Event.DONE.value, {} - return Event.EXECUTE_STEP.value, {"new_action": bridge_and_swap_action, "last_action": Event.EXECUTE_STEP.value}, {} - # if all actions have been executed we exit DecisionMaking - if current_action_index >= len(self.synchronized_data.actions): - self.context.logger.info("All actions have been executed") - return Event.DONE.value, {}, {} + bridge_swap_action = yield from self.prepare_bridge_swap_action(step_data, remaining_fee_allowance, remaining_gas_allowance) + if not bridge_swap_action: + return self._handle_failed_step(to_execute_step_index, to_execute_route_index, step_data, len(steps)) - positions = self.synchronized_data.positions + return Event.EXECUTE_STEP.value, { + "new_action": bridge_swap_action, + "last_action": Event.EXECUTE_STEP.value, + } - # If the previous round was not EvaluateStrategyRound, we need to update the balances after a transaction + def _handle_failed_step(self, to_execute_step_index, to_execute_route_index, step_data, total_steps) -> Tuple[Optional[str], Optional[Dict]]: + """Handle a failed step in the route.""" + if to_execute_step_index == 0: + self.context.logger.error("First step failed. Switching to next route..") + return Event.UPDATE.value, { + "last_executed_route_index": to_execute_route_index, + "last_executed_step_index": None, + "last_action": Action.SWITCH_ROUTE.value, + } + + self.context.logger.error("Intermediate step failed. Fetching new routes..") + if self.synchronized_data.routes_retry_attempt > MAX_RETRIES_FOR_ROUTES: + self.context.logger.error("Exceeded retry limit") + return Event.DONE.value, {} + + routes_retry_attempt = self.synchronized_data.routes_retry_attempt + 1 + find_route_action = { + "action": Action.FIND_BRIDGE_ROUTE.value, + "from_chain": step_data["from_chain"], + "to_chain": step_data["to_chain"], + "from_token": step_data["source_token"], + "from_token_symbol": step_data["source_token_symbol"], + "to_token": step_data["target_token"], + "to_token_symbol": step_data["target_token_symbol"] + } + + return Event.UPDATE.value, { + "last_executed_step_index": None, + "last_executed_route_index": None, + "new_action": find_route_action, + "last_executed_action_index": self.synchronized_data.last_executed_action_index + 1, + "routes_retry_attempt": routes_retry_attempt, + "last_action": Action.FIND_ROUTE.value, + } + + def _prepare_next_action(self, actions, current_action_index, last_round_id) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: + """Prepare the next action.""" + positions = self.synchronized_data.positions if last_round_id != EvaluateStrategyRound.auto_round_id(): positions = yield from self.get_positions() - # Prepare the next action next_action = Action(actions[current_action_index].get("action")) next_action_details = self.synchronized_data.actions[current_action_index] self.context.logger.info(f"ACTION DETAILS: {next_action_details}") - bridge_and_swap_actions = {} + return yield from self._prepare_action(next_action, positions, next_action_details) + + + def _prepare_action(self, next_action, positions, next_action_details) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: + """Prepare the action based on the next action type.""" if next_action == Action.ENTER_POOL: tx_hash, chain_id, safe_address = yield from self.get_enter_pool_tx_hash( positions, next_action_details @@ -2050,16 +2086,18 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict route for route in routes if len(route.get("steps", [])) <= self.synchronized_data.max_allowed_steps_in_a_route ] - + serialized_routes = json.dumps(routes) if not routes: self.context.logger.error("Error fetching routes") - return Event.DONE.value, {}, {} - - return Event.UPDATE.value, {"routes": serialized_routes, "last_executed_action_index": current_action_index, "last_action": Action.ROUTES_FETCHED.value}, {} + return Event.DONE.value, {} + + return Event.UPDATE.value, { + "routes": serialized_routes, + "last_action": Action.ROUTES_FETCHED.value, + } elif next_action == Action.BRIDGE_SWAP: - # wait for sometime to get the balances reflected yield from self.sleep(5) tx_hash = next_action_details.get("payload") chain_id = next_action_details.get("from_chain") @@ -2079,23 +2117,17 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict, Optional[Dict last_action = None if not tx_hash: - self.context.logger.error("There was an error preparing the next action") - return Event.DONE.value, {}, {} - - return ( - Event.SETTLE.value, - { - "tx_submitter": DecisionMakingRound.auto_round_id(), - "most_voted_tx_hash": tx_hash, - "chain_id": chain_id, - "safe_contract_address": safe_address, - "positions": positions, - # TO-DO: Decide on the correct method/logic for maintaining the period number for the last transaction. - "last_executed_action_index": current_action_index, - "last_action": last_action - }, - {}, - ) + return Event.DONE.value, {} + + return Event.SETTLE.value, { + "tx_submitter": DecisionMakingRound.auto_round_id(), + "most_voted_tx_hash": tx_hash, + "chain_id": chain_id, + "safe_contract_address": safe_address, + "positions": positions, + "last_executed_action_index": current_action_index, + "last_action": last_action, + } def get_decision_on_swap(self) -> Generator[None, None, str]: """Get decision on swap""" From 7043d30654f776d8db4746b005caa5490d23b804 Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Thu, 3 Oct 2024 13:14:42 +0530 Subject: [PATCH 3/7] fix: add signle simulation --- packages/packages.json | 8 +- .../valory/agents/optimus/aea-config.yaml | 5 +- packages/valory/services/optimus/service.yaml | 2 +- .../liquidity_trader_abci/behaviours.py | 86 +++++++------------ .../skills/liquidity_trader_abci/models.py | 3 + .../skills/liquidity_trader_abci/skill.yaml | 9 +- .../valory/skills/optimus_abci/skill.yaml | 5 +- 7 files changed, 51 insertions(+), 67 deletions(-) diff --git a/packages/packages.json b/packages/packages.json index cfbf7fb..f9fbad8 100644 --- a/packages/packages.json +++ b/packages/packages.json @@ -8,10 +8,10 @@ "contract/valory/merkl_distributor/0.1.0": "bafybeihaqsvmncuzmwv2r6iuzc5t7ur6ugdhephz7ydftypksjidpsylbq", "contract/valory/staking_token/0.1.0": "bafybeifrvtkofw5c26b3irm6izqfdpik6vpjhm6hqwcdzx333h6vhdanai", "contract/valory/staking_activity_checker/0.1.0": "bafybeibjzsi2r5b6xd4iwl4wbwldptnynryzsdpifym4mkv32ynswx22ou", - "skill/valory/liquidity_trader_abci/0.1.0": "bafybeidxpwveipbrdhzlawtzvkhtuwqp42ys2vw25tu5iqiu6tndlz7aom", - "skill/valory/optimus_abci/0.1.0": "bafybeibzjuqil6ipnzhmljlorobbjnv3ai3uv4cave5sv43k336mvbj2ia", - "agent/valory/optimus/0.1.0": "bafybeiftg63fekqan2uzenh5ro2fnitfqyc77ehu74vxjmsq2lyx56xiby", - "service/valory/optimus/0.1.0": "bafybeigxj6z6pvibmya534hm2tmvtoultb5ymqfvj5gmweimmbkk7jf52m" + "skill/valory/liquidity_trader_abci/0.1.0": "bafybeidnvedww6egshhgfpvac5t274g6aekqxnfqwr3c7qsztnso4zde5a", + "skill/valory/optimus_abci/0.1.0": "bafybeihwwwy74dwskaqbqmwijfp46jwhjzxruwjmfwrx37mptzugkg6cn4", + "agent/valory/optimus/0.1.0": "bafybeiddud4kumwbhu2pofd4hpwsofmmwcs6hggzuvzr32fsjyxyno6zxy", + "service/valory/optimus/0.1.0": "bafybeih54pxcqhq37nqahegqrceqhk675io4fo4x4m2nnrjfbbg6rilf2a" }, "third_party": { "protocol/open_aea/signing/1.0.0": "bafybeihv62fim3wl2bayavfcg3u5e5cxu3b7brtu4cn5xoxd6lqwachasi", diff --git a/packages/valory/agents/optimus/aea-config.yaml b/packages/valory/agents/optimus/aea-config.yaml index c1c83e4..355c211 100644 --- a/packages/valory/agents/optimus/aea-config.yaml +++ b/packages/valory/agents/optimus/aea-config.yaml @@ -35,8 +35,8 @@ protocols: skills: - valory/abstract_abci:0.1.0:bafybeidz54kvxhbdmpruzguuzzq7bjg4pekjb5amqobkxoy4oqknnobopu - valory/abstract_round_abci:0.1.0:bafybeiajjzuh6vf23crp55humonknirvv2f4s3dmdlfzch6tc5ow52pcgm -- valory/liquidity_trader_abci:0.1.0:bafybeidxpwveipbrdhzlawtzvkhtuwqp42ys2vw25tu5iqiu6tndlz7aom -- valory/optimus_abci:0.1.0:bafybeibzjuqil6ipnzhmljlorobbjnv3ai3uv4cave5sv43k336mvbj2ia +- valory/liquidity_trader_abci:0.1.0:bafybeidnvedww6egshhgfpvac5t274g6aekqxnfqwr3c7qsztnso4zde5a +- valory/optimus_abci:0.1.0:bafybeihwwwy74dwskaqbqmwijfp46jwhjzxruwjmfwrx37mptzugkg6cn4 - valory/registration_abci:0.1.0:bafybeiffipsowrqrkhjoexem7ern5ob4fabgif7wa6gtlszcoaop2e3oey - valory/reset_pause_abci:0.1.0:bafybeif4lgvbzsmzljesxbphycdv52ka7qnihyjrjpfaseclxadcmm6yiq - valory/termination_abci:0.1.0:bafybeiekkpo5qef5zaeagm3si6v45qxcojvtjqe4a5ceccvk4q7k3xi3bi @@ -214,6 +214,7 @@ models: min_balance_multiplier: ${int:5} multisend_contract_addresses: ${str:{"ethereum":"0x998739BFdAAdde7C933B942a68053933098f9EDa","optimism":"0xbE5b0013D2712DC4faF07726041C27ecFdBC35AD","base":"0x998739BFdAAdde7C933B942a68053933098f9EDa"}} lifi_advance_routes_url: ${str:https://li.quest/v1/advanced/routes} + lifi_fetch_step_transaction_url: ${str:https://li.quest/v1/advanced/stepTransaction} lifi_check_status_url: ${str:https://li.quest/v1/status} lifi_fetch_tools_url: ${str:https://li.quest/v1/tools} slippage_for_swap: ${float:0.09} diff --git a/packages/valory/services/optimus/service.yaml b/packages/valory/services/optimus/service.yaml index 2be0ff4..b55f9dc 100644 --- a/packages/valory/services/optimus/service.yaml +++ b/packages/valory/services/optimus/service.yaml @@ -6,7 +6,7 @@ aea_version: '>=1.0.0, <2.0.0' license: Apache-2.0 fingerprint: {} fingerprint_ignore_patterns: [] -agent: valory/optimus:0.1.0:bafybeiftg63fekqan2uzenh5ro2fnitfqyc77ehu74vxjmsq2lyx56xiby +agent: valory/optimus:0.1.0:bafybeiddud4kumwbhu2pofd4hpwsofmmwcs6hggzuvzr32fsjyxyno6zxy number_of_agents: 1 deployment: {} --- diff --git a/packages/valory/skills/liquidity_trader_abci/behaviours.py b/packages/valory/skills/liquidity_trader_abci/behaviours.py index 492b2e1..fea61ef 100644 --- a/packages/valory/skills/liquidity_trader_abci/behaviours.py +++ b/packages/valory/skills/liquidity_trader_abci/behaviours.py @@ -118,6 +118,7 @@ CAMPAIGN_TYPES = [1, 2] INTEGRATOR = "valory" WAITING_PERIOD_FOR_BALANCE_TO_REFLECT = 5 +MAX_STEP_COST_RATIO = 0.5 WaitableConditionType = Generator[None, None, Any] HTTP_NOT_FOUND = 404 @@ -1222,7 +1223,7 @@ def _filter_campaigns(self, chain, campaigns, filtered_pools): if dex_type in allowed_dexs: # type 1 and 2 stand for ERC20 and Concentrated liquidity campaigns respectively # https://docs.merkl.xyz/integrate-merkl/integrate-merkl-to-your-app#merkl-api - if campaign_type in [1, 2]: + if campaign_type in CAMPAIGN_TYPES: if not campaign_apr > self.current_pool.get("apr", 0.0): self.context.logger.info( "APR does not exceed the current pool APR" @@ -1784,12 +1785,10 @@ class DecisionMakingBehaviour(LiquidityTraderBaseBehaviour): def async_act(self) -> Generator: """Async act""" with self.context.benchmark_tool.measure(self.behaviour_id).local(): - self.context.logger.info(f"ROUTES: {self.synchronized_data.routes}") sender = self.context.agent_address ( next_event, - updates, - bridge_and_swap_actions, + updates ) = yield from self.get_next_event() payload = DecisionMakingPayload( @@ -1824,7 +1823,8 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict]]: last_round_id = self.context.state.round_sequence._abci_app._previous_rounds[-1].round_id if self.synchronized_data.last_action == Action.EXECUTE_STEP.value: - return yield from self._post_execute_step(actions, last_executed_action_index) + res = yield from self._post_execute_step(actions, last_executed_action_index) + return res if last_executed_action_index is not None: if self.synchronized_data.last_action == Action.ENTER_POOL.value: @@ -1837,13 +1837,15 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict]]: if last_executed_action_index is not None and self.synchronized_data.last_action in [ Action.ROUTES_FETCHED.value, Action.STEP_EXECUTED.value, Action.SWITCH_ROUTE.value ]: - return yield from self._post_execute_route_execution() + res = yield from self._post_execute_route_execution() + return res if current_action_index >= len(self.synchronized_data.actions): self.context.logger.info("All actions have been executed") return Event.DONE.value, {}, {} - return yield from self._prepare_next_action(actions, current_action_index, last_round_id) + res = yield from self._prepare_next_action(actions, current_action_index, last_round_id) + return res def _post_execute_step(self, actions, last_executed_action_index) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: """Handle the execution of a step.""" @@ -1979,7 +1981,8 @@ def _post_execute_route_execution(self) -> Generator[None, None, Tuple[Optional[ "last_action": Action.BRIDGE_SWAP_EXECUTED.value, } - return yield from self._execute_route_step(routes, to_execute_route_index, to_execute_step_index) + res = yield from self._execute_route_step(routes, to_execute_route_index, to_execute_step_index) + return res def _execute_route_step(self, routes, to_execute_route_index, to_execute_step_index) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: """Execute a step in the route.""" @@ -2062,11 +2065,6 @@ def _prepare_next_action(self, actions, current_action_index, last_round_id) -> next_action_details = self.synchronized_data.actions[current_action_index] self.context.logger.info(f"ACTION DETAILS: {next_action_details}") - return yield from self._prepare_action(next_action, positions, next_action_details) - - - def _prepare_action(self, next_action, positions, next_action_details) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: - """Prepare the action based on the next action type.""" if next_action == Action.ENTER_POOL: tx_hash, chain_id, safe_address = yield from self.get_enter_pool_tx_hash( positions, next_action_details @@ -2095,10 +2093,11 @@ def _prepare_action(self, next_action, positions, next_action_details) -> Genera return Event.UPDATE.value, { "routes": serialized_routes, "last_action": Action.ROUTES_FETCHED.value, + "last_executed_action_index": current_action_index } elif next_action == Action.BRIDGE_SWAP: - yield from self.sleep(5) + yield from self.sleep(WAITING_PERIOD_FOR_BALANCE_TO_REFLECT) tx_hash = next_action_details.get("payload") chain_id = next_action_details.get("from_chain") safe_address = next_action_details.get("safe_address") @@ -2496,8 +2495,7 @@ def prepare_bridge_swap_action(self, tx_info: Dict[str,Any], remaining_fee_allow from_chain, multisend_tx_hash, multisend_address ) if not payload_string: - all_steps_successful = False - break + return None bridge_and_swap_action = { "action": Action.BRIDGE_SWAP.value, @@ -2516,7 +2514,7 @@ def prepare_bridge_swap_action(self, tx_info: Dict[str,Any], remaining_fee_allow } return bridge_and_swap_action - def check_if_route_is_profitable(self, route: Dict[str, Any]) -> Generator[None, None, Optional[bool, float, float]]: + def check_if_route_is_profitable(self, route: Dict[str, Any]) -> Generator[None, None, Tuple[Optional[bool], Optional[float], Optional[float]]]: """Checks if the entire route is profitable""" step_transactions = yield from self._get_step_transactions_data(route) total_gas_cost = 0 @@ -2559,15 +2557,23 @@ def check_step_costs(self, step, remaining_fee_allowance, remaining_gas_allowanc step_fee = step_data.get("fee", 0) step_gas_cost = step_data.get("gas_cost", 0) - if step_index == total_steps - 1: + if total_steps != 1 and step_index == total_steps - 1: # For the last step, ensure it is not more than 50% of the remaining fee and gas allowance - if step_fee > 0.5 * remaining_fee_allowance or step_gas_cost > 0.5 * remaining_gas_allowance: - self.context.logger.error("Last step exceeds 50%% of the remaining fee or gas allowance. Dropping step.") + if step_fee > MAX_STEP_COST_RATIO * remaining_fee_allowance or step_gas_cost > MAX_STEP_COST_RATIO * remaining_gas_allowance: + self.context.logger.error( + f"Step exceeds 50% of the remaining fee or gas allowance. " + f"Step fee: {step_fee}, Remaining fee allowance: {remaining_fee_allowance}, " + f"Step gas cost: {step_gas_cost}, Remaining gas allowance: {remaining_gas_allowance}. Dropping step." + ) return False, None else: if step_fee > remaining_fee_allowance or step_gas_cost > remaining_gas_allowance: - self.context.logger.error("First step exceeds remaining fee or gas allowance. Dropping step.") + self.context.logger.error( + f"Step exceeds remaining fee or gas allowance. " + f"Step fee: {step_fee}, Remaining fee allowance: {remaining_fee_allowance}, " + f"Step gas cost: {step_gas_cost}, Remaining gas allowance: {remaining_gas_allowance}. Dropping step." + ) return False, None return True, step_data @@ -2666,7 +2672,7 @@ def _get_step_transaction( self, step: Dict[str, Any] ) -> Generator[None, None, Optional[Dict[str, Any]]]: """Get transaction data for a step from LiFi API""" - base_url = "https://li.quest/v1/advanced/stepTransaction" + base_url = self.params.lifi_fetch_step_transaction_url response = yield from self.get_http_response( "POST", base_url, @@ -2891,18 +2897,15 @@ def _simulate_transaction( "tenderly_project_slug": self.params.tenderly_project_slug, } api_url = url_template.format(**values) - - transfer_data = self._encode_transfer_data(token, safe_address, amount) + body = { "network_id": self.params.chain_to_chain_id_mapping.get(chain), - "block_number": "latest", - "save": True, - "save_if_fails": True, - "gas": 0, - "simulation_type": "quick", + "block_number": ledger_api.api.eth.get_block("latest")["number"], "from": self.context.agent_address, "to": safe_address, + "gas": 8000000, "input": tx_data, + "simulation_type": "quick", } response = yield from self.get_http_response( @@ -3001,31 +3004,6 @@ def get_claim_rewards_tx_hash( return payload_string, chain, safe_address - def _encode_transfer_data(self, token: str, to_address: str, amount: int) -> str: - transfer_data = ( - Web3() - .eth.contract( - address=token, - abi=[ - { - "constant": False, - "inputs": [ - {"name": "to", "type": "address"}, - {"name": "value", "type": "uint256"}, - ], - "name": "transfer", - "outputs": [{"name": "", "type": "bool"}], - "payable": False, - "stateMutability": "nonpayable", - "type": "function", - } - ], - ) - .encodeABI(fn_name="transfer", args=[to_address, amount]) - ) - - return transfer_data - def _get_data_from_mint_tx_receipt( self, tx_hash: str, chain: str ) -> Generator[None, None, Optional[Tuple[int, int]]]: diff --git a/packages/valory/skills/liquidity_trader_abci/models.py b/packages/valory/skills/liquidity_trader_abci/models.py index f25fbf2..06369e2 100644 --- a/packages/valory/skills/liquidity_trader_abci/models.py +++ b/packages/valory/skills/liquidity_trader_abci/models.py @@ -193,6 +193,9 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.lifi_advance_routes_url = self._ensure( "lifi_advance_routes_url", kwargs, str ) + self.lifi_fetch_step_transaction_url = self._ensure( + "lifi_fetch_step_transaction_url", kwargs, str + ) self.lifi_check_status_url = self._ensure("lifi_check_status_url", kwargs, str) self.slippage_for_swap = self._ensure("slippage_for_swap", kwargs, float) self.allowed_dexs: List[str] = self._ensure("allowed_dexs", kwargs, List[str]) diff --git a/packages/valory/skills/liquidity_trader_abci/skill.yaml b/packages/valory/skills/liquidity_trader_abci/skill.yaml index dffc9a1..da9dfdf 100644 --- a/packages/valory/skills/liquidity_trader_abci/skill.yaml +++ b/packages/valory/skills/liquidity_trader_abci/skill.yaml @@ -7,16 +7,16 @@ license: Apache-2.0 aea_version: '>=1.0.0, <2.0.0' fingerprint: __init__.py: bafybeia7bn2ahqqwkf63ptje6rfnftuwrsp33sswgpcbh5osbesxxr6g4m - behaviours.py: bafybeihttk6wduv3nrfplpzilvwrjknba457nacof72kasaiscwnyoqx3e + behaviours.py: bafybeih25g4kln6wdiic72gagixh4lxdjpken7efkmqca6qgja2athpgba dialogues.py: bafybeiay23otskx2go5xhtgdwfw2kd6rxd62sxxdu3njv7hageorl5zxzm fsm_specification.yaml: bafybeiabbiulb7k6xkjysulmy6o4ugnhxlpp5jiaeextvwj65q4ttadoeq handlers.py: bafybeidxw2lvgiifmo4siobpwuwbxscuifrdo3gnkjyn6bgexotj5f7zf4 - models.py: bafybeiahzdqz6megfdtiskfnh2hxukkhvp7lpjbif47druqsdnlpnzijnq + models.py: bafybeiatxcqswxjhipmubqwawqzncr7kckj4gwzawh7bp4rigepy4wirgy payloads.py: bafybeihi42vbsr2xyhzsp55tl6mqecqyy52jrhwjfw4trvrjlhfxuyaz3u pool_behaviour.py: bafybeiaheuesscgqzwjbpyrezgwpdbdfurlmfwbc462qv6rblwwxlx5dpm pools/balancer.py: bafybeigznhgv7ylo5dvlhxcqikhiuqlqtnx3ikv4tszyvkl2lpcuqgoa5u pools/uniswap.py: bafybeigmqptgmjaxscszohfusgxsexqyx4awuyw7p4g5l7k2qpeyq7vdcu - rounds.py: bafybeiafgvijxvardoabgj2turhg7eebvfemkszlkurn6iaff56w5jdwby + rounds.py: bafybeifnjkgmv4e6nnr4qutqwooswsvbtrqta7mysdhiub4b4zpbuzvcoq strategies/simple_strategy.py: bafybeiasu2nchowx6leksjllpuum4ckezxoj4o2m4sstavblplvvutmvzm strategy_behaviour.py: bafybeidk6sorg47kuuubamcccksi65x3txldyo7y2hm5opbye2ghmz2ljy fingerprint_ignore_patterns: [] @@ -157,7 +157,8 @@ models: apr_threshold: 5 min_balance_multiplier: 5 multisend_contract_addresses: '{"ethereum":"0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761","optimism":"0xbE5b0013D2712DC4faF07726041C27ecFdBC35AD"}' - lifi_advance_routes_url: https://li.quest/v1/quote + lifi_advance_routes_url: https://li.quest/v1/advanced/routes + lifi_fetch_step_transaction_url: https://li.quest/v1/advanced/stepTransaction lifi_check_status_url: https://li.quest/v1/status slippage_for_swap: 0.08 balancer_vault_contract_addresses: '{"optimism":"0xBA12222222228d8Ba445958a75a0704d566BF2C8","base":"0xBA12222222228d8Ba445958a75a0704d566BF2C8"}' diff --git a/packages/valory/skills/optimus_abci/skill.yaml b/packages/valory/skills/optimus_abci/skill.yaml index 298b249..64fb377 100644 --- a/packages/valory/skills/optimus_abci/skill.yaml +++ b/packages/valory/skills/optimus_abci/skill.yaml @@ -22,7 +22,7 @@ skills: - valory/registration_abci:0.1.0:bafybeiffipsowrqrkhjoexem7ern5ob4fabgif7wa6gtlszcoaop2e3oey - valory/reset_pause_abci:0.1.0:bafybeif4lgvbzsmzljesxbphycdv52ka7qnihyjrjpfaseclxadcmm6yiq - valory/termination_abci:0.1.0:bafybeiekkpo5qef5zaeagm3si6v45qxcojvtjqe4a5ceccvk4q7k3xi3bi -- valory/liquidity_trader_abci:0.1.0:bafybeidxpwveipbrdhzlawtzvkhtuwqp42ys2vw25tu5iqiu6tndlz7aom +- valory/liquidity_trader_abci:0.1.0:bafybeidnvedww6egshhgfpvac5t274g6aekqxnfqwr3c7qsztnso4zde5a - valory/transaction_settlement_abci:0.1.0:bafybeielv6eivt2z6nforq43xewl2vmpfwpdu2s2vfogobziljnwsclmlm behaviours: main: @@ -147,7 +147,8 @@ models: apr_threshold: 5 min_balance_multiplier: 5 multisend_contract_addresses: '{"ethereum":"0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761","optimism":"0xbE5b0013D2712DC4faF07726041C27ecFdBC35AD"}' - lifi_advance_routes_url: https://li.quest/v1/quote + lifi_advance_routes_url: https://li.quest/v1/advanced/routes + lifi_fetch_step_transaction_url: https://li.quest/v1/advanced/stepTransaction lifi_check_status_url: https://li.quest/v1/status slippage_for_swap: 0.08 balancer_vault_contract_addresses: '{"optimism":"0xBA12222222228d8Ba445958a75a0704d566BF2C8","base":"0xBA12222222228d8Ba445958a75a0704d566BF2C8"}' From 474b45a65204b03f80becb3b75cab597e64e1725 Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Thu, 3 Oct 2024 22:15:51 +0530 Subject: [PATCH 4/7] chore: run formatter --- packages/packages.json | 8 +- .../valory/agents/optimus/aea-config.yaml | 6 +- packages/valory/services/optimus/service.yaml | 2 +- .../liquidity_trader_abci/behaviours.py | 287 ++++++++++++------ .../skills/liquidity_trader_abci/rounds.py | 11 +- .../skills/liquidity_trader_abci/skill.yaml | 4 +- .../valory/skills/optimus_abci/skill.yaml | 2 +- 7 files changed, 217 insertions(+), 103 deletions(-) diff --git a/packages/packages.json b/packages/packages.json index f9fbad8..7405af7 100644 --- a/packages/packages.json +++ b/packages/packages.json @@ -8,10 +8,10 @@ "contract/valory/merkl_distributor/0.1.0": "bafybeihaqsvmncuzmwv2r6iuzc5t7ur6ugdhephz7ydftypksjidpsylbq", "contract/valory/staking_token/0.1.0": "bafybeifrvtkofw5c26b3irm6izqfdpik6vpjhm6hqwcdzx333h6vhdanai", "contract/valory/staking_activity_checker/0.1.0": "bafybeibjzsi2r5b6xd4iwl4wbwldptnynryzsdpifym4mkv32ynswx22ou", - "skill/valory/liquidity_trader_abci/0.1.0": "bafybeidnvedww6egshhgfpvac5t274g6aekqxnfqwr3c7qsztnso4zde5a", - "skill/valory/optimus_abci/0.1.0": "bafybeihwwwy74dwskaqbqmwijfp46jwhjzxruwjmfwrx37mptzugkg6cn4", - "agent/valory/optimus/0.1.0": "bafybeiddud4kumwbhu2pofd4hpwsofmmwcs6hggzuvzr32fsjyxyno6zxy", - "service/valory/optimus/0.1.0": "bafybeih54pxcqhq37nqahegqrceqhk675io4fo4x4m2nnrjfbbg6rilf2a" + "skill/valory/liquidity_trader_abci/0.1.0": "bafybeies2wtlbei2oajpguwtjvv6ksicdfyfw3alc5bku5aphbdeqtowq4", + "skill/valory/optimus_abci/0.1.0": "bafybeidvsbeythm25tizkanglnzey6gxfhjwhgtli2lxlbmhycgntcyqj4", + "agent/valory/optimus/0.1.0": "bafybeidnw6pubtwy3hcv6e757tyvk4arov5co3lqrqiezy2ar4fxnbmr34", + "service/valory/optimus/0.1.0": "bafybeif3oy24t5dhbgel4jgi76qvhebuwmjdqiwrbkgm2bvfanglekbtua" }, "third_party": { "protocol/open_aea/signing/1.0.0": "bafybeihv62fim3wl2bayavfcg3u5e5cxu3b7brtu4cn5xoxd6lqwachasi", diff --git a/packages/valory/agents/optimus/aea-config.yaml b/packages/valory/agents/optimus/aea-config.yaml index 355c211..c0f3917 100644 --- a/packages/valory/agents/optimus/aea-config.yaml +++ b/packages/valory/agents/optimus/aea-config.yaml @@ -35,8 +35,8 @@ protocols: skills: - valory/abstract_abci:0.1.0:bafybeidz54kvxhbdmpruzguuzzq7bjg4pekjb5amqobkxoy4oqknnobopu - valory/abstract_round_abci:0.1.0:bafybeiajjzuh6vf23crp55humonknirvv2f4s3dmdlfzch6tc5ow52pcgm -- valory/liquidity_trader_abci:0.1.0:bafybeidnvedww6egshhgfpvac5t274g6aekqxnfqwr3c7qsztnso4zde5a -- valory/optimus_abci:0.1.0:bafybeihwwwy74dwskaqbqmwijfp46jwhjzxruwjmfwrx37mptzugkg6cn4 +- valory/liquidity_trader_abci:0.1.0:bafybeies2wtlbei2oajpguwtjvv6ksicdfyfw3alc5bku5aphbdeqtowq4 +- valory/optimus_abci:0.1.0:bafybeidvsbeythm25tizkanglnzey6gxfhjwhgtli2lxlbmhycgntcyqj4 - valory/registration_abci:0.1.0:bafybeiffipsowrqrkhjoexem7ern5ob4fabgif7wa6gtlszcoaop2e3oey - valory/reset_pause_abci:0.1.0:bafybeif4lgvbzsmzljesxbphycdv52ka7qnihyjrjpfaseclxadcmm6yiq - valory/termination_abci:0.1.0:bafybeiekkpo5qef5zaeagm3si6v45qxcojvtjqe4a5ceccvk4q7k3xi3bi @@ -221,7 +221,7 @@ models: balancer_vault_contract_addresses: ${str:{"optimism":"0xBA12222222228d8Ba445958a75a0704d566BF2C8","base":"0xBA12222222228d8Ba445958a75a0704d566BF2C8"}} uniswap_position_manager_contract_addresses: ${str:{"optimism":"0xC36442b4a4522E871399CD717aBDD847Ab11FE88","base":"0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1"}} chain_to_chain_key_mapping: ${str:{"ethereum":"eth","optimism":"opt","base":"bas"}} - waiting_period_for_status_check: ${int:5} + waiting_period_for_status_check: ${int:10} max_num_of_retries: ${int:5} reward_claiming_time_period: ${int:28800} merkl_distributor_contract_addresses: ${str:{"optimism":"0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae","base":"0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae"}} diff --git a/packages/valory/services/optimus/service.yaml b/packages/valory/services/optimus/service.yaml index b55f9dc..e50e5cd 100644 --- a/packages/valory/services/optimus/service.yaml +++ b/packages/valory/services/optimus/service.yaml @@ -6,7 +6,7 @@ aea_version: '>=1.0.0, <2.0.0' license: Apache-2.0 fingerprint: {} fingerprint_ignore_patterns: [] -agent: valory/optimus:0.1.0:bafybeiddud4kumwbhu2pofd4hpwsofmmwcs6hggzuvzr32fsjyxyno6zxy +agent: valory/optimus:0.1.0:bafybeidnw6pubtwy3hcv6e757tyvk4arov5co3lqrqiezy2ar4fxnbmr34 number_of_agents: 1 deployment: {} --- diff --git a/packages/valory/skills/liquidity_trader_abci/behaviours.py b/packages/valory/skills/liquidity_trader_abci/behaviours.py index fea61ef..4cef817 100644 --- a/packages/valory/skills/liquidity_trader_abci/behaviours.py +++ b/packages/valory/skills/liquidity_trader_abci/behaviours.py @@ -41,7 +41,6 @@ from aea.configurations.data_types import PublicId from eth_abi import decode from eth_utils import keccak, to_bytes, to_hex -from web3 import Web3 from packages.valory.contracts.erc20.contract import ERC20 from packages.valory.contracts.gnosis_safe.contract import ( @@ -120,8 +119,7 @@ WAITING_PERIOD_FOR_BALANCE_TO_REFLECT = 5 MAX_STEP_COST_RATIO = 0.5 WaitableConditionType = Generator[None, None, Any] -HTTP_NOT_FOUND = 404 - +HTTP_NOT_FOUND = [400, 404] class DexTypes(Enum): @@ -1786,10 +1784,7 @@ def async_act(self) -> Generator: """Async act""" with self.context.benchmark_tool.measure(self.behaviour_id).local(): sender = self.context.agent_address - ( - next_event, - updates - ) = yield from self.get_next_event() + (next_event, updates) = yield from self.get_next_event() payload = DecisionMakingPayload( sender=sender, @@ -1820,10 +1815,17 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict]]: 0 if last_executed_action_index is None else last_executed_action_index + 1 ) - last_round_id = self.context.state.round_sequence._abci_app._previous_rounds[-1].round_id + last_round_id = self.context.state.round_sequence._abci_app._previous_rounds[ + -1 + ].round_id - if self.synchronized_data.last_action == Action.EXECUTE_STEP.value: - res = yield from self._post_execute_step(actions, last_executed_action_index) + if ( + self.synchronized_data.last_action == Action.EXECUTE_STEP.value + and last_round_id != DecisionMakingRound.auto_round_id() + ): + res = yield from self._post_execute_step( + actions, last_executed_action_index + ) return res if last_executed_action_index is not None: @@ -1831,29 +1833,46 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict]]: self._post_execute_enter_pool(actions, last_executed_action_index) if self.synchronized_data.last_action == Action.EXIT_POOL.value: self._post_execute_exit_pool() - if self.synchronized_data.last_action == Action.CLAIM_REWARDS.value and last_round_id != DecisionMakingRound.auto_round_id(): - return self._post_execute_claim_rewards(actions, last_executed_action_index) + if ( + self.synchronized_data.last_action == Action.CLAIM_REWARDS.value + and last_round_id != DecisionMakingRound.auto_round_id() + ): + return self._post_execute_claim_rewards( + actions, last_executed_action_index + ) - if last_executed_action_index is not None and self.synchronized_data.last_action in [ - Action.ROUTES_FETCHED.value, Action.STEP_EXECUTED.value, Action.SWITCH_ROUTE.value - ]: - res = yield from self._post_execute_route_execution() + if ( + last_executed_action_index is not None + and self.synchronized_data.last_action + in [ + Action.ROUTES_FETCHED.value, + Action.STEP_EXECUTED.value, + Action.SWITCH_ROUTE.value, + ] + ): + res = yield from self._process_route_execution() return res if current_action_index >= len(self.synchronized_data.actions): self.context.logger.info("All actions have been executed") return Event.DONE.value, {}, {} - res = yield from self._prepare_next_action(actions, current_action_index, last_round_id) + res = yield from self._prepare_next_action( + actions, current_action_index, last_round_id + ) return res - def _post_execute_step(self, actions, last_executed_action_index) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: + def _post_execute_step( + self, actions, last_executed_action_index + ) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: """Handle the execution of a step.""" self.context.logger.info("Checking the status of swap tx") + # we wait for some time before checking the status of the tx because the tx may take time to reflect on the lifi endpoint yield from self.sleep(self.params.waiting_period_for_status_check) decision = yield from self.get_decision_on_swap() self.context.logger.info(f"Action to take {decision}") + # If tx is pending then we wait until it gets confirmed or refunded if decision == Decision.WAIT: decision = yield from self._wait_for_swap_confirmation() @@ -1875,7 +1894,9 @@ def _wait_for_swap_confirmation(self) -> Generator[None, None, Optional[Decision break return decision - def _update_assets_after_swap(self, actions, last_executed_action_index) -> Tuple[Optional[str], Optional[Dict]]: + def _update_assets_after_swap( + self, actions, last_executed_action_index + ) -> Tuple[Optional[str], Optional[Dict]]: """Update assets after a successful swap.""" action = actions[last_executed_action_index] self._add_token_to_assets( @@ -1893,7 +1914,11 @@ def _update_assets_after_swap(self, actions, last_executed_action_index) -> Tupl "remaining_gas_allowance": action.get("remaining_gas_allowance"), } return Event.UPDATE.value, { - "last_executed_step_index": self.synchronized_data.last_executed_step_index + 1, + "last_executed_step_index": ( + self.synchronized_data.last_executed_step_index + 1 + if self.synchronized_data.last_executed_step_index is not None + else 0 + ), "fee_details": fee_details, "last_action": Action.STEP_EXECUTED.value, } @@ -1926,10 +1951,12 @@ def _post_execute_exit_pool(self): self.current_pool = {} self.store_current_pool() self.context.logger.info("Exit was successful! Removing current pool") + # when we exit the pool, it may take time to reflect the balance of our assets in safe yield from self.sleep(WAITING_PERIOD_FOR_BALANCE_TO_REFLECT) - - def _post_execute_claim_rewards(self, actions, last_executed_action_index) -> Tuple[Optional[str], Optional[Dict]]: + def _post_execute_claim_rewards( + self, actions, last_executed_action_index + ) -> Tuple[Optional[str], Optional[Dict]]: """Handle claiming rewards.""" action = actions[last_executed_action_index] chain = action.get("chain") @@ -1947,7 +1974,9 @@ def _post_execute_claim_rewards(self, actions, last_executed_action_index) -> Tu "last_action": Action.CLAIM_REWARDS.value, } - def _post_execute_route_execution(self) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: + def _process_route_execution( + self, + ) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: """Handle route execution.""" routes = self.synchronized_data.routes if not routes: @@ -1955,13 +1984,15 @@ def _post_execute_route_execution(self) -> Generator[None, None, Tuple[Optional[ return Event.DONE.value, {} last_executed_route_index = ( - -1 if self.synchronized_data.last_executed_route_index is None + -1 + if self.synchronized_data.last_executed_route_index is None else self.synchronized_data.last_executed_route_index ) to_execute_route_index = last_executed_route_index + 1 last_executed_step_index = ( - -1 if self.synchronized_data.last_executed_step_index is None + -1 + if self.synchronized_data.last_executed_step_index is None else self.synchronized_data.last_executed_step_index ) to_execute_step_index = last_executed_step_index + 1 @@ -1969,35 +2000,53 @@ def _post_execute_route_execution(self) -> Generator[None, None, Tuple[Optional[ if to_execute_route_index >= len(routes): self.context.logger.error("No more routes left to execute") return Event.DONE.value, {} - if to_execute_step_index >= len(routes[to_execute_route_index]): + if to_execute_step_index >= len( + routes[to_execute_route_index].get("steps", []) + ): self.context.logger.info("All steps executed successfully!") return Event.UPDATE.value, { "last_executed_route_index": None, "last_executed_step_index": None, - "fee_details": {}, - "routes": {}, + "fee_details": None, + "routes": None, "max_allowed_steps_in_a_route": None, "routes_retry_attempt": 0, "last_action": Action.BRIDGE_SWAP_EXECUTED.value, } - res = yield from self._execute_route_step(routes, to_execute_route_index, to_execute_step_index) + res = yield from self._execute_route_step( + routes, to_execute_route_index, to_execute_step_index + ) return res - def _execute_route_step(self, routes, to_execute_route_index, to_execute_step_index) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: + def _execute_route_step( + self, routes, to_execute_route_index, to_execute_step_index + ) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: """Execute a step in the route.""" steps = routes[to_execute_route_index].get("steps") step = steps[to_execute_step_index] total_fee = self.synchronized_data.fee_details.get("total_fee") total_gas_cost = self.synchronized_data.fee_details.get("total_gas_cost") - remaining_fee_allowance = self.synchronized_data.fee_details.get("remaining_fee_allowance") - remaining_gas_allowance = self.synchronized_data.fee_details.get("remaining_gas_allowance") + remaining_fee_allowance = self.synchronized_data.fee_details.get( + "remaining_fee_allowance" + ) + remaining_gas_allowance = self.synchronized_data.fee_details.get( + "remaining_gas_allowance" + ) if to_execute_step_index == 0: - is_profitable, total_fee, total_gas_cost = yield from self.check_if_route_is_profitable(routes[to_execute_route_index]) + ( + is_profitable, + total_fee, + total_gas_cost, + ) = yield from self.check_if_route_is_profitable( + routes[to_execute_route_index] + ) if not is_profitable: - self.context.logger.error("Route not profitable. Switching to next route..") + self.context.logger.error( + "Route not profitable. Switching to next route.." + ) return Event.UPDATE.value, { "last_executed_route_index": to_execute_route_index, "last_executed_step_index": None, @@ -2007,20 +2056,36 @@ def _execute_route_step(self, routes, to_execute_route_index, to_execute_step_in remaining_fee_allowance = total_fee remaining_gas_allowance = total_gas_cost - step_profitable, step_data = yield from self.check_step_costs(step, remaining_fee_allowance, remaining_gas_allowance, to_execute_step_index, len(steps)) + step_profitable, step_data = yield from self.check_step_costs( + step, + remaining_fee_allowance, + remaining_gas_allowance, + to_execute_step_index, + len(steps), + ) if not step_profitable: return Event.DONE.value, {} - bridge_swap_action = yield from self.prepare_bridge_swap_action(step_data, remaining_fee_allowance, remaining_gas_allowance) + self.context.logger.info( + f"Preparing bridge swap action for {step_data.get('source_token_symbol')}({step_data.get('from_chain')}) " + f"to {step_data.get('target_token_symbol')}({step_data.get('to_chain')}) using tool {step_data.get('tool')}" + ) + bridge_swap_action = yield from self.prepare_bridge_swap_action( + step_data, remaining_fee_allowance, remaining_gas_allowance + ) if not bridge_swap_action: - return self._handle_failed_step(to_execute_step_index, to_execute_route_index, step_data, len(steps)) + return self._handle_failed_step( + to_execute_step_index, to_execute_route_index, step_data, len(steps) + ) - return Event.EXECUTE_STEP.value, { + return Event.UPDATE.value, { "new_action": bridge_swap_action, - "last_action": Event.EXECUTE_STEP.value, + "last_action": Action.EXECUTE_STEP.value, } - def _handle_failed_step(self, to_execute_step_index, to_execute_route_index, step_data, total_steps) -> Tuple[Optional[str], Optional[Dict]]: + def _handle_failed_step( + self, to_execute_step_index, to_execute_route_index, step_data, total_steps + ) -> Tuple[Optional[str], Optional[Dict]]: """Handle a failed step in the route.""" if to_execute_step_index == 0: self.context.logger.error("First step failed. Switching to next route..") @@ -2043,19 +2108,23 @@ def _handle_failed_step(self, to_execute_step_index, to_execute_route_index, ste "from_token": step_data["source_token"], "from_token_symbol": step_data["source_token_symbol"], "to_token": step_data["target_token"], - "to_token_symbol": step_data["target_token_symbol"] + "to_token_symbol": step_data["target_token_symbol"], } return Event.UPDATE.value, { "last_executed_step_index": None, "last_executed_route_index": None, + "fee_details": None, + "routes": None, "new_action": find_route_action, - "last_executed_action_index": self.synchronized_data.last_executed_action_index + 1, "routes_retry_attempt": routes_retry_attempt, + "max_allowed_steps_in_a_route": total_steps - to_execute_step_index, "last_action": Action.FIND_ROUTE.value, } - def _prepare_next_action(self, actions, current_action_index, last_round_id) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: + def _prepare_next_action( + self, actions, current_action_index, last_round_id + ) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: """Prepare the next action.""" positions = self.synchronized_data.positions if last_round_id != EvaluateStrategyRound.auto_round_id(): @@ -2081,8 +2150,10 @@ def _prepare_next_action(self, actions, current_action_index, last_round_id) -> routes = yield from self.fetch_routes(positions, next_action_details) if self.synchronized_data.max_allowed_steps_in_a_route: routes = [ - route for route in routes - if len(route.get("steps", [])) <= self.synchronized_data.max_allowed_steps_in_a_route + route + for route in routes + if len(route.get("steps", [])) + <= self.synchronized_data.max_allowed_steps_in_a_route ] serialized_routes = json.dumps(routes) @@ -2093,7 +2164,7 @@ def _prepare_next_action(self, actions, current_action_index, last_round_id) -> return Event.UPDATE.value, { "routes": serialized_routes, "last_action": Action.ROUTES_FETCHED.value, - "last_executed_action_index": current_action_index + "last_executed_action_index": current_action_index, } elif next_action == Action.BRIDGE_SWAP: @@ -2166,15 +2237,15 @@ def get_swap_status( url = f"{self.params.lifi_check_status_url}?txHash={tx_hash}" self.context.logger.info(f"checking status from endpoint {url}") - - for _attempt in range(MAX_RETRIES_FOR_API_CALL): + MAX_RETRIES = 10 + for _attempt in range(MAX_RETRIES): response = yield from self.get_http_response( method="GET", url=url, headers={"accept": "application/json"}, ) - if response.status_code == HTTP_NOT_FOUND: + if response.status_code in HTTP_NOT_FOUND: self.context.logger.warning(f"Message {response.body}. Retrying..") yield from self.sleep(self.params.waiting_period_for_status_check) continue @@ -2204,7 +2275,9 @@ def get_swap_status( return status, sub_status - self.context.logger.error(f"Failed to fetch status after {MAX_RETRIES_FOR_API_CALL} retries.") + self.context.logger.error( + f"Failed to fetch status after {MAX_RETRIES} retries." + ) return None, None def get_enter_pool_tx_hash( @@ -2462,7 +2535,12 @@ def get_exit_pool_tx_hash( return payload_string, chain, safe_address - def prepare_bridge_swap_action(self, tx_info: Dict[str,Any], remaining_fee_allowance: float, remaining_gas_allowance: float) -> Generator[None, None, Optional[Dict]]: + def prepare_bridge_swap_action( + self, + tx_info: Dict[str, Any], + remaining_fee_allowance: float, + remaining_gas_allowance: float, + ) -> Generator[None, None, Optional[Dict]]: """Prepares the bridge swap action""" multisend_tx_hash = yield from self._build_multisend_tx(tx_info) if not multisend_tx_hash: @@ -2498,30 +2576,29 @@ def prepare_bridge_swap_action(self, tx_info: Dict[str,Any], remaining_fee_allow return None bridge_and_swap_action = { - "action": Action.BRIDGE_SWAP.value, - "from_chain": tx_info.get("from_chain"), - "to_chain": tx_info.get("to_chain"), - "from_token": tx_info.get("source_token"), - "from_token_symbol": tx_info.get("source_token_symbol"), - "to_token": tx_info.get("target_token"), - "to_token_symbol": tx_info.get("target_token_symbol"), - "payload": payload_string, - "safe_address": self.params.safe_contract_addresses.get( - from_chain - ), - "remaining_gas_allowance": remaining_gas_allowance - tx_info.get("gas_cost"), - "remaining_fee_allowance": remaining_fee_allowance - tx_info.get("fee") + "action": Action.BRIDGE_SWAP.value, + "from_chain": tx_info.get("from_chain"), + "to_chain": tx_info.get("to_chain"), + "from_token": tx_info.get("source_token"), + "from_token_symbol": tx_info.get("source_token_symbol"), + "to_token": tx_info.get("target_token"), + "to_token_symbol": tx_info.get("target_token_symbol"), + "payload": payload_string, + "safe_address": self.params.safe_contract_addresses.get(from_chain), + "remaining_gas_allowance": remaining_gas_allowance + - tx_info.get("gas_cost"), + "remaining_fee_allowance": remaining_fee_allowance - tx_info.get("fee"), } return bridge_and_swap_action - - def check_if_route_is_profitable(self, route: Dict[str, Any]) -> Generator[None, None, Tuple[Optional[bool], Optional[float], Optional[float]]]: + + def check_if_route_is_profitable( + self, route: Dict[str, Any] + ) -> Generator[None, None, Tuple[Optional[bool], Optional[float], Optional[float]]]: """Checks if the entire route is profitable""" step_transactions = yield from self._get_step_transactions_data(route) total_gas_cost = 0 total_fee = 0 - total_fee += sum( - float(tx_info.get("fee", 0)) for tx_info in step_transactions - ) + total_fee += sum(float(tx_info.get("fee", 0)) for tx_info in step_transactions) total_gas_cost += sum( float(tx_info.get("gas_cost", 0)) for tx_info in step_transactions ) @@ -2542,24 +2619,41 @@ def check_if_route_is_profitable(self, route: Dict[str, Any]) -> Generator[None, f"Details: total_fee={total_fee}, total_gas_cost={total_gas_cost}, from_amount_usd={from_amount_usd}, to_amount_usd={to_amount_usd}" ) - if fee_percentage > allowed_fee_percentage or gas_percentage > allowed_gas_percentage: + if ( + fee_percentage > allowed_fee_percentage + or gas_percentage > allowed_gas_percentage + ): self.context.logger.error("Route is not profitable!") return False, None, None self.context.logger.info("Route is profitable!") - return True, total_fee, total_gas_cost + return True, total_fee, total_gas_cost - def check_step_costs(self, step, remaining_fee_allowance, remaining_gas_allowance, step_index, total_steps) -> Generator[None, None, Tuple[Optional[bool], Optional[Dict[str,Any]]]]: + def check_step_costs( + self, + step, + remaining_fee_allowance, + remaining_gas_allowance, + step_index, + total_steps, + ) -> Generator[None, None, Tuple[Optional[bool], Optional[Dict[str, Any]]]]: """Check if the step costs are within the allowed range.""" step = self._set_step_addresses(step) step_data = yield from self._get_step_transaction(step) + if not step_data: + self.context.logger.error("Error fetching step transaction") + return False, None step_fee = step_data.get("fee", 0) step_gas_cost = step_data.get("gas_cost", 0) if total_steps != 1 and step_index == total_steps - 1: # For the last step, ensure it is not more than 50% of the remaining fee and gas allowance - if step_fee > MAX_STEP_COST_RATIO * remaining_fee_allowance or step_gas_cost > MAX_STEP_COST_RATIO * remaining_gas_allowance: + if math.ceil(step_fee) > math.ceil( + MAX_STEP_COST_RATIO * remaining_fee_allowance + ) or math.ceil(step_gas_cost) > math.ceil( + MAX_STEP_COST_RATIO * remaining_gas_allowance + ): self.context.logger.error( f"Step exceeds 50% of the remaining fee or gas allowance. " f"Step fee: {step_fee}, Remaining fee allowance: {remaining_fee_allowance}, " @@ -2568,7 +2662,9 @@ def check_step_costs(self, step, remaining_fee_allowance, remaining_gas_allowanc return False, None else: - if step_fee > remaining_fee_allowance or step_gas_cost > remaining_gas_allowance: + if math.ceil(step_fee) > math.ceil(remaining_fee_allowance) or math.ceil( + step_gas_cost + ) > math.ceil(remaining_gas_allowance): self.context.logger.error( f"Step exceeds remaining fee or gas allowance. " f"Step fee: {step_fee}, Remaining fee allowance: {remaining_fee_allowance}, " @@ -2576,6 +2672,9 @@ def check_step_costs(self, step, remaining_fee_allowance, remaining_gas_allowanc ) return False, None + self.context.logger.info( + f"Step is profitable! Step fee: {step_fee}, Step gas cost: {step_gas_cost}" + ) return True, step_data def _build_safe_tx( @@ -2754,7 +2853,7 @@ def _get_step_transaction( "fee": fee, "gas_cost": gas_cost, "from_amount_usd": from_amount_usd, - "to_amount_usd": to_amount_usd + "to_amount_usd": to_amount_usd, } def _set_step_addresses(self, step: Dict[str, Any]) -> Dict[str, Any]: @@ -2781,9 +2880,7 @@ def _set_step_addresses(self, step: Dict[str, Any]) -> Dict[str, Any]: step["action"]["fromAddress"] = self.params.safe_contract_addresses.get( from_chain ) - step["action"]["toAddress"] = self.params.safe_contract_addresses.get( - to_chain - ) + step["action"]["toAddress"] = self.params.safe_contract_addresses.get(to_chain) return step @@ -2897,16 +2994,32 @@ def _simulate_transaction( "tenderly_project_slug": self.params.tenderly_project_slug, } api_url = url_template.format(**values) - + + ledger_api_response = ( + ledger_api_response + ) = yield from self.get_ledger_api_response( + performative=LedgerApiMessage.Performative.GET_STATE, # type: ignore + ledger_callable="get_block", + block_identifier="latest", + chain_id="base", + ) + + if ledger_api_response.performative != LedgerApiMessage.Performative.STATE: + self.context.logger.error( + f"Could not fetch the latest blocok number: {ledger_api_response}" + ) + return None + + latest_block = int(ledger_api_response.state.body["number"]) + body = { - "network_id": self.params.chain_to_chain_id_mapping.get(chain), - "block_number": ledger_api.api.eth.get_block("latest")["number"], - "from": self.context.agent_address, - "to": safe_address, - "gas": 8000000, - "input": tx_data, - "simulation_type": "quick", - } + "network_id": self.params.chain_to_chain_id_mapping.get(chain), + "block_number": latest_block, + "from": self.context.agent_address, + "to": safe_address, + "simulation_type": "quick", + "data": tx_data, + } response = yield from self.get_http_response( "POST", diff --git a/packages/valory/skills/liquidity_trader_abci/rounds.py b/packages/valory/skills/liquidity_trader_abci/rounds.py index cb1587a..34f7ce0 100644 --- a/packages/valory/skills/liquidity_trader_abci/rounds.py +++ b/packages/valory/skills/liquidity_trader_abci/rounds.py @@ -172,7 +172,7 @@ def chain_id(self) -> Optional[str]: def period_number_at_last_cp(self) -> Optional[int]: """Get the period number at last cp.""" return cast(int, self.db.get("period_number_at_last_cp", 0)) - + @property def last_executed_route_index(self) -> Optional[int]: """Get the last executed route index.""" @@ -182,12 +182,12 @@ def last_executed_route_index(self) -> Optional[int]: def last_executed_step_index(self) -> Optional[int]: """Get the last executed step index.""" return cast(int, self.db.get("last_executed_step_index", None)) - + @property def routes_retry_attempt(self) -> Optional[int]: """Get the routes retry attempt index.""" return cast(int, self.db.get("routes_retry_attempt", 0)) - + @property def routes(self) -> Optional[List[Dict[str, Any]]]: """Get the routes""" @@ -209,7 +209,8 @@ def max_allowed_steps_in_a_route(self) -> Optional[int]: def last_action(self) -> Optional[str]: """Get the last action.""" return cast(str, self.db.get("last_action", None)) - + + class CallCheckpointRound(CollectSameUntilThresholdRound): """A round for the checkpoint call preparation.""" @@ -370,7 +371,7 @@ def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Event]]: index = 0 else: index = self.synchronized_data.last_executed_action_index + 1 - updated_actions.insert(new_action) + updated_actions.insert(index, new_action) serialized_actions = json.dumps(updated_actions) synchronized_data = synchronized_data.update( diff --git a/packages/valory/skills/liquidity_trader_abci/skill.yaml b/packages/valory/skills/liquidity_trader_abci/skill.yaml index da9dfdf..f03dd57 100644 --- a/packages/valory/skills/liquidity_trader_abci/skill.yaml +++ b/packages/valory/skills/liquidity_trader_abci/skill.yaml @@ -7,7 +7,7 @@ license: Apache-2.0 aea_version: '>=1.0.0, <2.0.0' fingerprint: __init__.py: bafybeia7bn2ahqqwkf63ptje6rfnftuwrsp33sswgpcbh5osbesxxr6g4m - behaviours.py: bafybeih25g4kln6wdiic72gagixh4lxdjpken7efkmqca6qgja2athpgba + behaviours.py: bafybeiayj3gwcsc2yicuyx2vu6vofmiuma7dhr52fwi5emkswv7wvye4x4 dialogues.py: bafybeiay23otskx2go5xhtgdwfw2kd6rxd62sxxdu3njv7hageorl5zxzm fsm_specification.yaml: bafybeiabbiulb7k6xkjysulmy6o4ugnhxlpp5jiaeextvwj65q4ttadoeq handlers.py: bafybeidxw2lvgiifmo4siobpwuwbxscuifrdo3gnkjyn6bgexotj5f7zf4 @@ -16,7 +16,7 @@ fingerprint: pool_behaviour.py: bafybeiaheuesscgqzwjbpyrezgwpdbdfurlmfwbc462qv6rblwwxlx5dpm pools/balancer.py: bafybeigznhgv7ylo5dvlhxcqikhiuqlqtnx3ikv4tszyvkl2lpcuqgoa5u pools/uniswap.py: bafybeigmqptgmjaxscszohfusgxsexqyx4awuyw7p4g5l7k2qpeyq7vdcu - rounds.py: bafybeifnjkgmv4e6nnr4qutqwooswsvbtrqta7mysdhiub4b4zpbuzvcoq + rounds.py: bafybeieiaolboklb6dexn3ylkj3xvohv55mymth66uko76jrirjnvptufu strategies/simple_strategy.py: bafybeiasu2nchowx6leksjllpuum4ckezxoj4o2m4sstavblplvvutmvzm strategy_behaviour.py: bafybeidk6sorg47kuuubamcccksi65x3txldyo7y2hm5opbye2ghmz2ljy fingerprint_ignore_patterns: [] diff --git a/packages/valory/skills/optimus_abci/skill.yaml b/packages/valory/skills/optimus_abci/skill.yaml index 64fb377..1a8f191 100644 --- a/packages/valory/skills/optimus_abci/skill.yaml +++ b/packages/valory/skills/optimus_abci/skill.yaml @@ -22,7 +22,7 @@ skills: - valory/registration_abci:0.1.0:bafybeiffipsowrqrkhjoexem7ern5ob4fabgif7wa6gtlszcoaop2e3oey - valory/reset_pause_abci:0.1.0:bafybeif4lgvbzsmzljesxbphycdv52ka7qnihyjrjpfaseclxadcmm6yiq - valory/termination_abci:0.1.0:bafybeiekkpo5qef5zaeagm3si6v45qxcojvtjqe4a5ceccvk4q7k3xi3bi -- valory/liquidity_trader_abci:0.1.0:bafybeidnvedww6egshhgfpvac5t274g6aekqxnfqwr3c7qsztnso4zde5a +- valory/liquidity_trader_abci:0.1.0:bafybeies2wtlbei2oajpguwtjvv6ksicdfyfw3alc5bku5aphbdeqtowq4 - valory/transaction_settlement_abci:0.1.0:bafybeielv6eivt2z6nforq43xewl2vmpfwpdu2s2vfogobziljnwsclmlm behaviours: main: From 4a8aea7b40ccb863ea38c5ab448d557f5e03617f Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Thu, 3 Oct 2024 22:23:14 +0530 Subject: [PATCH 5/7] chore: update packages --- packages/packages.json | 8 ++++---- packages/valory/agents/optimus/aea-config.yaml | 4 ++-- packages/valory/services/optimus/service.yaml | 2 +- packages/valory/skills/liquidity_trader_abci/skill.yaml | 2 +- packages/valory/skills/optimus_abci/skill.yaml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/packages.json b/packages/packages.json index 7405af7..2f0379c 100644 --- a/packages/packages.json +++ b/packages/packages.json @@ -8,10 +8,10 @@ "contract/valory/merkl_distributor/0.1.0": "bafybeihaqsvmncuzmwv2r6iuzc5t7ur6ugdhephz7ydftypksjidpsylbq", "contract/valory/staking_token/0.1.0": "bafybeifrvtkofw5c26b3irm6izqfdpik6vpjhm6hqwcdzx333h6vhdanai", "contract/valory/staking_activity_checker/0.1.0": "bafybeibjzsi2r5b6xd4iwl4wbwldptnynryzsdpifym4mkv32ynswx22ou", - "skill/valory/liquidity_trader_abci/0.1.0": "bafybeies2wtlbei2oajpguwtjvv6ksicdfyfw3alc5bku5aphbdeqtowq4", - "skill/valory/optimus_abci/0.1.0": "bafybeidvsbeythm25tizkanglnzey6gxfhjwhgtli2lxlbmhycgntcyqj4", - "agent/valory/optimus/0.1.0": "bafybeidnw6pubtwy3hcv6e757tyvk4arov5co3lqrqiezy2ar4fxnbmr34", - "service/valory/optimus/0.1.0": "bafybeif3oy24t5dhbgel4jgi76qvhebuwmjdqiwrbkgm2bvfanglekbtua" + "skill/valory/liquidity_trader_abci/0.1.0": "bafybeie6wkqi4h64f2h4q7zdcwavljai4uschnbvwgfpojabfjgzjzmjgu", + "skill/valory/optimus_abci/0.1.0": "bafybeielyqgyywy5esyj3kld2wlk6ceuzrd64u7snpab2yy2vdq747kqiq", + "agent/valory/optimus/0.1.0": "bafybeietckasw4kxcpt4ffiuv56vwd5edau7leiohxrfqzjdu7nz7ygib4", + "service/valory/optimus/0.1.0": "bafybeidvgxz6rrspwvhggenax5ak557ufujcrr677w3rrgg76b2yerj5de" }, "third_party": { "protocol/open_aea/signing/1.0.0": "bafybeihv62fim3wl2bayavfcg3u5e5cxu3b7brtu4cn5xoxd6lqwachasi", diff --git a/packages/valory/agents/optimus/aea-config.yaml b/packages/valory/agents/optimus/aea-config.yaml index c0f3917..1a575b1 100644 --- a/packages/valory/agents/optimus/aea-config.yaml +++ b/packages/valory/agents/optimus/aea-config.yaml @@ -35,8 +35,8 @@ protocols: skills: - valory/abstract_abci:0.1.0:bafybeidz54kvxhbdmpruzguuzzq7bjg4pekjb5amqobkxoy4oqknnobopu - valory/abstract_round_abci:0.1.0:bafybeiajjzuh6vf23crp55humonknirvv2f4s3dmdlfzch6tc5ow52pcgm -- valory/liquidity_trader_abci:0.1.0:bafybeies2wtlbei2oajpguwtjvv6ksicdfyfw3alc5bku5aphbdeqtowq4 -- valory/optimus_abci:0.1.0:bafybeidvsbeythm25tizkanglnzey6gxfhjwhgtli2lxlbmhycgntcyqj4 +- valory/liquidity_trader_abci:0.1.0:bafybeie6wkqi4h64f2h4q7zdcwavljai4uschnbvwgfpojabfjgzjzmjgu +- valory/optimus_abci:0.1.0:bafybeielyqgyywy5esyj3kld2wlk6ceuzrd64u7snpab2yy2vdq747kqiq - valory/registration_abci:0.1.0:bafybeiffipsowrqrkhjoexem7ern5ob4fabgif7wa6gtlszcoaop2e3oey - valory/reset_pause_abci:0.1.0:bafybeif4lgvbzsmzljesxbphycdv52ka7qnihyjrjpfaseclxadcmm6yiq - valory/termination_abci:0.1.0:bafybeiekkpo5qef5zaeagm3si6v45qxcojvtjqe4a5ceccvk4q7k3xi3bi diff --git a/packages/valory/services/optimus/service.yaml b/packages/valory/services/optimus/service.yaml index e50e5cd..8abc8be 100644 --- a/packages/valory/services/optimus/service.yaml +++ b/packages/valory/services/optimus/service.yaml @@ -6,7 +6,7 @@ aea_version: '>=1.0.0, <2.0.0' license: Apache-2.0 fingerprint: {} fingerprint_ignore_patterns: [] -agent: valory/optimus:0.1.0:bafybeidnw6pubtwy3hcv6e757tyvk4arov5co3lqrqiezy2ar4fxnbmr34 +agent: valory/optimus:0.1.0:bafybeietckasw4kxcpt4ffiuv56vwd5edau7leiohxrfqzjdu7nz7ygib4 number_of_agents: 1 deployment: {} --- diff --git a/packages/valory/skills/liquidity_trader_abci/skill.yaml b/packages/valory/skills/liquidity_trader_abci/skill.yaml index f03dd57..39118a2 100644 --- a/packages/valory/skills/liquidity_trader_abci/skill.yaml +++ b/packages/valory/skills/liquidity_trader_abci/skill.yaml @@ -7,7 +7,7 @@ license: Apache-2.0 aea_version: '>=1.0.0, <2.0.0' fingerprint: __init__.py: bafybeia7bn2ahqqwkf63ptje6rfnftuwrsp33sswgpcbh5osbesxxr6g4m - behaviours.py: bafybeiayj3gwcsc2yicuyx2vu6vofmiuma7dhr52fwi5emkswv7wvye4x4 + behaviours.py: bafybeicgmylovlho3hbml5r4n337n32qna3tq26ub3ioq66dlqvdxdrdmu dialogues.py: bafybeiay23otskx2go5xhtgdwfw2kd6rxd62sxxdu3njv7hageorl5zxzm fsm_specification.yaml: bafybeiabbiulb7k6xkjysulmy6o4ugnhxlpp5jiaeextvwj65q4ttadoeq handlers.py: bafybeidxw2lvgiifmo4siobpwuwbxscuifrdo3gnkjyn6bgexotj5f7zf4 diff --git a/packages/valory/skills/optimus_abci/skill.yaml b/packages/valory/skills/optimus_abci/skill.yaml index 1a8f191..3050141 100644 --- a/packages/valory/skills/optimus_abci/skill.yaml +++ b/packages/valory/skills/optimus_abci/skill.yaml @@ -22,7 +22,7 @@ skills: - valory/registration_abci:0.1.0:bafybeiffipsowrqrkhjoexem7ern5ob4fabgif7wa6gtlszcoaop2e3oey - valory/reset_pause_abci:0.1.0:bafybeif4lgvbzsmzljesxbphycdv52ka7qnihyjrjpfaseclxadcmm6yiq - valory/termination_abci:0.1.0:bafybeiekkpo5qef5zaeagm3si6v45qxcojvtjqe4a5ceccvk4q7k3xi3bi -- valory/liquidity_trader_abci:0.1.0:bafybeies2wtlbei2oajpguwtjvv6ksicdfyfw3alc5bku5aphbdeqtowq4 +- valory/liquidity_trader_abci:0.1.0:bafybeie6wkqi4h64f2h4q7zdcwavljai4uschnbvwgfpojabfjgzjzmjgu - valory/transaction_settlement_abci:0.1.0:bafybeielv6eivt2z6nforq43xewl2vmpfwpdu2s2vfogobziljnwsclmlm behaviours: main: From 33ef686c64914c94f73eec43aaf7af12c0dbccd2 Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Fri, 4 Oct 2024 13:46:15 +0530 Subject: [PATCH 6/7] fix: resolve rounding off issue --- packages/packages.json | 8 +- .../valory/agents/optimus/aea-config.yaml | 4 +- packages/valory/services/optimus/service.yaml | 2 +- .../liquidity_trader_abci/behaviours.py | 173 +++++++++--------- .../skills/liquidity_trader_abci/skill.yaml | 6 +- .../valory/skills/optimus_abci/skill.yaml | 6 +- 6 files changed, 104 insertions(+), 95 deletions(-) diff --git a/packages/packages.json b/packages/packages.json index 2f0379c..c9006bb 100644 --- a/packages/packages.json +++ b/packages/packages.json @@ -8,10 +8,10 @@ "contract/valory/merkl_distributor/0.1.0": "bafybeihaqsvmncuzmwv2r6iuzc5t7ur6ugdhephz7ydftypksjidpsylbq", "contract/valory/staking_token/0.1.0": "bafybeifrvtkofw5c26b3irm6izqfdpik6vpjhm6hqwcdzx333h6vhdanai", "contract/valory/staking_activity_checker/0.1.0": "bafybeibjzsi2r5b6xd4iwl4wbwldptnynryzsdpifym4mkv32ynswx22ou", - "skill/valory/liquidity_trader_abci/0.1.0": "bafybeie6wkqi4h64f2h4q7zdcwavljai4uschnbvwgfpojabfjgzjzmjgu", - "skill/valory/optimus_abci/0.1.0": "bafybeielyqgyywy5esyj3kld2wlk6ceuzrd64u7snpab2yy2vdq747kqiq", - "agent/valory/optimus/0.1.0": "bafybeietckasw4kxcpt4ffiuv56vwd5edau7leiohxrfqzjdu7nz7ygib4", - "service/valory/optimus/0.1.0": "bafybeidvgxz6rrspwvhggenax5ak557ufujcrr677w3rrgg76b2yerj5de" + "skill/valory/liquidity_trader_abci/0.1.0": "bafybeieijp4dzxzclevlupnic27sv4p6pfuiv6qk6w2fxwgf65bax7waq4", + "skill/valory/optimus_abci/0.1.0": "bafybeieivmuax5dcmbarbfq5fzctjpzqdknl6ki6dgnzpmklhfhxpf2vgy", + "agent/valory/optimus/0.1.0": "bafybeicaucgwqgjstcpu5w5kf2llyddfb7jsofmmz6tbbx4a2hdeymzksq", + "service/valory/optimus/0.1.0": "bafybeiedmpmp2k6q5oarzxzjwgc2ez26b2kez7g3fet7s5rortcibsvoze" }, "third_party": { "protocol/open_aea/signing/1.0.0": "bafybeihv62fim3wl2bayavfcg3u5e5cxu3b7brtu4cn5xoxd6lqwachasi", diff --git a/packages/valory/agents/optimus/aea-config.yaml b/packages/valory/agents/optimus/aea-config.yaml index 1a575b1..1c704b4 100644 --- a/packages/valory/agents/optimus/aea-config.yaml +++ b/packages/valory/agents/optimus/aea-config.yaml @@ -35,8 +35,8 @@ protocols: skills: - valory/abstract_abci:0.1.0:bafybeidz54kvxhbdmpruzguuzzq7bjg4pekjb5amqobkxoy4oqknnobopu - valory/abstract_round_abci:0.1.0:bafybeiajjzuh6vf23crp55humonknirvv2f4s3dmdlfzch6tc5ow52pcgm -- valory/liquidity_trader_abci:0.1.0:bafybeie6wkqi4h64f2h4q7zdcwavljai4uschnbvwgfpojabfjgzjzmjgu -- valory/optimus_abci:0.1.0:bafybeielyqgyywy5esyj3kld2wlk6ceuzrd64u7snpab2yy2vdq747kqiq +- valory/liquidity_trader_abci:0.1.0:bafybeieijp4dzxzclevlupnic27sv4p6pfuiv6qk6w2fxwgf65bax7waq4 +- valory/optimus_abci:0.1.0:bafybeieivmuax5dcmbarbfq5fzctjpzqdknl6ki6dgnzpmklhfhxpf2vgy - valory/registration_abci:0.1.0:bafybeiffipsowrqrkhjoexem7ern5ob4fabgif7wa6gtlszcoaop2e3oey - valory/reset_pause_abci:0.1.0:bafybeif4lgvbzsmzljesxbphycdv52ka7qnihyjrjpfaseclxadcmm6yiq - valory/termination_abci:0.1.0:bafybeiekkpo5qef5zaeagm3si6v45qxcojvtjqe4a5ceccvk4q7k3xi3bi diff --git a/packages/valory/services/optimus/service.yaml b/packages/valory/services/optimus/service.yaml index 8abc8be..b151eac 100644 --- a/packages/valory/services/optimus/service.yaml +++ b/packages/valory/services/optimus/service.yaml @@ -6,7 +6,7 @@ aea_version: '>=1.0.0, <2.0.0' license: Apache-2.0 fingerprint: {} fingerprint_ignore_patterns: [] -agent: valory/optimus:0.1.0:bafybeietckasw4kxcpt4ffiuv56vwd5edau7leiohxrfqzjdu7nz7ygib4 +agent: valory/optimus:0.1.0:bafybeicaucgwqgjstcpu5w5kf2llyddfb7jsofmmz6tbbx4a2hdeymzksq number_of_agents: 1 deployment: {} --- diff --git a/packages/valory/skills/liquidity_trader_abci/behaviours.py b/packages/valory/skills/liquidity_trader_abci/behaviours.py index 4cef817..18d6a47 100644 --- a/packages/valory/skills/liquidity_trader_abci/behaviours.py +++ b/packages/valory/skills/liquidity_trader_abci/behaviours.py @@ -365,9 +365,6 @@ def _get_balance( self, chain: str, token: str, positions: Optional[List[Dict[str, Any]]] = None ) -> Optional[int]: """Get balance""" - if not positions: - positions = self.synchronized_data.positions - for position in positions: if position.get("chain") == chain: for asset in position.get("assets", {}): @@ -376,6 +373,20 @@ def _get_balance( return None + def _get_token_decimals( + self, chain: str, asset_address: str + ) -> Generator[None, None, Optional[int]]: + """Get token decimals""" + decimals = yield from self.contract_interact( + performative=ContractApiMessage.Performative.GET_RAW_TRANSACTION, + contract_address=asset_address, + contract_public_id=ERC20.contract_id, + contract_callable="get_token_decimals", + data_key="data", + chain_id=chain, + ) + return decimals + def _store_data(self, data: Any, attribute: str, filepath: str) -> None: """Generic method to store data as JSON.""" if data is None: @@ -1454,20 +1465,6 @@ def _request_with_retries( self.context.logger.error(f"Request failed after {retries} retries.") return False, response_json - def _get_token_decimals( - self, chain: str, asset_address: str - ) -> Generator[None, None, Optional[int]]: - """Get token decimals""" - decimals = yield from self.contract_interact( - performative=ContractApiMessage.Performative.GET_RAW_TRANSACTION, - contract_address=asset_address, - contract_public_id=ERC20.contract_id, - contract_callable="get_token_decimals", - data_key="data", - chain_id=chain, - ) - return decimals - def _get_exit_pool_tokens(self) -> Generator[None, None, Optional[List[Any]]]: """Get exit pool tokens""" @@ -1810,15 +1807,18 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict]]: self.context.logger.info("No actions to prepare") return Event.DONE.value, {}, {} + positions = self.synchronized_data.positions + last_round_id = self.context.state.round_sequence._abci_app._previous_rounds[ + -1 + ].round_id + if last_round_id != EvaluateStrategyRound.auto_round_id(): + positions = yield from self.get_positions() + last_executed_action_index = self.synchronized_data.last_executed_action_index current_action_index = ( 0 if last_executed_action_index is None else last_executed_action_index + 1 ) - last_round_id = self.context.state.round_sequence._abci_app._previous_rounds[ - -1 - ].round_id - if ( self.synchronized_data.last_action == Action.EXECUTE_STEP.value and last_round_id != DecisionMakingRound.auto_round_id() @@ -1850,7 +1850,7 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict]]: Action.SWITCH_ROUTE.value, ] ): - res = yield from self._process_route_execution() + res = yield from self._process_route_execution(positions) return res if current_action_index >= len(self.synchronized_data.actions): @@ -1858,7 +1858,7 @@ def get_next_event(self) -> Generator[None, None, Tuple[str, Dict]]: return Event.DONE.value, {}, {} res = yield from self._prepare_next_action( - actions, current_action_index, last_round_id + positions, actions, current_action_index, last_round_id ) return res @@ -1975,7 +1975,7 @@ def _post_execute_claim_rewards( } def _process_route_execution( - self, + self, positions ) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: """Handle route execution.""" routes = self.synchronized_data.routes @@ -2015,25 +2015,19 @@ def _process_route_execution( } res = yield from self._execute_route_step( - routes, to_execute_route_index, to_execute_step_index + positions, routes, to_execute_route_index, to_execute_step_index ) return res def _execute_route_step( - self, routes, to_execute_route_index, to_execute_step_index + self, positions, routes, to_execute_route_index, to_execute_step_index ) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: """Execute a step in the route.""" steps = routes[to_execute_route_index].get("steps") step = steps[to_execute_step_index] - total_fee = self.synchronized_data.fee_details.get("total_fee") - total_gas_cost = self.synchronized_data.fee_details.get("total_gas_cost") - remaining_fee_allowance = self.synchronized_data.fee_details.get( - "remaining_fee_allowance" - ) - remaining_gas_allowance = self.synchronized_data.fee_details.get( - "remaining_gas_allowance" - ) + remaining_fee_allowance = 0 + remaining_gas_allowance = 0 if to_execute_step_index == 0: ( @@ -2056,6 +2050,14 @@ def _execute_route_step( remaining_fee_allowance = total_fee remaining_gas_allowance = total_gas_cost + else: + remaining_fee_allowance = self.synchronized_data.fee_details.get( + "remaining_fee_allowance" + ) + remaining_gas_allowance = self.synchronized_data.fee_details.get( + "remaining_gas_allowance" + ) + step_profitable, step_data = yield from self.check_step_costs( step, remaining_fee_allowance, @@ -2071,7 +2073,7 @@ def _execute_route_step( f"to {step_data.get('target_token_symbol')}({step_data.get('to_chain')}) using tool {step_data.get('tool')}" ) bridge_swap_action = yield from self.prepare_bridge_swap_action( - step_data, remaining_fee_allowance, remaining_gas_allowance + positions, step_data, remaining_fee_allowance, remaining_gas_allowance ) if not bridge_swap_action: return self._handle_failed_step( @@ -2123,13 +2125,9 @@ def _handle_failed_step( } def _prepare_next_action( - self, actions, current_action_index, last_round_id + self, positions, actions, current_action_index, last_round_id ) -> Generator[None, None, Tuple[Optional[str], Optional[Dict]]]: """Prepare the next action.""" - positions = self.synchronized_data.positions - if last_round_id != EvaluateStrategyRound.auto_round_id(): - positions = yield from self.get_positions() - next_action = Action(actions[current_action_index].get("action")) next_action_details = self.synchronized_data.actions[current_action_index] self.context.logger.info(f"ACTION DETAILS: {next_action_details}") @@ -2537,12 +2535,13 @@ def get_exit_pool_tx_hash( def prepare_bridge_swap_action( self, + positions: List[Dict[str, Any]], tx_info: Dict[str, Any], remaining_fee_allowance: float, remaining_gas_allowance: float, ) -> Generator[None, None, Optional[Dict]]: """Prepares the bridge swap action""" - multisend_tx_hash = yield from self._build_multisend_tx(tx_info) + multisend_tx_hash = yield from self._build_multisend_tx(positions, tx_info) if not multisend_tx_hash: return None @@ -2647,12 +2646,14 @@ def check_step_costs( step_fee = step_data.get("fee", 0) step_gas_cost = step_data.get("gas_cost", 0) + TOLERANCE = 0.02 + if total_steps != 1 and step_index == total_steps - 1: # For the last step, ensure it is not more than 50% of the remaining fee and gas allowance - if math.ceil(step_fee) > math.ceil( - MAX_STEP_COST_RATIO * remaining_fee_allowance - ) or math.ceil(step_gas_cost) > math.ceil( - MAX_STEP_COST_RATIO * remaining_gas_allowance + if ( + step_fee > MAX_STEP_COST_RATIO * remaining_fee_allowance + TOLERANCE + or step_gas_cost + > MAX_STEP_COST_RATIO * remaining_gas_allowance + TOLERANCE ): self.context.logger.error( f"Step exceeds 50% of the remaining fee or gas allowance. " @@ -2662,9 +2663,10 @@ def check_step_costs( return False, None else: - if math.ceil(step_fee) > math.ceil(remaining_fee_allowance) or math.ceil( - step_gas_cost - ) > math.ceil(remaining_gas_allowance): + if ( + step_fee > remaining_fee_allowance + TOLERANCE + or step_gas_cost > remaining_gas_allowance + TOLERANCE + ): self.context.logger.error( f"Step exceeds remaining fee or gas allowance. " f"Step fee: {step_fee}, Remaining fee allowance: {remaining_fee_allowance}, " @@ -2711,12 +2713,18 @@ def _build_safe_tx( payload_string = hash_payload_to_hex(**tx_params) return payload_string - def _build_multisend_tx(self, tx_info) -> Generator[None, None, Optional[str]]: + def _build_multisend_tx( + self, positions, tx_info + ) -> Generator[None, None, Optional[str]]: multisend_txs = [] + amount = self._get_balance( + tx_info.get("from_chain"), tx_info.get("source_token"), positions + ) + if tx_info.get("source_token") != ZERO_ADDRESS: approval_tx_payload = yield from self.get_approval_tx_hash( token_address=tx_info.get("source_token"), - amount=tx_info.get("amount"), + amount=amount, spender=tx_info.get("lifi_contract_address"), chain=tx_info.get("from_chain"), ) @@ -2730,11 +2738,7 @@ def _build_multisend_tx(self, tx_info) -> Generator[None, None, Optional[str]]: { "operation": MultiSendOperation.CALL, "to": tx_info.get("lifi_contract_address"), - "value": ( - 0 - if tx_info.get("source_token") != ZERO_ADDRESS - else tx_info.get("amount") - ), + "value": (0 if tx_info.get("source_token") != ZERO_ADDRESS else amount), "data": tx_info.get("tx_hash"), } ) @@ -2888,6 +2892,17 @@ def fetch_routes( self, positions, action ) -> Generator[None, None, Optional[List[Any]]]: """Get transaction data for route from LiFi API""" + + def round_down_amount(amount: int, decimals: int) -> int: + """Round down the amount to the nearest round_factor to avoid API rounding issues.""" + if decimals == 18: + # For tokens like ETH/WETH with 18 decimals, round to nearest 1000 wei + round_factor = 1000 + rounded_amount = (amount // round_factor) * round_factor + return rounded_amount + else: + return amount + from_chain = action.get("from_chain") to_chain = action.get("to_chain") from_chain_id = self.params.chain_to_chain_id_mapping.get(from_chain) @@ -2898,10 +2913,15 @@ def fetch_routes( to_token_symbol = action.get("to_token_symbol") allow_switch_chain = True slippage = self.params.slippage_for_swap - amount = self._get_balance(from_chain, from_token_address, positions) from_address = self.params.safe_contract_addresses.get(from_chain) to_address = self.params.safe_contract_addresses.get(to_chain) + amount = self._get_balance(from_chain, from_token_address, positions) + token_decimals = yield from self._get_token_decimals( + from_chain, from_token_address + ) + + amount = round_down_amount(amount, token_decimals) # TO:DO - Add logic to maintain a list of blacklisted bridges params = { "fromAddress": from_address, @@ -2995,30 +3015,16 @@ def _simulate_transaction( } api_url = url_template.format(**values) - ledger_api_response = ( - ledger_api_response - ) = yield from self.get_ledger_api_response( - performative=LedgerApiMessage.Performative.GET_STATE, # type: ignore - ledger_callable="get_block", - block_identifier="latest", - chain_id="base", - ) - - if ledger_api_response.performative != LedgerApiMessage.Performative.STATE: - self.context.logger.error( - f"Could not fetch the latest blocok number: {ledger_api_response}" - ) - return None - - latest_block = int(ledger_api_response.state.body["number"]) - body = { - "network_id": self.params.chain_to_chain_id_mapping.get(chain), - "block_number": latest_block, - "from": self.context.agent_address, - "to": safe_address, - "simulation_type": "quick", - "data": tx_data, + "simulations": [ + { + "network_id": self.params.chain_to_chain_id_mapping.get(chain), + "from": self.context.agent_address, + "to": safe_address, + "simulation_type": "quick", + "input": tx_data, + } + ] } response = yield from self.get_http_response( @@ -3040,10 +3046,13 @@ def _simulate_transaction( try: data = json.loads(response.body) if data: - simulation = data.get("simulation", {}) + simulation_results = data.get("simulation_results", []) status = False - if isinstance(simulation, Dict): - status = simulation.get("status", False) + if simulation_results: + for simulation_result in simulation_results: + simulation = simulation_result.get("simulation", {}) + if isinstance(simulation, Dict): + status = simulation.get("status", False) return status except (ValueError, TypeError) as e: diff --git a/packages/valory/skills/liquidity_trader_abci/skill.yaml b/packages/valory/skills/liquidity_trader_abci/skill.yaml index 39118a2..599c728 100644 --- a/packages/valory/skills/liquidity_trader_abci/skill.yaml +++ b/packages/valory/skills/liquidity_trader_abci/skill.yaml @@ -7,7 +7,7 @@ license: Apache-2.0 aea_version: '>=1.0.0, <2.0.0' fingerprint: __init__.py: bafybeia7bn2ahqqwkf63ptje6rfnftuwrsp33sswgpcbh5osbesxxr6g4m - behaviours.py: bafybeicgmylovlho3hbml5r4n337n32qna3tq26ub3ioq66dlqvdxdrdmu + behaviours.py: bafybeigznu3vzjzmptgv2myijgp6qxdij3trabwlrqwj5hqyzrmtfngleu dialogues.py: bafybeiay23otskx2go5xhtgdwfw2kd6rxd62sxxdu3njv7hageorl5zxzm fsm_specification.yaml: bafybeiabbiulb7k6xkjysulmy6o4ugnhxlpp5jiaeextvwj65q4ttadoeq handlers.py: bafybeidxw2lvgiifmo4siobpwuwbxscuifrdo3gnkjyn6bgexotj5f7zf4 @@ -171,7 +171,7 @@ models: intermediate_tokens: '{"ethereum":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2":{"symbol":"WETH","liquidity_provider":"0xF04a5cC80B1E94C69B48f5ee68a08CD2F09A7c3E"},"0xdAC17F958D2ee523a2206206994597C13D831ec7":{"symbol":"USDT","liquidity_provider":"0xcEe284F754E854890e311e3280b767F80797180d"},"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48":{"symbol":"USDC","liquidity_provider":"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"},"0x6B175474E89094C44Da98b954EedeAC495271d0F":{"symbol":"DAI","liquidity_provider":"0x517F9dD285e75b599234F7221227339478d0FcC8"},"0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84":{"symbol":"stETH","liquidity_provider":"0x4028DAAC072e492d34a3Afdbef0ba7e35D8b55C4"},"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0":{"symbol":"wstETH","liquidity_provider":"0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa"},"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599":{"symbol":"WBTC","liquidity_provider":"0xCBCdF9626bC03E24f779434178A73a0B4bad62eD"},"0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984":{"symbol":"UNI","liquidity_provider":"0x1d42064Fc4Beb5F8aAF85F4617AE8b3b5B8Bd801"}},"optimism":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0x4200000000000000000000000000000000000006"},"0x7F5c764cBc14f9669B88837ca1490cCa17c31607":{"symbol":"USDC.e","liquidity_provider":"0xD1F1baD4c9E6c44DeC1e9bF3B94902205c5Cd6C3"},"0x4200000000000000000000000000000000000006":{"symbol":"WETH","liquidity_provider":"0xBA12222222228d8Ba445958a75a0704d566BF2C8"},"0x94b008aA00579c1307B0EF2c499aD98a8ce58e58":{"symbol":"USDT","liquidity_provider":"0xA73C628eaf6e283E26A7b1f8001CF186aa4c0E8E"},"0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1":{"symbol":"DAI","liquidity_provider":"0x03aF20bDAaFfB4cC0A521796a223f7D85e2aAc31"},"0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb":{"symbol":"wstETH","liquidity_provider":"0x04F6C85A1B00F6D9B75f91FD23835974Cc07E65c"},"0x68f180fcCe6836688e9084f035309E29Bf0A2095":{"symbol":"WBTC","liquidity_provider":"0x078f358208685046a11C85e8ad32895DED33A249"},"0x76FB31fb4af56892A25e32cFC43De717950c9278":{"symbol":"AAVE","liquidity_provider":"0xf329e36C7bF6E5E86ce2150875a84Ce77f477375"},"0x4200000000000000000000000000000000000042":{"symbol":"OP","liquidity_provider":"0x2A82Ae142b2e62Cb7D10b55E323ACB1Cab663a26"}},"base":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0xd0b53D9277642d899DF5C87A3966A349A798F224"},"0x4200000000000000000000000000000000000006":{"symbol":"WETH","liquidity_provider":"0xBA12222222228d8Ba445958a75a0704d566BF2C8"},"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913":{"symbol":"USDC","liquidity_provider":"0x0B0A5886664376F59C351ba3f598C8A8B4D0A6f3"},"0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA":{"symbol":"USDbC","liquidity_provider":"0x0B25c51637c43decd6CC1C1e3da4518D54ddb528"},"0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb":{"symbol":"DAI","liquidity_provider":"0x927860797d07b1C46fbBe7f6f73D45C7E1BFBb27"},"0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452":{"symbol":"wstETH","liquidity_provider":"0x99CBC45ea5bb7eF3a5BC08FB1B7E56bB2442Ef0D"},"0xB6fe221Fe9EeF5aBa221c348bA20A1Bf5e73624c":{"symbol":"rETH","liquidity_provider":"0x95Fa1ddc9a78273f795e67AbE8f1Cd2Cd39831fF"},"0x532f27101965dd16442E59d40670FaF5eBB142E4":{"symbol":"BRETT","liquidity_provider":"0xBA3F945812a83471d709BCe9C3CA699A19FB46f7"}}}' lifi_fetch_tools_url: https://li.quest/v1/tools merkl_user_rewards_url: https://api.merkl.xyz/v3/userRewards - tenderly_bundle_simulation_url: https://api.tenderly.co/api/v1/account/{tenderly_account_slug}/project/{tenderly_project_slug}/simulate + tenderly_bundle_simulation_url: https://api.tenderly.co/api/v1/account/{tenderly_account_slug}/project/{tenderly_project_slug}/simulate-bundle tenderly_access_key: access_key tenderly_account_slug: account_slug tenderly_project_slug: project_slug @@ -183,7 +183,7 @@ models: assets_info_filename: assets.json pool_info_filename: current_pool.json merkl_fetch_campaigns_args: '{"url":"https://api.merkl.xyz/v3/campaigns","creator":"","live":"true"}' - min_swap_amount_threshold: 5 + min_swap_amount_threshold: 10 max_fee_percentage: 0.02 max_gas_percentage: 0.1 balancer_graphql_endpoints: '{"optimism":"https://api.studio.thegraph.com/query/75376/balancer-optimism-v2/version/latest","base":"https://api.studio.thegraph.com/query/24660/balancer-base-v2/version/latest"}' diff --git a/packages/valory/skills/optimus_abci/skill.yaml b/packages/valory/skills/optimus_abci/skill.yaml index 3050141..1fab6c2 100644 --- a/packages/valory/skills/optimus_abci/skill.yaml +++ b/packages/valory/skills/optimus_abci/skill.yaml @@ -22,7 +22,7 @@ skills: - valory/registration_abci:0.1.0:bafybeiffipsowrqrkhjoexem7ern5ob4fabgif7wa6gtlszcoaop2e3oey - valory/reset_pause_abci:0.1.0:bafybeif4lgvbzsmzljesxbphycdv52ka7qnihyjrjpfaseclxadcmm6yiq - valory/termination_abci:0.1.0:bafybeiekkpo5qef5zaeagm3si6v45qxcojvtjqe4a5ceccvk4q7k3xi3bi -- valory/liquidity_trader_abci:0.1.0:bafybeie6wkqi4h64f2h4q7zdcwavljai4uschnbvwgfpojabfjgzjzmjgu +- valory/liquidity_trader_abci:0.1.0:bafybeieijp4dzxzclevlupnic27sv4p6pfuiv6qk6w2fxwgf65bax7waq4 - valory/transaction_settlement_abci:0.1.0:bafybeielv6eivt2z6nforq43xewl2vmpfwpdu2s2vfogobziljnwsclmlm behaviours: main: @@ -161,7 +161,7 @@ models: intermediate_tokens: '{"ethereum":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"},"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2":{"symbol":"WETH","liquidity_provider":"0xF04a5cC80B1E94C69B48f5ee68a08CD2F09A7c3E"},"0xdAC17F958D2ee523a2206206994597C13D831ec7":{"symbol":"USDT","liquidity_provider":"0xcEe284F754E854890e311e3280b767F80797180d"},"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48":{"symbol":"USDC","liquidity_provider":"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"},"0x6B175474E89094C44Da98b954EedeAC495271d0F":{"symbol":"DAI","liquidity_provider":"0x517F9dD285e75b599234F7221227339478d0FcC8"},"0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84":{"symbol":"stETH","liquidity_provider":"0x4028DAAC072e492d34a3Afdbef0ba7e35D8b55C4"},"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0":{"symbol":"wstETH","liquidity_provider":"0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa"},"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599":{"symbol":"WBTC","liquidity_provider":"0xCBCdF9626bC03E24f779434178A73a0B4bad62eD"},"0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984":{"symbol":"UNI","liquidity_provider":"0x1d42064Fc4Beb5F8aAF85F4617AE8b3b5B8Bd801"}},"optimism":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0x4200000000000000000000000000000000000006"},"0x7F5c764cBc14f9669B88837ca1490cCa17c31607":{"symbol":"USDC.e","liquidity_provider":"0xD1F1baD4c9E6c44DeC1e9bF3B94902205c5Cd6C3"},"0x4200000000000000000000000000000000000006":{"symbol":"WETH","liquidity_provider":"0xBA12222222228d8Ba445958a75a0704d566BF2C8"},"0x94b008aA00579c1307B0EF2c499aD98a8ce58e58":{"symbol":"USDT","liquidity_provider":"0xA73C628eaf6e283E26A7b1f8001CF186aa4c0E8E"},"0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1":{"symbol":"DAI","liquidity_provider":"0x03aF20bDAaFfB4cC0A521796a223f7D85e2aAc31"},"0x1F32b1c2345538c0c6f582fCB022739c4A194Ebb":{"symbol":"wstETH","liquidity_provider":"0x04F6C85A1B00F6D9B75f91FD23835974Cc07E65c"},"0x68f180fcCe6836688e9084f035309E29Bf0A2095":{"symbol":"WBTC","liquidity_provider":"0x078f358208685046a11C85e8ad32895DED33A249"},"0x76FB31fb4af56892A25e32cFC43De717950c9278":{"symbol":"AAVE","liquidity_provider":"0xf329e36C7bF6E5E86ce2150875a84Ce77f477375"},"0x4200000000000000000000000000000000000042":{"symbol":"OP","liquidity_provider":"0x2A82Ae142b2e62Cb7D10b55E323ACB1Cab663a26"}},"base":{"0x0000000000000000000000000000000000000000":{"symbol":"ETH","liquidity_provider":"0xd0b53D9277642d899DF5C87A3966A349A798F224"},"0x4200000000000000000000000000000000000006":{"symbol":"WETH","liquidity_provider":"0xBA12222222228d8Ba445958a75a0704d566BF2C8"},"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913":{"symbol":"USDC","liquidity_provider":"0x0B0A5886664376F59C351ba3f598C8A8B4D0A6f3"},"0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA":{"symbol":"USDbC","liquidity_provider":"0x0B25c51637c43decd6CC1C1e3da4518D54ddb528"},"0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb":{"symbol":"DAI","liquidity_provider":"0x927860797d07b1C46fbBe7f6f73D45C7E1BFBb27"},"0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452":{"symbol":"wstETH","liquidity_provider":"0x99CBC45ea5bb7eF3a5BC08FB1B7E56bB2442Ef0D"},"0xB6fe221Fe9EeF5aBa221c348bA20A1Bf5e73624c":{"symbol":"rETH","liquidity_provider":"0x95Fa1ddc9a78273f795e67AbE8f1Cd2Cd39831fF"},"0x532f27101965dd16442E59d40670FaF5eBB142E4":{"symbol":"BRETT","liquidity_provider":"0xBA3F945812a83471d709BCe9C3CA699A19FB46f7"}}}' lifi_fetch_tools_url: https://li.quest/v1/tools merkl_user_rewards_url: https://api.merkl.xyz/v3/userRewards - tenderly_bundle_simulation_url: https://api.tenderly.co/api/v1/account/{tenderly_account_slug}/project/{tenderly_project_slug}/simulate + tenderly_bundle_simulation_url: https://api.tenderly.co/api/v1/account/{tenderly_account_slug}/project/{tenderly_project_slug}/simulate-bundle tenderly_access_key: access_key tenderly_account_slug: account_slug tenderly_project_slug: project_slug @@ -172,7 +172,7 @@ models: store_path: data assets_info_filename: assets.json pool_info_filename: current_pool.json - min_swap_amount_threshold: 5 + min_swap_amount_threshold: 10 max_fee_percentage: 0.02 max_gas_percentage: 0.1 merkl_fetch_campaigns_args: '{"url":"https://api.merkl.xyz/v3/campaigns","creator":"","live":"true"}' From ab03c744511b0180e22d51fe01c3531d68488890 Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Fri, 4 Oct 2024 14:42:02 +0530 Subject: [PATCH 7/7] fix: handle none case --- packages/packages.json | 8 ++++---- packages/valory/agents/optimus/aea-config.yaml | 4 ++-- packages/valory/services/optimus/service.yaml | 2 +- packages/valory/skills/liquidity_trader_abci/rounds.py | 6 ++++++ packages/valory/skills/liquidity_trader_abci/skill.yaml | 2 +- packages/valory/skills/optimus_abci/skill.yaml | 2 +- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/packages.json b/packages/packages.json index c9006bb..df21a4d 100644 --- a/packages/packages.json +++ b/packages/packages.json @@ -8,10 +8,10 @@ "contract/valory/merkl_distributor/0.1.0": "bafybeihaqsvmncuzmwv2r6iuzc5t7ur6ugdhephz7ydftypksjidpsylbq", "contract/valory/staking_token/0.1.0": "bafybeifrvtkofw5c26b3irm6izqfdpik6vpjhm6hqwcdzx333h6vhdanai", "contract/valory/staking_activity_checker/0.1.0": "bafybeibjzsi2r5b6xd4iwl4wbwldptnynryzsdpifym4mkv32ynswx22ou", - "skill/valory/liquidity_trader_abci/0.1.0": "bafybeieijp4dzxzclevlupnic27sv4p6pfuiv6qk6w2fxwgf65bax7waq4", - "skill/valory/optimus_abci/0.1.0": "bafybeieivmuax5dcmbarbfq5fzctjpzqdknl6ki6dgnzpmklhfhxpf2vgy", - "agent/valory/optimus/0.1.0": "bafybeicaucgwqgjstcpu5w5kf2llyddfb7jsofmmz6tbbx4a2hdeymzksq", - "service/valory/optimus/0.1.0": "bafybeiedmpmp2k6q5oarzxzjwgc2ez26b2kez7g3fet7s5rortcibsvoze" + "skill/valory/liquidity_trader_abci/0.1.0": "bafybeife34jpiod7ydz75yxceqyl4v7oi7ldj7ecgglvumqjisql3mmsua", + "skill/valory/optimus_abci/0.1.0": "bafybeia6efoc5br3ylbylmnvqjgovtjnlta3ews5fsw4rxqpwmmdinl3se", + "agent/valory/optimus/0.1.0": "bafybeia235zj6y4bpr6lq36t6zqdo2jabo7muvtknriyx7clbqjrqdk2ne", + "service/valory/optimus/0.1.0": "bafybeibn44yqjs7f2inqhmcl3jginx7xdgkk7lf4gduatqval3hpqkz6li" }, "third_party": { "protocol/open_aea/signing/1.0.0": "bafybeihv62fim3wl2bayavfcg3u5e5cxu3b7brtu4cn5xoxd6lqwachasi", diff --git a/packages/valory/agents/optimus/aea-config.yaml b/packages/valory/agents/optimus/aea-config.yaml index 1c704b4..27f4368 100644 --- a/packages/valory/agents/optimus/aea-config.yaml +++ b/packages/valory/agents/optimus/aea-config.yaml @@ -35,8 +35,8 @@ protocols: skills: - valory/abstract_abci:0.1.0:bafybeidz54kvxhbdmpruzguuzzq7bjg4pekjb5amqobkxoy4oqknnobopu - valory/abstract_round_abci:0.1.0:bafybeiajjzuh6vf23crp55humonknirvv2f4s3dmdlfzch6tc5ow52pcgm -- valory/liquidity_trader_abci:0.1.0:bafybeieijp4dzxzclevlupnic27sv4p6pfuiv6qk6w2fxwgf65bax7waq4 -- valory/optimus_abci:0.1.0:bafybeieivmuax5dcmbarbfq5fzctjpzqdknl6ki6dgnzpmklhfhxpf2vgy +- valory/liquidity_trader_abci:0.1.0:bafybeife34jpiod7ydz75yxceqyl4v7oi7ldj7ecgglvumqjisql3mmsua +- valory/optimus_abci:0.1.0:bafybeia6efoc5br3ylbylmnvqjgovtjnlta3ews5fsw4rxqpwmmdinl3se - valory/registration_abci:0.1.0:bafybeiffipsowrqrkhjoexem7ern5ob4fabgif7wa6gtlszcoaop2e3oey - valory/reset_pause_abci:0.1.0:bafybeif4lgvbzsmzljesxbphycdv52ka7qnihyjrjpfaseclxadcmm6yiq - valory/termination_abci:0.1.0:bafybeiekkpo5qef5zaeagm3si6v45qxcojvtjqe4a5ceccvk4q7k3xi3bi diff --git a/packages/valory/services/optimus/service.yaml b/packages/valory/services/optimus/service.yaml index b151eac..a528e5e 100644 --- a/packages/valory/services/optimus/service.yaml +++ b/packages/valory/services/optimus/service.yaml @@ -6,7 +6,7 @@ aea_version: '>=1.0.0, <2.0.0' license: Apache-2.0 fingerprint: {} fingerprint_ignore_patterns: [] -agent: valory/optimus:0.1.0:bafybeicaucgwqgjstcpu5w5kf2llyddfb7jsofmmz6tbbx4a2hdeymzksq +agent: valory/optimus:0.1.0:bafybeia235zj6y4bpr6lq36t6zqdo2jabo7muvtknriyx7clbqjrqdk2ne number_of_agents: 1 deployment: {} --- diff --git a/packages/valory/skills/liquidity_trader_abci/rounds.py b/packages/valory/skills/liquidity_trader_abci/rounds.py index 34f7ce0..a4d9d2a 100644 --- a/packages/valory/skills/liquidity_trader_abci/rounds.py +++ b/packages/valory/skills/liquidity_trader_abci/rounds.py @@ -108,6 +108,8 @@ def participant_to_positions_round(self) -> DeserializedCollection: def positions(self) -> List[Dict[str, Any]]: """Get the positions.""" serialized = self.db.get("positions", "[]") + if serialized is None: + serialized = "[]" positions = json.loads(serialized) return positions @@ -120,6 +122,8 @@ def participant_to_actions_round(self) -> DeserializedCollection: def actions(self) -> Optional[List[Dict[str, Any]]]: """Get the actions""" serialized = self.db.get("actions", "[]") + if serialized is None: + serialized = "[]" actions = json.loads(serialized) return actions @@ -192,6 +196,8 @@ def routes_retry_attempt(self) -> Optional[int]: def routes(self) -> Optional[List[Dict[str, Any]]]: """Get the routes""" serialized = self.db.get("routes", "[]") + if serialized is None: + serialized = "[]" routes = json.loads(serialized) return routes diff --git a/packages/valory/skills/liquidity_trader_abci/skill.yaml b/packages/valory/skills/liquidity_trader_abci/skill.yaml index 599c728..c408364 100644 --- a/packages/valory/skills/liquidity_trader_abci/skill.yaml +++ b/packages/valory/skills/liquidity_trader_abci/skill.yaml @@ -16,7 +16,7 @@ fingerprint: pool_behaviour.py: bafybeiaheuesscgqzwjbpyrezgwpdbdfurlmfwbc462qv6rblwwxlx5dpm pools/balancer.py: bafybeigznhgv7ylo5dvlhxcqikhiuqlqtnx3ikv4tszyvkl2lpcuqgoa5u pools/uniswap.py: bafybeigmqptgmjaxscszohfusgxsexqyx4awuyw7p4g5l7k2qpeyq7vdcu - rounds.py: bafybeieiaolboklb6dexn3ylkj3xvohv55mymth66uko76jrirjnvptufu + rounds.py: bafybeibgilm4ld2bvopruehepfcjn4xse7be62tszt74begusmpzrk2thu strategies/simple_strategy.py: bafybeiasu2nchowx6leksjllpuum4ckezxoj4o2m4sstavblplvvutmvzm strategy_behaviour.py: bafybeidk6sorg47kuuubamcccksi65x3txldyo7y2hm5opbye2ghmz2ljy fingerprint_ignore_patterns: [] diff --git a/packages/valory/skills/optimus_abci/skill.yaml b/packages/valory/skills/optimus_abci/skill.yaml index 1fab6c2..592a787 100644 --- a/packages/valory/skills/optimus_abci/skill.yaml +++ b/packages/valory/skills/optimus_abci/skill.yaml @@ -22,7 +22,7 @@ skills: - valory/registration_abci:0.1.0:bafybeiffipsowrqrkhjoexem7ern5ob4fabgif7wa6gtlszcoaop2e3oey - valory/reset_pause_abci:0.1.0:bafybeif4lgvbzsmzljesxbphycdv52ka7qnihyjrjpfaseclxadcmm6yiq - valory/termination_abci:0.1.0:bafybeiekkpo5qef5zaeagm3si6v45qxcojvtjqe4a5ceccvk4q7k3xi3bi -- valory/liquidity_trader_abci:0.1.0:bafybeieijp4dzxzclevlupnic27sv4p6pfuiv6qk6w2fxwgf65bax7waq4 +- valory/liquidity_trader_abci:0.1.0:bafybeife34jpiod7ydz75yxceqyl4v7oi7ldj7ecgglvumqjisql3mmsua - valory/transaction_settlement_abci:0.1.0:bafybeielv6eivt2z6nforq43xewl2vmpfwpdu2s2vfogobziljnwsclmlm behaviours: main: