# Claiming Winnings

Guide for claiming winnings from resolved prediction markets using the Python SDK. All claiming operations are gasless — no native tokens required.

## Prerequisites

You need a Level 2 client (private key + API credentials):

```python
import os
from dotenv import load_dotenv
from turbine_client import TurbineClient

load_dotenv()

client = TurbineClient(
    host=os.environ["TURBINE_HOST"],
    chain_id=int(os.environ.get("CHAIN_ID", "137")),
    private_key=os.environ["TURBINE_PRIVATE_KEY"],
    api_key_id=os.environ["TURBINE_API_KEY_ID"],
    api_private_key=os.environ["TURBINE_API_PRIVATE_KEY"],
)
```

## How Claiming Works

{% stepper %}
{% step %}

### Market resolution

When a market resolves, the winning outcome (YES or NO) is determined by the UMA oracle.
{% endstep %}

{% step %}

### Redeem winning tokens

Holders of the winning token can redeem each token for $1 USDC. Losing tokens become worthless.
{% endstep %}

{% step %}

### Gasless claim via SDK

The SDK signs an EIP-712 `RedeemPositions` permit and submits it to the relayer. No native gas is needed.
{% endstep %}
{% endstepper %}

## Check Resolution Status

Before claiming, verify the market is resolved:

```python
market_id = "0x..."

resolution = client.get_resolution(market_id)
print(f"Resolved: {resolution.resolved}")

if resolution.resolved:
    outcome = "YES" if resolution.outcome == 0 else "NO"
    print(f"Winner: {outcome}")
    print(f"Assertion ID: {resolution.assertion_id}")
```

## Claim from a Single Market

`claim_winnings()` takes the market's **contract address** (not the market ID). You can find it from the `Market` object:

```python
# Get the market to find the contract address
markets = client.get_markets()
market = next(m for m in markets if m.id == market_id)

# Claim winnings
try:
    result = client.claim_winnings(
        market_contract_address=market.contract_address
    )
    print(f"Claim submitted: {result}")
except ValueError as e:
    print(f"Cannot claim: {e}")
```

The method performs these steps internally:

{% stepper %}
{% step %}

### Query on-chain state

Queries the market contract on-chain for resolution status and condition data.
{% endstep %}

{% step %}

### Check balances

Checks your balance of the winning token.
{% endstep %}

{% step %}

### Sign permit

Signs an EIP-712 `RedeemPositions` permit.
{% endstep %}

{% step %}

### Submit to relayer

Submits to the relayer for gasless execution.
{% endstep %}
{% endstepper %}

It raises `ValueError` if:

* The market is not resolved yet
* You hold no winning tokens

## Batch Claim from Multiple Markets

Claim winnings from several resolved markets in a single batch transaction:

```python
contract_addresses = [
    "0xMarket1ContractAddress...",
    "0xMarket2ContractAddress...",
    "0xMarket3ContractAddress...",
]

try:
    result = client.batch_claim_winnings(contract_addresses)
    print(f"Batch claim submitted: {result}")
except ValueError as e:
    print(f"No markets to claim: {e}")
```

Markets that are not resolved or where you have no winning tokens are skipped automatically. The method only raises `ValueError` if *none* of the markets have claimable winnings.

## Find Markets with Claimable Winnings

Scan your positions for resolved markets you can claim from:

```python
# Get all your positions
positions = client.get_user_positions(
    address=client.address,
    chain_id=client.chain_id,
)

# Get markets and check which are resolved
markets = client.get_markets(chain_id=client.chain_id)
market_map = {m.id: m for m in markets}

claimable = []
for pos in positions:
    market = market_map.get(pos.market_id)
    if not market or not market.resolved:
        continue

    # Check if we hold winning tokens
    if market.winning_outcome == 0 and pos.yes_shares > 0:
        claimable.append(market)
        print(f"Claimable: {market.question}")
        print(f"  YES shares: {pos.yes_shares / 1e6:.2f} (payout: ${pos.yes_shares / 1e6:.2f} USDC)")

    elif market.winning_outcome == 1 and pos.no_shares > 0:
        claimable.append(market)
        print(f"Claimable: {market.question}")
        print(f"  NO shares: {pos.no_shares / 1e6:.2f} (payout: ${pos.no_shares / 1e6:.2f} USDC)")

# Batch claim all
if claimable:
    addresses = [m.contract_address for m in claimable]
    result = client.batch_claim_winnings(addresses)
    print(f"\nBatch claim submitted: {result}")
else:
    print("No claimable winnings found.")
```

## Monitor Claim Status

After submitting a claim, track its progress:

```python
# Check pending claims
pending = client.get_pending_claims()
for c in pending:
    print(f"Pending: market={c.market_address} payout={c.payout / 1e6:.2f} USDC tx={c.tx_hash}")

# Check failed claims
failed = client.get_failed_claims()
for c in failed:
    print(f"Failed: market={c.market_address} payout={c.payout / 1e6:.2f} USDC tx={c.tx_hash}")
```

## CLAIM\_ONLY\_MODE

The reference bot (`examples/price_action_bot.py`) supports a claim-only mode that disables trading and only claims winnings from previously traded markets:

```bash
CLAIM_ONLY_MODE=true TURBINE_PRIVATE_KEY=0x... python examples/price_action_bot.py
```

This is useful when you want to:

* Claim winnings without placing new orders
* Clean up positions from a previous trading session
* Run a background process that periodically claims resolved markets

## Complete Example

A standalone script that discovers and claims all available winnings:

```python
import os
import time
from dotenv import load_dotenv
from turbine_client import TurbineClient

load_dotenv()

client = TurbineClient(
    host=os.environ.get("TURBINE_HOST", "https://api.turbinefi.com"),
    chain_id=int(os.environ.get("CHAIN_ID", "137")),
    private_key=os.environ["TURBINE_PRIVATE_KEY"],
    api_key_id=os.environ["TURBINE_API_KEY_ID"],
    api_private_key=os.environ["TURBINE_API_PRIVATE_KEY"],
)

print(f"Wallet: {client.address}")
print(f"Chain: {client.chain_id}")

# Get positions and markets
positions = client.get_user_positions(address=client.address, chain_id=client.chain_id)
markets = client.get_markets(chain_id=client.chain_id)
market_map = {m.id: m for m in markets}

print(f"Found {len(positions)} positions across {len(markets)} markets\n")

# Find claimable markets
to_claim = []
total_payout = 0

for pos in positions:
    market = market_map.get(pos.market_id)
    if not market or not market.resolved:
        continue

    winning_shares = 0
    if market.winning_outcome == 0:
        winning_shares = pos.yes_shares
    elif market.winning_outcome == 1:
        winning_shares = pos.no_shares

    if winning_shares > 0:
        to_claim.append(market.contract_address)
        total_payout += winning_shares
        outcome = "YES" if market.winning_outcome == 0 else "NO"
        print(f"  {market.question}")
        print(f"    Winner: {outcome} | Your payout: ${winning_shares / 1e6:.2f} USDC")

if not to_claim:
    print("No claimable winnings found.")
    exit(0)

print(f"\nTotal payout: ${total_payout / 1e6:.2f} USDC from {len(to_claim)} markets")

# Claim all
if len(to_claim) == 1:
    result = client.claim_winnings(to_claim[0])
else:
    result = client.batch_claim_winnings(to_claim)

print(f"Claim submitted: {result}")

# Wait for confirmation
print("Checking claim status...")
time.sleep(10)

pending = client.get_pending_claims()
failed = client.get_failed_claims()

print(f"Pending: {len(pending)}")
print(f"Failed: {len(failed)}")

for f in failed:
    print(f"  FAILED: {f.market_address} — tx={f.tx_hash}")
```
