Skip to content

Markets Module

prediction_market_agent_tooling.markets

agent_market

ProcessedMarket

Bases: BaseModel

answer instance-attribute
answer: CategoricalProbabilisticAnswer

ProcessedTradedMarket

Bases: ProcessedMarket

trades instance-attribute
trades: list[PlacedTrade]
answer instance-attribute
answer: CategoricalProbabilisticAnswer

SortBy

Bases: str, Enum

CLOSING_SOONEST class-attribute instance-attribute
CLOSING_SOONEST = 'closing-soonest'
NEWEST class-attribute instance-attribute
NEWEST = 'newest'
HIGHEST_LIQUIDITY class-attribute instance-attribute
HIGHEST_LIQUIDITY = 'highest_liquidity'
LOWEST_LIQUIDITY class-attribute instance-attribute
LOWEST_LIQUIDITY = 'lowest_liquidity'
NONE class-attribute instance-attribute
NONE = 'none'

FilterBy

Bases: str, Enum

OPEN class-attribute instance-attribute
OPEN = 'open'
RESOLVED class-attribute instance-attribute
RESOLVED = 'resolved'
NONE class-attribute instance-attribute
NONE = 'none'

AgentMarket

Bases: BaseModel

Common market class that can be created from vendor specific markets. Contains everything that is needed for an agent to make a prediction.

base_url class-attribute
base_url: str
id instance-attribute
id: str
question instance-attribute
question: str
description instance-attribute
description: str | None
outcomes instance-attribute
outcomes: Sequence[OutcomeStr]
outcome_token_pool instance-attribute
outcome_token_pool: dict[OutcomeStr, OutcomeToken] | None
resolution instance-attribute
resolution: Resolution | None
created_time instance-attribute
created_time: DatetimeUTC | None
close_time instance-attribute
close_time: DatetimeUTC | None
probabilities instance-attribute
probabilities: dict[OutcomeStr, Probability]
url instance-attribute
url: str
volume instance-attribute
volume: CollateralToken | None
fees instance-attribute
fees: MarketFees
is_binary property
is_binary: bool
p_yes property
p_yes: Probability
p_no property
p_no: Probability
probable_resolution property
probable_resolution: Resolution
validate_probabilities
validate_probabilities(
    probs: dict[OutcomeStr, Probability],
    info: FieldValidationInfo,
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
@field_validator("probabilities")
def validate_probabilities(
    cls,
    probs: dict[OutcomeStr, Probability],
    info: FieldValidationInfo,
) -> dict[OutcomeStr, Probability]:
    outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
    if set(probs.keys()) != set(outcomes):
        raise ValueError("Keys of `probabilities` must match `outcomes` exactly.")
    total = float(sum(probs.values()))
    if not 0.999 <= total <= 1.001:
        # We simply log a warning because for some use-cases (e.g. existing positions), the
        # markets might be already closed hence no reliable outcome token prices exist anymore.
        logger.warning(f"Probabilities for market {info.data=} do not sum to 1.")
    return probs
validate_outcome_token_pool
validate_outcome_token_pool(
    outcome_token_pool: dict[str, OutcomeToken] | None,
    info: FieldValidationInfo,
) -> dict[str, OutcomeToken] | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
@field_validator("outcome_token_pool")
def validate_outcome_token_pool(
    cls,
    outcome_token_pool: dict[str, OutcomeToken] | None,
    info: FieldValidationInfo,
) -> dict[str, OutcomeToken] | None:
    outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
    if outcome_token_pool is not None:
        outcome_keys = set(outcome_token_pool.keys())
        expected_keys = set(outcomes)
        if outcome_keys != expected_keys:
            raise ValueError(
                f"Keys of outcome_token_pool ({outcome_keys}) do not match outcomes ({expected_keys})."
            )
    return outcome_token_pool
have_bet_on_market_since
have_bet_on_market_since(
    keys: APIKeys, since: timedelta
) -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
119
120
def have_bet_on_market_since(self, keys: APIKeys, since: timedelta) -> bool:
    raise NotImplementedError("Subclasses must implement this method")
get_outcome_token_pool_by_outcome
get_outcome_token_pool_by_outcome(
    outcome: OutcomeStr,
) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
122
123
124
125
126
127
128
def get_outcome_token_pool_by_outcome(self, outcome: OutcomeStr) -> OutcomeToken:
    if self.outcome_token_pool is None or not self.outcome_token_pool:
        return OutcomeToken(0)

    # We look up by index to avoid having to deal with case sensitivity issues.
    outcome_idx = self.get_outcome_index(outcome)
    return list(self.outcome_token_pool.values())[outcome_idx]
handle_legacy_fee
handle_legacy_fee(data: dict[str, Any]) -> dict[str, t.Any]
Source code in prediction_market_agent_tooling/markets/agent_market.py
130
131
132
133
134
135
136
@model_validator(mode="before")
def handle_legacy_fee(cls, data: dict[str, t.Any]) -> dict[str, t.Any]:
    # Backward compatibility for older `AgentMarket` without `fees`.
    if "fees" not in data and "fee" in data:
        data["fees"] = MarketFees(absolute=0.0, bet_proportion=data["fee"])
        del data["fee"]
    return data
market_outcome_for_probability_key
market_outcome_for_probability_key(
    probability_key: OutcomeStr,
) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
138
139
140
141
142
143
144
145
146
def market_outcome_for_probability_key(
    self, probability_key: OutcomeStr
) -> OutcomeStr:
    for market_outcome in self.outcomes:
        if market_outcome.lower() == probability_key.lower():
            return market_outcome
    raise ValueError(
        f"Could not find probability for probability key {probability_key}"
    )
probability_for_market_outcome
probability_for_market_outcome(
    market_outcome: OutcomeStr,
) -> Probability
Source code in prediction_market_agent_tooling/markets/agent_market.py
148
149
150
151
152
153
154
def probability_for_market_outcome(self, market_outcome: OutcomeStr) -> Probability:
    for k, v in self.probabilities.items():
        if k.lower() == market_outcome.lower():
            return v
    raise ValueError(
        f"Could not find probability for market outcome {market_outcome}"
    )
get_last_trade_p_yes
get_last_trade_p_yes() -> Probability | None

Get the last trade price for the YES outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1. Could be None if no trades were made.

Source code in prediction_market_agent_tooling/markets/agent_market.py
195
196
197
198
199
200
def get_last_trade_p_yes(self) -> Probability | None:
    """
    Get the last trade price for the YES outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1.
    Could be None if no trades were made.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_last_trade_p_no
get_last_trade_p_no() -> Probability | None

Get the last trade price for the NO outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1. Could be None if no trades were made.

Source code in prediction_market_agent_tooling/markets/agent_market.py
202
203
204
205
206
207
def get_last_trade_p_no(self) -> Probability | None:
    """
    Get the last trade price for the NO outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1.
    Could be None if no trades were made.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_last_trade_yes_outcome_price
get_last_trade_yes_outcome_price() -> (
    CollateralToken | None
)
Source code in prediction_market_agent_tooling/markets/agent_market.py
209
210
211
212
213
214
def get_last_trade_yes_outcome_price(self) -> CollateralToken | None:
    # Price on prediction markets are, by definition, equal to the probability of an outcome.
    # Just making it explicit in this function.
    if last_trade_p_yes := self.get_last_trade_p_yes():
        return CollateralToken(last_trade_p_yes)
    return None
get_last_trade_yes_outcome_price_usd
get_last_trade_yes_outcome_price_usd() -> USD | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
216
217
218
219
def get_last_trade_yes_outcome_price_usd(self) -> USD | None:
    if last_trade_yes_outcome_price := self.get_last_trade_yes_outcome_price():
        return self.get_token_in_usd(last_trade_yes_outcome_price)
    return None
get_last_trade_no_outcome_price
get_last_trade_no_outcome_price() -> CollateralToken | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
221
222
223
224
225
226
def get_last_trade_no_outcome_price(self) -> CollateralToken | None:
    # Price on prediction markets are, by definition, equal to the probability of an outcome.
    # Just making it explicit in this function.
    if last_trade_p_no := self.get_last_trade_p_no():
        return CollateralToken(last_trade_p_no)
    return None
get_last_trade_no_outcome_price_usd
get_last_trade_no_outcome_price_usd() -> USD | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
228
229
230
231
def get_last_trade_no_outcome_price_usd(self) -> USD | None:
    if last_trade_no_outcome_price := self.get_last_trade_no_outcome_price():
        return self.get_token_in_usd(last_trade_no_outcome_price)
    return None
get_liquidatable_amount
get_liquidatable_amount() -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
233
234
235
def get_liquidatable_amount(self) -> OutcomeToken:
    tiny_amount = self.get_tiny_bet_amount()
    return OutcomeToken.from_token(tiny_amount / 10)
get_token_in_usd
get_token_in_usd(x: CollateralToken) -> USD

Token of this market can have whatever worth (e.g. sDai and ETH markets will have different worth of 1 token). Use this to convert it to USD.

Source code in prediction_market_agent_tooling/markets/agent_market.py
237
238
239
240
241
def get_token_in_usd(self, x: CollateralToken) -> USD:
    """
    Token of this market can have whatever worth (e.g. sDai and ETH markets will have different worth of 1 token). Use this to convert it to USD.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_usd_in_token
get_usd_in_token(x: USD) -> CollateralToken

Markets on a single platform can have different tokens as collateral (sDai, wxDai, GNO, ...). Use this to convert USD to the token of this market.

Source code in prediction_market_agent_tooling/markets/agent_market.py
243
244
245
246
247
def get_usd_in_token(self, x: USD) -> CollateralToken:
    """
    Markets on a single platform can have different tokens as collateral (sDai, wxDai, GNO, ...). Use this to convert USD to the token of this market.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_sell_value_of_outcome_token
get_sell_value_of_outcome_token(
    outcome: OutcomeStr, amount: OutcomeToken
) -> CollateralToken

When you hold OutcomeToken(s), it's easy to calculate how much you get at the end if you win (1 OutcomeToken will equal to 1 Token). But use this to figure out, how much are these outcome tokens worth right now (for how much you can sell them).

Source code in prediction_market_agent_tooling/markets/agent_market.py
249
250
251
252
253
254
255
256
def get_sell_value_of_outcome_token(
    self, outcome: OutcomeStr, amount: OutcomeToken
) -> CollateralToken:
    """
    When you hold OutcomeToken(s), it's easy to calculate how much you get at the end if you win (1 OutcomeToken will equal to 1 Token).
    But use this to figure out, how much are these outcome tokens worth right now (for how much you can sell them).
    """
    raise NotImplementedError("Subclasses must implement this method")
get_in_usd
get_in_usd(x: USD | CollateralToken) -> USD
Source code in prediction_market_agent_tooling/markets/agent_market.py
258
259
260
261
def get_in_usd(self, x: USD | CollateralToken) -> USD:
    if isinstance(x, USD):
        return x
    return self.get_token_in_usd(x)
get_in_token
get_in_token(x: USD | CollateralToken) -> CollateralToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
263
264
265
266
def get_in_token(self, x: USD | CollateralToken) -> CollateralToken:
    if isinstance(x, CollateralToken):
        return x
    return self.get_usd_in_token(x)
get_tiny_bet_amount
get_tiny_bet_amount() -> CollateralToken

Tiny bet amount that the platform still allows us to do.

Source code in prediction_market_agent_tooling/markets/agent_market.py
268
269
270
271
272
def get_tiny_bet_amount(self) -> CollateralToken:
    """
    Tiny bet amount that the platform still allows us to do.
    """
    raise NotImplementedError("Subclasses must implement this method")
liquidate_existing_positions
liquidate_existing_positions(outcome: OutcomeStr) -> None
Source code in prediction_market_agent_tooling/markets/agent_market.py
274
275
def liquidate_existing_positions(self, outcome: OutcomeStr) -> None:
    raise NotImplementedError("Subclasses must implement this method")
place_bet
place_bet(outcome: OutcomeStr, amount: USD) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
277
278
def place_bet(self, outcome: OutcomeStr, amount: USD) -> str:
    raise NotImplementedError("Subclasses must implement this method")
buy_tokens
buy_tokens(outcome: OutcomeStr, amount: USD) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
280
281
def buy_tokens(self, outcome: OutcomeStr, amount: USD) -> str:
    return self.place_bet(outcome=outcome, amount=amount)
get_buy_token_amount
get_buy_token_amount(
    bet_amount: USD | CollateralToken, outcome: OutcomeStr
) -> OutcomeToken | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
283
284
285
286
def get_buy_token_amount(
    self, bet_amount: USD | CollateralToken, outcome: OutcomeStr
) -> OutcomeToken | None:
    raise NotImplementedError("Subclasses must implement this method")
sell_tokens
sell_tokens(
    outcome: OutcomeStr, amount: USD | OutcomeToken
) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
288
289
def sell_tokens(self, outcome: OutcomeStr, amount: USD | OutcomeToken) -> str:
    raise NotImplementedError("Subclasses must implement this method")
compute_fpmm_probabilities staticmethod
compute_fpmm_probabilities(
    balances: list[OutcomeWei],
) -> list[Probability]

Compute the implied probabilities in a Fixed Product Market Maker.

Parameters:

Name Type Description Default
balances List[float]

Balances of outcome tokens.

required

Returns:

Type Description
list[Probability]

List[float]: Implied probabilities for each outcome.

Source code in prediction_market_agent_tooling/markets/agent_market.py
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
@staticmethod
def compute_fpmm_probabilities(balances: list[OutcomeWei]) -> list[Probability]:
    """
    Compute the implied probabilities in a Fixed Product Market Maker.

    Args:
        balances (List[float]): Balances of outcome tokens.

    Returns:
        List[float]: Implied probabilities for each outcome.
    """
    if all(x.value == 0 for x in balances):
        return [Probability(0.0)] * len(balances)

    # converting to standard values for prod compatibility.
    values_balance = [i.value for i in balances]
    # Compute product of balances excluding each outcome
    excluded_products = []
    for i in range(len(values_balance)):
        other_balances = values_balance[:i] + values_balance[i + 1 :]
        excluded_products.append(prod(other_balances))

    # Normalize to sum to 1
    total = sum(excluded_products)
    if total == 0:
        return [Probability(0.0)] * len(balances)
    probabilities = [Probability(p / total) for p in excluded_products]

    return probabilities
build_probability_map_from_p_yes staticmethod
build_probability_map_from_p_yes(
    p_yes: Probability,
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
321
322
323
324
325
326
327
328
@staticmethod
def build_probability_map_from_p_yes(
    p_yes: Probability,
) -> dict[OutcomeStr, Probability]:
    return {
        OutcomeStr(YES_OUTCOME_LOWERCASE_IDENTIFIER): p_yes,
        OutcomeStr(NO_OUTCOME_LOWERCASE_IDENTIFIER): Probability(1.0 - p_yes),
    }
build_probability_map staticmethod
build_probability_map(
    outcome_token_amounts: list[OutcomeWei],
    outcomes: list[OutcomeStr],
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
330
331
332
333
334
335
@staticmethod
def build_probability_map(
    outcome_token_amounts: list[OutcomeWei], outcomes: list[OutcomeStr]
) -> dict[OutcomeStr, Probability]:
    probs = AgentMarket.compute_fpmm_probabilities(outcome_token_amounts)
    return {outcome: prob for outcome, prob in zip(outcomes, probs)}
get_markets staticmethod
get_markets(
    limit: int,
    sort_by: SortBy,
    filter_by: FilterBy = FilterBy.OPEN,
    created_after: Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
    fetch_categorical_markets: bool = False,
) -> t.Sequence[AgentMarket]
Source code in prediction_market_agent_tooling/markets/agent_market.py
337
338
339
340
341
342
343
344
345
346
@staticmethod
def get_markets(
    limit: int,
    sort_by: SortBy,
    filter_by: FilterBy = FilterBy.OPEN,
    created_after: t.Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
    fetch_categorical_markets: bool = False,
) -> t.Sequence["AgentMarket"]:
    raise NotImplementedError("Subclasses must implement this method")
get_binary_market staticmethod
get_binary_market(id: str) -> AgentMarket
Source code in prediction_market_agent_tooling/markets/agent_market.py
348
349
350
@staticmethod
def get_binary_market(id: str) -> "AgentMarket":
    raise NotImplementedError("Subclasses must implement this method")
redeem_winnings staticmethod
redeem_winnings(api_keys: APIKeys) -> None

On some markets (like Omen), it's needed to manually claim the winner bets. If it's not needed, just implement with pass.

Source code in prediction_market_agent_tooling/markets/agent_market.py
352
353
354
355
356
357
@staticmethod
def redeem_winnings(api_keys: APIKeys) -> None:
    """
    On some markets (like Omen), it's needed to manually claim the winner bets. If it's not needed, just implement with `pass`.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_trade_balance classmethod
get_trade_balance(api_keys: APIKeys) -> USD

Return balance that can be used to trade on the given market.

Source code in prediction_market_agent_tooling/markets/agent_market.py
359
360
361
362
363
364
@classmethod
def get_trade_balance(cls, api_keys: APIKeys) -> USD:
    """
    Return balance that can be used to trade on the given market.
    """
    raise NotImplementedError("Subclasses must implement this method")
verify_operational_balance staticmethod
verify_operational_balance(api_keys: APIKeys) -> bool

Return True if the user has enough of operational balance. If not needed, just return True. For example: Omen needs at least some xDai in the wallet to execute transactions.

Source code in prediction_market_agent_tooling/markets/agent_market.py
366
367
368
369
370
371
372
@staticmethod
def verify_operational_balance(api_keys: APIKeys) -> bool:
    """
    Return `True` if the user has enough of operational balance. If not needed, just return `True`.
    For example: Omen needs at least some xDai in the wallet to execute transactions.
    """
    raise NotImplementedError("Subclasses must implement this method")
store_prediction
store_prediction(
    processed_market: ProcessedMarket | None,
    keys: APIKeys,
    agent_name: str,
) -> None

If market allows to upload predictions somewhere, implement it in this method.

Source code in prediction_market_agent_tooling/markets/agent_market.py
374
375
376
377
378
379
380
381
382
383
def store_prediction(
    self,
    processed_market: ProcessedMarket | None,
    keys: APIKeys,
    agent_name: str,
) -> None:
    """
    If market allows to upload predictions somewhere, implement it in this method.
    """
    raise NotImplementedError("Subclasses must implement this method")
store_trades
store_trades(
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None

If market allows to upload trades somewhere, implement it in this method.

Source code in prediction_market_agent_tooling/markets/agent_market.py
385
386
387
388
389
390
391
392
393
394
395
def store_trades(
    self,
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None:
    """
    If market allows to upload trades somewhere, implement it in this method.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_bets_made_since staticmethod
get_bets_made_since(
    better_address: ChecksumAddress, start_time: DatetimeUTC
) -> list[Bet]
Source code in prediction_market_agent_tooling/markets/agent_market.py
397
398
399
400
401
@staticmethod
def get_bets_made_since(
    better_address: ChecksumAddress, start_time: DatetimeUTC
) -> list[Bet]:
    raise NotImplementedError("Subclasses must implement this method")
get_resolved_bets_made_since staticmethod
get_resolved_bets_made_since(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC,
    end_time: DatetimeUTC | None,
) -> list[ResolvedBet]
Source code in prediction_market_agent_tooling/markets/agent_market.py
403
404
405
406
407
408
409
@staticmethod
def get_resolved_bets_made_since(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC,
    end_time: DatetimeUTC | None,
) -> list[ResolvedBet]:
    raise NotImplementedError("Subclasses must implement this method")
is_closed
is_closed() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
411
412
def is_closed(self) -> bool:
    return self.close_time is not None and self.close_time <= utcnow()
is_resolved
is_resolved() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
414
415
def is_resolved(self) -> bool:
    return self.resolution is not None
get_liquidity
get_liquidity() -> CollateralToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
417
418
def get_liquidity(self) -> CollateralToken:
    raise NotImplementedError("Subclasses must implement this method")
has_liquidity
has_liquidity() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
420
421
def has_liquidity(self) -> bool:
    return self.get_liquidity() > 0
has_successful_resolution
has_successful_resolution() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
423
424
425
426
427
428
def has_successful_resolution(self) -> bool:
    return (
        self.resolution is not None
        and self.resolution.outcome is not None
        and not self.resolution.invalid
    )
has_unsuccessful_resolution
has_unsuccessful_resolution() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
430
431
def has_unsuccessful_resolution(self) -> bool:
    return self.resolution is not None and self.resolution.invalid
get_outcome_str_from_bool staticmethod
get_outcome_str_from_bool(outcome: bool) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
433
434
435
@staticmethod
def get_outcome_str_from_bool(outcome: bool) -> OutcomeStr:
    raise NotImplementedError("Subclasses must implement this method")
get_outcome_str
get_outcome_str(outcome_index: int) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
437
438
439
440
441
442
443
def get_outcome_str(self, outcome_index: int) -> OutcomeStr:
    try:
        return self.outcomes[outcome_index]
    except IndexError:
        raise IndexError(
            f"Outcome index `{outcome_index}` out of range for `{self.outcomes}`: `{self.outcomes}`."
        )
get_outcome_index
get_outcome_index(outcome: OutcomeStr) -> int
Source code in prediction_market_agent_tooling/markets/agent_market.py
445
446
447
448
449
450
def get_outcome_index(self, outcome: OutcomeStr) -> int:
    outcomes_lowercase = [o.lower() for o in self.outcomes]
    try:
        return outcomes_lowercase.index(outcome.lower())
    except ValueError:
        raise ValueError(f"Outcome `{outcome}` not found in `{self.outcomes}`.")
get_token_balance
get_token_balance(
    user_id: str, outcome: OutcomeStr
) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
452
453
def get_token_balance(self, user_id: str, outcome: OutcomeStr) -> OutcomeToken:
    raise NotImplementedError("Subclasses must implement this method")
get_position
get_position(user_id: str) -> ExistingPosition | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
455
456
def get_position(self, user_id: str) -> ExistingPosition | None:
    raise NotImplementedError("Subclasses must implement this method")
get_positions classmethod
get_positions(
    user_id: str,
    liquid_only: bool = False,
    larger_than: OutcomeToken = OutcomeToken(0),
) -> t.Sequence[ExistingPosition]

Get all non-zero positions a user has in any market.

If liquid_only is True, only return positions that can be sold.

If larger_than is not None, only return positions with a larger number of tokens than this amount.

Source code in prediction_market_agent_tooling/markets/agent_market.py
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
@classmethod
def get_positions(
    cls,
    user_id: str,
    liquid_only: bool = False,
    larger_than: OutcomeToken = OutcomeToken(0),
) -> t.Sequence[ExistingPosition]:
    """
    Get all non-zero positions a user has in any market.

    If `liquid_only` is True, only return positions that can be sold.

    If `larger_than` is not None, only return positions with a larger number
    of tokens than this amount.
    """
    raise NotImplementedError("Subclasses must implement this method")
can_be_traded
can_be_traded() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
475
476
477
478
def can_be_traded(self) -> bool:
    if self.is_closed() or not self.has_liquidity():
        return False
    return True
get_user_url classmethod
get_user_url(keys: APIKeys) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
480
481
482
@classmethod
def get_user_url(cls, keys: APIKeys) -> str:
    raise NotImplementedError("Subclasses must implement this method")
has_token_pool
has_token_pool() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
484
485
def has_token_pool(self) -> bool:
    return self.outcome_token_pool is not None
get_pool_tokens
get_pool_tokens(outcome: OutcomeStr) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
487
488
489
490
491
def get_pool_tokens(self, outcome: OutcomeStr) -> OutcomeToken:
    if not self.outcome_token_pool:
        raise ValueError("Outcome token pool is not available.")

    return self.outcome_token_pool[outcome]
get_user_balance staticmethod
get_user_balance(user_id: str) -> float
Source code in prediction_market_agent_tooling/markets/agent_market.py
493
494
495
@staticmethod
def get_user_balance(user_id: str) -> float:
    raise NotImplementedError("Subclasses must implement this method")
get_user_id staticmethod
get_user_id(api_keys: APIKeys) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
497
498
499
@staticmethod
def get_user_id(api_keys: APIKeys) -> str:
    raise NotImplementedError("Subclasses must implement this method")
get_most_recent_trade_datetime
get_most_recent_trade_datetime(
    user_id: str,
) -> DatetimeUTC | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
501
502
def get_most_recent_trade_datetime(self, user_id: str) -> DatetimeUTC | None:
    raise NotImplementedError("Subclasses must implement this method")

base_subgraph_handler

T module-attribute

T = TypeVar('T', bound=BaseModel)

BaseSubgraphHandler

BaseSubgraphHandler(timeout: int = 30)
Source code in prediction_market_agent_tooling/markets/base_subgraph_handler.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def __init__(self, timeout: int = 30) -> None:
    self.sg = Subgrounds(timeout=timeout)
    # Patch methods to retry on failure.
    self.sg.query_json = tenacity.retry(
        stop=tenacity.stop_after_attempt(3),
        wait=tenacity.wait_fixed(1),
        after=lambda x: logger.debug(f"query_json failed, {x.attempt_number=}."),
    )(self.sg.query_json)
    self.sg.load_subgraph = tenacity.retry(
        stop=tenacity.stop_after_attempt(3),
        wait=tenacity.wait_fixed(1),
        after=lambda x: logger.debug(f"load_subgraph failed, {x.attempt_number=}."),
    )(self.sg.load_subgraph)

    self.keys = APIKeys()
sg instance-attribute
sg = Subgrounds(timeout=timeout)
keys instance-attribute
keys = APIKeys()
do_query
do_query(
    fields: list[FieldPath], pydantic_model: Type[T]
) -> list[T]
Source code in prediction_market_agent_tooling/markets/base_subgraph_handler.py
47
48
49
50
51
def do_query(self, fields: list[FieldPath], pydantic_model: t.Type[T]) -> list[T]:
    result = self.sg.query_json(fields)
    items = self._parse_items_from_json(result)
    models = [pydantic_model.model_validate(i) for i in items]
    return models

blockchain_utils

UINT16_MAX module-attribute

UINT16_MAX = 2 ** 16 - 1

store_trades

store_trades(
    market_id: str,
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None
Source code in prediction_market_agent_tooling/markets/blockchain_utils.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
def store_trades(
    market_id: str,
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None:
    if traded_market is None:
        logger.warning(f"No prediction for market {market_id}, not storing anything.")
        return None

    yes_probability = traded_market.answer.get_yes_probability()
    if not yes_probability:
        logger.info("Skipping this since no yes_probability available.")
        return None
    reasoning = traded_market.answer.reasoning if traded_market.answer.reasoning else ""

    ipfs_hash_decoded = HexBytes(HASH_ZERO)
    if keys.enable_ipfs_upload:
        logger.info("Storing prediction on IPFS.")
        ipfs_hash = IPFSHandler(keys).store_agent_result(
            IPFSAgentResult(reasoning=reasoning, agent_name=agent_name)
        )
        ipfs_hash_decoded = ipfscidv0_to_byte32(ipfs_hash)

    # tx_hashes must be list of bytes32 (see Solidity contract).
    tx_hashes = [
        HexBytes(HexStr(i.id)) for i in traded_market.trades if i.id is not None
    ]

    estimated_probability_bps = int(yes_probability * BPS_CONSTANT)

    prediction = ContractPrediction(
        publisher=keys.bet_from_address,
        ipfs_hash=ipfs_hash_decoded,
        tx_hashes=tx_hashes,
        estimated_probability_bps=estimated_probability_bps,
    )
    tx_receipt = OmenAgentResultMappingContract().add_prediction(
        api_keys=keys,
        market_address=Web3.to_checksum_address(market_id),
        prediction=prediction,
        web3=web3,
    )
    logger.info(
        f"Added prediction to market {market_id}. - receipt {tx_receipt['transactionHash'].hex()}."
    )

categorize

infer_category

infer_category(
    question: str,
    categories: set[str],
    model: str = "gpt-3.5-turbo-0125",
) -> str
Source code in prediction_market_agent_tooling/markets/categorize.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@observe()
def infer_category(
    question: str,
    categories: set[str],
    model: str = "gpt-3.5-turbo-0125",
) -> str:
    try:
        from langchain.prompts import ChatPromptTemplate
        from langchain.schema.output_parser import StrOutputParser
        from langchain_openai import ChatOpenAI
    except ImportError:
        raise ImportError(
            "openai not installed, please install extras `langchain` to use this function."
        )
    prompt = ChatPromptTemplate.from_template(
        template="""Assign the following question: {question}

To one of these categories: {categories}

Write only the category itself, nothing else.
"""
    )

    research_evaluation_chain = (
        prompt
        | ChatOpenAI(
            model_name=model,
            openai_api_key=APIKeys().openai_api_key,
        )
        | StrOutputParser()
    )

    response: str = research_evaluation_chain.invoke(
        {"question": question, "categories": sorted(categories)},
        config=get_langfuse_langchain_config(),
    )

    formatted = response.strip().strip("'").strip('"').strip()

    return formatted

data_models

Decision module-attribute

Decision = Annotated[
    bool, BeforeValidator(to_boolean_outcome)
]

Resolution

Bases: BaseModel

outcome instance-attribute
outcome: OutcomeStr | None
invalid instance-attribute
invalid: bool
from_answer staticmethod
from_answer(answer: OutcomeStr) -> Resolution
Source code in prediction_market_agent_tooling/markets/data_models.py
25
26
27
@staticmethod
def from_answer(answer: OutcomeStr) -> "Resolution":
    return Resolution(outcome=answer, invalid=False)
find_outcome_matching_market
find_outcome_matching_market(
    market_outcomes: Sequence[OutcomeStr],
) -> OutcomeStr | None

Finds a matching outcome in the provided market outcomes.

Performs case-insensitive matching between this resolution's outcome and the provided market outcomes.

Source code in prediction_market_agent_tooling/markets/data_models.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def find_outcome_matching_market(
    self, market_outcomes: Sequence[OutcomeStr]
) -> OutcomeStr | None:
    """
    Finds a matching outcome in the provided market outcomes.

    Performs case-insensitive matching between this resolution's outcome
    and the provided market outcomes.

    """

    if not self.outcome:
        return None

    normalized_outcome = self.outcome.lower()
    for outcome in market_outcomes:
        if outcome.lower() == normalized_outcome:
            return outcome
    return None

Bet

Bases: BaseModel

id instance-attribute
id: str
amount instance-attribute
amount: CollateralToken
outcome instance-attribute
outcome: OutcomeStr
created_time instance-attribute
created_time: DatetimeUTC
market_question instance-attribute
market_question: str
market_id instance-attribute
market_id: str

ResolvedBet

Bases: Bet

market_outcome instance-attribute
market_outcome: OutcomeStr
resolved_time instance-attribute
resolved_time: DatetimeUTC
profit instance-attribute
profit: CollateralToken
is_correct property
is_correct: bool
id instance-attribute
id: str
amount instance-attribute
amount: CollateralToken
outcome instance-attribute
outcome: OutcomeStr
created_time instance-attribute
created_time: DatetimeUTC
market_question instance-attribute
market_question: str
market_id instance-attribute
market_id: str

ProbabilisticAnswer

Bases: BaseModel

p_yes instance-attribute
p_yes: Probability
confidence instance-attribute
confidence: float
reasoning class-attribute instance-attribute
reasoning: str | None = None
logprobs class-attribute instance-attribute
logprobs: list[FieldLogprobs] | None = None
p_no property
p_no: Probability
probable_resolution property
probable_resolution: Resolution

CategoricalProbabilisticAnswer

Bases: BaseModel

probabilities instance-attribute
probabilities: dict[OutcomeStr, Probability]
confidence instance-attribute
confidence: float
reasoning class-attribute instance-attribute
reasoning: str | None = None
probable_resolution property
probable_resolution: Resolution
to_probabilistic_answer
to_probabilistic_answer() -> ProbabilisticAnswer
Source code in prediction_market_agent_tooling/markets/data_models.py
135
136
137
138
139
140
def to_probabilistic_answer(self) -> ProbabilisticAnswer:
    p_yes = check_not_none(self.get_yes_probability())
    return ProbabilisticAnswer(
        p_yes=p_yes,
        confidence=self.confidence,
    )
from_probabilistic_answer staticmethod
from_probabilistic_answer(
    answer: ProbabilisticAnswer,
) -> CategoricalProbabilisticAnswer
Source code in prediction_market_agent_tooling/markets/data_models.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
@staticmethod
def from_probabilistic_answer(
    answer: ProbabilisticAnswer,
) -> "CategoricalProbabilisticAnswer":
    return CategoricalProbabilisticAnswer(
        probabilities={
            OutcomeStr(YES_OUTCOME_LOWERCASE_IDENTIFIER): answer.p_yes,
            OutcomeStr(NO_OUTCOME_LOWERCASE_IDENTIFIER): Probability(
                1 - answer.p_yes
            ),
        },
        confidence=answer.confidence,
        reasoning=answer.reasoning,
    )
probability_for_market_outcome
probability_for_market_outcome(
    market_outcome: OutcomeStr,
) -> Probability
Source code in prediction_market_agent_tooling/markets/data_models.py
157
158
159
160
161
162
163
def probability_for_market_outcome(self, market_outcome: OutcomeStr) -> Probability:
    for k, v in self.probabilities.items():
        if k.lower() == market_outcome.lower():
            return v
    raise ValueError(
        f"Could not find probability for market outcome {market_outcome}"
    )
get_yes_probability
get_yes_probability() -> Probability | None
Source code in prediction_market_agent_tooling/markets/data_models.py
165
166
167
168
169
170
171
172
173
def get_yes_probability(self) -> Probability | None:
    return next(
        (
            p
            for o, p in self.probabilities.items()
            if o.lower() == YES_OUTCOME_LOWERCASE_IDENTIFIER
        ),
        None,
    )

Position

Bases: BaseModel

market_id instance-attribute
market_id: str
amounts_current instance-attribute
amounts_current: dict[OutcomeStr, USD]
total_amount_current property
total_amount_current: USD

ExistingPosition

Bases: Position

amounts_potential instance-attribute
amounts_potential: dict[OutcomeStr, USD]
amounts_ot instance-attribute
amounts_ot: dict[OutcomeStr, OutcomeToken]
total_amount_potential property
total_amount_potential: USD
total_amount_ot property
total_amount_ot: OutcomeToken
market_id instance-attribute
market_id: str
amounts_current instance-attribute
amounts_current: dict[OutcomeStr, USD]
total_amount_current property
total_amount_current: USD

TradeType

Bases: str, Enum

SELL class-attribute instance-attribute
SELL = 'sell'
BUY class-attribute instance-attribute
BUY = 'buy'

Trade

Bases: BaseModel

trade_type instance-attribute
trade_type: TradeType
outcome instance-attribute
outcome: OutcomeStr
amount instance-attribute
amount: USD

PlacedTrade

Bases: Trade

id class-attribute instance-attribute
id: str | None = None
trade_type instance-attribute
trade_type: TradeType
outcome instance-attribute
outcome: OutcomeStr
amount instance-attribute
amount: USD
from_trade staticmethod
from_trade(trade: Trade, id: str) -> PlacedTrade
Source code in prediction_market_agent_tooling/markets/data_models.py
222
223
224
225
226
227
228
229
@staticmethod
def from_trade(trade: Trade, id: str) -> "PlacedTrade":
    return PlacedTrade(
        trade_type=trade.trade_type,
        outcome=trade.outcome,
        amount=trade.amount,
        id=id,
    )

SimulatedBetDetail

Bases: BaseModel

strategy instance-attribute
strategy: str
url instance-attribute
url: str
probabilities instance-attribute
probabilities: dict[OutcomeStr, Probability]
agent_prob_multi instance-attribute
agent_prob_multi: dict[OutcomeStr, Probability]
agent_conf instance-attribute
agent_conf: float
org_bet instance-attribute
org_bet: CollateralToken
sim_bet instance-attribute
sim_bet: CollateralToken
org_dir instance-attribute
org_dir: OutcomeStr
sim_dir instance-attribute
sim_dir: OutcomeStr
org_profit instance-attribute
org_profit: CollateralToken
sim_profit instance-attribute
sim_profit: CollateralToken
timestamp instance-attribute
timestamp: DatetimeUTC

SharpeOutput

Bases: BaseModel

annualized_volatility instance-attribute
annualized_volatility: float
mean_daily_return instance-attribute
mean_daily_return: float
annualized_sharpe_ratio instance-attribute
annualized_sharpe_ratio: float

SimulatedLifetimeDetail

Bases: BaseModel

p_yes_mse instance-attribute
p_yes_mse: float
total_bet_amount instance-attribute
total_bet_amount: CollateralToken
total_bet_profit instance-attribute
total_bet_profit: CollateralToken
total_simulated_amount instance-attribute
total_simulated_amount: CollateralToken
total_simulated_profit instance-attribute
total_simulated_profit: CollateralToken
roi instance-attribute
roi: float
simulated_roi instance-attribute
simulated_roi: float
maximize instance-attribute
maximize: float

to_boolean_outcome

to_boolean_outcome(value: str | bool) -> bool
Source code in prediction_market_agent_tooling/markets/data_models.py
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def to_boolean_outcome(value: str | bool) -> bool:
    if isinstance(value, bool):
        return value

    elif isinstance(value, str):
        value = value.lower().strip()

        if value in {"true", "yes", "y", "1"}:
            return True

        elif value in {"false", "no", "n", "0"}:
            return False

        else:
            raise ValueError(f"Expected a boolean string, but got {value}")

    else:
        raise ValueError(f"Expected a boolean or a string, but got {value}")

manifold

api

MANIFOLD_API_BASE_URL module-attribute
MANIFOLD_API_BASE_URL = 'https://api.manifold.markets'
MARKETS_LIMIT module-attribute
MARKETS_LIMIT = 1000
get_manifold_binary_markets
get_manifold_binary_markets(
    limit: int,
    term: str = "",
    topic_slug: Optional[str] = None,
    sort: Literal[
        "liquidity", "score", "newest", "close-date"
    ]
    | None = "liquidity",
    filter_: Literal[
        "open",
        "closed",
        "resolved",
        "closing-this-month",
        "closing-next-month",
    ]
    | None = "open",
    created_after: Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
) -> list[ManifoldMarket]
Source code in prediction_market_agent_tooling/markets/manifold/api.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
def get_manifold_binary_markets(
    limit: int,
    term: str = "",
    topic_slug: t.Optional[str] = None,
    sort: t.Literal["liquidity", "score", "newest", "close-date"] | None = "liquidity",
    filter_: (
        t.Literal[
            "open", "closed", "resolved", "closing-this-month", "closing-next-month"
        ]
        | None
    ) = "open",
    created_after: t.Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
) -> list[ManifoldMarket]:
    all_markets: list[ManifoldMarket] = []

    url = f"{MANIFOLD_API_BASE_URL}/v0/search-markets"
    params: dict[str, t.Union[str, int, float]] = {
        "term": term,
        "limit": min(limit, MARKETS_LIMIT),
        "contractType": "BINARY",
    }
    if sort:
        params["sort"] = sort
    if filter_:
        params["filter"] = filter_
    if topic_slug:
        params["topicSlug"] = topic_slug

    offset = 0
    while True:
        params["offset"] = offset
        response = requests.get(url, params=params)
        markets = response_list_to_model(response, ManifoldMarket)

        if not markets:
            break

        found_all_new_markets = False
        for market in markets:
            if created_after and market.createdTime < created_after:
                if sort == "newest":
                    found_all_new_markets = True
                    break
                else:
                    continue

            if excluded_questions and market.question in excluded_questions:
                continue

            all_markets.append(market)

        if found_all_new_markets:
            break

        if len(all_markets) >= limit:
            break

        offset += len(markets)

    return all_markets[:limit]
get_one_manifold_binary_market
get_one_manifold_binary_market() -> ManifoldMarket
Source code in prediction_market_agent_tooling/markets/manifold/api.py
98
99
def get_one_manifold_binary_market() -> ManifoldMarket:
    return get_manifold_binary_markets(1)[0]
place_bet
place_bet(
    amount: Mana,
    market_id: str,
    outcome: OutcomeStr,
    manifold_api_key: SecretStr,
) -> ManifoldBet
Source code in prediction_market_agent_tooling/markets/manifold/api.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
@tenacity.retry(
    stop=tenacity.stop_after_attempt(3),
    wait=tenacity.wait_fixed(1),
    after=lambda x: logger.debug(f"place_bet failed, {x.attempt_number=}."),
)
def place_bet(
    amount: Mana, market_id: str, outcome: OutcomeStr, manifold_api_key: SecretStr
) -> ManifoldBet:
    url = f"{MANIFOLD_API_BASE_URL}/v0/bet"
    params = {
        "amount": float(amount),  # Convert to float to avoid serialization issues.
        "contractId": market_id,
        "outcome": outcome,
    }

    headers = {
        "Authorization": f"Key {manifold_api_key.get_secret_value()}",
        "Content-Type": "application/json",
    }
    response = requests.post(url, json=params, headers=headers)

    if response.status_code == 200:
        data = response.json()
        if not data["isFilled"]:
            raise RuntimeError(
                f"Placing bet failed: {response.status_code} {response.reason} {response.text}"
            )
        return ManifoldBet.model_validate(data)
    else:
        raise Exception(
            f"Placing bet failed: {response.status_code} {response.reason} {response.text}"
        )
get_authenticated_user
get_authenticated_user(api_key: str) -> ManifoldUser
Source code in prediction_market_agent_tooling/markets/manifold/api.py
136
137
138
139
140
141
142
143
144
145
def get_authenticated_user(api_key: str) -> ManifoldUser:
    url = f"{MANIFOLD_API_BASE_URL}/v0/me"
    headers = {
        "Authorization": f"Key {api_key}",
        "Content-Type": "application/json",
        "Cache-Control": "private, no-store, max-age=0",
    }
    response = requests.get(url, headers=headers)
    response.raise_for_status()
    return ManifoldUser.model_validate(response.json())
get_manifold_market
get_manifold_market(market_id: str) -> FullManifoldMarket
Source code in prediction_market_agent_tooling/markets/manifold/api.py
148
149
150
151
152
153
154
155
@tenacity.retry(
    stop=tenacity.stop_after_attempt(3),
    wait=tenacity.wait_fixed(1),
    after=lambda x: logger.debug(f"get_manifold_market failed, {x.attempt_number=}."),
)
def get_manifold_market(market_id: str) -> FullManifoldMarket:
    url = f"{MANIFOLD_API_BASE_URL}/v0/market/{market_id}"
    return response_to_model(requests.get(url), FullManifoldMarket)
get_manifold_bets
get_manifold_bets(
    user_id: str,
    start_time: DatetimeUTC,
    end_time: Optional[DatetimeUTC],
) -> list[ManifoldBet]
Source code in prediction_market_agent_tooling/markets/manifold/api.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
@tenacity.retry(
    stop=tenacity.stop_after_attempt(3),
    wait=tenacity.wait_fixed(1),
    after=lambda x: logger.debug(f"get_manifold_bets failed, {x.attempt_number=}."),
)
def get_manifold_bets(
    user_id: str,
    start_time: DatetimeUTC,
    end_time: t.Optional[DatetimeUTC],
) -> list[ManifoldBet]:
    url = f"{MANIFOLD_API_BASE_URL}/v0/bets"

    params: dict[str, str] = {"userId": user_id}
    bets = response_list_to_model(requests.get(url, params=params), ManifoldBet)
    bets = [b for b in bets if b.createdTime >= start_time]
    if end_time:
        bets = [b for b in bets if b.createdTime < end_time]
    return bets
get_resolved_manifold_bets
get_resolved_manifold_bets(
    user_id: str,
    start_time: DatetimeUTC,
    end_time: Optional[DatetimeUTC],
) -> tuple[list[ManifoldBet], list[ManifoldMarket]]
Source code in prediction_market_agent_tooling/markets/manifold/api.py
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
def get_resolved_manifold_bets(
    user_id: str,
    start_time: DatetimeUTC,
    end_time: t.Optional[DatetimeUTC],
) -> tuple[list[ManifoldBet], list[ManifoldMarket]]:
    bets = get_manifold_bets(user_id, start_time, end_time)
    markets: list[ManifoldMarket] = par_map(
        items=bets,
        func=lambda bet: get_manifold_market(bet.contractId),
    )
    resolved_markets, resolved_bets = [], []
    for bet, market in zip(bets, markets):
        if market.is_resolved_non_cancelled():
            resolved_markets.append(market)
            resolved_bets.append(bet)
    return resolved_bets, resolved_markets
manifold_to_generic_resolved_bet
manifold_to_generic_resolved_bet(
    bet: ManifoldBet, market: ManifoldMarket
) -> ResolvedBet
Source code in prediction_market_agent_tooling/markets/manifold/api.py
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
def manifold_to_generic_resolved_bet(
    bet: ManifoldBet, market: ManifoldMarket
) -> ResolvedBet:
    if market.id != bet.contractId:
        raise ValueError(f"Bet {bet.contractId} and market {market.id} do not match.")
    if not market.is_resolved_non_cancelled():
        raise ValueError(f"Market {market.id} is not resolved.")
    if not market.resolutionTime:
        raise ValueError(f"Market {market.id} has no resolution time.")

    market_outcome = market.get_resolved_outcome()

    return ResolvedBet(
        id=bet.id,
        amount=bet.amount,
        outcome=bet.get_resolved_outcome(),
        created_time=bet.createdTime,
        market_question=market.question,
        market_id=market.id,
        market_outcome=market_outcome,
        resolved_time=market.resolutionTime,
        profit=bet.get_profit(market_outcome=market_outcome),
    )
get_market_positions
get_market_positions(
    market_id: str, user_id: str
) -> list[ManifoldContractMetric]
Source code in prediction_market_agent_tooling/markets/manifold/api.py
221
222
223
224
225
226
def get_market_positions(market_id: str, user_id: str) -> list[ManifoldContractMetric]:
    url = f"{MANIFOLD_API_BASE_URL}/v0/market/{market_id}/positions"
    params = {"userId": user_id}
    return response_list_to_model(
        requests.get(url, params=params), ManifoldContractMetric
    )
find_resolution_on_manifold
find_resolution_on_manifold(
    question: str, n: int = 100
) -> Resolution | None
Source code in prediction_market_agent_tooling/markets/manifold/api.py
229
230
231
232
233
234
235
236
237
def find_resolution_on_manifold(question: str, n: int = 100) -> Resolution | None:
    # Even with exact-match search, Manifold doesn't return it as the first result, increase `n` if you can't find market that you know exists.
    manifold_markets = get_manifold_binary_markets(
        n, term=question, filter_=None, sort=None
    )
    for manifold_market in manifold_markets:
        if manifold_market.question == question:
            return manifold_market.resolution
    return None

data_models

MANIFOLD_BASE_URL module-attribute
MANIFOLD_BASE_URL = 'https://manifold.markets'
ManifoldPool

Bases: BaseModel

NO instance-attribute
NO: OutcomeToken
YES instance-attribute
YES: OutcomeToken
size_for_outcome
size_for_outcome(outcome: str) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/manifold/data_models.py
37
38
39
40
41
def size_for_outcome(self, outcome: str) -> OutcomeToken:
    if hasattr(self, outcome):
        return OutcomeToken(getattr(self, outcome))
    else:
        should_not_happen(f"Unexpected outcome string, '{outcome}'.")
ManifoldAnswersMode

Bases: str, Enum

ANYONE class-attribute instance-attribute
ANYONE = 'ANYONE'
ONLY_CREATOR class-attribute instance-attribute
ONLY_CREATOR = 'ONLY_CREATOR'
DISABLED class-attribute instance-attribute
DISABLED = 'DISABLED'
ManifoldAnswer

Bases: BaseModel

createdTime instance-attribute
createdTime: DatetimeUTC
avatarUrl instance-attribute
avatarUrl: str
id instance-attribute
id: str
username instance-attribute
username: str
number instance-attribute
number: int
name instance-attribute
name: str
contractId instance-attribute
contractId: str
text instance-attribute
text: str
userId instance-attribute
userId: str
probability instance-attribute
probability: float
ManifoldMarket

Bases: BaseModel

https://docs.manifold.markets/api#get-v0markets

id instance-attribute
id: str
question instance-attribute
question: str
creatorId instance-attribute
creatorId: str
closeTime instance-attribute
closeTime: DatetimeUTC
createdTime instance-attribute
createdTime: DatetimeUTC
creatorAvatarUrl class-attribute instance-attribute
creatorAvatarUrl: Optional[str] = None
creatorName instance-attribute
creatorName: str
creatorUsername instance-attribute
creatorUsername: str
isResolved instance-attribute
isResolved: bool
resolution class-attribute instance-attribute
resolution: Optional[Resolution] = None
resolutionTime class-attribute instance-attribute
resolutionTime: Optional[DatetimeUTC] = None
lastBetTime class-attribute instance-attribute
lastBetTime: Optional[DatetimeUTC] = None
lastCommentTime class-attribute instance-attribute
lastCommentTime: Optional[DatetimeUTC] = None
lastUpdatedTime instance-attribute
lastUpdatedTime: DatetimeUTC
mechanism instance-attribute
mechanism: str
outcomeType instance-attribute
outcomeType: str
p class-attribute instance-attribute
p: Optional[float] = None
pool instance-attribute
pool: ManifoldPool
probability instance-attribute
probability: Probability
slug instance-attribute
slug: str
totalLiquidity class-attribute instance-attribute
totalLiquidity: Optional[CollateralToken] = None
uniqueBettorCount instance-attribute
uniqueBettorCount: int
url instance-attribute
url: str
volume instance-attribute
volume: CollateralToken
volume24Hours instance-attribute
volume24Hours: CollateralToken
outcomes property
outcomes: Sequence[OutcomeStr]
get_resolved_outcome
get_resolved_outcome() -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/manifold/data_models.py
 98
 99
100
101
102
def get_resolved_outcome(self) -> OutcomeStr:
    if self.resolution and self.resolution.outcome:
        return self.resolution.outcome
    else:
        raise ValueError(f"Market is not resolved. Resolution {self.resolution=}")
is_resolved_non_cancelled
is_resolved_non_cancelled() -> bool
Source code in prediction_market_agent_tooling/markets/manifold/data_models.py
104
105
106
107
108
109
110
111
def is_resolved_non_cancelled(self) -> bool:
    return (
        self.isResolved
        and self.resolutionTime is not None
        and self.resolution is not None
        and self.resolution.outcome is not None
        and not self.resolution.invalid
    )
validate_resolution
validate_resolution(v: Any) -> Resolution
Source code in prediction_market_agent_tooling/markets/manifold/data_models.py
113
114
115
@field_validator("resolution", mode="before")
def validate_resolution(cls, v: t.Any) -> Resolution:
    return validate_manifold_resolution(v)
FullManifoldMarket

Bases: ManifoldMarket

answers class-attribute instance-attribute
answers: list[ManifoldAnswer] | None = None
shouldAnswersSumToOne class-attribute instance-attribute
shouldAnswersSumToOne: bool | None = None
addAnswersMode class-attribute instance-attribute
addAnswersMode: ManifoldAnswersMode | None = None
options class-attribute instance-attribute
options: dict[str, int | str] | None = None
totalBounty class-attribute instance-attribute
totalBounty: float | None = None
bountyLeft class-attribute instance-attribute
bountyLeft: float | None = None
description instance-attribute
description: str | dict[str, Any]
textDescription instance-attribute
textDescription: str
coverImageUrl class-attribute instance-attribute
coverImageUrl: str | None = None
groupSlugs class-attribute instance-attribute
groupSlugs: list[str] | None = None
id instance-attribute
id: str
question instance-attribute
question: str
creatorId instance-attribute
creatorId: str
closeTime instance-attribute
closeTime: DatetimeUTC
createdTime instance-attribute
createdTime: DatetimeUTC
creatorAvatarUrl class-attribute instance-attribute
creatorAvatarUrl: Optional[str] = None
creatorName instance-attribute
creatorName: str
creatorUsername instance-attribute
creatorUsername: str
isResolved instance-attribute
isResolved: bool
resolution class-attribute instance-attribute
resolution: Optional[Resolution] = None
resolutionTime class-attribute instance-attribute
resolutionTime: Optional[DatetimeUTC] = None
lastBetTime class-attribute instance-attribute
lastBetTime: Optional[DatetimeUTC] = None
lastCommentTime class-attribute instance-attribute
lastCommentTime: Optional[DatetimeUTC] = None
lastUpdatedTime instance-attribute
lastUpdatedTime: DatetimeUTC
mechanism instance-attribute
mechanism: str
outcomeType instance-attribute
outcomeType: str
p class-attribute instance-attribute
p: Optional[float] = None
pool instance-attribute
pool: ManifoldPool
probability instance-attribute
probability: Probability
slug instance-attribute
slug: str
totalLiquidity class-attribute instance-attribute
totalLiquidity: Optional[CollateralToken] = None
uniqueBettorCount instance-attribute
uniqueBettorCount: int
url instance-attribute
url: str
volume instance-attribute
volume: CollateralToken
volume24Hours instance-attribute
volume24Hours: CollateralToken
outcomes property
outcomes: Sequence[OutcomeStr]
get_resolved_outcome
get_resolved_outcome() -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/manifold/data_models.py
 98
 99
100
101
102
def get_resolved_outcome(self) -> OutcomeStr:
    if self.resolution and self.resolution.outcome:
        return self.resolution.outcome
    else:
        raise ValueError(f"Market is not resolved. Resolution {self.resolution=}")
is_resolved_non_cancelled
is_resolved_non_cancelled() -> bool
Source code in prediction_market_agent_tooling/markets/manifold/data_models.py
104
105
106
107
108
109
110
111
def is_resolved_non_cancelled(self) -> bool:
    return (
        self.isResolved
        and self.resolutionTime is not None
        and self.resolution is not None
        and self.resolution.outcome is not None
        and not self.resolution.invalid
    )
validate_resolution
validate_resolution(v: Any) -> Resolution
Source code in prediction_market_agent_tooling/markets/manifold/data_models.py
113
114
115
@field_validator("resolution", mode="before")
def validate_resolution(cls, v: t.Any) -> Resolution:
    return validate_manifold_resolution(v)
ProfitCached

Bases: BaseModel

daily instance-attribute
daily: Mana
weekly instance-attribute
weekly: Mana
monthly instance-attribute
monthly: Mana
allTime instance-attribute
allTime: Mana
ManifoldUser

Bases: BaseModel

https://docs.manifold.markets/api#get-v0userusername

id instance-attribute
id: str
createdTime instance-attribute
createdTime: DatetimeUTC
name instance-attribute
name: str
username instance-attribute
username: str
url instance-attribute
url: str
avatarUrl class-attribute instance-attribute
avatarUrl: Optional[str] = None
bio class-attribute instance-attribute
bio: Optional[str] = None
bannerUrl class-attribute instance-attribute
bannerUrl: Optional[str] = None
website class-attribute instance-attribute
website: Optional[str] = None
twitterHandle class-attribute instance-attribute
twitterHandle: Optional[str] = None
discordHandle class-attribute instance-attribute
discordHandle: Optional[str] = None
isBot class-attribute instance-attribute
isBot: Optional[bool] = None
isAdmin class-attribute instance-attribute
isAdmin: Optional[bool] = None
isTrustworthy class-attribute instance-attribute
isTrustworthy: Optional[bool] = None
isBannedFromPosting class-attribute instance-attribute
isBannedFromPosting: Optional[bool] = None
userDeleted class-attribute instance-attribute
userDeleted: Optional[bool] = None
balance instance-attribute
balance: Mana
totalDeposits instance-attribute
totalDeposits: Mana
lastBetTime class-attribute instance-attribute
lastBetTime: Optional[DatetimeUTC] = None
currentBettingStreak class-attribute instance-attribute
currentBettingStreak: Optional[int] = None
profitCached instance-attribute
profitCached: ProfitCached
ManifoldBetFills

Bases: BaseModel

amount instance-attribute
amount: Mana
matchedBetId instance-attribute
matchedBetId: Optional[str]
shares instance-attribute
shares: float
timestamp instance-attribute
timestamp: int
ManifoldBetFees

Bases: BaseModel

platformFee instance-attribute
platformFee: float
liquidityFee instance-attribute
liquidityFee: float
creatorFee instance-attribute
creatorFee: float
get_total
get_total() -> float
Source code in prediction_market_agent_tooling/markets/manifold/data_models.py
182
183
def get_total(self) -> float:
    return sum([self.platformFee, self.liquidityFee, self.creatorFee])
ManifoldBet

Bases: BaseModel

https://docs.manifold.markets/api#get-v0bets

shares instance-attribute
shares: CollateralToken
probBefore instance-attribute
probBefore: Probability
isFilled class-attribute instance-attribute
isFilled: Optional[bool] = None
probAfter instance-attribute
probAfter: Probability
userId instance-attribute
userId: str
amount instance-attribute
amount: CollateralToken
contractId instance-attribute
contractId: str
id instance-attribute
id: str
fees instance-attribute
fees: ManifoldBetFees
isCancelled class-attribute instance-attribute
isCancelled: Optional[bool] = None
loanAmount instance-attribute
loanAmount: CollateralToken | None
orderAmount class-attribute instance-attribute
orderAmount: Optional[CollateralToken] = None
fills class-attribute instance-attribute
fills: Optional[list[ManifoldBetFills]] = None
createdTime instance-attribute
createdTime: DatetimeUTC
outcome instance-attribute
outcome: Resolution
validate_resolution
validate_resolution(v: Any) -> Resolution
Source code in prediction_market_agent_tooling/markets/manifold/data_models.py
207
208
209
@field_validator("outcome", mode="before")
def validate_resolution(cls, v: t.Any) -> Resolution:
    return validate_manifold_resolution(v)
get_resolved_outcome
get_resolved_outcome() -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/manifold/data_models.py
211
212
213
214
215
def get_resolved_outcome(self) -> OutcomeStr:
    if self.outcome.outcome:
        return self.outcome.outcome
    else:
        raise ValueError(f"Bet {self.id} is not resolved. {self.outcome=}")
get_profit
get_profit(market_outcome: OutcomeStr) -> CollateralToken
Source code in prediction_market_agent_tooling/markets/manifold/data_models.py
217
218
219
220
221
222
223
def get_profit(self, market_outcome: OutcomeStr) -> CollateralToken:
    profit = (
        self.shares - self.amount
        if self.get_resolved_outcome() == market_outcome
        else -self.amount
    )
    return profit
ManifoldContractMetric

Bases: BaseModel

https://docs.manifold.markets/api#get-v0marketmarketidpositions

contractId instance-attribute
contractId: str
hasNoShares instance-attribute
hasNoShares: bool
hasShares instance-attribute
hasShares: bool
hasYesShares instance-attribute
hasYesShares: bool
invested instance-attribute
invested: float
loan instance-attribute
loan: float
maxSharesOutcome instance-attribute
maxSharesOutcome: Optional[str]
payout instance-attribute
payout: float
profit instance-attribute
profit: float
profitPercent instance-attribute
profitPercent: float
totalShares instance-attribute
totalShares: dict[str, float]
userId instance-attribute
userId: str
userUsername instance-attribute
userUsername: str
userName instance-attribute
userName: str
userAvatarUrl instance-attribute
userAvatarUrl: str
lastBetTime instance-attribute
lastBetTime: DatetimeUTC
mana_to_usd
mana_to_usd(mana: Mana) -> USD
Source code in prediction_market_agent_tooling/markets/manifold/data_models.py
23
24
25
def mana_to_usd(mana: Mana) -> USD:
    # Not really, but for sake of simplicity. Mana are just play money.
    return USD(mana.value)
usd_to_mana
usd_to_mana(usd: USD) -> Mana
Source code in prediction_market_agent_tooling/markets/manifold/data_models.py
28
29
30
def usd_to_mana(usd: USD) -> Mana:
    # Not really, but for sake of simplicity. Mana are just play money.
    return Mana(usd.value)

manifold

ManifoldAgentMarket

Bases: AgentMarket

Manifold's market class that can be used by agents to make predictions.

base_url class-attribute
base_url: str = MANIFOLD_BASE_URL
fees class-attribute instance-attribute
fees: MarketFees = MarketFees(
    bet_proportion=0, absolute=0.25
)
current_p_yes instance-attribute
current_p_yes: Probability
id instance-attribute
id: str
question instance-attribute
question: str
description instance-attribute
description: str | None
outcomes instance-attribute
outcomes: Sequence[OutcomeStr]
outcome_token_pool instance-attribute
outcome_token_pool: dict[OutcomeStr, OutcomeToken] | None
resolution instance-attribute
resolution: Resolution | None
created_time instance-attribute
created_time: DatetimeUTC | None
close_time instance-attribute
close_time: DatetimeUTC | None
probabilities instance-attribute
probabilities: dict[OutcomeStr, Probability]
url instance-attribute
url: str
volume instance-attribute
volume: CollateralToken | None
is_binary property
is_binary: bool
p_yes property
p_yes: Probability
p_no property
p_no: Probability
probable_resolution property
probable_resolution: Resolution
get_last_trade_p_yes
get_last_trade_p_yes() -> Probability

On Manifold, probablities aren't updated after the closure, so we can just use the current probability

Source code in prediction_market_agent_tooling/markets/manifold/manifold.py
49
50
51
def get_last_trade_p_yes(self) -> Probability:
    """On Manifold, probablities aren't updated after the closure, so we can just use the current probability"""
    return self.current_p_yes
get_tiny_bet_amount
get_tiny_bet_amount() -> CollateralToken
Source code in prediction_market_agent_tooling/markets/manifold/manifold.py
53
54
def get_tiny_bet_amount(self) -> CollateralToken:
    return CollateralToken(1)
have_bet_on_market_since
have_bet_on_market_since(
    keys: APIKeys, since: timedelta
) -> bool
Source code in prediction_market_agent_tooling/markets/manifold/manifold.py
56
57
58
59
60
61
62
63
64
65
66
67
68
def have_bet_on_market_since(self, keys: APIKeys, since: timedelta) -> bool:
    start_time = utcnow() - since
    recently_betted_questions = set(
        get_manifold_market(b.contractId).question
        for b in get_manifold_bets(
            user_id=get_authenticated_user(
                keys.manifold_api_key.get_secret_value()
            ).id,
            start_time=start_time,
            end_time=None,
        )
    )
    return self.question in recently_betted_questions
place_bet
place_bet(outcome: OutcomeStr, amount: USD) -> str
Source code in prediction_market_agent_tooling/markets/manifold/manifold.py
70
71
72
73
74
75
76
77
78
def place_bet(self, outcome: OutcomeStr, amount: USD) -> str:
    self.get_usd_in_token(amount)
    bet = place_bet(
        amount=usd_to_mana(amount),
        market_id=self.id,
        outcome=outcome,
        manifold_api_key=APIKeys().manifold_api_key,
    )
    return bet.id
from_data_model staticmethod
from_data_model(
    model: FullManifoldMarket,
) -> ManifoldAgentMarket
Source code in prediction_market_agent_tooling/markets/manifold/manifold.py
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
@staticmethod
def from_data_model(model: FullManifoldMarket) -> "ManifoldAgentMarket":
    outcome_token_pool = {o: model.pool.size_for_outcome(o) for o in model.outcomes}

    prob_map = AgentMarket.build_probability_map(
        outcomes=list(outcome_token_pool.keys()),
        outcome_token_amounts=list(
            [i.as_outcome_wei for i in outcome_token_pool.values()]
        ),
    )

    return ManifoldAgentMarket(
        id=model.id,
        question=model.question,
        description=model.textDescription,
        outcomes=model.outcomes,
        resolution=model.resolution,
        created_time=model.createdTime,
        close_time=model.closeTime,
        current_p_yes=model.probability,
        url=model.url,
        volume=model.volume,
        outcome_token_pool=outcome_token_pool,
        probabilities=prob_map,
    )
get_markets staticmethod
get_markets(
    limit: int,
    sort_by: SortBy,
    filter_by: FilterBy = FilterBy.OPEN,
    created_after: Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
    fetch_categorical_markets: bool = False,
) -> t.Sequence[ManifoldAgentMarket]
Source code in prediction_market_agent_tooling/markets/manifold/manifold.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
@staticmethod
def get_markets(
    limit: int,
    sort_by: SortBy,
    filter_by: FilterBy = FilterBy.OPEN,
    created_after: t.Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
    fetch_categorical_markets: bool = False,
) -> t.Sequence["ManifoldAgentMarket"]:
    sort: t.Literal["newest", "close-date"] | None
    if sort_by == SortBy.CLOSING_SOONEST:
        sort = "close-date"
    elif sort_by == SortBy.NEWEST:
        sort = "newest"
    elif sort_by == SortBy.NONE:
        sort = None
    else:
        raise ValueError(f"Unknown sort_by: {sort_by}")

    filter_: t.Literal["open", "resolved"] | None
    if filter_by == FilterBy.OPEN:
        filter_ = "open"
    elif filter_by == FilterBy.RESOLVED:
        filter_ = "resolved"
    elif filter_by == FilterBy.NONE:
        filter_ = None
    else:
        raise ValueError(f"Unknown filter_by: {filter_by}")

    return [
        ManifoldAgentMarket.from_data_model(get_manifold_market(m.id))
        for m in get_manifold_binary_markets(
            limit=limit,
            sort=sort,
            created_after=created_after,
            filter_=filter_,
            excluded_questions=excluded_questions,
        )
    ]
redeem_winnings staticmethod
redeem_winnings(api_keys: APIKeys) -> None
Source code in prediction_market_agent_tooling/markets/manifold/manifold.py
146
147
148
149
@staticmethod
def redeem_winnings(api_keys: APIKeys) -> None:
    # It's done automatically on Manifold.
    pass
get_user_url classmethod
get_user_url(keys: APIKeys) -> str
Source code in prediction_market_agent_tooling/markets/manifold/manifold.py
151
152
153
@classmethod
def get_user_url(cls, keys: APIKeys) -> str:
    return get_authenticated_user(keys.manifold_api_key.get_secret_value()).url
get_user_id staticmethod
get_user_id(api_keys: APIKeys) -> str
Source code in prediction_market_agent_tooling/markets/manifold/manifold.py
155
156
157
@staticmethod
def get_user_id(api_keys: APIKeys) -> str:
    return api_keys.manifold_user_id
validate_probabilities
validate_probabilities(
    probs: dict[OutcomeStr, Probability],
    info: FieldValidationInfo,
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
@field_validator("probabilities")
def validate_probabilities(
    cls,
    probs: dict[OutcomeStr, Probability],
    info: FieldValidationInfo,
) -> dict[OutcomeStr, Probability]:
    outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
    if set(probs.keys()) != set(outcomes):
        raise ValueError("Keys of `probabilities` must match `outcomes` exactly.")
    total = float(sum(probs.values()))
    if not 0.999 <= total <= 1.001:
        # We simply log a warning because for some use-cases (e.g. existing positions), the
        # markets might be already closed hence no reliable outcome token prices exist anymore.
        logger.warning(f"Probabilities for market {info.data=} do not sum to 1.")
    return probs
validate_outcome_token_pool
validate_outcome_token_pool(
    outcome_token_pool: dict[str, OutcomeToken] | None,
    info: FieldValidationInfo,
) -> dict[str, OutcomeToken] | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
@field_validator("outcome_token_pool")
def validate_outcome_token_pool(
    cls,
    outcome_token_pool: dict[str, OutcomeToken] | None,
    info: FieldValidationInfo,
) -> dict[str, OutcomeToken] | None:
    outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
    if outcome_token_pool is not None:
        outcome_keys = set(outcome_token_pool.keys())
        expected_keys = set(outcomes)
        if outcome_keys != expected_keys:
            raise ValueError(
                f"Keys of outcome_token_pool ({outcome_keys}) do not match outcomes ({expected_keys})."
            )
    return outcome_token_pool
get_outcome_token_pool_by_outcome
get_outcome_token_pool_by_outcome(
    outcome: OutcomeStr,
) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
122
123
124
125
126
127
128
def get_outcome_token_pool_by_outcome(self, outcome: OutcomeStr) -> OutcomeToken:
    if self.outcome_token_pool is None or not self.outcome_token_pool:
        return OutcomeToken(0)

    # We look up by index to avoid having to deal with case sensitivity issues.
    outcome_idx = self.get_outcome_index(outcome)
    return list(self.outcome_token_pool.values())[outcome_idx]
handle_legacy_fee
handle_legacy_fee(data: dict[str, Any]) -> dict[str, t.Any]
Source code in prediction_market_agent_tooling/markets/agent_market.py
130
131
132
133
134
135
136
@model_validator(mode="before")
def handle_legacy_fee(cls, data: dict[str, t.Any]) -> dict[str, t.Any]:
    # Backward compatibility for older `AgentMarket` without `fees`.
    if "fees" not in data and "fee" in data:
        data["fees"] = MarketFees(absolute=0.0, bet_proportion=data["fee"])
        del data["fee"]
    return data
market_outcome_for_probability_key
market_outcome_for_probability_key(
    probability_key: OutcomeStr,
) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
138
139
140
141
142
143
144
145
146
def market_outcome_for_probability_key(
    self, probability_key: OutcomeStr
) -> OutcomeStr:
    for market_outcome in self.outcomes:
        if market_outcome.lower() == probability_key.lower():
            return market_outcome
    raise ValueError(
        f"Could not find probability for probability key {probability_key}"
    )
probability_for_market_outcome
probability_for_market_outcome(
    market_outcome: OutcomeStr,
) -> Probability
Source code in prediction_market_agent_tooling/markets/agent_market.py
148
149
150
151
152
153
154
def probability_for_market_outcome(self, market_outcome: OutcomeStr) -> Probability:
    for k, v in self.probabilities.items():
        if k.lower() == market_outcome.lower():
            return v
    raise ValueError(
        f"Could not find probability for market outcome {market_outcome}"
    )
get_last_trade_p_no
get_last_trade_p_no() -> Probability | None

Get the last trade price for the NO outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1. Could be None if no trades were made.

Source code in prediction_market_agent_tooling/markets/agent_market.py
202
203
204
205
206
207
def get_last_trade_p_no(self) -> Probability | None:
    """
    Get the last trade price for the NO outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1.
    Could be None if no trades were made.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_last_trade_yes_outcome_price
get_last_trade_yes_outcome_price() -> (
    CollateralToken | None
)
Source code in prediction_market_agent_tooling/markets/agent_market.py
209
210
211
212
213
214
def get_last_trade_yes_outcome_price(self) -> CollateralToken | None:
    # Price on prediction markets are, by definition, equal to the probability of an outcome.
    # Just making it explicit in this function.
    if last_trade_p_yes := self.get_last_trade_p_yes():
        return CollateralToken(last_trade_p_yes)
    return None
get_last_trade_yes_outcome_price_usd
get_last_trade_yes_outcome_price_usd() -> USD | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
216
217
218
219
def get_last_trade_yes_outcome_price_usd(self) -> USD | None:
    if last_trade_yes_outcome_price := self.get_last_trade_yes_outcome_price():
        return self.get_token_in_usd(last_trade_yes_outcome_price)
    return None
get_last_trade_no_outcome_price
get_last_trade_no_outcome_price() -> CollateralToken | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
221
222
223
224
225
226
def get_last_trade_no_outcome_price(self) -> CollateralToken | None:
    # Price on prediction markets are, by definition, equal to the probability of an outcome.
    # Just making it explicit in this function.
    if last_trade_p_no := self.get_last_trade_p_no():
        return CollateralToken(last_trade_p_no)
    return None
get_last_trade_no_outcome_price_usd
get_last_trade_no_outcome_price_usd() -> USD | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
228
229
230
231
def get_last_trade_no_outcome_price_usd(self) -> USD | None:
    if last_trade_no_outcome_price := self.get_last_trade_no_outcome_price():
        return self.get_token_in_usd(last_trade_no_outcome_price)
    return None
get_liquidatable_amount
get_liquidatable_amount() -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
233
234
235
def get_liquidatable_amount(self) -> OutcomeToken:
    tiny_amount = self.get_tiny_bet_amount()
    return OutcomeToken.from_token(tiny_amount / 10)
get_token_in_usd
get_token_in_usd(x: CollateralToken) -> USD

Token of this market can have whatever worth (e.g. sDai and ETH markets will have different worth of 1 token). Use this to convert it to USD.

Source code in prediction_market_agent_tooling/markets/agent_market.py
237
238
239
240
241
def get_token_in_usd(self, x: CollateralToken) -> USD:
    """
    Token of this market can have whatever worth (e.g. sDai and ETH markets will have different worth of 1 token). Use this to convert it to USD.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_usd_in_token
get_usd_in_token(x: USD) -> CollateralToken

Markets on a single platform can have different tokens as collateral (sDai, wxDai, GNO, ...). Use this to convert USD to the token of this market.

Source code in prediction_market_agent_tooling/markets/agent_market.py
243
244
245
246
247
def get_usd_in_token(self, x: USD) -> CollateralToken:
    """
    Markets on a single platform can have different tokens as collateral (sDai, wxDai, GNO, ...). Use this to convert USD to the token of this market.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_sell_value_of_outcome_token
get_sell_value_of_outcome_token(
    outcome: OutcomeStr, amount: OutcomeToken
) -> CollateralToken

When you hold OutcomeToken(s), it's easy to calculate how much you get at the end if you win (1 OutcomeToken will equal to 1 Token). But use this to figure out, how much are these outcome tokens worth right now (for how much you can sell them).

Source code in prediction_market_agent_tooling/markets/agent_market.py
249
250
251
252
253
254
255
256
def get_sell_value_of_outcome_token(
    self, outcome: OutcomeStr, amount: OutcomeToken
) -> CollateralToken:
    """
    When you hold OutcomeToken(s), it's easy to calculate how much you get at the end if you win (1 OutcomeToken will equal to 1 Token).
    But use this to figure out, how much are these outcome tokens worth right now (for how much you can sell them).
    """
    raise NotImplementedError("Subclasses must implement this method")
get_in_usd
get_in_usd(x: USD | CollateralToken) -> USD
Source code in prediction_market_agent_tooling/markets/agent_market.py
258
259
260
261
def get_in_usd(self, x: USD | CollateralToken) -> USD:
    if isinstance(x, USD):
        return x
    return self.get_token_in_usd(x)
get_in_token
get_in_token(x: USD | CollateralToken) -> CollateralToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
263
264
265
266
def get_in_token(self, x: USD | CollateralToken) -> CollateralToken:
    if isinstance(x, CollateralToken):
        return x
    return self.get_usd_in_token(x)
liquidate_existing_positions
liquidate_existing_positions(outcome: OutcomeStr) -> None
Source code in prediction_market_agent_tooling/markets/agent_market.py
274
275
def liquidate_existing_positions(self, outcome: OutcomeStr) -> None:
    raise NotImplementedError("Subclasses must implement this method")
buy_tokens
buy_tokens(outcome: OutcomeStr, amount: USD) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
280
281
def buy_tokens(self, outcome: OutcomeStr, amount: USD) -> str:
    return self.place_bet(outcome=outcome, amount=amount)
get_buy_token_amount
get_buy_token_amount(
    bet_amount: USD | CollateralToken, outcome: OutcomeStr
) -> OutcomeToken | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
283
284
285
286
def get_buy_token_amount(
    self, bet_amount: USD | CollateralToken, outcome: OutcomeStr
) -> OutcomeToken | None:
    raise NotImplementedError("Subclasses must implement this method")
sell_tokens
sell_tokens(
    outcome: OutcomeStr, amount: USD | OutcomeToken
) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
288
289
def sell_tokens(self, outcome: OutcomeStr, amount: USD | OutcomeToken) -> str:
    raise NotImplementedError("Subclasses must implement this method")
compute_fpmm_probabilities staticmethod
compute_fpmm_probabilities(
    balances: list[OutcomeWei],
) -> list[Probability]

Compute the implied probabilities in a Fixed Product Market Maker.

Parameters:

Name Type Description Default
balances List[float]

Balances of outcome tokens.

required

Returns:

Type Description
list[Probability]

List[float]: Implied probabilities for each outcome.

Source code in prediction_market_agent_tooling/markets/agent_market.py
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
@staticmethod
def compute_fpmm_probabilities(balances: list[OutcomeWei]) -> list[Probability]:
    """
    Compute the implied probabilities in a Fixed Product Market Maker.

    Args:
        balances (List[float]): Balances of outcome tokens.

    Returns:
        List[float]: Implied probabilities for each outcome.
    """
    if all(x.value == 0 for x in balances):
        return [Probability(0.0)] * len(balances)

    # converting to standard values for prod compatibility.
    values_balance = [i.value for i in balances]
    # Compute product of balances excluding each outcome
    excluded_products = []
    for i in range(len(values_balance)):
        other_balances = values_balance[:i] + values_balance[i + 1 :]
        excluded_products.append(prod(other_balances))

    # Normalize to sum to 1
    total = sum(excluded_products)
    if total == 0:
        return [Probability(0.0)] * len(balances)
    probabilities = [Probability(p / total) for p in excluded_products]

    return probabilities
build_probability_map_from_p_yes staticmethod
build_probability_map_from_p_yes(
    p_yes: Probability,
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
321
322
323
324
325
326
327
328
@staticmethod
def build_probability_map_from_p_yes(
    p_yes: Probability,
) -> dict[OutcomeStr, Probability]:
    return {
        OutcomeStr(YES_OUTCOME_LOWERCASE_IDENTIFIER): p_yes,
        OutcomeStr(NO_OUTCOME_LOWERCASE_IDENTIFIER): Probability(1.0 - p_yes),
    }
build_probability_map staticmethod
build_probability_map(
    outcome_token_amounts: list[OutcomeWei],
    outcomes: list[OutcomeStr],
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
330
331
332
333
334
335
@staticmethod
def build_probability_map(
    outcome_token_amounts: list[OutcomeWei], outcomes: list[OutcomeStr]
) -> dict[OutcomeStr, Probability]:
    probs = AgentMarket.compute_fpmm_probabilities(outcome_token_amounts)
    return {outcome: prob for outcome, prob in zip(outcomes, probs)}
get_binary_market staticmethod
get_binary_market(id: str) -> AgentMarket
Source code in prediction_market_agent_tooling/markets/agent_market.py
348
349
350
@staticmethod
def get_binary_market(id: str) -> "AgentMarket":
    raise NotImplementedError("Subclasses must implement this method")
get_trade_balance classmethod
get_trade_balance(api_keys: APIKeys) -> USD

Return balance that can be used to trade on the given market.

Source code in prediction_market_agent_tooling/markets/agent_market.py
359
360
361
362
363
364
@classmethod
def get_trade_balance(cls, api_keys: APIKeys) -> USD:
    """
    Return balance that can be used to trade on the given market.
    """
    raise NotImplementedError("Subclasses must implement this method")
verify_operational_balance staticmethod
verify_operational_balance(api_keys: APIKeys) -> bool

Return True if the user has enough of operational balance. If not needed, just return True. For example: Omen needs at least some xDai in the wallet to execute transactions.

Source code in prediction_market_agent_tooling/markets/agent_market.py
366
367
368
369
370
371
372
@staticmethod
def verify_operational_balance(api_keys: APIKeys) -> bool:
    """
    Return `True` if the user has enough of operational balance. If not needed, just return `True`.
    For example: Omen needs at least some xDai in the wallet to execute transactions.
    """
    raise NotImplementedError("Subclasses must implement this method")
store_prediction
store_prediction(
    processed_market: ProcessedMarket | None,
    keys: APIKeys,
    agent_name: str,
) -> None

If market allows to upload predictions somewhere, implement it in this method.

Source code in prediction_market_agent_tooling/markets/agent_market.py
374
375
376
377
378
379
380
381
382
383
def store_prediction(
    self,
    processed_market: ProcessedMarket | None,
    keys: APIKeys,
    agent_name: str,
) -> None:
    """
    If market allows to upload predictions somewhere, implement it in this method.
    """
    raise NotImplementedError("Subclasses must implement this method")
store_trades
store_trades(
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None

If market allows to upload trades somewhere, implement it in this method.

Source code in prediction_market_agent_tooling/markets/agent_market.py
385
386
387
388
389
390
391
392
393
394
395
def store_trades(
    self,
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None:
    """
    If market allows to upload trades somewhere, implement it in this method.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_bets_made_since staticmethod
get_bets_made_since(
    better_address: ChecksumAddress, start_time: DatetimeUTC
) -> list[Bet]
Source code in prediction_market_agent_tooling/markets/agent_market.py
397
398
399
400
401
@staticmethod
def get_bets_made_since(
    better_address: ChecksumAddress, start_time: DatetimeUTC
) -> list[Bet]:
    raise NotImplementedError("Subclasses must implement this method")
get_resolved_bets_made_since staticmethod
get_resolved_bets_made_since(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC,
    end_time: DatetimeUTC | None,
) -> list[ResolvedBet]
Source code in prediction_market_agent_tooling/markets/agent_market.py
403
404
405
406
407
408
409
@staticmethod
def get_resolved_bets_made_since(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC,
    end_time: DatetimeUTC | None,
) -> list[ResolvedBet]:
    raise NotImplementedError("Subclasses must implement this method")
is_closed
is_closed() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
411
412
def is_closed(self) -> bool:
    return self.close_time is not None and self.close_time <= utcnow()
is_resolved
is_resolved() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
414
415
def is_resolved(self) -> bool:
    return self.resolution is not None
get_liquidity
get_liquidity() -> CollateralToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
417
418
def get_liquidity(self) -> CollateralToken:
    raise NotImplementedError("Subclasses must implement this method")
has_liquidity
has_liquidity() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
420
421
def has_liquidity(self) -> bool:
    return self.get_liquidity() > 0
has_successful_resolution
has_successful_resolution() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
423
424
425
426
427
428
def has_successful_resolution(self) -> bool:
    return (
        self.resolution is not None
        and self.resolution.outcome is not None
        and not self.resolution.invalid
    )
has_unsuccessful_resolution
has_unsuccessful_resolution() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
430
431
def has_unsuccessful_resolution(self) -> bool:
    return self.resolution is not None and self.resolution.invalid
get_outcome_str_from_bool staticmethod
get_outcome_str_from_bool(outcome: bool) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
433
434
435
@staticmethod
def get_outcome_str_from_bool(outcome: bool) -> OutcomeStr:
    raise NotImplementedError("Subclasses must implement this method")
get_outcome_str
get_outcome_str(outcome_index: int) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
437
438
439
440
441
442
443
def get_outcome_str(self, outcome_index: int) -> OutcomeStr:
    try:
        return self.outcomes[outcome_index]
    except IndexError:
        raise IndexError(
            f"Outcome index `{outcome_index}` out of range for `{self.outcomes}`: `{self.outcomes}`."
        )
get_outcome_index
get_outcome_index(outcome: OutcomeStr) -> int
Source code in prediction_market_agent_tooling/markets/agent_market.py
445
446
447
448
449
450
def get_outcome_index(self, outcome: OutcomeStr) -> int:
    outcomes_lowercase = [o.lower() for o in self.outcomes]
    try:
        return outcomes_lowercase.index(outcome.lower())
    except ValueError:
        raise ValueError(f"Outcome `{outcome}` not found in `{self.outcomes}`.")
get_token_balance
get_token_balance(
    user_id: str, outcome: OutcomeStr
) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
452
453
def get_token_balance(self, user_id: str, outcome: OutcomeStr) -> OutcomeToken:
    raise NotImplementedError("Subclasses must implement this method")
get_position
get_position(user_id: str) -> ExistingPosition | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
455
456
def get_position(self, user_id: str) -> ExistingPosition | None:
    raise NotImplementedError("Subclasses must implement this method")
get_positions classmethod
get_positions(
    user_id: str,
    liquid_only: bool = False,
    larger_than: OutcomeToken = OutcomeToken(0),
) -> t.Sequence[ExistingPosition]

Get all non-zero positions a user has in any market.

If liquid_only is True, only return positions that can be sold.

If larger_than is not None, only return positions with a larger number of tokens than this amount.

Source code in prediction_market_agent_tooling/markets/agent_market.py
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
@classmethod
def get_positions(
    cls,
    user_id: str,
    liquid_only: bool = False,
    larger_than: OutcomeToken = OutcomeToken(0),
) -> t.Sequence[ExistingPosition]:
    """
    Get all non-zero positions a user has in any market.

    If `liquid_only` is True, only return positions that can be sold.

    If `larger_than` is not None, only return positions with a larger number
    of tokens than this amount.
    """
    raise NotImplementedError("Subclasses must implement this method")
can_be_traded
can_be_traded() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
475
476
477
478
def can_be_traded(self) -> bool:
    if self.is_closed() or not self.has_liquidity():
        return False
    return True
has_token_pool
has_token_pool() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
484
485
def has_token_pool(self) -> bool:
    return self.outcome_token_pool is not None
get_pool_tokens
get_pool_tokens(outcome: OutcomeStr) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
487
488
489
490
491
def get_pool_tokens(self, outcome: OutcomeStr) -> OutcomeToken:
    if not self.outcome_token_pool:
        raise ValueError("Outcome token pool is not available.")

    return self.outcome_token_pool[outcome]
get_user_balance staticmethod
get_user_balance(user_id: str) -> float
Source code in prediction_market_agent_tooling/markets/agent_market.py
493
494
495
@staticmethod
def get_user_balance(user_id: str) -> float:
    raise NotImplementedError("Subclasses must implement this method")
get_most_recent_trade_datetime
get_most_recent_trade_datetime(
    user_id: str,
) -> DatetimeUTC | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
501
502
def get_most_recent_trade_datetime(self, user_id: str) -> DatetimeUTC | None:
    raise NotImplementedError("Subclasses must implement this method")

utils

MANIFOLD_CANCEL_OUTCOME module-attribute
MANIFOLD_CANCEL_OUTCOME = 'CANCEL'
validate_manifold_resolution
validate_manifold_resolution(v: Any) -> Resolution
Source code in prediction_market_agent_tooling/markets/manifold/utils.py
 9
10
11
12
13
14
15
16
def validate_manifold_resolution(v: t.Any) -> Resolution:
    if isinstance(v, str):
        return (
            Resolution(outcome=OutcomeStr(v), invalid=False)
            if v != MANIFOLD_CANCEL_OUTCOME
            else Resolution(outcome=None, invalid=True)
        )
    raise ValueError(f"Expected a string, got {v} {type(v)}")

market_fees

MarketFees

Bases: BaseModel

bet_proportion class-attribute instance-attribute
bet_proportion: float = Field(..., ge=0.0, lt=1.0)
absolute instance-attribute
absolute: float
get_zero_fees staticmethod
get_zero_fees(
    bet_proportion: float = 0.0, absolute: float = 0.0
) -> MarketFees
Source code in prediction_market_agent_tooling/markets/market_fees.py
12
13
14
15
16
17
18
19
20
@staticmethod
def get_zero_fees(
    bet_proportion: float = 0.0,
    absolute: float = 0.0,
) -> "MarketFees":
    return MarketFees(
        bet_proportion=bet_proportion,
        absolute=absolute,
    )
total_fee_absolute_value
total_fee_absolute_value(bet_amount: float) -> float

Returns the total fee in absolute terms, including both proportional and fixed fees.

Source code in prediction_market_agent_tooling/markets/market_fees.py
22
23
24
25
26
def total_fee_absolute_value(self, bet_amount: float) -> float:
    """
    Returns the total fee in absolute terms, including both proportional and fixed fees.
    """
    return self.bet_proportion * bet_amount + self.absolute
total_fee_relative_value
total_fee_relative_value(bet_amount: float) -> float

Returns the total fee relative to the bet amount, including both proportional and fixed fees.

Source code in prediction_market_agent_tooling/markets/market_fees.py
28
29
30
31
32
33
34
35
def total_fee_relative_value(self, bet_amount: float) -> float:
    """
    Returns the total fee relative to the bet amount, including both proportional and fixed fees.
    """
    if bet_amount == 0:
        return 0.0
    total_fee = self.total_fee_absolute_value(bet_amount)
    return total_fee / bet_amount
get_after_fees
get_after_fees(
    bet_amount: CollateralToken,
) -> CollateralToken
Source code in prediction_market_agent_tooling/markets/market_fees.py
37
38
39
40
def get_after_fees(self, bet_amount: CollateralToken) -> CollateralToken:
    return bet_amount - CollateralToken(
        self.total_fee_absolute_value(bet_amount.value)
    )

markets

MARKET_TYPE_TO_AGENT_MARKET module-attribute

MARKET_TYPE_TO_AGENT_MARKET: dict[
    MarketType, type[AgentMarket]
] = {
    MANIFOLD: ManifoldAgentMarket,
    OMEN: OmenAgentMarket,
    POLYMARKET: PolymarketAgentMarket,
    METACULUS: MetaculusAgentMarket,
    SEER: SeerAgentMarket,
}

JOB_MARKET_TYPE_TO_JOB_AGENT_MARKET module-attribute

JOB_MARKET_TYPE_TO_JOB_AGENT_MARKET: dict[
    MarketType, type[JobAgentMarket]
] = {OMEN: OmenJobAgentMarket}

MarketType

Bases: str, Enum

OMEN class-attribute instance-attribute
OMEN = 'omen'
MANIFOLD class-attribute instance-attribute
MANIFOLD = 'manifold'
POLYMARKET class-attribute instance-attribute
POLYMARKET = 'polymarket'
METACULUS class-attribute instance-attribute
METACULUS = 'metaculus'
SEER class-attribute instance-attribute
SEER = 'seer'
market_class property
market_class: type[AgentMarket]
job_class property
job_class: type[JobAgentMarket]
is_blockchain_market property
is_blockchain_market: bool

get_binary_markets

get_binary_markets(
    limit: int,
    market_type: MarketType,
    filter_by: FilterBy = FilterBy.OPEN,
    sort_by: SortBy = SortBy.NONE,
    excluded_questions: set[str] | None = None,
    created_after: DatetimeUTC | None = None,
) -> t.Sequence[AgentMarket]
Source code in prediction_market_agent_tooling/markets/markets.py
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def get_binary_markets(
    limit: int,
    market_type: MarketType,
    filter_by: FilterBy = FilterBy.OPEN,
    sort_by: SortBy = SortBy.NONE,
    excluded_questions: set[str] | None = None,
    created_after: DatetimeUTC | None = None,
) -> t.Sequence[AgentMarket]:
    agent_market_class = MARKET_TYPE_TO_AGENT_MARKET[market_type]
    markets = agent_market_class.get_markets(
        limit=limit,
        sort_by=sort_by,
        filter_by=filter_by,
        created_after=created_after,
        excluded_questions=excluded_questions,
    )
    return markets

metaculus

api

METACULUS_API_BASE_URL module-attribute
METACULUS_API_BASE_URL = 'https://www.metaculus.com/api2'
get_auth_headers
get_auth_headers() -> dict[str, str]
Source code in prediction_market_agent_tooling/markets/metaculus/api.py
16
17
def get_auth_headers() -> dict[str, str]:
    return {"Authorization": f"Token {APIKeys().metaculus_api_key.get_secret_value()}"}
post_question_comment
post_question_comment(
    question_id: str, comment_text: str
) -> None

Post a comment on the question page as the bot user.

Source code in prediction_market_agent_tooling/markets/metaculus/api.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def post_question_comment(question_id: str, comment_text: str) -> None:
    """
    Post a comment on the question page as the bot user.
    """

    response = requests.post(
        f"{METACULUS_API_BASE_URL}/comments/",
        json={
            "comment_text": comment_text,
            "submit_type": "N",
            "include_latest_prediction": True,
            "question": question_id,
        },
        headers=get_auth_headers(),
    )
    response.raise_for_status()
make_prediction
make_prediction(
    question_id: str, p_yes: Probability
) -> None

Make a prediction for a question.

Source code in prediction_market_agent_tooling/markets/metaculus/api.py
38
39
40
41
42
43
44
45
46
47
48
def make_prediction(question_id: str, p_yes: Probability) -> None:
    """
    Make a prediction for a question.
    """
    url = f"{METACULUS_API_BASE_URL}/questions/{question_id}/predict/"
    response = requests.post(
        url,
        json={"prediction": p_yes},
        headers=get_auth_headers(),
    )
    response.raise_for_status()
get_question
get_question(question_id: str) -> MetaculusQuestion

Get all details about a specific question.

Source code in prediction_market_agent_tooling/markets/metaculus/api.py
51
52
53
54
55
56
57
58
59
def get_question(question_id: str) -> MetaculusQuestion:
    """
    Get all details about a specific question.
    """
    url = f"{METACULUS_API_BASE_URL}/questions/{question_id}/"
    return response_to_model(
        response=requests.get(url, headers=get_auth_headers()),
        model=MetaculusQuestion,
    )
get_questions
get_questions(
    limit: int,
    order_by: str | None = None,
    offset: int = 0,
    tournament_id: int | None = None,
    created_after: DatetimeUTC | None = None,
    status: str | None = None,
) -> list[MetaculusQuestion]

List detailed metaculus questions (i.e. markets)

Source code in prediction_market_agent_tooling/markets/metaculus/api.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
def get_questions(
    limit: int,
    order_by: str | None = None,
    offset: int = 0,
    tournament_id: int | None = None,
    created_after: DatetimeUTC | None = None,
    status: str | None = None,
) -> list[MetaculusQuestion]:
    """
    List detailed metaculus questions (i.e. markets)
    """
    url_params: dict[str, Union[int, str]] = {
        "limit": limit,
        "offset": offset,
        "has_group": "false",
        "forecast_type": "binary",
        "type": "forecast",
        "include_description": "true",
    }
    if order_by:
        url_params["order_by"] = order_by
    if tournament_id:
        url_params["project"] = tournament_id
    if created_after:
        url_params["created_time__gt"] = created_after.isoformat()
    if status:
        url_params["status"] = status

    url = f"{METACULUS_API_BASE_URL}/questions/"
    return response_to_model(
        response=requests.get(url, headers=get_auth_headers(), params=url_params),
        model=MetaculusQuestions,
    ).results

data_models

QuestionType

Bases: str, Enum

binary class-attribute instance-attribute
binary = 'binary'
AggregationItem

Bases: BaseModel

start_time instance-attribute
start_time: DatetimeUTC
end_time instance-attribute
end_time: DatetimeUTC | None
forecast_values instance-attribute
forecast_values: list[float] | None
forecaster_count instance-attribute
forecaster_count: int
interval_lower_bounds instance-attribute
interval_lower_bounds: list[float] | None
centers instance-attribute
centers: list[float] | None
interval_upper_bounds instance-attribute
interval_upper_bounds: list[float] | None
means instance-attribute
means: list[float] | None
histogram instance-attribute
histogram: list[list[float]] | None
Aggregation

Bases: BaseModel

history instance-attribute
history: list[AggregationItem]
latest instance-attribute
latest: AggregationItem | None
score_data instance-attribute
score_data: dict[str, Any]
Aggregations

Bases: BaseModel

recency_weighted instance-attribute
recency_weighted: Aggregation
unweighted instance-attribute
unweighted: Aggregation
single_aggregation instance-attribute
single_aggregation: Aggregation
metaculus_prediction instance-attribute
metaculus_prediction: Aggregation
MyForecast

Bases: BaseModel

start_time instance-attribute
start_time: DatetimeUTC
end_time instance-attribute
end_time: DatetimeUTC | None
forecast_values instance-attribute
forecast_values: list[float] | None
interval_lower_bounds instance-attribute
interval_lower_bounds: list[float] | None
centers instance-attribute
centers: list[float] | None
interval_upper_bounds instance-attribute
interval_upper_bounds: list[float] | None
MyAggregation

Bases: BaseModel

history instance-attribute
history: list[MyForecast]
latest instance-attribute
latest: MyForecast | None
score_data instance-attribute
score_data: dict[str, Any]
Question

Bases: BaseModel

aggregations instance-attribute
aggregations: Aggregations
my_forecasts instance-attribute
my_forecasts: MyAggregation
type instance-attribute
type: QuestionType
possibilities instance-attribute
possibilities: dict[str, str] | None
description instance-attribute
description: str
fine_print instance-attribute
fine_print: str
resolution_criteria instance-attribute
resolution_criteria: str
MetaculusQuestion

Bases: BaseModel

id instance-attribute
id: int
author_id instance-attribute
author_id: int
author_username instance-attribute
author_username: str
title instance-attribute
title: str
created_at instance-attribute
created_at: DatetimeUTC
published_at instance-attribute
published_at: DatetimeUTC | None
scheduled_close_time instance-attribute
scheduled_close_time: DatetimeUTC
scheduled_resolve_time instance-attribute
scheduled_resolve_time: DatetimeUTC
user_permission instance-attribute
user_permission: str
comment_count instance-attribute
comment_count: int
question instance-attribute
question: Question
page_url property
page_url: str
p_yes property
p_yes: Probability
MetaculusQuestions

Bases: BaseModel

next instance-attribute
next: str | None
previous instance-attribute
previous: str | None
results instance-attribute
results: list[MetaculusQuestion]

metaculus

MetaculusAgentMarket

Bases: AgentMarket

Metaculus' market class that can be used by agents to make predictions.

have_predicted instance-attribute
have_predicted: bool
base_url class-attribute
base_url: str = METACULUS_API_BASE_URL
description instance-attribute
description: str
fine_print instance-attribute
fine_print: str
resolution_criteria instance-attribute
resolution_criteria: str
fees class-attribute instance-attribute
fees: MarketFees = get_zero_fees()
id instance-attribute
id: str
question instance-attribute
question: str
outcomes instance-attribute
outcomes: Sequence[OutcomeStr]
outcome_token_pool instance-attribute
outcome_token_pool: dict[OutcomeStr, OutcomeToken] | None
resolution instance-attribute
resolution: Resolution | None
created_time instance-attribute
created_time: DatetimeUTC | None
close_time instance-attribute
close_time: DatetimeUTC | None
probabilities instance-attribute
probabilities: dict[OutcomeStr, Probability]
url instance-attribute
url: str
volume instance-attribute
volume: CollateralToken | None
is_binary property
is_binary: bool
p_yes property
p_yes: Probability
p_no property
p_no: Probability
probable_resolution property
probable_resolution: Resolution
validate_probabilities
validate_probabilities(
    probs: dict[OutcomeStr, Probability],
    info: FieldValidationInfo,
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/metaculus/metaculus.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@field_validator("probabilities")
def validate_probabilities(
    cls,
    probs: dict[OutcomeStr, Probability],
    info: FieldValidationInfo,
) -> dict[OutcomeStr, Probability]:
    outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
    # We don't check for outcomes match because Metaculus has no filled outcomes.
    total = float(sum(probs.values()))
    if not 0.999 <= total <= 1.001:
        # We simply log a warning because for some use-cases (e.g. existing positions), the
        # markets might be already closed hence no reliable outcome token prices exist anymore.
        logger.warning(f"Probabilities for market {info.data=} do not sum to 1.")
    return probs
from_data_model staticmethod
from_data_model(
    model: MetaculusQuestion,
) -> MetaculusAgentMarket
Source code in prediction_market_agent_tooling/markets/metaculus/metaculus.py
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@staticmethod
def from_data_model(model: MetaculusQuestion) -> "MetaculusAgentMarket":
    probabilities = AgentMarket.build_probability_map_from_p_yes(p_yes=model.p_yes)
    return MetaculusAgentMarket(
        id=str(model.id),
        question=model.title,
        outcomes=[],
        resolution=None,
        created_time=model.created_at,
        close_time=model.scheduled_close_time,
        url=model.page_url,
        volume=None,
        have_predicted=model.question.my_forecasts.latest is not None,
        outcome_token_pool=None,
        description=model.question.description,
        fine_print=model.question.fine_print,
        resolution_criteria=model.question.resolution_criteria,
        probabilities=probabilities,
    )
get_markets staticmethod
get_markets(
    limit: int,
    sort_by: SortBy = SortBy.NONE,
    filter_by: FilterBy = FilterBy.OPEN,
    created_after: Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
    tournament_id: int | None = None,
) -> t.Sequence[MetaculusAgentMarket]
Source code in prediction_market_agent_tooling/markets/metaculus/metaculus.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
@staticmethod
def get_markets(
    limit: int,
    sort_by: SortBy = SortBy.NONE,
    filter_by: FilterBy = FilterBy.OPEN,
    created_after: t.Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
    tournament_id: int | None = None,
) -> t.Sequence["MetaculusAgentMarket"]:
    order_by: str | None
    if sort_by == SortBy.NONE:
        order_by = None
    elif sort_by == SortBy.CLOSING_SOONEST:
        order_by = "-close_time"
    elif sort_by == SortBy.NEWEST:
        order_by = "-created_time"
    else:
        raise ValueError(f"Unknown sort_by: {sort_by}")

    status: str | None
    if filter_by == FilterBy.OPEN:
        status = "open"
    elif filter_by == FilterBy.RESOLVED:
        status = "resolved"
    elif filter_by == FilterBy.NONE:
        status = None
    else:
        raise ValueError(f"Unknown filter_by: {filter_by}")

    if excluded_questions:
        raise NotImplementedError(
            "Excluded questions are not suppoerted for Metaculus markets yet."
        )

    offset = 0
    question_page_size = 500
    all_questions = []
    while True:
        questions = get_questions(
            limit=question_page_size,
            offset=offset,
            order_by=order_by,
            created_after=created_after,
            status=status,
            tournament_id=tournament_id,
        )
        if not questions:
            break
        all_questions.extend(questions)
        offset += question_page_size

        if len(all_questions) >= limit:
            break
    return [MetaculusAgentMarket.from_data_model(q) for q in all_questions[:limit]]
store_prediction
store_prediction(
    processed_market: ProcessedMarket | None,
    keys: APIKeys,
    agent_name: str,
) -> None
Source code in prediction_market_agent_tooling/markets/metaculus/metaculus.py
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def store_prediction(
    self, processed_market: ProcessedMarket | None, keys: APIKeys, agent_name: str
) -> None:
    if (
        processed_market is not None
        and processed_market.answer.get_yes_probability() is not None
    ):
        yes_prob = check_not_none(processed_market.answer.get_yes_probability())
        make_prediction(self.id, yes_prob)
        post_question_comment(
            self.id,
            check_not_none(
                processed_market.answer.reasoning,
                "Reasoning must be provided for Metaculus.",
            ),
        )
get_user_id staticmethod
get_user_id(api_keys: APIKeys) -> str
Source code in prediction_market_agent_tooling/markets/metaculus/metaculus.py
147
148
149
@staticmethod
def get_user_id(api_keys: APIKeys) -> str:
    return str(api_keys.metaculus_user_id)
verify_operational_balance staticmethod
verify_operational_balance(api_keys: APIKeys) -> bool
Source code in prediction_market_agent_tooling/markets/metaculus/metaculus.py
151
152
153
154
@staticmethod
def verify_operational_balance(api_keys: APIKeys) -> bool:
    # No operational balance for Metaculus.
    return True
redeem_winnings staticmethod
redeem_winnings(api_keys: APIKeys) -> None
Source code in prediction_market_agent_tooling/markets/metaculus/metaculus.py
156
157
158
159
@staticmethod
def redeem_winnings(api_keys: APIKeys) -> None:
    # Nothing to redeem on Metaculus.
    pass
validate_outcome_token_pool
validate_outcome_token_pool(
    outcome_token_pool: dict[str, OutcomeToken] | None,
    info: FieldValidationInfo,
) -> dict[str, OutcomeToken] | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
@field_validator("outcome_token_pool")
def validate_outcome_token_pool(
    cls,
    outcome_token_pool: dict[str, OutcomeToken] | None,
    info: FieldValidationInfo,
) -> dict[str, OutcomeToken] | None:
    outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
    if outcome_token_pool is not None:
        outcome_keys = set(outcome_token_pool.keys())
        expected_keys = set(outcomes)
        if outcome_keys != expected_keys:
            raise ValueError(
                f"Keys of outcome_token_pool ({outcome_keys}) do not match outcomes ({expected_keys})."
            )
    return outcome_token_pool
have_bet_on_market_since
have_bet_on_market_since(
    keys: APIKeys, since: timedelta
) -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
119
120
def have_bet_on_market_since(self, keys: APIKeys, since: timedelta) -> bool:
    raise NotImplementedError("Subclasses must implement this method")
get_outcome_token_pool_by_outcome
get_outcome_token_pool_by_outcome(
    outcome: OutcomeStr,
) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
122
123
124
125
126
127
128
def get_outcome_token_pool_by_outcome(self, outcome: OutcomeStr) -> OutcomeToken:
    if self.outcome_token_pool is None or not self.outcome_token_pool:
        return OutcomeToken(0)

    # We look up by index to avoid having to deal with case sensitivity issues.
    outcome_idx = self.get_outcome_index(outcome)
    return list(self.outcome_token_pool.values())[outcome_idx]
handle_legacy_fee
handle_legacy_fee(data: dict[str, Any]) -> dict[str, t.Any]
Source code in prediction_market_agent_tooling/markets/agent_market.py
130
131
132
133
134
135
136
@model_validator(mode="before")
def handle_legacy_fee(cls, data: dict[str, t.Any]) -> dict[str, t.Any]:
    # Backward compatibility for older `AgentMarket` without `fees`.
    if "fees" not in data and "fee" in data:
        data["fees"] = MarketFees(absolute=0.0, bet_proportion=data["fee"])
        del data["fee"]
    return data
market_outcome_for_probability_key
market_outcome_for_probability_key(
    probability_key: OutcomeStr,
) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
138
139
140
141
142
143
144
145
146
def market_outcome_for_probability_key(
    self, probability_key: OutcomeStr
) -> OutcomeStr:
    for market_outcome in self.outcomes:
        if market_outcome.lower() == probability_key.lower():
            return market_outcome
    raise ValueError(
        f"Could not find probability for probability key {probability_key}"
    )
probability_for_market_outcome
probability_for_market_outcome(
    market_outcome: OutcomeStr,
) -> Probability
Source code in prediction_market_agent_tooling/markets/agent_market.py
148
149
150
151
152
153
154
def probability_for_market_outcome(self, market_outcome: OutcomeStr) -> Probability:
    for k, v in self.probabilities.items():
        if k.lower() == market_outcome.lower():
            return v
    raise ValueError(
        f"Could not find probability for market outcome {market_outcome}"
    )
get_last_trade_p_yes
get_last_trade_p_yes() -> Probability | None

Get the last trade price for the YES outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1. Could be None if no trades were made.

Source code in prediction_market_agent_tooling/markets/agent_market.py
195
196
197
198
199
200
def get_last_trade_p_yes(self) -> Probability | None:
    """
    Get the last trade price for the YES outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1.
    Could be None if no trades were made.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_last_trade_p_no
get_last_trade_p_no() -> Probability | None

Get the last trade price for the NO outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1. Could be None if no trades were made.

Source code in prediction_market_agent_tooling/markets/agent_market.py
202
203
204
205
206
207
def get_last_trade_p_no(self) -> Probability | None:
    """
    Get the last trade price for the NO outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1.
    Could be None if no trades were made.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_last_trade_yes_outcome_price
get_last_trade_yes_outcome_price() -> (
    CollateralToken | None
)
Source code in prediction_market_agent_tooling/markets/agent_market.py
209
210
211
212
213
214
def get_last_trade_yes_outcome_price(self) -> CollateralToken | None:
    # Price on prediction markets are, by definition, equal to the probability of an outcome.
    # Just making it explicit in this function.
    if last_trade_p_yes := self.get_last_trade_p_yes():
        return CollateralToken(last_trade_p_yes)
    return None
get_last_trade_yes_outcome_price_usd
get_last_trade_yes_outcome_price_usd() -> USD | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
216
217
218
219
def get_last_trade_yes_outcome_price_usd(self) -> USD | None:
    if last_trade_yes_outcome_price := self.get_last_trade_yes_outcome_price():
        return self.get_token_in_usd(last_trade_yes_outcome_price)
    return None
get_last_trade_no_outcome_price
get_last_trade_no_outcome_price() -> CollateralToken | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
221
222
223
224
225
226
def get_last_trade_no_outcome_price(self) -> CollateralToken | None:
    # Price on prediction markets are, by definition, equal to the probability of an outcome.
    # Just making it explicit in this function.
    if last_trade_p_no := self.get_last_trade_p_no():
        return CollateralToken(last_trade_p_no)
    return None
get_last_trade_no_outcome_price_usd
get_last_trade_no_outcome_price_usd() -> USD | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
228
229
230
231
def get_last_trade_no_outcome_price_usd(self) -> USD | None:
    if last_trade_no_outcome_price := self.get_last_trade_no_outcome_price():
        return self.get_token_in_usd(last_trade_no_outcome_price)
    return None
get_liquidatable_amount
get_liquidatable_amount() -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
233
234
235
def get_liquidatable_amount(self) -> OutcomeToken:
    tiny_amount = self.get_tiny_bet_amount()
    return OutcomeToken.from_token(tiny_amount / 10)
get_token_in_usd
get_token_in_usd(x: CollateralToken) -> USD

Token of this market can have whatever worth (e.g. sDai and ETH markets will have different worth of 1 token). Use this to convert it to USD.

Source code in prediction_market_agent_tooling/markets/agent_market.py
237
238
239
240
241
def get_token_in_usd(self, x: CollateralToken) -> USD:
    """
    Token of this market can have whatever worth (e.g. sDai and ETH markets will have different worth of 1 token). Use this to convert it to USD.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_usd_in_token
get_usd_in_token(x: USD) -> CollateralToken

Markets on a single platform can have different tokens as collateral (sDai, wxDai, GNO, ...). Use this to convert USD to the token of this market.

Source code in prediction_market_agent_tooling/markets/agent_market.py
243
244
245
246
247
def get_usd_in_token(self, x: USD) -> CollateralToken:
    """
    Markets on a single platform can have different tokens as collateral (sDai, wxDai, GNO, ...). Use this to convert USD to the token of this market.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_sell_value_of_outcome_token
get_sell_value_of_outcome_token(
    outcome: OutcomeStr, amount: OutcomeToken
) -> CollateralToken

When you hold OutcomeToken(s), it's easy to calculate how much you get at the end if you win (1 OutcomeToken will equal to 1 Token). But use this to figure out, how much are these outcome tokens worth right now (for how much you can sell them).

Source code in prediction_market_agent_tooling/markets/agent_market.py
249
250
251
252
253
254
255
256
def get_sell_value_of_outcome_token(
    self, outcome: OutcomeStr, amount: OutcomeToken
) -> CollateralToken:
    """
    When you hold OutcomeToken(s), it's easy to calculate how much you get at the end if you win (1 OutcomeToken will equal to 1 Token).
    But use this to figure out, how much are these outcome tokens worth right now (for how much you can sell them).
    """
    raise NotImplementedError("Subclasses must implement this method")
get_in_usd
get_in_usd(x: USD | CollateralToken) -> USD
Source code in prediction_market_agent_tooling/markets/agent_market.py
258
259
260
261
def get_in_usd(self, x: USD | CollateralToken) -> USD:
    if isinstance(x, USD):
        return x
    return self.get_token_in_usd(x)
get_in_token
get_in_token(x: USD | CollateralToken) -> CollateralToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
263
264
265
266
def get_in_token(self, x: USD | CollateralToken) -> CollateralToken:
    if isinstance(x, CollateralToken):
        return x
    return self.get_usd_in_token(x)
get_tiny_bet_amount
get_tiny_bet_amount() -> CollateralToken

Tiny bet amount that the platform still allows us to do.

Source code in prediction_market_agent_tooling/markets/agent_market.py
268
269
270
271
272
def get_tiny_bet_amount(self) -> CollateralToken:
    """
    Tiny bet amount that the platform still allows us to do.
    """
    raise NotImplementedError("Subclasses must implement this method")
liquidate_existing_positions
liquidate_existing_positions(outcome: OutcomeStr) -> None
Source code in prediction_market_agent_tooling/markets/agent_market.py
274
275
def liquidate_existing_positions(self, outcome: OutcomeStr) -> None:
    raise NotImplementedError("Subclasses must implement this method")
place_bet
place_bet(outcome: OutcomeStr, amount: USD) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
277
278
def place_bet(self, outcome: OutcomeStr, amount: USD) -> str:
    raise NotImplementedError("Subclasses must implement this method")
buy_tokens
buy_tokens(outcome: OutcomeStr, amount: USD) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
280
281
def buy_tokens(self, outcome: OutcomeStr, amount: USD) -> str:
    return self.place_bet(outcome=outcome, amount=amount)
get_buy_token_amount
get_buy_token_amount(
    bet_amount: USD | CollateralToken, outcome: OutcomeStr
) -> OutcomeToken | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
283
284
285
286
def get_buy_token_amount(
    self, bet_amount: USD | CollateralToken, outcome: OutcomeStr
) -> OutcomeToken | None:
    raise NotImplementedError("Subclasses must implement this method")
sell_tokens
sell_tokens(
    outcome: OutcomeStr, amount: USD | OutcomeToken
) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
288
289
def sell_tokens(self, outcome: OutcomeStr, amount: USD | OutcomeToken) -> str:
    raise NotImplementedError("Subclasses must implement this method")
compute_fpmm_probabilities staticmethod
compute_fpmm_probabilities(
    balances: list[OutcomeWei],
) -> list[Probability]

Compute the implied probabilities in a Fixed Product Market Maker.

Parameters:

Name Type Description Default
balances List[float]

Balances of outcome tokens.

required

Returns:

Type Description
list[Probability]

List[float]: Implied probabilities for each outcome.

Source code in prediction_market_agent_tooling/markets/agent_market.py
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
@staticmethod
def compute_fpmm_probabilities(balances: list[OutcomeWei]) -> list[Probability]:
    """
    Compute the implied probabilities in a Fixed Product Market Maker.

    Args:
        balances (List[float]): Balances of outcome tokens.

    Returns:
        List[float]: Implied probabilities for each outcome.
    """
    if all(x.value == 0 for x in balances):
        return [Probability(0.0)] * len(balances)

    # converting to standard values for prod compatibility.
    values_balance = [i.value for i in balances]
    # Compute product of balances excluding each outcome
    excluded_products = []
    for i in range(len(values_balance)):
        other_balances = values_balance[:i] + values_balance[i + 1 :]
        excluded_products.append(prod(other_balances))

    # Normalize to sum to 1
    total = sum(excluded_products)
    if total == 0:
        return [Probability(0.0)] * len(balances)
    probabilities = [Probability(p / total) for p in excluded_products]

    return probabilities
build_probability_map_from_p_yes staticmethod
build_probability_map_from_p_yes(
    p_yes: Probability,
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
321
322
323
324
325
326
327
328
@staticmethod
def build_probability_map_from_p_yes(
    p_yes: Probability,
) -> dict[OutcomeStr, Probability]:
    return {
        OutcomeStr(YES_OUTCOME_LOWERCASE_IDENTIFIER): p_yes,
        OutcomeStr(NO_OUTCOME_LOWERCASE_IDENTIFIER): Probability(1.0 - p_yes),
    }
build_probability_map staticmethod
build_probability_map(
    outcome_token_amounts: list[OutcomeWei],
    outcomes: list[OutcomeStr],
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
330
331
332
333
334
335
@staticmethod
def build_probability_map(
    outcome_token_amounts: list[OutcomeWei], outcomes: list[OutcomeStr]
) -> dict[OutcomeStr, Probability]:
    probs = AgentMarket.compute_fpmm_probabilities(outcome_token_amounts)
    return {outcome: prob for outcome, prob in zip(outcomes, probs)}
get_binary_market staticmethod
get_binary_market(id: str) -> AgentMarket
Source code in prediction_market_agent_tooling/markets/agent_market.py
348
349
350
@staticmethod
def get_binary_market(id: str) -> "AgentMarket":
    raise NotImplementedError("Subclasses must implement this method")
get_trade_balance classmethod
get_trade_balance(api_keys: APIKeys) -> USD

Return balance that can be used to trade on the given market.

Source code in prediction_market_agent_tooling/markets/agent_market.py
359
360
361
362
363
364
@classmethod
def get_trade_balance(cls, api_keys: APIKeys) -> USD:
    """
    Return balance that can be used to trade on the given market.
    """
    raise NotImplementedError("Subclasses must implement this method")
store_trades
store_trades(
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None

If market allows to upload trades somewhere, implement it in this method.

Source code in prediction_market_agent_tooling/markets/agent_market.py
385
386
387
388
389
390
391
392
393
394
395
def store_trades(
    self,
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None:
    """
    If market allows to upload trades somewhere, implement it in this method.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_bets_made_since staticmethod
get_bets_made_since(
    better_address: ChecksumAddress, start_time: DatetimeUTC
) -> list[Bet]
Source code in prediction_market_agent_tooling/markets/agent_market.py
397
398
399
400
401
@staticmethod
def get_bets_made_since(
    better_address: ChecksumAddress, start_time: DatetimeUTC
) -> list[Bet]:
    raise NotImplementedError("Subclasses must implement this method")
get_resolved_bets_made_since staticmethod
get_resolved_bets_made_since(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC,
    end_time: DatetimeUTC | None,
) -> list[ResolvedBet]
Source code in prediction_market_agent_tooling/markets/agent_market.py
403
404
405
406
407
408
409
@staticmethod
def get_resolved_bets_made_since(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC,
    end_time: DatetimeUTC | None,
) -> list[ResolvedBet]:
    raise NotImplementedError("Subclasses must implement this method")
is_closed
is_closed() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
411
412
def is_closed(self) -> bool:
    return self.close_time is not None and self.close_time <= utcnow()
is_resolved
is_resolved() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
414
415
def is_resolved(self) -> bool:
    return self.resolution is not None
get_liquidity
get_liquidity() -> CollateralToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
417
418
def get_liquidity(self) -> CollateralToken:
    raise NotImplementedError("Subclasses must implement this method")
has_liquidity
has_liquidity() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
420
421
def has_liquidity(self) -> bool:
    return self.get_liquidity() > 0
has_successful_resolution
has_successful_resolution() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
423
424
425
426
427
428
def has_successful_resolution(self) -> bool:
    return (
        self.resolution is not None
        and self.resolution.outcome is not None
        and not self.resolution.invalid
    )
has_unsuccessful_resolution
has_unsuccessful_resolution() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
430
431
def has_unsuccessful_resolution(self) -> bool:
    return self.resolution is not None and self.resolution.invalid
get_outcome_str_from_bool staticmethod
get_outcome_str_from_bool(outcome: bool) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
433
434
435
@staticmethod
def get_outcome_str_from_bool(outcome: bool) -> OutcomeStr:
    raise NotImplementedError("Subclasses must implement this method")
get_outcome_str
get_outcome_str(outcome_index: int) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
437
438
439
440
441
442
443
def get_outcome_str(self, outcome_index: int) -> OutcomeStr:
    try:
        return self.outcomes[outcome_index]
    except IndexError:
        raise IndexError(
            f"Outcome index `{outcome_index}` out of range for `{self.outcomes}`: `{self.outcomes}`."
        )
get_outcome_index
get_outcome_index(outcome: OutcomeStr) -> int
Source code in prediction_market_agent_tooling/markets/agent_market.py
445
446
447
448
449
450
def get_outcome_index(self, outcome: OutcomeStr) -> int:
    outcomes_lowercase = [o.lower() for o in self.outcomes]
    try:
        return outcomes_lowercase.index(outcome.lower())
    except ValueError:
        raise ValueError(f"Outcome `{outcome}` not found in `{self.outcomes}`.")
get_token_balance
get_token_balance(
    user_id: str, outcome: OutcomeStr
) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
452
453
def get_token_balance(self, user_id: str, outcome: OutcomeStr) -> OutcomeToken:
    raise NotImplementedError("Subclasses must implement this method")
get_position
get_position(user_id: str) -> ExistingPosition | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
455
456
def get_position(self, user_id: str) -> ExistingPosition | None:
    raise NotImplementedError("Subclasses must implement this method")
get_positions classmethod
get_positions(
    user_id: str,
    liquid_only: bool = False,
    larger_than: OutcomeToken = OutcomeToken(0),
) -> t.Sequence[ExistingPosition]

Get all non-zero positions a user has in any market.

If liquid_only is True, only return positions that can be sold.

If larger_than is not None, only return positions with a larger number of tokens than this amount.

Source code in prediction_market_agent_tooling/markets/agent_market.py
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
@classmethod
def get_positions(
    cls,
    user_id: str,
    liquid_only: bool = False,
    larger_than: OutcomeToken = OutcomeToken(0),
) -> t.Sequence[ExistingPosition]:
    """
    Get all non-zero positions a user has in any market.

    If `liquid_only` is True, only return positions that can be sold.

    If `larger_than` is not None, only return positions with a larger number
    of tokens than this amount.
    """
    raise NotImplementedError("Subclasses must implement this method")
can_be_traded
can_be_traded() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
475
476
477
478
def can_be_traded(self) -> bool:
    if self.is_closed() or not self.has_liquidity():
        return False
    return True
get_user_url classmethod
get_user_url(keys: APIKeys) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
480
481
482
@classmethod
def get_user_url(cls, keys: APIKeys) -> str:
    raise NotImplementedError("Subclasses must implement this method")
has_token_pool
has_token_pool() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
484
485
def has_token_pool(self) -> bool:
    return self.outcome_token_pool is not None
get_pool_tokens
get_pool_tokens(outcome: OutcomeStr) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
487
488
489
490
491
def get_pool_tokens(self, outcome: OutcomeStr) -> OutcomeToken:
    if not self.outcome_token_pool:
        raise ValueError("Outcome token pool is not available.")

    return self.outcome_token_pool[outcome]
get_user_balance staticmethod
get_user_balance(user_id: str) -> float
Source code in prediction_market_agent_tooling/markets/agent_market.py
493
494
495
@staticmethod
def get_user_balance(user_id: str) -> float:
    raise NotImplementedError("Subclasses must implement this method")
get_most_recent_trade_datetime
get_most_recent_trade_datetime(
    user_id: str,
) -> DatetimeUTC | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
501
502
def get_most_recent_trade_datetime(self, user_id: str) -> DatetimeUTC | None:
    raise NotImplementedError("Subclasses must implement this method")

omen

cow_contracts

CowGPv2SettlementContract

Bases: ContractOnGnosisChain

abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../../abis/gvp2_settlement.abi.json",
    )
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
address instance-attribute
address: ChecksumAddress
setPreSignature
setPreSignature(
    api_keys: APIKeys,
    orderId: HexBytes,
    signed: bool,
    web3: Web3 | None = None,
) -> None
Source code in prediction_market_agent_tooling/markets/omen/cow_contracts.py
22
23
24
25
26
27
28
29
30
31
32
33
34
def setPreSignature(
    self,
    api_keys: APIKeys,
    orderId: HexBytes,
    signed: bool,
    web3: Web3 | None = None,
) -> None:
    self.send(
        api_keys=api_keys,
        function_name="setPreSignature",
        function_params=[orderId, signed],
        web3=web3,
    )
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()

data_models

OMEN_TRUE_OUTCOME module-attribute
OMEN_TRUE_OUTCOME = OutcomeStr('Yes')
OMEN_FALSE_OUTCOME module-attribute
OMEN_FALSE_OUTCOME = OutcomeStr('No')
OMEN_BINARY_MARKET_OUTCOMES module-attribute
OMEN_BINARY_MARKET_OUTCOMES: Sequence[OutcomeStr] = [
    OMEN_TRUE_OUTCOME,
    OMEN_FALSE_OUTCOME,
]
INVALID_ANSWER module-attribute
INVALID_ANSWER = 115792089237316195423570985008687907853269984665640564039457584007913129639935
INVALID_ANSWER_HEX_BYTES module-attribute
INVALID_ANSWER_HEX_BYTES = HexBytes(INVALID_ANSWER)
INVALID_ANSWER_STR module-attribute
INVALID_ANSWER_STR = HexStr(hex())
OMEN_BASE_URL module-attribute
OMEN_BASE_URL = 'https://aiomen.eth.limo'
PRESAGIO_BASE_URL module-attribute
PRESAGIO_BASE_URL = 'https://presagio.pages.dev'
TEST_CATEGORY module-attribute
TEST_CATEGORY = 'test'
Condition

Bases: BaseModel

id instance-attribute
id: HexBytes
outcomeSlotCount instance-attribute
outcomeSlotCount: int
index_sets property
index_sets: List[int]
Question

Bases: BaseModel

id instance-attribute
id: HexBytes
title instance-attribute
title: str
data instance-attribute
data: str
templateId instance-attribute
templateId: int
outcomes instance-attribute
outcomes: Sequence[OutcomeStr]
isPendingArbitration instance-attribute
isPendingArbitration: bool
openingTimestamp instance-attribute
openingTimestamp: int
answerFinalizedTimestamp class-attribute instance-attribute
answerFinalizedTimestamp: Optional[DatetimeUTC] = None
currentAnswer class-attribute instance-attribute
currentAnswer: Optional[str] = None
question_id property
question_id: HexBytes
question_raw property
question_raw: str
n_outcomes property
n_outcomes: int
opening_datetime property
opening_datetime: DatetimeUTC
has_answer property
has_answer: bool
outcome_index property
outcome_index: int | None
is_binary property
is_binary: bool
has_valid_answer property
has_valid_answer: bool
boolean_outcome property
boolean_outcome: bool
OmenPosition

Bases: BaseModel

id instance-attribute
id: HexBytes
conditionIds instance-attribute
conditionIds: list[HexBytes]
collateralTokenAddress instance-attribute
collateralTokenAddress: HexAddress
indexSets instance-attribute
indexSets: list[int]
condition_id property
condition_id: HexBytes
index_set property
index_set: int
collateral_token_contract_address_checksummed property
collateral_token_contract_address_checksummed: (
    ChecksumAddress
)
get_collateral_token_contract
get_collateral_token_contract(
    web3: Web3 | None = None,
) -> ContractERC20OnGnosisChain
Source code in prediction_market_agent_tooling/markets/omen/data_models.py
179
180
181
182
183
184
185
186
187
def get_collateral_token_contract(
    self, web3: Web3 | None = None
) -> ContractERC20OnGnosisChain:
    web3 = web3 or ContractERC20OnGnosisChain.get_web3()
    return to_gnosis_chain_contract(
        init_collateral_token_contract(
            self.collateral_token_contract_address_checksummed, web3
        )
    )
OmenUserPosition

Bases: BaseModel

id instance-attribute
id: HexBytes
position instance-attribute
position: OmenPosition
balance instance-attribute
balance: OutcomeWei
wrappedBalance instance-attribute
wrappedBalance: OutcomeWei
totalBalance instance-attribute
totalBalance: OutcomeWei
redeemable property
redeemable: bool
OmenMarket

Bases: BaseModel

https://presagio.pages.dev

An Omen market goes through the following stages:

  1. creation - can add liquidty immediately, and trade immediately if there is liquidity
  2. closing - market is closed, and a question is simultaneously opened for answers on Reality
  3. finalizing - the question is finalized on reality (including any disputes)
  4. resolving - a manual step required by calling the Omen oracle contract
  5. redeeming - a user withdraws collateral tokens from the market
id instance-attribute
id: HexAddress
title instance-attribute
title: str
creator instance-attribute
creator: HexAddress
category instance-attribute
category: str
collateralVolume instance-attribute
collateralVolume: Wei
liquidityParameter instance-attribute
liquidityParameter: Wei
usdVolume instance-attribute
usdVolume: USD
collateralToken instance-attribute
collateralToken: HexAddress
outcomes instance-attribute
outcomes: Sequence[OutcomeStr]
outcomeTokenAmounts instance-attribute
outcomeTokenAmounts: list[OutcomeWei]
outcomeTokenMarginalPrices instance-attribute
outcomeTokenMarginalPrices: Optional[list[CollateralToken]]
fee instance-attribute
fee: Optional[Wei]
resolutionTimestamp class-attribute instance-attribute
resolutionTimestamp: Optional[int] = None
answerFinalizedTimestamp class-attribute instance-attribute
answerFinalizedTimestamp: Optional[int] = None
currentAnswer class-attribute instance-attribute
currentAnswer: Optional[HexBytes] = None
creationTimestamp instance-attribute
creationTimestamp: int
condition instance-attribute
condition: Condition
question instance-attribute
question: Question
openingTimestamp property
openingTimestamp: int
opening_datetime property
opening_datetime: DatetimeUTC
close_time property
close_time: DatetimeUTC
answer_index property
answer_index: Optional[int]
has_valid_answer property
has_valid_answer: bool
is_open property
is_open: bool
is_resolved property
is_resolved: bool
is_resolved_with_valid_answer property
is_resolved_with_valid_answer: bool
question_title property
question_title: str
creation_datetime property
creation_datetime: DatetimeUTC
finalized_datetime property
finalized_datetime: DatetimeUTC | None
has_bonded_outcome property
has_bonded_outcome: bool
market_maker_contract_address property
market_maker_contract_address: HexAddress
market_maker_contract_address_checksummed property
market_maker_contract_address_checksummed: ChecksumAddress
collateral_token_contract_address property
collateral_token_contract_address: HexAddress
collateral_token_contract_address_checksummed property
collateral_token_contract_address_checksummed: (
    ChecksumAddress
)
outcomeTokenProbabilities property
outcomeTokenProbabilities: Optional[list[Probability]]
yes_index property
yes_index: int
no_index property
no_index: int
current_p_no property
current_p_no: Probability
current_p_yes property
current_p_yes: Probability

Calculate the probability of the outcomes from the relative token amounts.

Note, not all markets reliably have outcomeTokenMarginalPrices, hence we use the relative proportion of outcomeTokenAmounts to calculate the probabilities.

The higher the proportion of available outcome tokens for a given outcome, the the lower the price of that token, and therefore the lower the probability of that outcome.

is_binary property
is_binary: bool
url property
url: str
outcome_from_answer
outcome_from_answer(answer: HexBytes) -> OutcomeStr | None
Source code in prediction_market_agent_tooling/markets/omen/data_models.py
402
403
404
405
def outcome_from_answer(self, answer: HexBytes) -> OutcomeStr | None:
    if answer == INVALID_ANSWER_HEX_BYTES:
        return None
    return self.outcomes[answer.as_int()]
get_resolution_enum_from_answer
get_resolution_enum_from_answer(
    answer: HexBytes,
) -> Resolution
Source code in prediction_market_agent_tooling/markets/omen/data_models.py
407
408
409
410
411
def get_resolution_enum_from_answer(self, answer: HexBytes) -> Resolution:
    if outcome := self.outcome_from_answer(answer):
        return Resolution.from_answer(outcome)

    return Resolution(outcome=None, invalid=True)
get_resolution_enum
get_resolution_enum() -> t.Optional[Resolution]
Source code in prediction_market_agent_tooling/markets/omen/data_models.py
413
414
415
416
417
418
419
420
def get_resolution_enum(self) -> t.Optional[Resolution]:
    if not self.is_resolved_with_valid_answer:
        return None
    return self.get_resolution_enum_from_answer(
        check_not_none(
            self.currentAnswer, "Can not be None if `is_resolved_with_valid_answer`"
        )
    )
from_created_market staticmethod
from_created_market(model: CreatedMarket) -> OmenMarket

OmenMarket is meant to be retrieved from subgraph, however in tests against local chain it's very handy to create it out of CreatedMarket, which is collection of events that are emitted during the market creation in omen_create_market_tx function.

Source code in prediction_market_agent_tooling/markets/omen/data_models.py
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
@staticmethod
def from_created_market(model: "CreatedMarket") -> "OmenMarket":
    """
    OmenMarket is meant to be retrieved from subgraph, however in tests against local chain it's very handy to create it out of `CreatedMarket`,
    which is collection of events that are emitted during the market creation in omen_create_market_tx function.
    """
    if len(model.market_event.conditionIds) != 1:
        raise ValueError(
            f"Unexpected number of conditions: {len(model.market_event.conditionIds)}"
        )
    outcome_token_amounts = model.funding_event.outcome_token_amounts
    return OmenMarket(
        id=HexAddress(
            HexStr(model.market_event.fixedProductMarketMaker.lower())
        ),  # Lowering to be identical with subgraph's output.
        title=model.question_event.parsed_question.question,
        creator=HexAddress(
            HexStr(model.market_event.creator.lower())
        ),  # Lowering to be identical with subgraph's output.
        category=model.question_event.parsed_question.category,
        collateralVolume=Wei(0),  # No volume possible yet.
        liquidityParameter=calculate_liquidity_parameter(outcome_token_amounts),
        usdVolume=USD(0),  # No volume possible yet.
        fee=model.fee,
        collateralToken=HexAddress(
            HexStr(model.market_event.collateralToken.lower())
        ),  # Lowering to be identical with subgraph's output.
        outcomes=model.question_event.parsed_question.outcomes,
        outcomeTokenAmounts=outcome_token_amounts,
        outcomeTokenMarginalPrices=calculate_marginal_prices(outcome_token_amounts),
        answerFinalizedTimestamp=None,  # It's a fresh market.
        currentAnswer=None,  # It's a fresh market.
        creationTimestamp=model.market_creation_timestamp,
        condition=Condition(
            id=model.market_event.conditionIds[0],
            outcomeSlotCount=len(model.question_event.parsed_question.outcomes),
        ),
        question=Question(
            id=model.question_event.question_id,
            title=model.question_event.parsed_question.question,
            data=model.question_event.question,  # Question in the event holds the "raw" data.
            templateId=model.question_event.template_id,
            outcomes=model.question_event.parsed_question.outcomes,
            isPendingArbitration=False,  # Can not be, it's a fresh market.
            openingTimestamp=model.question_event.opening_ts,
            answerFinalizedTimestamp=None,  # It's a new one, can not be.
            currentAnswer=None,  # It's a new one, no answer yet.
        ),
    )
OmenBetCreator

Bases: BaseModel

id instance-attribute
id: HexAddress
OmenBet

Bases: BaseModel

id instance-attribute
id: HexAddress
title instance-attribute
title: str
collateralToken instance-attribute
collateralToken: HexAddress
outcomeTokenMarginalPrice instance-attribute
outcomeTokenMarginalPrice: CollateralToken
oldOutcomeTokenMarginalPrice instance-attribute
oldOutcomeTokenMarginalPrice: CollateralToken
type instance-attribute
type: str
creator instance-attribute
creator: OmenBetCreator
creationTimestamp instance-attribute
creationTimestamp: int
collateralAmount instance-attribute
collateralAmount: Wei
feeAmount instance-attribute
feeAmount: Wei
outcomeIndex instance-attribute
outcomeIndex: int
outcomeTokensTraded instance-attribute
outcomeTokensTraded: OutcomeWei
transactionHash instance-attribute
transactionHash: HexBytes
fpmm instance-attribute
fpmm: OmenMarket
collateral_amount_token property
collateral_amount_token: CollateralToken
collateral_token_checksummed property
collateral_token_checksummed: ChecksumAddress
creation_datetime property
creation_datetime: DatetimeUTC
old_probability property
old_probability: Probability
probability property
probability: Probability
get_collateral_amount_usd
get_collateral_amount_usd() -> USD
Source code in prediction_market_agent_tooling/markets/omen/data_models.py
559
560
561
562
def get_collateral_amount_usd(self) -> USD:
    return get_token_in_usd(
        self.collateral_amount_token, self.collateral_token_checksummed
    )
get_profit
get_profit() -> CollateralToken
Source code in prediction_market_agent_tooling/markets/omen/data_models.py
564
565
566
567
568
569
570
571
572
573
574
575
def get_profit(self) -> CollateralToken:
    bet_amount = self.collateral_amount_token

    if not self.fpmm.has_valid_answer:
        return CollateralToken(0)

    profit = (
        self.outcomeTokensTraded.as_outcome_token.as_token - bet_amount
        if self.outcomeIndex == self.fpmm.answer_index
        else -bet_amount
    )
    return profit
to_bet
to_bet() -> Bet
Source code in prediction_market_agent_tooling/markets/omen/data_models.py
577
578
579
580
581
582
583
584
585
586
def to_bet(self) -> Bet:
    return Bet(
        id=str(self.transactionHash),
        # Use the transaction hash instead of the bet id - both are valid, but we return the transaction hash from the trade functions, so be consistent here.
        amount=self.collateral_amount_token,
        outcome=self.fpmm.outcomes[self.outcomeIndex],
        created_time=self.creation_datetime,
        market_question=self.title,
        market_id=self.fpmm.id,
    )
to_generic_resolved_bet
to_generic_resolved_bet() -> ResolvedBet
Source code in prediction_market_agent_tooling/markets/omen/data_models.py
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
def to_generic_resolved_bet(self) -> ResolvedBet:
    if not self.fpmm.is_resolved_with_valid_answer:
        raise ValueError(
            f"Bet with title {self.title} is not resolved. It has no resolution time."
        )

    return ResolvedBet(
        id=self.transactionHash.hex(),
        # Use the transaction hash instead of the bet id - both are valid, but we return the transaction hash from the trade functions, so be consistent here.
        amount=self.collateral_amount_token,
        outcome=self.fpmm.outcomes[self.outcomeIndex],
        created_time=self.creation_datetime,
        market_question=self.title,
        market_id=self.fpmm.id,
        market_outcome=self.fpmm.outcomes[self.outcomeIndex],
        resolved_time=check_not_none(self.fpmm.finalized_datetime),
        profit=self.get_profit(),
    )
FixedProductMarketMakersData

Bases: BaseModel

fixedProductMarketMakers instance-attribute
fixedProductMarketMakers: list[OmenMarket]
FixedProductMarketMakersResponse

Bases: BaseModel

data instance-attribute
data: FixedProductMarketMakersData
RealityQuestion

Bases: BaseModel

id instance-attribute
id: str
user instance-attribute
user: HexAddress
historyHash instance-attribute
historyHash: HexBytes | None
updatedTimestamp instance-attribute
updatedTimestamp: int
contentHash instance-attribute
contentHash: HexBytes
questionId instance-attribute
questionId: HexBytes
answerFinalizedTimestamp instance-attribute
answerFinalizedTimestamp: int | None
currentScheduledFinalizationTimestamp instance-attribute
currentScheduledFinalizationTimestamp: int
updated_datetime property
updated_datetime: DatetimeUTC
answer_finalized_datetime property
answer_finalized_datetime: DatetimeUTC | None
current_scheduled_finalization_datetime property
current_scheduled_finalization_datetime: DatetimeUTC
url property
url: str
RealityAnswer

Bases: BaseModel

id instance-attribute
id: str
timestamp instance-attribute
timestamp: int
answer instance-attribute
answer: HexBytes
lastBond instance-attribute
lastBond: Wei
bondAggregate instance-attribute
bondAggregate: Wei
question instance-attribute
question: RealityQuestion
createdBlock instance-attribute
createdBlock: int
timestamp_datetime property
timestamp_datetime: DatetimeUTC
RealityResponse

Bases: BaseModel

This is similar to RealityAnswer, but contains additional fields, most importantly historyHash.

id instance-attribute
id: str
timestamp instance-attribute
timestamp: int
answer instance-attribute
answer: HexBytes
isUnrevealed instance-attribute
isUnrevealed: bool
isCommitment instance-attribute
isCommitment: bool
bond instance-attribute
bond: xDaiWei
user instance-attribute
user: HexAddress
historyHash instance-attribute
historyHash: HexBytes
question instance-attribute
question: RealityQuestion
createdBlock instance-attribute
createdBlock: int
revealedBlock instance-attribute
revealedBlock: int | None
bond_xdai property
bond_xdai: xDai
user_checksummed property
user_checksummed: ChecksumAddress
RealityAnswers

Bases: BaseModel

answers instance-attribute
answers: list[RealityAnswer]
RealityAnswersResponse

Bases: BaseModel

data instance-attribute
data: RealityAnswers
ParsedQuestion

Bases: BaseModel

question instance-attribute
question: str
outcomes instance-attribute
outcomes: Sequence[OutcomeStr]
language instance-attribute
language: str
category instance-attribute
category: str
RealitioLogNewQuestionEvent

Bases: BaseModel

question_id instance-attribute
question_id: HexBytes
user instance-attribute
user: HexAddress
template_id instance-attribute
template_id: int
question instance-attribute
question: str
content_hash instance-attribute
content_hash: HexBytes
arbitrator instance-attribute
arbitrator: HexAddress
timeout instance-attribute
timeout: int
opening_ts instance-attribute
opening_ts: int
nonce instance-attribute
nonce: int
created instance-attribute
created: int
user_checksummed property
user_checksummed: ChecksumAddress
parsed_question property
parsed_question: ParsedQuestion
OmenFixedProductMarketMakerCreationEvent

Bases: BaseModel

creator instance-attribute
creator: HexAddress
fixedProductMarketMaker instance-attribute
fixedProductMarketMaker: HexAddress
conditionalTokens instance-attribute
conditionalTokens: HexAddress
collateralToken instance-attribute
collateralToken: HexAddress
conditionIds instance-attribute
conditionIds: list[HexBytes]
fee instance-attribute
fee: int
creator_checksummed property
creator_checksummed: ChecksumAddress
fixed_product_market_maker_checksummed property
fixed_product_market_maker_checksummed: ChecksumAddress
conditional_tokens_checksummed property
conditional_tokens_checksummed: ChecksumAddress
collateral_token_checksummed property
collateral_token_checksummed: ChecksumAddress
ConditionPreparationEvent

Bases: BaseModel

conditionId instance-attribute
conditionId: HexBytes
oracle instance-attribute
oracle: HexAddress
questionId instance-attribute
questionId: HexBytes
outcomeSlotCount instance-attribute
outcomeSlotCount: int
FPMMFundingAddedEvent

Bases: BaseModel

funder instance-attribute
funder: HexAddress
amountsAdded instance-attribute
amountsAdded: list[Wei]
sharesMinted instance-attribute
sharesMinted: Wei
outcome_token_amounts property
outcome_token_amounts: list[OutcomeWei]
CreatedMarket

Bases: BaseModel

market_creation_timestamp instance-attribute
market_creation_timestamp: int
market_event instance-attribute
market_event: OmenFixedProductMarketMakerCreationEvent
funding_event instance-attribute
funding_event: FPMMFundingAddedEvent
condition_id instance-attribute
condition_id: HexBytes
question_event instance-attribute
question_event: RealitioLogNewQuestionEvent
condition_event instance-attribute
condition_event: ConditionPreparationEvent | None
initial_funds instance-attribute
initial_funds: Wei
fee instance-attribute
fee: Wei
distribution_hint instance-attribute
distribution_hint: list[OutcomeWei] | None
url property
url: str
ContractPrediction

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(populate_by_name=True)
publisher class-attribute instance-attribute
publisher: str = Field(..., alias='publisherAddress')
ipfs_hash class-attribute instance-attribute
ipfs_hash: HexBytes = Field(..., alias='ipfsHash')
tx_hashes class-attribute instance-attribute
tx_hashes: list[HexBytes] = Field(..., alias='txHashes')
estimated_probability_bps class-attribute instance-attribute
estimated_probability_bps: int = Field(
    ..., alias="estimatedProbabilityBps"
)
estimated_probability property
estimated_probability: Probability
boolean_outcome property
boolean_outcome: bool
publisher_checksummed property
publisher_checksummed: ChecksumAddress
from_tuple staticmethod
from_tuple(values: tuple[Any, ...]) -> ContractPrediction
Source code in prediction_market_agent_tooling/markets/omen/data_models.py
847
848
849
850
851
852
853
854
@staticmethod
def from_tuple(values: tuple[t.Any, ...]) -> "ContractPrediction":
    return ContractPrediction(
        publisher=values[0],
        ipfs_hash=values[1],
        tx_hashes=values[2],
        estimated_probability_bps=values[3],
    )
IPFSAgentResult

Bases: BaseModel

reasoning instance-attribute
reasoning: str
agent_name instance-attribute
agent_name: str
model_config class-attribute instance-attribute
model_config = ConfigDict(extra='forbid')
PayoutRedemptionEvent

Bases: BaseModel

redeemer instance-attribute
redeemer: HexAddress
collateralToken instance-attribute
collateralToken: HexAddress
parentCollectionId instance-attribute
parentCollectionId: HexBytes
conditionId instance-attribute
conditionId: HexBytes
indexSets instance-attribute
indexSets: list[int]
payout instance-attribute
payout: Wei
construct_presagio_url
construct_presagio_url(market_id: HexAddress) -> str
Source code in prediction_market_agent_tooling/markets/omen/data_models.py
54
55
def construct_presagio_url(market_id: HexAddress) -> str:
    return f"{PRESAGIO_BASE_URL}/markets?id={market_id}"
get_boolean_outcome
get_boolean_outcome(outcome_str: str) -> bool
Source code in prediction_market_agent_tooling/markets/omen/data_models.py
58
59
60
61
62
63
def get_boolean_outcome(outcome_str: str) -> bool:
    if outcome_str == OMEN_TRUE_OUTCOME:
        return True
    if outcome_str == OMEN_FALSE_OUTCOME:
        return False
    raise ValueError(f"Outcome `{outcome_str}` is not a valid boolean outcome.")
get_bet_outcome
get_bet_outcome(binary_outcome: bool) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/omen/data_models.py
66
67
def get_bet_outcome(binary_outcome: bool) -> OutcomeStr:
    return OMEN_TRUE_OUTCOME if binary_outcome else OMEN_FALSE_OUTCOME
calculate_liquidity_parameter
calculate_liquidity_parameter(
    outcome_token_amounts: list[OutcomeWei],
) -> Wei

Converted to Python from https://github.com/protofire/omen-subgraph/blob/f92bbfb6fa31ed9cd5985c416a26a2f640837d8b/src/utils/fpmm.ts#L171.

Source code in prediction_market_agent_tooling/markets/omen/data_models.py
477
478
479
480
481
482
483
484
485
486
487
488
def calculate_liquidity_parameter(
    outcome_token_amounts: list[OutcomeWei],
) -> Wei:
    """
    Converted to Python from https://github.com/protofire/omen-subgraph/blob/f92bbfb6fa31ed9cd5985c416a26a2f640837d8b/src/utils/fpmm.ts#L171.
    """
    amounts_product = Wei(1)
    for amount in outcome_token_amounts:
        amounts_product *= amount.as_wei
    n = len(outcome_token_amounts)
    liquidity_parameter = amounts_product.value ** (1.0 / n)
    return Wei(liquidity_parameter)
calculate_marginal_prices
calculate_marginal_prices(
    outcome_token_amounts: list[OutcomeWei],
) -> list[CollateralToken] | None

Converted to Python from https://github.com/protofire/omen-subgraph/blob/f92bbfb6fa31ed9cd5985c416a26a2f640837d8b/src/utils/fpmm.ts#L197.

Source code in prediction_market_agent_tooling/markets/omen/data_models.py
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
def calculate_marginal_prices(
    outcome_token_amounts: list[OutcomeWei],
) -> list[CollateralToken] | None:
    """
    Converted to Python from https://github.com/protofire/omen-subgraph/blob/f92bbfb6fa31ed9cd5985c416a26a2f640837d8b/src/utils/fpmm.ts#L197.
    """
    all_non_zero = all(x != 0 for x in outcome_token_amounts)
    if not all_non_zero:
        return None

    n_outcomes = len(outcome_token_amounts)
    weights: list[Wei] = []

    for i in range(n_outcomes):
        weight = Wei(1)
        for j in range(n_outcomes):
            if i != j:
                weight *= outcome_token_amounts[j].as_wei.value
        weights.append(weight)

    sum_weights = sum(weights, start=Wei(0))

    marginal_prices = [weights[i].value / sum_weights.value for i in range(n_outcomes)]
    return [CollateralToken(mp) for mp in marginal_prices]
format_realitio_question
format_realitio_question(
    question: str,
    outcomes: Sequence[str],
    category: str,
    language: str,
    template_id: int,
) -> str

If you add a new template id here, also add to the parsing function below.

Source code in prediction_market_agent_tooling/markets/omen/data_models.py
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
def format_realitio_question(
    question: str,
    outcomes: t.Sequence[str],
    category: str,
    language: str,
    template_id: int,
) -> str:
    """If you add a new template id here, also add to the parsing function below."""

    # Escape characters for JSON troubles on Reality.eth.
    question = question.replace('"', '\\"')

    if template_id == 2:
        return "␟".join(
            [
                question,
                ",".join(f'"{o}"' for o in outcomes),
                category,
                language,
            ]
        )

    raise ValueError(f"Unsupported template id {template_id}.")
parse_realitio_question
parse_realitio_question(
    question_raw: str, template_id: int
) -> ParsedQuestion

If you add a new template id here, also add to the encoding function above.

Source code in prediction_market_agent_tooling/markets/omen/data_models.py
724
725
726
727
728
729
730
731
732
733
def parse_realitio_question(question_raw: str, template_id: int) -> "ParsedQuestion":
    """If you add a new template id here, also add to the encoding function above."""
    if template_id == 2:
        question, outcomes_raw, category, language = question_raw.split("␟")
        outcomes = [OutcomeStr(o.strip('"')) for o in outcomes_raw.split(",")]
        return ParsedQuestion(
            question=question, outcomes=outcomes, category=category, language=language
        )

    raise ValueError(f"Unsupported template id {template_id}.")

omen

OMEN_DEFAULT_REALITIO_BOND_VALUE module-attribute
OMEN_DEFAULT_REALITIO_BOND_VALUE = xDai(0.01)
OMEN_TINY_BET_AMOUNT module-attribute
OMEN_TINY_BET_AMOUNT = USD(0.01)
OmenAgentMarket

Bases: AgentMarket

Omen's market class that can be used by agents to make predictions.

base_url class-attribute
base_url: str = PRESAGIO_BASE_URL
creator instance-attribute
creator: HexAddress
collateral_token_contract_address_checksummed instance-attribute
collateral_token_contract_address_checksummed: (
    ChecksumAddress
)
market_maker_contract_address_checksummed instance-attribute
market_maker_contract_address_checksummed: ChecksumAddress
condition instance-attribute
condition: Condition
finalized_time instance-attribute
finalized_time: DatetimeUTC | None
created_time instance-attribute
created_time: DatetimeUTC
close_time instance-attribute
close_time: DatetimeUTC
description class-attribute instance-attribute
description: str | None = None
yes_index property
yes_index: int
no_index property
no_index: int
id instance-attribute
id: str
question instance-attribute
question: str
outcomes instance-attribute
outcomes: Sequence[OutcomeStr]
outcome_token_pool instance-attribute
outcome_token_pool: dict[OutcomeStr, OutcomeToken] | None
resolution instance-attribute
resolution: Resolution | None
probabilities instance-attribute
probabilities: dict[OutcomeStr, Probability]
url instance-attribute
url: str
volume instance-attribute
volume: CollateralToken | None
fees instance-attribute
fees: MarketFees
is_binary property
is_binary: bool
p_yes property
p_yes: Probability
p_no property
p_no: Probability
probable_resolution property
probable_resolution: Resolution
get_p_yes_history_cached
get_p_yes_history_cached() -> list[Probability]
Source code in prediction_market_agent_tooling/markets/omen/omen.py
124
125
126
127
def get_p_yes_history_cached(self) -> list[Probability]:
    if self._binary_market_p_yes_history is None:
        self._binary_market_p_yes_history = get_binary_market_p_yes_history(self)
    return self._binary_market_p_yes_history
get_last_trade_p_yes
get_last_trade_p_yes() -> Probability | None

On Omen, probablities converge after the resolution, so we need to get market's predicted probability from the trade history.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
129
130
131
132
133
134
135
def get_last_trade_p_yes(self) -> Probability | None:
    """On Omen, probablities converge after the resolution, so we need to get market's predicted probability from the trade history."""
    return (
        self.get_p_yes_history_cached()[-1]
        if self.get_p_yes_history_cached()
        else None
    )
get_last_trade_p_no
get_last_trade_p_no() -> Probability | None

On Omen, probablities converge after the resolution, so we need to get market's predicted probability from the trade history.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
137
138
139
140
141
142
143
144
def get_last_trade_p_no(self) -> Probability | None:
    """On Omen, probablities converge after the resolution, so we need to get market's predicted probability from the trade history."""
    last_trade_p_yes = self.get_last_trade_p_yes()
    return (
        Probability(1.0 - last_trade_p_yes)
        if last_trade_p_yes is not None
        else None
    )
get_liquidity_in_wei
get_liquidity_in_wei(web3: Web3 | None = None) -> Wei
Source code in prediction_market_agent_tooling/markets/omen/omen.py
146
147
def get_liquidity_in_wei(self, web3: Web3 | None = None) -> Wei:
    return self.get_contract().totalSupply(web3)
get_liquidity
get_liquidity(web3: Web3 | None = None) -> CollateralToken
Source code in prediction_market_agent_tooling/markets/omen/omen.py
149
150
def get_liquidity(self, web3: Web3 | None = None) -> CollateralToken:
    return self.get_liquidity_in_wei(web3).as_token
get_tiny_bet_amount
get_tiny_bet_amount() -> CollateralToken
Source code in prediction_market_agent_tooling/markets/omen/omen.py
152
153
def get_tiny_bet_amount(self) -> CollateralToken:
    return self.get_in_token(OMEN_TINY_BET_AMOUNT)
get_token_in_usd
get_token_in_usd(x: CollateralToken) -> USD
Source code in prediction_market_agent_tooling/markets/omen/omen.py
155
156
def get_token_in_usd(self, x: CollateralToken) -> USD:
    return get_token_in_usd(x, self.collateral_token_contract_address_checksummed)
get_usd_in_token
get_usd_in_token(x: USD) -> CollateralToken
Source code in prediction_market_agent_tooling/markets/omen/omen.py
158
159
def get_usd_in_token(self, x: USD) -> CollateralToken:
    return get_usd_in_token(x, self.collateral_token_contract_address_checksummed)
have_bet_on_market_since
have_bet_on_market_since(
    keys: APIKeys, since: timedelta
) -> bool
Source code in prediction_market_agent_tooling/markets/omen/omen.py
161
162
163
164
165
166
167
def have_bet_on_market_since(self, keys: APIKeys, since: timedelta) -> bool:
    start_time = utcnow() - since
    prev_bets = self.get_bets_made_since(
        better_address=keys.bet_from_address, start_time=start_time
    )

    return self.id in [b.market_id for b in prev_bets]
liquidate_existing_positions
liquidate_existing_positions(
    bet_outcome: OutcomeStr,
    web3: Web3 | None = None,
    api_keys: APIKeys | None = None,
    larger_than: OutcomeToken | None = None,
) -> None

Liquidates all previously existing positions.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
def liquidate_existing_positions(
    self,
    bet_outcome: OutcomeStr,
    web3: Web3 | None = None,
    api_keys: APIKeys | None = None,
    larger_than: OutcomeToken | None = None,
) -> None:
    """
    Liquidates all previously existing positions.

    """
    api_keys = api_keys if api_keys is not None else APIKeys()
    better_address = api_keys.bet_from_address
    larger_than = (
        larger_than if larger_than is not None else self.get_liquidatable_amount()
    )
    prev_positions_for_market = self.get_positions(
        user_id=better_address, liquid_only=True, larger_than=larger_than
    )

    for prev_position in prev_positions_for_market:
        for position_outcome, token_amount in prev_position.amounts_ot.items():
            if position_outcome != bet_outcome:
                self.sell_tokens(
                    outcome=position_outcome,
                    amount=token_amount,
                    auto_withdraw=True,
                    web3=web3,
                    api_keys=api_keys,
                )
place_bet
place_bet(
    outcome: OutcomeStr,
    amount: USD,
    auto_deposit: bool = True,
    web3: Web3 | None = None,
    api_keys: APIKeys | None = None,
) -> str
Source code in prediction_market_agent_tooling/markets/omen/omen.py
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
def place_bet(
    self,
    outcome: OutcomeStr,
    amount: USD,
    auto_deposit: bool = True,
    web3: Web3 | None = None,
    api_keys: APIKeys | None = None,
) -> str:
    if not self.can_be_traded():
        raise ValueError(
            f"Market {self.id} is not open for trading. Cannot place bet."
        )
    return binary_omen_buy_outcome_tx(
        api_keys=api_keys if api_keys is not None else APIKeys(),
        amount=amount,
        market=self,
        outcome=outcome,
        auto_deposit=auto_deposit,
        web3=web3,
    )
buy_tokens
buy_tokens(
    outcome: OutcomeStr,
    amount: USD,
    web3: Web3 | None = None,
    api_keys: APIKeys | None = None,
) -> str
Source code in prediction_market_agent_tooling/markets/omen/omen.py
221
222
223
224
225
226
227
228
229
230
231
232
233
def buy_tokens(
    self,
    outcome: OutcomeStr,
    amount: USD,
    web3: Web3 | None = None,
    api_keys: APIKeys | None = None,
) -> str:
    return self.place_bet(
        outcome=outcome,
        amount=amount,
        web3=web3,
        api_keys=api_keys,
    )
get_sell_value_of_outcome_token
get_sell_value_of_outcome_token(
    outcome: OutcomeStr,
    amount: OutcomeToken,
    web3: Web3 | None = None,
) -> CollateralToken

Market can have as collateral token GNO for example. When you place bet, you buy shares with GNO. For example, you get 10 shares for 1 GNO. When selling, you need to provide the amount in GNO, which is cumbersome because you know how much shares you have, but you don't have the price of the shares in GNO. Use this to convert how much collateral token (GNO in our example) to sell, to get the amount of shares you want to sell.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
def get_sell_value_of_outcome_token(
    self, outcome: OutcomeStr, amount: OutcomeToken, web3: Web3 | None = None
) -> CollateralToken:
    """
    Market can have as collateral token GNO for example.
    When you place bet, you buy shares with GNO. For example, you get 10 shares for 1 GNO.
    When selling, you need to provide the amount in GNO, which is cumbersome because you know how much shares you have, but you don't have the price of the shares in GNO.
    Use this to convert how much collateral token (GNO in our example) to sell, to get the amount of shares you want to sell.
    """

    pool_balance = get_conditional_tokens_balance_for_market(
        self, self.market_maker_contract_address_checksummed, web3=web3
    )
    outcome_idx = self.index_set_to_outcome_index(self.get_index_set(outcome))
    collateral = calculate_sell_amount_in_collateral(
        shares_to_sell=amount,
        outcome_index=outcome_idx,
        pool_balances=[x.as_outcome_token for x in pool_balance.values()],
        fees=self.fees,
    )

    return collateral
sell_tokens
sell_tokens(
    outcome: OutcomeStr,
    amount: USD | OutcomeToken,
    auto_withdraw: bool = True,
    api_keys: APIKeys | None = None,
    web3: Web3 | None = None,
) -> str
Source code in prediction_market_agent_tooling/markets/omen/omen.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
def sell_tokens(
    self,
    outcome: OutcomeStr,
    amount: USD | OutcomeToken,
    auto_withdraw: bool = True,
    api_keys: APIKeys | None = None,
    web3: Web3 | None = None,
) -> str:
    if not self.can_be_traded():
        raise ValueError(
            f"Market {self.id} is not open for trading. Cannot sell tokens."
        )
    return binary_omen_sell_outcome_tx(
        amount=amount,
        api_keys=api_keys if api_keys is not None else APIKeys(),
        market=self,
        outcome=outcome,
        auto_withdraw=auto_withdraw,
        web3=web3,
    )
was_any_bet_outcome_correct
was_any_bet_outcome_correct(
    resolved_omen_bets: List[OmenBet],
) -> bool | None
Source code in prediction_market_agent_tooling/markets/omen/omen.py
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
def was_any_bet_outcome_correct(
    self, resolved_omen_bets: t.List[OmenBet]
) -> bool | None:
    resolved_bets_for_market = [
        bet
        for bet in resolved_omen_bets
        if bet.fpmm.id == self.id and bet.fpmm.is_resolved
    ]

    # If there were no bets for this market, we conservatively say that
    # this method was called incorrectly, hence we raise an Error.
    if not resolved_bets_for_market:
        raise ValueError(f"No resolved bets provided for market {self.id}")

    if not resolved_bets_for_market[0].fpmm.has_valid_answer:
        # We return None if the market was resolved as invalid, as we were neither right or wrong.
        return None

    # We iterate through bets since agent could have placed bets on multiple outcomes.
    # If one of the bets was correct, we return true since there is a redeemable amount to be retrieved.
    for bet in resolved_bets_for_market:
        # Like Olas, we assert correctness by matching index
        if bet.outcomeIndex == check_not_none(
            bet.fpmm.question.outcome_index,
            "Shouldn't be None if the market is resolved",
        ):
            return True

    return False
market_redeemable_by
market_redeemable_by(user: ChecksumAddress) -> bool

Will return true if given user placed a bet on this market and that bet has a balance. If the user never placed a bet on this market, this correctly return False.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
309
310
311
312
313
314
315
316
317
318
319
320
321
def market_redeemable_by(self, user: ChecksumAddress) -> bool:
    """
    Will return true if given user placed a bet on this market and that bet has a balance.
    If the user never placed a bet on this market, this correctly return False.
    """
    positions = OmenSubgraphHandler().get_positions(condition_id=self.condition.id)
    user_positions = OmenSubgraphHandler().get_user_positions(
        better_address=user,
        position_id_in=[p.id for p in positions],
        # After redeem, this will became zero.
        total_balance_bigger_than=OutcomeWei(0),
    )
    return len(user_positions) > 0
redeem_positions
redeem_positions(api_keys: APIKeys) -> None
Source code in prediction_market_agent_tooling/markets/omen/omen.py
323
324
325
326
327
328
329
330
331
332
333
334
335
def redeem_positions(
    self,
    api_keys: APIKeys,
) -> None:
    for_public_key = api_keys.bet_from_address
    market_is_redeemable = self.market_redeemable_by(user=for_public_key)
    if not market_is_redeemable:
        logger.debug(
            f"Position on market {self.id} was already redeemed or no bets were placed at all by {for_public_key=}."
        )
        return None

    omen_redeem_full_position_tx(api_keys=api_keys, market=self)
from_created_market staticmethod
from_created_market(
    model: CreatedMarket,
) -> OmenAgentMarket
Source code in prediction_market_agent_tooling/markets/omen/omen.py
337
338
339
@staticmethod
def from_created_market(model: "CreatedMarket") -> "OmenAgentMarket":
    return OmenAgentMarket.from_data_model(OmenMarket.from_created_market(model))
from_data_model staticmethod
from_data_model(model: OmenMarket) -> OmenAgentMarket
Source code in prediction_market_agent_tooling/markets/omen/omen.py
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
@staticmethod
def from_data_model(model: OmenMarket) -> "OmenAgentMarket":
    return OmenAgentMarket(
        id=model.id,
        question=model.title,
        creator=model.creator,
        outcomes=model.outcomes,
        collateral_token_contract_address_checksummed=model.collateral_token_contract_address_checksummed,
        market_maker_contract_address_checksummed=model.market_maker_contract_address_checksummed,
        resolution=model.get_resolution_enum(),
        created_time=model.creation_datetime,
        finalized_time=model.finalized_datetime,
        condition=model.condition,
        url=model.url,
        volume=model.collateralVolume.as_token,
        close_time=model.close_time,
        fees=MarketFees(
            bet_proportion=(
                model.fee.as_token.value if model.fee is not None else 0.0
            ),
            absolute=0,
        ),
        outcome_token_pool={
            model.outcomes[i]: model.outcomeTokenAmounts[i].as_outcome_token
            for i in range(len(model.outcomes))
        },
        probabilities=AgentMarket.build_probability_map(
            outcome_token_amounts=model.outcomeTokenAmounts,
            outcomes=list(model.outcomes),
        ),
    )
get_markets staticmethod
get_markets(
    limit: int,
    sort_by: SortBy,
    filter_by: FilterBy = FilterBy.OPEN,
    created_after: Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
    fetch_categorical_markets: bool = False,
) -> t.Sequence[OmenAgentMarket]
Source code in prediction_market_agent_tooling/markets/omen/omen.py
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
@staticmethod
def get_markets(
    limit: int,
    sort_by: SortBy,
    filter_by: FilterBy = FilterBy.OPEN,
    created_after: t.Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
    fetch_categorical_markets: bool = False,
) -> t.Sequence["OmenAgentMarket"]:
    return [
        OmenAgentMarket.from_data_model(m)
        for m in OmenSubgraphHandler().get_omen_markets_simple(
            limit=limit,
            sort_by=sort_by,
            filter_by=filter_by,
            created_after=created_after,
            excluded_questions=excluded_questions,
            include_categorical_markets=fetch_categorical_markets,
        )
    ]
get_binary_market staticmethod
get_binary_market(id: str) -> OmenAgentMarket
Source code in prediction_market_agent_tooling/markets/omen/omen.py
394
395
396
397
398
399
400
@staticmethod
def get_binary_market(id: str) -> "OmenAgentMarket":
    return OmenAgentMarket.from_data_model(
        OmenSubgraphHandler().get_omen_market_by_market_id(
            market_id=HexAddress(HexStr(id))
        )
    )
redeem_winnings staticmethod
redeem_winnings(api_keys: APIKeys) -> None
Source code in prediction_market_agent_tooling/markets/omen/omen.py
402
403
404
@staticmethod
def redeem_winnings(api_keys: APIKeys) -> None:
    redeem_from_all_user_positions(api_keys)
get_trade_balance staticmethod
get_trade_balance(
    api_keys: APIKeys, web3: Web3 | None = None
) -> USD
Source code in prediction_market_agent_tooling/markets/omen/omen.py
406
407
408
409
410
411
412
413
414
415
416
417
@staticmethod
def get_trade_balance(api_keys: APIKeys, web3: Web3 | None = None) -> USD:
    native_usd = get_xdai_in_usd(
        get_balances(api_keys.bet_from_address, web3=web3).xdai
    )
    keeping_usd = get_token_in_usd(
        KEEPING_ERC20_TOKEN.balance_of_in_tokens(
            api_keys.bet_from_address, web3=web3
        ),
        KEEPING_ERC20_TOKEN.address,
    )
    return keeping_usd + native_usd
verify_operational_balance staticmethod
verify_operational_balance(api_keys: APIKeys) -> bool
Source code in prediction_market_agent_tooling/markets/omen/omen.py
419
420
421
422
423
424
@staticmethod
def verify_operational_balance(api_keys: APIKeys) -> bool:
    return get_balances(
        # Use `public_key`, not `bet_from_address` because transaction costs are paid from the EOA wallet.
        api_keys.public_key,
    ).xdai > xDai(0.001)
store_prediction
store_prediction(
    processed_market: ProcessedMarket | None,
    keys: APIKeys,
    agent_name: str,
) -> None

On Omen, we have to store predictions along with trades, see store_trades.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
426
427
428
429
def store_prediction(
    self, processed_market: ProcessedMarket | None, keys: APIKeys, agent_name: str
) -> None:
    """On Omen, we have to store predictions along with trades, see `store_trades`."""
store_trades
store_trades(
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None
Source code in prediction_market_agent_tooling/markets/omen/omen.py
431
432
433
434
435
436
437
438
439
440
441
442
443
def store_trades(
    self,
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None:
    return store_trades(
        market_id=self.id,
        traded_market=traded_market,
        keys=keys,
        agent_name=agent_name,
    )
get_bets_made_since staticmethod
get_bets_made_since(
    better_address: ChecksumAddress, start_time: DatetimeUTC
) -> list[Bet]
Source code in prediction_market_agent_tooling/markets/omen/omen.py
445
446
447
448
449
450
451
452
453
@staticmethod
def get_bets_made_since(
    better_address: ChecksumAddress, start_time: DatetimeUTC
) -> list[Bet]:
    bets = OmenSubgraphHandler().get_bets(
        better_address=better_address, start_time=start_time
    )
    bets.sort(key=lambda x: x.creation_datetime)
    return [b.to_bet() for b in bets]
get_resolved_bets_made_since staticmethod
get_resolved_bets_made_since(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC,
    end_time: DatetimeUTC | None,
    market_resolved_before: DatetimeUTC | None = None,
    market_resolved_after: DatetimeUTC | None = None,
) -> list[ResolvedBet]
Source code in prediction_market_agent_tooling/markets/omen/omen.py
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
@staticmethod
def get_resolved_bets_made_since(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC,
    end_time: DatetimeUTC | None,
    market_resolved_before: DatetimeUTC | None = None,
    market_resolved_after: DatetimeUTC | None = None,
) -> list[ResolvedBet]:
    subgraph_handler = OmenSubgraphHandler()
    bets = subgraph_handler.get_resolved_bets_with_valid_answer(
        better_address=better_address,
        start_time=start_time,
        end_time=end_time,
        market_id=None,
        market_resolved_before=market_resolved_before,
        market_resolved_after=market_resolved_after,
    )
    generic_bets = [b.to_generic_resolved_bet() for b in bets]
    return generic_bets
get_contract
get_contract() -> OmenFixedProductMarketMakerContract
Source code in prediction_market_agent_tooling/markets/omen/omen.py
475
476
477
478
479
480
def get_contract(
    self,
) -> OmenFixedProductMarketMakerContract:
    return OmenFixedProductMarketMakerContract(
        address=self.market_maker_contract_address_checksummed,
    )
get_index_set
get_index_set(outcome: OutcomeStr) -> int
Source code in prediction_market_agent_tooling/markets/omen/omen.py
482
483
def get_index_set(self, outcome: OutcomeStr) -> int:
    return self.get_outcome_index(outcome) + 1
index_set_to_outcome_index
index_set_to_outcome_index(index_set: int) -> int
Source code in prediction_market_agent_tooling/markets/omen/omen.py
485
486
def index_set_to_outcome_index(cls, index_set: int) -> int:
    return index_set - 1
index_set_to_outcome_str
index_set_to_outcome_str(index_set: int) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/omen/omen.py
488
489
490
491
def index_set_to_outcome_str(cls, index_set: int) -> OutcomeStr:
    return OutcomeStr(
        cls.get_outcome_str(cls.index_set_to_outcome_index(index_set))
    )
get_outcome_str_from_bool staticmethod
get_outcome_str_from_bool(outcome: bool) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/omen/omen.py
493
494
495
496
497
@staticmethod
def get_outcome_str_from_bool(outcome: bool) -> OutcomeStr:
    return (
        OutcomeStr(OMEN_TRUE_OUTCOME) if outcome else OutcomeStr(OMEN_FALSE_OUTCOME)
    )
get_token_balance
get_token_balance(
    user_id: str,
    outcome: OutcomeStr,
    web3: Web3 | None = None,
) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/omen/omen.py
499
500
501
502
503
504
505
506
def get_token_balance(
    self, user_id: str, outcome: OutcomeStr, web3: Web3 | None = None
) -> OutcomeToken:
    index_set = self.get_index_set(outcome)
    balances = get_conditional_tokens_balance_for_market(
        self, Web3.to_checksum_address(user_id), web3=web3
    )
    return balances[index_set].as_outcome_token
get_position
get_position(user_id: str) -> ExistingPosition | None
Source code in prediction_market_agent_tooling/markets/omen/omen.py
508
509
510
511
512
513
514
515
516
517
518
def get_position(self, user_id: str) -> ExistingPosition | None:
    liquidatable_amount = self.get_liquidatable_amount()
    existing_positions = self.get_positions(
        user_id=user_id,
        liquid_only=True,
        larger_than=liquidatable_amount,
    )
    existing_position = next(
        iter([i for i in existing_positions if i.market_id == self.id]), None
    )
    return existing_position
get_positions classmethod
get_positions(
    user_id: str,
    liquid_only: bool = False,
    larger_than: OutcomeToken = OutcomeToken(0),
) -> t.Sequence[ExistingPosition]
Source code in prediction_market_agent_tooling/markets/omen/omen.py
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
@classmethod
def get_positions(
    cls,
    user_id: str,
    liquid_only: bool = False,
    larger_than: OutcomeToken = OutcomeToken(0),
) -> t.Sequence[ExistingPosition]:
    sgh = OmenSubgraphHandler()
    omen_positions = sgh.get_user_positions(
        better_address=Web3.to_checksum_address(user_id),
        total_balance_bigger_than=larger_than.as_outcome_wei,
    )

    # Sort positions and corresponding markets by condition_id
    omen_positions_dict: dict[HexBytes, list[OmenUserPosition]] = defaultdict(list)

    for omen_position in omen_positions:
        omen_positions_dict[omen_position.position.condition_id].append(
            omen_position
        )

    # We include categorical markets below simply because we are already filtering on condition_ids.
    omen_markets: dict[HexBytes, OmenMarket] = {
        m.condition.id: m
        for m in sgh.get_omen_markets(
            limit=None,
            condition_id_in=list(omen_positions_dict.keys()),
            include_categorical_markets=True,
        )
    }

    if len(omen_markets) != len(omen_positions_dict):
        missing_conditions_ids = set(
            omen_position.position.condition_id for omen_position in omen_positions
        ) - set(market.condition.id for market in omen_markets.values())
        raise ValueError(
            f"Number of condition ids for markets {len(omen_markets)} and positions {len(omen_positions_dict)} are not equal. "
            f"Missing condition ids: {missing_conditions_ids}"
        )

    positions = []
    for condition_id, omen_positions in tqdm(
        omen_positions_dict.items(), mininterval=3
    ):
        market = cls.from_data_model(omen_markets[condition_id])

        # Skip markets that cannot be traded if `liquid_only`` is True.
        if liquid_only and not market.can_be_traded():
            continue

        amounts_ot: dict[OutcomeStr, OutcomeToken] = {}

        for omen_position in omen_positions:
            outecome_str = market.index_set_to_outcome_str(
                omen_position.position.index_set
            )

            # Validate that outcomes are unique for a given condition_id.
            if outecome_str in amounts_ot:
                raise ValueError(
                    f"Outcome {outecome_str} already exists in {amounts_ot=}"
                )

            amounts_ot[outecome_str] = omen_position.totalBalance.as_outcome_token

        amounts_current = {
            k: market.get_token_in_usd(
                # If the market is not open for trading anymore, then current value is equal to potential value.
                market.get_sell_value_of_outcome_token(k, v)
                if market.can_be_traded()
                else v.as_token
            )
            for k, v in amounts_ot.items()
        }
        amounts_potential = {
            k: market.get_token_in_usd(v.as_token) for k, v in amounts_ot.items()
        }
        positions.append(
            ExistingPosition(
                market_id=market.id,
                amounts_current=amounts_current,
                amounts_potential=amounts_potential,
                amounts_ot=amounts_ot,
            )
        )

    return positions
get_user_url classmethod
get_user_url(keys: APIKeys) -> str
Source code in prediction_market_agent_tooling/markets/omen/omen.py
608
609
610
@classmethod
def get_user_url(cls, keys: APIKeys) -> str:
    return get_omen_user_url(keys.bet_from_address)
get_buy_token_amount
get_buy_token_amount(
    bet_amount: USD | CollateralToken, outcome: OutcomeStr
) -> OutcomeToken

Note: this is only valid if the market instance's token pool is up-to-date with the smart contract.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
def get_buy_token_amount(
    self, bet_amount: USD | CollateralToken, outcome: OutcomeStr
) -> OutcomeToken:
    """
    Note: this is only valid if the market instance's token pool is
    up-to-date with the smart contract.
    """
    outcome_token_pool = check_not_none(self.outcome_token_pool)
    amount = get_buy_outcome_token_amount(
        investment_amount=self.get_in_token(bet_amount),
        outcome_index=self.get_outcome_index(outcome),
        pool_balances=[outcome_token_pool[x] for x in self.outcomes],
        fees=self.fees,
    )
    return amount
get_user_balance staticmethod
get_user_balance(user_id: str) -> float
Source code in prediction_market_agent_tooling/markets/omen/omen.py
642
643
644
@staticmethod
def get_user_balance(user_id: str) -> float:
    return float(get_balances(Web3.to_checksum_address(user_id)).total)
get_user_id staticmethod
get_user_id(api_keys: APIKeys) -> str
Source code in prediction_market_agent_tooling/markets/omen/omen.py
646
647
648
@staticmethod
def get_user_id(api_keys: APIKeys) -> str:
    return api_keys.bet_from_address
get_most_recent_trade_datetime
get_most_recent_trade_datetime(
    user_id: str,
) -> DatetimeUTC | None
Source code in prediction_market_agent_tooling/markets/omen/omen.py
650
651
652
653
654
655
656
657
658
659
660
661
662
def get_most_recent_trade_datetime(self, user_id: str) -> DatetimeUTC | None:
    sgh = OmenSubgraphHandler()
    trades = sgh.get_trades(
        sort_by_field=sgh.trades_subgraph.FpmmTrade.creationTimestamp,
        sort_direction="desc",
        limit=1,
        better_address=Web3.to_checksum_address(user_id),
        market_id=Web3.to_checksum_address(self.id),
    )
    if not trades:
        return None

    return trades[0].creation_datetime
validate_probabilities
validate_probabilities(
    probs: dict[OutcomeStr, Probability],
    info: FieldValidationInfo,
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
@field_validator("probabilities")
def validate_probabilities(
    cls,
    probs: dict[OutcomeStr, Probability],
    info: FieldValidationInfo,
) -> dict[OutcomeStr, Probability]:
    outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
    if set(probs.keys()) != set(outcomes):
        raise ValueError("Keys of `probabilities` must match `outcomes` exactly.")
    total = float(sum(probs.values()))
    if not 0.999 <= total <= 1.001:
        # We simply log a warning because for some use-cases (e.g. existing positions), the
        # markets might be already closed hence no reliable outcome token prices exist anymore.
        logger.warning(f"Probabilities for market {info.data=} do not sum to 1.")
    return probs
validate_outcome_token_pool
validate_outcome_token_pool(
    outcome_token_pool: dict[str, OutcomeToken] | None,
    info: FieldValidationInfo,
) -> dict[str, OutcomeToken] | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
@field_validator("outcome_token_pool")
def validate_outcome_token_pool(
    cls,
    outcome_token_pool: dict[str, OutcomeToken] | None,
    info: FieldValidationInfo,
) -> dict[str, OutcomeToken] | None:
    outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
    if outcome_token_pool is not None:
        outcome_keys = set(outcome_token_pool.keys())
        expected_keys = set(outcomes)
        if outcome_keys != expected_keys:
            raise ValueError(
                f"Keys of outcome_token_pool ({outcome_keys}) do not match outcomes ({expected_keys})."
            )
    return outcome_token_pool
get_outcome_token_pool_by_outcome
get_outcome_token_pool_by_outcome(
    outcome: OutcomeStr,
) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
122
123
124
125
126
127
128
def get_outcome_token_pool_by_outcome(self, outcome: OutcomeStr) -> OutcomeToken:
    if self.outcome_token_pool is None or not self.outcome_token_pool:
        return OutcomeToken(0)

    # We look up by index to avoid having to deal with case sensitivity issues.
    outcome_idx = self.get_outcome_index(outcome)
    return list(self.outcome_token_pool.values())[outcome_idx]
handle_legacy_fee
handle_legacy_fee(data: dict[str, Any]) -> dict[str, t.Any]
Source code in prediction_market_agent_tooling/markets/agent_market.py
130
131
132
133
134
135
136
@model_validator(mode="before")
def handle_legacy_fee(cls, data: dict[str, t.Any]) -> dict[str, t.Any]:
    # Backward compatibility for older `AgentMarket` without `fees`.
    if "fees" not in data and "fee" in data:
        data["fees"] = MarketFees(absolute=0.0, bet_proportion=data["fee"])
        del data["fee"]
    return data
market_outcome_for_probability_key
market_outcome_for_probability_key(
    probability_key: OutcomeStr,
) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
138
139
140
141
142
143
144
145
146
def market_outcome_for_probability_key(
    self, probability_key: OutcomeStr
) -> OutcomeStr:
    for market_outcome in self.outcomes:
        if market_outcome.lower() == probability_key.lower():
            return market_outcome
    raise ValueError(
        f"Could not find probability for probability key {probability_key}"
    )
probability_for_market_outcome
probability_for_market_outcome(
    market_outcome: OutcomeStr,
) -> Probability
Source code in prediction_market_agent_tooling/markets/agent_market.py
148
149
150
151
152
153
154
def probability_for_market_outcome(self, market_outcome: OutcomeStr) -> Probability:
    for k, v in self.probabilities.items():
        if k.lower() == market_outcome.lower():
            return v
    raise ValueError(
        f"Could not find probability for market outcome {market_outcome}"
    )
get_last_trade_yes_outcome_price
get_last_trade_yes_outcome_price() -> (
    CollateralToken | None
)
Source code in prediction_market_agent_tooling/markets/agent_market.py
209
210
211
212
213
214
def get_last_trade_yes_outcome_price(self) -> CollateralToken | None:
    # Price on prediction markets are, by definition, equal to the probability of an outcome.
    # Just making it explicit in this function.
    if last_trade_p_yes := self.get_last_trade_p_yes():
        return CollateralToken(last_trade_p_yes)
    return None
get_last_trade_yes_outcome_price_usd
get_last_trade_yes_outcome_price_usd() -> USD | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
216
217
218
219
def get_last_trade_yes_outcome_price_usd(self) -> USD | None:
    if last_trade_yes_outcome_price := self.get_last_trade_yes_outcome_price():
        return self.get_token_in_usd(last_trade_yes_outcome_price)
    return None
get_last_trade_no_outcome_price
get_last_trade_no_outcome_price() -> CollateralToken | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
221
222
223
224
225
226
def get_last_trade_no_outcome_price(self) -> CollateralToken | None:
    # Price on prediction markets are, by definition, equal to the probability of an outcome.
    # Just making it explicit in this function.
    if last_trade_p_no := self.get_last_trade_p_no():
        return CollateralToken(last_trade_p_no)
    return None
get_last_trade_no_outcome_price_usd
get_last_trade_no_outcome_price_usd() -> USD | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
228
229
230
231
def get_last_trade_no_outcome_price_usd(self) -> USD | None:
    if last_trade_no_outcome_price := self.get_last_trade_no_outcome_price():
        return self.get_token_in_usd(last_trade_no_outcome_price)
    return None
get_liquidatable_amount
get_liquidatable_amount() -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
233
234
235
def get_liquidatable_amount(self) -> OutcomeToken:
    tiny_amount = self.get_tiny_bet_amount()
    return OutcomeToken.from_token(tiny_amount / 10)
get_in_usd
get_in_usd(x: USD | CollateralToken) -> USD
Source code in prediction_market_agent_tooling/markets/agent_market.py
258
259
260
261
def get_in_usd(self, x: USD | CollateralToken) -> USD:
    if isinstance(x, USD):
        return x
    return self.get_token_in_usd(x)
get_in_token
get_in_token(x: USD | CollateralToken) -> CollateralToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
263
264
265
266
def get_in_token(self, x: USD | CollateralToken) -> CollateralToken:
    if isinstance(x, CollateralToken):
        return x
    return self.get_usd_in_token(x)
compute_fpmm_probabilities staticmethod
compute_fpmm_probabilities(
    balances: list[OutcomeWei],
) -> list[Probability]

Compute the implied probabilities in a Fixed Product Market Maker.

Parameters:

Name Type Description Default
balances List[float]

Balances of outcome tokens.

required

Returns:

Type Description
list[Probability]

List[float]: Implied probabilities for each outcome.

Source code in prediction_market_agent_tooling/markets/agent_market.py
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
@staticmethod
def compute_fpmm_probabilities(balances: list[OutcomeWei]) -> list[Probability]:
    """
    Compute the implied probabilities in a Fixed Product Market Maker.

    Args:
        balances (List[float]): Balances of outcome tokens.

    Returns:
        List[float]: Implied probabilities for each outcome.
    """
    if all(x.value == 0 for x in balances):
        return [Probability(0.0)] * len(balances)

    # converting to standard values for prod compatibility.
    values_balance = [i.value for i in balances]
    # Compute product of balances excluding each outcome
    excluded_products = []
    for i in range(len(values_balance)):
        other_balances = values_balance[:i] + values_balance[i + 1 :]
        excluded_products.append(prod(other_balances))

    # Normalize to sum to 1
    total = sum(excluded_products)
    if total == 0:
        return [Probability(0.0)] * len(balances)
    probabilities = [Probability(p / total) for p in excluded_products]

    return probabilities
build_probability_map_from_p_yes staticmethod
build_probability_map_from_p_yes(
    p_yes: Probability,
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
321
322
323
324
325
326
327
328
@staticmethod
def build_probability_map_from_p_yes(
    p_yes: Probability,
) -> dict[OutcomeStr, Probability]:
    return {
        OutcomeStr(YES_OUTCOME_LOWERCASE_IDENTIFIER): p_yes,
        OutcomeStr(NO_OUTCOME_LOWERCASE_IDENTIFIER): Probability(1.0 - p_yes),
    }
build_probability_map staticmethod
build_probability_map(
    outcome_token_amounts: list[OutcomeWei],
    outcomes: list[OutcomeStr],
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
330
331
332
333
334
335
@staticmethod
def build_probability_map(
    outcome_token_amounts: list[OutcomeWei], outcomes: list[OutcomeStr]
) -> dict[OutcomeStr, Probability]:
    probs = AgentMarket.compute_fpmm_probabilities(outcome_token_amounts)
    return {outcome: prob for outcome, prob in zip(outcomes, probs)}
is_closed
is_closed() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
411
412
def is_closed(self) -> bool:
    return self.close_time is not None and self.close_time <= utcnow()
is_resolved
is_resolved() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
414
415
def is_resolved(self) -> bool:
    return self.resolution is not None
has_liquidity
has_liquidity() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
420
421
def has_liquidity(self) -> bool:
    return self.get_liquidity() > 0
has_successful_resolution
has_successful_resolution() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
423
424
425
426
427
428
def has_successful_resolution(self) -> bool:
    return (
        self.resolution is not None
        and self.resolution.outcome is not None
        and not self.resolution.invalid
    )
has_unsuccessful_resolution
has_unsuccessful_resolution() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
430
431
def has_unsuccessful_resolution(self) -> bool:
    return self.resolution is not None and self.resolution.invalid
get_outcome_str
get_outcome_str(outcome_index: int) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
437
438
439
440
441
442
443
def get_outcome_str(self, outcome_index: int) -> OutcomeStr:
    try:
        return self.outcomes[outcome_index]
    except IndexError:
        raise IndexError(
            f"Outcome index `{outcome_index}` out of range for `{self.outcomes}`: `{self.outcomes}`."
        )
get_outcome_index
get_outcome_index(outcome: OutcomeStr) -> int
Source code in prediction_market_agent_tooling/markets/agent_market.py
445
446
447
448
449
450
def get_outcome_index(self, outcome: OutcomeStr) -> int:
    outcomes_lowercase = [o.lower() for o in self.outcomes]
    try:
        return outcomes_lowercase.index(outcome.lower())
    except ValueError:
        raise ValueError(f"Outcome `{outcome}` not found in `{self.outcomes}`.")
can_be_traded
can_be_traded() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
475
476
477
478
def can_be_traded(self) -> bool:
    if self.is_closed() or not self.has_liquidity():
        return False
    return True
has_token_pool
has_token_pool() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
484
485
def has_token_pool(self) -> bool:
    return self.outcome_token_pool is not None
get_pool_tokens
get_pool_tokens(outcome: OutcomeStr) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
487
488
489
490
491
def get_pool_tokens(self, outcome: OutcomeStr) -> OutcomeToken:
    if not self.outcome_token_pool:
        raise ValueError("Outcome token pool is not available.")

    return self.outcome_token_pool[outcome]
get_omen_user_url
get_omen_user_url(address: ChecksumAddress) -> str
Source code in prediction_market_agent_tooling/markets/omen/omen.py
665
666
def get_omen_user_url(address: ChecksumAddress) -> str:
    return f"https://gnosisscan.io/address/{address}"
omen_buy_outcome_tx
omen_buy_outcome_tx(
    api_keys: APIKeys,
    amount: USD | CollateralToken,
    market: OmenAgentMarket,
    outcome: OutcomeStr,
    auto_deposit: bool,
    web3: Web3 | None = None,
    slippage: float = 0.01,
) -> str

Bets the given amount for the given outcome in the given market.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
@tenacity.retry(
    stop=tenacity.stop_after_attempt(3),
    wait=tenacity.wait_fixed(1),
    after=lambda x: logger.debug(f"omen_buy_outcome_tx failed, {x.attempt_number=}."),
)
def omen_buy_outcome_tx(
    api_keys: APIKeys,
    amount: USD | CollateralToken,
    market: OmenAgentMarket,
    outcome: OutcomeStr,
    auto_deposit: bool,
    web3: Web3 | None = None,
    slippage: float = 0.01,
) -> str:
    """
    Bets the given amount for the given outcome in the given market.
    """
    market_contract: OmenFixedProductMarketMakerContract = market.get_contract()
    collateral_token_contract = market_contract.get_collateral_token_contract(web3)

    amount_token = market.get_in_token(amount)
    amount_wei = amount_token.as_wei

    logger.info(
        f"Buying asked {amount.value=} {amount.symbol}, converted to {amount_token.value=} {amount_token.symbol} for {outcome=} in market {market.url=}."
    )

    # Get the index of the outcome we want to buy.
    outcome_index: int = market.get_outcome_index(outcome)

    # Calculate the amount of shares we will get for the given investment amount.
    expected_shares = market_contract.calcBuyAmount(
        amount_wei, outcome_index, web3=web3
    )
    # Allow small slippage.
    expected_shares = expected_shares.without_fraction(slippage)
    # Approve the market maker to withdraw our collateral token.
    collateral_token_contract.approve(
        api_keys=api_keys,
        for_address=market_contract.address,
        amount_wei=amount_wei,
        web3=web3,
    )

    if auto_deposit:
        auto_deposit_collateral_token(
            collateral_token_contract, amount_wei, api_keys, web3
        )

    # Buy shares using the deposited xDai in the collateral token.
    tx_receipt = market_contract.buy(
        api_keys=api_keys,
        amount_wei=amount_wei,
        outcome_index=outcome_index,
        min_outcome_tokens_to_buy=expected_shares,
        web3=web3,
    )

    return tx_receipt["transactionHash"].hex()
binary_omen_buy_outcome_tx
binary_omen_buy_outcome_tx(
    api_keys: APIKeys,
    amount: USD | CollateralToken,
    market: OmenAgentMarket,
    outcome: OutcomeStr,
    auto_deposit: bool,
    web3: Web3 | None = None,
) -> str
Source code in prediction_market_agent_tooling/markets/omen/omen.py
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
def binary_omen_buy_outcome_tx(
    api_keys: APIKeys,
    amount: USD | CollateralToken,
    market: OmenAgentMarket,
    outcome: OutcomeStr,
    auto_deposit: bool,
    web3: Web3 | None = None,
) -> str:
    return omen_buy_outcome_tx(
        api_keys=api_keys,
        amount=amount,
        market=market,
        outcome=outcome,
        auto_deposit=auto_deposit,
        web3=web3,
    )
omen_sell_outcome_tx
omen_sell_outcome_tx(
    api_keys: APIKeys,
    amount: OutcomeToken | CollateralToken | USD,
    market: OmenAgentMarket,
    outcome: OutcomeStr,
    auto_withdraw: bool,
    web3: Web3 | None = None,
    slippage: float = 0.01,
) -> str

Sells the given xDai value of shares corresponding to the given outcome in the given market.

The number of shares sold will depend on the share price at the time of the transaction.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
def omen_sell_outcome_tx(
    api_keys: APIKeys,
    amount: OutcomeToken | CollateralToken | USD,
    market: OmenAgentMarket,
    outcome: OutcomeStr,
    auto_withdraw: bool,
    web3: Web3 | None = None,
    slippage: float = 0.01,
) -> str:
    """
    Sells the given xDai value of shares corresponding to the given outcome in
    the given market.

    The number of shares sold will depend on the share price at the time of the
    transaction.
    """
    market_contract: OmenFixedProductMarketMakerContract = market.get_contract()
    conditional_token_contract = OmenConditionalTokenContract()
    collateral_token_contract = market_contract.get_collateral_token_contract(web3)

    amount_token = (
        market.get_sell_value_of_outcome_token(outcome, amount, web3)
        if isinstance(amount, OutcomeToken)
        else market.get_in_token(amount)
    )
    amount_wei = amount_token.as_wei

    logger.info(
        f"Selling asked {amount.value=} {amount.symbol}, converted to {amount_wei.as_token.value=} {amount_wei.as_token.symbol} for {outcome=} in market {market.url=}."
    )

    # Verify, that markets uses conditional tokens that we expect.
    if (
        market_contract.conditionalTokens(web3=web3)
        != conditional_token_contract.address
    ):
        raise ValueError(
            f"Market {market.id} uses conditional token that we didn't expect, {market_contract.conditionalTokens()} != {conditional_token_contract.address=}"
        )

    # Get the index of the outcome we want to sell.
    outcome_index: int = market.get_outcome_index(outcome)

    # Calculate the amount of shares we will sell for the given selling amount of collateral.
    max_outcome_tokens_to_sell = market_contract.calcSellAmount(
        amount_wei, outcome_index, web3=web3
    )
    # Allow small slippage.
    max_outcome_tokens_to_sell = max_outcome_tokens_to_sell.with_fraction(slippage)

    # Approve the market maker to move our (all) conditional tokens.
    conditional_token_contract.setApprovalForAll(
        api_keys=api_keys,
        for_address=market_contract.address,
        approve=True,
        web3=web3,
    )
    # Sell the shares.
    tx_receipt = market_contract.sell(
        api_keys,
        amount_wei,
        outcome_index,
        max_outcome_tokens_to_sell,
        web3=web3,
    )
    if auto_withdraw:
        auto_withdraw_collateral_token(
            collateral_token_contract=collateral_token_contract,
            amount_wei=amount_wei,
            api_keys=api_keys,
            web3=web3,
        )

    return tx_receipt["transactionHash"].hex()
binary_omen_sell_outcome_tx
binary_omen_sell_outcome_tx(
    api_keys: APIKeys,
    amount: OutcomeToken | CollateralToken | USD,
    market: OmenAgentMarket,
    outcome: OutcomeStr,
    auto_withdraw: bool,
    web3: Web3 | None = None,
) -> str
Source code in prediction_market_agent_tooling/markets/omen/omen.py
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
def binary_omen_sell_outcome_tx(
    api_keys: APIKeys,
    amount: OutcomeToken | CollateralToken | USD,
    market: OmenAgentMarket,
    outcome: OutcomeStr,
    auto_withdraw: bool,
    web3: Web3 | None = None,
) -> str:
    return omen_sell_outcome_tx(
        api_keys=api_keys,
        amount=amount,
        market=market,
        outcome=outcome,
        auto_withdraw=auto_withdraw,
        web3=web3,
    )
omen_create_market_tx
omen_create_market_tx(
    api_keys: APIKeys,
    initial_funds: USD | CollateralToken,
    question: str,
    closing_time: DatetimeUTC,
    category: str,
    language: str,
    outcomes: Sequence[str],
    auto_deposit: bool,
    finalization_timeout: timedelta = REALITY_DEFAULT_FINALIZATION_TIMEOUT,
    fee_perc: float = OMEN_DEFAULT_MARKET_FEE_PERC,
    distribution_hint: list[OutcomeWei] | None = None,
    collateral_token_address: ChecksumAddress = WrappedxDaiContract().address,
    arbitrator: Arbitrator = Arbitrator.KLEROS_31_JURORS_WITH_APPEAL,
    web3: Web3 | None = None,
) -> CreatedMarket

Based on omen-exchange TypeScript code: https://github.com/protofire/omen-exchange/blob/b0b9a3e71b415d6becf21fe428e1c4fc0dad2e80/app/src/services/cpk/cpk.ts#L308

Source code in prediction_market_agent_tooling/markets/omen/omen.py
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
def omen_create_market_tx(
    api_keys: APIKeys,
    initial_funds: USD | CollateralToken,
    question: str,
    closing_time: DatetimeUTC,
    category: str,
    language: str,
    outcomes: t.Sequence[str],
    auto_deposit: bool,
    finalization_timeout: timedelta = REALITY_DEFAULT_FINALIZATION_TIMEOUT,
    fee_perc: float = OMEN_DEFAULT_MARKET_FEE_PERC,
    distribution_hint: list[OutcomeWei] | None = None,
    collateral_token_address: ChecksumAddress = WrappedxDaiContract().address,
    arbitrator: Arbitrator = Arbitrator.KLEROS_31_JURORS_WITH_APPEAL,
    web3: Web3 | None = None,
) -> CreatedMarket:
    """
    Based on omen-exchange TypeScript code: https://github.com/protofire/omen-exchange/blob/b0b9a3e71b415d6becf21fe428e1c4fc0dad2e80/app/src/services/cpk/cpk.ts#L308
    """
    web3 = (
        web3 or OmenFixedProductMarketMakerFactoryContract.get_web3()
    )  # Default to Gnosis web3.
    initial_funds_in_collateral = (
        get_usd_in_token(initial_funds, collateral_token_address)
        if isinstance(initial_funds, USD)
        else initial_funds
    )
    initial_funds_in_collateral_wei = initial_funds_in_collateral.as_wei

    realitio_contract = OmenRealitioContract()
    conditional_token_contract = OmenConditionalTokenContract()
    collateral_token_contract = to_gnosis_chain_contract(
        init_collateral_token_contract(collateral_token_address, web3)
    )
    factory_contract = OmenFixedProductMarketMakerFactoryContract()
    oracle_contract = OmenOracleContract()

    # These checks were originally maded somewhere in the middle of the process, but it's safer to do them right away.
    # Double check that the oracle's realitio address is the same as we are using.
    if oracle_contract.realitio() != realitio_contract.address:
        raise RuntimeError(
            "The oracle's realitio address is not the same as we are using."
        )
    # Double check that the oracle's conditional tokens address is the same as we are using.
    if oracle_contract.conditionalTokens() != conditional_token_contract.address:
        raise RuntimeError(
            "The oracle's conditional tokens address is not the same as we are using."
        )

    if auto_deposit:
        auto_deposit_collateral_token(
            collateral_token_contract,
            initial_funds_in_collateral_wei,
            api_keys=api_keys,
            web3=web3,
        )

    # Create the question on Realitio.
    question_event = realitio_contract.askQuestion(
        api_keys=api_keys,
        question=question,
        category=category,
        outcomes=outcomes,
        language=language,
        arbitrator=arbitrator,
        opening=closing_time,  # The question is opened at the closing time of the market.
        timeout=finalization_timeout,
        web3=web3,
    )

    # Construct the condition id.
    cond_event: ConditionPreparationEvent | None = None
    condition_id = conditional_token_contract.getConditionId(
        question_id=question_event.question_id,
        oracle_address=oracle_contract.address,
        outcomes_slot_count=len(outcomes),
        web3=web3,
    )
    if not conditional_token_contract.does_condition_exists(condition_id, web3=web3):
        cond_event = conditional_token_contract.prepareCondition(
            api_keys=api_keys,
            question_id=question_event.question_id,
            oracle_address=oracle_contract.address,
            outcomes_slot_count=len(outcomes),
            web3=web3,
        )

    # Approve the market maker to withdraw our collateral token.
    collateral_token_contract.approve(
        api_keys=api_keys,
        for_address=factory_contract.address,
        amount_wei=initial_funds_in_collateral_wei,
        web3=web3,
    )

    # Create the market.
    fee = CollateralToken(fee_perc).as_wei
    (
        market_event,
        funding_event,
        receipt_tx,
    ) = factory_contract.create2FixedProductMarketMaker(
        api_keys=api_keys,
        condition_id=condition_id,
        fee=fee,
        distribution_hint=distribution_hint,
        initial_funds_wei=initial_funds_in_collateral_wei,
        collateral_token_address=collateral_token_contract.address,
        web3=web3,
    )

    # Note: In the Omen's Typescript code, there is futher a creation of `stakingRewardsFactoryAddress`,
    # (https://github.com/protofire/omen-exchange/blob/763d9c9d05ebf9edacbc1dbaa561aa5d08813c0f/app/src/services/cpk/fns.ts#L979)
    # but address of stakingRewardsFactoryAddress on xDai/Gnosis is 0x0000000000000000000000000000000000000000,
    # so skipping it here.

    return CreatedMarket(
        market_creation_timestamp=get_receipt_block_timestamp(receipt_tx, web3),
        market_event=market_event,
        funding_event=funding_event,
        condition_id=condition_id,
        question_event=question_event,
        condition_event=cond_event,
        initial_funds=initial_funds_in_collateral_wei,
        fee=fee,
        distribution_hint=distribution_hint,
    )
omen_fund_market_tx
omen_fund_market_tx(
    api_keys: APIKeys,
    market: OmenAgentMarket,
    funds: USD | CollateralToken,
    auto_deposit: bool,
    web3: Web3 | None = None,
) -> None
Source code in prediction_market_agent_tooling/markets/omen/omen.py
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
def omen_fund_market_tx(
    api_keys: APIKeys,
    market: OmenAgentMarket,
    funds: USD | CollateralToken,
    auto_deposit: bool,
    web3: Web3 | None = None,
) -> None:
    funds_in_collateral = market.get_in_token(funds)
    funds_in_collateral_wei = funds_in_collateral.as_wei
    market_contract = market.get_contract()
    collateral_token_contract = market_contract.get_collateral_token_contract(web3=web3)

    collateral_token_contract.approve(
        api_keys=api_keys,
        for_address=market_contract.address,
        amount_wei=funds_in_collateral_wei,
        web3=web3,
    )

    if auto_deposit:
        auto_deposit_collateral_token(
            collateral_token_contract, funds_in_collateral_wei, api_keys, web3
        )

    market_contract.addFunding(api_keys, funds_in_collateral_wei, web3=web3)
omen_redeem_full_position_tx
omen_redeem_full_position_tx(
    api_keys: APIKeys,
    market: OmenAgentMarket,
    auto_withdraw: bool = True,
    web3: Web3 | None = None,
) -> None

Redeems position from a given Omen market. Note that we check if there is a balance to be redeemed before sending the transaction.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
def omen_redeem_full_position_tx(
    api_keys: APIKeys,
    market: OmenAgentMarket,
    auto_withdraw: bool = True,
    web3: Web3 | None = None,
) -> None:
    """
    Redeems position from a given Omen market. Note that we check if there is a balance
    to be redeemed before sending the transaction.
    """

    from_address = api_keys.bet_from_address

    market_contract: OmenFixedProductMarketMakerContract = market.get_contract()
    conditional_token_contract = OmenConditionalTokenContract()
    collateral_token_contract = market_contract.get_collateral_token_contract(web3)

    # Verify, that markets uses conditional tokens that we expect.
    if market_contract.conditionalTokens() != conditional_token_contract.address:
        raise ValueError(
            f"Market {market.id} uses conditional token that we didn't expect, {market_contract.conditionalTokens()} != {conditional_token_contract.address=}"
        )

    if not market.is_resolved():
        logger.debug("Cannot redeem winnings if market is not yet resolved. Exiting.")
        return

    amount_per_index = get_conditional_tokens_balance_for_market(
        market, from_address, web3
    )
    amount_wei = sum(amount_per_index.values(), start=OutcomeWei.zero())
    if amount_wei == 0:
        logger.debug("No balance to claim. Exiting.")
        return

    if not conditional_token_contract.is_condition_resolved(market.condition.id):
        logger.debug("Market not yet resolved, not possible to claim")
        return

    redeem_event = conditional_token_contract.redeemPositions(
        api_keys=api_keys,
        collateral_token_address=market.collateral_token_contract_address_checksummed,
        condition_id=market.condition.id,
        index_sets=market.condition.index_sets,
        web3=web3,
    )

    logger.info(
        f"Redeemed {redeem_event.payout.as_token} {collateral_token_contract.symbol_cached(web3=web3)} from market {market.question=} ({market.url})."
    )

    if auto_withdraw:
        auto_withdraw_collateral_token(
            collateral_token_contract=collateral_token_contract,
            amount_wei=redeem_event.payout,
            api_keys=api_keys,
            web3=web3,
        )
get_conditional_tokens_balance_for_market
get_conditional_tokens_balance_for_market(
    market: OmenAgentMarket,
    from_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> dict[int, OutcomeWei]

We derive the withdrawable balance from the ConditionalTokens contract through CollectionId -> PositionId (which also serves as tokenId) -> TokenBalances.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
def get_conditional_tokens_balance_for_market(
    market: OmenAgentMarket,
    from_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> dict[int, OutcomeWei]:
    """
    We derive the withdrawable balance from the ConditionalTokens contract through CollectionId -> PositionId (which
    also serves as tokenId) -> TokenBalances.
    """
    balance_per_index_set: dict[int, OutcomeWei] = {}
    conditional_token_contract = OmenConditionalTokenContract()
    parent_collection_id = build_parent_collection_id()

    for index_set in market.condition.index_sets:
        collection_id = conditional_token_contract.getCollectionId(
            parent_collection_id, market.condition.id, index_set, web3=web3
        )
        # Note that collection_id is returned as bytes, which is accepted by the contract calls downstream.
        position_id: int = conditional_token_contract.getPositionId(
            market.collateral_token_contract_address_checksummed,
            collection_id,
            web3=web3,
        )
        balance_for_position = conditional_token_contract.balanceOf(
            from_address=from_address, position_id=position_id, web3=web3
        )
        balance_per_index_set[index_set] = balance_for_position

    return balance_per_index_set
omen_remove_fund_market_tx
omen_remove_fund_market_tx(
    api_keys: APIKeys,
    market: OmenAgentMarket,
    shares: Wei | None,
    web3: Web3 | None = None,
    auto_withdraw: bool = True,
) -> None

Removes funding from a given OmenMarket (moving the funds from the OmenMarket to the ConditionalTokens contract), and finally calls the mergePositions method which transfers collateralToken from the ConditionalTokens contract to the address corresponding to from_private_key.

Warning: Liquidity removal works on the principle of getting market's shares, not the collateral token itself. After we remove funding, using the mergePositions we get min(shares per index) of collateral token back, but the remaining shares can be converted back only after the market is resolved. That can be done using the redeem_from_all_user_positions function below.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
def omen_remove_fund_market_tx(
    api_keys: APIKeys,
    market: OmenAgentMarket,
    shares: Wei | None,
    web3: Web3 | None = None,
    auto_withdraw: bool = True,
) -> None:
    """
    Removes funding from a given OmenMarket (moving the funds from the OmenMarket to the
    ConditionalTokens contract), and finally calls the `mergePositions` method which transfers collateralToken from the ConditionalTokens contract to the address corresponding to `from_private_key`.

    Warning: Liquidity removal works on the principle of getting market's shares, not the collateral token itself.
    After we remove funding, using the `mergePositions` we get `min(shares per index)` of collateral token back, but the remaining shares can be converted back only after the market is resolved.
    That can be done using the `redeem_from_all_user_positions` function below.
    """
    from_address = api_keys.bet_from_address
    market_contract = market.get_contract()
    market_collateral_token_contract = market_contract.get_collateral_token_contract(
        web3=web3
    )
    original_balance = market_collateral_token_contract.balanceOf(
        from_address, web3=web3
    )

    total_shares = market_contract.balanceOf(from_address, web3=web3)
    if total_shares == 0:
        logger.info("No shares to remove.")
        return

    if shares is None or shares > total_shares:
        logger.debug(
            f"shares available to claim {total_shares} - defaulting to a total removal."
        )
        shares = total_shares

    market_contract.removeFunding(api_keys=api_keys, remove_funding=shares, web3=web3)

    conditional_tokens = OmenConditionalTokenContract()
    amount_per_index_set = get_conditional_tokens_balance_for_market(
        market, from_address, web3
    )
    # We fetch the minimum balance of outcome token - for ex, in this tx (https://gnosisscan.io/tx/0xc31c4e9bc6a60cf7db9991a40ec2f2a06e3539f8cb8dd81b6af893cef6f40cd7#eventlog) - event #460, this should yield 9804940144070370149. This amount matches what is displayed in the Omen UI. # web3-private-key-ok
    # See similar logic from Olas
    # https://github.com/valory-xyz/market-creator/blob/4bc47f696fb5ecb61c3b7ec8c001ff2ab6c60fcf/packages/valory/skills/market_creation_manager_abci/behaviours.py#L1308
    amount_to_merge = min(amount_per_index_set.values())

    result = conditional_tokens.mergePositions(
        api_keys=api_keys,
        collateral_token_address=market.collateral_token_contract_address_checksummed,
        conditionId=market.condition.id,
        index_sets=market.condition.index_sets,
        amount=amount_to_merge,
        web3=web3,
    )

    new_balance = market_collateral_token_contract.balanceOf(from_address, web3=web3)
    balance_diff = new_balance - original_balance

    logger.debug(f"Result from merge positions {result}")
    logger.info(
        f"Withdrawn {balance_diff.as_token} {market_collateral_token_contract.symbol_cached(web3=web3)} from liquidity at {market.url=}."
    )

    if auto_withdraw:
        auto_withdraw_collateral_token(
            collateral_token_contract=market_collateral_token_contract,
            amount_wei=balance_diff,
            api_keys=api_keys,
            web3=web3,
        )
redeem_from_all_user_positions
redeem_from_all_user_positions(
    api_keys: APIKeys,
    web3: Web3 | None = None,
    auto_withdraw: bool = True,
) -> None

Redeems from all user positions where the user didn't redeem yet.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
def redeem_from_all_user_positions(
    api_keys: APIKeys,
    web3: Web3 | None = None,
    auto_withdraw: bool = True,
) -> None:
    """
    Redeems from all user positions where the user didn't redeem yet.
    """
    public_key = api_keys.bet_from_address

    conditional_token_contract = OmenConditionalTokenContract()
    user_positions = OmenSubgraphHandler().get_user_positions(
        public_key,
        # After redeem, this will became zero and we won't re-process it.
        total_balance_bigger_than=OutcomeWei(0),
    )

    for index, user_position in enumerate(user_positions):
        condition_id = user_position.position.condition_id

        if not conditional_token_contract.is_condition_resolved(condition_id):
            logger.info(
                f"[{index + 1} / {len(user_positions)}] Skipping redeem, {user_position.id=} isn't resolved yet."
            )
            continue

        logger.info(
            f"[{index + 1} / {len(user_positions)}] Processing redeem from {user_position.id=}."
        )
        collateral_token_contract = (
            user_position.position.get_collateral_token_contract(web3=web3)
        )

        redeem_event = conditional_token_contract.redeemPositions(
            api_keys=api_keys,
            collateral_token_address=user_position.position.collateral_token_contract_address_checksummed,
            condition_id=condition_id,
            index_sets=user_position.position.indexSets,
            web3=web3,
        )

        logger.info(
            f"Redeemed {redeem_event.payout.as_token} {collateral_token_contract.symbol_cached(web3=web3)} from position {user_position.id=}."
        )

        if auto_withdraw:
            auto_withdraw_collateral_token(
                collateral_token_contract=collateral_token_contract,
                amount_wei=redeem_event.payout,
                api_keys=api_keys,
                web3=web3,
            )
get_binary_market_p_yes_history
get_binary_market_p_yes_history(
    market: OmenAgentMarket,
) -> list[Probability]
Source code in prediction_market_agent_tooling/markets/omen/omen.py
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
def get_binary_market_p_yes_history(market: OmenAgentMarket) -> list[Probability]:
    history: list[Probability] = []
    trades = sorted(
        OmenSubgraphHandler().get_trades(
            # We need to look at price both after buying or selling, so get trades, not bets.
            market_id=market.market_maker_contract_address_checksummed,
            end_time=market.close_time,
            # Even after market is closed, there can be many `Sell` trades which will converge the probability to the true one.
        ),
        key=lambda x: x.creation_datetime,
    )

    for index, trade in enumerate(trades):
        # We need to append the old probability to have also the initial state of the market (before any bet placement).
        history.append(
            trade.old_probability
            if trade.outcomeIndex == market.yes_index
            else Probability(1 - trade.old_probability)
        )

        # At the last trade, we also need to append the new probability, to have the market latest state.
        if index == len(trades) - 1:
            history.append(
                trade.probability
                if trade.outcomeIndex == market.yes_index
                else Probability(1 - trade.probability)
            )

    return history
send_keeping_token_to_eoa_xdai
send_keeping_token_to_eoa_xdai(
    api_keys: APIKeys,
    min_required_balance: xDai,
    multiplier: float = 1.0,
    web3: Web3 | None = None,
) -> None

Keeps xDai balance above the minimum required balance by transfering keeping token to xDai. Optionally, the amount to transfer can be multiplied by the multiplier, which can be useful to keep a buffer.

Source code in prediction_market_agent_tooling/markets/omen/omen.py
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
def send_keeping_token_to_eoa_xdai(
    api_keys: APIKeys,
    min_required_balance: xDai,
    multiplier: float = 1.0,
    web3: Web3 | None = None,
) -> None:
    """
    Keeps xDai balance above the minimum required balance by transfering keeping token to xDai.
    Optionally, the amount to transfer can be multiplied by the `multiplier`, which can be useful to keep a buffer.
    """
    # Only wxDai can be withdrawn to xDai. Anything else needs to be swapped to wxDai first.
    wxdai_contract = WrappedxDaiContract()

    if KEEPING_ERC20_TOKEN.address != wxdai_contract.address:
        raise RuntimeError(
            "Only wxDai can be withdrawn to xDai. Rest is not implemented for simplicity for now. It would require trading using CoW, or double withdrawing from sDai"
        )

    current_balances_eoa = get_balances(api_keys.public_key, web3)
    current_balances_betting = get_balances(api_keys.bet_from_address, web3)

    # xDai needs to be in our wallet where we pay transaction fees, so do not check for Safe's balance here, but for EOA.
    if current_balances_eoa.xdai >= min_required_balance:
        logger.info(
            f"Current xDai balance {current_balances_eoa.xdai} is more or equal than the required minimum balance {min_required_balance}."
        )
        return

    need_to_withdraw = (min_required_balance - current_balances_eoa.xdai) * multiplier
    need_to_withdraw_wei = need_to_withdraw.as_xdai_wei

    if current_balances_eoa.wxdai >= need_to_withdraw.as_token:
        # If EOA has enough of wxDai, simply withdraw it.
        logger.info(
            f"Withdrawing {need_to_withdraw} wxDai from EOA to keep the EOA's xDai balance above the minimum required balance {min_required_balance}."
        )
        wxdai_contract.withdraw(
            api_keys=api_keys.copy_without_safe_address(),
            amount_wei=need_to_withdraw_wei.as_wei,
            web3=web3,
        )

    elif current_balances_betting.wxdai >= need_to_withdraw.as_token:
        # If Safe has enough of wxDai:
        # First send them to EOA's address.
        logger.info(
            f"Transfering {need_to_withdraw} wxDai from betting address to EOA's address."
        )
        wxdai_contract.transferFrom(
            api_keys=api_keys,
            sender=api_keys.bet_from_address,
            recipient=api_keys.public_key,
            amount_wei=need_to_withdraw_wei.as_wei,
            web3=web3,
        )
        # And then simply withdraw it.
        logger.info(
            f"Withdrawing {need_to_withdraw} wxDai from EOA to keep the EOA's xDai balance above the minimum required balance {min_required_balance}."
        )
        wxdai_contract.withdraw(
            api_keys=api_keys.copy_without_safe_address(),
            amount_wei=need_to_withdraw_wei.as_wei,
            web3=web3,
        )

    else:
        raise OutOfFundsError(
            f"Current wxDai balance ({current_balances_eoa=} for {api_keys.public_key=}, {current_balances_betting=} for {api_keys.bet_from_address=}) is less than the required minimum wxDai to withdraw {need_to_withdraw}."
        )
get_buy_outcome_token_amount
get_buy_outcome_token_amount(
    investment_amount: CollateralToken,
    outcome_index: int,
    pool_balances: list[OutcomeToken],
    fees: MarketFees,
) -> OutcomeToken

Calculates the amount of outcome tokens received for a given investment

Taken from https://github.com/gnosis/conditional-tokens-market-makers/blob/6814c0247c745680bb13298d4f0dd7f5b574d0db/contracts/FixedProductMarketMaker.sol#L264

Source code in prediction_market_agent_tooling/markets/omen/omen.py
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
def get_buy_outcome_token_amount(
    investment_amount: CollateralToken,
    outcome_index: int,
    pool_balances: list[OutcomeToken],
    fees: MarketFees,
) -> OutcomeToken:
    """
    Calculates the amount of outcome tokens received for a given investment

    Taken from https://github.com/gnosis/conditional-tokens-market-makers/blob/6814c0247c745680bb13298d4f0dd7f5b574d0db/contracts/FixedProductMarketMaker.sol#L264
    """
    if outcome_index >= len(pool_balances):
        raise ValueError("invalid outcome index")

    investment_amount_minus_fees = fees.get_after_fees(investment_amount)
    investment_amount_minus_fees_as_ot = OutcomeToken(
        investment_amount_minus_fees.value
    )

    buy_token_pool_balance = pool_balances[outcome_index]
    ending_outcome_balance = buy_token_pool_balance

    # Calculate the ending balance considering all other outcomes
    for i, pool_balance in enumerate(pool_balances):
        if i != outcome_index:
            denominator = pool_balance + investment_amount_minus_fees_as_ot
            ending_outcome_balance = OutcomeToken(
                (ending_outcome_balance * pool_balance / denominator)
            )

    if ending_outcome_balance <= 0:
        raise ValueError("must have non-zero balances")

    result = (
        buy_token_pool_balance
        + investment_amount_minus_fees_as_ot
        - ending_outcome_balance
    )
    return result

omen_constants

WRAPPED_XDAI_CONTRACT_ADDRESS module-attribute
WRAPPED_XDAI_CONTRACT_ADDRESS = to_checksum_address(
    "0xe91d153e0b41518a2ce8dd3d7944fa863463a97d"
)
SDAI_CONTRACT_ADDRESS module-attribute
SDAI_CONTRACT_ADDRESS = to_checksum_address(
    "0xaf204776c7245bF4147c2612BF6e5972Ee483701"
)

omen_contracts

OMEN_DEFAULT_MARKET_FEE_PERC module-attribute
OMEN_DEFAULT_MARKET_FEE_PERC = 0.02
REALITY_DEFAULT_FINALIZATION_TIMEOUT module-attribute
REALITY_DEFAULT_FINALIZATION_TIMEOUT = timedelta(days=3)
COLLATERAL_TOKEN_CHOICE_TO_ADDRESS module-attribute
COLLATERAL_TOKEN_CHOICE_TO_ADDRESS = {
    wxdai: address,
    sdai: address,
    gno: address,
    metri_super_user: address,
}
OmenOracleContract

Bases: ContractOnGnosisChain

abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../../abis/omen_oracle.abi.json",
    )
)
address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0xAB16D643bA051C11962DA645f74632d3130c81E2"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
realitio
realitio() -> ChecksumAddress
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
68
69
70
def realitio(self) -> ChecksumAddress:
    realitio_address: ChecksumAddress = self.call("realitio")
    return realitio_address
conditionalTokens
conditionalTokens() -> ChecksumAddress
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
72
73
74
def conditionalTokens(self) -> ChecksumAddress:
    realitio_address: ChecksumAddress = self.call("conditionalTokens")
    return realitio_address
resolve
resolve(
    api_keys: APIKeys,
    question_id: HexBytes,
    template_id: int,
    question_raw: str,
    n_outcomes: int,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
def resolve(
    self,
    api_keys: APIKeys,
    question_id: HexBytes,
    template_id: int,
    question_raw: str,
    n_outcomes: int,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="resolve",
        function_params=dict(
            questionId=question_id,
            templateId=template_id,
            question=question_raw,
            numOutcomes=n_outcomes,
        ),
        web3=web3,
    )
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
OmenConditionalTokenContract

Bases: ContractOnGnosisChain

abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../../abis/omen_fpmm_conditionaltokens.abi.json",
    )
)
address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0xCeAfDD6bc0bEF976fdCd1112955828E00543c0Ce"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
getConditionId
getConditionId(
    question_id: HexBytes,
    oracle_address: ChecksumAddress,
    outcomes_slot_count: int,
    web3: Web3 | None = None,
) -> HexBytes
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def getConditionId(
    self,
    question_id: HexBytes,
    oracle_address: ChecksumAddress,
    outcomes_slot_count: int,
    web3: Web3 | None = None,
) -> HexBytes:
    id_ = HexBytes(
        self.call(
            "getConditionId",
            [oracle_address, question_id, outcomes_slot_count],
            web3=web3,
        )
    )
    return id_
balanceOf
balanceOf(
    from_address: ChecksumAddress,
    position_id: int,
    web3: Web3 | None = None,
) -> OutcomeWei
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
130
131
132
133
134
135
136
def balanceOf(
    self, from_address: ChecksumAddress, position_id: int, web3: Web3 | None = None
) -> OutcomeWei:
    balance = OutcomeWei(
        self.call("balanceOf", [from_address, position_id], web3=web3)
    )
    return balance
getCollectionId
getCollectionId(
    parent_collection_id: HexStr,
    condition_id: HexBytes,
    index_set: int,
    web3: Web3 | None = None,
) -> HexBytes
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
def getCollectionId(
    self,
    parent_collection_id: HexStr,
    condition_id: HexBytes,
    index_set: int,
    web3: Web3 | None = None,
) -> HexBytes:
    collection_id = HexBytes(
        self.call(
            "getCollectionId",
            [parent_collection_id, condition_id, index_set],
            web3=web3,
        )
    )
    return collection_id
getPositionId
getPositionId(
    collateral_token_address: ChecksumAddress,
    collection_id: HexBytes,
    web3: Web3 | None = None,
) -> int
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
154
155
156
157
158
159
160
161
162
163
164
165
def getPositionId(
    self,
    collateral_token_address: ChecksumAddress,
    collection_id: HexBytes,
    web3: Web3 | None = None,
) -> int:
    position_id: int = self.call(
        "getPositionId",
        [collateral_token_address, collection_id],
        web3=web3,
    )
    return position_id
mergePositions
mergePositions(
    api_keys: APIKeys,
    collateral_token_address: ChecksumAddress,
    conditionId: HexBytes,
    index_sets: List[int],
    amount: OutcomeWei,
    parent_collection_id: HexStr = build_parent_collection_id(),
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def mergePositions(
    self,
    api_keys: APIKeys,
    collateral_token_address: ChecksumAddress,
    conditionId: HexBytes,
    index_sets: t.List[int],
    amount: OutcomeWei,
    parent_collection_id: HexStr = build_parent_collection_id(),
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="mergePositions",
        function_params=[
            collateral_token_address,
            parent_collection_id,
            conditionId,
            index_sets,
            amount,
        ],
        web3=web3,
    )
redeemPositions
redeemPositions(
    api_keys: APIKeys,
    collateral_token_address: HexAddress,
    condition_id: HexBytes,
    index_sets: List[int],
    parent_collection_id: HexStr = build_parent_collection_id(),
    web3: Web3 | None = None,
) -> PayoutRedemptionEvent
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
def redeemPositions(
    self,
    api_keys: APIKeys,
    collateral_token_address: HexAddress,
    condition_id: HexBytes,
    index_sets: t.List[int],
    parent_collection_id: HexStr = build_parent_collection_id(),
    web3: Web3 | None = None,
) -> PayoutRedemptionEvent:
    receipt_tx = self.send(
        api_keys=api_keys,
        function_name="redeemPositions",
        function_params=[
            collateral_token_address,
            parent_collection_id,
            condition_id,
            index_sets,
        ],
        web3=web3,
    )
    redeem_event_logs = (
        self.get_web3_contract(web3=web3)
        .events.PayoutRedemption()
        .process_receipt(receipt_tx)
    )
    redeem_event = PayoutRedemptionEvent(**redeem_event_logs[0]["args"])
    return redeem_event
getOutcomeSlotCount
getOutcomeSlotCount(
    condition_id: HexBytes, web3: Web3 | None = None
) -> int
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
218
219
220
221
222
def getOutcomeSlotCount(
    self, condition_id: HexBytes, web3: Web3 | None = None
) -> int:
    count: int = self.call("getOutcomeSlotCount", [condition_id], web3=web3)
    return count
does_condition_exists
does_condition_exists(
    condition_id: HexBytes, web3: Web3 | None = None
) -> bool
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
224
225
226
227
def does_condition_exists(
    self, condition_id: HexBytes, web3: Web3 | None = None
) -> bool:
    return self.getOutcomeSlotCount(condition_id, web3=web3) > 0
is_condition_resolved
is_condition_resolved(
    condition_id: HexBytes, web3: Web3 | None = None
) -> bool
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
229
230
231
232
233
234
235
def is_condition_resolved(
    self, condition_id: HexBytes, web3: Web3 | None = None
) -> bool:
    # from ConditionalTokens.redeemPositions:
    # uint den = payoutDenominator[conditionId]; require(den > 0, "result for condition not received yet");
    payout_for_condition = self.payoutDenominator(condition_id, web3=web3)
    return payout_for_condition > 0
payoutDenominator
payoutDenominator(
    condition_id: HexBytes, web3: Web3 | None = None
) -> int
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
237
238
239
240
241
242
243
def payoutDenominator(
    self, condition_id: HexBytes, web3: Web3 | None = None
) -> int:
    payoutForCondition: int = self.call(
        "payoutDenominator", [condition_id], web3=web3
    )
    return payoutForCondition
setApprovalForAll
setApprovalForAll(
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    approve: bool,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
def setApprovalForAll(
    self,
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    approve: bool,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="setApprovalForAll",
        function_params=[
            for_address,
            approve,
        ],
        tx_params=tx_params,
        web3=web3,
    )
prepareCondition
prepareCondition(
    api_keys: APIKeys,
    oracle_address: ChecksumAddress,
    question_id: HexBytes,
    outcomes_slot_count: int,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> ConditionPreparationEvent
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
def prepareCondition(
    self,
    api_keys: APIKeys,
    oracle_address: ChecksumAddress,
    question_id: HexBytes,
    outcomes_slot_count: int,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> ConditionPreparationEvent:
    receipt_tx = self.send(
        api_keys=api_keys,
        function_name="prepareCondition",
        function_params=[
            oracle_address,
            question_id,
            outcomes_slot_count,
        ],
        tx_params=tx_params,
        web3=web3,
    )

    event_logs = (
        self.get_web3_contract(web3=web3)
        .events.ConditionPreparation()
        .process_receipt(receipt_tx)
    )
    cond_event = ConditionPreparationEvent(**event_logs[0]["args"])

    return cond_event
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
OmenFixedProductMarketMakerContract

Bases: ContractOnGnosisChain

abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../../abis/omen_fpmm.abi.json",
    )
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
address instance-attribute
address: ChecksumAddress
balanceOf
balanceOf(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> Wei
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
306
307
308
def balanceOf(self, for_address: ChecksumAddress, web3: Web3 | None = None) -> Wei:
    balance = Wei(self.call("balanceOf", [for_address], web3=web3))
    return balance
calcBuyAmount
calcBuyAmount(
    investment_amount: Wei,
    outcome_index: int,
    web3: Web3 | None = None,
) -> OutcomeWei

Returns amount of shares we will get for the given outcome_index for the given investment amount.

Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
310
311
312
313
314
315
316
317
318
319
def calcBuyAmount(
    self, investment_amount: Wei, outcome_index: int, web3: Web3 | None = None
) -> OutcomeWei:
    """
    Returns amount of shares we will get for the given outcome_index for the given investment amount.
    """
    calculated_shares = OutcomeWei(
        self.call("calcBuyAmount", [investment_amount, outcome_index], web3=web3)
    )
    return calculated_shares
calcSellAmount
calcSellAmount(
    return_amount: Wei,
    outcome_index: int,
    web3: Web3 | None = None,
) -> OutcomeWei

Returns amount of shares we will sell for the requested wei.

Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
321
322
323
324
325
326
327
328
329
330
def calcSellAmount(
    self, return_amount: Wei, outcome_index: int, web3: Web3 | None = None
) -> OutcomeWei:
    """
    Returns amount of shares we will sell for the requested wei.
    """
    calculated_shares = OutcomeWei(
        self.call("calcSellAmount", [return_amount, outcome_index], web3=web3)
    )
    return calculated_shares
conditionalTokens
conditionalTokens(
    web3: Web3 | None = None,
) -> ChecksumAddress
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
332
333
334
def conditionalTokens(self, web3: Web3 | None = None) -> ChecksumAddress:
    address: HexAddress = self.call("conditionalTokens", web3=web3)
    return Web3.to_checksum_address(address)
collateralToken
collateralToken(
    web3: Web3 | None = None,
) -> ChecksumAddress
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
336
337
338
def collateralToken(self, web3: Web3 | None = None) -> ChecksumAddress:
    address: HexAddress = self.call("collateralToken", web3=web3)
    return Web3.to_checksum_address(address)
buy
buy(
    api_keys: APIKeys,
    amount_wei: Wei,
    outcome_index: int,
    min_outcome_tokens_to_buy: OutcomeWei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def buy(
    self,
    api_keys: APIKeys,
    amount_wei: Wei,
    outcome_index: int,
    min_outcome_tokens_to_buy: OutcomeWei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="buy",
        function_params=[
            amount_wei,
            outcome_index,
            min_outcome_tokens_to_buy,
        ],
        tx_params=tx_params,
        web3=web3,
    )
sell
sell(
    api_keys: APIKeys,
    amount_wei: Wei,
    outcome_index: int,
    max_outcome_tokens_to_sell: OutcomeWei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
def sell(
    self,
    api_keys: APIKeys,
    amount_wei: Wei,
    outcome_index: int,
    max_outcome_tokens_to_sell: OutcomeWei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="sell",
        function_params=[
            amount_wei,
            outcome_index,
            max_outcome_tokens_to_sell,
        ],
        tx_params=tx_params,
        web3=web3,
    )
addFunding
addFunding(
    api_keys: APIKeys,
    add_funding: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt

Funding is added in Weis (xDai) and then converted to shares.

Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def addFunding(
    self,
    api_keys: APIKeys,
    add_funding: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Funding is added in Weis (xDai) and then converted to shares.
    """
    # `addFunding` with `distribution_hint` can be used only during the market creation, so forcing empty here.
    distribution_hint: list[int] = []
    return self.send(
        api_keys=api_keys,
        function_name="addFunding",
        function_params=[add_funding, distribution_hint],
        tx_params=tx_params,
        web3=web3,
    )
removeFunding
removeFunding(
    api_keys: APIKeys,
    remove_funding: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt

Remove funding is done in shares.

Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
def removeFunding(
    self,
    api_keys: APIKeys,
    remove_funding: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Remove funding is done in shares.
    """
    return self.send(
        api_keys=api_keys,
        function_name="removeFunding",
        function_params=[remove_funding],
        tx_params=tx_params,
        web3=web3,
    )
totalSupply
totalSupply(web3: Web3 | None = None) -> Wei
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
420
421
422
423
def totalSupply(self, web3: Web3 | None = None) -> Wei:
    # This is the liquidity you seen on the Omen website (but in Wei).
    total_supply = Wei(self.call("totalSupply", web3=web3))
    return total_supply
get_collateral_token_contract
get_collateral_token_contract(
    web3: Web3 | None = None,
) -> ContractERC20OnGnosisChain
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
425
426
427
428
429
430
431
def get_collateral_token_contract(
    self, web3: Web3 | None = None
) -> ContractERC20OnGnosisChain:
    web3 = web3 or self.get_web3()
    return to_gnosis_chain_contract(
        init_collateral_token_contract(self.collateralToken(web3=web3), web3)
    )
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
MetriSuperGroup

Bases: ContractERC20OnGnosisChain

address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0x7147A7405fCFe5CFa30c6d5363f9f357a317d082"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../abis/erc20.abi.json",
    )
)
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
symbol
symbol(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
200
201
202
def symbol(self, web3: Web3 | None = None) -> str:
    symbol: str = self.call("symbol", web3=web3)
    return symbol
symbol_cached
symbol_cached(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
204
205
206
207
208
209
210
def symbol_cached(self, web3: Web3 | None = None) -> str:
    web3 = web3 or self.get_web3()
    cache_key = create_contract_method_cache_key(self.symbol, web3)
    if cache_key not in self._cache:
        self._cache[cache_key] = self.symbol(web3=web3)
    value: str = self._cache[cache_key]
    return value
allowance
allowance(
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
212
213
214
215
216
217
218
219
220
221
def allowance(
    self,
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei:
    allowance_for_user: int = self.call(
        "allowance", function_params=[owner, for_address], web3=web3
    )
    return Wei(allowance_for_user)
approve
approve(
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
def approve(
    self,
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="approve",
        function_params=[
            for_address,
            amount_wei,
        ],
        tx_params=tx_params,
        web3=web3,
    )
transferFrom
transferFrom(
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
def transferFrom(
    self,
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="transferFrom",
        function_params=[sender, recipient, amount_wei],
        tx_params=tx_params,
        web3=web3,
    )
balanceOf
balanceOf(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
259
260
261
def balanceOf(self, for_address: ChecksumAddress, web3: Web3 | None = None) -> Wei:
    balance = Wei(self.call("balanceOf", [for_address], web3=web3))
    return balance
balance_of_in_tokens
balance_of_in_tokens(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken
Source code in prediction_market_agent_tooling/tools/contract.py
263
264
265
266
def balance_of_in_tokens(
    self, for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken:
    return self.balanceOf(for_address, web3=web3).as_token
GNOContract

Bases: ContractERC20OnGnosisChain

address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0x9c58bacc331c9aa871afd802db6379a98e80cedb"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../abis/erc20.abi.json",
    )
)
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
symbol
symbol(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
200
201
202
def symbol(self, web3: Web3 | None = None) -> str:
    symbol: str = self.call("symbol", web3=web3)
    return symbol
symbol_cached
symbol_cached(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
204
205
206
207
208
209
210
def symbol_cached(self, web3: Web3 | None = None) -> str:
    web3 = web3 or self.get_web3()
    cache_key = create_contract_method_cache_key(self.symbol, web3)
    if cache_key not in self._cache:
        self._cache[cache_key] = self.symbol(web3=web3)
    value: str = self._cache[cache_key]
    return value
allowance
allowance(
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
212
213
214
215
216
217
218
219
220
221
def allowance(
    self,
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei:
    allowance_for_user: int = self.call(
        "allowance", function_params=[owner, for_address], web3=web3
    )
    return Wei(allowance_for_user)
approve
approve(
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
def approve(
    self,
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="approve",
        function_params=[
            for_address,
            amount_wei,
        ],
        tx_params=tx_params,
        web3=web3,
    )
transferFrom
transferFrom(
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
def transferFrom(
    self,
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="transferFrom",
        function_params=[sender, recipient, amount_wei],
        tx_params=tx_params,
        web3=web3,
    )
balanceOf
balanceOf(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
259
260
261
def balanceOf(self, for_address: ChecksumAddress, web3: Web3 | None = None) -> Wei:
    balance = Wei(self.call("balanceOf", [for_address], web3=web3))
    return balance
balance_of_in_tokens
balance_of_in_tokens(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken
Source code in prediction_market_agent_tooling/tools/contract.py
263
264
265
266
def balance_of_in_tokens(
    self, for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken:
    return self.balanceOf(for_address, web3=web3).as_token
WETHContract

Bases: ContractERC20OnGnosisChain

address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../abis/erc20.abi.json",
    )
)
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
symbol
symbol(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
200
201
202
def symbol(self, web3: Web3 | None = None) -> str:
    symbol: str = self.call("symbol", web3=web3)
    return symbol
symbol_cached
symbol_cached(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
204
205
206
207
208
209
210
def symbol_cached(self, web3: Web3 | None = None) -> str:
    web3 = web3 or self.get_web3()
    cache_key = create_contract_method_cache_key(self.symbol, web3)
    if cache_key not in self._cache:
        self._cache[cache_key] = self.symbol(web3=web3)
    value: str = self._cache[cache_key]
    return value
allowance
allowance(
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
212
213
214
215
216
217
218
219
220
221
def allowance(
    self,
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei:
    allowance_for_user: int = self.call(
        "allowance", function_params=[owner, for_address], web3=web3
    )
    return Wei(allowance_for_user)
approve
approve(
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
def approve(
    self,
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="approve",
        function_params=[
            for_address,
            amount_wei,
        ],
        tx_params=tx_params,
        web3=web3,
    )
transferFrom
transferFrom(
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
def transferFrom(
    self,
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="transferFrom",
        function_params=[sender, recipient, amount_wei],
        tx_params=tx_params,
        web3=web3,
    )
balanceOf
balanceOf(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
259
260
261
def balanceOf(self, for_address: ChecksumAddress, web3: Web3 | None = None) -> Wei:
    balance = Wei(self.call("balanceOf", [for_address], web3=web3))
    return balance
balance_of_in_tokens
balance_of_in_tokens(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken
Source code in prediction_market_agent_tooling/tools/contract.py
263
264
265
266
def balance_of_in_tokens(
    self, for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken:
    return self.balanceOf(for_address, web3=web3).as_token
EUReContract

Bases: ContractERC20OnGnosisChain

address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0xcB444e90D8198415266c6a2724b7900fb12FC56E"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../abis/erc20.abi.json",
    )
)
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
symbol
symbol(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
200
201
202
def symbol(self, web3: Web3 | None = None) -> str:
    symbol: str = self.call("symbol", web3=web3)
    return symbol
symbol_cached
symbol_cached(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
204
205
206
207
208
209
210
def symbol_cached(self, web3: Web3 | None = None) -> str:
    web3 = web3 or self.get_web3()
    cache_key = create_contract_method_cache_key(self.symbol, web3)
    if cache_key not in self._cache:
        self._cache[cache_key] = self.symbol(web3=web3)
    value: str = self._cache[cache_key]
    return value
allowance
allowance(
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
212
213
214
215
216
217
218
219
220
221
def allowance(
    self,
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei:
    allowance_for_user: int = self.call(
        "allowance", function_params=[owner, for_address], web3=web3
    )
    return Wei(allowance_for_user)
approve
approve(
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
def approve(
    self,
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="approve",
        function_params=[
            for_address,
            amount_wei,
        ],
        tx_params=tx_params,
        web3=web3,
    )
transferFrom
transferFrom(
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
def transferFrom(
    self,
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="transferFrom",
        function_params=[sender, recipient, amount_wei],
        tx_params=tx_params,
        web3=web3,
    )
balanceOf
balanceOf(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
259
260
261
def balanceOf(self, for_address: ChecksumAddress, web3: Web3 | None = None) -> Wei:
    balance = Wei(self.call("balanceOf", [for_address], web3=web3))
    return balance
balance_of_in_tokens
balance_of_in_tokens(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken
Source code in prediction_market_agent_tooling/tools/contract.py
263
264
265
266
def balance_of_in_tokens(
    self, for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken:
    return self.balanceOf(for_address, web3=web3).as_token
SAFEContract

Bases: ContractERC20OnGnosisChain

address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0x4d18815D14fe5c3304e87B3FA18318baa5c23820"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../abis/erc20.abi.json",
    )
)
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
symbol
symbol(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
200
201
202
def symbol(self, web3: Web3 | None = None) -> str:
    symbol: str = self.call("symbol", web3=web3)
    return symbol
symbol_cached
symbol_cached(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
204
205
206
207
208
209
210
def symbol_cached(self, web3: Web3 | None = None) -> str:
    web3 = web3 or self.get_web3()
    cache_key = create_contract_method_cache_key(self.symbol, web3)
    if cache_key not in self._cache:
        self._cache[cache_key] = self.symbol(web3=web3)
    value: str = self._cache[cache_key]
    return value
allowance
allowance(
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
212
213
214
215
216
217
218
219
220
221
def allowance(
    self,
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei:
    allowance_for_user: int = self.call(
        "allowance", function_params=[owner, for_address], web3=web3
    )
    return Wei(allowance_for_user)
approve
approve(
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
def approve(
    self,
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="approve",
        function_params=[
            for_address,
            amount_wei,
        ],
        tx_params=tx_params,
        web3=web3,
    )
transferFrom
transferFrom(
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
def transferFrom(
    self,
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="transferFrom",
        function_params=[sender, recipient, amount_wei],
        tx_params=tx_params,
        web3=web3,
    )
balanceOf
balanceOf(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
259
260
261
def balanceOf(self, for_address: ChecksumAddress, web3: Web3 | None = None) -> Wei:
    balance = Wei(self.call("balanceOf", [for_address], web3=web3))
    return balance
balance_of_in_tokens
balance_of_in_tokens(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken
Source code in prediction_market_agent_tooling/tools/contract.py
263
264
265
266
def balance_of_in_tokens(
    self, for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken:
    return self.balanceOf(for_address, web3=web3).as_token
COWContract

Bases: ContractERC20OnGnosisChain

address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0x177127622c4A00F3d409B75571e12cB3c8973d3c"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../abis/erc20.abi.json",
    )
)
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
symbol
symbol(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
200
201
202
def symbol(self, web3: Web3 | None = None) -> str:
    symbol: str = self.call("symbol", web3=web3)
    return symbol
symbol_cached
symbol_cached(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
204
205
206
207
208
209
210
def symbol_cached(self, web3: Web3 | None = None) -> str:
    web3 = web3 or self.get_web3()
    cache_key = create_contract_method_cache_key(self.symbol, web3)
    if cache_key not in self._cache:
        self._cache[cache_key] = self.symbol(web3=web3)
    value: str = self._cache[cache_key]
    return value
allowance
allowance(
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
212
213
214
215
216
217
218
219
220
221
def allowance(
    self,
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei:
    allowance_for_user: int = self.call(
        "allowance", function_params=[owner, for_address], web3=web3
    )
    return Wei(allowance_for_user)
approve
approve(
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
def approve(
    self,
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="approve",
        function_params=[
            for_address,
            amount_wei,
        ],
        tx_params=tx_params,
        web3=web3,
    )
transferFrom
transferFrom(
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
def transferFrom(
    self,
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="transferFrom",
        function_params=[sender, recipient, amount_wei],
        tx_params=tx_params,
        web3=web3,
    )
balanceOf
balanceOf(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
259
260
261
def balanceOf(self, for_address: ChecksumAddress, web3: Web3 | None = None) -> Wei:
    balance = Wei(self.call("balanceOf", [for_address], web3=web3))
    return balance
balance_of_in_tokens
balance_of_in_tokens(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken
Source code in prediction_market_agent_tooling/tools/contract.py
263
264
265
266
def balance_of_in_tokens(
    self, for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken:
    return self.balanceOf(for_address, web3=web3).as_token
WrappedxDaiContract

Bases: ContractDepositableWrapperERC20OnGnosisChain

address class-attribute instance-attribute
address: ChecksumAddress = WRAPPED_XDAI_CONTRACT_ADDRESS
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../abis/depositablewrapper_erc20.abi.json",
    )
)
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
symbol
symbol(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
200
201
202
def symbol(self, web3: Web3 | None = None) -> str:
    symbol: str = self.call("symbol", web3=web3)
    return symbol
symbol_cached
symbol_cached(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
204
205
206
207
208
209
210
def symbol_cached(self, web3: Web3 | None = None) -> str:
    web3 = web3 or self.get_web3()
    cache_key = create_contract_method_cache_key(self.symbol, web3)
    if cache_key not in self._cache:
        self._cache[cache_key] = self.symbol(web3=web3)
    value: str = self._cache[cache_key]
    return value
allowance
allowance(
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
212
213
214
215
216
217
218
219
220
221
def allowance(
    self,
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei:
    allowance_for_user: int = self.call(
        "allowance", function_params=[owner, for_address], web3=web3
    )
    return Wei(allowance_for_user)
approve
approve(
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
def approve(
    self,
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="approve",
        function_params=[
            for_address,
            amount_wei,
        ],
        tx_params=tx_params,
        web3=web3,
    )
transferFrom
transferFrom(
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
def transferFrom(
    self,
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="transferFrom",
        function_params=[sender, recipient, amount_wei],
        tx_params=tx_params,
        web3=web3,
    )
balanceOf
balanceOf(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
259
260
261
def balanceOf(self, for_address: ChecksumAddress, web3: Web3 | None = None) -> Wei:
    balance = Wei(self.call("balanceOf", [for_address], web3=web3))
    return balance
balance_of_in_tokens
balance_of_in_tokens(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken
Source code in prediction_market_agent_tooling/tools/contract.py
263
264
265
266
def balance_of_in_tokens(
    self, for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken:
    return self.balanceOf(for_address, web3=web3).as_token
deposit
deposit(
    api_keys: APIKeys,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
282
283
284
285
286
287
288
289
290
291
292
293
294
295
def deposit(
    self,
    api_keys: APIKeys,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send_with_value(
        api_keys=api_keys,
        function_name="deposit",
        amount_wei=amount_wei,
        tx_params=tx_params,
        web3=web3,
    )
withdraw
withdraw(
    api_keys: APIKeys,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
def withdraw(
    self,
    api_keys: APIKeys,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="withdraw",
        function_params=[amount_wei],
        tx_params=tx_params,
        web3=web3,
    )
sDaiContract

Bases: ContractERC4626OnGnosisChain

address class-attribute instance-attribute
address: ChecksumAddress = SDAI_CONTRACT_ADDRESS
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../abis/erc4626.abi.json",
    )
)
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
symbol
symbol(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
200
201
202
def symbol(self, web3: Web3 | None = None) -> str:
    symbol: str = self.call("symbol", web3=web3)
    return symbol
symbol_cached
symbol_cached(web3: Web3 | None = None) -> str
Source code in prediction_market_agent_tooling/tools/contract.py
204
205
206
207
208
209
210
def symbol_cached(self, web3: Web3 | None = None) -> str:
    web3 = web3 or self.get_web3()
    cache_key = create_contract_method_cache_key(self.symbol, web3)
    if cache_key not in self._cache:
        self._cache[cache_key] = self.symbol(web3=web3)
    value: str = self._cache[cache_key]
    return value
allowance
allowance(
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
212
213
214
215
216
217
218
219
220
221
def allowance(
    self,
    owner: ChecksumAddress,
    for_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> Wei:
    allowance_for_user: int = self.call(
        "allowance", function_params=[owner, for_address], web3=web3
    )
    return Wei(allowance_for_user)
approve
approve(
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
def approve(
    self,
    api_keys: APIKeys,
    for_address: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="approve",
        function_params=[
            for_address,
            amount_wei,
        ],
        tx_params=tx_params,
        web3=web3,
    )
transferFrom
transferFrom(
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
def transferFrom(
    self,
    api_keys: APIKeys,
    sender: ChecksumAddress,
    recipient: ChecksumAddress,
    amount_wei: Wei,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="transferFrom",
        function_params=[sender, recipient, amount_wei],
        tx_params=tx_params,
        web3=web3,
    )
balanceOf
balanceOf(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
259
260
261
def balanceOf(self, for_address: ChecksumAddress, web3: Web3 | None = None) -> Wei:
    balance = Wei(self.call("balanceOf", [for_address], web3=web3))
    return balance
balance_of_in_tokens
balance_of_in_tokens(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken
Source code in prediction_market_agent_tooling/tools/contract.py
263
264
265
266
def balance_of_in_tokens(
    self, for_address: ChecksumAddress, web3: Web3 | None = None
) -> CollateralToken:
    return self.balanceOf(for_address, web3=web3).as_token
asset
asset(web3: Web3 | None = None) -> ChecksumAddress
Source code in prediction_market_agent_tooling/tools/contract.py
324
325
326
def asset(self, web3: Web3 | None = None) -> ChecksumAddress:
    address = self.call("asset", web3=web3)
    return Web3.to_checksum_address(address)
deposit
deposit(
    api_keys: APIKeys,
    amount_wei: Wei,
    receiver: ChecksumAddress | None = None,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
def deposit(
    self,
    api_keys: APIKeys,
    amount_wei: Wei,
    receiver: ChecksumAddress | None = None,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    receiver = receiver or api_keys.bet_from_address
    return self.send(
        api_keys=api_keys,
        function_name="deposit",
        function_params=[amount_wei, receiver],
        tx_params=tx_params,
        web3=web3,
    )
withdraw
withdraw(
    api_keys: APIKeys,
    assets_wei: Wei,
    receiver: ChecksumAddress | None = None,
    owner: ChecksumAddress | None = None,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
def withdraw(
    self,
    api_keys: APIKeys,
    assets_wei: Wei,
    receiver: ChecksumAddress | None = None,
    owner: ChecksumAddress | None = None,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    # assets_wei is the amount of assets (underlying erc20 token amount) we want to withdraw.
    receiver = receiver or api_keys.bet_from_address
    owner = owner or api_keys.bet_from_address
    return self.send(
        api_keys=api_keys,
        function_name="withdraw",
        function_params=[assets_wei, receiver, owner],
        tx_params=tx_params,
        web3=web3,
    )
withdraw_in_shares
withdraw_in_shares(
    api_keys: APIKeys,
    shares_wei: Wei,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
365
366
367
368
369
370
def withdraw_in_shares(
    self, api_keys: APIKeys, shares_wei: Wei, web3: Web3 | None = None
) -> TxReceipt:
    # shares_wei is the amount of shares we want to withdraw.
    assets = self.convertToAssets(shares_wei, web3=web3)
    return self.withdraw(api_keys=api_keys, assets_wei=assets, web3=web3)
convertToShares
convertToShares(
    assets: Wei, web3: Web3 | None = None
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
372
373
374
def convertToShares(self, assets: Wei, web3: Web3 | None = None) -> Wei:
    shares = Wei(self.call("convertToShares", [assets], web3=web3))
    return shares
convertToAssets
convertToAssets(
    shares: Wei, web3: Web3 | None = None
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
376
377
378
def convertToAssets(self, shares: Wei, web3: Web3 | None = None) -> Wei:
    assets = Wei(self.call("convertToAssets", [shares], web3=web3))
    return assets
get_asset_token_contract
get_asset_token_contract(
    web3: Web3 | None = None,
) -> (
    ContractERC20OnGnosisChain
    | ContractDepositableWrapperERC20OnGnosisChain
)
Source code in prediction_market_agent_tooling/tools/contract.py
543
544
545
546
def get_asset_token_contract(
    self, web3: Web3 | None = None
) -> ContractERC20OnGnosisChain | ContractDepositableWrapperERC20OnGnosisChain:
    return to_gnosis_chain_contract(super().get_asset_token_contract(web3=web3))
get_asset_token_balance
get_asset_token_balance(
    for_address: ChecksumAddress, web3: Web3 | None = None
) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
390
391
392
393
394
def get_asset_token_balance(
    self, for_address: ChecksumAddress, web3: Web3 | None = None
) -> Wei:
    asset_token_contract = self.get_asset_token_contract(web3=web3)
    return asset_token_contract.balanceOf(for_address, web3=web3)
deposit_asset_token
deposit_asset_token(
    asset_value: Wei,
    api_keys: APIKeys,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/tools/contract.py
396
397
398
399
400
401
402
403
404
405
406
407
408
409
def deposit_asset_token(
    self, asset_value: Wei, api_keys: APIKeys, web3: Web3 | None = None
) -> TxReceipt:
    for_address = api_keys.bet_from_address
    web3 = web3 or self.get_web3()

    asset_token_contract = self.get_asset_token_contract(web3=web3)
    # Approve vault to withdraw the erc-20 token from the user.
    asset_token_contract.approve(api_keys, self.address, asset_value, web3=web3)

    # Deposit asset token (erc20) and we will receive shares in this vault.
    receipt = self.deposit(api_keys, asset_value, for_address, web3=web3)

    return receipt
get_in_shares
get_in_shares(amount: Wei, web3: Web3 | None = None) -> Wei
Source code in prediction_market_agent_tooling/tools/contract.py
411
412
413
def get_in_shares(self, amount: Wei, web3: Web3 | None = None) -> Wei:
    # We send erc20 to the vault and receive shares in return, which can have a different value.
    return self.convertToShares(amount, web3=web3)
OmenFixedProductMarketMakerFactoryContract

Bases: ContractOnGnosisChain

abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../../abis/omen_fpmm_factory.abi.json",
    )
)
address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0x9083A2B699c0a4AD06F63580BDE2635d26a3eeF0"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
create2FixedProductMarketMaker
create2FixedProductMarketMaker(
    api_keys: APIKeys,
    condition_id: HexBytes,
    initial_funds_wei: Wei,
    collateral_token_address: ChecksumAddress,
    fee: Wei,
    distribution_hint: list[OutcomeWei] | None = None,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> tuple[
    OmenFixedProductMarketMakerCreationEvent,
    FPMMFundingAddedEvent,
    TxReceipt,
]
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
def create2FixedProductMarketMaker(
    self,
    api_keys: APIKeys,
    condition_id: HexBytes,
    initial_funds_wei: Wei,
    collateral_token_address: ChecksumAddress,
    fee: Wei,  # This is actually fee in %, 'where 100% == 1 xDai'.
    distribution_hint: list[OutcomeWei] | None = None,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> tuple[
    OmenFixedProductMarketMakerCreationEvent, FPMMFundingAddedEvent, TxReceipt
]:
    web3 = web3 or self.get_web3()
    receipt_tx = self.send(
        api_keys=api_keys,
        function_name="create2FixedProductMarketMaker",
        function_params=dict(
            saltNonce=random.randint(
                0, 1000000
            ),  # See https://github.com/protofire/omen-exchange/blob/923756c3a9ac370f8e89af8193393a53531e2c0f/app/src/services/cpk/fns.ts#L942.
            conditionalTokens=OmenConditionalTokenContract().address,
            collateralToken=collateral_token_address,
            conditionIds=[condition_id],
            fee=fee,
            initialFunds=initial_funds_wei,
            distributionHint=distribution_hint or [],
        ),
        tx_params=tx_params,
        web3=web3,
    )

    market_event_logs = (
        self.get_web3_contract(web3=web3)
        .events.FixedProductMarketMakerCreation()
        .process_receipt(receipt_tx)
    )
    market_event = OmenFixedProductMarketMakerCreationEvent(
        **market_event_logs[0]["args"]
    )
    funding_event_logs = (
        self.get_web3_contract(web3=web3)
        .events.FPMMFundingAdded()
        .process_receipt(receipt_tx)
    )
    funding_event = FPMMFundingAddedEvent(**funding_event_logs[0]["args"])

    return market_event, funding_event, receipt_tx
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
Arbitrator

Bases: str, Enum

KLEROS_511_JURORS_WITHOUT_APPEAL class-attribute instance-attribute
KLEROS_511_JURORS_WITHOUT_APPEAL = (
    "kleros_511_jurors_without_appeal"
)
KLEROS_31_JURORS_WITH_APPEAL class-attribute instance-attribute
KLEROS_31_JURORS_WITH_APPEAL = (
    "kleros_31_jurors_with_appeal"
)
DXDAO class-attribute instance-attribute
DXDAO = 'dxdao'
is_kleros property
is_kleros: bool
OmenDxDaoContract

Bases: ContractOnGnosisChain

abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../../abis/omen_dxdao.abi.json",
    )
)
address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0xFe14059344b74043Af518d12931600C0f52dF7c5"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
OmenKlerosContract

Bases: ContractOnGnosisChain

abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../../abis/omen_kleros.abi.json",
    )
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
address instance-attribute
address: ChecksumAddress
from_arbitrator staticmethod
from_arbitrator(
    arbitrator: Arbitrator,
) -> OmenKlerosContract

See https://docs.kleros.io/developer/deployment-addresses for all available addresses.

Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
@staticmethod
def from_arbitrator(arbitrator: "Arbitrator") -> "OmenKlerosContract":
    """
    See https://docs.kleros.io/developer/deployment-addresses for all available addresses.
    """
    if arbitrator == Arbitrator.KLEROS_511_JURORS_WITHOUT_APPEAL:
        address = "0xe40DD83a262da3f56976038F1554Fe541Fa75ecd"

    elif arbitrator == Arbitrator.KLEROS_31_JURORS_WITH_APPEAL:
        address = "0x5562Ac605764DC4039fb6aB56a74f7321396Cdf2"

    else:
        raise ValueError(f"Unsupported arbitrator: {arbitrator=}")

    return OmenKlerosContract(address=Web3.to_checksum_address(address))
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
OmenRealitioContract

Bases: ContractOnGnosisChain

abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../../abis/omen_realitio.abi.json",
    )
)
address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0x79e32aE03fb27B07C89c0c568F80287C01ca2E57"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
get_arbitrator_contract staticmethod
get_arbitrator_contract(
    arbitrator: Arbitrator,
) -> ContractOnGnosisChain
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
605
606
607
608
609
610
611
612
613
@staticmethod
def get_arbitrator_contract(
    arbitrator: Arbitrator,
) -> ContractOnGnosisChain:
    if arbitrator.is_kleros:
        return OmenKlerosContract.from_arbitrator(arbitrator)
    if arbitrator == Arbitrator.DXDAO:
        return OmenDxDaoContract()
    raise ValueError(f"Unknown arbitrator: {arbitrator}")
askQuestion
askQuestion(
    api_keys: APIKeys,
    question: str,
    category: str,
    outcomes: Sequence[str],
    language: str,
    arbitrator: Arbitrator,
    opening: DatetimeUTC,
    timeout: timedelta,
    nonce: int | None = None,
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> RealitioLogNewQuestionEvent

After the question is created, you can find it at https://reality.eth.link/app/#!/creator/{from_address}.

Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
def askQuestion(
    self,
    api_keys: APIKeys,
    question: str,
    category: str,
    outcomes: t.Sequence[str],
    language: str,
    arbitrator: Arbitrator,
    opening: DatetimeUTC,
    timeout: timedelta,
    nonce: int | None = None,
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> RealitioLogNewQuestionEvent:
    """
    After the question is created, you can find it at https://reality.eth.link/app/#!/creator/{from_address}.
    """
    web3 = web3 or self.get_web3()
    arbitrator_contract_address = self.get_arbitrator_contract(arbitrator).address
    # See https://realitio.github.io/docs/html/contracts.html#templates
    # for possible template ids and how to format the question.
    template_id = 2
    realitio_question = format_realitio_question(
        question=question,
        outcomes=outcomes,
        category=category,
        language=language,
        template_id=template_id,
    )
    receipt_tx = self.send(
        api_keys=api_keys,
        function_name="askQuestion",
        function_params=dict(
            template_id=template_id,
            question=realitio_question,
            arbitrator=arbitrator_contract_address,
            timeout=int(timeout.total_seconds()),
            opening_ts=int(opening.timestamp()),
            nonce=(
                nonce if nonce is not None else random.randint(0, 1000000)
            ),  # Two equal questions need to have different nonces.
        ),
        tx_params=tx_params,
        web3=web3,
    )

    event_logs = (
        self.get_web3_contract(web3=web3)
        .events.LogNewQuestion()
        .process_receipt(receipt_tx)
    )
    question_event = RealitioLogNewQuestionEvent(**event_logs[0]["args"])

    return question_event
submitAnswer
submitAnswer(
    api_keys: APIKeys,
    question_id: HexBytes,
    answer: HexBytes,
    bond: xDaiWei,
    max_previous: xDaiWei | None = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
def submitAnswer(
    self,
    api_keys: APIKeys,
    question_id: HexBytes,
    answer: HexBytes,
    bond: xDaiWei,
    max_previous: xDaiWei | None = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    if max_previous is None:
        # If not provided, defaults to 0, which means no checking,
        # same as on Omen website: https://github.com/protofire/omen-exchange/blob/763d9c9d05ebf9edacbc1dbaa561aa5d08813c0f/app/src/services/realitio.ts#L363.
        max_previous = xDaiWei(0)

    return self.send_with_value(
        api_keys=api_keys,
        function_name="submitAnswer",
        function_params=dict(
            question_id=question_id,
            answer=answer,
            max_previous=max_previous,
        ),
        amount_wei=bond.as_wei,
        web3=web3,
    )
submit_answer
submit_answer(
    api_keys: APIKeys,
    question_id: HexBytes,
    outcome_index: int,
    bond: xDaiWei,
    max_previous: xDaiWei | None = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
def submit_answer(
    self,
    api_keys: APIKeys,
    question_id: HexBytes,
    outcome_index: int,
    bond: xDaiWei,
    max_previous: xDaiWei | None = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    # Normalise the answer to lowercase, to match Enum values as [YES, NO] against outcomes as ["Yes", "No"].

    return self.submitAnswer(
        api_keys=api_keys,
        question_id=question_id,
        answer=int_to_hexbytes(
            outcome_index
        ),  # Contract's method expects answer index in bytes.
        bond=bond,
        max_previous=max_previous,
        web3=web3,
    )
submit_answer_invalid
submit_answer_invalid(
    api_keys: APIKeys,
    question_id: HexBytes,
    bond: xDaiWei,
    max_previous: xDaiWei | None = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
def submit_answer_invalid(
    self,
    api_keys: APIKeys,
    question_id: HexBytes,
    bond: xDaiWei,
    max_previous: xDaiWei | None = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.submitAnswer(
        api_keys=api_keys,
        question_id=question_id,
        answer=INVALID_ANSWER_HEX_BYTES,
        bond=bond,
        max_previous=max_previous,
        web3=web3,
    )
claimWinnings
claimWinnings(
    api_keys: APIKeys,
    question_id: HexBytes,
    history_hashes: list[HexBytes],
    addresses: list[ChecksumAddress],
    bonds: list[xDaiWei],
    answers: list[HexBytes],
    tx_params: Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
def claimWinnings(
    self,
    api_keys: APIKeys,
    question_id: HexBytes,
    history_hashes: list[HexBytes],
    addresses: list[ChecksumAddress],
    bonds: list[xDaiWei],
    answers: list[HexBytes],
    tx_params: t.Optional[TxParams] = None,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="claimWinnings",
        function_params=dict(
            question_id=question_id,
            history_hashes=history_hashes,
            addrs=addresses,
            bonds=bonds,
            answers=answers,
        ),
        tx_params=tx_params,
        web3=web3,
    )
balanceOf
balanceOf(
    from_address: ChecksumAddress, web3: Web3 | None = None
) -> xDaiWei
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
760
761
762
763
764
765
766
def balanceOf(
    self,
    from_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> xDaiWei:
    balance = xDaiWei(self.call("balanceOf", [from_address], web3=web3))
    return balance
withdraw
withdraw(
    api_keys: APIKeys, web3: Web3 | None = None
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
768
769
770
771
772
773
def withdraw(
    self,
    api_keys: APIKeys,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(api_keys=api_keys, function_name="withdraw", web3=web3)
getOpeningTS
getOpeningTS(
    question_id: HexBytes, web3: Web3 | None = None
) -> int
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
775
776
777
778
779
780
781
782
783
784
785
def getOpeningTS(
    self,
    question_id: HexBytes,
    web3: Web3 | None = None,
) -> int:
    ts: int = self.call(
        function_name="getOpeningTS",
        function_params=[question_id],
        web3=web3,
    )
    return ts
getFinalizeTS
getFinalizeTS(
    question_id: HexBytes, web3: Web3 | None = None
) -> int
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
787
788
789
790
791
792
793
794
795
796
797
def getFinalizeTS(
    self,
    question_id: HexBytes,
    web3: Web3 | None = None,
) -> int:
    ts: int = self.call(
        function_name="getFinalizeTS",
        function_params=[question_id],
        web3=web3,
    )
    return ts
isFinalized
isFinalized(
    question_id: HexBytes, web3: Web3 | None = None
) -> bool
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
799
800
801
802
803
804
805
806
807
808
809
def isFinalized(
    self,
    question_id: HexBytes,
    web3: Web3 | None = None,
) -> bool:
    is_finalized: bool = self.call(
        function_name="isFinalized",
        function_params=[question_id],
        web3=web3,
    )
    return is_finalized
isPendingArbitration
isPendingArbitration(
    question_id: HexBytes, web3: Web3 | None = None
) -> bool
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
811
812
813
814
815
816
817
818
819
820
821
def isPendingArbitration(
    self,
    question_id: HexBytes,
    web3: Web3 | None = None,
) -> bool:
    is_pending_arbitration: bool = self.call(
        function_name="isPendingArbitration",
        function_params=[question_id],
        web3=web3,
    )
    return is_pending_arbitration
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
OmenAgentResultMappingContract

Bases: ContractOnGnosisChain

abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../../abis/omen_agentresultmapping.abi.json",
    )
)
address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0x260E1077dEA98e738324A6cEfB0EE9A272eD471a"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
get_predictions
get_predictions(
    market_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> list[ContractPrediction]
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
838
839
840
841
842
843
844
845
846
def get_predictions(
    self,
    market_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> list[ContractPrediction]:
    prediction_tuples = self.call(
        "getPredictions", function_params=[market_address], web3=web3
    )
    return [ContractPrediction.from_tuple(p) for p in prediction_tuples]
add_prediction
add_prediction(
    api_keys: APIKeys,
    market_address: ChecksumAddress,
    prediction: ContractPrediction,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
848
849
850
851
852
853
854
855
856
857
858
859
860
def add_prediction(
    self,
    api_keys: APIKeys,
    market_address: ChecksumAddress,
    prediction: ContractPrediction,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="addPrediction",
        function_params=[market_address, prediction.model_dump(by_alias=True)],
        web3=web3,
    )
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
OmenThumbnailMapping

Bases: ContractOnGnosisChain

abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../../abis/omen_thumbnailmapping.abi.json",
    )
)
address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0xe0cf08311F03850497B0ed6A2cf067f1750C3eFc"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
construct_ipfs_url staticmethod
construct_ipfs_url(ipfs_hash: IPFSCIDVersion0) -> str
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
875
876
877
@staticmethod
def construct_ipfs_url(ipfs_hash: IPFSCIDVersion0) -> str:
    return f"https://ipfs.io/ipfs/{ipfs_hash}"
get
get(
    market_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> IPFSCIDVersion0 | None
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
879
880
881
882
883
884
885
886
887
def get(
    self,
    market_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> IPFSCIDVersion0 | None:
    hash_bytes = HexBytes(
        self.call("get", function_params=[market_address], web3=web3)
    )
    return byte32_to_ipfscidv0(hash_bytes) if hash_bytes != ZERO_BYTES else None
get_url
get_url(
    market_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> str | None
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
889
890
891
892
893
894
895
def get_url(
    self,
    market_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> str | None:
    hash_ = self.get(market_address, web3)
    return self.construct_ipfs_url(hash_) if hash_ is not None else None
set
set(
    api_keys: APIKeys,
    market_address: ChecksumAddress,
    image_hash: IPFSCIDVersion0,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
897
898
899
900
901
902
903
904
905
906
907
908
909
def set(
    self,
    api_keys: APIKeys,
    market_address: ChecksumAddress,
    image_hash: IPFSCIDVersion0,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="set",
        function_params=[market_address, ipfscidv0_to_byte32(image_hash)],
        web3=web3,
    )
remove
remove(
    api_keys: APIKeys,
    market_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
911
912
913
914
915
916
917
918
919
920
921
922
def remove(
    self,
    api_keys: APIKeys,
    market_address: ChecksumAddress,
    web3: Web3 | None = None,
) -> TxReceipt:
    return self.send(
        api_keys=api_keys,
        function_name="remove",
        function_params=[market_address],
        web3=web3,
    )
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
CollateralTokenChoice

Bases: str, Enum

wxdai class-attribute instance-attribute
wxdai = 'wxdai'
sdai class-attribute instance-attribute
sdai = 'sdai'
gno class-attribute instance-attribute
gno = 'gno'
metri_super_user class-attribute instance-attribute
metri_super_user = 'metri_super_user'
build_parent_collection_id
build_parent_collection_id() -> HexStr
Source code in prediction_market_agent_tooling/markets/omen/omen_contracts.py
98
99
def build_parent_collection_id() -> HexStr:
    return HASH_ZERO  # Taken from Olas

omen_resolving

claim_bonds_on_realitio_questions
claim_bonds_on_realitio_questions(
    api_keys: APIKeys,
    questions: list[RealityQuestion],
    auto_withdraw: bool,
    web3: Web3 | None = None,
    skip_failed: bool = False,
) -> list[HexBytes]
Source code in prediction_market_agent_tooling/markets/omen/omen_resolving.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def claim_bonds_on_realitio_questions(
    api_keys: APIKeys,
    questions: list[RealityQuestion],
    auto_withdraw: bool,
    web3: Web3 | None = None,
    skip_failed: bool = False,
) -> list[HexBytes]:
    claimed_questions: list[HexBytes] = []

    for idx, question in enumerate(questions):
        logger.info(
            f"[{idx+1} / {len(questions)}] Claiming bond for {question.questionId=} {question.url=}"
        )
        try:
            claim_bonds_on_realitio_question(
                api_keys, question, auto_withdraw=auto_withdraw, web3=web3
            )
            claimed_questions.append(question.questionId)
        except Exception as e:
            if not skip_failed:
                raise e
            logger.warning(
                f"Failed to claim bond for {question.url=}, {question.questionId=}: {e}"
            )

    return claimed_questions
claim_bonds_on_realitio_question
claim_bonds_on_realitio_question(
    api_keys: APIKeys,
    question: RealityQuestion,
    auto_withdraw: bool,
    web3: Web3 | None = None,
) -> None
Source code in prediction_market_agent_tooling/markets/omen/omen_resolving.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
def claim_bonds_on_realitio_question(
    api_keys: APIKeys,
    question: RealityQuestion,
    auto_withdraw: bool,
    web3: Web3 | None = None,
) -> None:
    public_key = api_keys.bet_from_address
    realitio_contract = OmenRealitioContract()

    # Get all answers for the question.
    responses = OmenSubgraphHandler().get_responses(
        limit=None, question_id=question.questionId
    )

    # They need to be processed in order.
    responses = sorted(responses, key=lambda x: x.timestamp)

    if not responses:
        raise ValueError(f"No answers found for {question.questionId.hex()=}")

    if responses[-1].question.historyHash == ZERO_BYTES:
        raise ValueError(f"Already claimed {question.questionId.hex()=}.")

    history_hashes: list[HexBytes] = []
    addresses: list[ChecksumAddress] = []
    bonds: list[xDaiWei] = []
    answers: list[HexBytes] = []

    # Caller must provide the answer history, in reverse order.
    # See https://gnosisscan.io/address/0x79e32aE03fb27B07C89c0c568F80287C01ca2E57#code#L625 for the `claimWinnings` logic.
    reversed_responses = list(reversed(responses))

    for i, response in enumerate(reversed_responses):
        # second-last-to-first, the hash of each history entry. (Final one should be empty).
        if i == len(reversed_responses) - 1:
            history_hashes.append(ZERO_BYTES)
        else:
            history_hashes.append(reversed_responses[i + 1].historyHash)

        # last-to-first, the address of each answerer or commitment sender
        addresses.append(Web3.to_checksum_address(response.user))
        # last-to-first, the bond supplied with each answer or commitment
        bonds.append(response.bond)
        # last-to-first, each answer supplied, or commitment ID if the answer was supplied with commit->reveal
        answers.append(response.answer)

    realitio_contract.claimWinnings(
        api_keys=api_keys,
        question_id=question.questionId,
        history_hashes=history_hashes,
        addresses=addresses,
        bonds=bonds,
        answers=answers,
        web3=web3,
    )

    current_balance = realitio_contract.balanceOf(public_key, web3=web3)
    # Keeping balance on Realitio is not useful, so it's recommended to just withdraw it.
    if current_balance > 0 and auto_withdraw:
        logger.info(
            f"Withdrawing remaining balance {current_balance.as_xdai} xDai from Realitio."
        )
        realitio_contract.withdraw(api_keys, web3=web3)
finalize_markets
finalize_markets(
    api_keys: APIKeys,
    markets_with_resolutions: list[
        tuple[OmenMarket, Resolution | None]
    ],
    realitio_bond: xDai,
    wait_n_days_before_invalid: int = 30,
    web3: Web3 | None = None,
) -> list[HexAddress]
Source code in prediction_market_agent_tooling/markets/omen/omen_resolving.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
def finalize_markets(
    api_keys: APIKeys,
    markets_with_resolutions: list[tuple[OmenMarket, Resolution | None]],
    realitio_bond: xDai,
    wait_n_days_before_invalid: int = 30,
    web3: Web3 | None = None,
) -> list[HexAddress]:
    finalized_markets: list[HexAddress] = []

    for idx, (market, resolution) in enumerate(markets_with_resolutions):
        logger.info(
            f"[{idx+1} / {len(markets_with_resolutions)}] Looking into {market.url=} {market.question_title=}"
        )

        # If we don't have enough of xDai for bond, try to get it from the keeping token.
        send_keeping_token_to_eoa_xdai(
            api_keys=api_keys,
            min_required_balance=realitio_bond + MINIMUM_NATIVE_TOKEN_IN_EOA_FOR_FEES,
            web3=web3,
        )

        closed_before_days = (utcnow() - market.close_time).days

        if resolution is None:
            if closed_before_days > wait_n_days_before_invalid:
                logger.warning(
                    f"Finalizing as invalid, market closed before {closed_before_days} days: {market.url=}"
                )
                omen_submit_invalid_answer_market_tx(
                    api_keys,
                    market,
                    realitio_bond,
                    web3=web3,
                )

            else:
                logger.warning(
                    f"Skipping, no resolution provided, market closed before {closed_before_days} days: {market.url=}"
                )

        elif resolution.invalid:
            logger.warning(
                f"Invalid resolution found, {resolution=}, for {market.url=}, finalizing as invalid."
            )
            omen_submit_invalid_answer_market_tx(
                api_keys,
                market,
                realitio_bond,
                web3=web3,
            )

        else:
            logger.info(f"Found resolution {resolution=} for {market.url=}")
            omen_submit_answer_market_tx(
                api_keys,
                market,
                resolution,
                realitio_bond,
                web3=web3,
            )
            finalized_markets.append(market.id)
            logger.info(f"Finalized {market.url=}")

    return finalized_markets
resolve_markets
resolve_markets(
    api_keys: APIKeys,
    markets: list[OmenMarket],
    web3: Web3 | None = None,
) -> list[HexAddress]
Source code in prediction_market_agent_tooling/markets/omen/omen_resolving.py
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
def resolve_markets(
    api_keys: APIKeys,
    markets: list[OmenMarket],
    web3: Web3 | None = None,
) -> list[HexAddress]:
    resolved_markets: list[HexAddress] = []

    for idx, market in enumerate(markets):
        logger.info(
            f"[{idx+1} / {len(markets)}] Resolving {market.url=} {market.question_title=}"
        )
        omen_resolve_market_tx(api_keys, market, web3=web3)
        resolved_markets.append(market.id)

    return resolved_markets
omen_submit_answer_market_tx
omen_submit_answer_market_tx(
    api_keys: APIKeys,
    market: OmenMarket,
    resolution: Resolution,
    bond: xDai,
    web3: Web3 | None = None,
) -> None

After the answer is submitted, there is waiting period where the answer can be challenged by others. And after the period is over, you need to resolve the market using omen_resolve_market_tx.

Source code in prediction_market_agent_tooling/markets/omen/omen_resolving.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
def omen_submit_answer_market_tx(
    api_keys: APIKeys,
    market: OmenMarket,
    resolution: Resolution,
    bond: xDai,
    web3: Web3 | None = None,
) -> None:
    """
    After the answer is submitted, there is waiting period where the answer can be challenged by others.
    And after the period is over, you need to resolve the market using `omen_resolve_market_tx`.
    """
    realitio_contract = OmenRealitioContract()
    outcome_matching_market = check_not_none(
        resolution.find_outcome_matching_market(market.outcomes)
    )
    outcome_index = market.outcomes.index(outcome_matching_market)
    realitio_contract.submit_answer(
        api_keys=api_keys,
        question_id=market.question.id,
        outcome_index=outcome_index,
        bond=bond.as_xdai_wei,
        web3=web3,
    )
omen_submit_invalid_answer_market_tx
omen_submit_invalid_answer_market_tx(
    api_keys: APIKeys,
    market: OmenMarket,
    bond: xDai,
    web3: Web3 | None = None,
) -> None

After the answer is submitted, there is waiting period where the answer can be challenged by others. And after the period is over, you need to resolve the market using omen_resolve_market_tx.

Source code in prediction_market_agent_tooling/markets/omen/omen_resolving.py
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
def omen_submit_invalid_answer_market_tx(
    api_keys: APIKeys,
    market: OmenMarket,
    bond: xDai,
    web3: Web3 | None = None,
) -> None:
    """
    After the answer is submitted, there is waiting period where the answer can be challenged by others.
    And after the period is over, you need to resolve the market using `omen_resolve_market_tx`.
    """
    realitio_contract = OmenRealitioContract()
    realitio_contract.submit_answer_invalid(
        api_keys=api_keys,
        question_id=market.question.id,
        bond=bond.as_xdai_wei,
        web3=web3,
    )
omen_resolve_market_tx
omen_resolve_market_tx(
    api_keys: APIKeys,
    market: OmenMarket,
    web3: Web3 | None = None,
) -> None

Market can be resolved after the answer if finalized on Reality.

Source code in prediction_market_agent_tooling/markets/omen/omen_resolving.py
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
def omen_resolve_market_tx(
    api_keys: APIKeys,
    market: OmenMarket,
    web3: Web3 | None = None,
) -> None:
    """
    Market can be resolved after the answer if finalized on Reality.
    """
    oracle_contract = OmenOracleContract()
    try:
        oracle_contract.resolve(
            api_keys=api_keys,
            question_id=market.question.id,
            template_id=market.question.templateId,
            question_raw=market.question.question_raw,
            n_outcomes=market.question.n_outcomes,
            web3=web3,
        )
    except BaseException as e:
        e = extract_error_from_retry_error(e)
        if "condition not prepared or found" in str(e):
            # We can't do anything about these, so just skip them with warning.
            logger.warning(
                f"Market {market.url=} not resolved, because `condition not prepared or found`, skipping."
            )
        elif "payout denominator already set" in str(e):
            # We can just skip, it's been resolved already.
            logger.info(f"Market {market.url=} is already resolved.")
        else:
            raise
find_resolution_on_other_markets
find_resolution_on_other_markets(
    market: OmenMarket,
) -> Resolution | None
Source code in prediction_market_agent_tooling/markets/omen/omen_resolving.py
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
def find_resolution_on_other_markets(market: OmenMarket) -> Resolution | None:
    resolution: Resolution | None = None

    for market_type in MarketType:
        match market_type:
            case MarketType.OMEN:
                # We are going to resolve it on Omen, so we can't find the answer there.
                continue

            case MarketType.MANIFOLD:
                logger.info(f"Looking on Manifold for {market.question_title=}")
                resolution = find_resolution_on_manifold(market.question_title)

            # TODO: Uncomment after https://github.com/gnosis/prediction-market-agent-tooling/issues/459 is done.
            # case MarketType.POLYMARKET:
            #     logger.info(f"Looking on Polymarket for {market.question_title=}")
            #     resolution = find_resolution_on_polymarket(market.question_title)

            case _:
                logger.warning(
                    f"Unknown market type {market_type} in replication resolving."
                )
                continue

        if resolution is not None:
            return resolution

    return resolution

omen_subgraph_handler

SAFE_COLLATERAL_TOKENS module-attribute
SAFE_COLLATERAL_TOKENS = (
    WrappedxDaiContract(),
    sDaiContract(),
    GNOContract(),
    WETHContract(),
    EUReContract(),
    SAFEContract(),
    COWContract(),
)
SAFE_COLLATERAL_TOKENS_ADDRESSES module-attribute
SAFE_COLLATERAL_TOKENS_ADDRESSES = tuple(
    address for contract in SAFE_COLLATERAL_TOKENS
)
OmenSubgraphHandler
OmenSubgraphHandler()

Bases: BaseSubgraphHandler

Class responsible for handling interactions with Omen subgraphs (trades, conditionalTokens).

Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
def __init__(self) -> None:
    super().__init__()

    # Load the subgraph
    self.trades_subgraph = self.sg.load_subgraph(
        self.OMEN_TRADES_SUBGRAPH.format(
            graph_api_key=self.keys.graph_api_key.get_secret_value()
        )
    )
    self.conditional_tokens_subgraph = self.sg.load_subgraph(
        self.CONDITIONAL_TOKENS_SUBGRAPH.format(
            graph_api_key=self.keys.graph_api_key.get_secret_value()
        )
    )
    self.realityeth_subgraph = self.sg.load_subgraph(
        self.REALITYETH_GRAPH_URL.format(
            graph_api_key=self.keys.graph_api_key.get_secret_value()
        )
    )
    self.omen_image_mapping_subgraph = self.sg.load_subgraph(
        self.OMEN_IMAGE_MAPPING_GRAPH_URL.format(
            graph_api_key=self.keys.graph_api_key.get_secret_value()
        )
    )

    self.omen_agent_result_mapping_subgraph = self.sg.load_subgraph(
        self.OMEN_AGENT_RESULT_MAPPING_GRAPH_URL.format(
            graph_api_key=self.keys.graph_api_key.get_secret_value()
        )
    )
OMEN_TRADES_SUBGRAPH class-attribute instance-attribute
OMEN_TRADES_SUBGRAPH = "https://gateway-arbitrum.network.thegraph.com/api/{graph_api_key}/subgraphs/id/9fUVQpFwzpdWS9bq5WkAnmKbNNcoBwatMR4yZq81pbbz"
CONDITIONAL_TOKENS_SUBGRAPH class-attribute instance-attribute
CONDITIONAL_TOKENS_SUBGRAPH = "https://gateway-arbitrum.network.thegraph.com/api/{graph_api_key}/subgraphs/id/7s9rGBffUTL8kDZuxvvpuc46v44iuDarbrADBFw5uVp2"
REALITYETH_GRAPH_URL class-attribute instance-attribute
REALITYETH_GRAPH_URL = "https://gateway-arbitrum.network.thegraph.com/api/{graph_api_key}/subgraphs/id/E7ymrCnNcQdAAgLbdFWzGE5mvr5Mb5T9VfT43FqA7bNh"
OMEN_IMAGE_MAPPING_GRAPH_URL class-attribute instance-attribute
OMEN_IMAGE_MAPPING_GRAPH_URL = "https://gateway-arbitrum.network.thegraph.com/api/{graph_api_key}/subgraphs/id/EWN14ciGK53PpUiKSm7kMWQ6G4iz3tDrRLyZ1iXMQEdu"
OMEN_AGENT_RESULT_MAPPING_GRAPH_URL class-attribute instance-attribute
OMEN_AGENT_RESULT_MAPPING_GRAPH_URL = "https://gateway-arbitrum.network.thegraph.com/api/{graph_api_key}/subgraphs/id/J6bJEnbqJpAvNyQE8i58M9mKF4zqo33BEJRdnXmqa6Kn"
INVALID_ANSWER class-attribute instance-attribute
INVALID_ANSWER = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
trades_subgraph instance-attribute
trades_subgraph = load_subgraph(
    format(graph_api_key=get_secret_value())
)
conditional_tokens_subgraph instance-attribute
conditional_tokens_subgraph = load_subgraph(
    format(graph_api_key=get_secret_value())
)
realityeth_subgraph instance-attribute
realityeth_subgraph = load_subgraph(
    format(graph_api_key=get_secret_value())
)
omen_image_mapping_subgraph instance-attribute
omen_image_mapping_subgraph = load_subgraph(
    format(graph_api_key=get_secret_value())
)
omen_agent_result_mapping_subgraph instance-attribute
omen_agent_result_mapping_subgraph = load_subgraph(
    format(graph_api_key=get_secret_value())
)
sg instance-attribute
sg = Subgrounds(timeout=timeout)
keys instance-attribute
keys = APIKeys()
get_omen_markets_simple
get_omen_markets_simple(
    limit: Optional[int],
    filter_by: FilterBy,
    sort_by: SortBy,
    include_categorical_markets: bool = False,
    created_after: DatetimeUTC | None = None,
    excluded_questions: set[str] | None = None,
    collateral_token_address_in: tuple[ChecksumAddress, ...]
    | None = SAFE_COLLATERAL_TOKENS_ADDRESSES,
    category: str | None = None,
    creator_in: Sequence[HexAddress] | None = None,
) -> t.List[OmenMarket]

Simplified get_omen_markets method, which allows to fetch markets based on the filter_by and sort_by values.

Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
def get_omen_markets_simple(
    self,
    limit: t.Optional[int],
    # Enumerated values for simpler usage.
    filter_by: FilterBy,
    sort_by: SortBy,
    include_categorical_markets: bool = False,
    # Additional filters, these can not be modified by the enums above.
    created_after: DatetimeUTC | None = None,
    excluded_questions: set[str] | None = None,  # question titles
    collateral_token_address_in: (
        tuple[ChecksumAddress, ...] | None
    ) = SAFE_COLLATERAL_TOKENS_ADDRESSES,
    category: str | None = None,
    creator_in: t.Sequence[HexAddress] | None = None,
) -> t.List[OmenMarket]:
    """
    Simplified `get_omen_markets` method, which allows to fetch markets based on the filter_by and sort_by values.
    """
    # These values need to be set according to the filter_by value, so they can not be passed as arguments.
    resolved: bool | None = None
    opened_after: DatetimeUTC | None = None
    liquidity_bigger_than: Wei | None = None

    if filter_by == FilterBy.RESOLVED:
        resolved = True
    elif filter_by == FilterBy.OPEN:
        # We can not use `resolved=False` + `finalized=False` here,
        # because even closed markets don't need to be resolved yet (e.g. if someone forgot to finalize the question on reality).
        opened_after = utcnow()
        # Even if the market isn't closed yet, liquidity can be withdrawn to 0, which essentially closes the market.
        liquidity_bigger_than = Wei(0)
    elif filter_by == FilterBy.NONE:
        pass
    else:
        raise ValueError(f"Unknown filter_by: {filter_by}")

    sort_direction, sort_by_field = self._build_sort_params(sort_by)

    all_markets = self.get_omen_markets(
        limit=limit,
        resolved=resolved,
        question_opened_after=opened_after,
        liquidity_bigger_than=liquidity_bigger_than,
        sort_direction=sort_direction,
        sort_by_field=sort_by_field,
        created_after=created_after,
        question_excluded_titles=excluded_questions,
        collateral_token_address_in=collateral_token_address_in,
        category=category,
        creator_in=creator_in,
        include_categorical_markets=include_categorical_markets,
    )

    return all_markets
get_omen_markets
get_omen_markets(
    limit: Optional[int],
    creator: HexAddress | None = None,
    creator_in: Sequence[HexAddress] | None = None,
    created_after: DatetimeUTC | None = None,
    question_opened_before: DatetimeUTC | None = None,
    question_opened_after: DatetimeUTC | None = None,
    question_finalized_before: DatetimeUTC | None = None,
    question_finalized_after: DatetimeUTC | None = None,
    question_with_answers: bool | None = None,
    question_pending_arbitration: bool | None = None,
    question_id: HexBytes | None = None,
    question_id_in: list[HexBytes] | None = None,
    question_current_answer_before: DatetimeUTC
    | None = None,
    question_excluded_titles: set[str] | None = None,
    resolved: bool | None = None,
    liquidity_bigger_than: Wei | None = None,
    condition_id_in: list[HexBytes] | None = None,
    id_in: list[str] | None = None,
    sort_by_field: FieldPath | None = None,
    sort_direction: str | None = None,
    collateral_token_address_in: tuple[ChecksumAddress, ...]
    | None = SAFE_COLLATERAL_TOKENS_ADDRESSES,
    category: str | None = None,
    include_categorical_markets: bool = True,
    include_scalar_markets: bool = False,
) -> t.List[OmenMarket]

Complete method to fetch Omen markets with various filters, use get_omen_markets_simple for simplified version that uses FilterBy and SortBy enums.

Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
def get_omen_markets(
    self,
    limit: t.Optional[int],
    creator: HexAddress | None = None,
    creator_in: t.Sequence[HexAddress] | None = None,
    created_after: DatetimeUTC | None = None,
    question_opened_before: DatetimeUTC | None = None,
    question_opened_after: DatetimeUTC | None = None,
    question_finalized_before: DatetimeUTC | None = None,
    question_finalized_after: DatetimeUTC | None = None,
    question_with_answers: bool | None = None,
    question_pending_arbitration: bool | None = None,
    question_id: HexBytes | None = None,
    question_id_in: list[HexBytes] | None = None,
    question_current_answer_before: DatetimeUTC | None = None,
    question_excluded_titles: set[str] | None = None,
    resolved: bool | None = None,
    liquidity_bigger_than: Wei | None = None,
    condition_id_in: list[HexBytes] | None = None,
    id_in: list[str] | None = None,
    sort_by_field: FieldPath | None = None,
    sort_direction: str | None = None,
    collateral_token_address_in: (
        tuple[ChecksumAddress, ...] | None
    ) = SAFE_COLLATERAL_TOKENS_ADDRESSES,
    category: str | None = None,
    include_categorical_markets: bool = True,
    include_scalar_markets: bool = False,
) -> t.List[OmenMarket]:
    """
    Complete method to fetch Omen  markets with various filters, use `get_omen_markets_simple` for simplified version that uses FilterBy and SortBy enums.
    """
    where_stms = self._build_where_statements(
        creator=creator,
        creator_in=creator_in,
        created_after=created_after,
        question_opened_before=question_opened_before,
        question_opened_after=question_opened_after,
        question_finalized_before=question_finalized_before,
        question_finalized_after=question_finalized_after,
        question_with_answers=question_with_answers,
        question_pending_arbitration=question_pending_arbitration,
        question_id=question_id,
        question_id_in=question_id_in,
        question_current_answer_before=question_current_answer_before,
        question_excluded_titles=question_excluded_titles,
        resolved=resolved,
        condition_id_in=condition_id_in,
        id_in=id_in,
        liquidity_bigger_than=liquidity_bigger_than,
        collateral_token_address_in=collateral_token_address_in,
        category=category,
        include_categorical_markets=include_categorical_markets,
        include_scalar_markets=include_scalar_markets,
    )

    # These values can not be set to `None`, but they can be omitted.
    optional_params = {}
    if sort_by_field is not None:
        optional_params["orderBy"] = sort_by_field
    if sort_direction is not None:
        optional_params["orderDirection"] = sort_direction

    markets = self.trades_subgraph.Query.fixedProductMarketMakers(
        first=(
            limit if limit else sys.maxsize
        ),  # if not limit, we fetch all possible markets
        where=unwrap_generic_value(where_stms),
        **optional_params,
    )

    fields = self._get_fields_for_markets(markets)

    omen_markets = self.do_query(fields=fields, pydantic_model=OmenMarket)
    return omen_markets
get_omen_market_by_market_id
get_omen_market_by_market_id(
    market_id: HexAddress, block_number: int | None = None
) -> OmenMarket
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
def get_omen_market_by_market_id(
    self, market_id: HexAddress, block_number: int | None = None
) -> OmenMarket:
    query_filters: dict[str, t.Any] = {"id": market_id.lower()}
    if block_number:
        query_filters["block"] = {"number": block_number}

    markets = self.trades_subgraph.Query.fixedProductMarketMaker(**query_filters)

    fields = self._get_fields_for_markets(markets)
    omen_markets = self.do_query(fields=fields, pydantic_model=OmenMarket)

    if len(omen_markets) != 1:
        raise ValueError(
            f"Fetched wrong number of markets. Expected 1 but got {len(omen_markets)}"
        )

    return omen_markets[0]
get_positions
get_positions(
    condition_id: HexBytes | None = None,
) -> list[OmenPosition]
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
def get_positions(
    self,
    condition_id: HexBytes | None = None,
) -> list[OmenPosition]:
    where_stms: dict[str, t.Any] = {}

    if condition_id is not None:
        where_stms["conditionIds_contains"] = [condition_id.hex()]

    positions = self.conditional_tokens_subgraph.Query.positions(
        first=sys.maxsize, where=unwrap_generic_value(where_stms)
    )
    fields = self._get_fields_for_positions(positions)
    result = self.sg.query_json(fields)
    items = self._parse_items_from_json(result)
    return [OmenPosition.model_validate(i) for i in items]
get_user_positions
get_user_positions(
    better_address: ChecksumAddress | None = None,
    user_position_id_in: list[HexBytes] | None = None,
    position_id_in: list[HexBytes] | None = None,
    total_balance_bigger_than: OutcomeWei | None = None,
) -> list[OmenUserPosition]
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
def get_user_positions(
    self,
    better_address: ChecksumAddress | None = None,
    user_position_id_in: list[HexBytes] | None = None,
    position_id_in: list[HexBytes] | None = None,
    total_balance_bigger_than: OutcomeWei | None = None,
) -> list[OmenUserPosition]:
    where_stms: dict[str, t.Any] = {
        "position_": {},
    }

    if better_address is not None:
        where_stms["user"] = better_address.lower()

    if total_balance_bigger_than is not None:
        where_stms["totalBalance_gt"] = total_balance_bigger_than

    if user_position_id_in is not None:
        where_stms["id_in"] = [x.hex() for x in user_position_id_in]

    if position_id_in is not None:
        where_stms["position_"]["positionId_in"] = [x.hex() for x in position_id_in]

    positions = self.conditional_tokens_subgraph.Query.userPositions(
        first=sys.maxsize, where=unwrap_generic_value(where_stms)
    )
    fields = self._get_fields_for_user_positions(positions)
    result = self.sg.query_json(fields)
    items = self._parse_items_from_json(result)
    return [OmenUserPosition.model_validate(i) for i in items]
get_trades
get_trades(
    limit: int | None = None,
    better_address: ChecksumAddress | None = None,
    start_time: DatetimeUTC | None = None,
    end_time: Optional[DatetimeUTC] = None,
    market_id: Optional[ChecksumAddress] = None,
    filter_by_answer_finalized_not_null: bool = False,
    type_: Literal["Buy", "Sell"] | None = None,
    market_opening_after: DatetimeUTC | None = None,
    market_resolved_before: DatetimeUTC | None = None,
    market_resolved_after: DatetimeUTC | None = None,
    collateral_amount_more_than: Wei | None = None,
    sort_by_field: FieldPath | None = None,
    sort_direction: str | None = None,
) -> list[OmenBet]
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
def get_trades(
    self,
    limit: int | None = None,
    better_address: ChecksumAddress | None = None,
    start_time: DatetimeUTC | None = None,
    end_time: t.Optional[DatetimeUTC] = None,
    market_id: t.Optional[ChecksumAddress] = None,
    filter_by_answer_finalized_not_null: bool = False,
    type_: t.Literal["Buy", "Sell"] | None = None,
    market_opening_after: DatetimeUTC | None = None,
    market_resolved_before: DatetimeUTC | None = None,
    market_resolved_after: DatetimeUTC | None = None,
    collateral_amount_more_than: Wei | None = None,
    sort_by_field: FieldPath | None = None,
    sort_direction: str | None = None,
) -> list[OmenBet]:
    if not end_time:
        end_time = utcnow()

    trade = self.trades_subgraph.FpmmTrade
    where_stms = []
    if start_time:
        where_stms.append(trade.creationTimestamp >= to_int_timestamp(start_time))
    if end_time:
        where_stms.append(trade.creationTimestamp <= to_int_timestamp(end_time))
    if type_:
        where_stms.append(trade.type == type_)
    if better_address:
        where_stms.append(trade.creator == better_address.lower())
    if market_id:
        where_stms.append(trade.fpmm == market_id.lower())
    if filter_by_answer_finalized_not_null:
        where_stms.append(trade.fpmm.answerFinalizedTimestamp != None)
    if market_opening_after is not None:
        where_stms.append(
            trade.fpmm.openingTimestamp > to_int_timestamp(market_opening_after)
        )
    if market_resolved_after is not None:
        where_stms.append(
            trade.fpmm.resolutionTimestamp > to_int_timestamp(market_resolved_after)
        )
    if market_resolved_before is not None:
        where_stms.append(
            trade.fpmm.resolutionTimestamp
            < to_int_timestamp(market_resolved_before)
        )
    if collateral_amount_more_than is not None:
        where_stms.append(
            trade.collateralAmount > collateral_amount_more_than.value
        )

    # These values can not be set to `None`, but they can be omitted.
    optional_params = {}
    if sort_by_field is not None:
        optional_params["orderBy"] = sort_by_field
    if sort_direction is not None:
        optional_params["orderDirection"] = sort_direction

    trades = self.trades_subgraph.Query.fpmmTrades(
        first=limit if limit else sys.maxsize,
        where=unwrap_generic_value(where_stms),
        **optional_params,
    )
    fields = self._get_fields_for_bets(trades)
    result = self.sg.query_json(fields)
    items = self._parse_items_from_json(result)
    return [OmenBet.model_validate(i) for i in items]
get_bets
get_bets(
    better_address: ChecksumAddress | None = None,
    start_time: DatetimeUTC | None = None,
    end_time: Optional[DatetimeUTC] = None,
    market_id: Optional[ChecksumAddress] = None,
    filter_by_answer_finalized_not_null: bool = False,
    market_opening_after: DatetimeUTC | None = None,
    market_resolved_before: DatetimeUTC | None = None,
    market_resolved_after: DatetimeUTC | None = None,
    collateral_amount_more_than: Wei | None = None,
) -> list[OmenBet]
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
def get_bets(
    self,
    better_address: ChecksumAddress | None = None,
    start_time: DatetimeUTC | None = None,
    end_time: t.Optional[DatetimeUTC] = None,
    market_id: t.Optional[ChecksumAddress] = None,
    filter_by_answer_finalized_not_null: bool = False,
    market_opening_after: DatetimeUTC | None = None,
    market_resolved_before: DatetimeUTC | None = None,
    market_resolved_after: DatetimeUTC | None = None,
    collateral_amount_more_than: Wei | None = None,
) -> list[OmenBet]:
    return self.get_trades(
        better_address=better_address,
        start_time=start_time,
        end_time=end_time,
        market_id=market_id,
        filter_by_answer_finalized_not_null=filter_by_answer_finalized_not_null,
        type_="Buy",  # We consider `bet` to be only the `Buy` trade types.
        market_opening_after=market_opening_after,
        market_resolved_before=market_resolved_before,
        market_resolved_after=market_resolved_after,
        collateral_amount_more_than=collateral_amount_more_than,
    )
get_resolved_bets
get_resolved_bets(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC | None = None,
    end_time: Optional[DatetimeUTC] = None,
    market_id: Optional[ChecksumAddress] = None,
    market_resolved_before: DatetimeUTC | None = None,
    market_resolved_after: DatetimeUTC | None = None,
) -> list[OmenBet]
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
def get_resolved_bets(
    self,
    better_address: ChecksumAddress,
    start_time: DatetimeUTC | None = None,
    end_time: t.Optional[DatetimeUTC] = None,
    market_id: t.Optional[ChecksumAddress] = None,
    market_resolved_before: DatetimeUTC | None = None,
    market_resolved_after: DatetimeUTC | None = None,
) -> list[OmenBet]:
    omen_bets = self.get_bets(
        better_address=better_address,
        start_time=start_time,
        end_time=end_time,
        market_id=market_id,
        filter_by_answer_finalized_not_null=True,
        market_resolved_before=market_resolved_before,
        market_resolved_after=market_resolved_after,
    )
    return [b for b in omen_bets if b.fpmm.is_resolved]
get_resolved_bets_with_valid_answer
get_resolved_bets_with_valid_answer(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC | None = None,
    end_time: Optional[DatetimeUTC] = None,
    market_resolved_before: DatetimeUTC | None = None,
    market_resolved_after: DatetimeUTC | None = None,
    market_id: Optional[ChecksumAddress] = None,
) -> list[OmenBet]
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
def get_resolved_bets_with_valid_answer(
    self,
    better_address: ChecksumAddress,
    start_time: DatetimeUTC | None = None,
    end_time: t.Optional[DatetimeUTC] = None,
    market_resolved_before: DatetimeUTC | None = None,
    market_resolved_after: DatetimeUTC | None = None,
    market_id: t.Optional[ChecksumAddress] = None,
) -> list[OmenBet]:
    bets = self.get_resolved_bets(
        better_address=better_address,
        start_time=start_time,
        end_time=end_time,
        market_id=market_id,
        market_resolved_before=market_resolved_before,
        market_resolved_after=market_resolved_after,
    )
    return [b for b in bets if b.fpmm.is_resolved_with_valid_answer]
get_reality_question_filters staticmethod
get_reality_question_filters(
    user: HexAddress | None,
    claimed: bool | None,
    current_answer_before: DatetimeUTC | None,
    finalized_before: DatetimeUTC | None,
    finalized_after: DatetimeUTC | None,
    with_answers: bool | None,
    pending_arbitration: bool | None,
    question_id: HexBytes | None,
    question_id_in: list[HexBytes] | None,
    opened_before: Optional[DatetimeUTC],
    opened_after: Optional[DatetimeUTC],
    excluded_titles: set[str] | None,
) -> dict[str, t.Any]

Be aware, both Omen subgraph and Reality subgraph are indexing questions, but their fields are a bit different.

Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
@staticmethod
def get_reality_question_filters(
    user: HexAddress | None,
    claimed: bool | None,
    current_answer_before: DatetimeUTC | None,
    finalized_before: DatetimeUTC | None,
    finalized_after: DatetimeUTC | None,
    with_answers: bool | None,
    pending_arbitration: bool | None,
    question_id: HexBytes | None,
    question_id_in: list[HexBytes] | None,
    opened_before: t.Optional[DatetimeUTC],
    opened_after: t.Optional[DatetimeUTC],
    excluded_titles: set[str] | None,
) -> dict[str, t.Any]:
    """
    Be aware, both Omen subgraph and Reality subgraph are indexing questions, but their fields are a bit different.
    """
    where_stms: dict[str, t.Any] = {}

    if user is not None:
        where_stms["user"] = user.lower()

    if question_id is not None:
        where_stms["questionId"] = question_id.hex()

    if claimed is not None:
        if claimed:
            where_stms["historyHash"] = ZERO_BYTES.hex()
        else:
            where_stms["historyHash_not"] = ZERO_BYTES.hex()

    if current_answer_before is not None:
        where_stms["currentAnswerTimestamp_lt"] = to_int_timestamp(
            current_answer_before
        )

    if opened_before:
        where_stms["openingTimestamp_lt"] = to_int_timestamp(opened_before)

    if opened_after:
        where_stms["openingTimestamp_gt"] = to_int_timestamp(opened_after)

    if finalized_before is not None:
        where_stms["answerFinalizedTimestamp_lt"] = to_int_timestamp(
            finalized_before
        )

    if finalized_after is not None:
        where_stms["answerFinalizedTimestamp_gt"] = to_int_timestamp(
            finalized_after
        )

    if with_answers is not None:
        if with_answers:
            where_stms["currentAnswer_not"] = None
        else:
            where_stms["currentAnswer"] = None

    if pending_arbitration is not None:
        where_stms["isPendingArbitration"] = pending_arbitration

    if question_id_in is not None:
        # Be aware: On Omen subgraph, question's `id` represents `questionId` on reality subgraph. And `id` on reality subraph is just a weird concat of multiple things from the question.
        where_stms["questionId_in"] = [x.hex() for x in question_id_in]

    if excluded_titles:
        # Be aware: This is called `title_not_in` on Omen subgraph.
        where_stms["qTitle_not_in"] = [i for i in excluded_titles]

    return where_stms
get_omen_question_filters staticmethod
get_omen_question_filters(
    current_answer_before: DatetimeUTC | None,
    finalized_before: DatetimeUTC | None,
    finalized_after: DatetimeUTC | None,
    with_answers: bool | None,
    pending_arbitration: bool | None,
    question_id: HexBytes | None,
    question_id_in: list[HexBytes] | None,
    opened_before: Optional[DatetimeUTC],
    opened_after: Optional[DatetimeUTC],
    excluded_titles: set[str] | None,
) -> dict[str, t.Any]

Be aware, both Omen subgraph and Reality subgraph are indexing questions, but their fields are a bit different.

Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
@staticmethod
def get_omen_question_filters(
    current_answer_before: DatetimeUTC | None,
    finalized_before: DatetimeUTC | None,
    finalized_after: DatetimeUTC | None,
    with_answers: bool | None,
    pending_arbitration: bool | None,
    question_id: HexBytes | None,
    question_id_in: list[HexBytes] | None,
    opened_before: t.Optional[DatetimeUTC],
    opened_after: t.Optional[DatetimeUTC],
    excluded_titles: set[str] | None,
) -> dict[str, t.Any]:
    """
    Be aware, both Omen subgraph and Reality subgraph are indexing questions, but their fields are a bit different.
    """
    where_stms: dict[str, t.Any] = {}

    if question_id is not None:
        where_stms["id"] = question_id.hex()

    if current_answer_before is not None:
        where_stms["currentAnswerTimestamp_lt"] = to_int_timestamp(
            current_answer_before
        )

    if opened_before:
        where_stms["openingTimestamp_lt"] = to_int_timestamp(opened_before)

    if opened_after:
        where_stms["openingTimestamp_gt"] = to_int_timestamp(opened_after)

    if finalized_before is not None:
        where_stms["answerFinalizedTimestamp_lt"] = to_int_timestamp(
            finalized_before
        )

    if finalized_after is not None:
        where_stms["answerFinalizedTimestamp_gt"] = to_int_timestamp(
            finalized_after
        )

    if with_answers is not None:
        if with_answers:
            where_stms["currentAnswer_not"] = None
        else:
            where_stms["currentAnswer"] = None

    if pending_arbitration is not None:
        where_stms["isPendingArbitration"] = pending_arbitration

    if question_id_in is not None:
        # Be aware: On Omen subgraph, question's `id` represents `questionId` on reality subgraph. And `id` on reality subraph is just a weird concat of multiple things from the question.
        where_stms["id_in"] = [x.hex() for x in question_id_in]

    if excluded_titles:
        # Be aware: This is called `qTitle_not_in` on Omen subgraph.
        where_stms["title_not_in"] = [i for i in excluded_titles]

    return where_stms
get_questions
get_questions(
    limit: int | None,
    user: HexAddress | None = None,
    claimed: bool | None = None,
    current_answer_before: DatetimeUTC | None = None,
    finalized_before: DatetimeUTC | None = None,
    finalized_after: DatetimeUTC | None = None,
    with_answers: bool | None = None,
    pending_arbitration: bool | None = None,
    question_id_in: list[HexBytes] | None = None,
    question_id: HexBytes | None = None,
    opened_before: DatetimeUTC | None = None,
    opened_after: DatetimeUTC | None = None,
    excluded_titles: set[str] | None = None,
) -> list[RealityQuestion]
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
def get_questions(
    self,
    limit: int | None,
    user: HexAddress | None = None,
    claimed: bool | None = None,
    current_answer_before: DatetimeUTC | None = None,
    finalized_before: DatetimeUTC | None = None,
    finalized_after: DatetimeUTC | None = None,
    with_answers: bool | None = None,
    pending_arbitration: bool | None = None,
    question_id_in: list[HexBytes] | None = None,
    question_id: HexBytes | None = None,
    opened_before: DatetimeUTC | None = None,
    opened_after: DatetimeUTC | None = None,
    excluded_titles: set[str] | None = None,
) -> list[RealityQuestion]:
    where_stms: dict[str, t.Any] = self.get_reality_question_filters(
        user=user,
        claimed=claimed,
        finalized_before=finalized_before,
        finalized_after=finalized_after,
        with_answers=with_answers,
        pending_arbitration=pending_arbitration,
        current_answer_before=current_answer_before,
        question_id_in=question_id_in,
        question_id=question_id,
        opened_before=opened_before,
        opened_after=opened_after,
        excluded_titles=excluded_titles,
    )
    questions = self.realityeth_subgraph.Query.questions(
        first=(
            limit if limit else sys.maxsize
        ),  # if not limit, we fetch all possible
        where=unwrap_generic_value(where_stms),
    )
    fields = self._get_fields_for_reality_questions(questions)
    result = self.sg.query_json(fields)
    items = self._parse_items_from_json(result)
    return [RealityQuestion.model_validate(i) for i in items]
get_answers
get_answers(question_id: HexBytes) -> list[RealityAnswer]
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
861
862
863
864
865
866
867
868
869
870
871
872
873
874
def get_answers(self, question_id: HexBytes) -> list[RealityAnswer]:
    answer = self.realityeth_subgraph.Answer
    # subgrounds complains if bytes is passed, hence we convert it to HexStr
    where_stms = [
        answer.question.questionId == question_id.hex(),
    ]

    answers = self.realityeth_subgraph.Query.answers(
        where=unwrap_generic_value(where_stms)
    )
    fields = self._get_fields_for_answers(answers)
    result = self.sg.query_json(fields)
    items = self._parse_items_from_json(result)
    return [RealityAnswer.model_validate(i) for i in items]
get_responses
get_responses(
    limit: int | None,
    user: HexAddress | None = None,
    question_user: HexAddress | None = None,
    question_claimed: bool | None = None,
    question_opened_before: Optional[DatetimeUTC] = None,
    question_opened_after: Optional[DatetimeUTC] = None,
    question_finalized_before: Optional[DatetimeUTC] = None,
    question_finalized_after: Optional[DatetimeUTC] = None,
    question_with_answers: bool | None = None,
    question_pending_arbitration: bool | None = None,
    question_id: HexBytes | None = None,
    question_id_in: list[HexBytes] | None = None,
    question_current_answer_before: DatetimeUTC
    | None = None,
    question_excluded_titles: set[str] | None = None,
) -> list[RealityResponse]
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
def get_responses(
    self,
    limit: int | None,
    user: HexAddress | None = None,
    question_user: HexAddress | None = None,
    question_claimed: bool | None = None,
    question_opened_before: t.Optional[DatetimeUTC] = None,
    question_opened_after: t.Optional[DatetimeUTC] = None,
    question_finalized_before: t.Optional[DatetimeUTC] = None,
    question_finalized_after: t.Optional[DatetimeUTC] = None,
    question_with_answers: bool | None = None,
    question_pending_arbitration: bool | None = None,
    question_id: HexBytes | None = None,
    question_id_in: list[HexBytes] | None = None,
    question_current_answer_before: DatetimeUTC | None = None,
    question_excluded_titles: set[str] | None = None,
) -> list[RealityResponse]:
    where_stms: dict[str, t.Any] = {}

    if user is not None:
        where_stms["user"] = user.lower()

    where_stms["question_"] = self.get_reality_question_filters(
        user=question_user,
        question_id=question_id,
        claimed=question_claimed,
        opened_before=question_opened_before,
        opened_after=question_opened_after,
        finalized_before=question_finalized_before,
        finalized_after=question_finalized_after,
        with_answers=question_with_answers,
        pending_arbitration=question_pending_arbitration,
        current_answer_before=question_current_answer_before,
        question_id_in=question_id_in,
        excluded_titles=question_excluded_titles,
    )

    responses = self.realityeth_subgraph.Query.responses(
        first=(
            limit if limit else sys.maxsize
        ),  # if not limit, we fetch all possible
        where=unwrap_generic_value(where_stms),
    )
    fields = self._get_fields_for_responses(responses)
    result = self.sg.query_json(fields)
    items = self._parse_items_from_json(result)
    return [RealityResponse.model_validate(i) for i in items]
get_markets_from_all_user_positions
get_markets_from_all_user_positions(
    user_positions: list[OmenUserPosition],
) -> list[OmenMarket]
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
924
925
926
927
928
929
930
931
932
933
def get_markets_from_all_user_positions(
    self, user_positions: list[OmenUserPosition]
) -> list[OmenMarket]:
    unique_condition_ids: list[HexBytes] = list(
        set(sum([u.position.conditionIds for u in user_positions], []))
    )
    markets = self.get_omen_markets(
        limit=sys.maxsize, condition_id_in=unique_condition_ids
    )
    return markets
get_market_from_user_position
get_market_from_user_position(
    user_position: OmenUserPosition,
) -> OmenMarket

Markets and user positions are uniquely connected via condition_ids

Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
935
936
937
938
939
940
941
942
943
944
945
def get_market_from_user_position(
    self, user_position: OmenUserPosition
) -> OmenMarket:
    """Markets and user positions are uniquely connected via condition_ids"""
    condition_ids = user_position.position.conditionIds
    markets = self.get_omen_markets(limit=1, condition_id_in=condition_ids)
    if len(markets) != 1:
        raise ValueError(
            f"Incorrect number of markets fetched {len(markets)}, expected 1."
        )
    return markets[0]
get_market_image_url
get_market_image_url(market_id: HexAddress) -> str | None
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
947
948
949
950
951
952
953
954
955
956
957
def get_market_image_url(self, market_id: HexAddress) -> str | None:
    image = self.omen_image_mapping_subgraph.Query.omenThumbnailMapping(
        id=market_id.lower()
    )
    fields = [image.id, image.image_hash]
    result = self.sg.query_json(fields)
    items = self._parse_items_from_json(result)
    if not items:
        return None
    parsed = byte32_to_ipfscidv0(HexBytes(items[0]["image_hash"]))
    return OmenThumbnailMapping.construct_ipfs_url(parsed)
get_market_image
get_market_image(market_id: HexAddress) -> ImageType | None
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
959
960
961
962
963
964
965
def get_market_image(self, market_id: HexAddress) -> ImageType | None:
    image_url = self.get_market_image_url(market_id)
    return (
        Image.open(requests.get(image_url, stream=True).raw)  # type: ignore[arg-type]
        if image_url is not None
        else None
    )
get_agent_results_for_market
get_agent_results_for_market(
    market_id: HexAddress | None = None,
) -> list[ContractPrediction]
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
def get_agent_results_for_market(
    self, market_id: HexAddress | None = None
) -> list[ContractPrediction]:
    where_stms = {}
    if market_id:
        where_stms["marketAddress"] = market_id.lower()

    prediction_added = (
        self.omen_agent_result_mapping_subgraph.Query.predictionAddeds(
            where=unwrap_generic_value(where_stms),
            orderBy="blockNumber",
            orderDirection="asc",
        )
    )
    fields = [
        prediction_added.publisherAddress,
        prediction_added.ipfsHash,
        prediction_added.txHashes,
        prediction_added.estimatedProbabilityBps,
    ]
    result = self.sg.query_json(fields)
    items = self._parse_items_from_json(result)
    if not items:
        return []
    return [ContractPrediction.model_validate(i) for i in items]
get_agent_results_for_bet
get_agent_results_for_bet(
    bet: OmenBet,
) -> ContractPrediction | None
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
def get_agent_results_for_bet(self, bet: OmenBet) -> ContractPrediction | None:
    results = [
        result
        for result in self.get_agent_results_for_market(bet.fpmm.id)
        if bet.transactionHash in result.tx_hashes
    ]

    if not results:
        return None
    elif len(results) > 1:
        raise RuntimeError("Multiple results found for a single bet.")

    return results[0]
do_query
do_query(
    fields: list[FieldPath], pydantic_model: Type[T]
) -> list[T]
Source code in prediction_market_agent_tooling/markets/base_subgraph_handler.py
47
48
49
50
51
def do_query(self, fields: list[FieldPath], pydantic_model: t.Type[T]) -> list[T]:
    result = self.sg.query_json(fields)
    items = self._parse_items_from_json(result)
    models = [pydantic_model.model_validate(i) for i in items]
    return models
get_omen_market_by_market_id_cached
get_omen_market_by_market_id_cached(
    market_id: HexAddress, block_number: int
) -> OmenMarket
Source code in prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py
1008
1009
1010
1011
1012
1013
1014
1015
@persistent_inmemory_cache
def get_omen_market_by_market_id_cached(
    market_id: HexAddress,
    block_number: int,  # Force `block_number` to be provided, because `latest` block constantly updates.
) -> OmenMarket:
    return OmenSubgraphHandler().get_omen_market_by_market_id(
        market_id, block_number=block_number
    )

polymarket

api

POLYMARKET_API_BASE_URL module-attribute
POLYMARKET_API_BASE_URL = 'https://clob.polymarket.com/'
MARKETS_LIMIT module-attribute
MARKETS_LIMIT = 100
get_polymarkets
get_polymarkets(
    limit: int,
    with_rewards: bool = False,
    next_cursor: str | None = None,
) -> MarketsEndpointResponse
Source code in prediction_market_agent_tooling/markets/polymarket/api.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@tenacity.retry(
    stop=tenacity.stop_after_attempt(5),
    wait=tenacity.wait_chain(*[tenacity.wait_fixed(n) for n in range(1, 6)]),
    after=lambda x: logger.debug(f"get_polymarkets failed, {x.attempt_number=}."),
)
def get_polymarkets(
    limit: int,
    with_rewards: bool = False,
    next_cursor: str | None = None,
) -> MarketsEndpointResponse:
    url = (
        f"{POLYMARKET_API_BASE_URL}/{'sampling-markets' if with_rewards else 'markets'}"
    )
    params: dict[str, str | int | float | None] = {
        "limit": min(limit, MARKETS_LIMIT),
    }
    if next_cursor is not None:
        params["next_cursor"] = next_cursor
    return response_to_model(requests.get(url, params=params), MarketsEndpointResponse)
get_polymarket_binary_markets
get_polymarket_binary_markets(
    limit: int,
    closed: bool | None = False,
    excluded_questions: set[str] | None = None,
    with_rewards: bool = False,
    main_markets_only: bool = True,
) -> list[PolymarketMarketWithPrices]

See https://learn.polymarket.com/trading-rewards for information about rewards.

Source code in prediction_market_agent_tooling/markets/polymarket/api.py
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def get_polymarket_binary_markets(
    limit: int,
    closed: bool | None = False,
    excluded_questions: set[str] | None = None,
    with_rewards: bool = False,
    main_markets_only: bool = True,
) -> list[PolymarketMarketWithPrices]:
    """
    See https://learn.polymarket.com/trading-rewards for information about rewards.
    """

    all_markets: list[PolymarketMarketWithPrices] = []
    next_cursor: str | None = None

    while True:
        response = get_polymarkets(
            limit, with_rewards=with_rewards, next_cursor=next_cursor
        )

        for market in response.data:
            # Closed markets means resolved markets.
            if closed is not None and market.closed != closed:
                continue

            # Skip markets that are inactive.
            # Documentation does not provide more details about this, but if API returns them, website gives "Oops...we didn't forecast this".
            if not market.active:
                continue

            # Skip also those that were archived.
            # Again nothing about it in documentation and API doesn't seem to return them, but to be safe.
            if market.archived:
                continue

            if excluded_questions and market.question in excluded_questions:
                continue

            # Atm we work with binary markets only.
            if sorted(token.outcome for token in market.tokens) != [
                POLYMARKET_FALSE_OUTCOME,
                POLYMARKET_TRUE_OUTCOME,
            ]:
                continue

            # This is pretty slow to do here, but our safest option at the moment. So keep it as the last filter.
            # TODO: Add support for `description` for `AgentMarket` and if it isn't None, use it in addition to the question in all agents. Then this can be removed.
            if main_markets_only and not market.fetch_if_its_a_main_market():
                continue

            tokens_with_price = get_market_tokens_with_prices(market)
            market_with_prices = PolymarketMarketWithPrices.model_validate(
                {**market.model_dump(), "tokens": tokens_with_price}
            )

            all_markets.append(market_with_prices)

        if len(all_markets) >= limit:
            break

        next_cursor = response.next_cursor

        if next_cursor == "LTE=":
            # 'LTE=' means the end.
            break

    return all_markets[:limit]
get_polymarket_market
get_polymarket_market(
    condition_id: str,
) -> PolymarketMarket
Source code in prediction_market_agent_tooling/markets/polymarket/api.py
112
113
114
def get_polymarket_market(condition_id: str) -> PolymarketMarket:
    url = f"{POLYMARKET_API_BASE_URL}/markets/{condition_id}"
    return response_to_model(requests.get(url), PolymarketMarket)
get_token_price
get_token_price(
    token_id: str, side: Literal["buy", "sell"]
) -> PolymarketPriceResponse
Source code in prediction_market_agent_tooling/markets/polymarket/api.py
117
118
119
120
121
122
def get_token_price(
    token_id: str, side: t.Literal["buy", "sell"]
) -> PolymarketPriceResponse:
    url = f"{POLYMARKET_API_BASE_URL}/price"
    params = {"token_id": token_id, "side": side}
    return response_to_model(requests.get(url, params=params), PolymarketPriceResponse)
get_market_tokens_with_prices
get_market_tokens_with_prices(
    market: PolymarketMarket,
) -> list[PolymarketTokenWithPrices]
Source code in prediction_market_agent_tooling/markets/polymarket/api.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def get_market_tokens_with_prices(
    market: PolymarketMarket,
) -> list[PolymarketTokenWithPrices]:
    tokens_with_prices = [
        PolymarketTokenWithPrices(
            token_id=token.token_id,
            outcome=token.outcome,
            winner=token.winner,
            prices=Prices(
                BUY=get_token_price(token.token_id, "buy").price_dec,
                SELL=get_token_price(token.token_id, "sell").price_dec,
            ),
        )
        for token in market.tokens
    ]
    return tokens_with_prices

data_models

PolymarketRewards

Bases: BaseModel

min_size instance-attribute
min_size: int
max_spread instance-attribute
max_spread: float | None
event_start_date class-attribute instance-attribute
event_start_date: DatetimeUTC | None = None
event_end_date class-attribute instance-attribute
event_end_date: DatetimeUTC | None = None
in_game_multiplier class-attribute instance-attribute
in_game_multiplier: int | None = None
reward_epoch class-attribute instance-attribute
reward_epoch: int | None = None
PolymarketToken

Bases: BaseModel

token_id instance-attribute
token_id: str
outcome instance-attribute
outcome: OutcomeStr
winner instance-attribute
winner: bool
PolymarketMarket

Bases: BaseModel

enable_order_book instance-attribute
enable_order_book: bool
active instance-attribute
active: bool
closed instance-attribute
closed: bool
archived instance-attribute
archived: bool
minimum_order_size instance-attribute
minimum_order_size: str | float
minimum_tick_size instance-attribute
minimum_tick_size: str | float
condition_id instance-attribute
condition_id: str
question_id instance-attribute
question_id: str
question instance-attribute
question: str
description instance-attribute
description: str
market_slug instance-attribute
market_slug: str
end_date_iso instance-attribute
end_date_iso: DatetimeUTC | None
game_start_time instance-attribute
game_start_time: DatetimeUTC | None
seconds_delay instance-attribute
seconds_delay: int
fpmm instance-attribute
fpmm: str
maker_base_fee instance-attribute
maker_base_fee: int
taker_base_fee instance-attribute
taker_base_fee: int
notifications_enabled instance-attribute
notifications_enabled: bool
neg_risk instance-attribute
neg_risk: bool
neg_risk_market_id instance-attribute
neg_risk_market_id: str
neg_risk_request_id instance-attribute
neg_risk_request_id: str
icon instance-attribute
icon: str
image instance-attribute
image: str
rewards instance-attribute
rewards: PolymarketRewards
tokens instance-attribute
tokens: tuple[PolymarketToken, ...]
is_50_50_outcome instance-attribute
is_50_50_outcome: bool
categories class-attribute instance-attribute
categories: list[str] | None = None
parent_categories class-attribute instance-attribute
parent_categories: list[str] | None = None
accepting_orders instance-attribute
accepting_orders: bool
id property
id: str
url property
url: str
resolution property
resolution: Resolution | None
fetch_full_market
fetch_full_market() -> PolymarketFullMarket | None
Source code in prediction_market_agent_tooling/markets/polymarket/data_models.py
92
93
def fetch_full_market(self) -> PolymarketFullMarket | None:
    return PolymarketFullMarket.fetch_from_url(self.url)
fetch_if_its_a_main_market
fetch_if_its_a_main_market() -> bool
Source code in prediction_market_agent_tooling/markets/polymarket/data_models.py
 95
 96
 97
 98
 99
100
101
102
103
def fetch_if_its_a_main_market(self) -> bool:
    # On Polymarket, there are markets that are actually a group of multiple Yes/No markets, for example https://polymarket.com/event/presidential-election-winner-2024.
    # But API returns them individually, and then we receive questions such as "Will any other Republican Politician win the 2024 US Presidential Election?",
    # which are naturally unpredictable without futher details.
    # This is a heuristic to filter them out.
    # Warning: This is a very slow operation, as it requires fetching the website. Use it only when necessary.
    full_market = self.fetch_full_market()
    # `full_market` can be None, if this class come from a multiple Yes/No market, becase then, the constructed URL is invalid (and there is now way to construct an valid one from the data we have).
    return full_market is not None and full_market.is_main_market
MarketsEndpointResponse

Bases: BaseModel

limit instance-attribute
limit: int
count instance-attribute
count: int
next_cursor instance-attribute
next_cursor: str
data instance-attribute
data: list[PolymarketMarket]
PolymarketPriceResponse

Bases: BaseModel

price instance-attribute
price: str
price_dec property
price_dec: USDC
Prices

Bases: BaseModel

BUY instance-attribute
BUY: USDC
SELL instance-attribute
SELL: USDC
PolymarketTokenWithPrices

Bases: PolymarketToken

prices instance-attribute
prices: Prices
token_id instance-attribute
token_id: str
outcome instance-attribute
outcome: OutcomeStr
winner instance-attribute
winner: bool
PolymarketMarketWithPrices

Bases: PolymarketMarket

tokens instance-attribute
tokens: tuple[PolymarketTokenWithPrices, ...]
p_yes property
p_yes: Probability
enable_order_book instance-attribute
enable_order_book: bool
active instance-attribute
active: bool
closed instance-attribute
closed: bool
archived instance-attribute
archived: bool
minimum_order_size instance-attribute
minimum_order_size: str | float
minimum_tick_size instance-attribute
minimum_tick_size: str | float
condition_id instance-attribute
condition_id: str
question_id instance-attribute
question_id: str
question instance-attribute
question: str
description instance-attribute
description: str
market_slug instance-attribute
market_slug: str
end_date_iso instance-attribute
end_date_iso: DatetimeUTC | None
game_start_time instance-attribute
game_start_time: DatetimeUTC | None
seconds_delay instance-attribute
seconds_delay: int
fpmm instance-attribute
fpmm: str
maker_base_fee instance-attribute
maker_base_fee: int
taker_base_fee instance-attribute
taker_base_fee: int
notifications_enabled instance-attribute
notifications_enabled: bool
neg_risk instance-attribute
neg_risk: bool
neg_risk_market_id instance-attribute
neg_risk_market_id: str
neg_risk_request_id instance-attribute
neg_risk_request_id: str
icon instance-attribute
icon: str
image instance-attribute
image: str
rewards instance-attribute
rewards: PolymarketRewards
is_50_50_outcome instance-attribute
is_50_50_outcome: bool
categories class-attribute instance-attribute
categories: list[str] | None = None
parent_categories class-attribute instance-attribute
parent_categories: list[str] | None = None
accepting_orders instance-attribute
accepting_orders: bool
id property
id: str
url property
url: str
resolution property
resolution: Resolution | None
fetch_full_market
fetch_full_market() -> PolymarketFullMarket | None
Source code in prediction_market_agent_tooling/markets/polymarket/data_models.py
92
93
def fetch_full_market(self) -> PolymarketFullMarket | None:
    return PolymarketFullMarket.fetch_from_url(self.url)
fetch_if_its_a_main_market
fetch_if_its_a_main_market() -> bool
Source code in prediction_market_agent_tooling/markets/polymarket/data_models.py
 95
 96
 97
 98
 99
100
101
102
103
def fetch_if_its_a_main_market(self) -> bool:
    # On Polymarket, there are markets that are actually a group of multiple Yes/No markets, for example https://polymarket.com/event/presidential-election-winner-2024.
    # But API returns them individually, and then we receive questions such as "Will any other Republican Politician win the 2024 US Presidential Election?",
    # which are naturally unpredictable without futher details.
    # This is a heuristic to filter them out.
    # Warning: This is a very slow operation, as it requires fetching the website. Use it only when necessary.
    full_market = self.fetch_full_market()
    # `full_market` can be None, if this class come from a multiple Yes/No market, becase then, the constructed URL is invalid (and there is now way to construct an valid one from the data we have).
    return full_market is not None and full_market.is_main_market

data_models_web

Autogenerated using datamodel-codegen from a single Polymarket response. Then adjusted, fixed. Keep in mind that not all fields were used so far, so there might be some bugs.

These models are based on what Polymarket's website returns in the response, and prediction_market_agent_tooling/markets/polymarket/data_models.py are based on what their API returns.

POLYMARKET_BASE_URL module-attribute
POLYMARKET_BASE_URL = 'https://polymarket.com'
POLYMARKET_TRUE_OUTCOME module-attribute
POLYMARKET_TRUE_OUTCOME = 'Yes'
POLYMARKET_FALSE_OUTCOME module-attribute
POLYMARKET_FALSE_OUTCOME = 'No'
ImageOptimized

Bases: BaseModel

imageUrlOptimized instance-attribute
imageUrlOptimized: str
IconOptimized

Bases: BaseModel

imageUrlOptimized instance-attribute
imageUrlOptimized: str
Event

Bases: BaseModel

id instance-attribute
id: str
slug instance-attribute
slug: str
ticker instance-attribute
ticker: str
title instance-attribute
title: str
series class-attribute instance-attribute
series: Any | None = None
Event1

Bases: BaseModel

startDate class-attribute instance-attribute
startDate: DatetimeUTC | None = None
slug instance-attribute
slug: str
Market1

Bases: BaseModel

slug instance-attribute
slug: str
question instance-attribute
question: str
image instance-attribute
image: str
volume class-attribute instance-attribute
volume: USDC | None = None
outcomes instance-attribute
outcomes: Sequence[OutcomeStr]
outcomePrices instance-attribute
outcomePrices: list[USDC]
active instance-attribute
active: bool
archived instance-attribute
archived: bool
closed instance-attribute
closed: bool
orderPriceMinTickSize instance-attribute
orderPriceMinTickSize: USDC
clobTokenIds instance-attribute
clobTokenIds: str
events class-attribute instance-attribute
events: list[Event1] | None = None
ResolutionData

Bases: BaseModel

id instance-attribute
id: str
author instance-attribute
author: str
lastUpdateTimestamp instance-attribute
lastUpdateTimestamp: int
status instance-attribute
status: str
wasDisputed instance-attribute
wasDisputed: bool
price instance-attribute
price: str
proposedPrice instance-attribute
proposedPrice: str
reproposedPrice instance-attribute
reproposedPrice: str
updates instance-attribute
updates: str
newVersionQ instance-attribute
newVersionQ: bool
transactionHash instance-attribute
transactionHash: str
logIndex instance-attribute
logIndex: str
Market

Bases: BaseModel

id instance-attribute
id: str
question instance-attribute
question: str
conditionId instance-attribute
conditionId: str
slug instance-attribute
slug: str
twitterCardImage class-attribute instance-attribute
twitterCardImage: Any | None = None
resolutionSource class-attribute instance-attribute
resolutionSource: str | None = None
endDate instance-attribute
endDate: DatetimeUTC
category class-attribute instance-attribute
category: Any | None = None
ammType class-attribute instance-attribute
ammType: Any | None = None
description instance-attribute
description: str
liquidity class-attribute instance-attribute
liquidity: str | None = None
startDate class-attribute instance-attribute
startDate: DatetimeUTC | None = None
createdAt instance-attribute
createdAt: DatetimeUTC
xAxisValue class-attribute instance-attribute
xAxisValue: Any | None = None
yAxisValue class-attribute instance-attribute
yAxisValue: Any | None = None
denominationToken class-attribute instance-attribute
denominationToken: Any | None = None
fee class-attribute instance-attribute
fee: str | None = None
lowerBound class-attribute instance-attribute
lowerBound: Any | None = None
upperBound class-attribute instance-attribute
upperBound: Any | None = None
outcomes instance-attribute
outcomes: Sequence[OutcomeStr]
image class-attribute instance-attribute
image: str | None = None
icon class-attribute instance-attribute
icon: str | None = None
imageOptimized class-attribute instance-attribute
imageOptimized: Any | None = None
iconOptimized class-attribute instance-attribute
iconOptimized: Any | None = None
outcomePrices instance-attribute
outcomePrices: list[USDC]
volume class-attribute instance-attribute
volume: USDC | None = None
active instance-attribute
active: bool
marketType class-attribute instance-attribute
marketType: str | None = None
formatType class-attribute instance-attribute
formatType: Any | None = None
lowerBoundDate class-attribute instance-attribute
lowerBoundDate: Any | None = None
upperBoundDate class-attribute instance-attribute
upperBoundDate: Any | None = None
closed instance-attribute
closed: bool
marketMakerAddress instance-attribute
marketMakerAddress: HexAddress
closedTime class-attribute instance-attribute
closedTime: DatetimeUTC | None = None
wideFormat class-attribute instance-attribute
wideFormat: bool | None = None
new class-attribute instance-attribute
new: bool | None = None
sentDiscord class-attribute instance-attribute
sentDiscord: Any | None = None
mailchimpTag class-attribute instance-attribute
mailchimpTag: Any | None = None
featured class-attribute instance-attribute
featured: bool | None = None
submitted_by class-attribute instance-attribute
submitted_by: str | None = None
subcategory class-attribute instance-attribute
subcategory: Any | None = None
categoryMailchimpTag class-attribute instance-attribute
categoryMailchimpTag: Any | None = None
archived class-attribute instance-attribute
archived: bool | None = None
resolvedBy class-attribute instance-attribute
resolvedBy: str | None = None
restricted class-attribute instance-attribute
restricted: bool | None = None
groupItemTitle class-attribute instance-attribute
groupItemTitle: str | None = None
groupItemThreshold class-attribute instance-attribute
groupItemThreshold: str | None = None
questionID class-attribute instance-attribute
questionID: str | None = None
umaEndDate class-attribute instance-attribute
umaEndDate: Any | None = None
enableOrderBook class-attribute instance-attribute
enableOrderBook: bool | None = None
orderPriceMinTickSize class-attribute instance-attribute
orderPriceMinTickSize: float | None = None
orderMinSize class-attribute instance-attribute
orderMinSize: int | None = None
umaResolutionStatus class-attribute instance-attribute
umaResolutionStatus: Any | None = None
curationOrder class-attribute instance-attribute
curationOrder: Any | None = None
volumeNum class-attribute instance-attribute
volumeNum: USDC | None = None
liquidityNum class-attribute instance-attribute
liquidityNum: float | None = None
endDateIso class-attribute instance-attribute
endDateIso: DatetimeUTC | None = None
startDateIso class-attribute instance-attribute
startDateIso: DatetimeUTC | None = None
umaEndDateIso class-attribute instance-attribute
umaEndDateIso: DatetimeUTC | None = None
commentsEnabled class-attribute instance-attribute
commentsEnabled: bool | None = None
disqusThread class-attribute instance-attribute
disqusThread: Any | None = None
gameStartTime class-attribute instance-attribute
gameStartTime: Any | None = None
secondsDelay class-attribute instance-attribute
secondsDelay: int | None = None
clobTokenIds class-attribute instance-attribute
clobTokenIds: list[str] | None = None
liquidityAmm class-attribute instance-attribute
liquidityAmm: float | None = None
liquidityClob class-attribute instance-attribute
liquidityClob: float | None = None
makerBaseFee class-attribute instance-attribute
makerBaseFee: int | None = None
takerBaseFee class-attribute instance-attribute
takerBaseFee: int | None = None
negRisk class-attribute instance-attribute
negRisk: Any | None = None
negRiskRequestID class-attribute instance-attribute
negRiskRequestID: Any | None = None
negRiskMarketID class-attribute instance-attribute
negRiskMarketID: Any | None = None
events class-attribute instance-attribute
events: list[Event] | None = None
markets class-attribute instance-attribute
markets: list[Market1] | None = None
lower_bound_date class-attribute instance-attribute
lower_bound_date: Any | None = None
upper_bound_date class-attribute instance-attribute
upper_bound_date: Any | None = None
market_type class-attribute instance-attribute
market_type: str | None = None
resolution_source class-attribute instance-attribute
resolution_source: str | None = None
end_date class-attribute instance-attribute
end_date: str | None = None
amm_type class-attribute instance-attribute
amm_type: Any | None = None
x_axis_value class-attribute instance-attribute
x_axis_value: Any | None = None
y_axis_value class-attribute instance-attribute
y_axis_value: Any | None = None
denomination_token class-attribute instance-attribute
denomination_token: Any | None = None
resolved_by class-attribute instance-attribute
resolved_by: str | None = None
upper_bound class-attribute instance-attribute
upper_bound: Any | None = None
lower_bound class-attribute instance-attribute
lower_bound: Any | None = None
created_at class-attribute instance-attribute
created_at: str | None = None
updated_at class-attribute instance-attribute
updated_at: Any | None = None
closed_time class-attribute instance-attribute
closed_time: Any | None = None
wide_format class-attribute instance-attribute
wide_format: bool | None = None
volume_num class-attribute instance-attribute
volume_num: USDC | None = None
liquidity_num class-attribute instance-attribute
liquidity_num: USDC | None = None
image_raw class-attribute instance-attribute
image_raw: str | None = None
resolutionData instance-attribute
resolutionData: ResolutionData
resolution property
resolution: Resolution | None
field_validator_closedTime
field_validator_closedTime(
    v: str | None = None,
) -> str | None
Source code in prediction_market_agent_tooling/markets/polymarket/data_models_web.py
170
171
172
@field_validator("closedTime", mode="before")
def field_validator_closedTime(cls, v: str | None = None) -> str | None:
    return v.replace("+00", "") if v else None
Category

Bases: BaseModel

id instance-attribute
id: str
label instance-attribute
label: str
parentCategory instance-attribute
parentCategory: str
slug instance-attribute
slug: str
Bid

Bases: BaseModel

price instance-attribute
price: str
size instance-attribute
size: str
Ask

Bases: BaseModel

price instance-attribute
price: str
size instance-attribute
size: str
MarketBidsAndAsks

Bases: BaseModel

market instance-attribute
market: str
asset_id instance-attribute
asset_id: str
hash instance-attribute
hash: str
bids instance-attribute
bids: list[Bid]
asks instance-attribute
asks: list[Ask]
lastUpdated instance-attribute
lastUpdated: int
PolymarketFullMarket

Bases: BaseModel

id instance-attribute
id: str
ticker instance-attribute
ticker: str
slug instance-attribute
slug: str
title instance-attribute
title: str
subtitle class-attribute instance-attribute
subtitle: Any | None = None
description instance-attribute
description: str
commentCount class-attribute instance-attribute
commentCount: int | None = None
resolutionSource class-attribute instance-attribute
resolutionSource: str | None = None
startDate class-attribute instance-attribute
startDate: DatetimeUTC | None = None
endDate instance-attribute
endDate: DatetimeUTC
image class-attribute instance-attribute
image: str | None = None
icon class-attribute instance-attribute
icon: str | None = None
featuredImage class-attribute instance-attribute
featuredImage: str | None = None
active instance-attribute
active: bool
closed instance-attribute
closed: bool
archived class-attribute instance-attribute
archived: bool | None = None
new class-attribute instance-attribute
new: bool | None = None
featured class-attribute instance-attribute
featured: bool | None = None
restricted class-attribute instance-attribute
restricted: bool | None = None
liquidity class-attribute instance-attribute
liquidity: USDC | None = None
volume class-attribute instance-attribute
volume: USDC | None = None
volume24hr class-attribute instance-attribute
volume24hr: USDC | None = None
competitive class-attribute instance-attribute
competitive: float | None = None
openInterest class-attribute instance-attribute
openInterest: int | None = None
sortBy class-attribute instance-attribute
sortBy: str | None = None
createdAt instance-attribute
createdAt: DatetimeUTC
commentsEnabled class-attribute instance-attribute
commentsEnabled: bool | None = None
disqusThread class-attribute instance-attribute
disqusThread: Any | None = None
updatedAt instance-attribute
updatedAt: DatetimeUTC
enableOrderBook class-attribute instance-attribute
enableOrderBook: bool | None = None
liquidityAmm class-attribute instance-attribute
liquidityAmm: float | None = None
liquidityClob class-attribute instance-attribute
liquidityClob: float | None = None
imageOptimized class-attribute instance-attribute
imageOptimized: ImageOptimized | None = None
iconOptimized class-attribute instance-attribute
iconOptimized: IconOptimized | None = None
featuredImageOptimized class-attribute instance-attribute
featuredImageOptimized: str | None = None
negRisk class-attribute instance-attribute
negRisk: Any | None = None
negRiskMarketID class-attribute instance-attribute
negRiskMarketID: Any | None = None
negRiskFeeBips class-attribute instance-attribute
negRiskFeeBips: Any | None = None
markets instance-attribute
markets: list[Market]
categories class-attribute instance-attribute
categories: list[Category] | None = None
series class-attribute instance-attribute
series: Any | None = None
image_raw class-attribute instance-attribute
image_raw: str | None = None
url property
url: str
is_main_market property
is_main_market: bool
main_market property
main_market: Market
fetch_from_url staticmethod
fetch_from_url(url: str) -> PolymarketFullMarket | None

Get the full market data from the Polymarket website.

Returns None if this market's url returns "Oops...we didn't forecast this", see check_if_its_a_main_market method for more details.

Warning: This is a very slow operation, as it requires fetching the website. Use it only when necessary.

Source code in prediction_market_agent_tooling/markets/polymarket/data_models_web.py
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
@staticmethod
def fetch_from_url(url: str) -> "PolymarketFullMarket | None":
    """
    Get the full market data from the Polymarket website.

    Returns None if this market's url returns "Oops...we didn't forecast this", see `check_if_its_a_main_market` method for more details.

    Warning: This is a very slow operation, as it requires fetching the website. Use it only when necessary.
    """
    logger.info(f"Fetching full market from {url}")

    # Fetch the website as a normal browser would.
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
    }
    content = requests.get(url, headers=headers).text

    # Find the JSON with the data within the content.
    start_tag = """<script id="__NEXT_DATA__" type="application/json" crossorigin="anonymous">"""
    start_idx = content.find(start_tag) + len(start_tag)
    end_idx = content.find("</script>", start_idx)
    response_data = content[start_idx:end_idx]

    # Parsing.
    response_dict = json.loads(response_data)
    response_model = PolymarketWebResponse.model_validate(response_dict)

    full_market_queries = [
        q
        for q in response_model.props.pageProps.dehydratedState.queries
        if isinstance(q.state.data, PolymarketFullMarket)
    ]

    # We expect either 0 markets (if it doesn't exist) or 1 market.
    if len(full_market_queries) not in (0, 1):
        raise ValueError(
            f"Unexpected number of queries in the response, please check it out and modify the code accordingly: `{response_dict}`"
        )

    # It will be `PolymarketFullMarket` thanks to the filter above.
    market = (
        t.cast(PolymarketFullMarket, full_market_queries[0].state.data)
        if full_market_queries
        else None
    )

    if market is None:
        logger.warning(f"No polymarket found for {url}")

    return market
PriceSide

Bases: BaseModel

price instance-attribute
price: USDC
side instance-attribute
side: Literal['BUY', 'SELL']
State

Bases: BaseModel

data instance-attribute
data: (
    PolymarketFullMarket
    | MarketBidsAndAsks
    | PriceSide
    | dict[str, MarketBidsAndAsks]
    | dict[str, PriceSide]
    | None
)
dataUpdateCount instance-attribute
dataUpdateCount: int
dataUpdatedAt instance-attribute
dataUpdatedAt: int
error class-attribute instance-attribute
error: Any | None = None
errorUpdateCount instance-attribute
errorUpdateCount: int
errorUpdatedAt instance-attribute
errorUpdatedAt: int
fetchFailureCount instance-attribute
fetchFailureCount: int
fetchFailureReason class-attribute instance-attribute
fetchFailureReason: Any | None = None
fetchMeta class-attribute instance-attribute
fetchMeta: Any | None = None
isInvalidated instance-attribute
isInvalidated: bool
status instance-attribute
status: str
fetchStatus instance-attribute
fetchStatus: str
Query

Bases: BaseModel

state instance-attribute
state: State
queryKey instance-attribute
queryKey: list[str]
queryHash instance-attribute
queryHash: str
DehydratedState

Bases: BaseModel

mutations instance-attribute
mutations: list[Any]
queries instance-attribute
queries: list[Query]
PageProps

Bases: BaseModel

key instance-attribute
key: str
dehydratedState instance-attribute
dehydratedState: DehydratedState
eslug instance-attribute
eslug: str
mslug class-attribute instance-attribute
mslug: str | None = None
isSingleMarket instance-attribute
isSingleMarket: bool
Props

Bases: BaseModel

pageProps instance-attribute
pageProps: PageProps
Query1

Bases: BaseModel

slug instance-attribute
slug: list[str]
PolymarketWebResponse

Bases: BaseModel

props instance-attribute
props: Props
page instance-attribute
page: str
query instance-attribute
query: Query1
buildId instance-attribute
buildId: str
isFallback instance-attribute
isFallback: bool
gsp instance-attribute
gsp: bool
locale instance-attribute
locale: str
locales instance-attribute
locales: list[str]
defaultLocale instance-attribute
defaultLocale: str
scriptLoader instance-attribute
scriptLoader: list[Any]
construct_polymarket_url
construct_polymarket_url(slug: str) -> str

Note: This works only if it's a single main market, not sub-market of some more general question.

Source code in prediction_market_agent_tooling/markets/polymarket/data_models_web.py
416
417
418
419
420
def construct_polymarket_url(slug: str) -> str:
    """
    Note: This works only if it's a single main market, not sub-market of some more general question.
    """
    return f"{POLYMARKET_BASE_URL}/event/{slug}"

polymarket

PolymarketAgentMarket

Bases: AgentMarket

Polymarket's market class that can be used by agents to make predictions.

base_url class-attribute
base_url: str = POLYMARKET_BASE_URL
fees class-attribute instance-attribute
fees: MarketFees = get_zero_fees()
id instance-attribute
id: str
question instance-attribute
question: str
description instance-attribute
description: str | None
outcomes instance-attribute
outcomes: Sequence[OutcomeStr]
outcome_token_pool instance-attribute
outcome_token_pool: dict[OutcomeStr, OutcomeToken] | None
resolution instance-attribute
resolution: Resolution | None
created_time instance-attribute
created_time: DatetimeUTC | None
close_time instance-attribute
close_time: DatetimeUTC | None
probabilities instance-attribute
probabilities: dict[OutcomeStr, Probability]
url instance-attribute
url: str
volume instance-attribute
volume: CollateralToken | None
is_binary property
is_binary: bool
p_yes property
p_yes: Probability
p_no property
p_no: Probability
probable_resolution property
probable_resolution: Resolution
from_data_model staticmethod
from_data_model(
    model: PolymarketMarketWithPrices,
) -> PolymarketAgentMarket
Source code in prediction_market_agent_tooling/markets/polymarket/polymarket.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@staticmethod
def from_data_model(model: PolymarketMarketWithPrices) -> "PolymarketAgentMarket":
    return PolymarketAgentMarket(
        id=model.id,
        question=model.question,
        description=model.description,
        outcomes=[x.outcome for x in model.tokens],
        resolution=model.resolution,
        created_time=None,
        close_time=model.end_date_iso,
        url=model.url,
        volume=None,
        outcome_token_pool=None,
        probabilities={},  # ToDo - Implement when fixing Polymarket
    )
get_tiny_bet_amount
get_tiny_bet_amount() -> CollateralToken
Source code in prediction_market_agent_tooling/markets/polymarket/polymarket.py
51
52
def get_tiny_bet_amount(self) -> CollateralToken:
    raise NotImplementedError("TODO: Implement to allow betting on Polymarket.")
place_bet
place_bet(outcome: OutcomeStr, amount: USD) -> str
Source code in prediction_market_agent_tooling/markets/polymarket/polymarket.py
54
55
def place_bet(self, outcome: OutcomeStr, amount: USD) -> str:
    raise NotImplementedError("TODO: Implement to allow betting on Polymarket.")
get_markets staticmethod
get_markets(
    limit: int,
    sort_by: SortBy = SortBy.NONE,
    filter_by: FilterBy = FilterBy.OPEN,
    created_after: Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
    fetch_categorical_markets: bool = False,
) -> t.Sequence[PolymarketAgentMarket]
Source code in prediction_market_agent_tooling/markets/polymarket/polymarket.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@staticmethod
def get_markets(
    limit: int,
    sort_by: SortBy = SortBy.NONE,
    filter_by: FilterBy = FilterBy.OPEN,
    created_after: t.Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
    fetch_categorical_markets: bool = False,
) -> t.Sequence["PolymarketAgentMarket"]:
    if sort_by != SortBy.NONE:
        raise ValueError(f"Unsuported sort_by {sort_by} for Polymarket.")

    if created_after is not None:
        raise ValueError(f"Unsuported created_after for Polymarket.")

    closed: bool | None
    if filter_by == FilterBy.OPEN:
        closed = False
    elif filter_by == FilterBy.RESOLVED:
        closed = True
    elif filter_by == FilterBy.NONE:
        closed = None
    else:
        raise ValueError(f"Unknown filter_by: {filter_by}")

    return [
        PolymarketAgentMarket.from_data_model(m)
        for m in get_polymarket_binary_markets(
            limit=limit,
            closed=closed,
            excluded_questions=excluded_questions,
        )
    ]
validate_probabilities
validate_probabilities(
    probs: dict[OutcomeStr, Probability],
    info: FieldValidationInfo,
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
@field_validator("probabilities")
def validate_probabilities(
    cls,
    probs: dict[OutcomeStr, Probability],
    info: FieldValidationInfo,
) -> dict[OutcomeStr, Probability]:
    outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
    if set(probs.keys()) != set(outcomes):
        raise ValueError("Keys of `probabilities` must match `outcomes` exactly.")
    total = float(sum(probs.values()))
    if not 0.999 <= total <= 1.001:
        # We simply log a warning because for some use-cases (e.g. existing positions), the
        # markets might be already closed hence no reliable outcome token prices exist anymore.
        logger.warning(f"Probabilities for market {info.data=} do not sum to 1.")
    return probs
validate_outcome_token_pool
validate_outcome_token_pool(
    outcome_token_pool: dict[str, OutcomeToken] | None,
    info: FieldValidationInfo,
) -> dict[str, OutcomeToken] | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
@field_validator("outcome_token_pool")
def validate_outcome_token_pool(
    cls,
    outcome_token_pool: dict[str, OutcomeToken] | None,
    info: FieldValidationInfo,
) -> dict[str, OutcomeToken] | None:
    outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
    if outcome_token_pool is not None:
        outcome_keys = set(outcome_token_pool.keys())
        expected_keys = set(outcomes)
        if outcome_keys != expected_keys:
            raise ValueError(
                f"Keys of outcome_token_pool ({outcome_keys}) do not match outcomes ({expected_keys})."
            )
    return outcome_token_pool
have_bet_on_market_since
have_bet_on_market_since(
    keys: APIKeys, since: timedelta
) -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
119
120
def have_bet_on_market_since(self, keys: APIKeys, since: timedelta) -> bool:
    raise NotImplementedError("Subclasses must implement this method")
get_outcome_token_pool_by_outcome
get_outcome_token_pool_by_outcome(
    outcome: OutcomeStr,
) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
122
123
124
125
126
127
128
def get_outcome_token_pool_by_outcome(self, outcome: OutcomeStr) -> OutcomeToken:
    if self.outcome_token_pool is None or not self.outcome_token_pool:
        return OutcomeToken(0)

    # We look up by index to avoid having to deal with case sensitivity issues.
    outcome_idx = self.get_outcome_index(outcome)
    return list(self.outcome_token_pool.values())[outcome_idx]
handle_legacy_fee
handle_legacy_fee(data: dict[str, Any]) -> dict[str, t.Any]
Source code in prediction_market_agent_tooling/markets/agent_market.py
130
131
132
133
134
135
136
@model_validator(mode="before")
def handle_legacy_fee(cls, data: dict[str, t.Any]) -> dict[str, t.Any]:
    # Backward compatibility for older `AgentMarket` without `fees`.
    if "fees" not in data and "fee" in data:
        data["fees"] = MarketFees(absolute=0.0, bet_proportion=data["fee"])
        del data["fee"]
    return data
market_outcome_for_probability_key
market_outcome_for_probability_key(
    probability_key: OutcomeStr,
) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
138
139
140
141
142
143
144
145
146
def market_outcome_for_probability_key(
    self, probability_key: OutcomeStr
) -> OutcomeStr:
    for market_outcome in self.outcomes:
        if market_outcome.lower() == probability_key.lower():
            return market_outcome
    raise ValueError(
        f"Could not find probability for probability key {probability_key}"
    )
probability_for_market_outcome
probability_for_market_outcome(
    market_outcome: OutcomeStr,
) -> Probability
Source code in prediction_market_agent_tooling/markets/agent_market.py
148
149
150
151
152
153
154
def probability_for_market_outcome(self, market_outcome: OutcomeStr) -> Probability:
    for k, v in self.probabilities.items():
        if k.lower() == market_outcome.lower():
            return v
    raise ValueError(
        f"Could not find probability for market outcome {market_outcome}"
    )
get_last_trade_p_yes
get_last_trade_p_yes() -> Probability | None

Get the last trade price for the YES outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1. Could be None if no trades were made.

Source code in prediction_market_agent_tooling/markets/agent_market.py
195
196
197
198
199
200
def get_last_trade_p_yes(self) -> Probability | None:
    """
    Get the last trade price for the YES outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1.
    Could be None if no trades were made.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_last_trade_p_no
get_last_trade_p_no() -> Probability | None

Get the last trade price for the NO outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1. Could be None if no trades were made.

Source code in prediction_market_agent_tooling/markets/agent_market.py
202
203
204
205
206
207
def get_last_trade_p_no(self) -> Probability | None:
    """
    Get the last trade price for the NO outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1.
    Could be None if no trades were made.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_last_trade_yes_outcome_price
get_last_trade_yes_outcome_price() -> (
    CollateralToken | None
)
Source code in prediction_market_agent_tooling/markets/agent_market.py
209
210
211
212
213
214
def get_last_trade_yes_outcome_price(self) -> CollateralToken | None:
    # Price on prediction markets are, by definition, equal to the probability of an outcome.
    # Just making it explicit in this function.
    if last_trade_p_yes := self.get_last_trade_p_yes():
        return CollateralToken(last_trade_p_yes)
    return None
get_last_trade_yes_outcome_price_usd
get_last_trade_yes_outcome_price_usd() -> USD | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
216
217
218
219
def get_last_trade_yes_outcome_price_usd(self) -> USD | None:
    if last_trade_yes_outcome_price := self.get_last_trade_yes_outcome_price():
        return self.get_token_in_usd(last_trade_yes_outcome_price)
    return None
get_last_trade_no_outcome_price
get_last_trade_no_outcome_price() -> CollateralToken | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
221
222
223
224
225
226
def get_last_trade_no_outcome_price(self) -> CollateralToken | None:
    # Price on prediction markets are, by definition, equal to the probability of an outcome.
    # Just making it explicit in this function.
    if last_trade_p_no := self.get_last_trade_p_no():
        return CollateralToken(last_trade_p_no)
    return None
get_last_trade_no_outcome_price_usd
get_last_trade_no_outcome_price_usd() -> USD | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
228
229
230
231
def get_last_trade_no_outcome_price_usd(self) -> USD | None:
    if last_trade_no_outcome_price := self.get_last_trade_no_outcome_price():
        return self.get_token_in_usd(last_trade_no_outcome_price)
    return None
get_liquidatable_amount
get_liquidatable_amount() -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
233
234
235
def get_liquidatable_amount(self) -> OutcomeToken:
    tiny_amount = self.get_tiny_bet_amount()
    return OutcomeToken.from_token(tiny_amount / 10)
get_token_in_usd
get_token_in_usd(x: CollateralToken) -> USD

Token of this market can have whatever worth (e.g. sDai and ETH markets will have different worth of 1 token). Use this to convert it to USD.

Source code in prediction_market_agent_tooling/markets/agent_market.py
237
238
239
240
241
def get_token_in_usd(self, x: CollateralToken) -> USD:
    """
    Token of this market can have whatever worth (e.g. sDai and ETH markets will have different worth of 1 token). Use this to convert it to USD.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_usd_in_token
get_usd_in_token(x: USD) -> CollateralToken

Markets on a single platform can have different tokens as collateral (sDai, wxDai, GNO, ...). Use this to convert USD to the token of this market.

Source code in prediction_market_agent_tooling/markets/agent_market.py
243
244
245
246
247
def get_usd_in_token(self, x: USD) -> CollateralToken:
    """
    Markets on a single platform can have different tokens as collateral (sDai, wxDai, GNO, ...). Use this to convert USD to the token of this market.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_sell_value_of_outcome_token
get_sell_value_of_outcome_token(
    outcome: OutcomeStr, amount: OutcomeToken
) -> CollateralToken

When you hold OutcomeToken(s), it's easy to calculate how much you get at the end if you win (1 OutcomeToken will equal to 1 Token). But use this to figure out, how much are these outcome tokens worth right now (for how much you can sell them).

Source code in prediction_market_agent_tooling/markets/agent_market.py
249
250
251
252
253
254
255
256
def get_sell_value_of_outcome_token(
    self, outcome: OutcomeStr, amount: OutcomeToken
) -> CollateralToken:
    """
    When you hold OutcomeToken(s), it's easy to calculate how much you get at the end if you win (1 OutcomeToken will equal to 1 Token).
    But use this to figure out, how much are these outcome tokens worth right now (for how much you can sell them).
    """
    raise NotImplementedError("Subclasses must implement this method")
get_in_usd
get_in_usd(x: USD | CollateralToken) -> USD
Source code in prediction_market_agent_tooling/markets/agent_market.py
258
259
260
261
def get_in_usd(self, x: USD | CollateralToken) -> USD:
    if isinstance(x, USD):
        return x
    return self.get_token_in_usd(x)
get_in_token
get_in_token(x: USD | CollateralToken) -> CollateralToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
263
264
265
266
def get_in_token(self, x: USD | CollateralToken) -> CollateralToken:
    if isinstance(x, CollateralToken):
        return x
    return self.get_usd_in_token(x)
liquidate_existing_positions
liquidate_existing_positions(outcome: OutcomeStr) -> None
Source code in prediction_market_agent_tooling/markets/agent_market.py
274
275
def liquidate_existing_positions(self, outcome: OutcomeStr) -> None:
    raise NotImplementedError("Subclasses must implement this method")
buy_tokens
buy_tokens(outcome: OutcomeStr, amount: USD) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
280
281
def buy_tokens(self, outcome: OutcomeStr, amount: USD) -> str:
    return self.place_bet(outcome=outcome, amount=amount)
get_buy_token_amount
get_buy_token_amount(
    bet_amount: USD | CollateralToken, outcome: OutcomeStr
) -> OutcomeToken | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
283
284
285
286
def get_buy_token_amount(
    self, bet_amount: USD | CollateralToken, outcome: OutcomeStr
) -> OutcomeToken | None:
    raise NotImplementedError("Subclasses must implement this method")
sell_tokens
sell_tokens(
    outcome: OutcomeStr, amount: USD | OutcomeToken
) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
288
289
def sell_tokens(self, outcome: OutcomeStr, amount: USD | OutcomeToken) -> str:
    raise NotImplementedError("Subclasses must implement this method")
compute_fpmm_probabilities staticmethod
compute_fpmm_probabilities(
    balances: list[OutcomeWei],
) -> list[Probability]

Compute the implied probabilities in a Fixed Product Market Maker.

Parameters:

Name Type Description Default
balances List[float]

Balances of outcome tokens.

required

Returns:

Type Description
list[Probability]

List[float]: Implied probabilities for each outcome.

Source code in prediction_market_agent_tooling/markets/agent_market.py
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
@staticmethod
def compute_fpmm_probabilities(balances: list[OutcomeWei]) -> list[Probability]:
    """
    Compute the implied probabilities in a Fixed Product Market Maker.

    Args:
        balances (List[float]): Balances of outcome tokens.

    Returns:
        List[float]: Implied probabilities for each outcome.
    """
    if all(x.value == 0 for x in balances):
        return [Probability(0.0)] * len(balances)

    # converting to standard values for prod compatibility.
    values_balance = [i.value for i in balances]
    # Compute product of balances excluding each outcome
    excluded_products = []
    for i in range(len(values_balance)):
        other_balances = values_balance[:i] + values_balance[i + 1 :]
        excluded_products.append(prod(other_balances))

    # Normalize to sum to 1
    total = sum(excluded_products)
    if total == 0:
        return [Probability(0.0)] * len(balances)
    probabilities = [Probability(p / total) for p in excluded_products]

    return probabilities
build_probability_map_from_p_yes staticmethod
build_probability_map_from_p_yes(
    p_yes: Probability,
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
321
322
323
324
325
326
327
328
@staticmethod
def build_probability_map_from_p_yes(
    p_yes: Probability,
) -> dict[OutcomeStr, Probability]:
    return {
        OutcomeStr(YES_OUTCOME_LOWERCASE_IDENTIFIER): p_yes,
        OutcomeStr(NO_OUTCOME_LOWERCASE_IDENTIFIER): Probability(1.0 - p_yes),
    }
build_probability_map staticmethod
build_probability_map(
    outcome_token_amounts: list[OutcomeWei],
    outcomes: list[OutcomeStr],
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
330
331
332
333
334
335
@staticmethod
def build_probability_map(
    outcome_token_amounts: list[OutcomeWei], outcomes: list[OutcomeStr]
) -> dict[OutcomeStr, Probability]:
    probs = AgentMarket.compute_fpmm_probabilities(outcome_token_amounts)
    return {outcome: prob for outcome, prob in zip(outcomes, probs)}
get_binary_market staticmethod
get_binary_market(id: str) -> AgentMarket
Source code in prediction_market_agent_tooling/markets/agent_market.py
348
349
350
@staticmethod
def get_binary_market(id: str) -> "AgentMarket":
    raise NotImplementedError("Subclasses must implement this method")
redeem_winnings staticmethod
redeem_winnings(api_keys: APIKeys) -> None

On some markets (like Omen), it's needed to manually claim the winner bets. If it's not needed, just implement with pass.

Source code in prediction_market_agent_tooling/markets/agent_market.py
352
353
354
355
356
357
@staticmethod
def redeem_winnings(api_keys: APIKeys) -> None:
    """
    On some markets (like Omen), it's needed to manually claim the winner bets. If it's not needed, just implement with `pass`.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_trade_balance classmethod
get_trade_balance(api_keys: APIKeys) -> USD

Return balance that can be used to trade on the given market.

Source code in prediction_market_agent_tooling/markets/agent_market.py
359
360
361
362
363
364
@classmethod
def get_trade_balance(cls, api_keys: APIKeys) -> USD:
    """
    Return balance that can be used to trade on the given market.
    """
    raise NotImplementedError("Subclasses must implement this method")
verify_operational_balance staticmethod
verify_operational_balance(api_keys: APIKeys) -> bool

Return True if the user has enough of operational balance. If not needed, just return True. For example: Omen needs at least some xDai in the wallet to execute transactions.

Source code in prediction_market_agent_tooling/markets/agent_market.py
366
367
368
369
370
371
372
@staticmethod
def verify_operational_balance(api_keys: APIKeys) -> bool:
    """
    Return `True` if the user has enough of operational balance. If not needed, just return `True`.
    For example: Omen needs at least some xDai in the wallet to execute transactions.
    """
    raise NotImplementedError("Subclasses must implement this method")
store_prediction
store_prediction(
    processed_market: ProcessedMarket | None,
    keys: APIKeys,
    agent_name: str,
) -> None

If market allows to upload predictions somewhere, implement it in this method.

Source code in prediction_market_agent_tooling/markets/agent_market.py
374
375
376
377
378
379
380
381
382
383
def store_prediction(
    self,
    processed_market: ProcessedMarket | None,
    keys: APIKeys,
    agent_name: str,
) -> None:
    """
    If market allows to upload predictions somewhere, implement it in this method.
    """
    raise NotImplementedError("Subclasses must implement this method")
store_trades
store_trades(
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None

If market allows to upload trades somewhere, implement it in this method.

Source code in prediction_market_agent_tooling/markets/agent_market.py
385
386
387
388
389
390
391
392
393
394
395
def store_trades(
    self,
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None:
    """
    If market allows to upload trades somewhere, implement it in this method.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_bets_made_since staticmethod
get_bets_made_since(
    better_address: ChecksumAddress, start_time: DatetimeUTC
) -> list[Bet]
Source code in prediction_market_agent_tooling/markets/agent_market.py
397
398
399
400
401
@staticmethod
def get_bets_made_since(
    better_address: ChecksumAddress, start_time: DatetimeUTC
) -> list[Bet]:
    raise NotImplementedError("Subclasses must implement this method")
get_resolved_bets_made_since staticmethod
get_resolved_bets_made_since(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC,
    end_time: DatetimeUTC | None,
) -> list[ResolvedBet]
Source code in prediction_market_agent_tooling/markets/agent_market.py
403
404
405
406
407
408
409
@staticmethod
def get_resolved_bets_made_since(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC,
    end_time: DatetimeUTC | None,
) -> list[ResolvedBet]:
    raise NotImplementedError("Subclasses must implement this method")
is_closed
is_closed() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
411
412
def is_closed(self) -> bool:
    return self.close_time is not None and self.close_time <= utcnow()
is_resolved
is_resolved() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
414
415
def is_resolved(self) -> bool:
    return self.resolution is not None
get_liquidity
get_liquidity() -> CollateralToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
417
418
def get_liquidity(self) -> CollateralToken:
    raise NotImplementedError("Subclasses must implement this method")
has_liquidity
has_liquidity() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
420
421
def has_liquidity(self) -> bool:
    return self.get_liquidity() > 0
has_successful_resolution
has_successful_resolution() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
423
424
425
426
427
428
def has_successful_resolution(self) -> bool:
    return (
        self.resolution is not None
        and self.resolution.outcome is not None
        and not self.resolution.invalid
    )
has_unsuccessful_resolution
has_unsuccessful_resolution() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
430
431
def has_unsuccessful_resolution(self) -> bool:
    return self.resolution is not None and self.resolution.invalid
get_outcome_str_from_bool staticmethod
get_outcome_str_from_bool(outcome: bool) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
433
434
435
@staticmethod
def get_outcome_str_from_bool(outcome: bool) -> OutcomeStr:
    raise NotImplementedError("Subclasses must implement this method")
get_outcome_str
get_outcome_str(outcome_index: int) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
437
438
439
440
441
442
443
def get_outcome_str(self, outcome_index: int) -> OutcomeStr:
    try:
        return self.outcomes[outcome_index]
    except IndexError:
        raise IndexError(
            f"Outcome index `{outcome_index}` out of range for `{self.outcomes}`: `{self.outcomes}`."
        )
get_outcome_index
get_outcome_index(outcome: OutcomeStr) -> int
Source code in prediction_market_agent_tooling/markets/agent_market.py
445
446
447
448
449
450
def get_outcome_index(self, outcome: OutcomeStr) -> int:
    outcomes_lowercase = [o.lower() for o in self.outcomes]
    try:
        return outcomes_lowercase.index(outcome.lower())
    except ValueError:
        raise ValueError(f"Outcome `{outcome}` not found in `{self.outcomes}`.")
get_token_balance
get_token_balance(
    user_id: str, outcome: OutcomeStr
) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
452
453
def get_token_balance(self, user_id: str, outcome: OutcomeStr) -> OutcomeToken:
    raise NotImplementedError("Subclasses must implement this method")
get_position
get_position(user_id: str) -> ExistingPosition | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
455
456
def get_position(self, user_id: str) -> ExistingPosition | None:
    raise NotImplementedError("Subclasses must implement this method")
get_positions classmethod
get_positions(
    user_id: str,
    liquid_only: bool = False,
    larger_than: OutcomeToken = OutcomeToken(0),
) -> t.Sequence[ExistingPosition]

Get all non-zero positions a user has in any market.

If liquid_only is True, only return positions that can be sold.

If larger_than is not None, only return positions with a larger number of tokens than this amount.

Source code in prediction_market_agent_tooling/markets/agent_market.py
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
@classmethod
def get_positions(
    cls,
    user_id: str,
    liquid_only: bool = False,
    larger_than: OutcomeToken = OutcomeToken(0),
) -> t.Sequence[ExistingPosition]:
    """
    Get all non-zero positions a user has in any market.

    If `liquid_only` is True, only return positions that can be sold.

    If `larger_than` is not None, only return positions with a larger number
    of tokens than this amount.
    """
    raise NotImplementedError("Subclasses must implement this method")
can_be_traded
can_be_traded() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
475
476
477
478
def can_be_traded(self) -> bool:
    if self.is_closed() or not self.has_liquidity():
        return False
    return True
get_user_url classmethod
get_user_url(keys: APIKeys) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
480
481
482
@classmethod
def get_user_url(cls, keys: APIKeys) -> str:
    raise NotImplementedError("Subclasses must implement this method")
has_token_pool
has_token_pool() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
484
485
def has_token_pool(self) -> bool:
    return self.outcome_token_pool is not None
get_pool_tokens
get_pool_tokens(outcome: OutcomeStr) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
487
488
489
490
491
def get_pool_tokens(self, outcome: OutcomeStr) -> OutcomeToken:
    if not self.outcome_token_pool:
        raise ValueError("Outcome token pool is not available.")

    return self.outcome_token_pool[outcome]
get_user_balance staticmethod
get_user_balance(user_id: str) -> float
Source code in prediction_market_agent_tooling/markets/agent_market.py
493
494
495
@staticmethod
def get_user_balance(user_id: str) -> float:
    raise NotImplementedError("Subclasses must implement this method")
get_user_id staticmethod
get_user_id(api_keys: APIKeys) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
497
498
499
@staticmethod
def get_user_id(api_keys: APIKeys) -> str:
    raise NotImplementedError("Subclasses must implement this method")
get_most_recent_trade_datetime
get_most_recent_trade_datetime(
    user_id: str,
) -> DatetimeUTC | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
501
502
def get_most_recent_trade_datetime(self, user_id: str) -> DatetimeUTC | None:
    raise NotImplementedError("Subclasses must implement this method")

utils

find_resolution_on_polymarket
find_resolution_on_polymarket(
    question: str,
) -> Resolution | None
Source code in prediction_market_agent_tooling/markets/polymarket/utils.py
 9
10
11
12
13
14
15
16
def find_resolution_on_polymarket(question: str) -> Resolution | None:
    full_market = find_full_polymarket(question)
    # TODO: Only main markets are supported right now, add logic for others if needed.
    return (
        full_market.main_market.resolution
        if full_market and full_market.is_main_market
        else None
    )
find_full_polymarket
find_full_polymarket(
    question: str,
) -> PolymarketFullMarket | None
Source code in prediction_market_agent_tooling/markets/polymarket/utils.py
19
20
21
22
23
def find_full_polymarket(question: str) -> PolymarketFullMarket | None:
    polymarket_url = find_url_to_polymarket(question)
    return (
        PolymarketFullMarket.fetch_from_url(polymarket_url) if polymarket_url else None
    )
find_url_to_polymarket
find_url_to_polymarket(question: str) -> str | None
Source code in prediction_market_agent_tooling/markets/polymarket/utils.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
def find_url_to_polymarket(question: str) -> str | None:
    # Manually create potential Polymarket's slug from the question.
    replace_chars = {
        ":": "",
        "’": "",
        "“": "",
        "$": "",
        " ": "-",
    }
    slug = "".join(replace_chars.get(char, char) for char in question.lower())

    # Search for the links to the Polymarket's market page on Google.
    links = search_google_gcp(
        # For some reason, just giving it in the query works better than using `site_search`, `exact_terms` or other parameters of the google search.
        query=f"{MarketType.POLYMARKET.market_class.base_url} {question}",
        num=10,
    )

    for link in links:
        link_slug = link.split("/")[-1]

        # If the link is from Polymarket and the slug is in the link, we assume it's the right market.
        if (
            MarketType.POLYMARKET.market_class.base_url in link
            and link_slug
            # Only `startswith`, because long questions get truncated in the slug.
            and slug.startswith(link_slug)
        ):
            return link

    return None

seer

data_models

SEER_BASE_URL module-attribute
SEER_BASE_URL = 'https://app.seer.pm'
CreateCategoricalMarketsParams

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(populate_by_name=True)
market_name class-attribute instance-attribute
market_name: str = Field(..., alias='marketName')
outcomes instance-attribute
outcomes: Sequence[OutcomeStr]
question_start class-attribute instance-attribute
question_start: str = Field(
    alias="questionStart", default=""
)
question_end class-attribute instance-attribute
question_end: str = Field(alias='questionEnd', default='')
outcome_type class-attribute instance-attribute
outcome_type: str = Field(alias='outcomeType', default='')
parent_outcome class-attribute instance-attribute
parent_outcome: int = Field(
    alias="parentOutcome", default=0
)
parent_market class-attribute instance-attribute
parent_market: HexAddress = Field(
    alias="parentMarket", default=ADDRESS_ZERO
)
category instance-attribute
category: str
lang instance-attribute
lang: str
lower_bound class-attribute instance-attribute
lower_bound: int = Field(alias='lowerBound', default=0)
upper_bound class-attribute instance-attribute
upper_bound: int = Field(alias='upperBound', default=0)
min_bond class-attribute instance-attribute
min_bond: Web3Wei = Field(..., alias='minBond')
opening_time class-attribute instance-attribute
opening_time: int = Field(..., alias='openingTime')
token_names class-attribute instance-attribute
token_names: list[str] = Field(..., alias='tokenNames')
SeerMarket

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(populate_by_name=True)
id instance-attribute
id: HexBytes
creator instance-attribute
creator: HexAddress
title class-attribute instance-attribute
title: str = Field(alias='marketName')
outcomes instance-attribute
outcomes: Sequence[OutcomeStr]
wrapped_tokens class-attribute instance-attribute
wrapped_tokens: list[HexAddress] = Field(
    alias="wrappedTokens"
)
parent_outcome class-attribute instance-attribute
parent_outcome: int = Field(alias='parentOutcome')
parent_market class-attribute instance-attribute
parent_market: Optional[SeerParentMarket] = Field(
    alias="parentMarket", default=None
)
collateral_token class-attribute instance-attribute
collateral_token: HexAddress = Field(
    alias="collateralToken"
)
condition_id class-attribute instance-attribute
condition_id: HexBytes = Field(alias='conditionId')
opening_ts class-attribute instance-attribute
opening_ts: int = Field(alias='openingTs')
block_timestamp class-attribute instance-attribute
block_timestamp: int = Field(alias='blockTimestamp')
has_answers class-attribute instance-attribute
has_answers: bool | None = Field(alias='hasAnswers')
payout_reported class-attribute instance-attribute
payout_reported: bool = Field(alias='payoutReported')
payout_numerators class-attribute instance-attribute
payout_numerators: list[int] = Field(
    alias="payoutNumerators"
)
outcomes_supply class-attribute instance-attribute
outcomes_supply: int = Field(alias='outcomesSupply')
has_valid_answer property
has_valid_answer: bool
is_resolved property
is_resolved: bool
is_resolved_with_valid_answer property
is_resolved_with_valid_answer: bool
is_binary property
is_binary: bool
collateral_token_contract_address_checksummed property
collateral_token_contract_address_checksummed: (
    ChecksumAddress
)
close_time property
close_time: DatetimeUTC
created_time property
created_time: DatetimeUTC
url property
url: str
is_redeemable
is_redeemable(
    owner: ChecksumAddress, web3: Web3 | None = None
) -> bool
Source code in prediction_market_agent_tooling/markets/seer/data_models.py
90
91
92
93
94
95
96
97
def is_redeemable(self, owner: ChecksumAddress, web3: Web3 | None = None) -> bool:
    token_balances = self.get_outcome_token_balances(owner, web3)
    if not self.payout_reported:
        return False
    return any(
        payout and balance > 0
        for payout, balance in zip(self.payout_numerators, token_balances)
    )
get_outcome_token_balances
get_outcome_token_balances(
    owner: ChecksumAddress, web3: Web3 | None = None
) -> list[OutcomeWei]
Source code in prediction_market_agent_tooling/markets/seer/data_models.py
 99
100
101
102
103
104
105
106
107
108
109
def get_outcome_token_balances(
    self, owner: ChecksumAddress, web3: Web3 | None = None
) -> list[OutcomeWei]:
    return [
        OutcomeWei.from_wei(
            ContractERC20OnGnosisChain(
                address=Web3.to_checksum_address(token)
            ).balanceOf(owner, web3=web3)
        )
        for token in self.wrapped_tokens
    ]
RedeemParams

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(populate_by_name=True)
market instance-attribute
market: ChecksumAddress
outcome_indices class-attribute instance-attribute
outcome_indices: list[int] = Field(alias='outcomeIndexes')
amounts instance-attribute
amounts: list[OutcomeWei]
ExactInputSingleParams

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(populate_by_name=True)
token_in class-attribute instance-attribute
token_in: ChecksumAddress = Field(alias='tokenIn')
token_out class-attribute instance-attribute
token_out: ChecksumAddress = Field(alias='tokenOut')
recipient instance-attribute
recipient: ChecksumAddress
deadline class-attribute instance-attribute
deadline: int = Field(
    default_factory=lambda: int(timestamp())
)
amount_in class-attribute instance-attribute
amount_in: Wei = Field(alias='amountIn')
amount_out_minimum class-attribute instance-attribute
amount_out_minimum: Wei = Field(alias='amountOutMinimum')
limit_sqrt_price class-attribute instance-attribute
limit_sqrt_price: Wei = Field(
    alias="limitSqrtPrice", default_factory=lambda: Wei(0)
)

exceptions

PriceCalculationError

Bases: Exception

Raised when there's an error discovering prices for a market.

price_manager

PriceManager
PriceManager(
    seer_market: SeerMarket,
    seer_subgraph: SeerSubgraphHandler,
)
Source code in prediction_market_agent_tooling/markets/seer/price_manager.py
50
51
52
def __init__(self, seer_market: SeerMarket, seer_subgraph: SeerSubgraphHandler):
    self.seer_market = seer_market
    self.seer_subgraph = seer_subgraph
seer_market instance-attribute
seer_market = seer_market
seer_subgraph instance-attribute
seer_subgraph = seer_subgraph
build staticmethod
build(market_id: HexBytes) -> PriceManager
Source code in prediction_market_agent_tooling/markets/seer/price_manager.py
54
55
56
57
58
@staticmethod
def build(market_id: HexBytes) -> "PriceManager":
    s = SeerSubgraphHandler()
    market = s.get_market_by_id(market_id=market_id)
    return PriceManager(seer_market=market, seer_subgraph=s)
get_price_for_token
get_price_for_token(
    token: ChecksumAddress,
    collateral_exchange_amount: CollateralToken
    | None = None,
) -> CollateralToken | None
Source code in prediction_market_agent_tooling/markets/seer/price_manager.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
@cached(TTLCache(maxsize=100, ttl=5 * 60), key=_make_cache_key)
def get_price_for_token(
    self,
    token: ChecksumAddress,
    collateral_exchange_amount: CollateralToken | None = None,
) -> CollateralToken | None:
    collateral_exchange_amount = (
        collateral_exchange_amount
        if collateral_exchange_amount is not None
        else CollateralToken(1)
    )

    try:
        buy_token_amount = get_buy_token_amount_else_raise(
            sell_amount=collateral_exchange_amount.as_wei,
            sell_token=self.seer_market.collateral_token_contract_address_checksummed,
            buy_token=token,
        )
        price = collateral_exchange_amount.as_wei / buy_token_amount
        return CollateralToken(price)

    except Exception as e:
        logger.warning(
            f"Could not get quote for {token=} from Cow, exception {e=}. Falling back to pools. "
        )
        return self.get_token_price_from_pools(token=token)
pool_token0_matches_token staticmethod
pool_token0_matches_token(
    token: ChecksumAddress, pool: SeerPool
) -> bool
Source code in prediction_market_agent_tooling/markets/seer/price_manager.py
97
98
99
@staticmethod
def pool_token0_matches_token(token: ChecksumAddress, pool: SeerPool) -> bool:
    return pool.token0.id.hex().lower() == token.lower()
get_token_price_from_pools
get_token_price_from_pools(
    token: ChecksumAddress,
) -> CollateralToken | None
Source code in prediction_market_agent_tooling/markets/seer/price_manager.py
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def get_token_price_from_pools(
    self,
    token: ChecksumAddress,
) -> CollateralToken | None:
    pool = SeerSubgraphHandler().get_pool_by_token(
        token_address=token,
        collateral_address=self.seer_market.collateral_token_contract_address_checksummed,
    )

    if not pool:
        logger.warning(f"Could not find a pool for {token=}")
        return None

    # The mapping below is odd but surprisingly the Algebra subgraph delivers the token1Price
    # for the token0 and the token0Price for the token1 pool.
    # For example, in a outcomeYES (token0)/sDAI pool (token1), token1Price is the price of outcomeYES in units of sDAI.
    price = (
        pool.token1Price
        if self.pool_token0_matches_token(token=token, pool=pool)
        else pool.token0Price
    )
    return price
build_probability_map
build_probability_map() -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/seer/price_manager.py
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
def build_probability_map(self) -> dict[OutcomeStr, Probability]:
    # Inspired by https://github.com/seer-pm/demo/blob/ca682153a6b4d4dd3dcc4ad8bdcbe32202fc8fe7/web/src/hooks/useMarketOdds.ts#L15
    price_data: dict[HexAddress, CollateralToken] = {}

    for idx, wrapped_token in enumerate(self.seer_market.wrapped_tokens):
        price = self.get_price_for_token(
            token=Web3.to_checksum_address(wrapped_token),
        )
        # It's okay if invalid (last) outcome has price 0, but not the other outcomes.
        if price is None and idx != len(self.seer_market.wrapped_tokens) - 1:
            raise PriceCalculationError(
                f"Couldn't get price for {wrapped_token} for market {self.seer_market.url}."
            )
        price_data[wrapped_token] = (
            price if price is not None else CollateralToken.zero()
        )

    # We normalize the prices to sum up to 1.
    normalized_prices = {}

    if not price_data or (
        sum(price_data.values(), start=CollateralToken.zero())
        == CollateralToken.zero()
    ):
        raise PriceCalculationError(
            f"All prices for market {self.seer_market.url} are zero. This shouldn't happen."
        )

    for outcome_token, price in price_data.items():
        old_price = price
        new_price = Probability(
            price / (sum(price_data.values(), start=CollateralToken.zero()))
        )
        self._log_track_price_normalization_diff(
            old_price=old_price.value, normalized_price=new_price
        )
        outcome = self.seer_market.outcomes[
            self.seer_market.wrapped_tokens.index(outcome_token)
        ]
        normalized_prices[OutcomeStr(outcome)] = new_price

    return normalized_prices

seer

SEER_TINY_BET_AMOUNT module-attribute
SEER_TINY_BET_AMOUNT = USD(0.1)
SeerAgentMarket

Bases: AgentMarket

wrapped_tokens instance-attribute
wrapped_tokens: list[ChecksumAddress]
creator instance-attribute
creator: HexAddress
collateral_token_contract_address_checksummed instance-attribute
collateral_token_contract_address_checksummed: (
    ChecksumAddress
)
condition_id instance-attribute
condition_id: HexBytes
description class-attribute instance-attribute
description: str | None = None
outcomes_supply instance-attribute
outcomes_supply: int
base_url class-attribute
base_url: str
id instance-attribute
id: str
question instance-attribute
question: str
outcomes instance-attribute
outcomes: Sequence[OutcomeStr]
outcome_token_pool instance-attribute
outcome_token_pool: dict[OutcomeStr, OutcomeToken] | None
resolution instance-attribute
resolution: Resolution | None
created_time instance-attribute
created_time: DatetimeUTC | None
close_time instance-attribute
close_time: DatetimeUTC | None
probabilities instance-attribute
probabilities: dict[OutcomeStr, Probability]
url instance-attribute
url: str
volume instance-attribute
volume: CollateralToken | None
fees instance-attribute
fees: MarketFees
is_binary property
is_binary: bool
p_yes property
p_yes: Probability
p_no property
p_no: Probability
probable_resolution property
probable_resolution: Resolution
get_collateral_token_contract
get_collateral_token_contract(
    web3: Web3 | None = None,
) -> ContractERC20OnGnosisChain
Source code in prediction_market_agent_tooling/markets/seer/seer.py
91
92
93
94
95
96
97
98
99
def get_collateral_token_contract(
    self, web3: Web3 | None = None
) -> ContractERC20OnGnosisChain:
    web3 = web3 or RPCConfig().get_web3()
    return to_gnosis_chain_contract(
        init_collateral_token_contract(
            self.collateral_token_contract_address_checksummed, web3
        )
    )
store_prediction
store_prediction(
    processed_market: ProcessedMarket | None,
    keys: APIKeys,
    agent_name: str,
) -> None

On Seer, we have to store predictions along with trades, see store_trades.

Source code in prediction_market_agent_tooling/markets/seer/seer.py
101
102
103
104
105
106
107
def store_prediction(
    self,
    processed_market: ProcessedMarket | None,
    keys: APIKeys,
    agent_name: str,
) -> None:
    """On Seer, we have to store predictions along with trades, see `store_trades`."""
store_trades
store_trades(
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None
Source code in prediction_market_agent_tooling/markets/seer/seer.py
109
110
111
112
113
114
115
116
def store_trades(
    self,
    traded_market: ProcessedTradedMarket | None,
    keys: APIKeys,
    agent_name: str,
    web3: Web3 | None = None,
) -> None:
    pass
get_token_in_usd
get_token_in_usd(x: CollateralToken) -> USD
Source code in prediction_market_agent_tooling/markets/seer/seer.py
118
119
def get_token_in_usd(self, x: CollateralToken) -> USD:
    return get_token_in_usd(x, self.collateral_token_contract_address_checksummed)
get_usd_in_token
get_usd_in_token(x: USD) -> CollateralToken
Source code in prediction_market_agent_tooling/markets/seer/seer.py
121
122
def get_usd_in_token(self, x: USD) -> CollateralToken:
    return get_usd_in_token(x, self.collateral_token_contract_address_checksummed)
get_buy_token_amount
get_buy_token_amount(
    bet_amount: USD | CollateralToken,
    outcome_str: OutcomeStr,
) -> OutcomeToken | None

Returns number of outcome tokens returned for a given bet expressed in collateral units.

Source code in prediction_market_agent_tooling/markets/seer/seer.py
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
def get_buy_token_amount(
    self, bet_amount: USD | CollateralToken, outcome_str: OutcomeStr
) -> OutcomeToken | None:
    """Returns number of outcome tokens returned for a given bet expressed in collateral units."""

    if outcome_str not in self.outcomes:
        raise ValueError(
            f"Outcome {outcome_str} not found in market outcomes {self.outcomes}"
        )

    outcome_token = self.get_wrapped_token_for_outcome(outcome_str)

    bet_amount_in_tokens = self.get_in_token(bet_amount)

    p = PriceManager.build(market_id=HexBytes(HexStr(self.id)))
    price = p.get_price_for_token(
        token=outcome_token, collateral_exchange_amount=bet_amount_in_tokens
    )
    if not price:
        logger.info(f"Could not get price for token {outcome_token}")
        return None

    amount_outcome_tokens = bet_amount_in_tokens / price
    return OutcomeToken(amount_outcome_tokens)
get_sell_value_of_outcome_token
get_sell_value_of_outcome_token(
    outcome: OutcomeStr, amount: OutcomeToken
) -> CollateralToken
Source code in prediction_market_agent_tooling/markets/seer/seer.py
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
def get_sell_value_of_outcome_token(
    self, outcome: OutcomeStr, amount: OutcomeToken
) -> CollateralToken:
    if amount == amount.zero():
        return CollateralToken.zero()

    wrapped_outcome_token = self.get_wrapped_token_for_outcome(outcome)

    # We calculate how much collateral we would get back if we sold `amount` of outcome token.
    value_outcome_token_in_collateral = get_buy_token_amount_else_raise(
        sell_amount=amount.as_outcome_wei.as_wei,
        sell_token=wrapped_outcome_token,
        buy_token=self.collateral_token_contract_address_checksummed,
    )
    return value_outcome_token_in_collateral.as_token
get_trade_balance staticmethod
get_trade_balance(api_keys: APIKeys) -> USD
Source code in prediction_market_agent_tooling/markets/seer/seer.py
165
166
167
@staticmethod
def get_trade_balance(api_keys: APIKeys) -> USD:
    return OmenAgentMarket.get_trade_balance(api_keys=api_keys)
get_tiny_bet_amount
get_tiny_bet_amount() -> CollateralToken
Source code in prediction_market_agent_tooling/markets/seer/seer.py
169
170
def get_tiny_bet_amount(self) -> CollateralToken:
    return self.get_in_token(SEER_TINY_BET_AMOUNT)
get_position_else_raise
get_position_else_raise(
    user_id: str, web3: Web3 | None = None
) -> ExistingPosition

Fetches position from the user in a given market. We ignore the INVALID balances since we are only interested in binary outcomes.

Source code in prediction_market_agent_tooling/markets/seer/seer.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
def get_position_else_raise(
    self, user_id: str, web3: Web3 | None = None
) -> ExistingPosition:
    """
    Fetches position from the user in a given market.
    We ignore the INVALID balances since we are only interested in binary outcomes.
    """

    amounts_ot: dict[OutcomeStr, OutcomeToken] = {}

    for outcome_str, wrapped_token in zip(self.outcomes, self.wrapped_tokens):
        outcome_token_balance_wei = OutcomeWei.from_wei(
            ContractERC20OnGnosisChain(address=wrapped_token).balanceOf(
                for_address=Web3.to_checksum_address(user_id), web3=web3
            )
        )

        amounts_ot[
            OutcomeStr(outcome_str)
        ] = outcome_token_balance_wei.as_outcome_token

    amounts_current = {
        k: self.get_token_in_usd(self.get_sell_value_of_outcome_token(k, v))
        for k, v in amounts_ot.items()
    }
    amounts_potential = {
        k: self.get_token_in_usd(v.as_token) for k, v in amounts_ot.items()
    }
    return ExistingPosition(
        market_id=self.id,
        amounts_current=amounts_current,
        amounts_potential=amounts_potential,
        amounts_ot=amounts_ot,
    )
get_position
get_position(
    user_id: str, web3: Web3 | None = None
) -> ExistingPosition | None
Source code in prediction_market_agent_tooling/markets/seer/seer.py
207
208
209
210
211
212
213
214
def get_position(
    self, user_id: str, web3: Web3 | None = None
) -> ExistingPosition | None:
    try:
        return self.get_position_else_raise(user_id=user_id, web3=web3)
    except Exception as e:
        logger.warning(f"Could not get position for user {user_id}, exception {e}")
        return None
get_user_id staticmethod
get_user_id(api_keys: APIKeys) -> str
Source code in prediction_market_agent_tooling/markets/seer/seer.py
216
217
218
@staticmethod
def get_user_id(api_keys: APIKeys) -> str:
    return OmenAgentMarket.get_user_id(api_keys)
redeem_winnings staticmethod
redeem_winnings(api_keys: APIKeys) -> None
Source code in prediction_market_agent_tooling/markets/seer/seer.py
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
@staticmethod
def redeem_winnings(api_keys: APIKeys) -> None:
    web3 = RPCConfig().get_web3()
    subgraph = SeerSubgraphHandler()

    closed_markets = subgraph.get_markets(
        filter_by=FilterBy.RESOLVED, sort_by=SortBy.NEWEST
    )
    filtered_markets = SeerAgentMarket._filter_markets_contained_in_trades(
        api_keys, closed_markets
    )

    market_balances = {
        market.id: market.get_outcome_token_balances(
            api_keys.bet_from_address, web3
        )
        for market in filtered_markets
    }

    markets_to_redeem = [
        market
        for market in filtered_markets
        if market.is_redeemable(owner=api_keys.bet_from_address, web3=web3)
    ]

    gnosis_router = GnosisRouter()
    for market in markets_to_redeem:
        try:
            params = RedeemParams(
                market=Web3.to_checksum_address(market.id),
                outcome_indices=list(range(len(market.payout_numerators))),
                amounts=market_balances[market.id],
            )
            gnosis_router.redeem_to_base(api_keys, params=params, web3=web3)
            logger.info(f"Redeemed market {market.id.hex()}")
        except Exception as e:
            logger.error(f"Failed to redeem market {market.id.hex()}, {e}")
have_bet_on_market_since
have_bet_on_market_since(
    keys: APIKeys, since: timedelta
) -> bool

Check if the user has placed a bet on this market since a specific time using Cow API.

Source code in prediction_market_agent_tooling/markets/seer/seer.py
285
286
287
288
289
290
291
292
293
294
295
296
297
def have_bet_on_market_since(self, keys: APIKeys, since: timedelta) -> bool:
    """Check if the user has placed a bet on this market since a specific time using Cow API."""
    # Cow endpoint doesn't allow us to filter by time.
    start_time = utcnow() - since
    prev_orders = get_orders_by_owner(owner=keys.bet_from_address)
    for order in prev_orders:
        if order.creationDate >= start_time and {
            Web3.to_checksum_address(order.sellToken),
            Web3.to_checksum_address(order.buyToken),
        }.intersection(set(self.wrapped_tokens)):
            return True

    return False
verify_operational_balance staticmethod
verify_operational_balance(api_keys: APIKeys) -> bool
Source code in prediction_market_agent_tooling/markets/seer/seer.py
299
300
301
@staticmethod
def verify_operational_balance(api_keys: APIKeys) -> bool:
    return OmenAgentMarket.verify_operational_balance(api_keys=api_keys)
from_data_model_with_subgraph staticmethod
from_data_model_with_subgraph(
    model: SeerMarket,
    seer_subgraph: SeerSubgraphHandler,
    must_have_prices: bool,
) -> t.Optional[SeerAgentMarket]
Source code in prediction_market_agent_tooling/markets/seer/seer.py
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
@staticmethod
def from_data_model_with_subgraph(
    model: SeerMarket,
    seer_subgraph: SeerSubgraphHandler,
    must_have_prices: bool,
) -> t.Optional["SeerAgentMarket"]:
    price_manager = PriceManager(seer_market=model, seer_subgraph=seer_subgraph)

    probability_map = {}
    try:
        probability_map = price_manager.build_probability_map()
    except PriceCalculationError as e:
        logger.info(
            f"Error when calculating probabilities for market {model.id.hex()} - {e}"
        )
        if must_have_prices:
            # Price calculation failed, so don't return the market
            return None

    market = SeerAgentMarket(
        id=model.id.hex(),
        question=model.title,
        creator=model.creator,
        created_time=model.created_time,
        outcomes=model.outcomes,
        collateral_token_contract_address_checksummed=model.collateral_token_contract_address_checksummed,
        condition_id=model.condition_id,
        url=model.url,
        close_time=model.close_time,
        wrapped_tokens=[Web3.to_checksum_address(i) for i in model.wrapped_tokens],
        fees=MarketFees.get_zero_fees(),
        outcome_token_pool=None,
        outcomes_supply=model.outcomes_supply,
        resolution=None,
        volume=None,
        probabilities=probability_map,
    )

    return market
get_markets staticmethod
get_markets(
    limit: int,
    sort_by: SortBy,
    filter_by: FilterBy = FilterBy.OPEN,
    created_after: Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
    fetch_categorical_markets: bool = False,
) -> t.Sequence[SeerAgentMarket]
Source code in prediction_market_agent_tooling/markets/seer/seer.py
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
@staticmethod
def get_markets(
    limit: int,
    sort_by: SortBy,
    filter_by: FilterBy = FilterBy.OPEN,
    created_after: t.Optional[DatetimeUTC] = None,
    excluded_questions: set[str] | None = None,
    fetch_categorical_markets: bool = False,
) -> t.Sequence["SeerAgentMarket"]:
    seer_subgraph = SeerSubgraphHandler()
    markets = seer_subgraph.get_markets(
        limit=limit,
        sort_by=sort_by,
        filter_by=filter_by,
        include_categorical_markets=fetch_categorical_markets,
    )

    # We exclude the None values below because `from_data_model_with_subgraph` can return None, which
    # represents an invalid market.
    seer_agent_markets = [
        market
        for m in markets
        if (
            market := SeerAgentMarket.from_data_model_with_subgraph(
                model=m,
                seer_subgraph=seer_subgraph,
                must_have_prices=filter_by == FilterBy.OPEN,
            )
        )
        is not None
    ]

    if filter_by == FilterBy.OPEN:
        # Extra manual filter for liquidity, as subgraph is sometimes unreliable.
        seer_agent_markets = [m for m in seer_agent_markets if m.has_liquidity()]

    return seer_agent_markets
get_outcome_str_from_idx
get_outcome_str_from_idx(outcome_index: int) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/seer/seer.py
381
382
def get_outcome_str_from_idx(self, outcome_index: int) -> OutcomeStr:
    return self.outcomes[outcome_index]
get_liquidity_for_outcome
get_liquidity_for_outcome(
    outcome: OutcomeStr, web3: Web3 | None = None
) -> CollateralToken

Liquidity per outcome is comprised of the balance of outcomeToken + collateralToken held by the pool itself (see https://github.com/seer-pm/demo/blob/7bfd0a062780ed6567f65714c4fc4f6e6cdf1c4f/web/netlify/functions/utils/fetchPools.ts#L35-L42).

Source code in prediction_market_agent_tooling/markets/seer/seer.py
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
def get_liquidity_for_outcome(
    self, outcome: OutcomeStr, web3: Web3 | None = None
) -> CollateralToken:
    """Liquidity per outcome is comprised of the balance of outcomeToken + collateralToken held by the pool itself (see https://github.com/seer-pm/demo/blob/7bfd0a062780ed6567f65714c4fc4f6e6cdf1c4f/web/netlify/functions/utils/fetchPools.ts#L35-L42)."""

    outcome_token = self.get_wrapped_token_for_outcome(outcome)
    pool = SeerSubgraphHandler().get_pool_by_token(
        token_address=outcome_token,
        collateral_address=self.collateral_token_contract_address_checksummed,
    )
    if not pool:
        logger.info(
            f"Could not fetch pool for token {outcome_token}, no liquidity available for outcome."
        )
        return CollateralToken(0)
    p = PriceManager.build(HexBytes(HexStr(self.id)))
    total = CollateralToken(0)

    for token_address in [pool.token0.id, pool.token1.id]:
        token_address_checksummed = Web3.to_checksum_address(token_address)
        token_contract = ContractERC20OnGnosisChain(
            address=token_address_checksummed
        )

        token_balance = token_contract.balance_of_in_tokens(
            for_address=Web3.to_checksum_address(HexAddress(HexStr(pool.id.hex()))),
            web3=web3,
        )

        # get price
        token_price_in_sdai = (
            p.get_token_price_from_pools(token=token_address_checksummed)
            if token_address_checksummed
            != self.collateral_token_contract_address_checksummed
            else CollateralToken(1.0)
        )

        # We ignore the liquidity in outcome tokens if price unknown.
        if token_price_in_sdai:
            sdai_balance = token_balance * token_price_in_sdai
            total += sdai_balance

    return total
get_liquidity
get_liquidity() -> CollateralToken
Source code in prediction_market_agent_tooling/markets/seer/seer.py
428
429
430
431
432
433
434
435
def get_liquidity(self) -> CollateralToken:
    liquidity_in_collateral = CollateralToken(0)
    # We ignore the invalid outcome
    for outcome in self.outcomes[:-1]:
        liquidity_for_outcome = self.get_liquidity_for_outcome(outcome)
        liquidity_in_collateral += liquidity_for_outcome

    return liquidity_in_collateral
has_liquidity_for_outcome
has_liquidity_for_outcome(outcome: OutcomeStr) -> bool
Source code in prediction_market_agent_tooling/markets/seer/seer.py
437
438
439
def has_liquidity_for_outcome(self, outcome: OutcomeStr) -> bool:
    liquidity = self.get_liquidity_for_outcome(outcome)
    return liquidity > CollateralToken(0)
has_liquidity
has_liquidity() -> bool
Source code in prediction_market_agent_tooling/markets/seer/seer.py
441
442
443
444
445
def has_liquidity(self) -> bool:
    # We define a market as having liquidity if it has liquidity for all outcomes except for the invalid (index -1)
    return all(
        [self.has_liquidity_for_outcome(outcome) for outcome in self.outcomes[:-1]]
    )
get_wrapped_token_for_outcome
get_wrapped_token_for_outcome(
    outcome: OutcomeStr,
) -> ChecksumAddress
Source code in prediction_market_agent_tooling/markets/seer/seer.py
447
448
449
def get_wrapped_token_for_outcome(self, outcome: OutcomeStr) -> ChecksumAddress:
    outcome_idx = self.outcomes.index(outcome)
    return self.wrapped_tokens[outcome_idx]
place_bet
place_bet(
    outcome: OutcomeStr,
    amount: USD,
    auto_deposit: bool = True,
    web3: Web3 | None = None,
    api_keys: APIKeys | None = None,
) -> str
Source code in prediction_market_agent_tooling/markets/seer/seer.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
def place_bet(
    self,
    outcome: OutcomeStr,
    amount: USD,
    auto_deposit: bool = True,
    web3: Web3 | None = None,
    api_keys: APIKeys | None = None,
) -> str:
    outcome_token = self.get_wrapped_token_for_outcome(outcome)
    api_keys = api_keys if api_keys is not None else APIKeys()
    if not self.can_be_traded():
        raise ValueError(
            f"Market {self.id} is not open for trading. Cannot place bet."
        )

    amount_in_token = self.get_usd_in_token(amount)
    amount_wei = amount_in_token.as_wei
    collateral_contract = self.get_collateral_token_contract()

    if auto_deposit:
        auto_deposit_collateral_token(
            collateral_contract, amount_wei, api_keys, web3
        )

    collateral_balance = collateral_contract.balanceOf(
        api_keys.bet_from_address, web3=web3
    )
    if collateral_balance < amount_wei:
        raise ValueError(
            f"Balance {collateral_balance} not enough for bet size {amount}"
        )

    return self._swap_tokens_with_fallback(
        sell_token=collateral_contract.address,
        buy_token=outcome_token,
        amount_wei=amount_wei,
        api_keys=api_keys,
        web3=web3,
    )
sell_tokens
sell_tokens(
    outcome: OutcomeStr,
    amount: USD | OutcomeToken,
    auto_withdraw: bool = True,
    api_keys: APIKeys | None = None,
    web3: Web3 | None = None,
) -> str

Sells the given number of shares for the given outcome in the given market.

Source code in prediction_market_agent_tooling/markets/seer/seer.py
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
def sell_tokens(
    self,
    outcome: OutcomeStr,
    amount: USD | OutcomeToken,
    auto_withdraw: bool = True,
    api_keys: APIKeys | None = None,
    web3: Web3 | None = None,
) -> str:
    """
    Sells the given number of shares for the given outcome in the given market.
    """
    outcome_token = self.get_wrapped_token_for_outcome(outcome)
    api_keys = api_keys if api_keys is not None else APIKeys()

    token_amount = (
        amount.as_outcome_wei.as_wei
        if isinstance(amount, OutcomeToken)
        else self.get_in_token(amount).as_wei
    )

    return self._swap_tokens_with_fallback(
        sell_token=outcome_token,
        buy_token=Web3.to_checksum_address(
            self.collateral_token_contract_address_checksummed
        ),
        amount_wei=token_amount,
        api_keys=api_keys,
        web3=web3,
    )
get_token_balance
get_token_balance(
    user_id: str,
    outcome: OutcomeStr,
    web3: Web3 | None = None,
) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/seer/seer.py
575
576
577
578
579
580
581
582
583
584
585
def get_token_balance(
    self, user_id: str, outcome: OutcomeStr, web3: Web3 | None = None
) -> OutcomeToken:
    erc20_token = ContractERC20OnGnosisChain(
        address=self.get_wrapped_token_for_outcome(outcome)
    )
    return OutcomeToken.from_token(
        erc20_token.balance_of_in_tokens(
            for_address=Web3.to_checksum_address(user_id), web3=web3
        )
    )
validate_probabilities
validate_probabilities(
    probs: dict[OutcomeStr, Probability],
    info: FieldValidationInfo,
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
@field_validator("probabilities")
def validate_probabilities(
    cls,
    probs: dict[OutcomeStr, Probability],
    info: FieldValidationInfo,
) -> dict[OutcomeStr, Probability]:
    outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
    if set(probs.keys()) != set(outcomes):
        raise ValueError("Keys of `probabilities` must match `outcomes` exactly.")
    total = float(sum(probs.values()))
    if not 0.999 <= total <= 1.001:
        # We simply log a warning because for some use-cases (e.g. existing positions), the
        # markets might be already closed hence no reliable outcome token prices exist anymore.
        logger.warning(f"Probabilities for market {info.data=} do not sum to 1.")
    return probs
validate_outcome_token_pool
validate_outcome_token_pool(
    outcome_token_pool: dict[str, OutcomeToken] | None,
    info: FieldValidationInfo,
) -> dict[str, OutcomeToken] | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
@field_validator("outcome_token_pool")
def validate_outcome_token_pool(
    cls,
    outcome_token_pool: dict[str, OutcomeToken] | None,
    info: FieldValidationInfo,
) -> dict[str, OutcomeToken] | None:
    outcomes: t.Sequence[OutcomeStr] = check_not_none(info.data.get("outcomes"))
    if outcome_token_pool is not None:
        outcome_keys = set(outcome_token_pool.keys())
        expected_keys = set(outcomes)
        if outcome_keys != expected_keys:
            raise ValueError(
                f"Keys of outcome_token_pool ({outcome_keys}) do not match outcomes ({expected_keys})."
            )
    return outcome_token_pool
get_outcome_token_pool_by_outcome
get_outcome_token_pool_by_outcome(
    outcome: OutcomeStr,
) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
122
123
124
125
126
127
128
def get_outcome_token_pool_by_outcome(self, outcome: OutcomeStr) -> OutcomeToken:
    if self.outcome_token_pool is None or not self.outcome_token_pool:
        return OutcomeToken(0)

    # We look up by index to avoid having to deal with case sensitivity issues.
    outcome_idx = self.get_outcome_index(outcome)
    return list(self.outcome_token_pool.values())[outcome_idx]
handle_legacy_fee
handle_legacy_fee(data: dict[str, Any]) -> dict[str, t.Any]
Source code in prediction_market_agent_tooling/markets/agent_market.py
130
131
132
133
134
135
136
@model_validator(mode="before")
def handle_legacy_fee(cls, data: dict[str, t.Any]) -> dict[str, t.Any]:
    # Backward compatibility for older `AgentMarket` without `fees`.
    if "fees" not in data and "fee" in data:
        data["fees"] = MarketFees(absolute=0.0, bet_proportion=data["fee"])
        del data["fee"]
    return data
market_outcome_for_probability_key
market_outcome_for_probability_key(
    probability_key: OutcomeStr,
) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
138
139
140
141
142
143
144
145
146
def market_outcome_for_probability_key(
    self, probability_key: OutcomeStr
) -> OutcomeStr:
    for market_outcome in self.outcomes:
        if market_outcome.lower() == probability_key.lower():
            return market_outcome
    raise ValueError(
        f"Could not find probability for probability key {probability_key}"
    )
probability_for_market_outcome
probability_for_market_outcome(
    market_outcome: OutcomeStr,
) -> Probability
Source code in prediction_market_agent_tooling/markets/agent_market.py
148
149
150
151
152
153
154
def probability_for_market_outcome(self, market_outcome: OutcomeStr) -> Probability:
    for k, v in self.probabilities.items():
        if k.lower() == market_outcome.lower():
            return v
    raise ValueError(
        f"Could not find probability for market outcome {market_outcome}"
    )
get_last_trade_p_yes
get_last_trade_p_yes() -> Probability | None

Get the last trade price for the YES outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1. Could be None if no trades were made.

Source code in prediction_market_agent_tooling/markets/agent_market.py
195
196
197
198
199
200
def get_last_trade_p_yes(self) -> Probability | None:
    """
    Get the last trade price for the YES outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1.
    Could be None if no trades were made.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_last_trade_p_no
get_last_trade_p_no() -> Probability | None

Get the last trade price for the NO outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1. Could be None if no trades were made.

Source code in prediction_market_agent_tooling/markets/agent_market.py
202
203
204
205
206
207
def get_last_trade_p_no(self) -> Probability | None:
    """
    Get the last trade price for the NO outcome. This can be different from the current p_yes, for example if market is closed and it's probabilities are fixed to 0 and 1.
    Could be None if no trades were made.
    """
    raise NotImplementedError("Subclasses must implement this method")
get_last_trade_yes_outcome_price
get_last_trade_yes_outcome_price() -> (
    CollateralToken | None
)
Source code in prediction_market_agent_tooling/markets/agent_market.py
209
210
211
212
213
214
def get_last_trade_yes_outcome_price(self) -> CollateralToken | None:
    # Price on prediction markets are, by definition, equal to the probability of an outcome.
    # Just making it explicit in this function.
    if last_trade_p_yes := self.get_last_trade_p_yes():
        return CollateralToken(last_trade_p_yes)
    return None
get_last_trade_yes_outcome_price_usd
get_last_trade_yes_outcome_price_usd() -> USD | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
216
217
218
219
def get_last_trade_yes_outcome_price_usd(self) -> USD | None:
    if last_trade_yes_outcome_price := self.get_last_trade_yes_outcome_price():
        return self.get_token_in_usd(last_trade_yes_outcome_price)
    return None
get_last_trade_no_outcome_price
get_last_trade_no_outcome_price() -> CollateralToken | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
221
222
223
224
225
226
def get_last_trade_no_outcome_price(self) -> CollateralToken | None:
    # Price on prediction markets are, by definition, equal to the probability of an outcome.
    # Just making it explicit in this function.
    if last_trade_p_no := self.get_last_trade_p_no():
        return CollateralToken(last_trade_p_no)
    return None
get_last_trade_no_outcome_price_usd
get_last_trade_no_outcome_price_usd() -> USD | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
228
229
230
231
def get_last_trade_no_outcome_price_usd(self) -> USD | None:
    if last_trade_no_outcome_price := self.get_last_trade_no_outcome_price():
        return self.get_token_in_usd(last_trade_no_outcome_price)
    return None
get_liquidatable_amount
get_liquidatable_amount() -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
233
234
235
def get_liquidatable_amount(self) -> OutcomeToken:
    tiny_amount = self.get_tiny_bet_amount()
    return OutcomeToken.from_token(tiny_amount / 10)
get_in_usd
get_in_usd(x: USD | CollateralToken) -> USD
Source code in prediction_market_agent_tooling/markets/agent_market.py
258
259
260
261
def get_in_usd(self, x: USD | CollateralToken) -> USD:
    if isinstance(x, USD):
        return x
    return self.get_token_in_usd(x)
get_in_token
get_in_token(x: USD | CollateralToken) -> CollateralToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
263
264
265
266
def get_in_token(self, x: USD | CollateralToken) -> CollateralToken:
    if isinstance(x, CollateralToken):
        return x
    return self.get_usd_in_token(x)
liquidate_existing_positions
liquidate_existing_positions(outcome: OutcomeStr) -> None
Source code in prediction_market_agent_tooling/markets/agent_market.py
274
275
def liquidate_existing_positions(self, outcome: OutcomeStr) -> None:
    raise NotImplementedError("Subclasses must implement this method")
buy_tokens
buy_tokens(outcome: OutcomeStr, amount: USD) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
280
281
def buy_tokens(self, outcome: OutcomeStr, amount: USD) -> str:
    return self.place_bet(outcome=outcome, amount=amount)
compute_fpmm_probabilities staticmethod
compute_fpmm_probabilities(
    balances: list[OutcomeWei],
) -> list[Probability]

Compute the implied probabilities in a Fixed Product Market Maker.

Parameters:

Name Type Description Default
balances List[float]

Balances of outcome tokens.

required

Returns:

Type Description
list[Probability]

List[float]: Implied probabilities for each outcome.

Source code in prediction_market_agent_tooling/markets/agent_market.py
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
@staticmethod
def compute_fpmm_probabilities(balances: list[OutcomeWei]) -> list[Probability]:
    """
    Compute the implied probabilities in a Fixed Product Market Maker.

    Args:
        balances (List[float]): Balances of outcome tokens.

    Returns:
        List[float]: Implied probabilities for each outcome.
    """
    if all(x.value == 0 for x in balances):
        return [Probability(0.0)] * len(balances)

    # converting to standard values for prod compatibility.
    values_balance = [i.value for i in balances]
    # Compute product of balances excluding each outcome
    excluded_products = []
    for i in range(len(values_balance)):
        other_balances = values_balance[:i] + values_balance[i + 1 :]
        excluded_products.append(prod(other_balances))

    # Normalize to sum to 1
    total = sum(excluded_products)
    if total == 0:
        return [Probability(0.0)] * len(balances)
    probabilities = [Probability(p / total) for p in excluded_products]

    return probabilities
build_probability_map_from_p_yes staticmethod
build_probability_map_from_p_yes(
    p_yes: Probability,
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
321
322
323
324
325
326
327
328
@staticmethod
def build_probability_map_from_p_yes(
    p_yes: Probability,
) -> dict[OutcomeStr, Probability]:
    return {
        OutcomeStr(YES_OUTCOME_LOWERCASE_IDENTIFIER): p_yes,
        OutcomeStr(NO_OUTCOME_LOWERCASE_IDENTIFIER): Probability(1.0 - p_yes),
    }
build_probability_map staticmethod
build_probability_map(
    outcome_token_amounts: list[OutcomeWei],
    outcomes: list[OutcomeStr],
) -> dict[OutcomeStr, Probability]
Source code in prediction_market_agent_tooling/markets/agent_market.py
330
331
332
333
334
335
@staticmethod
def build_probability_map(
    outcome_token_amounts: list[OutcomeWei], outcomes: list[OutcomeStr]
) -> dict[OutcomeStr, Probability]:
    probs = AgentMarket.compute_fpmm_probabilities(outcome_token_amounts)
    return {outcome: prob for outcome, prob in zip(outcomes, probs)}
get_binary_market staticmethod
get_binary_market(id: str) -> AgentMarket
Source code in prediction_market_agent_tooling/markets/agent_market.py
348
349
350
@staticmethod
def get_binary_market(id: str) -> "AgentMarket":
    raise NotImplementedError("Subclasses must implement this method")
get_bets_made_since staticmethod
get_bets_made_since(
    better_address: ChecksumAddress, start_time: DatetimeUTC
) -> list[Bet]
Source code in prediction_market_agent_tooling/markets/agent_market.py
397
398
399
400
401
@staticmethod
def get_bets_made_since(
    better_address: ChecksumAddress, start_time: DatetimeUTC
) -> list[Bet]:
    raise NotImplementedError("Subclasses must implement this method")
get_resolved_bets_made_since staticmethod
get_resolved_bets_made_since(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC,
    end_time: DatetimeUTC | None,
) -> list[ResolvedBet]
Source code in prediction_market_agent_tooling/markets/agent_market.py
403
404
405
406
407
408
409
@staticmethod
def get_resolved_bets_made_since(
    better_address: ChecksumAddress,
    start_time: DatetimeUTC,
    end_time: DatetimeUTC | None,
) -> list[ResolvedBet]:
    raise NotImplementedError("Subclasses must implement this method")
is_closed
is_closed() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
411
412
def is_closed(self) -> bool:
    return self.close_time is not None and self.close_time <= utcnow()
is_resolved
is_resolved() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
414
415
def is_resolved(self) -> bool:
    return self.resolution is not None
has_successful_resolution
has_successful_resolution() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
423
424
425
426
427
428
def has_successful_resolution(self) -> bool:
    return (
        self.resolution is not None
        and self.resolution.outcome is not None
        and not self.resolution.invalid
    )
has_unsuccessful_resolution
has_unsuccessful_resolution() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
430
431
def has_unsuccessful_resolution(self) -> bool:
    return self.resolution is not None and self.resolution.invalid
get_outcome_str_from_bool staticmethod
get_outcome_str_from_bool(outcome: bool) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
433
434
435
@staticmethod
def get_outcome_str_from_bool(outcome: bool) -> OutcomeStr:
    raise NotImplementedError("Subclasses must implement this method")
get_outcome_str
get_outcome_str(outcome_index: int) -> OutcomeStr
Source code in prediction_market_agent_tooling/markets/agent_market.py
437
438
439
440
441
442
443
def get_outcome_str(self, outcome_index: int) -> OutcomeStr:
    try:
        return self.outcomes[outcome_index]
    except IndexError:
        raise IndexError(
            f"Outcome index `{outcome_index}` out of range for `{self.outcomes}`: `{self.outcomes}`."
        )
get_outcome_index
get_outcome_index(outcome: OutcomeStr) -> int
Source code in prediction_market_agent_tooling/markets/agent_market.py
445
446
447
448
449
450
def get_outcome_index(self, outcome: OutcomeStr) -> int:
    outcomes_lowercase = [o.lower() for o in self.outcomes]
    try:
        return outcomes_lowercase.index(outcome.lower())
    except ValueError:
        raise ValueError(f"Outcome `{outcome}` not found in `{self.outcomes}`.")
get_positions classmethod
get_positions(
    user_id: str,
    liquid_only: bool = False,
    larger_than: OutcomeToken = OutcomeToken(0),
) -> t.Sequence[ExistingPosition]

Get all non-zero positions a user has in any market.

If liquid_only is True, only return positions that can be sold.

If larger_than is not None, only return positions with a larger number of tokens than this amount.

Source code in prediction_market_agent_tooling/markets/agent_market.py
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
@classmethod
def get_positions(
    cls,
    user_id: str,
    liquid_only: bool = False,
    larger_than: OutcomeToken = OutcomeToken(0),
) -> t.Sequence[ExistingPosition]:
    """
    Get all non-zero positions a user has in any market.

    If `liquid_only` is True, only return positions that can be sold.

    If `larger_than` is not None, only return positions with a larger number
    of tokens than this amount.
    """
    raise NotImplementedError("Subclasses must implement this method")
can_be_traded
can_be_traded() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
475
476
477
478
def can_be_traded(self) -> bool:
    if self.is_closed() or not self.has_liquidity():
        return False
    return True
get_user_url classmethod
get_user_url(keys: APIKeys) -> str
Source code in prediction_market_agent_tooling/markets/agent_market.py
480
481
482
@classmethod
def get_user_url(cls, keys: APIKeys) -> str:
    raise NotImplementedError("Subclasses must implement this method")
has_token_pool
has_token_pool() -> bool
Source code in prediction_market_agent_tooling/markets/agent_market.py
484
485
def has_token_pool(self) -> bool:
    return self.outcome_token_pool is not None
get_pool_tokens
get_pool_tokens(outcome: OutcomeStr) -> OutcomeToken
Source code in prediction_market_agent_tooling/markets/agent_market.py
487
488
489
490
491
def get_pool_tokens(self, outcome: OutcomeStr) -> OutcomeToken:
    if not self.outcome_token_pool:
        raise ValueError("Outcome token pool is not available.")

    return self.outcome_token_pool[outcome]
get_user_balance staticmethod
get_user_balance(user_id: str) -> float
Source code in prediction_market_agent_tooling/markets/agent_market.py
493
494
495
@staticmethod
def get_user_balance(user_id: str) -> float:
    raise NotImplementedError("Subclasses must implement this method")
get_most_recent_trade_datetime
get_most_recent_trade_datetime(
    user_id: str,
) -> DatetimeUTC | None
Source code in prediction_market_agent_tooling/markets/agent_market.py
501
502
def get_most_recent_trade_datetime(self, user_id: str) -> DatetimeUTC | None:
    raise NotImplementedError("Subclasses must implement this method")
seer_create_market_tx
seer_create_market_tx(
    api_keys: APIKeys,
    initial_funds: USD | CollateralToken,
    question: str,
    opening_time: DatetimeUTC,
    language: str,
    outcomes: Sequence[OutcomeStr],
    auto_deposit: bool,
    category: str,
    min_bond: xDai,
    web3: Web3 | None = None,
) -> ChecksumAddress
Source code in prediction_market_agent_tooling/markets/seer/seer.py
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
def seer_create_market_tx(
    api_keys: APIKeys,
    initial_funds: USD | CollateralToken,
    question: str,
    opening_time: DatetimeUTC,
    language: str,
    outcomes: t.Sequence[OutcomeStr],
    auto_deposit: bool,
    category: str,
    min_bond: xDai,
    web3: Web3 | None = None,
) -> ChecksumAddress:
    web3 = web3 or SeerMarketFactory.get_web3()  # Default to Gnosis web3.

    factory_contract = SeerMarketFactory()
    collateral_token_address = factory_contract.collateral_token(web3=web3)
    collateral_token_contract = to_gnosis_chain_contract(
        init_collateral_token_contract(collateral_token_address, web3)
    )

    initial_funds_in_collateral = (
        get_usd_in_token(initial_funds, collateral_token_address)
        if isinstance(initial_funds, USD)
        else initial_funds
    )
    initial_funds_in_collateral_wei = initial_funds_in_collateral.as_wei

    if auto_deposit:
        auto_deposit_collateral_token(
            collateral_token_contract=collateral_token_contract,
            api_keys=api_keys,
            collateral_amount_wei_or_usd=initial_funds_in_collateral_wei,
            web3=web3,
        )

    # Approve the market maker to withdraw our collateral token.
    collateral_token_contract.approve(
        api_keys=api_keys,
        for_address=factory_contract.address,
        amount_wei=initial_funds_in_collateral_wei,
        web3=web3,
    )

    # Create the market.
    params = factory_contract.build_market_params(
        market_question=question,
        outcomes=outcomes,
        opening_time=opening_time,
        language=language,
        category=category,
        min_bond=min_bond,
    )
    tx_receipt = factory_contract.create_categorical_market(
        api_keys=api_keys, params=params, web3=web3
    )

    # ToDo - Add liquidity to market on Swapr (https://github.com/gnosis/prediction-market-agent-tooling/issues/497)
    market_address = extract_market_address_from_tx(
        factory_contract=factory_contract, tx_receipt=tx_receipt, web3=web3
    )
    return market_address
extract_market_address_from_tx
extract_market_address_from_tx(
    factory_contract: SeerMarketFactory,
    tx_receipt: TxReceipt,
    web3: Web3,
) -> ChecksumAddress

We extract the newly created market from the NewMarket event emitted in the transaction.

Source code in prediction_market_agent_tooling/markets/seer/seer.py
651
652
653
654
655
656
657
658
659
660
661
def extract_market_address_from_tx(
    factory_contract: SeerMarketFactory, tx_receipt: TxReceipt, web3: Web3
) -> ChecksumAddress:
    """We extract the newly created market from the NewMarket event emitted in the transaction."""
    event_logs = (
        factory_contract.get_web3_contract(web3=web3)
        .events.NewMarket()
        .process_receipt(tx_receipt)
    )
    new_market_event = NewMarketEvent(**event_logs[0]["args"])
    return Web3.to_checksum_address(new_market_event.market)

seer_contracts

SeerMarketFactory

Bases: ContractOnGnosisChain

abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../../abis/seer_market_factory.abi.json",
    )
)
address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0x83183da839ce8228e31ae41222ead9edbb5cdcf1"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
build_market_params staticmethod
build_market_params(
    market_question: str,
    outcomes: Sequence[OutcomeStr],
    opening_time: DatetimeUTC,
    min_bond: xDai,
    language: str = "en_US",
    category: str = "misc",
) -> CreateCategoricalMarketsParams
Source code in prediction_market_agent_tooling/markets/seer/seer_contracts.py
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@staticmethod
def build_market_params(
    market_question: str,
    outcomes: t.Sequence[OutcomeStr],
    opening_time: DatetimeUTC,
    min_bond: xDai,
    language: str = "en_US",
    category: str = "misc",
) -> CreateCategoricalMarketsParams:
    return CreateCategoricalMarketsParams(
        market_name=market_question,
        token_names=[
            o.upper() for o in outcomes
        ],  # Following usual token names on Seer (YES,NO).
        min_bond=min_bond.as_xdai_wei.value,
        opening_time=int(opening_time.timestamp()),
        outcomes=list(outcomes),
        lang=language,
        category=category,
    )
market_count
market_count(web3: Web3 | None = None) -> int
Source code in prediction_market_agent_tooling/markets/seer/seer_contracts.py
62
63
64
def market_count(self, web3: Web3 | None = None) -> int:
    count: int = self.call("marketCount", web3=web3)
    return count
market_at_index
market_at_index(
    index: int, web3: Web3 | None = None
) -> ChecksumAddress
Source code in prediction_market_agent_tooling/markets/seer/seer_contracts.py
66
67
68
def market_at_index(self, index: int, web3: Web3 | None = None) -> ChecksumAddress:
    market_address: str = self.call("markets", function_params=[index], web3=web3)
    return Web3.to_checksum_address(market_address)
collateral_token
collateral_token(
    web3: Web3 | None = None,
) -> ChecksumAddress
Source code in prediction_market_agent_tooling/markets/seer/seer_contracts.py
70
71
72
def collateral_token(self, web3: Web3 | None = None) -> ChecksumAddress:
    collateral_token_address: str = self.call("collateralToken", web3=web3)
    return Web3.to_checksum_address(collateral_token_address)
create_categorical_market
create_categorical_market(
    api_keys: APIKeys,
    params: CreateCategoricalMarketsParams,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/seer/seer_contracts.py
74
75
76
77
78
79
80
81
82
83
84
85
86
def create_categorical_market(
    self,
    api_keys: APIKeys,
    params: CreateCategoricalMarketsParams,
    web3: Web3 | None = None,
) -> TxReceipt:
    receipt_tx = self.send(
        api_keys=api_keys,
        function_name="createCategoricalMarket",
        function_params=[params.model_dump(by_alias=True)],
        web3=web3,
    )
    return receipt_tx
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
GnosisRouter

Bases: ContractOnGnosisChain

abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../../abis/seer_gnosis_router.abi.json",
    )
)
address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0xeC9048b59b3467415b1a38F63416407eA0c70fB8"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
redeem_to_base
redeem_to_base(
    api_keys: APIKeys,
    params: RedeemParams,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/seer/seer_contracts.py
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
def redeem_to_base(
    self,
    api_keys: APIKeys,
    params: RedeemParams,
    web3: Web3 | None = None,
) -> TxReceipt:
    params_dict = params.model_dump(by_alias=True)
    # We explicity set amounts since OutcomeWei gets serialized as dict
    params_dict["amounts"] = [amount.value for amount in params.amounts]
    receipt_tx = self.send(
        api_keys=api_keys,
        function_name="redeemToBase",
        function_params=params_dict,
        web3=web3,
    )
    return receipt_tx
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()
SwaprRouterContract

Bases: ContractOnGnosisChain

abi class-attribute instance-attribute
abi: ABI = abi_field_validator(
    join(
        dirname(realpath(__file__)),
        "../../abis/swapr_router.abi.json",
    )
)
address class-attribute instance-attribute
address: ChecksumAddress = to_checksum_address(
    "0xffb643e73f280b97809a8b41f7232ab401a04ee1"
)
CHAIN_ID class-attribute instance-attribute
CHAIN_ID = chain_id
exact_input_single
exact_input_single(
    api_keys: APIKeys,
    params: ExactInputSingleParams,
    web3: Web3 | None = None,
) -> TxReceipt
Source code in prediction_market_agent_tooling/markets/seer/seer_contracts.py
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
def exact_input_single(
    self,
    api_keys: APIKeys,
    params: ExactInputSingleParams,
    web3: Web3 | None = None,
) -> TxReceipt:
    erc20_token = ContractERC20OnGnosisChain(address=params.token_in)

    if (
        erc20_token.allowance(api_keys.bet_from_address, self.address, web3=web3)
        < params.amount_in
    ):
        erc20_token.approve(api_keys, self.address, params.amount_in, web3=web3)

    return self.send(
        api_keys=api_keys,
        function_name="exactInputSingle",
        function_params=[tuple(dict(params).values())],
        web3=web3,
    )
merge_contract_abis classmethod
merge_contract_abis(
    contracts: list[ContractBaseClass],
) -> ABI
Source code in prediction_market_agent_tooling/tools/contract.py
77
78
79
80
81
82
@classmethod
def merge_contract_abis(cls, contracts: list["ContractBaseClass"]) -> ABI:
    merged_abi: list[dict[t.Any, t.Any]] = sum(
        (json.loads(contract.abi) for contract in contracts), []
    )
    return abi_field_validator(json.dumps(merged_abi))
get_web3_contract
get_web3_contract(web3: Web3 | None = None) -> Web3Contract
Source code in prediction_market_agent_tooling/tools/contract.py
84
85
86
def get_web3_contract(self, web3: Web3 | None = None) -> Web3Contract:
    web3 = web3 or self.get_web3()
    return web3.eth.contract(address=self.address, abi=self.abi)
call
call(
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    web3: Web3 | None = None,
) -> t.Any

Used for reading from the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def call(
    self,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    web3: Web3 | None = None,
) -> t.Any:
    """
    Used for reading from the contract.
    """
    web3 = web3 or self.get_web3()
    return call_function_on_contract(
        web3=web3,
        contract_address=self.address,
        contract_abi=self.abi,
        function_name=function_name,
        function_params=function_params,
    )
send
send(
    api_keys: APIKeys,
    function_name: str,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract.

Source code in prediction_market_agent_tooling/tools/contract.py
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def send(
    self,
    api_keys: APIKeys,
    function_name: str,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract.
    """

    if api_keys.safe_address_checksum:
        return send_function_on_contract_tx_using_safe(
            web3=web3 or self.get_web3(),
            contract_address=self.address,
            contract_abi=self.abi,
            from_private_key=api_keys.bet_from_private_key,
            safe_address=api_keys.safe_address_checksum,
            function_name=function_name,
            function_params=function_params,
            tx_params=tx_params,
            timeout=timeout,
        )
    return send_function_on_contract_tx(
        web3=web3 or self.get_web3(),
        contract_address=self.address,
        contract_abi=self.abi,
        from_private_key=api_keys.bet_from_private_key,
        function_name=function_name,
        function_params=function_params,
        tx_params=tx_params,
        timeout=timeout,
    )
send_with_value
send_with_value(
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: Optional[
        list[Any] | dict[str, Any]
    ] = None,
    tx_params: Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt

Used for changing a state (writing) to the contract, including sending chain's native currency.

Source code in prediction_market_agent_tooling/tools/contract.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def send_with_value(
    self,
    api_keys: APIKeys,
    function_name: str,
    amount_wei: Wei,
    function_params: t.Optional[list[t.Any] | dict[str, t.Any]] = None,
    tx_params: t.Optional[TxParams] = None,
    timeout: int = 180,
    web3: Web3 | None = None,
) -> TxReceipt:
    """
    Used for changing a state (writing) to the contract, including sending chain's native currency.
    """
    return self.send(
        api_keys=api_keys,
        function_name=function_name,
        function_params=function_params,
        tx_params={"value": amount_wei.value, **(tx_params or {})},
        timeout=timeout,
        web3=web3,
    )
get_transaction_count classmethod
get_transaction_count(
    for_address: ChecksumAddress,
) -> Nonce
Source code in prediction_market_agent_tooling/tools/contract.py
164
165
166
@classmethod
def get_transaction_count(cls, for_address: ChecksumAddress) -> Nonce:
    return cls.get_web3().eth.get_transaction_count(for_address)
get_web3 classmethod
get_web3() -> Web3
Source code in prediction_market_agent_tooling/tools/contract.py
168
169
170
@classmethod
def get_web3(cls) -> Web3:
    return RPCConfig().get_web3()

seer_subgraph_handler

SeerSubgraphHandler
SeerSubgraphHandler()

Bases: BaseSubgraphHandler

Class responsible for handling interactions with Seer subgraphs.

Source code in prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py
36
37
38
39
40
41
42
43
44
45
46
47
48
def __init__(self) -> None:
    super().__init__()

    self.seer_subgraph = self.sg.load_subgraph(
        self.SEER_SUBGRAPH.format(
            graph_api_key=self.keys.graph_api_key.get_secret_value()
        )
    )
    self.swapr_algebra_subgraph = self.sg.load_subgraph(
        self.SWAPR_ALGEBRA_SUBGRAPH.format(
            graph_api_key=self.keys.graph_api_key.get_secret_value()
        )
    )
SEER_SUBGRAPH class-attribute instance-attribute
SEER_SUBGRAPH = "https://gateway-arbitrum.network.thegraph.com/api/{graph_api_key}/subgraphs/id/B4vyRqJaSHD8dRDb3BFRoAzuBK18c1QQcXq94JbxDxWH"
SWAPR_ALGEBRA_SUBGRAPH class-attribute instance-attribute
SWAPR_ALGEBRA_SUBGRAPH = "https://gateway-arbitrum.network.thegraph.com/api/{graph_api_key}/subgraphs/id/AAA1vYjxwFHzbt6qKwLHNcDSASyr1J1xVViDH8gTMFMR"
INVALID_ANSWER class-attribute instance-attribute
INVALID_ANSWER = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
seer_subgraph instance-attribute
seer_subgraph = load_subgraph(
    format(graph_api_key=get_secret_value())
)
swapr_algebra_subgraph instance-attribute
swapr_algebra_subgraph = load_subgraph(
    format(graph_api_key=get_secret_value())
)
sg instance-attribute
sg = Subgrounds(timeout=timeout)
keys instance-attribute
keys = APIKeys()
filter_bicategorical_markets staticmethod
filter_bicategorical_markets(
    markets: list[SeerMarket],
) -> list[SeerMarket]
Source code in prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py
72
73
74
75
@staticmethod
def filter_bicategorical_markets(markets: list[SeerMarket]) -> list[SeerMarket]:
    # We do an extra check for the invalid outcome for safety.
    return [m for m in markets if len(m.outcomes) == 3]
get_markets
get_markets(
    filter_by: FilterBy,
    sort_by: SortBy = SortBy.NONE,
    limit: int | None = None,
    outcome_supply_gt_if_open: Wei = Wei(0),
    include_conditional_markets: bool = True,
    include_categorical_markets: bool = True,
) -> list[SeerMarket]
Source code in prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
def get_markets(
    self,
    filter_by: FilterBy,
    sort_by: SortBy = SortBy.NONE,
    limit: int | None = None,
    outcome_supply_gt_if_open: Wei = Wei(0),
    include_conditional_markets: bool = True,
    include_categorical_markets: bool = True,
) -> list[SeerMarket]:
    sort_direction, sort_by_field = self._build_sort_params(sort_by)

    """Returns markets that contain 2 categories plus an invalid outcome."""
    # Binary markets on Seer contain 3 outcomes: OutcomeA, outcomeB and an Invalid option.
    where_stms = self._build_where_statements(
        filter_by=filter_by,
        outcome_supply_gt_if_open=outcome_supply_gt_if_open,
        include_conditional_markets=include_conditional_markets,
        include_categorical_markets=include_categorical_markets,
    )

    # These values can not be set to `None`, but they can be omitted.
    optional_params = {}
    if sort_by_field is not None:
        optional_params["orderBy"] = sort_by_field
    if sort_direction is not None:
        optional_params["orderDirection"] = sort_direction

    markets_field = self.seer_subgraph.Query.markets(
        first=(
            limit if limit else sys.maxsize
        ),  # if not limit, we fetch all possible markets,
        where=unwrap_generic_value(where_stms),
        **optional_params,
    )
    fields = self._get_fields_for_markets(markets_field)
    markets = self.do_query(fields=fields, pydantic_model=SeerMarket)
    return markets
get_market_by_id
get_market_by_id(market_id: HexBytes) -> SeerMarket
Source code in prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py
192
193
194
195
196
197
198
199
200
def get_market_by_id(self, market_id: HexBytes) -> SeerMarket:
    markets_field = self.seer_subgraph.Query.market(id=market_id.hex().lower())
    fields = self._get_fields_for_markets(markets_field)
    markets = self.do_query(fields=fields, pydantic_model=SeerMarket)
    if len(markets) != 1:
        raise ValueError(
            f"Fetched wrong number of markets. Expected 1 but got {len(markets)}"
        )
    return markets[0]
get_pool_by_token
get_pool_by_token(
    token_address: ChecksumAddress,
    collateral_address: ChecksumAddress,
) -> SeerPool | None
Source code in prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def get_pool_by_token(
    self, token_address: ChecksumAddress, collateral_address: ChecksumAddress
) -> SeerPool | None:
    # We iterate through the wrapped tokens and put them in a where clause so that we hit the subgraph endpoint just once.
    wheres = []
    wheres.extend(
        [
            {
                "token0": token_address.lower(),
                "token1": collateral_address.lower(),
            },
            {
                "token0": collateral_address.lower(),
                "token1": token_address.lower(),
            },
        ]
    )

    optional_params = {}
    optional_params["orderBy"] = self.swapr_algebra_subgraph.Pool.liquidity
    optional_params["orderDirection"] = "desc"

    pools_field = self.swapr_algebra_subgraph.Query.pools(
        where=unwrap_generic_value({"or": wheres}), **optional_params
    )

    fields = self._get_fields_for_pools(pools_field)
    pools = self.do_query(fields=fields, pydantic_model=SeerPool)
    # We assume there is only one pool for outcomeToken/sDAI.
    if len(pools) > 1:
        logger.info(
            f"Multiple pools found for token {token_address}, selecting the first."
        )
    if pools:
        # We select the first one
        return pools[0]
    return None
do_query
do_query(
    fields: list[FieldPath], pydantic_model: Type[T]
) -> list[T]
Source code in prediction_market_agent_tooling/markets/base_subgraph_handler.py
47
48
49
50
51
def do_query(self, fields: list[FieldPath], pydantic_model: t.Type[T]) -> list[T]:
    result = self.sg.query_json(fields)
    items = self._parse_items_from_json(result)
    models = [pydantic_model.model_validate(i) for i in items]
    return models

subgraph_data_models

SeerToken

Bases: BaseModel

id instance-attribute
id: HexBytes
name instance-attribute
name: str
symbol instance-attribute
symbol: str
SeerPool

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(populate_by_name=True)
id instance-attribute
id: HexBytes
liquidity instance-attribute
liquidity: int
token0 instance-attribute
token0: SeerToken
token1 instance-attribute
token1: SeerToken
token0Price instance-attribute
token0Price: CollateralToken
token1Price instance-attribute
token1Price: CollateralToken
sqrtPrice instance-attribute
sqrtPrice: int
NewMarketEvent

Bases: BaseModel

market instance-attribute
market: HexAddress
marketName instance-attribute
marketName: str
parentMarket instance-attribute
parentMarket: HexAddress
conditionId instance-attribute
conditionId: HexBytes
questionId instance-attribute
questionId: HexBytes
questionsIds instance-attribute
questionsIds: list[HexBytes]
CreateCategoricalMarketsParams

Bases: BaseModel

model_config class-attribute instance-attribute
model_config = ConfigDict(populate_by_name=True)
market_name class-attribute instance-attribute
market_name: str = Field(..., alias='marketName')
outcomes instance-attribute
outcomes: list[OutcomeStr]
question_start class-attribute instance-attribute
question_start: str = Field(
    alias="questionStart", default=""
)
question_end class-attribute instance-attribute
question_end: str = Field(alias='questionEnd', default='')
outcome_type class-attribute instance-attribute
outcome_type: str = Field(alias='outcomeType', default='')
parent_outcome class-attribute instance-attribute
parent_outcome: int = Field(
    alias="parentOutcome", default=0
)
parent_market class-attribute instance-attribute
parent_market: HexAddress = Field(
    alias="parentMarket", default=ADDRESS_ZERO
)
category instance-attribute
category: str
lang instance-attribute
lang: str
lower_bound class-attribute instance-attribute
lower_bound: int = Field(alias='lowerBound', default=0)
upper_bound class-attribute instance-attribute
upper_bound: int = Field(alias='upperBound', default=0)
min_bond class-attribute instance-attribute
min_bond: int = Field(..., alias='minBond')
opening_time class-attribute instance-attribute
opening_time: int = Field(..., alias='openingTime')
token_names class-attribute instance-attribute
token_names: list[str] = Field(..., alias='tokenNames')
SeerParentMarket

Bases: BaseModel

id instance-attribute
id: HexBytes

swap_pool_handler

SwapPoolHandler
SwapPoolHandler(
    api_keys: APIKeys,
    market_id: str,
    collateral_token_address: ChecksumAddress,
    seer_subgraph: SeerSubgraphHandler | None = None,
)
Source code in prediction_market_agent_tooling/markets/seer/swap_pool_handler.py
25
26
27
28
29
30
31
32
33
34
35
def __init__(
    self,
    api_keys: APIKeys,
    market_id: str,
    collateral_token_address: ChecksumAddress,
    seer_subgraph: SeerSubgraphHandler | None = None,
):
    self.api_keys = api_keys
    self.market_id = market_id
    self.collateral_token_address = collateral_token_address
    self.seer_subgraph = seer_subgraph or SeerSubgraphHandler()
api_keys instance-attribute
api_keys = api_keys
market_id instance-attribute
market_id = market_id
collateral_token_address instance-attribute
collateral_token_address = collateral_token_address
seer_subgraph instance-attribute
seer_subgraph = seer_subgraph or SeerSubgraphHandler()
buy_or_sell_outcome_token
buy_or_sell_outcome_token(
    amount_wei: Wei,
    token_in: ChecksumAddress,
    token_out: ChecksumAddress,
    web3: Web3 | None = None,
) -> TxReceipt

Buys/sells outcome_tokens in exchange for collateral tokens

Source code in prediction_market_agent_tooling/markets/seer/swap_pool_handler.py
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def buy_or_sell_outcome_token(
    self,
    amount_wei: Wei,
    token_in: ChecksumAddress,
    token_out: ChecksumAddress,
    web3: Web3 | None = None,
) -> TxReceipt:
    """Buys/sells outcome_tokens in exchange for collateral tokens"""
    if self.collateral_token_address not in [token_in, token_out]:
        raise ValueError(
            f"trading outcome_token for a token different than collateral_token {self.collateral_token_address} is not supported. {token_in=} {token_out=}"
        )

    outcome_token = (
        token_in if token_in != self.collateral_token_address else token_out
    )

    # We could use a quoter contract (https://github.com/SwaprHQ/swapr-sdk/blob/develop/src/entities/trades/swapr-v3/constants.ts#L7), but since there is normally 1 pool per outcome token/collateral pair, it's not necessary.

    price_outcome_token = PriceManager.build(
        HexBytes(HexStr(self.market_id))
    ).get_token_price_from_pools(token=outcome_token)
    if not price_outcome_token:
        raise ValueError(
            f"Could not find price for {outcome_token=} and {self.collateral_token_address}"
        )

    amount_out_minimum = self._calculate_amount_out_minimum(
        amount_wei=amount_wei,
        token_in=token_in,
        price_outcome_token=price_outcome_token,
    )

    p = ExactInputSingleParams(
        token_in=token_in,
        token_out=token_out,
        recipient=self.api_keys.bet_from_address,
        amount_in=amount_wei,
        amount_out_minimum=amount_out_minimum,
    )

    tx_receipt = SwaprRouterContract().exact_input_single(
        api_keys=self.api_keys, params=p, web3=web3
    )
    return tx_receipt