gnosis / prediction-market-agent-tooling

Tools to benchmark, deploy and monitor prediction market agents.
GNU Lesser General Public License v3.0
18 stars 4 forks source link

Understand and fix tests_integration/markets/omen/test_omen.py::test_omen_buy_and_sell_outcome 'sell tokens' hack #271

Closed evangriffiths closed 5 months ago

evangriffiths commented 5 months ago

The CI test appears to be flaky. It passed originally!, but appears to fail depending on:

Screenshot 2024-06-10 at 17 02 53

Understand why there is this discrepancy, and fix.

gabrielfior commented 5 months ago

Writing down some notes for when we get to this issue: -> When we first load an OmenAgentMarket, the property outcome_token_amounts comes from the subgraph. If we trade against that market on the fork (local_web3) or inside an RPC from Tenderly, the subgraph doesn't pick that up, hence the reserves are not affected. The proper way to fetch an updated value is by reading data directly from the contract, using the web3 provider from the test.

-> Let's say Bob wants to sell 10 Yes outcome tokens to market A. The functon to be called is market_contract.sell, with args (from function sell(uint returnAmount, uint outcomeIndex, uint maxOutcomeTokensToSell) external {):

function sell(uint returnAmount, uint outcomeIndex, uint maxOutcomeTokensToSell) external {

-> maxOutcomeTokensToSell is trivial and can be set as equal the number of outcome tokens the user has (via market.get_token_balance() -> The tricky part is returnAmount, which is the current value in xDAI of maxOutcomeTokensToSell (or whichever amount of outcome_tokens the user wants to sell). What we need here is the inverse of calcSellAmount, which is used in the smart contract as per below:

uint outcomeTokensToSell = calcSellAmount(returnAmount, outcomeIndex);

There is already an implementation for this function (calculate_sell_amount_in_collateral - link) - however it delivers results that have a small discrepancy (~ 1e-3 xDAI) from the correct value. This results in a non-zero position even though the user tries to sell his entire outcome_tokens position (see test).

evangriffiths commented 5 months ago

-> When we first load an OmenAgentMarket, the property outcome_token_amounts comes from the subgraph. If we trade against that market on the fork (local_web3) or inside an RPC from Tenderly, the subgraph doesn't pick that up, hence the reserves are not affected. The proper way to fetch an updated value is by reading data directly from the contract, using the web3 provider from the test.

This is the issue!!

The test was doing:

market = get_market() # market.outcome_token_amounts is fixed now
market.buy_tokens(amount_xdai) # the pool balance changes, but market.outcome_token_amounts doesn't!
market.sell_tokens(amount_tokens) # the calculation uses incorrect pool balance amounts!!

The reason why the microchain didn't see this problem is because it always does get_market before buying and selling, so the outcome_token_amounts are always up to date with the subgraph. I've fixed it now (here https://github.com/gnosis/prediction-market-agent-tooling/pull/272) to get the pool balance via the smart contract just before calculating the sell colateral.

evangriffiths commented 5 months ago

Fixed by https://github.com/gnosis/prediction-market-agent-tooling/pull/272