diff --git a/.github/workflows/merge_rules.yaml b/.github/workflows/merge_rules.yaml new file mode 100644 index 000000000..187009101 --- /dev/null +++ b/.github/workflows/merge_rules.yaml @@ -0,0 +1,14 @@ +name: 'Check Branch' + +on: + pull_request: + +jobs: + check_branch: + runs-on: ubuntu-latest + steps: + - name: Check branch + if: github.base_ref == 'main' && github.head_ref != 'develop' + run: | + echo "ERROR: You can only merge to main from develop." + exit 1 diff --git a/packages/packages.json b/packages/packages.json index 71c5ff244..4ab7b8690 100644 --- a/packages/packages.json +++ b/packages/packages.json @@ -5,7 +5,7 @@ "custom/jhehemann/kelly_criterion/0.1.0": "bafybeif55cu7cf6znyma7kxus4wxa2doarhau2xmndo57iegshxorivwmq", "custom/w1kke/always_blue/0.1.0": "bafybeieshu32h3es2fslduuhr7nimuvh2vuibyeqdunzrcggaeohekg3jm", "custom/valory/kelly_criterion_no_conf/0.1.0": "bafybeibxfp27rzrfnp7sxq62vwv32pdvrijxi7vzg7ihukkaka3bwzrgae", - "contract/valory/market_maker/0.1.0": "bafybeihrz4q5ia7jnflsv2myg6kkung2dfiogqrevuy7xlmqws7tr27rdi", + "contract/valory/market_maker/0.1.0": "bafybeibevdc5trbi2qgt2tvwbsr2h5xvonfhcjwfmozftzvef575fdvbjq", "contract/valory/realitio/0.1.0": "bafybeietgux6kkhdquspy35qera7gjwwqwrremmoeatjzwwokjb2lzsata", "contract/valory/realitio_proxy/0.1.0": "bafybeidx37xzjjmapwacedgzhum6grfzhp5vhouz4zu3pvpgdy5pgb2fr4", "contract/valory/conditional_tokens/0.1.0": "bafybeibnzmqmeph4cj5vfh3s622mo2o5627vjjwc6bptrhj4dk65mzgvhe", @@ -15,15 +15,15 @@ "contract/valory/mech_activity/0.1.0": "bafybeibmqmle5fnal3gxlpdmcos2kogzra4q3pr3o5nh7shplxuilji3t4", "contract/valory/staking_token/0.1.0": "bafybeiep4r6qyilbfgzdvx6t7zvpgaioxqktmxm7puwtnbpb2ftlib43gy", "contract/valory/relayer/0.1.0": "bafybeicawmds6czx7db2lcktvexwrp245jpekgulndtos5s5zdid3ilvq4", - "skill/valory/market_manager_abci/0.1.0": "bafybeiaru2d32wpmcgqs64eepxud4idgubc3vmsbdwbia7gygipql2mmqi", - "skill/valory/decision_maker_abci/0.1.0": "bafybeie5xp55kt3cjghhmk4v6mdcagfgk4vkneaxdttnxmtdcbfrxwc26a", - "skill/valory/trader_abci/0.1.0": "bafybeid3sehixzitg2xnsgr76h64rsyp2tngzycygyj26nkkg5gzsqzwfi", - "skill/valory/tx_settlement_multiplexer_abci/0.1.0": "bafybeicztsvr3hnmzlg6irafx3phqfftmnk4zrfph4krsnpinekkjdlon4", + "skill/valory/market_manager_abci/0.1.0": "bafybeiaf37mqv45yptqqd6e7fxv5cjrrxrh4pjpe3bs6zkiou53e5lpoia", + "skill/valory/decision_maker_abci/0.1.0": "bafybeieqpqlpr4f2ulpnecw6qizaq2vucmkqnggmdj5n5wi5bzmuipbj4a", + "skill/valory/trader_abci/0.1.0": "bafybeictflzdal74ri34qxmnyorbykygdgpisaj3px6cuu3d52soigmvoa", + "skill/valory/tx_settlement_multiplexer_abci/0.1.0": "bafybeigsdvhghcfwrwttekotansxa3ps6jrul5xpt2bkrdmiu6y5osfnxi", "skill/valory/staking_abci/0.1.0": "bafybeicupccurmrg7qesivonlyt3nryarsmk5qf5yh6auno64wn45bybvq", "skill/valory/check_stop_trading_abci/0.1.0": "bafybeieduekpd4zbvjztyxyooppqnmjvup6jfp74uo6hhupvtvzzscdzkq", - "agent/valory/trader/0.1.0": "bafybeiczpkr2drsdppith4foyeby6zf5uxugjfjfhl7yocvr3mcdkjzjpa", - "service/valory/trader/0.1.0": "bafybeiaa3a3e2mg2eowfryszytitwuvs6icxezv5pjtubwnizp2u4s7mte", - "service/valory/trader_pearl/0.1.0": "bafybeiboqxm3lhlqils5tohg7bqlc7pqfpv7hkhj3xixtj6ybdpeemm4f4" + "agent/valory/trader/0.1.0": "bafybeihe6dn75yxe2f6dbtaj72j6glxwtviv7i7copg3sde2fin4xblnk4", + "service/valory/trader/0.1.0": "bafybeib72thsyyalobemgnc6vfunxqdvqlf744bphyfzvyixqheiie3wom", + "service/valory/trader_pearl/0.1.0": "bafybeidbc6ob2mpecv5o2tgdlx6snyh2iew7o3igginbxmhdq3remt4zca" }, "third_party": { "protocol/open_aea/signing/1.0.0": "bafybeihv62fim3wl2bayavfcg3u5e5cxu3b7brtu4cn5xoxd6lqwachasi", diff --git a/packages/valory/agents/trader/aea-config.yaml b/packages/valory/agents/trader/aea-config.yaml index 91833da5b..d3c0ad21f 100644 --- a/packages/valory/agents/trader/aea-config.yaml +++ b/packages/valory/agents/trader/aea-config.yaml @@ -19,7 +19,7 @@ contracts: - valory/gnosis_safe:0.1.0:bafybeih3ropivth4wn7zbzudisx3qezbht5jyndd4w7az7fq634lpozoge - valory/gnosis_safe_proxy_factory:0.1.0:bafybeieg57u3z7cdlmdamad5e6lk7kmsli2zurzkg3sl4y7lhekcu4y3au - valory/service_registry:0.1.0:bafybeiaop64kwdoetxtedoehabmsalojmms7ihuoqcdwxtwb2hk5i6bzye -- valory/market_maker:0.1.0:bafybeihrz4q5ia7jnflsv2myg6kkung2dfiogqrevuy7xlmqws7tr27rdi +- valory/market_maker:0.1.0:bafybeibevdc5trbi2qgt2tvwbsr2h5xvonfhcjwfmozftzvef575fdvbjq - valory/multisend:0.1.0:bafybeig5byt5urg2d2bsecufxe5ql7f4mezg3mekfleeh32nmuusx66p4y - valory/mech:0.1.0:bafybeiejfjfoxqggghcme43sx53q5gruefrws3k2jam2opkxl5uzffoarm - valory/conditional_tokens:0.1.0:bafybeibnzmqmeph4cj5vfh3s622mo2o5627vjjwc6bptrhj4dk65mzgvhe @@ -45,10 +45,10 @@ skills: - valory/reset_pause_abci:0.1.0:bafybeigrdlxed3xlsnxtjhnsbl3cojruihxcqx4jxhgivkd5i2fkjncgba - valory/termination_abci:0.1.0:bafybeib5l7jhew5ic6iq24dd23nidcoimzqkrk556gqywhoziatj33zvwm - valory/transaction_settlement_abci:0.1.0:bafybeic7q7recyka272udwcupblwbkc3jkodgp74fvcdxb7urametg5dae -- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeicztsvr3hnmzlg6irafx3phqfftmnk4zrfph4krsnpinekkjdlon4 -- valory/market_manager_abci:0.1.0:bafybeiaru2d32wpmcgqs64eepxud4idgubc3vmsbdwbia7gygipql2mmqi -- valory/decision_maker_abci:0.1.0:bafybeie5xp55kt3cjghhmk4v6mdcagfgk4vkneaxdttnxmtdcbfrxwc26a -- valory/trader_abci:0.1.0:bafybeid3sehixzitg2xnsgr76h64rsyp2tngzycygyj26nkkg5gzsqzwfi +- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeigsdvhghcfwrwttekotansxa3ps6jrul5xpt2bkrdmiu6y5osfnxi +- valory/market_manager_abci:0.1.0:bafybeiaf37mqv45yptqqd6e7fxv5cjrrxrh4pjpe3bs6zkiou53e5lpoia +- valory/decision_maker_abci:0.1.0:bafybeieqpqlpr4f2ulpnecw6qizaq2vucmkqnggmdj5n5wi5bzmuipbj4a +- valory/trader_abci:0.1.0:bafybeictflzdal74ri34qxmnyorbykygdgpisaj3px6cuu3d52soigmvoa - valory/staking_abci:0.1.0:bafybeicupccurmrg7qesivonlyt3nryarsmk5qf5yh6auno64wn45bybvq - valory/check_stop_trading_abci:0.1.0:bafybeieduekpd4zbvjztyxyooppqnmjvup6jfp74uo6hhupvtvzzscdzkq - valory/mech_interact_abci:0.1.0:bafybeid6m3i5ofq7vuogqapdnoshhq7mswmudhvfcr2craw25fdwtoe3lm @@ -264,10 +264,12 @@ models: p_yes_field_part: ${str:p_yes_} p_no_field_part: ${str:p_no_} confidence_field_part: ${str:confidence_} + info_utility_field_part: ${str:info_utility_} part_prefix_mode: ${bool:true} bet_amount_field: ${str:collateral_amount} results_filename: ${str:benchmarking_results.csv} randomness: ${str:benchmarking_randomness} + nr_mech_calls: ${int:60} acc_info_fields: args: tool: ${str:tool} diff --git a/packages/valory/contracts/market_maker/contract.py b/packages/valory/contracts/market_maker/contract.py index 71c233962..e77c282ca 100644 --- a/packages/valory/contracts/market_maker/contract.py +++ b/packages/valory/contracts/market_maker/contract.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023 Valory AG +# Copyright 2024 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -133,3 +133,54 @@ def get_buy_data( outcomeIndex=outcome_index, minOutcomeTokensToBuy=min_outcome_tokens_to_buy, ) + + @classmethod + def calc_sell_amount( + cls, + ledger_api: EthereumApi, + contract_address: str, + return_amount: int, + outcome_index: int, + ) -> JSONLike: + """ + Calculate the buy amount. + + :param ledger_api: the ledger API object + :param contract_address: the contract address + :param return_amount: the amount the user will have returned + :param outcome_index: the index of the answer's outcome that the user wants to sell for + :return: the outcomeTokenSellAmount + """ + outcome_token_sell_amount = cls._method_call( + ledger_api, + contract_address, + "calcSellAmount", + returnAmount=return_amount, + outcomeIndex=outcome_index, + ) + return dict(outcomeTokenSellAmount=outcome_token_sell_amount) + + def get_sell_data( + cls, + ledger_api: LedgerApi, + contract_address: str, + return_amount: int, + outcome_index: int, + max_outcome_tokens_to_sell: int, + ) -> Dict[str, bytes]: + """Gets the encoded arguments for a sell tx, which should only be called via the multisig. + + :param ledger_api: the ledger API object + :param contract_address: the contract address + :param return_amount: the amount the user have returned + :param outcome_index: the index of the answer's outcome that the user wants to sell tokens for + :param max_outcome_tokens_to_sell: the output of the `calcSellAmount` contract method + """ + return cls._encode_abi( + ledger_api, + contract_address, + "sell", + returnAmount=return_amount, + outcomeIndex=outcome_index, + maxOutcomeTokenSellAmount=max_outcome_tokens_to_sell, + ) diff --git a/packages/valory/contracts/market_maker/contract.yaml b/packages/valory/contracts/market_maker/contract.yaml index fbcae9d97..0065c491b 100644 --- a/packages/valory/contracts/market_maker/contract.yaml +++ b/packages/valory/contracts/market_maker/contract.yaml @@ -9,7 +9,7 @@ fingerprint: README.md: bafybeiegnihrovfkk5big52pl4bo6evt5toqvvmft2jgnq6ofdbhfp7xwa __init__.py: bafybeicoucixii3fv5xlpk3zfewm4ys4okidcng54bhtjxvwup7g2jcjza build/FixedProductMarketMaker.json: bafybeigim7n3f67r5czfc5wp2m7cxzxwvnhxops3n5j2zlawenan7qrrtu - contract.py: bafybeicfiicuke4ly5hpcxtyvnob5bgzrqsopuxalruxp2m3w4koxmhrqa + contract.py: bafybeifcopgnnts3dbfh636vvdsgporlkt2olwr2iwuwaom5vjcrtbe5wi fingerprint_ignore_patterns: [] contracts: [] class_name: FixedProductMarketMakerContract diff --git a/packages/valory/services/trader/service.yaml b/packages/valory/services/trader/service.yaml index 2db18986a..426d2e023 100644 --- a/packages/valory/services/trader/service.yaml +++ b/packages/valory/services/trader/service.yaml @@ -7,7 +7,7 @@ license: Apache-2.0 fingerprint: README.md: bafybeigtuothskwyvrhfosps2bu6suauycolj67dpuxqvnicdrdu7yhtvq fingerprint_ignore_patterns: [] -agent: valory/trader:0.1.0:bafybeiczpkr2drsdppith4foyeby6zf5uxugjfjfhl7yocvr3mcdkjzjpa +agent: valory/trader:0.1.0:bafybeihe6dn75yxe2f6dbtaj72j6glxwtviv7i7copg3sde2fin4xblnk4 number_of_agents: 4 deployment: agent: @@ -167,10 +167,12 @@ type: skill p_yes_field_part: ${BENCHMARKING_MODE_P_YES_FIELD_PART:str:p_yes_} p_no_field_part: ${BENCHMARKING_MODE_P_NO_FIELD_PART:str:p_no_} confidence_field_part: ${BENCHMARKING_MODE_CONFIDENCE_FIELD_PART:str:confidence_} + info_utility_field_part: ${BENCHMARKING_MODE_INFO_UTILITY_FIELD_PART:str:info_utility_} part_prefix_mode: ${BENCHMARKING_MODE_PART_PREFIX_MODE:bool:true} bet_amount_field: ${BENCHMARKING_MODE_BET_AMOUNT_FIELD:str:collateral_amount} results_filename: ${BENCHMARKING_MODE_RESULTS_FILENAME:str:benchmarking_results.csv} randomness: ${BENCHMARKING_MODE_RANDOMNESS:str:benchmarking_randomness} + nr_mech_calls: ${BENCHMARKING_MECH_CALLS:int:60} acc_info_fields: &id005 args: tool: ${ACC_INFO_FIELDS_TOOL:str:tool} diff --git a/packages/valory/services/trader_pearl/service.yaml b/packages/valory/services/trader_pearl/service.yaml index 4dec82079..955e19033 100644 --- a/packages/valory/services/trader_pearl/service.yaml +++ b/packages/valory/services/trader_pearl/service.yaml @@ -8,7 +8,7 @@ license: Apache-2.0 fingerprint: README.md: bafybeibg7bdqpioh4lmvknw3ygnllfku32oca4eq5pqtvdrdsgw6buko7e fingerprint_ignore_patterns: [] -agent: valory/trader:0.1.0:bafybeiczpkr2drsdppith4foyeby6zf5uxugjfjfhl7yocvr3mcdkjzjpa +agent: valory/trader:0.1.0:bafybeihe6dn75yxe2f6dbtaj72j6glxwtviv7i7copg3sde2fin4xblnk4 number_of_agents: 1 deployment: agent: diff --git a/packages/valory/skills/decision_maker_abci/behaviours/base.py b/packages/valory/skills/decision_maker_abci/behaviours/base.py index 805518b7b..3c70377b2 100644 --- a/packages/valory/skills/decision_maker_abci/behaviours/base.py +++ b/packages/valory/skills/decision_maker_abci/behaviours/base.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ from aea.configurations.data_types import PublicId from aea.protocols.base import Message from aea.protocols.dialogue.base import Dialogue +from hexbytes import HexBytes from packages.valory.contracts.erc20.contract import ERC20 from packages.valory.contracts.gnosis_safe.contract import ( @@ -68,6 +69,7 @@ from packages.valory.skills.market_manager_abci.bets import ( Bet, CONFIDENCE_FIELD, + INFO_UTILITY_FIELD, P_NO_FIELD, P_YES_FIELD, PredictionResponse, @@ -354,12 +356,14 @@ def check_balance(self) -> WaitableConditionType: def update_bet_transaction_information(self) -> None: """Get whether the bet's invested amount should be updated.""" sampled_bet = self.sampled_bet - # Update the bet's invested amount, the new bet amount is added to previously invested amount - sampled_bet.invested_amount += self.synchronized_data.bet_amount + + # Update the bet's invested amount + updated = sampled_bet.update_investments(self.synchronized_data.bet_amount) + if not updated: + self.context.logger.error("Could not update the investments!") + # Update bet transaction timestamp sampled_bet.processed_timestamp = self.synced_timestamp - # update no of bets made - sampled_bet.n_bets += 1 # Update Queue number for priority logic sampled_bet.queue_status = sampled_bet.queue_status.next_status() @@ -683,6 +687,7 @@ def _write_benchmark_results( P_YES_FIELD, P_NO_FIELD, CONFIDENCE_FIELD, + INFO_UTILITY_FIELD, self.benchmarking_mode.bet_amount_field, L0_START_FIELD, L1_START_FIELD, @@ -701,6 +706,7 @@ def _write_benchmark_results( prediction_response.p_yes, prediction_response.p_no, prediction_response.confidence, + prediction_response.info_utility, bet_amount, liquidity_info.l0_start, liquidity_info.l1_start, @@ -719,6 +725,35 @@ def finish_behaviour(self, payload: BaseTxPayload) -> Generator: self.set_done() + def build_approval_tx( + self, amount: int, spender: str, token: str + ) -> WaitableConditionType: + """Build an ERC20 approve transaction.""" + response_msg = yield from self.get_contract_api_response( + performative=ContractApiMessage.Performative.GET_STATE, # type: ignore + contract_address=self.collateral_token, + contract_id=str(ERC20.contract_id), + contract_callable="build_approval_tx", + spender=spender, + amount=amount, + ) + + if response_msg.performative != ContractApiMessage.Performative.STATE: + self.context.logger.info(f"Could not build approval tx: {response_msg}") + return False + + approval_data = response_msg.state.body.get("data") + if approval_data is None: + self.context.logger.info(f"Could not build approval tx: {response_msg}") + return False + + batch = MultisendBatch( + to=token, + data=HexBytes(approval_data), + ) + self.multisend_batches.append(batch) + return True + class BaseSubscriptionBehaviour(DecisionMakerBaseBehaviour, ABC): """Base class for subscription behaviours.""" diff --git a/packages/valory/skills/decision_maker_abci/behaviours/bet_placement.py b/packages/valory/skills/decision_maker_abci/behaviours/bet_placement.py index 70c39408c..bfda67fd3 100644 --- a/packages/valory/skills/decision_maker_abci/behaviours/bet_placement.py +++ b/packages/valory/skills/decision_maker_abci/behaviours/bet_placement.py @@ -99,30 +99,12 @@ def _build_exchange_tx(self) -> WaitableConditionType: def _build_approval_tx(self) -> WaitableConditionType: """Build an ERC20 approve transaction.""" - response_msg = yield from self.get_contract_api_response( - performative=ContractApiMessage.Performative.GET_STATE, # type: ignore - contract_address=self.collateral_token, - contract_id=str(ERC20.contract_id), - contract_callable="build_approval_tx", - spender=self.market_maker_contract_address, - amount=self.investment_amount, + status = yield from self.build_approval_tx( + self.investment_amount, + self.market_maker_contract_address, + self.collateral_token, ) - - if response_msg.performative != ContractApiMessage.Performative.STATE: - self.context.logger.info(f"Could not build approval tx: {response_msg}") - return False - - approval_data = response_msg.state.body.get("data") - if approval_data is None: - self.context.logger.info(f"Could not build approval tx: {response_msg}") - return False - - batch = MultisendBatch( - to=self.collateral_token, - data=HexBytes(approval_data), - ) - self.multisend_batches.append(batch) - return True + return status def _calc_buy_amount(self) -> WaitableConditionType: """Calculate the buy amount of the conditional token.""" @@ -209,6 +191,7 @@ def async_act(self) -> Generator: if self.benchmarking_mode.enabled: # simulate the bet placement with self.context.benchmark_tool.measure(self.behaviour_id).local(): + self.update_bet_transaction_information() payload = BetPlacementPayload( agent, None, None, True, self.wallet_balance ) diff --git a/packages/valory/skills/decision_maker_abci/behaviours/decision_receive.py b/packages/valory/skills/decision_maker_abci/behaviours/decision_receive.py index 7e17bb43f..67440732d 100644 --- a/packages/valory/skills/decision_maker_abci/behaviours/decision_receive.py +++ b/packages/valory/skills/decision_maker_abci/behaviours/decision_receive.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -126,6 +126,9 @@ def _next_dataset_row(self) -> Optional[Dict[str, str]]: else: # no more bets available for this market msg = f"No more mock responses for the market with id: {sampled_bet_id}" + self.sampled_bet.queue_status = ( + self.sampled_bet.queue_status.mark_benchmarking_done() + ) self.context.logger.info(msg) self.shared_state.last_benchmarking_has_run = True self._rows_exceeded = True @@ -157,6 +160,7 @@ def _parse_dataset_row(self, row: Dict[str, str]) -> str: P_YES_FIELD: mode.p_yes_field_part, P_NO_FIELD: mode.p_no_field_part, CONFIDENCE_FIELD: mode.confidence_field_part, + INFO_UTILITY_FIELD: mode.info_utility_field_part, }.items(): if mode.part_prefix_mode: fields[prediction_attribute] = row[field_part + mech_tool] @@ -171,8 +175,6 @@ def _parse_dataset_row(self, row: Dict[str, str]) -> str: float(fields[P_YES_FIELD]), ) - # set the info utility to zero as it does not matter for the benchmark - fields[INFO_UTILITY_FIELD] = "0" return json.dumps(fields) def _mock_response(self) -> None: @@ -514,6 +516,9 @@ def _is_profitable( else: self._write_benchmark_results(prediction_response) + self.context.logger.info("Increasing Mech call count by 1") + self.shared_state.benchmarking_mech_calls += 1 + return is_profitable, bet_amount def _update_selected_bet( @@ -531,8 +536,6 @@ def _update_selected_bet( self.context.logger.info( f"with the timestamp:{datetime.fromtimestamp(active_sampled_bet.processed_timestamp)}" ) - if prediction_response is not None: - active_sampled_bet.n_bets += 1 self.store_bets() @@ -569,6 +572,8 @@ def async_act(self) -> Generator: prediction_response, bet_amount, ) + self.context.logger.info("Increasing Mech call count by 1") + self.shared_state.benchmarking_mech_calls += 1 if prediction_response is not None: self.policy.tool_responded( diff --git a/packages/valory/skills/decision_maker_abci/behaviours/sampling.py b/packages/valory/skills/decision_maker_abci/behaviours/sampling.py index 944149a49..1a51ec72f 100644 --- a/packages/valory/skills/decision_maker_abci/behaviours/sampling.py +++ b/packages/valory/skills/decision_maker_abci/behaviours/sampling.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -138,6 +138,31 @@ def _sampled_bet_idx(self, bets: List[Bet]) -> int: return self.bets.index(sorted_bets[0]) + def _sampling_benchmarking_bet(self, bets: List[Bet]) -> Optional[int]: + """Sample bet for benchmarking""" + to_process_bets, processed_bets, reprocessed_bets = self._get_bets_queue_wise( + bets + ) + + self.context.logger.info(f"TO_PROCESS_LEN: {len(to_process_bets)}") + self.context.logger.info(f"PROCESSED_LEN: {len(processed_bets)}") + self.context.logger.info(f"REPROCESSED_LEN: {len(reprocessed_bets)}") + + self.context.logger.info( + f"MECH CALLS MADE: {self.shared_state.benchmarking_mech_calls}" + ) + + if ( + self.shared_state.benchmarking_mech_calls + == self.benchmarking_mode.nr_mech_calls + ): + return None + + bets_to_sort: List[Bet] = to_process_bets or processed_bets or reprocessed_bets + sorted_bets = self._sort_by_priority_logic(bets_to_sort) + + return self.bets.index(sorted_bets[0]) + def _sample(self) -> Optional[int]: """Sample a bet, mark it as processed, and return its index.""" # modify time "NOW" in benchmarking mode @@ -164,6 +189,11 @@ def _sample(self) -> Optional[int]: self.context.logger.warning(msg) return None + if self.benchmarking_mode.enabled: + idx = self._sampling_benchmarking_bet(available_bets) + if not idx: + return None + # sample a bet using the priority logic idx = self._sampled_bet_idx(available_bets) sampled_bet = self.bets[idx] @@ -190,6 +220,8 @@ def _benchmarking_inc_day(self) -> Tuple[bool, bool]: if benchmarking_finished: self.context.logger.info("No more days to simulate in benchmarking mode.") + self.shared_state.benchmarking_mech_calls = 0 + day_increased = True return benchmarking_finished, day_increased @@ -204,6 +236,9 @@ def async_act(self) -> Generator: # day increase simulation and benchmarking finished check if idx is None and self.benchmarking_mode.enabled: benchmarking_finished, day_increased = self._benchmarking_inc_day() + for bet in self.bets: + bet.queue_status = bet.queue_status.move_to_fresh() + bet.queue_status = bet.queue_status.move_to_process() self.store_bets() diff --git a/packages/valory/skills/decision_maker_abci/behaviours/sell_outcome_token.py b/packages/valory/skills/decision_maker_abci/behaviours/sell_outcome_token.py new file mode 100644 index 000000000..0bf228d07 --- /dev/null +++ b/packages/valory/skills/decision_maker_abci/behaviours/sell_outcome_token.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2024 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + + +"""This module contains the behaviour for selling a token.""" +from typing import Any, Generator, Optional, cast + +from hexbytes import HexBytes + +from packages.valory.contracts.market_maker.contract import ( + FixedProductMarketMakerContract, +) +from packages.valory.protocols.contract_api import ContractApiMessage +from packages.valory.skills.decision_maker_abci.behaviours.base import ( + DecisionMakerBaseBehaviour, + WaitableConditionType, +) +from packages.valory.skills.decision_maker_abci.models import MultisendBatch +from packages.valory.skills.decision_maker_abci.payloads import MultisigTxPayload +from packages.valory.skills.decision_maker_abci.states.sell_outcome_token import ( + SellOutcomeTokenRound, +) + + +class SellTokenBehaviour(DecisionMakerBaseBehaviour): + """A behaviour in which the agents sell a token.""" + + matching_round = SellOutcomeTokenRound + + def __init__(self, **kwargs: Any) -> None: + """Initialize the sell token behaviour.""" + super().__init__(**kwargs) + self.sell_amount: float = 0.0 + + @property + def market_maker_contract_address(self) -> str: + """Get the contract address of the market maker on which the service is going to place the bet.""" + return self.sampled_bet.id + + @property + def outcome_index(self) -> int: + """Get the index of the outcome for which the service is going to sell token.""" + return cast(int, self.synchronized_data.previous_vote) + + @property + def return_amount(self) -> int: + """Get the amount expected to be returned after the sell tx is completed.""" + previous_vote = self.synchronized_data.previous_vote + + if previous_vote == 0: + return self.sampled_bet.invested_amount_yes + + else: + return self.sampled_bet.invested_amount_no + + def _build_approval_tx(self) -> WaitableConditionType: + """Build an ERC20 approve transaction.""" + status = yield from self.build_approval_tx( + self.return_amount, + self.market_maker_contract_address, + self.sampled_bet.get_outcome(self.outcome_index), + ) + return status + + def _calc_sell_amount(self) -> WaitableConditionType: + """Calculate the sell amount of the conditional token.""" + response_msg = yield from self.get_contract_api_response( + performative=ContractApiMessage.Performative.GET_RAW_TRANSACTION, # type: ignore + contract_address=self.market_maker_contract_address, + contract_id=str(FixedProductMarketMakerContract.contract_id), + contract_callable="calc_sell_amount", + return_amount=self.return_amount, + outcome_index=self.outcome_index, + ) + if response_msg.performative != ContractApiMessage.Performative.RAW_TRANSACTION: + self.context.logger.error( + f"Could not calculate the sell amount: {response_msg}" + ) + return False + + sell_amount = response_msg.raw_transaction.body.get( + "outcomeTokenSellAmount", None + ) + if sell_amount is None: + self.context.logger.error( + f"Something went wrong while trying to get the outcomeTokenSellAmount amount for the conditional token: {response_msg}" + ) + return False + + self.sell_amount = sell_amount + return True + + def _build_sell_tx(self) -> WaitableConditionType: + """Get the sell tx data encoded.""" + response_msg = yield from self.get_contract_api_response( + performative=ContractApiMessage.Performative.GET_STATE, # type: ignore + contract_address=self.market_maker_contract_address, + contract_id=str(FixedProductMarketMakerContract.contract_id), + contract_callable="get_sell_data", + return_amount=self.return_amount, + outcome_index=self.outcome_index, + max_outcome_tokens_to_sell=self.sell_amount, + ) + if response_msg.performative != ContractApiMessage.Performative.STATE: + self.context.logger.error( + f"Could not get the data for the buy transaction: {response_msg}" + ) + return False + + sell_data = response_msg.state.body.get("data", None) + if sell_data is None: + self.context.logger.error( + f"Something went wrong while trying to encode the buy data: {response_msg}" + ) + return False + + batch = MultisendBatch( + to=self.market_maker_contract_address, + data=HexBytes(sell_data), + ) + self.multisend_batches.append(batch) + return True + + def _prepare_safe_tx(self) -> Generator[None, None, Optional[str]]: + """Prepare the safe transaction for selling an outcome token and return the hex for the tx settlement skill.""" + for step in ( + self._build_approval_tx, + self._calc_sell_amount, + self._build_sell_tx, + self._build_multisend_data, + self._build_multisend_safe_tx_hash, + ): + yield from self.wait_for_condition_with_sleep(step) + + outcome = self.sampled_bet.get_outcome(self.outcome_index) + investment = self._collateral_amount_info(self.return_amount) + self.context.logger.info( + f"Preparing a multisig transaction to sell the outcome token for {outcome!r}, with confidence " + f"{self.synchronized_data.confidence!r}, for the amount of {investment}, which is equal to the amount of " + f"{self.sell_amount!r} WEI of the conditional token corresponding to {outcome!r}." + ) + + return self.tx_hex + + def async_act(self) -> Generator: + """Do the action.""" + + agent = self.context.agent_address + + with self.context.benchmark_tool.measure(self.behaviour_id).local(): + tx_submitter = betting_tx_hex = mocking_mode = None + + # if the vote is the same as the previous vote then there is no change in the supported outcome, so we + # should not sell + if self.synchronized_data.vote == self.synchronized_data.previous_vote: + payload = MultisigTxPayload( + agent, tx_submitter, betting_tx_hex, mocking_mode + ) + + yield from self.finish_behaviour(payload) + + tx_submitter = self.matching_round.auto_round_id() + betting_tx_hex = yield from self._prepare_safe_tx() + + payload = MultisigTxPayload( + agent, tx_submitter, betting_tx_hex, mocking_mode + ) + + yield from self.finish_behaviour(payload) diff --git a/packages/valory/skills/decision_maker_abci/models.py b/packages/valory/skills/decision_maker_abci/models.py index 8d7616784..ac6f9590f 100644 --- a/packages/valory/skills/decision_maker_abci/models.py +++ b/packages/valory/skills/decision_maker_abci/models.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -227,6 +227,9 @@ def __init__(self, *args: Any, skill_context: SkillContext, **kwargs: Any) -> No # the key is the market id/question_id self.bet_id_row_manager: Dict[str, List[int]] = {} + # mech call counter for benchmarking behaviour + self.benchmarking_mech_calls: int = 0 + @property def mock_question_id(self) -> Any: """Get the mock question id.""" @@ -445,11 +448,11 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: "tool_punishment_multiplier", kwargs, int ) self.contract_timeout: float = self._ensure("contract_timeout", kwargs, float) - self.file_hash_to_strategies: Dict[ - str, List[str] - ] = nested_list_todict_workaround( - kwargs, - "file_hash_to_strategies_json", + self.file_hash_to_strategies: Dict[str, List[str]] = ( + nested_list_todict_workaround( + kwargs, + "file_hash_to_strategies_json", + ) ) self.strategies_kwargs: Dict[str, List[Any]] = nested_list_todict_workaround( kwargs, "strategies_kwargs" @@ -461,11 +464,11 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: ) self.use_nevermined = self._ensure("use_nevermined", kwargs, bool) self.rpc_sleep_time: int = self._ensure("rpc_sleep_time", kwargs, int) - self.mech_to_subscription_params: Dict[ - str, Any - ] = nested_list_todict_workaround( - kwargs, - "mech_to_subscription_params", + self.mech_to_subscription_params: Dict[str, Any] = ( + nested_list_todict_workaround( + kwargs, + "mech_to_subscription_params", + ) ) self.service_endpoint = self._ensure("service_endpoint", kwargs, str) self.safe_voting_range = self._ensure("safe_voting_range", kwargs, int) @@ -548,6 +551,9 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.confidence_field_part: str = self._ensure( "confidence_field_part", kwargs, str ) + self.info_utility_field_part: str = self._ensure( + "info_utility_field_part", kwargs, str + ) # this is the mode for the p and confidence parts # if the flag is `True`, then the field parts are used as prefixes, otherwise as suffixes self.part_prefix_mode: bool = self._ensure("part_prefix_mode", kwargs, bool) @@ -556,6 +562,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self._ensure("results_filename", kwargs, str) ) self.randomness: str = self._ensure("randomness", kwargs, str) + self.nr_mech_calls: int = self._ensure("nr_mech_calls", kwargs, int) super().__init__(*args, **kwargs) diff --git a/packages/valory/skills/decision_maker_abci/skill.yaml b/packages/valory/skills/decision_maker_abci/skill.yaml index 5fbe5ded9..182fd5f77 100644 --- a/packages/valory/skills/decision_maker_abci/skill.yaml +++ b/packages/valory/skills/decision_maker_abci/skill.yaml @@ -12,19 +12,20 @@ fingerprint: README.md: bafybeia367zzdwndvlhw27rvnwodytjo3ms7gbc3q7mhrrjqjgfasnk47i __init__.py: bafybeih563ujnigeci2ldzh7hakbau6a222vsed7leg3b7lq32vcn3nm4a behaviours/__init__.py: bafybeih6ddz2ocvm6x6ytvlbcz6oi4snb5ee5xh5h65nq4w2qf7fd7zfky - behaviours/base.py: bafybeieqvxpzjnwjgq4ffhjojle2q7onif44nefrlza6pxriewisqcm324 - behaviours/bet_placement.py: bafybeia4listbfzsk4n4wkc4ycaftxgywjnl3mmpcqhuo3nwwia4n3oufu + behaviours/base.py: bafybeibijvmc6q5q5dqbk7nhs7azarjyzoifstwmwkybj7aohnc4twdpqu + behaviours/bet_placement.py: bafybeig7rdvdk2matibxsllrcxcmsazxaznwrmf5q7va4ads7yzs5xs4pa behaviours/blacklisting.py: bafybeieuqoup2vrmrtvjfqnr5mzrvkegc7afb2oeujzq2itsbhcsham2se behaviours/check_benchmarking.py: bafybeiao2lyj7apezkqrpgsyzb3dwvrdgsrgtprf6iuhsmlsufvxfl5bci behaviours/claim_subscription.py: bafybeigbqkhc6mb73rbwaks32tfiqx6u2xza43uiy6rvbtrnqd6m4fru3e - behaviours/decision_receive.py: bafybeibsthk6fjydkengwzgpgz5v44xpta67nnlyxt6uugx2voitqgojpq + behaviours/decision_receive.py: bafybeigqcyz6jc2wvyt2joeelxw3g6d4bg62uy2scekemusiwefrflwszu behaviours/decision_request.py: bafybeia22omb7tvocyfe3z2ucn5au5mcas7dg37ha42u7znefzrewjpk7y behaviours/handle_failed_tx.py: bafybeiashwlfp6ty3g6ukgmliaghwu6yiunbqpjmyrzheokw3pbcr2ckaq behaviours/order_subscription.py: bafybeib3maqohhx35wzryy4otdcjp5thkr4sbp27ksvwidy3pwm444itra behaviours/randomness.py: bafybeiaoj3awyyg2onhpsdsn3dyczs23gr4smuzqcbw3e5ocljwxswjkce behaviours/reedem.py: bafybeidjmhh6c6shbg25d7exmc4nnp4heqbqselwuxj7rp2ss665lrytxe behaviours/round_behaviour.py: bafybeih63hpia2bwwzu563hxs5yd3t5ycvxvkfnhvxbzghbyy3mw3xjl3i - behaviours/sampling.py: bafybeicvtxjv5rxlsdrmbtetqwzzau6is47guystvw245grd6s2qs5pxea + behaviours/sampling.py: bafybeibqsil76csuvocsop5cq4srf5dwzwidg55tusv7yt4z4bkozu6dyu + behaviours/sell_outcome_token.py: bafybeicjpqnukexia457s73yp34yvgefpkgaikainmjf3sxgdk5c4kfqba behaviours/storage_manager.py: bafybeic6wca37fkwonbsrwme55xnklfbqtheknroudayzfxdge4pxdbm7y behaviours/tool_selection.py: bafybeienlxcgjs3ogyofli3d7q3p5rst3mcxxcnwqf7qolqjeefjtixeke dialogues.py: bafybeigpwuzku3we7axmxeamg7vn656maww6emuztau5pg3ebsoquyfdqm @@ -32,18 +33,18 @@ fingerprint: handlers.py: bafybeibf42562x3d5i66yf5p3vi6a2oolhwwxr32pjqtuxz5w4gmg3r4oa io_/__init__.py: bafybeifxgmmwjqzezzn3e6keh2bfo4cyo7y5dq2ept3stfmgglbrzfl5rq io_/loader.py: bafybeih3sdsx5dhe4kzhtoafexjgkutsujwqy3zcdrlrkhtdks45bc7exa - models.py: bafybeibifjp45ii5amaghmdk5yjha7if5qpyreg2axpcxcj3ek23ys4fxm + models.py: bafybeibflo4n6f63mbe2e2pcg2qq6rmz2ixhtbml23etg4cfj3qubhp3f4 payloads.py: bafybeieygushjlrzwzpnhagjgpbs3goot3pnfheh6yawuwctrk3uoeesfm policy.py: bafybeidofgwvk6sudz75tvuduskuphtn3amtib2irzw5hr3qcfn5pdwuc4 redeem_info.py: bafybeifiiix4gihfo4avraxt34sfw35v6dqq45do2drrssei2shbps63mm rounds.py: bafybeidjve7efycfkkbignqky4x6awvrobn4w32grxiubxxiiparr7xd2i states/__init__.py: bafybeid23llnyp6j257dluxmrnztugo5llsrog7kua53hllyktz4dqhqoy - states/base.py: bafybeiglqvym3ri6hurx4k7hrnykzbmslxe3vuj23djt6hai4czii4vbqq + states/base.py: bafybeibg2vscnm4deglh3mygox3pgv4mqr7vjk4liane5wb3k3vmp5yj6u states/bet_placement.py: bafybeih5eopyxubczys5u5t3bdxbxpc7mmfdyqrpqsbm2uha5jc2phza4i states/blacklisting.py: bafybeiapelgjhbjjn4uq4z5gspyirqzwzgccg5anktrp5kxdwamfnfw5mi states/check_benchmarking.py: bafybeifvto757zbfzy7mpehblyjd7zqboarxesjfiobtnbxew4nkltfkim states/claim_subscription.py: bafybeidlubctpk5djsredvvwdhubs34rztud3lw7pwp5kj6ggzah6dvyly - states/decision_receive.py: bafybeiexc26g7z7by6eziawjld52nglljiwlj6oam5ramtbyo6un2tqs5y + states/decision_receive.py: bafybeigtwaovqjkti6uiaqsjjnaf4urfps2yenextjml2ux4jktvixpmxm states/decision_request.py: bafybeiarv3r5j7cfvxmudki2llbdl2pvf24p5mvsva6bdgrylnwdyag5xy states/final_states.py: bafybeicjrrojo3gmfaxzicwloyorlnqgzl6a2avevo4nvhoh424zwzmbti states/handle_failed_tx.py: bafybeiha5wkl4u4jlj7txpmhuzfnibnu3ix5zw4vmufjunoyuq6s6ubhnu @@ -51,6 +52,7 @@ fingerprint: states/randomness.py: bafybeiceoo4nx3t4dofpwczw3v5mclramwmzpwjs6hv7l56arodrjx4l5u states/redeem.py: bafybeica6cn4xg7shea2wjhbqnddgxe5zao2hkmceltze7qknxdhtsoaxe states/sampling.py: bafybeif2yuwl5swelp7oh5nfuupdf3vg2ijjzapk2xqht7e6i6ggcsl2zy + states/sell_outcome_token.py: bafybeievk4w3zbi4buicml7nivhpepsoed6dsayauqhyk2bkkyqxdil2xm states/tool_selection.py: bafybeiak5ihuie4nxh3sguiea6pcdgyxr4k4xyzvq6o2uj5xpf7urocawy tests/__init__.py: bafybeiakpi3k3kc7wrjj7hrluvjcj36lu2gezpmrctwiz5yg2fe7ggnf3i tests/behaviours/__init__.py: bafybeic7icz7lfhfepdkqkase7y7zn3a6pwdw6fx4ah2hajmgejawpolc4 @@ -85,7 +87,7 @@ connections: - valory/http_server:0.22.0:bafybeihpgu56ovmq4npazdbh6y6ru5i7zuv6wvdglpxavsckyih56smu7m contracts: - valory/gnosis_safe:0.1.0:bafybeih3ropivth4wn7zbzudisx3qezbht5jyndd4w7az7fq634lpozoge -- valory/market_maker:0.1.0:bafybeihrz4q5ia7jnflsv2myg6kkung2dfiogqrevuy7xlmqws7tr27rdi +- valory/market_maker:0.1.0:bafybeibevdc5trbi2qgt2tvwbsr2h5xvonfhcjwfmozftzvef575fdvbjq - valory/erc20:0.1.0:bafybeid2p2jyvjjlcsqugnawksdzsca6ljghpqbp2kfi3cxuxoy2233dbi - valory/multisend:0.1.0:bafybeig5byt5urg2d2bsecufxe5ql7f4mezg3mekfleeh32nmuusx66p4y - valory/mech:0.1.0:bafybeiejfjfoxqggghcme43sx53q5gruefrws3k2jam2opkxl5uzffoarm @@ -101,7 +103,7 @@ protocols: - valory/http:1.0.0:bafybeifugzl63kfdmwrxwphrnrhj7bn6iruxieme3a4ntzejf6kmtuwmae skills: - valory/abstract_round_abci:0.1.0:bafybeib733xfbndtpvkf44mtk7oyodnficgloo6xhn7xmqxxeos33es65u -- valory/market_manager_abci:0.1.0:bafybeiaru2d32wpmcgqs64eepxud4idgubc3vmsbdwbia7gygipql2mmqi +- valory/market_manager_abci:0.1.0:bafybeiaf37mqv45yptqqd6e7fxv5cjrrxrh4pjpe3bs6zkiou53e5lpoia - valory/transaction_settlement_abci:0.1.0:bafybeic7q7recyka272udwcupblwbkc3jkodgp74fvcdxb7urametg5dae - valory/mech_interact_abci:0.1.0:bafybeid6m3i5ofq7vuogqapdnoshhq7mswmudhvfcr2craw25fdwtoe3lm - valory/staking_abci:0.1.0:bafybeicupccurmrg7qesivonlyt3nryarsmk5qf5yh6auno64wn45bybvq @@ -342,10 +344,12 @@ models: p_yes_field_part: p_yes_ p_no_field_part: p_no_ confidence_field_part: confidence_ + info_utility_field_part: info_utility_ part_prefix_mode: true bet_amount_field: collateral_amount results_filename: benchmarking_results.csv randomness: benchmarking_randomness + nr_mech_calls: 60 class_name: BenchmarkingMode acc_info_fields: args: diff --git a/packages/valory/skills/decision_maker_abci/states/base.py b/packages/valory/skills/decision_maker_abci/states/base.py index 421eec4c9..8c3f2d305 100644 --- a/packages/valory/skills/decision_maker_abci/states/base.py +++ b/packages/valory/skills/decision_maker_abci/states/base.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -152,6 +152,12 @@ def vote(self) -> Optional[int]: vote = self.db.get_strict("vote") return int(vote) if vote is not None else None + @property + def previous_vote(self) -> Optional[int]: + """Get the bet's previous vote index.""" + previous_vote = self.db.get_strict("previous_vote") + return int(previous_vote) if previous_vote is not None else None + @property def confidence(self) -> float: """Get the vote's confidence.""" diff --git a/packages/valory/skills/decision_maker_abci/states/decision_receive.py b/packages/valory/skills/decision_maker_abci/states/decision_receive.py index 282e48c08..124ef17af 100644 --- a/packages/valory/skills/decision_maker_abci/states/decision_receive.py +++ b/packages/valory/skills/decision_maker_abci/states/decision_receive.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -55,6 +55,7 @@ class DecisionReceiveRound(CollectSameUntilThresholdRound): def end_block(self) -> Optional[Tuple[SynchronizedData, Enum]]: """Process the end of the block.""" + res = super().end_block() if res is None: return None diff --git a/packages/valory/skills/decision_maker_abci/states/sell_outcome_token.py b/packages/valory/skills/decision_maker_abci/states/sell_outcome_token.py new file mode 100644 index 000000000..3fdeb628c --- /dev/null +++ b/packages/valory/skills/decision_maker_abci/states/sell_outcome_token.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2024 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + + +"""This module contains the sell outcome token state of the decision-making abci app.""" + +from packages.valory.skills.decision_maker_abci.states.base import TxPreparationRound + + +class SellOutcomeTokenRound(TxPreparationRound): + """A round for selling a token.""" diff --git a/packages/valory/skills/market_manager_abci/bets.py b/packages/valory/skills/market_manager_abci/bets.py index 6380aae48..a50934d03 100644 --- a/packages/valory/skills/market_manager_abci/bets.py +++ b/packages/valory/skills/market_manager_abci/bets.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # -# Copyright 2023-2024 Valory AG +# Copyright 2023-2025 Valory AG # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,6 +28,8 @@ from typing import Any, Dict, List, Optional, Union +YES = "yes" +NO = "no" P_YES_FIELD = "p_yes" P_NO_FIELD = "p_no" CONFIDENCE_FIELD = "confidence" @@ -44,6 +46,7 @@ class QueueStatus(Enum): TO_PROCESS = 1 # Bets that are ready to be processed PROCESSED = 2 # Bets that have been processed REPROCESSED = 3 # Bets that have been reprocessed + BENCHMARKING_DONE = 4 def is_fresh(self) -> bool: """Check if the bet is fresh.""" @@ -61,10 +64,14 @@ def move_to_process(self) -> "QueueStatus": def move_to_fresh(self) -> "QueueStatus": """Move the bet to the fresh status.""" - if self != QueueStatus.EXPIRED: + if self not in [QueueStatus.EXPIRED, QueueStatus.BENCHMARKING_DONE]: return QueueStatus.FRESH return self + def mark_benchmarking_done(self) -> "QueueStatus": + """Function to establish the benchmarking done status""" + return QueueStatus.BENCHMARKING_DONE + def next_status(self) -> "QueueStatus": """Get the next status in the queue.""" if self == QueueStatus.TO_PROCESS: @@ -137,18 +144,59 @@ class Bet: prediction_response: PredictionResponse = dataclasses.field( default_factory=get_default_prediction_response ) - invested_amount: float = 0.0 position_liquidity: int = 0 potential_net_profit: int = 0 processed_timestamp: int = 0 - n_bets: int = 0 queue_status: QueueStatus = QueueStatus.FRESH + # a mapping from vote to investment amounts + investments: Dict[str, List[int]] = dataclasses.field(default_factory=dict) + + @property + def yes_investments(self) -> List[int]: + """Get the yes investments.""" + return self.investments[self.yes] + + @property + def no_investments(self) -> List[int]: + """Get the no investments.""" + return self.investments[self.no] + + @property + def n_yes_bets(self) -> int: + """Get the number of yes bets.""" + return len(self.yes_investments) + + @property + def n_no_bets(self) -> int: + """Get the number of no bets.""" + return len(self.no_investments) + + @property + def n_bets(self) -> int: + """Get the number of bets.""" + return self.n_yes_bets + self.n_no_bets + + @property + def invested_amount_yes(self) -> int: + """Get the amount invested in yes bets.""" + return sum(self.yes_investments) + + @property + def invested_amount_no(self) -> int: + """Get the amount invested in no bets.""" + return sum(self.no_investments) + + @property + def invested_amount(self) -> int: + """Get the amount invested in bets.""" + return self.invested_amount_yes + self.invested_amount_no def __post_init__(self) -> None: """Post initialization to adjust the values.""" self._validate() self._cast() self._check_usefulness() + self.investments = {self.yes: [], self.no: []} def __lt__(self, other: "Bet") -> bool: """Implements less than operator.""" @@ -226,7 +274,7 @@ def _get_binary_outcome(self, no: bool) -> str: """Get an outcome only if it is binary.""" if self.outcomeSlotCount == BINARY_N_SLOTS: return self.get_outcome(int(no)) - requested_outcome = "no" if no else "yes" + requested_outcome = NO if no else YES error = ( f"A {requested_outcome!r} outcome is only available for binary questions." ) @@ -242,6 +290,17 @@ def no(self) -> str: """Return the "no" outcome.""" return self._get_binary_outcome(True) + def update_investments(self, amount: int) -> bool: + """Get the investments for the current vote type.""" + vote = self.prediction_response.vote + if vote is None: + return False + + vote_name = self.get_outcome(vote) + to_update = self.investments[vote_name] + to_update.append(amount) + return True + def update_market_info(self, bet: "Bet") -> None: """Update the bet's market information.""" if ( @@ -311,14 +370,12 @@ def hook(data: Dict[str, Any]) -> Union[Bet, PredictionResponse, Dict[str, Bet]] if bet_annotations == data_attributes: data["queue_status"] = QueueStatus(data["queue_status"]) return Bet(**data) - else: - # fetch missing attributes from the data - missing_attributes = set(bet_annotations) - set(data_attributes) - new_attributes = {"queue_status", "invested_amount"} - if missing_attributes == new_attributes: - data["queue_status"] = QueueStatus(0) - data["invested_amount"] = 0 - return Bet(**data) + elif "id" in data_attributes: + common_attributes = set(bet_annotations) & set(data_attributes) + data = {key: data[key] for key in common_attributes} + if "queue_status" in data: + data["queue_status"] = QueueStatus(data["queue_status"]) + return Bet(**data) return data diff --git a/packages/valory/skills/market_manager_abci/skill.yaml b/packages/valory/skills/market_manager_abci/skill.yaml index 0bff3c686..9f3788b0a 100644 --- a/packages/valory/skills/market_manager_abci/skill.yaml +++ b/packages/valory/skills/market_manager_abci/skill.yaml @@ -9,7 +9,7 @@ fingerprint: README.md: bafybeie6miwn67uin3bphukmf7qgiifh4xtm42i5v3nuyqxzxtehxsqvcq __init__.py: bafybeigrtedqzlq5mtql2ssjsdriw76ml3666m4e2c3fay6vmyzofl6v6e behaviours.py: bafybeicfoszavcyrzahe6qaydlaf27mpbwui7a6wgdbstydbzxmdisxhju - bets.py: bafybeibx3x5nasuj6loneeat2lb7fr7kgsmnz7on7f4gxfbpzerzdvawou + bets.py: bafybeictfcbtigszoso4tmvc5xhumta4nhtjsvxpuoukwzgkltlukvne5a dialogues.py: bafybeiebofyykseqp3fmif36cqmmyf3k7d2zbocpl6t6wnlpv4szghrxbm fsm_specification.yaml: bafybeic5cvwfbiu5pywyp3h5s2elvu7jqdrcwayay7o3v3ow47vu2jw53q graph_tooling/__init__.py: bafybeigzo7nhbzafyq3fuhrlewksjvmzttiuk4vonrggtjtph4rw4ncpk4 diff --git a/packages/valory/skills/trader_abci/fsm_specification.yaml b/packages/valory/skills/trader_abci/fsm_specification.yaml index 0778e7f01..b2a80f938 100644 --- a/packages/valory/skills/trader_abci/fsm_specification.yaml +++ b/packages/valory/skills/trader_abci/fsm_specification.yaml @@ -2,7 +2,8 @@ alphabet_in: - BENCHMARKING_DISABLED - BENCHMARKING_ENABLED - BENCHMARKING_FINISHED -- BET_PLACEMENT_DONE +- BET_PLACEMENT_NO_SELL_DONE +- BET_PLACEMENT_SELL_DONE - BLACKLIST - CHECKS_PASSED - CHECK_HISTORY @@ -33,6 +34,7 @@ alphabet_in: - RESET_AND_PAUSE_TIMEOUT - RESET_TIMEOUT - ROUND_TIMEOUT +- SELL_OUTCOME_TOKEN_DONE - SERVICE_EVICTED - SERVICE_NOT_STAKED - SKIP_REQUEST @@ -49,6 +51,7 @@ alphabet_in: default_start_state: RegistrationStartupRound final_states: - FailedMultiplexerRound +- FinishedSellOutcomeTokenTxRound - ImpossibleRound - ServiceEvictedRound label: TraderAbciApp @@ -70,6 +73,7 @@ states: - DecisionRequestRound - FailedMultiplexerRound - FinalizationRound +- FinishedSellOutcomeTokenTxRound - HandleFailedTxRound - ImpossibleRound - MechRequestRound @@ -172,10 +176,12 @@ transition_func: (MechResponseRound, DONE): DecisionReceiveRound (MechResponseRound, NO_MAJORITY): MechResponseRound (MechResponseRound, ROUND_TIMEOUT): MechResponseRound - (PostTxSettlementRound, BET_PLACEMENT_DONE): RedeemRound + (PostTxSettlementRound, BET_PLACEMENT_NO_SELL_DONE): RedeemRound + (PostTxSettlementRound, BET_PLACEMENT_SELL_DONE): RedeemRound (PostTxSettlementRound, MECH_REQUESTING_DONE): MechResponseRound (PostTxSettlementRound, REDEEMING_DONE): CallCheckpointRound (PostTxSettlementRound, ROUND_TIMEOUT): PostTxSettlementRound + (PostTxSettlementRound, SELL_OUTCOME_TOKEN_DONE): FinishedSellOutcomeTokenTxRound (PostTxSettlementRound, STAKING_DONE): ResetAndPauseRound (PostTxSettlementRound, SUBSCRIPTION_DONE): ClaimRound (PostTxSettlementRound, UNRECOGNIZED): FailedMultiplexerRound diff --git a/packages/valory/skills/trader_abci/skill.yaml b/packages/valory/skills/trader_abci/skill.yaml index 82acd3971..01f90a9ba 100644 --- a/packages/valory/skills/trader_abci/skill.yaml +++ b/packages/valory/skills/trader_abci/skill.yaml @@ -11,7 +11,7 @@ fingerprint: behaviours.py: bafybeigc6hszbu66ccajny5eh7thfgsrlr36je4mzziwp4mupgvtaeu6aa composition.py: bafybeifxerfvssuhodqmtvkz6umlmrmdqjv5ptpszhnwlavzxaavdpdyly dialogues.py: bafybeiebofyykseqp3fmif36cqmmyf3k7d2zbocpl6t6wnlpv4szghrxbm - fsm_specification.yaml: bafybeigfs3ffn5r3uo4d4aif7uwe3u7z5eqdx42zegifz6wswrsdx4d6xm + fsm_specification.yaml: bafybeiab3boumxzlycla4yp7gyfrdncks64gnfdskoatwqsynjtru36kye handlers.py: bafybeibbxybbi66em63ad33cllymypr3za3f5xvor3m2krhuxoyxnqjnxu models.py: bafybeih2vkf4ln7n7ar27iemho7w7sdr4clmhbnhbcznmsri6mc2skkky4 tests/__init__.py: bafybeiadatapyjh3e7ucg2ehz77oms3ihrbutwb2cs2tkjehy54utwvuyi @@ -26,9 +26,9 @@ skills: - valory/reset_pause_abci:0.1.0:bafybeigrdlxed3xlsnxtjhnsbl3cojruihxcqx4jxhgivkd5i2fkjncgba - valory/transaction_settlement_abci:0.1.0:bafybeic7q7recyka272udwcupblwbkc3jkodgp74fvcdxb7urametg5dae - valory/termination_abci:0.1.0:bafybeib5l7jhew5ic6iq24dd23nidcoimzqkrk556gqywhoziatj33zvwm -- valory/market_manager_abci:0.1.0:bafybeiaru2d32wpmcgqs64eepxud4idgubc3vmsbdwbia7gygipql2mmqi -- valory/decision_maker_abci:0.1.0:bafybeie5xp55kt3cjghhmk4v6mdcagfgk4vkneaxdttnxmtdcbfrxwc26a -- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeicztsvr3hnmzlg6irafx3phqfftmnk4zrfph4krsnpinekkjdlon4 +- valory/market_manager_abci:0.1.0:bafybeiaf37mqv45yptqqd6e7fxv5cjrrxrh4pjpe3bs6zkiou53e5lpoia +- valory/decision_maker_abci:0.1.0:bafybeieqpqlpr4f2ulpnecw6qizaq2vucmkqnggmdj5n5wi5bzmuipbj4a +- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeigsdvhghcfwrwttekotansxa3ps6jrul5xpt2bkrdmiu6y5osfnxi - valory/staking_abci:0.1.0:bafybeicupccurmrg7qesivonlyt3nryarsmk5qf5yh6auno64wn45bybvq - valory/check_stop_trading_abci:0.1.0:bafybeieduekpd4zbvjztyxyooppqnmjvup6jfp74uo6hhupvtvzzscdzkq - valory/mech_interact_abci:0.1.0:bafybeid6m3i5ofq7vuogqapdnoshhq7mswmudhvfcr2craw25fdwtoe3lm @@ -282,10 +282,12 @@ models: p_yes_field_part: p_yes_ p_no_field_part: p_no_ confidence_field_part: confidence_ + info_utility_field_part: info_utility_ part_prefix_mode: true bet_amount_field: collateral_amount results_filename: benchmarking_results.csv randomness: benchmarking_randomness + nr_mech_calls: 60 class_name: BenchmarkingMode acc_info_fields: args: diff --git a/packages/valory/skills/tx_settlement_multiplexer_abci/fsm_specification.yaml b/packages/valory/skills/tx_settlement_multiplexer_abci/fsm_specification.yaml index d21d8966f..d671e1d6b 100644 --- a/packages/valory/skills/tx_settlement_multiplexer_abci/fsm_specification.yaml +++ b/packages/valory/skills/tx_settlement_multiplexer_abci/fsm_specification.yaml @@ -1,11 +1,13 @@ alphabet_in: -- BET_PLACEMENT_DONE +- BET_PLACEMENT_NO_SELL_DONE +- BET_PLACEMENT_SELL_DONE - CHECKS_PASSED - MECH_REQUESTING_DONE - NO_MAJORITY - REDEEMING_DONE - REFILL_REQUIRED - ROUND_TIMEOUT +- SELL_OUTCOME_TOKEN_DONE - STAKING_DONE - SUBSCRIPTION_DONE - UNRECOGNIZED @@ -16,6 +18,7 @@ final_states: - FinishedBetPlacementTxRound - FinishedMechRequestTxRound - FinishedRedeemingTxRound +- FinishedSellOutcomeTokenTxRound - FinishedStakingTxRound - FinishedSubscriptionTxRound label: TxSettlementMultiplexerAbciApp @@ -28,15 +31,18 @@ states: - FinishedBetPlacementTxRound - FinishedMechRequestTxRound - FinishedRedeemingTxRound +- FinishedSellOutcomeTokenTxRound - FinishedStakingTxRound - FinishedSubscriptionTxRound - PostTxSettlementRound - PreTxSettlementRound transition_func: - (PostTxSettlementRound, BET_PLACEMENT_DONE): FinishedBetPlacementTxRound + (PostTxSettlementRound, BET_PLACEMENT_NO_SELL_DONE): FinishedBetPlacementTxRound + (PostTxSettlementRound, BET_PLACEMENT_SELL_DONE): FinishedBetPlacementTxRound (PostTxSettlementRound, MECH_REQUESTING_DONE): FinishedMechRequestTxRound (PostTxSettlementRound, REDEEMING_DONE): FinishedRedeemingTxRound (PostTxSettlementRound, ROUND_TIMEOUT): PostTxSettlementRound + (PostTxSettlementRound, SELL_OUTCOME_TOKEN_DONE): FinishedSellOutcomeTokenTxRound (PostTxSettlementRound, STAKING_DONE): FinishedStakingTxRound (PostTxSettlementRound, SUBSCRIPTION_DONE): FinishedSubscriptionTxRound (PostTxSettlementRound, UNRECOGNIZED): FailedMultiplexerRound diff --git a/packages/valory/skills/tx_settlement_multiplexer_abci/rounds.py b/packages/valory/skills/tx_settlement_multiplexer_abci/rounds.py index cee817002..ec8912db0 100644 --- a/packages/valory/skills/tx_settlement_multiplexer_abci/rounds.py +++ b/packages/valory/skills/tx_settlement_multiplexer_abci/rounds.py @@ -42,6 +42,9 @@ SubscriptionRound, ) from packages.valory.skills.decision_maker_abci.states.redeem import RedeemRound +from packages.valory.skills.decision_maker_abci.states.sell_outcome_token import ( + SellOutcomeTokenRound, +) from packages.valory.skills.mech_interact_abci.states.request import MechRequestRound from packages.valory.skills.staking_abci.rounds import CallCheckpointRound @@ -52,8 +55,10 @@ class Event(Enum): CHECKS_PASSED = "checks_passed" REFILL_REQUIRED = "refill_required" MECH_REQUESTING_DONE = "mech_requesting_done" - BET_PLACEMENT_DONE = "bet_placement_done" + BET_PLACEMENT_NO_SELL_DONE = "bet_placement_done_no_sell" + BET_PLACEMENT_SELL_DONE = "bet_placement_done_sell" REDEEMING_DONE = "redeeming_done" + SELL_OUTCOME_TOKEN_DONE = "sell_outcome_token_done" # nosec STAKING_DONE = "staking_done" SUBSCRIPTION_DONE = "subscription_done" ROUND_TIMEOUT = "round_timeout" @@ -91,15 +96,19 @@ def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Enum]]: """ submitter_to_event: Dict[str, Event] = { MechRequestRound.auto_round_id(): Event.MECH_REQUESTING_DONE, - BetPlacementRound.auto_round_id(): Event.BET_PLACEMENT_DONE, + BetPlacementRound.auto_round_id(): Event.BET_PLACEMENT_NO_SELL_DONE, RedeemRound.auto_round_id(): Event.REDEEMING_DONE, CallCheckpointRound.auto_round_id(): Event.STAKING_DONE, SubscriptionRound.auto_round_id(): Event.SUBSCRIPTION_DONE, + SellOutcomeTokenRound.auto_round_id(): Event.SELL_OUTCOME_TOKEN_DONE, } synced_data = SynchronizedData(self.synchronized_data.db) event = submitter_to_event.get(synced_data.tx_submitter, Event.UNRECOGNIZED) + if synced_data.vote != synced_data.previous_vote: + event = Event.BET_PLACEMENT_SELL_DONE + # if a mech request was just performed, increase the utilized tool's counter if event == Event.MECH_REQUESTING_DONE: policy = synced_data.policy @@ -108,11 +117,22 @@ def end_block(self) -> Optional[Tuple[BaseSynchronizedData, Enum]]: self.synchronized_data.update(policy=policy_update) # if a bet was just placed, edit the utilized tools mapping - if event == Event.BET_PLACEMENT_DONE: + if ( + event == Event.BET_PLACEMENT_NO_SELL_DONE + or event == Event.BET_PLACEMENT_SELL_DONE + ): utilized_tools = synced_data.utilized_tools utilized_tools[synced_data.final_tx_hash] = synced_data.mech_tool tools_update = json.dumps(utilized_tools, sort_keys=True) - self.synchronized_data.update(utilized_tools=tools_update) + vote = synced_data.vote + self.synchronized_data.update( + utilized_tools=tools_update, previous_vote=vote + ) + + # if all tokens for an outcome have been sold, set the previous vote to None as we no longer have any + # investment in that outcome + if event == Event.SELL_OUTCOME_TOKEN_DONE: + self.synchronized_data.update(previous_vote=None) return synced_data, event @@ -141,6 +161,10 @@ class FinishedSubscriptionTxRound(DegenerateRound): """Finished subscription round.""" +class FinishedSellOutcomeTokenTxRound(DegenerateRound): + """Finished sell outcome token round.""" + + class FailedMultiplexerRound(DegenerateRound): """Round that represents failure in identifying the transmitter round.""" @@ -160,21 +184,24 @@ class TxSettlementMultiplexerAbciApp(AbciApp[Event]): - round timeout: 0. 1. PostTxSettlementRound - mech requesting done: 3. - - bet placement done: 4. + - bet placement done no sell: 4. + - bet placement done sell: 4. - redeeming done: 6. + - sell outcome token done: 8. - staking done: 7. - subscription done: 5. - round timeout: 1. - - unrecognized: 8. + - unrecognized: 9. 2. ChecksPassedRound 3. FinishedMechRequestTxRound 4. FinishedBetPlacementTxRound 5. FinishedSubscriptionTxRound 6. FinishedRedeemingTxRound 7. FinishedStakingTxRound - 8. FailedMultiplexerRound + 8. FinishedSellOutcomeTokenTxRound + 9. FailedMultiplexerRound - Final states: {ChecksPassedRound, FailedMultiplexerRound, FinishedBetPlacementTxRound, FinishedMechRequestTxRound, FinishedRedeemingTxRound, FinishedStakingTxRound, FinishedSubscriptionTxRound} + Final states: {ChecksPassedRound, FailedMultiplexerRound, FinishedBetPlacementTxRound, FinishedMechRequestTxRound, FinishedRedeemingTxRound, FinishedSellOutcomeTokenTxRound, FinishedStakingTxRound, FinishedSubscriptionTxRound} Timeouts: round timeout: 30.0 @@ -191,8 +218,10 @@ class TxSettlementMultiplexerAbciApp(AbciApp[Event]): }, PostTxSettlementRound: { Event.MECH_REQUESTING_DONE: FinishedMechRequestTxRound, - Event.BET_PLACEMENT_DONE: FinishedBetPlacementTxRound, + Event.BET_PLACEMENT_NO_SELL_DONE: FinishedBetPlacementTxRound, + Event.BET_PLACEMENT_SELL_DONE: FinishedBetPlacementTxRound, Event.REDEEMING_DONE: FinishedRedeemingTxRound, + Event.SELL_OUTCOME_TOKEN_DONE: FinishedSellOutcomeTokenTxRound, Event.STAKING_DONE: FinishedStakingTxRound, Event.SUBSCRIPTION_DONE: FinishedSubscriptionTxRound, Event.ROUND_TIMEOUT: PostTxSettlementRound, @@ -204,6 +233,7 @@ class TxSettlementMultiplexerAbciApp(AbciApp[Event]): FinishedSubscriptionTxRound: {}, FinishedRedeemingTxRound: {}, FinishedStakingTxRound: {}, + FinishedSellOutcomeTokenTxRound: {}, FailedMultiplexerRound: {}, } event_to_timeout: Dict[Event, float] = { @@ -216,6 +246,7 @@ class TxSettlementMultiplexerAbciApp(AbciApp[Event]): FinishedRedeemingTxRound, FinishedStakingTxRound, FinishedSubscriptionTxRound, + FinishedSellOutcomeTokenTxRound, FailedMultiplexerRound, } db_pre_conditions: Dict[AppState, Set[str]] = { @@ -230,4 +261,5 @@ class TxSettlementMultiplexerAbciApp(AbciApp[Event]): FinishedStakingTxRound: set(), FailedMultiplexerRound: set(), FinishedSubscriptionTxRound: set(), + FinishedSellOutcomeTokenTxRound: set(), } diff --git a/packages/valory/skills/tx_settlement_multiplexer_abci/skill.yaml b/packages/valory/skills/tx_settlement_multiplexer_abci/skill.yaml index a5be8144e..c485aff94 100644 --- a/packages/valory/skills/tx_settlement_multiplexer_abci/skill.yaml +++ b/packages/valory/skills/tx_settlement_multiplexer_abci/skill.yaml @@ -10,10 +10,10 @@ fingerprint: __init__.py: bafybeide6k22zk4f3hyzhpapaoddsnxpw5elqcfvrxxj4nfvpzctv6jqhu behaviours.py: bafybeictumcqn2pgo7y2duemvzoaafognfhl6s6il3tv53hq66tf7xgpsu dialogues.py: bafybeiebofyykseqp3fmif36cqmmyf3k7d2zbocpl6t6wnlpv4szghrxbm - fsm_specification.yaml: bafybeicd7us5ffvsrxm7ybaanyzojbhmo7xtsfd3swtdem4dfsxkidxw7a + fsm_specification.yaml: bafybeieoyrx3nt3qsvd5n2bkryzq6twvrphmbqn3fs4gts6eoy2tvksxoy handlers.py: bafybeiafbqr7ojfcbwohvee7x4zzswad3ymfrrbjlfz7uuuttmn3qdfs6q models.py: bafybeigtmxoecoow663hgqnyinxarlrttyyt5ghpbdamdv4tc4kikcfx3a - rounds.py: bafybeig3dhhrf5tkj63b3bk2mqfprcwzk3galz2ukzvdenz4g2femaixku + rounds.py: bafybeihbxi4m7eyvkmenhcdzy3ijzajaxqrcyts3asvqe36l2til7kngzy tests/__init__.py: bafybeiat74pbtmxvylsz7karp57qp2v7y6wtrsz572jkrghbcssoudgjay tests/test_handlers.py: bafybeiayuktfupylm3p3ygufjb66swzxhpbmioqoffwuauakfgbkwrv7ma fingerprint_ignore_patterns: [] @@ -23,7 +23,7 @@ protocols: - valory/ledger_api:1.0.0:bafybeihdk6psr4guxmbcrc26jr2cbgzpd5aljkqvpwo64bvaz7tdti2oni skills: - valory/abstract_round_abci:0.1.0:bafybeib733xfbndtpvkf44mtk7oyodnficgloo6xhn7xmqxxeos33es65u -- valory/decision_maker_abci:0.1.0:bafybeie5xp55kt3cjghhmk4v6mdcagfgk4vkneaxdttnxmtdcbfrxwc26a +- valory/decision_maker_abci:0.1.0:bafybeieqpqlpr4f2ulpnecw6qizaq2vucmkqnggmdj5n5wi5bzmuipbj4a - valory/staking_abci:0.1.0:bafybeicupccurmrg7qesivonlyt3nryarsmk5qf5yh6auno64wn45bybvq - valory/mech_interact_abci:0.1.0:bafybeid6m3i5ofq7vuogqapdnoshhq7mswmudhvfcr2craw25fdwtoe3lm behaviours: