prediction-market-agent-tooling 0.57.17__py3-none-any.whl → 0.57.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,13 @@
1
1
  import typing as t
2
2
 
3
+ from eth_account.signers.local import LocalAccount
3
4
  from pydantic import Field
4
5
  from pydantic.types import SecretStr
5
6
  from pydantic.v1.types import SecretStr as SecretStrV1
6
7
  from pydantic_settings import BaseSettings, SettingsConfigDict
7
8
  from safe_eth.eth import EthereumClient
8
9
  from safe_eth.safe.safe import SafeV141
10
+ from web3 import Account
9
11
 
10
12
  from prediction_market_agent_tooling.gtypes import (
11
13
  ChainID,
@@ -184,6 +186,12 @@ class APIKeys(BaseSettings):
184
186
  self.SQLALCHEMY_DB_URL, "SQLALCHEMY_DB_URL missing in the environment."
185
187
  )
186
188
 
189
+ def get_account(self) -> LocalAccount:
190
+ acc: LocalAccount = Account.from_key(
191
+ self.bet_from_private_key.get_secret_value()
192
+ )
193
+ return acc
194
+
187
195
  def model_dump_public(self) -> dict[str, t.Any]:
188
196
  return {
189
197
  k: v
@@ -69,15 +69,18 @@ from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import (
69
69
  )
70
70
  from prediction_market_agent_tooling.tools.balances import get_balances
71
71
  from prediction_market_agent_tooling.tools.contract import (
72
- ContractDepositableWrapperERC20BaseClass,
73
- ContractERC4626BaseClass,
74
- auto_deposit_collateral_token,
75
72
  init_collateral_token_contract,
76
73
  to_gnosis_chain_contract,
77
74
  )
78
75
  from prediction_market_agent_tooling.tools.custom_exceptions import OutOfFundsError
79
76
  from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes
80
77
  from prediction_market_agent_tooling.tools.ipfs.ipfs_handler import IPFSHandler
78
+ from prediction_market_agent_tooling.tools.tokens.auto_deposit import (
79
+ auto_deposit_collateral_token,
80
+ )
81
+ from prediction_market_agent_tooling.tools.tokens.auto_withdraw import (
82
+ auto_withdraw_collateral_token,
83
+ )
81
84
  from prediction_market_agent_tooling.tools.utils import (
82
85
  BPS_CONSTANT,
83
86
  DatetimeUTC,
@@ -901,19 +904,12 @@ def omen_sell_outcome_tx(
901
904
  max_outcome_tokens_to_sell,
902
905
  web3=web3,
903
906
  )
904
- if auto_withdraw and (
905
- isinstance(collateral_token_contract, ContractERC4626BaseClass)
906
- or isinstance(
907
- collateral_token_contract, ContractDepositableWrapperERC20BaseClass
908
- )
909
- ):
910
- collateral_token_contract.withdraw(
911
- api_keys,
912
- remove_fraction(
913
- amount_wei,
914
- 0.001, # Allow 0.1% slippage.
915
- ),
916
- web3,
907
+ if auto_withdraw:
908
+ auto_withdraw_collateral_token(
909
+ collateral_token_contract=collateral_token_contract,
910
+ amount_wei=amount_wei,
911
+ api_keys=api_keys,
912
+ web3=web3,
917
913
  )
918
914
 
919
915
  return tx_receipt["transactionHash"].hex()
@@ -0,0 +1,8 @@
1
+ from web3 import Web3
2
+
3
+ WRAPPED_XDAI_CONTRACT_ADDRESS = Web3.to_checksum_address(
4
+ "0xe91d153e0b41518a2ce8dd3d7944fa863463a97d"
5
+ )
6
+ SDAI_CONTRACT_ADDRESS = Web3.to_checksum_address(
7
+ "0xaf204776c7245bF4147c2612BF6e5972Ee483701"
8
+ )
@@ -31,6 +31,10 @@ from prediction_market_agent_tooling.markets.omen.data_models import (
31
31
  RealitioLogNewQuestionEvent,
32
32
  format_realitio_question,
33
33
  )
34
+ from prediction_market_agent_tooling.markets.omen.omen_constants import (
35
+ SDAI_CONTRACT_ADDRESS,
36
+ WRAPPED_XDAI_CONTRACT_ADDRESS,
37
+ )
34
38
  from prediction_market_agent_tooling.tools.contract import (
35
39
  ContractDepositableWrapperERC20OnGnosisChain,
36
40
  ContractERC20OnGnosisChain,
@@ -419,18 +423,26 @@ class OmenFixedProductMarketMakerContract(ContractOnGnosisChain):
419
423
  )
420
424
 
421
425
 
422
- class WrappedxDaiContract(ContractDepositableWrapperERC20OnGnosisChain):
426
+ class GNOContract(ContractERC20OnGnosisChain):
423
427
  address: ChecksumAddress = Web3.to_checksum_address(
424
- "0xe91d153e0b41518a2ce8dd3d7944fa863463a97d"
428
+ "0x9c58bacc331c9aa871afd802db6379a98e80cedb"
425
429
  )
426
430
 
427
431
 
428
- class sDaiContract(ContractERC4626OnGnosisChain):
432
+ class WETHContract(ContractERC20OnGnosisChain):
429
433
  address: ChecksumAddress = Web3.to_checksum_address(
430
- "0xaf204776c7245bF4147c2612BF6e5972Ee483701"
434
+ "0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1"
431
435
  )
432
436
 
433
437
 
438
+ class WrappedxDaiContract(ContractDepositableWrapperERC20OnGnosisChain):
439
+ address: ChecksumAddress = WRAPPED_XDAI_CONTRACT_ADDRESS
440
+
441
+
442
+ class sDaiContract(ContractERC4626OnGnosisChain):
443
+ address: ChecksumAddress = SDAI_CONTRACT_ADDRESS
444
+
445
+
434
446
  OMEN_DEFAULT_MARKET_FEE_PERC = 0.02 # 2% fee from the buying shares amount.
435
447
  REALITY_DEFAULT_FINALIZATION_TIMEOUT = timedelta(days=3)
436
448
 
@@ -9,11 +9,13 @@ from prediction_market_agent_tooling.markets.seer.seer_contracts import (
9
9
  SeerMarketFactory,
10
10
  )
11
11
  from prediction_market_agent_tooling.tools.contract import (
12
- auto_deposit_collateral_token,
13
12
  init_collateral_token_contract,
14
13
  to_gnosis_chain_contract,
15
14
  )
16
15
  from prediction_market_agent_tooling.tools.datetime_utc import DatetimeUTC
16
+ from prediction_market_agent_tooling.tools.tokens.auto_deposit import (
17
+ auto_deposit_collateral_token,
18
+ )
17
19
  from prediction_market_agent_tooling.tools.web3_utils import xdai_to_wei
18
20
 
19
21
 
@@ -22,11 +22,7 @@ from prediction_market_agent_tooling.gtypes import (
22
22
  )
23
23
  from prediction_market_agent_tooling.tools.data_models import MessageContainer
24
24
  from prediction_market_agent_tooling.tools.hexbytes_custom import HexBytes
25
- from prediction_market_agent_tooling.tools.utils import (
26
- BPS_CONSTANT,
27
- DatetimeUTC,
28
- should_not_happen,
29
- )
25
+ from prediction_market_agent_tooling.tools.utils import BPS_CONSTANT, DatetimeUTC
30
26
  from prediction_market_agent_tooling.tools.web3_utils import (
31
27
  call_function_on_contract,
32
28
  send_function_on_contract_tx,
@@ -798,99 +794,6 @@ def init_collateral_token_contract(
798
794
  )
799
795
 
800
796
 
801
- def auto_deposit_collateral_token(
802
- collateral_token_contract: ContractERC20BaseClass,
803
- amount_wei: Wei,
804
- api_keys: APIKeys,
805
- web3: Web3 | None,
806
- ) -> None:
807
- if isinstance(collateral_token_contract, ContractERC4626BaseClass):
808
- auto_deposit_erc4626(collateral_token_contract, amount_wei, api_keys, web3)
809
-
810
- elif isinstance(
811
- collateral_token_contract, ContractDepositableWrapperERC20BaseClass
812
- ):
813
- auto_deposit_depositable_wrapper_erc20(
814
- collateral_token_contract, amount_wei, api_keys, web3
815
- )
816
-
817
- elif isinstance(collateral_token_contract, ContractERC20BaseClass):
818
- if (
819
- collateral_token_contract.balanceOf(
820
- for_address=api_keys.bet_from_address, web3=web3
821
- )
822
- < amount_wei
823
- ):
824
- raise ValueError(
825
- f"Not enough of the collateral token, but it's not a wrapper token that we can deposit automatically."
826
- )
827
-
828
- else:
829
- should_not_happen("Unsupported ERC20 contract type.")
830
-
831
-
832
- def auto_deposit_depositable_wrapper_erc20(
833
- collateral_token_contract: ContractDepositableWrapperERC20BaseClass,
834
- amount_wei: Wei,
835
- api_keys: APIKeys,
836
- web3: Web3 | None,
837
- ) -> None:
838
- collateral_token_balance = collateral_token_contract.balanceOf(
839
- for_address=api_keys.bet_from_address, web3=web3
840
- )
841
-
842
- # If we have enough of the collateral token, we don't need to deposit.
843
- if collateral_token_balance >= amount_wei:
844
- return
845
-
846
- # If we don't have enough, we need to deposit the difference.
847
- left_to_deposit = Wei(amount_wei - collateral_token_balance)
848
- collateral_token_contract.deposit(api_keys, left_to_deposit, web3=web3)
849
-
850
-
851
- def auto_deposit_erc4626(
852
- collateral_token_contract: ContractERC4626BaseClass,
853
- asset_amount_wei: Wei,
854
- api_keys: APIKeys,
855
- web3: Web3 | None,
856
- ) -> None:
857
- for_address = api_keys.bet_from_address
858
- collateral_token_balance_in_shares = collateral_token_contract.balanceOf(
859
- for_address=for_address, web3=web3
860
- )
861
- asset_amount_wei_in_shares = collateral_token_contract.convertToShares(
862
- asset_amount_wei, web3
863
- )
864
-
865
- # If we have enough shares, we don't need to deposit.
866
- if collateral_token_balance_in_shares >= asset_amount_wei_in_shares:
867
- return
868
-
869
- # If we need to deposit into erc4626, we first need to have enough of the asset token.
870
- asset_token_contract = collateral_token_contract.get_asset_token_contract(web3=web3)
871
-
872
- # If the asset token is Depositable Wrapper ERC-20, we can deposit it, in case we don't have enough.
873
- if (
874
- collateral_token_contract.get_asset_token_balance(for_address, web3)
875
- < asset_amount_wei
876
- ):
877
- if isinstance(asset_token_contract, ContractDepositableWrapperERC20BaseClass):
878
- auto_deposit_depositable_wrapper_erc20(
879
- asset_token_contract, asset_amount_wei, api_keys, web3
880
- )
881
- else:
882
- raise ValueError(
883
- "Not enough of the asset token, but it's not a depositable wrapper token that we can deposit automatically."
884
- )
885
-
886
- # Finally, we can deposit the asset token into the erc4626 vault.
887
- collateral_token_balance_in_assets = collateral_token_contract.convertToAssets(
888
- collateral_token_balance_in_shares, web3
889
- )
890
- left_to_deposit = Wei(asset_amount_wei - collateral_token_balance_in_assets)
891
- collateral_token_contract.deposit_asset_token(left_to_deposit, api_keys, web3)
892
-
893
-
894
797
  def to_gnosis_chain_contract(
895
798
  contract: ContractERC20BaseClass,
896
799
  ) -> ContractERC20OnGnosisChain:
@@ -0,0 +1,133 @@
1
+ import asyncio
2
+ from datetime import timedelta
3
+
4
+ import httpx
5
+ from cowdao_cowpy import swap_tokens
6
+ from cowdao_cowpy.common.chains import Chain
7
+ from cowdao_cowpy.common.config import SupportedChainId
8
+ from cowdao_cowpy.common.constants import CowContractAddress
9
+ from cowdao_cowpy.order_book.api import OrderBookApi
10
+ from cowdao_cowpy.order_book.config import Envs, OrderBookAPIConfigFactory
11
+ from cowdao_cowpy.order_book.generated.model import (
12
+ Address,
13
+ OrderMetaData,
14
+ OrderQuoteRequest,
15
+ OrderQuoteSide1,
16
+ OrderQuoteSideKindSell,
17
+ OrderStatus,
18
+ TokenAmount,
19
+ )
20
+ from eth_account.signers.local import LocalAccount
21
+ from eth_typing.evm import ChecksumAddress
22
+ from web3 import Web3
23
+ from web3.types import Wei
24
+
25
+ from prediction_market_agent_tooling.config import APIKeys
26
+ from prediction_market_agent_tooling.gtypes import ChecksumAddress, Wei, wei_type
27
+ from prediction_market_agent_tooling.loggers import logger
28
+ from prediction_market_agent_tooling.tools.contract import ContractERC20OnGnosisChain
29
+ from prediction_market_agent_tooling.tools.utils import utcnow
30
+
31
+
32
+ def get_order_book_api(env: Envs, chain: Chain) -> OrderBookApi:
33
+ chain_id = SupportedChainId(chain.value[0])
34
+ return OrderBookApi(OrderBookAPIConfigFactory.get_config(env, chain_id))
35
+
36
+
37
+ def get_buy_token_amount(
38
+ amount_wei: Wei,
39
+ sell_token: ChecksumAddress,
40
+ buy_token: ChecksumAddress,
41
+ chain: Chain = Chain.GNOSIS,
42
+ env: Envs = "prod",
43
+ ) -> Wei:
44
+ order_book_api = get_order_book_api(env, chain)
45
+ order_quote_request = OrderQuoteRequest(
46
+ sellToken=Address(sell_token),
47
+ buyToken=Address(buy_token),
48
+ from_=Address(
49
+ "0x1234567890abcdef1234567890abcdef12345678"
50
+ ), # Just random address, doesn't matter.
51
+ )
52
+ order_side = OrderQuoteSide1(
53
+ kind=OrderQuoteSideKindSell.sell,
54
+ sellAmountBeforeFee=TokenAmount(str(amount_wei)),
55
+ )
56
+ order_quote = asyncio.run(
57
+ order_book_api.post_quote(order_quote_request, order_side)
58
+ )
59
+ return wei_type(order_quote.quote.buyAmount.root)
60
+
61
+
62
+ def swap_tokens_waiting(
63
+ amount_wei: Wei,
64
+ sell_token: ChecksumAddress,
65
+ buy_token: ChecksumAddress,
66
+ api_keys: APIKeys,
67
+ chain: Chain = Chain.GNOSIS,
68
+ env: Envs = "prod",
69
+ web3: Web3 | None = None,
70
+ ) -> OrderMetaData:
71
+ account = api_keys.get_account()
72
+
73
+ # Approve the CoW Swap Vault Relayer to get the sell token.
74
+ ContractERC20OnGnosisChain(address=sell_token).approve(
75
+ api_keys,
76
+ Web3.to_checksum_address(CowContractAddress.VAULT_RELAYER.value),
77
+ amount_wei=amount_wei,
78
+ web3=web3,
79
+ )
80
+
81
+ # CoW library uses async, so we need to wrap the call in asyncio.run for us to use it.
82
+ return asyncio.run(
83
+ swap_tokens_waiting_async(
84
+ amount_wei, sell_token, buy_token, account, chain, env
85
+ )
86
+ )
87
+
88
+
89
+ async def swap_tokens_waiting_async(
90
+ amount_wei: Wei,
91
+ sell_token: ChecksumAddress,
92
+ buy_token: ChecksumAddress,
93
+ account: LocalAccount,
94
+ chain: Chain,
95
+ env: Envs,
96
+ timeout: timedelta = timedelta(seconds=60),
97
+ ) -> OrderMetaData:
98
+ order = await swap_tokens(
99
+ amount=amount_wei,
100
+ sell_token=sell_token,
101
+ buy_token=buy_token,
102
+ account=account,
103
+ chain=chain,
104
+ env=env,
105
+ )
106
+ logger.info(f"Order created: {order}")
107
+ start_time = utcnow()
108
+
109
+ while True:
110
+ async with httpx.AsyncClient() as client:
111
+ response = await client.get(order.url)
112
+ order_metadata = OrderMetaData.model_validate(response.json())
113
+
114
+ if order_metadata.status == OrderStatus.fulfilled:
115
+ logger.info(f"Order {order.uid} ({order.url}) completed.")
116
+ return order_metadata
117
+
118
+ elif order_metadata.status in (
119
+ OrderStatus.cancelled,
120
+ OrderStatus.expired,
121
+ ):
122
+ raise ValueError(f"Order {order.uid} failed. {order.url}")
123
+
124
+ if utcnow() - start_time > timeout:
125
+ raise TimeoutError(
126
+ f"Timeout waiting for order {order.uid} to be completed. {order.url}"
127
+ )
128
+
129
+ logger.info(
130
+ f"Order status of {order.uid} ({order.url}): {order_metadata.status}, waiting..."
131
+ )
132
+
133
+ await asyncio.sleep(3.14)
@@ -0,0 +1,156 @@
1
+ from web3 import Web3
2
+
3
+ from prediction_market_agent_tooling.config import APIKeys
4
+ from prediction_market_agent_tooling.gtypes import Wei, wei_type
5
+ from prediction_market_agent_tooling.loggers import logger
6
+ from prediction_market_agent_tooling.tools.contract import (
7
+ ContractDepositableWrapperERC20BaseClass,
8
+ ContractERC20BaseClass,
9
+ ContractERC20OnGnosisChain,
10
+ ContractERC4626BaseClass,
11
+ )
12
+ from prediction_market_agent_tooling.tools.cow.cow_order import (
13
+ get_buy_token_amount,
14
+ swap_tokens_waiting,
15
+ )
16
+ from prediction_market_agent_tooling.tools.tokens.main_token import KEEPING_ERC20_TOKEN
17
+ from prediction_market_agent_tooling.tools.utils import should_not_happen
18
+ from prediction_market_agent_tooling.tools.web3_utils import wei_to_xdai
19
+
20
+
21
+ def auto_deposit_collateral_token(
22
+ collateral_token_contract: ContractERC20BaseClass,
23
+ amount_wei: Wei,
24
+ api_keys: APIKeys,
25
+ web3: Web3 | None = None,
26
+ ) -> None:
27
+ if isinstance(collateral_token_contract, ContractDepositableWrapperERC20BaseClass):
28
+ # In this case, we can use deposit function directly, no need to go through DEX.
29
+ auto_deposit_depositable_wrapper_erc20(
30
+ collateral_token_contract, amount_wei, api_keys, web3
31
+ )
32
+
33
+ elif isinstance(collateral_token_contract, ContractERC4626BaseClass):
34
+ auto_deposit_erc4626(collateral_token_contract, amount_wei, api_keys, web3)
35
+
36
+ elif isinstance(collateral_token_contract, ContractERC20BaseClass):
37
+ auto_deposit_erc20(collateral_token_contract, amount_wei, api_keys, web3)
38
+
39
+ else:
40
+ should_not_happen("Unsupported ERC20 contract type.")
41
+
42
+
43
+ def auto_deposit_depositable_wrapper_erc20(
44
+ collateral_token_contract: ContractDepositableWrapperERC20BaseClass,
45
+ amount_wei: Wei,
46
+ api_keys: APIKeys,
47
+ web3: Web3 | None,
48
+ ) -> None:
49
+ collateral_token_balance = collateral_token_contract.balanceOf(
50
+ for_address=api_keys.bet_from_address, web3=web3
51
+ )
52
+
53
+ # If we have enough of the collateral token, we don't need to deposit.
54
+ if collateral_token_balance >= amount_wei:
55
+ return
56
+
57
+ # If we don't have enough, we need to deposit the difference.
58
+ left_to_deposit = Wei(amount_wei - collateral_token_balance)
59
+ logger.info(
60
+ f"Depositing {wei_to_xdai(left_to_deposit)} {collateral_token_contract.symbol()}."
61
+ )
62
+ collateral_token_contract.deposit(api_keys, left_to_deposit, web3=web3)
63
+
64
+
65
+ def auto_deposit_erc4626(
66
+ collateral_token_contract: ContractERC4626BaseClass,
67
+ asset_amount_wei: Wei,
68
+ api_keys: APIKeys,
69
+ web3: Web3 | None,
70
+ ) -> None:
71
+ for_address = api_keys.bet_from_address
72
+ collateral_token_balance_in_shares = collateral_token_contract.balanceOf(
73
+ for_address=for_address, web3=web3
74
+ )
75
+ asset_amount_wei_in_shares = collateral_token_contract.convertToShares(
76
+ asset_amount_wei, web3
77
+ )
78
+
79
+ # If we have enough shares, we don't need to deposit.
80
+ if collateral_token_balance_in_shares >= asset_amount_wei_in_shares:
81
+ return
82
+
83
+ # If we need to deposit into erc4626, we first need to have enough of the asset token.
84
+ asset_token_contract = collateral_token_contract.get_asset_token_contract(web3=web3)
85
+
86
+ if isinstance(asset_token_contract, ContractDepositableWrapperERC20BaseClass):
87
+ # If the asset token is Depositable Wrapper ERC-20, we don't need to go through DEX.
88
+ # First, calculate how much of asset token we need to deposit into the vault.
89
+ collateral_token_balance_in_assets = collateral_token_contract.convertToAssets(
90
+ collateral_token_balance_in_shares, web3
91
+ )
92
+ left_to_deposit = Wei(asset_amount_wei - collateral_token_balance_in_assets)
93
+ if (
94
+ collateral_token_contract.get_asset_token_balance(for_address, web3)
95
+ < left_to_deposit
96
+ ):
97
+ # If we don't have enough of asset token to deposit into the vault, deposit that one first.
98
+ auto_deposit_depositable_wrapper_erc20(
99
+ asset_token_contract, left_to_deposit, api_keys, web3
100
+ )
101
+ # And finally, we can deposit the asset token into the erc4626 vault directly as well, without DEX.
102
+ collateral_token_contract.deposit_asset_token(left_to_deposit, api_keys, web3)
103
+
104
+ else:
105
+ # Otherwise, we need to go through DEX.
106
+ auto_deposit_erc20(collateral_token_contract, asset_amount_wei, api_keys, web3)
107
+
108
+
109
+ def auto_deposit_erc20(
110
+ collateral_token_contract: ContractERC20BaseClass,
111
+ amount_xdai_wei: Wei,
112
+ api_keys: APIKeys,
113
+ web3: Web3 | None,
114
+ ) -> None:
115
+ # How much it is in the other token (collateral token).
116
+ collateral_amount_wei = get_buy_token_amount(
117
+ amount_xdai_wei,
118
+ KEEPING_ERC20_TOKEN.address,
119
+ collateral_token_contract.address,
120
+ )
121
+ # How much do we have already in the other token (collateral token).
122
+ collateral_balance_wei = collateral_token_contract.balanceOf(
123
+ api_keys.bet_from_address
124
+ )
125
+ # Amount of collateral token remaining to get.
126
+ remaining_to_get_in_collateral_wei = max(
127
+ 0, collateral_amount_wei - collateral_balance_wei
128
+ )
129
+ if not remaining_to_get_in_collateral_wei:
130
+ return
131
+ # Estimate of how much of the source token we need to sell in order to fill the remaining collateral amount, with 1% slippage to be sure.
132
+ amount_to_sell_wei = wei_type(
133
+ (remaining_to_get_in_collateral_wei * amount_xdai_wei)
134
+ / collateral_amount_wei
135
+ * 1.01
136
+ )
137
+ # If we don't have enough of the source token.
138
+ if amount_to_sell_wei > ContractERC20OnGnosisChain(
139
+ address=KEEPING_ERC20_TOKEN.address
140
+ ).balanceOf(api_keys.bet_from_address):
141
+ # Try to deposit it, if it's depositable token (like Wrapped xDai, agent could have xDai).
142
+ if isinstance(KEEPING_ERC20_TOKEN, ContractDepositableWrapperERC20BaseClass):
143
+ auto_deposit_depositable_wrapper_erc20(
144
+ KEEPING_ERC20_TOKEN, amount_to_sell_wei, api_keys, web3
145
+ )
146
+ else:
147
+ raise ValueError(
148
+ "Not enough of the source token to sell to get the desired amount of the collateral token."
149
+ )
150
+ swap_tokens_waiting(
151
+ amount_wei=amount_to_sell_wei,
152
+ sell_token=KEEPING_ERC20_TOKEN.address,
153
+ buy_token=collateral_token_contract.address,
154
+ api_keys=api_keys,
155
+ web3=web3,
156
+ )
@@ -0,0 +1,62 @@
1
+ from web3 import Web3
2
+
3
+ from prediction_market_agent_tooling.config import APIKeys
4
+ from prediction_market_agent_tooling.gtypes import Wei
5
+ from prediction_market_agent_tooling.tools.contract import (
6
+ ContractERC20BaseClass,
7
+ ContractERC4626BaseClass,
8
+ )
9
+ from prediction_market_agent_tooling.tools.cow.cow_order import (
10
+ get_buy_token_amount,
11
+ swap_tokens_waiting,
12
+ )
13
+ from prediction_market_agent_tooling.tools.tokens.main_token import KEEPING_ERC20_TOKEN
14
+ from prediction_market_agent_tooling.tools.utils import should_not_happen
15
+ from prediction_market_agent_tooling.tools.web3_utils import remove_fraction
16
+
17
+
18
+ def auto_withdraw_collateral_token(
19
+ collateral_token_contract: ContractERC20BaseClass,
20
+ amount_wei: Wei,
21
+ api_keys: APIKeys,
22
+ web3: Web3 | None = None,
23
+ slippage: float = 0.001,
24
+ ) -> None:
25
+ # Small slippage as exact exchange rate constantly changes and we don't care about small differences.
26
+ amount_wei = remove_fraction(
27
+ amount_wei,
28
+ slippage,
29
+ )
30
+
31
+ if collateral_token_contract.address == KEEPING_ERC20_TOKEN.address:
32
+ # Do nothing, as this is the token we want to keep.
33
+ return
34
+ elif (
35
+ isinstance(collateral_token_contract, ContractERC4626BaseClass)
36
+ and collateral_token_contract.get_asset_token_contract().address
37
+ == KEEPING_ERC20_TOKEN.address
38
+ ):
39
+ # If the ERC4626 is backed by KEEPING_ERC20_TOKEN, we can withdraw it directly, no need to go through DEX.
40
+ collateral_token_contract.withdraw(
41
+ api_keys,
42
+ amount_wei,
43
+ web3=web3,
44
+ )
45
+ elif isinstance(collateral_token_contract, ContractERC20BaseClass):
46
+ # Otherwise, DEX will handle the rest of token swaps.
47
+ # First, convert `amount_wei` from xDai-based value into the collateral token-based value.
48
+ collateral_amount_wei = get_buy_token_amount(
49
+ amount_wei,
50
+ KEEPING_ERC20_TOKEN.address,
51
+ collateral_token_contract.address,
52
+ )
53
+ # And then sell it.
54
+ swap_tokens_waiting(
55
+ amount_wei=collateral_amount_wei,
56
+ sell_token=collateral_token_contract.address,
57
+ buy_token=KEEPING_ERC20_TOKEN.address,
58
+ api_keys=api_keys,
59
+ web3=web3,
60
+ )
61
+ else:
62
+ should_not_happen("Unsupported ERC20 contract type.")
@@ -0,0 +1,13 @@
1
+ from prediction_market_agent_tooling.markets.omen.omen_constants import (
2
+ WRAPPED_XDAI_CONTRACT_ADDRESS,
3
+ )
4
+ from prediction_market_agent_tooling.tools.contract import (
5
+ ContractDepositableWrapperERC20OnGnosisChain,
6
+ )
7
+
8
+ # This is the token where agents will hold their funds,
9
+ # except for a small portion that will be kept in the native token of the network to pay for the fees.
10
+ # If changed, then keep in mind that we assume this token is equal to 1 USD.
11
+ KEEPING_ERC20_TOKEN = ContractDepositableWrapperERC20OnGnosisChain(
12
+ address=WRAPPED_XDAI_CONTRACT_ADDRESS
13
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: prediction-market-agent-tooling
3
- Version: 0.57.17
3
+ Version: 0.57.18
4
4
  Summary: Tools to benchmark, deploy and monitor prediction market agents.
5
5
  Author: Gnosis
6
6
  Requires-Python: >=3.10,<3.13
@@ -14,6 +14,7 @@ Provides-Extra: openai
14
14
  Provides-Extra: optuna
15
15
  Requires-Dist: autoflake (>=2.2.1,<3.0.0)
16
16
  Requires-Dist: base58 (>=1.0.2,<2.0)
17
+ Requires-Dist: cowdao-cowpy (>=1.0.0rc1,<2.0.0)
17
18
  Requires-Dist: cron-validator (>=1.0.8,<2.0.0)
18
19
  Requires-Dist: eth-account (>=0.8.0,<0.12.0)
19
20
  Requires-Dist: eth-keys (>=0.6.1,<0.7.0)
@@ -24,6 +25,7 @@ Requires-Dist: google-cloud-functions (>=1.16.0,<2.0.0)
24
25
  Requires-Dist: google-cloud-resource-manager (>=1.12.0,<2.0.0)
25
26
  Requires-Dist: google-cloud-secret-manager (>=2.18.2,<3.0.0)
26
27
  Requires-Dist: hishel (>=0.0.31,<0.0.32)
28
+ Requires-Dist: httpx (>=0.25.2,<0.26.0)
27
29
  Requires-Dist: isort (>=5.13.2,<6.0.0)
28
30
  Requires-Dist: langchain (>=0.2.6,<0.3.0) ; extra == "langchain"
29
31
  Requires-Dist: langchain-community (>=0.0.19)
@@ -22,7 +22,7 @@ prediction_market_agent_tooling/benchmark/__init__.py,sha256=47DEQpj8HBSa-_TImW-
22
22
  prediction_market_agent_tooling/benchmark/agents.py,sha256=B1-uWdyeN4GGKMWGK_-CcAFJg1m9Y_XuaeIHPB29QR8,3971
23
23
  prediction_market_agent_tooling/benchmark/benchmark.py,sha256=MqTiaaJ3cYiOLUVR7OyImLWxcEya3Rl5JyFYW-K0lwM,17097
24
24
  prediction_market_agent_tooling/benchmark/utils.py,sha256=D0MfUkVZllmvcU0VOurk9tcKT7JTtwwOp-63zuCBVuc,2880
25
- prediction_market_agent_tooling/config.py,sha256=Ffb1ftRiguAxa5cS0nXvKK01HomdqzFDCsp8rFd5taM,7553
25
+ prediction_market_agent_tooling/config.py,sha256=QBrbqG9wN1JFBUt_kCl2ybPamobKP6UhfiaRZK_VjMo,7805
26
26
  prediction_market_agent_tooling/deploy/agent.py,sha256=58Ms6wFcRM3yhpdDVT-ohryZhHJNinn1Z4MdrmCMXvM,23627
27
27
  prediction_market_agent_tooling/deploy/agent_example.py,sha256=dIIdZashExWk9tOdyDjw87AuUcGyM7jYxNChYrVK2dM,1001
28
28
  prediction_market_agent_tooling/deploy/betting_strategy.py,sha256=kMrIE3wMv_IB6nJd_1DmDXDkEZhsXFOgyTd7JZ0gqHI,13068
@@ -52,8 +52,9 @@ prediction_market_agent_tooling/markets/metaculus/data_models.py,sha256=Suxa7xEL
52
52
  prediction_market_agent_tooling/markets/metaculus/metaculus.py,sha256=86TIx6cavEWc8Cv4KpZxSvwiSw9oFybXE3YB49pg-CA,4377
53
53
  prediction_market_agent_tooling/markets/omen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
54
  prediction_market_agent_tooling/markets/omen/data_models.py,sha256=uT8ILKrg2g4jGodPxtolPErk25buNzMYndb01ZL2dYE,28421
55
- prediction_market_agent_tooling/markets/omen/omen.py,sha256=w6uf_hDpVS2_9LnfmC3ZX9KZSMtXeuUvyOr1fs9nGZU,51880
56
- prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=baXJwk-jSI3-FV-k239oCNOA4oKz6LT47juX8AKpW3A,28297
55
+ prediction_market_agent_tooling/markets/omen/omen.py,sha256=xNHYn5diKjKlkeKoYNr8DrE0gSMsU8poGhxBjw2PZow,51783
56
+ prediction_market_agent_tooling/markets/omen/omen_constants.py,sha256=D9oflYKafLQiHYtB5sScMHqmXyzM8JP8J0yATmc4SQQ,233
57
+ prediction_market_agent_tooling/markets/omen/omen_contracts.py,sha256=EXqBlVivbmW8aBQ65O09X2xkyesHAop49GUl1tUffWA,28648
57
58
  prediction_market_agent_tooling/markets/omen/omen_resolving.py,sha256=E1BVDvQd1qYtCxmfC94kJtGkmQqpGPHL3zTkcs5wW6M,9697
58
59
  prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py,sha256=mZ0CgKfHj0gLFq9plNpBhNqMuclb8V3qNagWfLYcpUc,38806
59
60
  prediction_market_agent_tooling/markets/polymarket/api.py,sha256=UZ4_TG8ceb9Y-qgsOKs8Qiv8zDt957QkT8IX2c83yqo,4800
@@ -62,7 +63,7 @@ prediction_market_agent_tooling/markets/polymarket/data_models_web.py,sha256=VZh
62
63
  prediction_market_agent_tooling/markets/polymarket/polymarket.py,sha256=NRoZK71PtH8kkangMqme7twcAXhRJSSabbmOir-UnAI,3418
63
64
  prediction_market_agent_tooling/markets/polymarket/utils.py,sha256=DImFxeMg8lTfsEDZ8FavndW38TfUsCkawcVGnucsuGo,2029
64
65
  prediction_market_agent_tooling/markets/seer/data_models.py,sha256=DmxkAfusRbupqBDjohSFhsYyW7WjKZ1sQKpm8xeAMi4,2017
65
- prediction_market_agent_tooling/markets/seer/seer.py,sha256=sPjhWdC3ri3xavD_7dnGAUFyZvPrrUOJoVXNhE8fvqM,3277
66
+ prediction_market_agent_tooling/markets/seer/seer.py,sha256=l87L82M-RHMBJjfdtNXWgGLc979n2Fub9Z702zE8I9c,3351
66
67
  prediction_market_agent_tooling/markets/seer/seer_contracts.py,sha256=E7CYAKZiK6cg3dyj1kJuIPKSYYUft98F64shF5S0g4s,2730
67
68
  prediction_market_agent_tooling/markets/seer/seer_subgraph_handler.py,sha256=KraQe4GgbdMcYnvpEJqcSHXQqgyDiC7IjkIOHAfma9s,5528
68
69
  prediction_market_agent_tooling/monitor/financial_metrics/financial_metrics.py,sha256=fjIgjDIx5MhH5mwf7S0cspLOOSU3elYLhGYoIiM26mU,2746
@@ -83,8 +84,9 @@ prediction_market_agent_tooling/tools/betting_strategies/utils.py,sha256=kpIb-ci
83
84
  prediction_market_agent_tooling/tools/caches/db_cache.py,sha256=aafau_n_AUbLIwkyIRiTPgKB0dmM0767mSqyPDLF2A4,10576
84
85
  prediction_market_agent_tooling/tools/caches/inmemory_cache.py,sha256=ZW5iI5rmjqeAebu5T7ftRnlkxiL02IC-MxCfDB80x7w,1506
85
86
  prediction_market_agent_tooling/tools/caches/serializers.py,sha256=vFDx4fsPxclXp2q0sv27j4al_M_Tj9aR2JJP-xNHQXA,2151
86
- prediction_market_agent_tooling/tools/contract.py,sha256=VKSH-31Cf3DwtHpH9KZnQr08IxmfyvYvuqkcOK7dUN8,29608
87
+ prediction_market_agent_tooling/tools/contract.py,sha256=JkkrXm75_XUZ33jVlXF7v-Ge9RLSfXQeY523H_kuThQ,26069
87
88
  prediction_market_agent_tooling/tools/costs.py,sha256=EaAJ7v9laD4VEV3d8B44M4u3_oEO_H16jRVCdoZ93Uw,954
89
+ prediction_market_agent_tooling/tools/cow/cow_order.py,sha256=ohriN_65BZ69f_BV6bNg_AwZjidip4yi4d2G6Ddy5Qg,4238
88
90
  prediction_market_agent_tooling/tools/custom_exceptions.py,sha256=Fh8z1fbwONvP4-j7AmV_PuEcoqb6-QXa9PJ9m7guMcM,93
89
91
  prediction_market_agent_tooling/tools/data_models.py,sha256=jDQ7FU0QQhXlcgJh5VZZGwDTYP2OPAqKPHZFewCPAUY,732
90
92
  prediction_market_agent_tooling/tools/datetime_utc.py,sha256=8_WackjtjC8zHXrhQFTGQ6e6Fz_6llWoKR4CSFvIv9I,2766
@@ -109,11 +111,14 @@ prediction_market_agent_tooling/tools/singleton.py,sha256=CiIELUiI-OeS7U7eeHEt0r
109
111
  prediction_market_agent_tooling/tools/streamlit_user_login.py,sha256=NXEqfjT9Lc9QtliwSGRASIz1opjQ7Btme43H4qJbzgE,3010
110
112
  prediction_market_agent_tooling/tools/tavily/tavily_models.py,sha256=5ldQs1pZe6uJ5eDAuP4OLpzmcqYShlIV67kttNFvGS0,342
111
113
  prediction_market_agent_tooling/tools/tavily/tavily_search.py,sha256=Kw2mXNkMTYTEe1MBSTqhQmLoeXtgb6CkmHlcAJvhtqE,3809
114
+ prediction_market_agent_tooling/tools/tokens/auto_deposit.py,sha256=o8_ERfPL-ps9FLvH5vgdiSRJQ4dZONJw9KK9sHgeP2I,6390
115
+ prediction_market_agent_tooling/tools/tokens/auto_withdraw.py,sha256=B02maQkl3wNptWFtZdnDosICJE5BfnLUVqxp5uJIaPA,2353
116
+ prediction_market_agent_tooling/tools/tokens/main_token.py,sha256=5iHO7-iehSlXuue6ocVrr4IsklVjm7QHIwln4BsebJA,573
112
117
  prediction_market_agent_tooling/tools/transaction_cache.py,sha256=K5YKNL2_tR10Iw2TD9fuP-CTGpBbZtNdgbd0B_R7pjg,1814
113
118
  prediction_market_agent_tooling/tools/utils.py,sha256=jLG4nbEoIzzJiZ4RgMx4Q969Zdl0p0s63p8uET_0Fuw,6440
114
119
  prediction_market_agent_tooling/tools/web3_utils.py,sha256=wqUDCed3iNrn1Wao1iwGN6tzIrhpzrTRj319wlveJEo,12275
115
- prediction_market_agent_tooling-0.57.17.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
116
- prediction_market_agent_tooling-0.57.17.dist-info/METADATA,sha256=4gih1dpxZvldnv0oRnp5g0e8-dhNYHxjXzKHRdIIRd8,8452
117
- prediction_market_agent_tooling-0.57.17.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
118
- prediction_market_agent_tooling-0.57.17.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
119
- prediction_market_agent_tooling-0.57.17.dist-info/RECORD,,
120
+ prediction_market_agent_tooling-0.57.18.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
121
+ prediction_market_agent_tooling-0.57.18.dist-info/METADATA,sha256=KXSS97cfCzPxg-0Q8DQr7zV8UW6lUkYTkgss3vAc1uI,8540
122
+ prediction_market_agent_tooling-0.57.18.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
123
+ prediction_market_agent_tooling-0.57.18.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
124
+ prediction_market_agent_tooling-0.57.18.dist-info/RECORD,,