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 1722fb49f..1809f6b85 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": "bafybeigmclk3pe3h2b57kcem4xlhkfgca5sosbstyqqoq6pnkt7uuhzvwi", - "skill/valory/decision_maker_abci/0.1.0": "bafybeiczxtmesnkxtcm55bkjcsuyg7qllywdioonefr2fovripwv27yozm", - "skill/valory/trader_abci/0.1.0": "bafybeiahjgvfeq3b6c5fq3hkvooermantgqv5tbpn5we5fumsnsyipe5jy", - "skill/valory/tx_settlement_multiplexer_abci/0.1.0": "bafybeid6nvwb4e2tllkft252vaqtf24xmrfgd4inf44vzzounp4ms4xn4m", + "skill/valory/market_manager_abci/0.1.0": "bafybeidjcrfbucn2tcv5hhkubsj3rg23aelcwustirnlikrp5tmtpz3wf4", + "skill/valory/decision_maker_abci/0.1.0": "bafybeie3hsdpcbsz7njcjz6ntld5t5jdb6c2usgtg6q4zfibmsux3qrq3m", + "skill/valory/trader_abci/0.1.0": "bafybeics7hoohukzjnbazs7puelzuhm6qsstaoe32nuvcfjy7whlsra654", + "skill/valory/tx_settlement_multiplexer_abci/0.1.0": "bafybeigaw742u3t4vrrdmc4dbqszfexuaxifr4i5ent5nfayaalzj6pely", "skill/valory/staking_abci/0.1.0": "bafybeibjgipzfle3b2gtuh42u2y6umgf47a5qrdtbvni46tox4lgv6wm4i", "skill/valory/check_stop_trading_abci/0.1.0": "bafybeier2qz5xkiha5krgav7hfrxvb5666csxyko4drfgouj5htjmmvwni", - "agent/valory/trader/0.1.0": "bafybeihwgq4znzidrzin3fq2bgyk7avn43z67cuxqunnev7vayfhilotdi", - "service/valory/trader/0.1.0": "bafybeibvzb24iz34faipmhvmux65vav6xfvh7ytydc2oxo3yupjxlam7ce", - "service/valory/trader_pearl/0.1.0": "bafybeiexnbofsonvfsuftx5jzrcdgp475yd4nnrvtlqs6utqdeq4scwwpu" + "agent/valory/trader/0.1.0": "bafybeigtfcgbgheepqwy2csg6mv6zn6elred2qvsmfdtdn7z66n7d2ashq", + "service/valory/trader/0.1.0": "bafybeigmpalhtvjzowehvrjwpo7whyhwnkyih3eqywvhharbcge3qgnwma", + "service/valory/trader_pearl/0.1.0": "bafybeiajduerdhztmreyv5ziyf6kuqr54ykfnkxb4ed7dufgm64muor2we" }, "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 4fa1e7b53..f495cc95b 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:bafybeid6nvwb4e2tllkft252vaqtf24xmrfgd4inf44vzzounp4ms4xn4m -- valory/market_manager_abci:0.1.0:bafybeigmclk3pe3h2b57kcem4xlhkfgca5sosbstyqqoq6pnkt7uuhzvwi -- valory/decision_maker_abci:0.1.0:bafybeiczxtmesnkxtcm55bkjcsuyg7qllywdioonefr2fovripwv27yozm -- valory/trader_abci:0.1.0:bafybeiahjgvfeq3b6c5fq3hkvooermantgqv5tbpn5we5fumsnsyipe5jy +- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeigaw742u3t4vrrdmc4dbqszfexuaxifr4i5ent5nfayaalzj6pely +- valory/market_manager_abci:0.1.0:bafybeidjcrfbucn2tcv5hhkubsj3rg23aelcwustirnlikrp5tmtpz3wf4 +- valory/decision_maker_abci:0.1.0:bafybeie3hsdpcbsz7njcjz6ntld5t5jdb6c2usgtg6q4zfibmsux3qrq3m +- valory/trader_abci:0.1.0:bafybeics7hoohukzjnbazs7puelzuhm6qsstaoe32nuvcfjy7whlsra654 - valory/staking_abci:0.1.0:bafybeibjgipzfle3b2gtuh42u2y6umgf47a5qrdtbvni46tox4lgv6wm4i - valory/check_stop_trading_abci:0.1.0:bafybeier2qz5xkiha5krgav7hfrxvb5666csxyko4drfgouj5htjmmvwni - valory/mech_interact_abci:0.1.0:bafybeid6m3i5ofq7vuogqapdnoshhq7mswmudhvfcr2craw25fdwtoe3lm 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 cc32ec182..c1157dfe3 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:bafybeihwgq4znzidrzin3fq2bgyk7avn43z67cuxqunnev7vayfhilotdi +agent: valory/trader:0.1.0:bafybeigtfcgbgheepqwy2csg6mv6zn6elred2qvsmfdtdn7z66n7d2ashq number_of_agents: 4 deployment: agent: diff --git a/packages/valory/services/trader_pearl/service.yaml b/packages/valory/services/trader_pearl/service.yaml index 36d6d6d52..a4b2b5893 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:bafybeihwgq4znzidrzin3fq2bgyk7avn43z67cuxqunnev7vayfhilotdi +agent: valory/trader:0.1.0:bafybeigtfcgbgheepqwy2csg6mv6zn6elred2qvsmfdtdn7z66n7d2ashq 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..bfef348b7 100644 --- a/packages/valory/skills/decision_maker_abci/behaviours/base.py +++ b/packages/valory/skills/decision_maker_abci/behaviours/base.py @@ -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 ( @@ -354,12 +355,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() @@ -719,6 +722,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..96b59ac87 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.""" 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 5ad86dec8..596e63e3f 100644 --- a/packages/valory/skills/decision_maker_abci/behaviours/decision_receive.py +++ b/packages/valory/skills/decision_maker_abci/behaviours/decision_receive.py @@ -518,8 +518,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() 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/skill.yaml b/packages/valory/skills/decision_maker_abci/skill.yaml index dac0ec1d2..b58ef666a 100644 --- a/packages/valory/skills/decision_maker_abci/skill.yaml +++ b/packages/valory/skills/decision_maker_abci/skill.yaml @@ -9,15 +9,16 @@ description: This skill is responsible for the decision making and placing the b license: Apache-2.0 aea_version: '>=1.0.0, <2.0.0' fingerprint: + .coverage: bafybeiamyhdetx6urx43npwxxsid2o26dt5akzyvtyvyeb33v4h4rhrqii README.md: bafybeia367zzdwndvlhw27rvnwodytjo3ms7gbc3q7mhrrjqjgfasnk47i __init__.py: bafybeih563ujnigeci2ldzh7hakbau6a222vsed7leg3b7lq32vcn3nm4a behaviours/__init__.py: bafybeih6ddz2ocvm6x6ytvlbcz6oi4snb5ee5xh5h65nq4w2qf7fd7zfky - behaviours/base.py: bafybeieqvxpzjnwjgq4ffhjojle2q7onif44nefrlza6pxriewisqcm324 - behaviours/bet_placement.py: bafybeia4listbfzsk4n4wkc4ycaftxgywjnl3mmpcqhuo3nwwia4n3oufu + behaviours/base.py: bafybeibgmle4ggpuocvpmexoofln6bu6bxtf4csusigr4d7yaobmn452vu + behaviours/bet_placement.py: bafybeihq4z2jhzzk4qrufydnedk5iplkfshexq3ktzfn65te4g6qpp346m behaviours/blacklisting.py: bafybeicah7vcruhbakrhpsxdmw3ftq72o5e3plfhhqsnvhk3vk6iud6og4 behaviours/check_benchmarking.py: bafybeiao2lyj7apezkqrpgsyzb3dwvrdgsrgtprf6iuhsmlsufvxfl5bci behaviours/claim_subscription.py: bafybeigbqkhc6mb73rbwaks32tfiqx6u2xza43uiy6rvbtrnqd6m4fru3e - behaviours/decision_receive.py: bafybeifrsnxref7yyl4hq4nmh4xa4rden4wygv5y5ugvqb5ji7zgj7mv5m + behaviours/decision_receive.py: bafybeidoyo6f5oxw2jpimt2vse3xaz6nhzhg4l4thzl6kmb2f7jkr3y5im behaviours/decision_request.py: bafybeia22omb7tvocyfe3z2ucn5au5mcas7dg37ha42u7znefzrewjpk7y behaviours/handle_failed_tx.py: bafybeidxpc6u575ymct5tdwutvzov6zqfdoio5irgldn3fw7q3lg36mmxm behaviours/order_subscription.py: bafybeib3maqohhx35wzryy4otdcjp5thkr4sbp27ksvwidy3pwm444itra @@ -25,6 +26,7 @@ fingerprint: behaviours/reedem.py: bafybeidjmhh6c6shbg25d7exmc4nnp4heqbqselwuxj7rp2ss665lrytxe behaviours/round_behaviour.py: bafybeih63hpia2bwwzu563hxs5yd3t5ycvxvkfnhvxbzghbyy3mw3xjl3i behaviours/sampling.py: bafybeihe2waj53xrhonw4mgipagsgl3xoorcwzuqzyxwhn3mw7mjyeewfa + behaviours/sell_outcome_token.py: bafybeicjpqnukexia457s73yp34yvgefpkgaikainmjf3sxgdk5c4kfqba behaviours/storage_manager.py: bafybeiez6daaj2bufxdcsghtmqybyrzdh74z26cc4ajsqsiy5krgjo2tla behaviours/tool_selection.py: bafybeienlxcgjs3ogyofli3d7q3p5rst3mcxxcnwqf7qolqjeefjtixeke dialogues.py: bafybeigpwuzku3we7axmxeamg7vn656maww6emuztau5pg3ebsoquyfdqm @@ -38,12 +40,12 @@ fingerprint: redeem_info.py: bafybeifiiix4gihfo4avraxt34sfw35v6dqq45do2drrssei2shbps63mm rounds.py: bafybeiazjcsukgefair52aw37hhvxzlopnzqqmi4ntqrinakljlcm4kt4a states/__init__.py: bafybeid23llnyp6j257dluxmrnztugo5llsrog7kua53hllyktz4dqhqoy - states/base.py: bafybeiatr6cqa3juxkhm6p4h7dhol7tfmxh2fo6nr2gtc6wuwyrtu3k3xm + states/base.py: bafybeibav3grn7qyr5herrq2idhd3h3pprlu6ntth2gzeebz6tpkmnus5q states/bet_placement.py: bafybeih5eopyxubczys5u5t3bdxbxpc7mmfdyqrpqsbm2uha5jc2phza4i states/blacklisting.py: bafybeiapelgjhbjjn4uq4z5gspyirqzwzgccg5anktrp5kxdwamfnfw5mi states/check_benchmarking.py: bafybeiabv6pq7q45jd3nkor5afmlycqgec5ctuwcfbdukkjjm4imesv4ni states/claim_subscription.py: bafybeiampifhdoztggwj6gthl2hfzecmjcwnm6nic2o47q4je7j4x3ujne - states/decision_receive.py: bafybeib3eahaehvmcrdojnlrz34q2crdb3ao6qun2wohokdf3t3ywwfa4y + states/decision_receive.py: bafybeigydiheb5a7l65vrtahnwtoj2yjgcxmxkpdtgxl4n56tptxlarhfy states/decision_request.py: bafybeiarv3r5j7cfvxmudki2llbdl2pvf24p5mvsva6bdgrylnwdyag5xy states/final_states.py: bafybeicjrrojo3gmfaxzicwloyorlnqgzl6a2avevo4nvhoh424zwzmbti states/handle_failed_tx.py: bafybeihewm2vernvhktuorljdupjqcg2p5vs6wvsira2d62wkoyo5xlzjm @@ -51,6 +53,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 +88,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 +104,7 @@ protocols: - valory/http:1.0.0:bafybeifugzl63kfdmwrxwphrnrhj7bn6iruxieme3a4ntzejf6kmtuwmae skills: - valory/abstract_round_abci:0.1.0:bafybeib733xfbndtpvkf44mtk7oyodnficgloo6xhn7xmqxxeos33es65u -- valory/market_manager_abci:0.1.0:bafybeigmclk3pe3h2b57kcem4xlhkfgca5sosbstyqqoq6pnkt7uuhzvwi +- valory/market_manager_abci:0.1.0:bafybeidjcrfbucn2tcv5hhkubsj3rg23aelcwustirnlikrp5tmtpz3wf4 - valory/transaction_settlement_abci:0.1.0:bafybeic7q7recyka272udwcupblwbkc3jkodgp74fvcdxb7urametg5dae - valory/mech_interact_abci:0.1.0:bafybeid6m3i5ofq7vuogqapdnoshhq7mswmudhvfcr2craw25fdwtoe3lm - valory/staking_abci:0.1.0:bafybeibjgipzfle3b2gtuh42u2y6umgf47a5qrdtbvni46tox4lgv6wm4i diff --git a/packages/valory/skills/decision_maker_abci/states/base.py b/packages/valory/skills/decision_maker_abci/states/base.py index 76dd9a67e..e94b0a2c0 100644 --- a/packages/valory/skills/decision_maker_abci/states/base.py +++ b/packages/valory/skills/decision_maker_abci/states/base.py @@ -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 678b6b2df..a82555cc2 100644 --- a/packages/valory/skills/decision_maker_abci/states/decision_receive.py +++ b/packages/valory/skills/decision_maker_abci/states/decision_receive.py @@ -54,6 +54,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 704217e09..5bf14ebb0 100644 --- a/packages/valory/skills/market_manager_abci/bets.py +++ b/packages/valory/skills/market_manager_abci/bets.py @@ -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" @@ -136,18 +138,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.""" @@ -225,7 +268,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." ) @@ -241,6 +284,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 ( diff --git a/packages/valory/skills/market_manager_abci/skill.yaml b/packages/valory/skills/market_manager_abci/skill.yaml index 0da138f12..109c15b96 100644 --- a/packages/valory/skills/market_manager_abci/skill.yaml +++ b/packages/valory/skills/market_manager_abci/skill.yaml @@ -6,10 +6,11 @@ description: This skill implements the MarketManager for an AEA. license: Apache-2.0 aea_version: '>=1.0.0, <2.0.0' fingerprint: + .coverage: bafybeiamyhdetx6urx43npwxxsid2o26dt5akzyvtyvyeb33v4h4rhrqii README.md: bafybeie6miwn67uin3bphukmf7qgiifh4xtm42i5v3nuyqxzxtehxsqvcq __init__.py: bafybeigrtedqzlq5mtql2ssjsdriw76ml3666m4e2c3fay6vmyzofl6v6e behaviours.py: bafybeicfoszavcyrzahe6qaydlaf27mpbwui7a6wgdbstydbzxmdisxhju - bets.py: bafybeiajcbn6wx76guw76htahep6rvndlj4rlofjo2g3bsiegcwv5syv3a + bets.py: bafybeiaajxu2e7a2gsvtqpggh7mzthyowhd6ukf47vmv2lcq3so4ih5tze 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 f00240b02..dfe664222 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 95ba76b79..50f5e22fc 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: bafybeiea2w6rhdxwc2ogvdnoxw2wbklnlspsoxyzhpz6h6x6dnctqurnoi + fsm_specification.yaml: bafybeidmvysaytecqg6e75qwl3x2badc2j33pzrqjpvzn2qmpdvk24z6v4 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:bafybeigmclk3pe3h2b57kcem4xlhkfgca5sosbstyqqoq6pnkt7uuhzvwi -- valory/decision_maker_abci:0.1.0:bafybeiczxtmesnkxtcm55bkjcsuyg7qllywdioonefr2fovripwv27yozm -- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeid6nvwb4e2tllkft252vaqtf24xmrfgd4inf44vzzounp4ms4xn4m +- valory/market_manager_abci:0.1.0:bafybeidjcrfbucn2tcv5hhkubsj3rg23aelcwustirnlikrp5tmtpz3wf4 +- valory/decision_maker_abci:0.1.0:bafybeie3hsdpcbsz7njcjz6ntld5t5jdb6c2usgtg6q4zfibmsux3qrq3m +- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeigaw742u3t4vrrdmc4dbqszfexuaxifr4i5ent5nfayaalzj6pely - valory/staking_abci:0.1.0:bafybeibjgipzfle3b2gtuh42u2y6umgf47a5qrdtbvni46tox4lgv6wm4i - valory/check_stop_trading_abci:0.1.0:bafybeier2qz5xkiha5krgav7hfrxvb5666csxyko4drfgouj5htjmmvwni - valory/mech_interact_abci:0.1.0:bafybeid6m3i5ofq7vuogqapdnoshhq7mswmudhvfcr2craw25fdwtoe3lm 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 a884fb930..29eec48a5 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:bafybeiczxtmesnkxtcm55bkjcsuyg7qllywdioonefr2fovripwv27yozm +- valory/decision_maker_abci:0.1.0:bafybeie3hsdpcbsz7njcjz6ntld5t5jdb6c2usgtg6q4zfibmsux3qrq3m - valory/staking_abci:0.1.0:bafybeibjgipzfle3b2gtuh42u2y6umgf47a5qrdtbvni46tox4lgv6wm4i - valory/mech_interact_abci:0.1.0:bafybeid6m3i5ofq7vuogqapdnoshhq7mswmudhvfcr2craw25fdwtoe3lm behaviours: