-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cut out tooling from the PMA repository #3
Conversation
Warning Rate Limit Exceeded@kongzii has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 12 minutes and 42 seconds before requesting another review. How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. WalkthroughThe project introduces a suite of tools for deploying prediction market agents on Google Cloud Platform and interacting with platforms like Manifold. It includes deploying agents, configuring API keys, handling market types, and executing transactions. This update enhances the project's capabilities for automated market interactions, offering detailed deployment management and market strategy. Changes
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (invoked as PR comments)
Additionally, you can add CodeRabbit Configration File (
|
@coderabbitai review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Status
Actionable comments generated: 11
Configuration used: CodeRabbit UI
Files ignored due to path filters (6)
mypy.ini
is excluded by:!**/*.ini
poetry.lock
is excluded by:!**/*.lock
prediction_market_agent_tooling/abis/omen_fpmm.abi.json
is excluded by:!**/*.json
prediction_market_agent_tooling/abis/omen_fpmm_conditionaltokens.abi.json
is excluded by:!**/*.json
prediction_market_agent_tooling/abis/wxdai.abi.json
is excluded by:!**/*.json
pyproject.toml
is excluded by:!**/*.toml
Files selected for processing (19)
- examples/cloud_deployment/gcp/agent.py (1 hunks)
- examples/cloud_deployment/gcp/deploy.py (1 hunks)
- prediction_market_agent_tooling/config.py (1 hunks)
- prediction_market_agent_tooling/deploy/agent.py (1 hunks)
- prediction_market_agent_tooling/deploy/gcp/deploy.py (1 hunks)
- prediction_market_agent_tooling/deploy/gcp/utils.py (1 hunks)
- prediction_market_agent_tooling/gtypes.py (1 hunks)
- prediction_market_agent_tooling/markets/data_models.py (1 hunks)
- prediction_market_agent_tooling/markets/manifold.py (1 hunks)
- prediction_market_agent_tooling/markets/markets.py (1 hunks)
- prediction_market_agent_tooling/markets/omen.py (1 hunks)
- prediction_market_agent_tooling/tools/gnosis_rpc.py (1 hunks)
- prediction_market_agent_tooling/tools/utils.py (1 hunks)
- prediction_market_agent_tooling/tools/web3_utils.py (1 hunks)
- tests/deploy/test_deploy.py (1 hunks)
- tests/markets/test_manifold.py (1 hunks)
- tests/markets/test_omen.py (1 hunks)
- tests/test_smoke.py (1 hunks)
- tests/utils.py (1 hunks)
Additional comments: 18
tests/test_smoke.py (1)
- 1-1: LGTM!
tests/utils.py (1)
- 1-3: LGTM!
tests/markets/test_manifold.py (1)
- 1-12: LGTM!
prediction_market_agent_tooling/tools/gnosis_rpc.py (1)
- 1-19: LGTM!
examples/cloud_deployment/gcp/agent.py (1)
- 1-24: LGTM!
tests/deploy/test_deploy.py (1)
- 1-30: LGTM!
prediction_market_agent_tooling/gtypes.py (1)
- 1-37: LGTM!
prediction_market_agent_tooling/config.py (1)
- 1-34: LGTM!
examples/cloud_deployment/gcp/deploy.py (1)
- 1-37: LGTM!
tests/markets/test_omen.py (1)
- 1-47: LGTM!
prediction_market_agent_tooling/tools/utils.py (1)
- 1-65: LGTM!
prediction_market_agent_tooling/markets/manifold.py (1)
- 1-70: LGTM!
prediction_market_agent_tooling/deploy/gcp/deploy.py (1)
- 1-83: LGTM!
prediction_market_agent_tooling/markets/markets.py (1)
- 1-95: LGTM!
prediction_market_agent_tooling/deploy/agent.py (1)
- 1-90: LGTM!
prediction_market_agent_tooling/deploy/gcp/utils.py (1)
- 1-116: LGTM!
prediction_market_agent_tooling/tools/web3_utils.py (1)
- 1-104: LGTM!
prediction_market_agent_tooling/markets/data_models.py (1)
- 1-162: LGTM!
import os | ||
import requests | ||
from typing import Optional | ||
from web3 import Web3 | ||
from web3.types import TxReceipt, TxParams | ||
from prediction_market_agent_tooling.markets.data_models import OmenMarket | ||
from prediction_market_agent_tooling.tools.web3_utils import ( | ||
call_function_on_contract, | ||
call_function_on_contract_tx, | ||
WXDAI_ABI, | ||
xdai_to_wei, | ||
remove_fraction, | ||
add_fraction, | ||
check_tx_receipt, | ||
ONE_NONCE, | ||
Nonce, | ||
) | ||
from prediction_market_agent_tooling.tools.gnosis_rpc import GNOSIS_RPC_URL | ||
from prediction_market_agent_tooling.gtypes import ( | ||
ABI, | ||
HexAddress, | ||
PrivateKey, | ||
xDai, | ||
Wei, | ||
ChecksumAddress, | ||
OmenOutcomeToken, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider grouping imports from the same module/package together to improve readability.
- from prediction_market_agent_tooling.markets.data_models import OmenMarket
- from prediction_market_agent_tooling.tools.web3_utils import (
- call_function_on_contract,
- ...
- )
- from prediction_market_agent_tooling.tools.gnosis_rpc import GNOSIS_RPC_URL
- from prediction_market_agent_tooling.gtypes import (
- ABI,
- ...
- )
+ from prediction_market_agent_tooling import (
+ markets,
+ tools,
+ gtypes,
+ )
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
import os | |
import requests | |
from typing import Optional | |
from web3 import Web3 | |
from web3.types import TxReceipt, TxParams | |
from prediction_market_agent_tooling.markets.data_models import OmenMarket | |
from prediction_market_agent_tooling.tools.web3_utils import ( | |
call_function_on_contract, | |
call_function_on_contract_tx, | |
WXDAI_ABI, | |
xdai_to_wei, | |
remove_fraction, | |
add_fraction, | |
check_tx_receipt, | |
ONE_NONCE, | |
Nonce, | |
) | |
from prediction_market_agent_tooling.tools.gnosis_rpc import GNOSIS_RPC_URL | |
from prediction_market_agent_tooling.gtypes import ( | |
ABI, | |
HexAddress, | |
PrivateKey, | |
xDai, | |
Wei, | |
ChecksumAddress, | |
OmenOutcomeToken, | |
) | |
import os | |
import requests | |
from typing import Optional | |
from web3 import Web3 | |
from web3.types import TxReceipt, TxParams | |
from prediction_market_agent_tooling import ( | |
markets, | |
tools, | |
gtypes, | |
) |
with open( | ||
os.path.join( | ||
os.path.dirname(os.path.realpath(__file__)), "../abis/omen_fpmm.abi.json" | ||
) | ||
) as f: | ||
# File content taken from https://github.com/protofire/omen-exchange/blob/master/app/src/abi/marketMaker.json. | ||
# Factory contract at https://gnosisscan.io/address/0x9083a2b699c0a4ad06f63580bde2635d26a3eef0. | ||
OMEN_FPMM_ABI = ABI(f.read()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate the existence of the ABI file at the specified path to prevent runtime errors.
with open( | ||
os.path.join( | ||
os.path.dirname(os.path.realpath(__file__)), | ||
"../abis/omen_fpmm_conditionaltokens.abi.json", | ||
) | ||
) as f: | ||
# Based on the code from OMEN_FPMM_ABI's factory contract. | ||
OMEN_FPMM_CONDITIONALTOKENS_ABI = ABI(f.read()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate the existence of the ABI file at the specified path to prevent runtime errors.
def get_omen_markets(first: int, outcomes: list[str]) -> list[OmenMarket]: | ||
markets = requests.post( | ||
THEGRAPH_QUERY_URL, | ||
json={ | ||
"query": _QUERY_GET_FIXED_PRODUCT_MARKETS_MAKERS, | ||
"variables": { | ||
"first": first, | ||
"outcomes": outcomes, | ||
}, | ||
}, | ||
headers={"Content-Type": "application/json"}, | ||
).json()["data"]["fixedProductMarketMakers"] | ||
return [OmenMarket.model_validate(market) for market in markets] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for the HTTP request to manage potential request failures or non-200 responses.
def get_market(market_id: str) -> OmenMarket: | ||
market = requests.post( | ||
THEGRAPH_QUERY_URL, | ||
json={ | ||
"query": _QUERY_GET_SINGLE_FIXED_PRODUCT_MARKET_MAKER, | ||
"variables": { | ||
"id": market_id, | ||
}, | ||
}, | ||
headers={"Content-Type": "application/json"}, | ||
).json()["data"]["fixedProductMarketMaker"] | ||
return OmenMarket.model_validate(market) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for the HTTP request to manage potential request failures or non-200 responses.
def omen_approve_all_market_maker_to_move_conditionaltokens_tx( | ||
web3: Web3, | ||
market: OmenMarket, | ||
approve: bool, | ||
from_address: ChecksumAddress, | ||
from_private_key: PrivateKey, | ||
tx_params: Optional[TxParams] = None, | ||
) -> TxReceipt: | ||
# Get the address of conditional token's of this market. | ||
conditionaltokens_address = omen_get_market_maker_conditionaltokens_address( | ||
web3, market | ||
) | ||
return call_function_on_contract_tx( | ||
web3=web3, | ||
contract_address=Web3.to_checksum_address(conditionaltokens_address), | ||
contract_abi=OMEN_FPMM_CONDITIONALTOKENS_ABI, | ||
from_address=from_address, | ||
from_private_key=from_private_key, | ||
function_name="setApprovalForAll", | ||
function_params=[ | ||
market.market_maker_contract_address_checksummed, | ||
approve, | ||
], | ||
tx_params=tx_params, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider extracting the repeated pattern of calling call_function_on_contract_tx
into a utility function to reduce code duplication.
def omen_deposit_collateral_token_tx( | ||
web3: Web3, | ||
market: OmenMarket, | ||
amount_wei: Wei, | ||
from_address: ChecksumAddress, | ||
from_private_key: PrivateKey, | ||
tx_params: Optional[TxParams] = None, | ||
) -> TxReceipt: | ||
return call_function_on_contract_tx( | ||
web3=web3, | ||
contract_address=market.collateral_token_contract_address_checksummed, | ||
contract_abi=WXDAI_ABI, | ||
from_address=from_address, | ||
from_private_key=from_private_key, | ||
function_name="deposit", | ||
tx_params={"value": amount_wei, **(tx_params or {})}, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider extracting the repeated pattern of calling call_function_on_contract_tx
into a utility function to reduce code duplication.
def omen_withdraw_collateral_token_tx( | ||
web3: Web3, | ||
market: OmenMarket, | ||
amount_wei: Wei, | ||
from_address: ChecksumAddress, | ||
from_private_key: PrivateKey, | ||
tx_params: Optional[TxParams] = None, | ||
) -> TxReceipt: | ||
return call_function_on_contract_tx( | ||
web3=web3, | ||
contract_address=market.collateral_token_contract_address_checksummed, | ||
contract_abi=WXDAI_ABI, | ||
from_address=from_address, | ||
from_private_key=from_private_key, | ||
function_name="withdraw", | ||
function_params=[amount_wei], | ||
tx_params=tx_params or {}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider extracting the repeated pattern of calling call_function_on_contract_tx
into a utility function to reduce code duplication.
def omen_buy_outcome_tx( | ||
amount: xDai, | ||
from_address: ChecksumAddress, | ||
from_private_key: PrivateKey, | ||
market: OmenMarket, | ||
outcome: str, | ||
auto_deposit: bool, | ||
) -> None: | ||
""" | ||
Bets the given amount of xDai for the given outcome in the given market. | ||
""" | ||
web3 = Web3(Web3.HTTPProvider(GNOSIS_RPC_URL)) | ||
amount_wei = xdai_to_wei(amount) | ||
from_address_checksummed = Web3.to_checksum_address(from_address) | ||
|
||
# Get the index of the outcome we want to buy. | ||
outcome_index: int = market.get_outcome_index(outcome) | ||
|
||
# Get the current nonce for the given from_address. | ||
# If making multiple transactions quickly after each other, | ||
# it's better to increae it manually (otherwise we could get stale value from the network and error out). | ||
nonce: Nonce = web3.eth.get_transaction_count(from_address_checksummed) | ||
|
||
# Calculate the amount of shares we will get for the given investment amount. | ||
expected_shares = omen_calculate_buy_amount(web3, market, amount_wei, outcome_index) | ||
# Allow 1% slippage. | ||
expected_shares = remove_fraction(expected_shares, 0.01) | ||
# Approve the market maker to withdraw our collateral token. | ||
approve_tx_receipt = omen_approve_market_maker_to_spend_collateral_token_tx( | ||
web3=web3, | ||
market=market, | ||
amount_wei=amount_wei, | ||
from_address=from_address_checksummed, | ||
from_private_key=from_private_key, | ||
tx_params={"nonce": nonce}, | ||
) | ||
nonce = Nonce(nonce + ONE_NONCE) # Increase after each tx. | ||
check_tx_receipt(approve_tx_receipt) | ||
# Deposit xDai to the collateral token, | ||
# this can be skipped, if we know we already have enough collateral tokens. | ||
if auto_deposit: | ||
deposit_receipt = omen_deposit_collateral_token_tx( | ||
web3=web3, | ||
market=market, | ||
amount_wei=amount_wei, | ||
from_address=from_address_checksummed, | ||
from_private_key=from_private_key, | ||
tx_params={"nonce": nonce}, | ||
) | ||
nonce = Nonce(nonce + ONE_NONCE) # Increase after each tx. | ||
check_tx_receipt(deposit_receipt) | ||
# Buy shares using the deposited xDai in the collateral token. | ||
buy_receipt = omen_buy_shares_tx( | ||
web3, | ||
market, | ||
amount_wei, | ||
outcome_index, | ||
expected_shares, | ||
from_address_checksummed, | ||
from_private_key, | ||
tx_params={"nonce": nonce}, | ||
) | ||
nonce = Nonce(nonce + ONE_NONCE) # Increase after each tx. | ||
check_tx_receipt(buy_receipt) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure nonce management is robust against race conditions and consider using a nonce manager if transactions are sent in quick succession.
def omen_sell_outcome_tx( | ||
amount: xDai, | ||
from_address: ChecksumAddress, | ||
from_private_key: PrivateKey, | ||
market: OmenMarket, | ||
outcome: str, | ||
auto_withdraw: bool, | ||
) -> None: | ||
""" | ||
Sells the given amount of shares for the given outcome in the given market. | ||
""" | ||
web3 = Web3(Web3.HTTPProvider(GNOSIS_RPC_URL)) | ||
amount_wei = xdai_to_wei(amount) | ||
from_address_checksummed = Web3.to_checksum_address(from_address) | ||
|
||
# Get the index of the outcome we want to buy. | ||
outcome_index: int = market.get_outcome_index(outcome) | ||
|
||
# Get the current nonce for the given from_address. | ||
# If making multiple transactions quickly after each other, | ||
# it's better to increae it manually (otherwise we could get stale value from the network and error out). | ||
nonce: Nonce = web3.eth.get_transaction_count(from_address_checksummed) | ||
|
||
# Calculate the amount of shares we will sell for the given selling amount of xdai. | ||
max_outcome_tokens_to_sell = omen_calculate_sell_amount( | ||
web3, market, amount_wei, outcome_index | ||
) | ||
# Allow 1% slippage. | ||
max_outcome_tokens_to_sell = add_fraction(max_outcome_tokens_to_sell, 0.01) | ||
|
||
# Approve the market maker to move our (all) conditional tokens. | ||
approve_tx_receipt = omen_approve_all_market_maker_to_move_conditionaltokens_tx( | ||
web3=web3, | ||
market=market, | ||
approve=True, | ||
from_address=from_address_checksummed, | ||
from_private_key=from_private_key, | ||
tx_params={"nonce": nonce}, | ||
) | ||
nonce = Nonce(nonce + ONE_NONCE) # Increase after each tx. | ||
check_tx_receipt(approve_tx_receipt) | ||
# Sell the shares. | ||
sell_receipt = omen_sell_shares_tx( | ||
web3, | ||
market, | ||
amount_wei, | ||
outcome_index, | ||
max_outcome_tokens_to_sell, | ||
from_address_checksummed, | ||
from_private_key, | ||
tx_params={"nonce": nonce}, | ||
) | ||
nonce = Nonce(nonce + ONE_NONCE) # Increase after each tx. | ||
check_tx_receipt(sell_receipt) | ||
if auto_withdraw: | ||
# Optionally, withdraw from the collateral token back to the `from_address` wallet. | ||
withdraw_receipt = omen_withdraw_collateral_token_tx( | ||
web3=web3, | ||
market=market, | ||
amount_wei=amount_wei, | ||
from_address=from_address_checksummed, | ||
from_private_key=from_private_key, | ||
tx_params={"nonce": nonce}, | ||
) | ||
nonce = Nonce(nonce + ONE_NONCE) # Increase after each tx. | ||
check_tx_receipt(withdraw_receipt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure nonce management is robust against race conditions and consider using a nonce manager if transactions are sent in quick succession.
|
||
|
||
class APIKeys(BaseSettings): | ||
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found out that Pydantic supports this, so we don't have to manually call os.getenv and changing env variables at runtime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah so good!
LOCAL = "local" | ||
|
||
|
||
class DeployableAgent(BaseModel): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What was the motivation to use BaseModel here? Seems like it doesn't have any fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assumed it would have fields when I started working on it, but now looks like it will just be a class containing only static methods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, then let's keep it, it can always be removed 😄
from cron_validator import CronValidator | ||
|
||
|
||
def deploy_to_gcp( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved these from the agent's file to a separate, gcp-specific, file.
return get_manifold_binary_markets(1)[0] | ||
|
||
|
||
def place_bet(amount: Mana, market_id: str, outcome: bool) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the api key as we talked about it on slack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Status
Actionable comments generated: 0
Configuration used: CodeRabbit UI
Files selected for processing (7)
- examples/cloud_deployment/gcp/agent.py (1 hunks)
- prediction_market_agent_tooling/config.py (1 hunks)
- prediction_market_agent_tooling/deploy/agent.py (1 hunks)
- prediction_market_agent_tooling/markets/manifold.py (1 hunks)
- prediction_market_agent_tooling/markets/markets.py (1 hunks)
- tests/deploy/test_deploy.py (1 hunks)
- tests/markets/test_manifold.py (1 hunks)
Files skipped from review as they are similar to previous changes (7)
- examples/cloud_deployment/gcp/agent.py
- prediction_market_agent_tooling/config.py
- prediction_market_agent_tooling/deploy/agent.py
- prediction_market_agent_tooling/markets/manifold.py
- prediction_market_agent_tooling/markets/markets.py
- tests/deploy/test_deploy.py
- tests/markets/test_manifold.py
Almost exactly the same as in the other repository, will comment on differences.
Summary by CodeRabbit
New Features
DeployableCoinFlipAgent
for deploying a coin flip agent in prediction markets, with a Flask endpoint for operation.Bug Fixes
Documentation
Refactor
Style
Tests
DeployableCoinFlipAgent
, interactions with Manifold and Omen markets, and utility functions.test_smoke
function with a return type hint.