Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the strategy more sensible #72

Merged
merged 17 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ These are the description of the variables used by the Trader service:
- `OMEN_CREATORS`: addresses of the market creator(s) that the service will track
for placing bets on Omen. The address `0x89c5cc945dd550BcFfb72Fe42BfF002429F46Fec` corresponds to the Market creator agent for the Hackathon.
- `BET_AMOUNT_PER_THRESHOLD_X`: amount (wei) to bet when the prediction returned by the AI Mech surpasses a threshold of `X`% confidence for a given prediction market. In the values provided above the amounts vary between 0.03 xDAI (60% confidence) and 0.1 xDAI (100% confidence).
- `BET_THRESHOLD`: threshold (wei) for placing a bet. A bet will only be placed if `expected_return - bet_fees >= BET_THRESHOLD`. [See below](#some-notes-on-the-service).
- `BET_THRESHOLD`: threshold (wei) for placing a bet. A bet will only be placed if `potential_net_profit - BET_THRESHOLD >= 0`. [See below](#some-notes-on-the-service).
- `PROMPT_TEMPLATE`: prompt to be used with the prediction AI Mech. Please keep it as a single line including the placeholders `@{question}`, `@{yes}` and `@{no}`.


Expand Down Expand Up @@ -153,8 +153,15 @@ Once you have configured (exported) the environment variables, you are in positi
Please take into consideration the following:

- If the service does not have enough funds for placing a bet, you will see an `Event.INSUFICIENT_FUNDS` in the service logs.
- If the service determines that a bet is not profitable (i.e., `expected_return - bet_fees < BET_THRESHOLD`), you will see an `Event.UNPROFITABLE` in the service logs, and the service will transition into the blacklisting round. This round blacklists a bet for a predetermined amount of time. This can be adjusted by using the `BLACKLISTING_DURATION` environment variable.
- For simplicity, the current implementation considers `expected_return = bet_amount`, although this calculation might be refined.
- If the service determines that a bet is not profitable
(i.e., `potential_net_profit - BET_THRESHOLD < 0`), you will see an `Event.UNPROFITABLE` in the service logs,
and the service will transition into the blacklisting round.
This round blacklists a bet for a predetermined amount of time.
This can be adjusted by using the `BLACKLISTING_DURATION` environment variable.
- For simplicity,
the current implementation considers `potential_net_profit = num_shares - net_bet_amount - mech_price - BET_THRESHOLD`,
although this calculation might be refined.
The `net_bet_amount` is the bet amount minus the FPMM's fees.
- When assigning `BET_THRESHOLD` take into consideration that fees (at the time of writing this guide) are in the range of 0.02 xDAI. See, for example, [here](https://api.thegraph.com/subgraphs/name/protofire/omen-xdai/graphql?query=%7B%0A++fixedProductMarketMakers%28%0A++++where%3A+%7B%0A++++++creator_in%3A+%5B%220x89c5cc945dd550BcFfb72Fe42BfF002429F46Fec%22%5D%2C%0A++++++outcomeSlotCount%3A+2%2C%0A++++++isPendingArbitration%3A+false%0A++++%7D%2C%0A++++orderBy%3A+creationTimestamp%0A++++orderDirection%3A+desc%0A++%29%7B%0A++++fee%0A++%7D%0A%7D). We urge you to keep an eye on these fees, as they might vary.

## For advanced users
Expand Down
12 changes: 6 additions & 6 deletions packages/packages.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"dev": {
"skill/valory/market_manager_abci/0.1.0": "bafybeibwcm2wqoucpbep6pav4fb5mofhjahxiqszul4wcv4xr52dei6pe4",
"skill/valory/decision_maker_abci/0.1.0": "bafybeiejy2zxj5wzriay2vwecgvxxprwvs6rt32627jtjfn7gtsy46eimy",
"skill/valory/trader_abci/0.1.0": "bafybeiexo6pdhhdipnceixtls47jonhflcqsxnbv2c333zmgfxmoygfwgu",
"skill/valory/market_manager_abci/0.1.0": "bafybeih6tmi4blhdqkjgtvqkwbtbky5kzhl57h6ze4rtr3r2rh43pkhvyi",
"skill/valory/decision_maker_abci/0.1.0": "bafybeiapptasim4es2d6c5y77xgdb4gxerptjrqs5yuk2f4j6jtgkjxnx4",
"skill/valory/trader_abci/0.1.0": "bafybeib6ftuul7xywf6jo3djf5oxbpf2v56mk5jo3apcrjw6b6t6jyeuyu",
"contract/valory/market_maker/0.1.0": "bafybeiftimqgvrbval2lxp7au6y72amioo4gtcdth2dflrbwa47i6opyb4",
"agent/valory/trader/0.1.0": "bafybeidclpkg6uwii2d5gk74b4l6pv3vfgrg5tt3njumhk2ab3uidcogky",
"service/valory/trader/0.1.0": "bafybeidug5h7tac43yp5t6mfyl3qdmfwzh63otz63l547iiguosyyygmnq",
"agent/valory/trader/0.1.0": "bafybeigqa5m2glgteznsq5u7b6xozxacxwdc3fzqszjydur7jggdvz4bti",
"service/valory/trader/0.1.0": "bafybeifbkjth5pf3uivauzwy442z6fxq2brnpc4oa5o42fa4abyhcpnsxe",
"contract/valory/erc20/0.1.0": "bafybeifjwr6rwklgg2uk2zkfysn55qqy7dfi4jx7sek6lzdup37fynhpxe",
"skill/valory/tx_settlement_multiplexer_abci/0.1.0": "bafybeiffvwkifzq4eiqr4syfbbsfjcrsea5wj55ttismoxozt6trithu7u",
"skill/valory/tx_settlement_multiplexer_abci/0.1.0": "bafybeiekmihdxbomflh7baa4nnh73hgigd4taqsld7swa4lmlltpcisn6u",
"contract/valory/mech/0.1.0": "bafybeie753wdqks6k4x5fqlpo7tgll2avutjcaodpwlptqvzefsi5xbvai",
"contract/valory/realitio/0.1.0": "bafybeid5cdncqui3egi57eh6ptz2yqxsjpnmwzdc2siewuown3gktdwm6m",
"contract/valory/realitio_proxy/0.1.0": "bafybeibvndq6756qck7forgeavhdbn6ykgqs2ufyg7n5g6qdfpveatxuwy",
Expand Down
8 changes: 4 additions & 4 deletions packages/valory/agents/trader/aea-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ skills:
- valory/reset_pause_abci:0.1.0:bafybeifoihgilpfr76hc5skzspm6qehkwivx7ld2cy3veipcsi4gr2c7na
- valory/termination_abci:0.1.0:bafybeigcsls72uosoui2y5ppmnvsljjhnxakkeh3fdohklcg66aqq4g7xu
- valory/transaction_settlement_abci:0.1.0:bafybeiglsnh2hvfau5gab7requh34k4sbqwbjvrhhqjpes4hakcwq46cpi
- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeiffvwkifzq4eiqr4syfbbsfjcrsea5wj55ttismoxozt6trithu7u
- valory/market_manager_abci:0.1.0:bafybeibwcm2wqoucpbep6pav4fb5mofhjahxiqszul4wcv4xr52dei6pe4
- valory/decision_maker_abci:0.1.0:bafybeiejy2zxj5wzriay2vwecgvxxprwvs6rt32627jtjfn7gtsy46eimy
- valory/trader_abci:0.1.0:bafybeiexo6pdhhdipnceixtls47jonhflcqsxnbv2c333zmgfxmoygfwgu
- valory/tx_settlement_multiplexer_abci:0.1.0:bafybeiekmihdxbomflh7baa4nnh73hgigd4taqsld7swa4lmlltpcisn6u
- valory/market_manager_abci:0.1.0:bafybeih6tmi4blhdqkjgtvqkwbtbky5kzhl57h6ze4rtr3r2rh43pkhvyi
- valory/decision_maker_abci:0.1.0:bafybeiapptasim4es2d6c5y77xgdb4gxerptjrqs5yuk2f4j6jtgkjxnx4
- valory/trader_abci:0.1.0:bafybeib6ftuul7xywf6jo3djf5oxbpf2v56mk5jo3apcrjw6b6t6jyeuyu
default_ledger: ethereum
required_ledgers:
- ethereum
Expand Down
2 changes: 1 addition & 1 deletion packages/valory/services/trader/service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ license: Apache-2.0
fingerprint:
README.md: bafybeigtuothskwyvrhfosps2bu6suauycolj67dpuxqvnicdrdu7yhtvq
fingerprint_ignore_patterns: []
agent: valory/trader:0.1.0:bafybeidclpkg6uwii2d5gk74b4l6pv3vfgrg5tt3njumhk2ab3uidcogky
agent: valory/trader:0.1.0:bafybeigqa5m2glgteznsq5u7b6xozxacxwdc3fzqszjydur7jggdvz4bti
number_of_agents: 4
deployment: {}
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

"""This module contains the behaviour for the decision-making of the skill."""

from math import prod
from typing import Any, Generator, Optional, Tuple, Union

from packages.valory.contracts.mech.contract import Mech
Expand All @@ -36,6 +37,7 @@
from packages.valory.skills.decision_maker_abci.states.decision_receive import (
DecisionReceiveRound,
)
from packages.valory.skills.market_manager_abci.bets import BINARY_N_SLOTS


IPFS_HASH_PREFIX = "f01701220"
Expand Down Expand Up @@ -221,20 +223,81 @@ def _get_decision(

return self.mech_response.result.vote, self.mech_response.result.confidence

def _is_profitable(self, confidence: float) -> bool:
def _calc_binary_shares(self, net_bet_amount: int, vote: int) -> Tuple[int, int]:
"""Calculate the claimed shares. This calculation only works for binary markets."""
Comment on lines +227 to +228
Copy link
Collaborator Author

@Adamantios Adamantios Sep 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output of this method was tested with multiple examples and the returned number of shares is accurate.

bet = self.synchronized_data.sampled_bet

# calculate the pool's k (x*y=k)
token_amounts = bet.outcomeTokenAmounts
if token_amounts is None:
return 0, 0
k = prod(token_amounts)

# the OMEN market trades an equal amount of the investment to each of the tokens in the pool
# here we calculate the bet amount per pool's token
bet_per_token = net_bet_amount / BINARY_N_SLOTS

# calculate the number of the traded tokens
prices = bet.outcomeTokenMarginalPrices
if prices is None:
return 0, 0
tokens_traded = [int(bet_per_token / prices[i]) for i in range(BINARY_N_SLOTS)]

# get the shares for the answer that the service has selected
selected_shares = tokens_traded.pop(vote)

# get the shares for the opposite answer
other_shares = tokens_traded.pop()

# get the number of tokens in the pool for the answer that the service has selected
selected_type_tokens_in_pool = token_amounts.pop(vote)

# get the number of tokens in the pool for the opposite answer
other_tokens_in_pool = token_amounts.pop()

# the OMEN market then trades the opposite tokens to the tokens of the answer that has been selected,
# preserving the balance of the pool
# here we calculate the number of shares that we get after trading the tokens for the opposite answer
tokens_remaining_in_pool = int(k / (other_tokens_in_pool + other_shares))
swapped_shares = selected_type_tokens_in_pool - tokens_remaining_in_pool

# calculate the resulting number of shares if the service would take that position
num_shares = selected_shares + swapped_shares
# calculate the available number of shares
price = prices[vote]
available_shares = int(selected_type_tokens_in_pool * price)

return num_shares, available_shares

def _is_profitable(self, confidence: float, vote: int) -> bool:
"""Whether the decision is profitable or not."""
bet = self.synchronized_data.sampled_bet
bet_amount = self.params.get_bet_amount(confidence)
net_bet_amount = int(bet_amount * (1 - self.wei_to_native(bet.fee)))
num_shares, available_shares = self._calc_binary_shares(net_bet_amount, vote)
mech_price = self.synchronized_data.mech_price
bet_threshold = self.params.bet_threshold

if bet_threshold < 0:
if bet_threshold <= 0:
self.context.logger.warning(
f"A negative bet threshold was given ({bet_threshold}), "
f"which means that the profitability check will be bypassed!"
f"A non-positive bet threshold was given ({bet_threshold}). The threshold will be disabled, "
f"which means that any non-negative potential profit will be considered profitable!"
)
return True
bet_threshold = 0

bet_amount = self.params.get_bet_amount(confidence)
fee = self.synchronized_data.sampled_bet.fee
return bet_amount - fee >= bet_threshold
potential_net_profit = num_shares - net_bet_amount - mech_price - bet_threshold
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that the mech's price should be removed from the profitability calculation as this has already been paid.
If 0 < num_shares - net_bet_amount - bet_threshold < mech_price, then the service would at least have a chance to cover a part of the mech's costs plus the threshold's amount.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it can be removed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok! We need to add the mech cost to the outer decision making problem later

Copy link
Collaborator Author

@Adamantios Adamantios Sep 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed on 30bf870 and opened #75.

is_profitable = potential_net_profit >= 0 and num_shares <= available_shares
shares_out = self.wei_to_native(num_shares)
available_in = self.wei_to_native(available_shares)
shares_out_of = f"{shares_out} / {available_in}"
self.context.logger.info(
f"The current liquidity of the market is {bet.scaledLiquidityMeasure} xDAI. "
f"The potential net profit is {self.wei_to_native(potential_net_profit)} xDAI "
f"from buying {shares_out_of} shares for the option {bet.get_outcome(vote)}.\n"
f"Decision for profitability of this market: {is_profitable}."
)

return is_profitable

def async_act(self) -> Generator:
"""Do the action."""
Expand All @@ -243,7 +306,7 @@ def async_act(self) -> Generator:
vote, confidence = yield from self._get_decision()
is_profitable = None
if vote is not None and confidence is not None:
is_profitable = self._is_profitable(confidence)
is_profitable = self._is_profitable(confidence, vote)
payload = DecisionReceivePayload(
self.context.agent_address,
is_profitable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
SAFE_GAS,
WaitableConditionType,
)
from packages.valory.skills.decision_maker_abci.payloads import MultisigTxPayload
from packages.valory.skills.decision_maker_abci.payloads import RequestPayload
from packages.valory.skills.decision_maker_abci.states.decision_request import (
DecisionRequestRound,
)
Expand Down Expand Up @@ -197,11 +197,11 @@ def async_act(self) -> Generator:
"""Do the action."""

with self.context.benchmark_tool.measure(self.behaviour_id).local():
tx_submitter = mech_tx_hex = None
tx_submitter = mech_tx_hex = price = None
if self.n_slots_supported:
tx_submitter = self.matching_round.auto_round_id()
mech_tx_hex = yield from self._prepare_safe_tx()
price = self.price
agent = self.context.agent_address
payload = MultisigTxPayload(agent, tx_submitter, mech_tx_hex)

payload = RequestPayload(agent, tx_submitter, mech_tx_hex, price)
yield from self.finish_behaviour(payload)
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def _sample(self) -> Tuple[Optional[str], Optional[int]]:

idx = self._sampled_bet_idx(available_bets)

if self.synchronized_data.bets[idx].usdLiquidityMeasure == 0:
if self.synchronized_data.bets[idx].scaledLiquidityMeasure == 0:
msg = "There were no unprocessed bets with non-zero liquidity!"
self.context.logger.warning(msg)
return None, None
Expand Down
7 changes: 7 additions & 0 deletions packages/valory/skills/decision_maker_abci/payloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ class MultisigTxPayload(BaseTxPayload):
tx_hash: Optional[str]


@dataclass(frozen=True)
class RequestPayload(MultisigTxPayload):
"""Represents a transaction payload for preparing an on-chain transaction for a mech request."""

price: Optional[int]


@dataclass(frozen=True)
class VotingPayload(BaseTxPayload):
"""Represents a transaction payload for voting."""
Expand Down
14 changes: 7 additions & 7 deletions packages/valory/skills/decision_maker_abci/skill.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,25 @@ fingerprint:
behaviours/base.py: bafybeibrqxw7jdslsew226tss3p3s7fhj6rkwz6fig46zgnp54ijyspgly
behaviours/bet_placement.py: bafybeicdmayxmxvydechibri7z45wrx7n354eny4s5aval5mk2pqdszh64
behaviours/blacklisting.py: bafybeicvespraci44y2dtddy4wi7cdhjuyk6crjs7ztnssm2rcrovha3hm
behaviours/decision_receive.py: bafybeigjct7st66x7n3go5vdp62wtdxgmgw233hyyvd3a63ly4x3ptdqbq
behaviours/decision_request.py: bafybeienx5mvflzrgbocqdnqwkyvi3s5xxcgwcaxcrzd2vqazqntn3pyfi
behaviours/decision_receive.py: bafybeiagj6ilqz7rfjaqa4b3rtunyhigtxhwkb4auylquq77ltjdczksj4
behaviours/decision_request.py: bafybeifjlh5cfitjd6wjcvcgoji2bhsi4r5nzpqocotwprmn26eiphlmqq
behaviours/handle_failed_tx.py: bafybeidxpc6u575ymct5tdwutvzov6zqfdoio5irgldn3fw7q3lg36mmxm
behaviours/reedem.py: bafybeians77pl4tc6enkf4vmkzeg4kjjnjugtm5to36ma4z6x4a5hurmuq
behaviours/round_behaviour.py: bafybeifk5utwuaneima4rdeow7tcpbe6hcc2utlzxcw3w7vsm5zw7zpamm
behaviours/sampling.py: bafybeiesza5cols7opbpp3g3ozyqmskl5g6cqdjow4vsq4r7tet2xkyruq
behaviours/sampling.py: bafybeiadikynvkaofbko72jc45xthhmmjfmlkpgramormhxwk5u47rnwdu
dialogues.py: bafybeigpwuzku3we7axmxeamg7vn656maww6emuztau5pg3ebsoquyfdqm
fsm_specification.yaml: bafybeigrljw66oxyvn5wqecfgkcx7ozkjg7xuv75zcjmo25fft37qyed6y
handlers.py: bafybeihj33szgrcxnpd73s4nvluyxwwsvhjum2cuq3ilhhe6vfola3k7vy
models.py: bafybeibyfpucv2e6kltxpjkwobeg3u2ejrceba6cyuecuwbmpv4q7zmu3m
payloads.py: bafybeigate7v3haokrquzojwl6ycgf3zosclgjhmiuqhrx3kpbnj6hacte
payloads.py: bafybeifbnyviargcj5w5kbuuvc3o4y5sdogtuynd2b4ca4xsfbi3cqcwlm
redeem_info.py: bafybeie3s7syjr5dfwg33l4663zhkeiokvpulenndwboiyv4imzxr4bdyy
rounds.py: bafybeihpstybessozkb3hjxhf3gvf323zw4d575ihmxrsuzcyhqtbsruoq
states/__init__.py: bafybeid23llnyp6j257dluxmrnztugo5llsrog7kua53hllyktz4dqhqoy
states/base.py: bafybeigyicytkdcho5l4i6a6b724zv7otwekecvztbf7wvx7zh6crugbny
states/base.py: bafybeif42mqu6wu55iyjyqxto3poyta22gdsswgtus55lo4qpmv74wvlmm
states/bet_placement.py: bafybeibalhxhp2c4oljmiwqi6ds3g36fgtabmf42mb5sgq6z22znrcbhda
states/blacklisting.py: bafybeiao747i4z7owk6dmwfuzdijag55m3ryj3nowfoggvczpgk3koza44
states/decision_receive.py: bafybeifm3oyq2aji7f5yag6wpe4vr3ivi74pybdsk2jvmziiidx5nt7t4a
states/decision_request.py: bafybeiguje2p3qmqvg6bujf5n7ncr4ss63mviqp4rbnequel37ocuhhvgm
states/decision_request.py: bafybeic7otc3hjb753svbmur3yyk6szahc25yii3x4w4vcnpfz6jwvacuu
states/final_states.py: bafybeidiwhuyd5zm2cq7vhv2owcrxdpm7fnvn3db6p6tql4jz5hgpalflu
states/handle_failed_tx.py: bafybeihewm2vernvhktuorljdupjqcg2p5vs6wvsira2d62wkoyo5xlzjm
states/redeem.py: bafybeifl7qgs2xvm4nykloec5tq47sriqah3dzahv3gppvgtrrxzw5yyyq
Expand All @@ -53,7 +53,7 @@ protocols:
- valory/contract_api:1.0.0:bafybeiasywsvax45qmugus5kxogejj66c5taen27h4voriodz7rgushtqa
skills:
- valory/abstract_round_abci:0.1.0:bafybeif3cqkks5qx3lqi6nwwhebcirhazt2vidw3sueeqsyxvjeszjt3om
- valory/market_manager_abci:0.1.0:bafybeibwcm2wqoucpbep6pav4fb5mofhjahxiqszul4wcv4xr52dei6pe4
- valory/market_manager_abci:0.1.0:bafybeih6tmi4blhdqkjgtvqkwbtbky5kzhl57h6ze4rtr3r2rh43pkhvyi
- valory/transaction_settlement_abci:0.1.0:bafybeiglsnh2hvfau5gab7requh34k4sbqwbjvrhhqjpes4hakcwq46cpi
behaviours:
main:
Expand Down
9 changes: 7 additions & 2 deletions packages/valory/skills/decision_maker_abci/states/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"""This module contains the base functionality for the rounds of the decision-making abci app."""

from enum import Enum
from typing import Optional
from typing import Optional, Tuple

from packages.valory.skills.abstract_round_abci.base import (
CollectSameUntilThresholdRound,
Expand Down Expand Up @@ -70,6 +70,11 @@ def sampled_bet(self) -> Bet:
"""Get the sampled bet."""
return self.bets[self.sampled_bet_index]

@property
def mech_price(self) -> int:
"""Get the mech's request price."""
return int(self.db.get_strict("mech_price"))

@property
def vote(self) -> Optional[int]:
"""Get the bet's vote index."""
Expand Down Expand Up @@ -115,7 +120,7 @@ class TxPreparationRound(CollectSameUntilThresholdRound):
done_event = Event.DONE
none_event = Event.NONE
no_majority_event = Event.NO_MAJORITY
selection_key = (
selection_key: Tuple[str, ...] = (
get_name(SynchronizedData.tx_submitter),
get_name(SynchronizedData.most_voted_tx_hash),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,25 @@

"""This module contains the decision requesting state of the decision-making abci app."""

from typing import Type

from packages.valory.skills.abstract_round_abci.base import get_name
from packages.valory.skills.decision_maker_abci.payloads import (
MultisigTxPayload,
RequestPayload,
)
from packages.valory.skills.decision_maker_abci.states.base import (
Event,
SynchronizedData,
TxPreparationRound,
)


class DecisionRequestRound(TxPreparationRound):
"""A round in which the agents prepare a tx to initiate a request to a mech to determine the answer to a bet."""

payload_class: Type[MultisigTxPayload] = RequestPayload
selection_key = TxPreparationRound.selection_key + (
get_name(SynchronizedData.mech_price),
)
none_event = Event.SLOTS_UNSUPPORTED_ERROR
Loading