Skip to content

Commit

Permalink
Merge branch 'main' into evan/kelly-bet-full
Browse files Browse the repository at this point in the history
  • Loading branch information
evangriffiths committed Sep 10, 2024
2 parents 43b4b97 + fffbc83 commit e5fcd68
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 224 deletions.
393 changes: 199 additions & 194 deletions poetry.lock

Large diffs are not rendered by default.

17 changes: 15 additions & 2 deletions prediction_market_agent_tooling/deploy/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
SortBy,
)
from prediction_market_agent_tooling.markets.data_models import (
Position,
ProbabilisticAnswer,
Trade,
)
Expand Down Expand Up @@ -299,6 +300,7 @@ def initialize_langfuse(self) -> None:
self.verify_market = observe()(self.verify_market) # type: ignore[method-assign]
self.answer_binary_market = observe()(self.answer_binary_market) # type: ignore[method-assign]
self.process_market = observe()(self.process_market) # type: ignore[method-assign]
self.build_trades = observe()(self.build_trades) # type: ignore[method-assign]

def update_langfuse_trace_by_market(
self, market_type: MarketType, market: AgentMarket
Expand Down Expand Up @@ -403,6 +405,16 @@ def get_markets(
)
return available_markets

def build_trades(
self,
market: AgentMarket,
answer: ProbabilisticAnswer,
existing_position: Position | None,
) -> list[Trade]:
trades = self.strategy.calculate_trades(existing_position, answer, market)
BettingStrategy.assert_trades_currency_match_markets(market, trades)
return trades

def before_process_market(
self, market_type: MarketType, market: AgentMarket
) -> None:
Expand All @@ -429,8 +441,9 @@ def process_market(
return None

existing_position = market.get_position(user_id=APIKeys().bet_from_address)
trades = self.strategy.calculate_trades(existing_position, answer, market)
BettingStrategy.assert_trades_currency_match_markets(market, trades)
trades = self.build_trades(
market=market, answer=answer, existing_position=existing_position
)

if self.place_bet:
for trade in trades:
Expand Down
2 changes: 1 addition & 1 deletion prediction_market_agent_tooling/deploy/betting_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def _build_rebalance_trades_from_positions(
continue
trade_type = TradeType.SELL if diff_amount < 0 else TradeType.BUY
trade = Trade(
amount=TokenAmount(amount=diff_amount, currency=market.currency),
amount=TokenAmount(amount=abs(diff_amount), currency=market.currency),
outcome=outcome_bool,
trade_type=trade_type,
)
Expand Down
11 changes: 8 additions & 3 deletions prediction_market_agent_tooling/markets/omen/omen.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,32 +611,36 @@ def omen_buy_outcome_tx(
market_contract: OmenFixedProductMarketMakerContract = market.get_contract()
collateral_token_contract = market_contract.get_collateral_token_contract()

# In case of ERC4626, obtained (for example) sDai out of xDai could be lower than the `amount_wei`, so we need to handle it.
amount_wei_to_buy = collateral_token_contract.get_in_shares(amount_wei, web3)

# 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
amount_wei_to_buy, outcome_index, web3=web3
)
# Allow 1% slippage.
expected_shares = remove_fraction(expected_shares, 0.01)
# 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,
amount_wei=amount_wei_to_buy,
web3=web3,
)

if auto_deposit:
# In auto-depositing, we need to deposit the original `amount_wei`, e.g. we can deposit 2 xDai, but receive 1.8 sDai, so for the bet we will use `amount_wei_to_buy`.
auto_deposit_collateral_token(
collateral_token_contract, amount_wei, api_keys, web3
)

# Buy shares using the deposited xDai in the collateral token.
market_contract.buy(
api_keys=api_keys,
amount_wei=amount_wei,
amount_wei=amount_wei_to_buy,
outcome_index=outcome_index,
min_outcome_tokens_to_buy=expected_shares,
web3=web3,
Expand Down Expand Up @@ -827,6 +831,7 @@ def omen_create_market_tx(
web3=web3,
)

# In case of ERC4626, obtained (for example) sDai out of xDai could be lower than the `amount_wei`, so we need to handle it.
initial_funds_in_shares = collateral_token_contract.get_in_shares(
amount=initial_funds_wei, web3=web3
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@
byte32_to_ipfscidv0,
)

# TODO: Agents don't know how to convert value between other tokens, we assume 1 unit = 1xDai = $1 (for example if market would be in wETH, betting 1 unit of wETH would be crazy :D)
SAFE_COLLATERAL_TOKEN_MARKETS = (
WrappedxDaiContract().address,
sDaiContract().address,
)


class OmenSubgraphHandler(metaclass=SingletonMeta):
"""
Expand Down Expand Up @@ -318,6 +324,8 @@ def get_omen_binary_markets_simple(
# Additional filters, these can not be modified by the enums above.
created_after: datetime | None = None,
excluded_questions: set[str] | None = None, # question titles
collateral_token_address_in: tuple[ChecksumAddress, ...]
| None = SAFE_COLLATERAL_TOKEN_MARKETS,
) -> t.List[OmenMarket]:
"""
Simplified `get_omen_binary_markets` method, which allows to fetch markets based on the filter_by and sort_by values.
Expand Down Expand Up @@ -354,6 +362,7 @@ def get_omen_binary_markets_simple(
sort_by_field=sort_by_field,
created_after=created_after,
excluded_questions=excluded_questions,
collateral_token_address_in=collateral_token_address_in,
)

def get_omen_binary_markets(
Expand All @@ -375,12 +384,8 @@ def get_omen_binary_markets(
sort_by_field: FieldPath | None = None,
sort_direction: str | None = None,
outcomes: list[str] = [OMEN_TRUE_OUTCOME, OMEN_FALSE_OUTCOME],
# TODO: Agents don't know how to convert value between other tokens, we assume 1 unit = 1xDai = $1 (for example if market would be in wETH, betting 1 unit of wETH would be crazy :D)
collateral_token_address_in: tuple[ChecksumAddress, ...]
| None = (
WrappedxDaiContract().address,
sDaiContract().address,
),
| None = SAFE_COLLATERAL_TOKEN_MARKETS,
) -> t.List[OmenMarket]:
"""
Complete method to fetch Omen binary markets with various filters, use `get_omen_binary_markets_simple` for simplified version that uses FilterBy and SortBy enums.
Expand Down
7 changes: 6 additions & 1 deletion prediction_market_agent_tooling/tools/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ def get_asset_token_contract(
web3 = web3 or self.get_web3()
contract = init_collateral_token_contract(self.asset(), web3=web3)
assert not isinstance(
contract, ContractERC4626OnGnosisChain
contract, ContractERC4626BaseClass
), "Asset token should be either Depositable Wrapper ERC-20 or ERC-20." # Shrinking down possible types.
return contract

Expand Down Expand Up @@ -417,6 +417,11 @@ class ContractERC4626OnGnosisChain(
ERC-4626 standard base class with Gnosis Chain configuration.
"""

def get_asset_token_contract(
self, web3: Web3 | None = None
) -> ContractERC20OnGnosisChain | ContractDepositableWrapperERC20OnGnosisChain:
return to_gnosis_chain_contract(super().get_asset_token_contract(web3=web3))


def contract_implements_function(
contract_address: ChecksumAddress,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "prediction-market-agent-tooling"
version = "0.48.11"
version = "0.48.12"
description = "Tools to benchmark, deploy and monitor prediction market agents."
authors = ["Gnosis"]
readme = "README.md"
Expand Down
12 changes: 12 additions & 0 deletions tests/contracts/test_contracts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from web3 import Web3

from prediction_market_agent_tooling.markets.omen.omen_contracts import sDaiContract


def test_sdai_asset_balance_of() -> None:
assert (
sDaiContract().get_asset_token_balance(
Web3.to_checksum_address("0x7d3A0DA18e14CCb63375cdC250E8A8399997816F")
)
>= 0
)
41 changes: 24 additions & 17 deletions tests_integration_with_local_chain/markets/omen/test_omen.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from prediction_market_agent_tooling.markets.omen.omen_contracts import (
OMEN_DEFAULT_MARKET_FEE,
ContractDepositableWrapperERC20OnGnosisChain,
ContractERC4626OnGnosisChain,
OmenConditionalTokenContract,
OmenFixedProductMarketMakerContract,
OmenRealitioContract,
Expand Down Expand Up @@ -309,23 +310,41 @@ def get_market_outcome_tokens() -> TokenAmount:
assert np.isclose(remaining_tokens.amount, 0, atol=1e-5)


@pytest.mark.parametrize(
"collateral_token_address, expected_symbol",
[
(WrappedxDaiContract().address, "WXDAI"),
(sDaiContract().address, "sDAI"),
],
)
def test_place_bet_with_autodeposit(
collateral_token_address: ChecksumAddress,
expected_symbol: str,
local_web3: Web3,
test_keys: APIKeys,
) -> None:
market = OmenAgentMarket.from_data_model(pick_binary_market())
market = OmenAgentMarket.from_data_model(
OmenSubgraphHandler().get_omen_binary_markets_simple(
limit=1,
filter_by=FilterBy.OPEN,
sort_by=SortBy.CLOSING_SOONEST,
collateral_token_address_in=(collateral_token_address,),
)[0]
)
initial_balances = get_balances(address=test_keys.bet_from_address, web3=local_web3)
collateral_token_contract = market.get_contract().get_collateral_token_contract()
assert (
collateral_token_contract.symbol() == "WXDAI"
), "Should have retrieve wxDai market."
collateral_token_contract.symbol() == expected_symbol
), f"Should have retrieve {expected_symbol} market."
assert isinstance(
collateral_token_contract, ContractDepositableWrapperERC20OnGnosisChain
), "wxDai market should adhere to this class."
) or isinstance(
collateral_token_contract, ContractERC4626OnGnosisChain
), "Omen market should adhere to one of these classes."

# Start by moving all funds from wxdai to xdai
if initial_balances.wxdai > 0:
collateral_token_contract.withdraw(
WrappedxDaiContract().withdraw(
api_keys=test_keys,
amount_wei=xdai_to_wei(initial_balances.wxdai),
web3=local_web3,
Expand All @@ -336,20 +355,8 @@ def test_place_bet_with_autodeposit(
assert initial_balances.wxdai == xdai_type(0)
assert initial_balances.xdai > xdai_type(0)

# Convert half of the xDai to wxDai
collateral_token_contract.deposit(
api_keys=test_keys,
amount_wei=xdai_to_wei(xdai_type(initial_balances.xdai * 0.5)),
web3=local_web3,
)
new_balances = get_balances(address=test_keys.bet_from_address, web3=local_web3)
assert np.allclose(new_balances.total, initial_balances.total)

# Try to place a bet with 90% of the xDai funds
bet_amount = BetAmount(amount=initial_balances.xdai * 0.9, currency=Currency.xDai)
assert new_balances.xdai < bet_amount.amount
assert new_balances.wxdai < bet_amount.amount
assert new_balances.total > bet_amount.amount
market.place_bet(
outcome=True,
amount=bet_amount,
Expand Down

0 comments on commit e5fcd68

Please sign in to comment.