prediction-market-agent-tooling 0.57.17__py3-none-any.whl → 0.57.19__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.
- prediction_market_agent_tooling/config.py +8 -0
- prediction_market_agent_tooling/markets/omen/omen.py +12 -16
- prediction_market_agent_tooling/markets/omen/omen_constants.py +8 -0
- prediction_market_agent_tooling/markets/omen/omen_contracts.py +16 -4
- prediction_market_agent_tooling/markets/seer/seer.py +3 -1
- prediction_market_agent_tooling/tools/contract.py +1 -98
- prediction_market_agent_tooling/tools/cow/cow_order.py +133 -0
- prediction_market_agent_tooling/tools/tokens/auto_deposit.py +156 -0
- prediction_market_agent_tooling/tools/tokens/auto_withdraw.py +62 -0
- prediction_market_agent_tooling/tools/tokens/main_token.py +13 -0
- {prediction_market_agent_tooling-0.57.17.dist-info → prediction_market_agent_tooling-0.57.19.dist-info}/METADATA +4 -2
- {prediction_market_agent_tooling-0.57.17.dist-info → prediction_market_agent_tooling-0.57.19.dist-info}/RECORD +15 -10
- {prediction_market_agent_tooling-0.57.17.dist-info → prediction_market_agent_tooling-0.57.19.dist-info}/LICENSE +0 -0
- {prediction_market_agent_tooling-0.57.17.dist-info → prediction_market_agent_tooling-0.57.19.dist-info}/WHEEL +0 -0
- {prediction_market_agent_tooling-0.57.17.dist-info → prediction_market_agent_tooling-0.57.19.dist-info}/entry_points.txt +0 -0
@@ -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
|
905
|
-
|
906
|
-
|
907
|
-
|
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()
|
@@ -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
|
426
|
+
class GNOContract(ContractERC20OnGnosisChain):
|
423
427
|
address: ChecksumAddress = Web3.to_checksum_address(
|
424
|
-
"
|
428
|
+
"0x9c58bacc331c9aa871afd802db6379a98e80cedb"
|
425
429
|
)
|
426
430
|
|
427
431
|
|
428
|
-
class
|
432
|
+
class WETHContract(ContractERC20OnGnosisChain):
|
429
433
|
address: ChecksumAddress = Web3.to_checksum_address(
|
430
|
-
"
|
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.
|
3
|
+
Version: 0.57.19
|
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,<1.0.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)
|
@@ -47,7 +49,7 @@ Requires-Dist: safe-eth-py (>=6.0.0b41,<7.0.0)
|
|
47
49
|
Requires-Dist: scikit-learn (>=1.3.1,<2.0.0)
|
48
50
|
Requires-Dist: sqlmodel (>=0.0.22,<0.0.23)
|
49
51
|
Requires-Dist: streamlit (>=1.31.0,<2.0.0)
|
50
|
-
Requires-Dist: subgrounds (>=1.9.
|
52
|
+
Requires-Dist: subgrounds-gnosis (>=1.9.2,<2.0.0)
|
51
53
|
Requires-Dist: tabulate (>=0.9.0,<0.10.0)
|
52
54
|
Requires-Dist: tavily-python (>=0.5.0,<0.6.0)
|
53
55
|
Requires-Dist: tqdm (>=4.66.2,<5.0.0)
|
@@ -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=
|
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=
|
56
|
-
prediction_market_agent_tooling/markets/omen/
|
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=
|
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=
|
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.
|
116
|
-
prediction_market_agent_tooling-0.57.
|
117
|
-
prediction_market_agent_tooling-0.57.
|
118
|
-
prediction_market_agent_tooling-0.57.
|
119
|
-
prediction_market_agent_tooling-0.57.
|
120
|
+
prediction_market_agent_tooling-0.57.19.dist-info/LICENSE,sha256=6or154nLLU6bELzjh0mCreFjt0m2v72zLi3yHE0QbeE,7650
|
121
|
+
prediction_market_agent_tooling-0.57.19.dist-info/METADATA,sha256=NAaCqa6AaiRsgR3wu3r70MKXHPeDc9UuqmkdjMlL6AI,8546
|
122
|
+
prediction_market_agent_tooling-0.57.19.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
123
|
+
prediction_market_agent_tooling-0.57.19.dist-info/entry_points.txt,sha256=m8PukHbeH5g0IAAmOf_1Ahm-sGAMdhSSRQmwtpmi2s8,81
|
124
|
+
prediction_market_agent_tooling-0.57.19.dist-info/RECORD,,
|
File without changes
|
File without changes
|