Skip to main content

Polymarket Proxy wallet redeem SDK - Execute redeem operations on Polymarket using proxy wallets

Project description

poly-web3

PyPI Python License

Python SDK for redeeming and splitting/merging Polymarket positions via Proxy/Safe wallets (gas-free).

English | 中文

Python >= 3.11
pip install poly-web3
from poly_web3 import PolyWeb3Service

service = PolyWeb3Service(
    clob_client=client,
    relayer_client=relayer_client,
)

# Redeem all redeemable positions for the current account.
service.redeem_all(batch_size=10)

# Split/Merge for binary markets (amount in human USDC units).
service.split("0x...", 10)
service.merge("0x...", 10)

See the full example

Redeem Behavior Notes

  • Redeemable positions are fetched via the official Positions API, which typically has ~1 minute latency.
  • redeem_all returns an empty list if there are no redeemable positions. If the returned list contains None, the redeem failed and should be retried.

Split/Merge Notes

  • split/merge are designed for binary markets (Yes/No) and use the default partition internally.
  • amount is in human units (USDC), and is converted to base units internally.

FAQ

  1. UI shows redeemable, but redeem_all returns []: The official Positions API can be delayed by 1–3 minutes. Wait a bit and retry.
  2. RPC error during redeem: Switch RPC endpoints by setting rpc_url when instantiating PolyWeb3Service.
  3. Redeem stuck in execute: The official relayer may be congested. Stop redeeming for 1 hour to avoid nonce looping from repeated submissions.
  4. Relayer client returns 403: You need to apply for Builder API access and use a valid key. Reference: Polymarket Builders — Introduction: https://docs.polymarket.com/developers/builders/builder-intro
  5. Relayer daily limit: The official relayer typically limits to 100 requests per day. Prefer batch redeem (batch_size) to reduce the number of requests and avoid hitting the limit.

About the Project

This project is a Python rewrite of Polymarket's official TypeScript implementation of builder-relayer-client, designed to provide Python developers with a convenient tool for executing Proxy and Safe wallet redeem operations on Polymarket.

Important Notes:

  • This project implements official CTF redeem plus binary split/merge operations
  • Other features (such as trading, order placement, etc.) are not within the scope of this project

Some Polymarket-related redeem or write operations implemented in this project depend on access granted through Polymarket's Builder program. To perform real redeem operations against Polymarket, you must apply for and obtain a Builder key/credentials via Polymarket's official Builder application process. After approval you will receive the credentials required to use the Builder API—only then will the redeem flows in this repository work against the live service. For local development or automated tests, use mocks or testnet setups instead of real keys to avoid exposing production credentials.

Reference:

Current Status:

  • Proxy Wallet - Fully supported for redeem/split/merge
  • Safe Wallet - Fully supported for redeem/split/merge
  • 🚧 EOA Wallet - Under development

We welcome community contributions! If you'd like to help implement EOA wallet redeem functionality, or have other improvement suggestions, please feel free to submit a Pull Request.

Installation

pip install poly-web3

Or using uv:

uv add poly-web3

Requirements

  • Python >= 3.11

Dependencies

  • py-clob-client >= 0.25.0 - Polymarket CLOB client
  • py-builder-relayer-client >= 0.0.1 - Builder Relayer client
  • web3 >= 7.0.0 - Web3.py library
  • eth-utils == 5.3.1 - Ethereum utilities library

Quick Start

Basic Usage - Execute Redeem

import os
import dotenv
from py_builder_relayer_client.client import RelayClient
from py_builder_signing_sdk.config import BuilderConfig
from py_builder_signing_sdk.sdk_types import BuilderApiKeyCreds
from py_clob_client.client import ClobClient
from poly_web3 import RELAYER_URL, PolyWeb3Service

dotenv.load_dotenv()

# Initialize ClobClient
host = "https://clob.polymarket.com"
chain_id = 137  # Polygon mainnet
client = ClobClient(
    host,
    key=os.getenv("POLY_API_KEY"),
    chain_id=chain_id,
    signature_type=1,  # Proxy wallet type (signature_type=2 for Safe)
    funder=os.getenv("POLYMARKET_PROXY_ADDRESS"),
)

client.set_api_creds(client.create_or_derive_api_creds())

# Initialize RelayerClient
relayer_client = RelayClient(
    RELAYER_URL,
    chain_id,
    os.getenv("POLY_API_KEY"),
    BuilderConfig(
        local_builder_creds=BuilderApiKeyCreds(
            key=os.getenv("BUILDER_KEY"),
            secret=os.getenv("BUILDER_SECRET"),
            passphrase=os.getenv("BUILDER_PASSPHRASE"),
        )
    ),
)

# Create service instance
service = PolyWeb3Service(
    clob_client=client,
    relayer_client=relayer_client,
    rpc_url="https://polygon-bor.publicnode.com",  # optional
)


# Redeem all positions that are currently redeemable
redeem_all_result = service.redeem_all(batch_size=10)
print(f"Redeem all result: {redeem_all_result}")
# If redeem_all_result contains None, refer to README FAQ and retry.
if redeem_all_result and any(item is None for item in redeem_all_result):
    print("Redeem failed for some items; please retry.")

# Execute redeem operation (batch)
condition_ids = [
    "0xc3df016175463c44f9c9f98bddaa3bf3daaabb14b069fb7869621cffe73ddd1c",
    "0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
]
redeem_batch_result = service.redeem(condition_ids, batch_size=10)
print(f"Redeem batch result: {redeem_batch_result}")
if redeem_all_result and any(item is None for item in redeem_all_result):
    print("Redeem failed for some items; please retry.")

Basic Usage - Split/Merge (Binary Markets)

# amount is in human units (USDC)
split_result = service.split(
    "0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
    1.5,
)
print(f"Split result: {split_result}")

merge_result = service.merge(
    "0x31fb435a9506d14f00b9de5e5e4491cf2223b6d40a2525d9afa8b620b61b50e2",
    1.5,
)
print(f"Merge result: {merge_result}")

API Documentation

PolyWeb3Service

The main service class that automatically selects the appropriate service implementation based on wallet type.

Methods

redeem(condition_ids: list[str], batch_size: int = 20)

Execute redeem operation.

Parameters:

  • condition_ids (list[str]): List of condition IDs
  • batch_size (int): Batch size for redeem requests

Returns:

  • dict | list[dict]: Transaction result(s) containing transaction status and related information

Examples:

# Batch redeem
result = service.redeem(["0x...", "0x..."], batch_size=10)
redeem_all(batch_size: int = 20) -> list[dict]

Redeem all positions that are currently redeemable for the authenticated account.

Returns:

  • list[dict]: List of redeem results; empty list if no redeemable positions. If the list contains None, the redeem failed and should be retried.

Examples:

# Redeem all positions that can be redeemed
service.redeem_all(batch_size=10)
split(condition_id: str, amount: int | float | str)

Split a binary (Yes/No) position. amount is in human USDC units.

Parameters:

  • condition_id (str): Condition ID
  • amount (int | float | str): Amount in USDC

Returns:

  • dict | None: Transaction result

Examples:

result = service.split("0x...", 1.25)
merge(condition_id: str, amount: int | float | str)

Merge a binary (Yes/No) position. amount is in human USDC units.

Parameters:

  • condition_id (str): Condition ID
  • amount (int | float | str): Amount in USDC

Returns:

  • dict | None: Transaction result

Examples:

result = service.merge("0x...", 1.25)

Optional APIs

is_condition_resolved(condition_id: str) -> bool

Check if the specified condition is resolved.

Parameters:

  • condition_id (str): Condition ID (32-byte hexadecimal string)

Returns:

  • bool: Returns True if the condition is resolved, otherwise False
get_winning_indexes(condition_id: str) -> list[int]

Get the list of winning indexes.

Parameters:

  • condition_id (str): Condition ID

Returns:

  • list[int]: List of winning indexes
get_redeemable_index_and_balance(condition_id: str, owner: str) -> list[tuple]

Get redeemable indexes and balances for the specified address.

Parameters:

  • condition_id (str): Condition ID
  • owner (str): Wallet address

Returns:

  • list[tuple]: List of tuples containing (index, balance), balance is in USDC units

Optional: Query Operations

Before executing redeem, you can optionally check the condition status and query redeemable balances:

# Check if condition is resolved
condition_id = "0xc3df016175463c44f9c9f98bddaa3bf3daaabb14b069fb7869621cffe73ddd1c"
can_redeem = service.is_condition_resolved(condition_id)

# Get redeemable indexes and balances
redeem_balance = service.get_redeemable_index_and_balance(
    condition_id, owner=client.builder.funder
)

print(f"Can redeem: {can_redeem}")
print(f"Redeemable balance: {redeem_balance}")

Project Structure

poly_web3/
├── __init__.py              # Main entry point, exports PolyWeb3Service
├── const.py                 # Constant definitions (contract addresses, ABIs, etc.)
├── schema.py                # Data models (WalletType, etc.)
├── signature/               # Signature-related modules
│   ├── build.py            # Proxy wallet derivation and struct hashing
│   ├── hash_message.py     # Message hashing
│   └── secp256k1.py        # secp256k1 signing
└── web3_service/           # Web3 service implementations
    ├── base.py             # Base service class
    ├── proxy_service.py    # Proxy wallet service (✅ Implemented)
    ├── eoa_service.py      # EOA wallet service (🚧 Under development)
    └── safe_service.py     # Safe wallet service (✅ Implemented)

Notes

  1. Environment Variable Security: Make sure .env file is added to .gitignore, do not commit sensitive information to the code repository
  2. Network Support: Currently mainly supports Polygon mainnet (chain_id: 137), Amoy testnet may have limited functionality
  3. Wallet Type: Proxy (signature_type: 1) and Safe (signature_type: 2) are supported; EOA wallet operations are under development
  4. Gas Fees: Transactions are executed through Relayer, gas fees are handled by the Relayer

Development

Install Development Dependencies

uv pip install -e ".[dev]"

Run Examples

python examples/example_redeem.py
python examples/example_split_merge.py

Contributing

Simple contribution flow:

  1. Open an Issue to describe the change (bug/feature/doc).
  2. Fork and create a branch: feat/xxx or fix/xxx.
  3. Make changes and update/add docs if needed.
  4. Run: uv run python -m examples.example_redeem or uv run python -m examples.example_split_merge (if applicable).
  5. Open a Pull Request and link the Issue.

License

MIT

Author

PinBar

Related Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

poly_web3-1.0.2.tar.gz (21.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

poly_web3-1.0.2-py3-none-any.whl (21.7 kB view details)

Uploaded Python 3

File details

Details for the file poly_web3-1.0.2.tar.gz.

File metadata

  • Download URL: poly_web3-1.0.2.tar.gz
  • Upload date:
  • Size: 21.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for poly_web3-1.0.2.tar.gz
Algorithm Hash digest
SHA256 af9aab8370cf86c99bd8b85ea0c673f08c4605c995e162c5d943939076930d06
MD5 4f1e887efac20ace4ebe4a3f9a4d1f49
BLAKE2b-256 07be20f8fd72a886244e9fd6b5ff5e755498b81c0453bc5bf4d9c47c8008c6d9

See more details on using hashes here.

File details

Details for the file poly_web3-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: poly_web3-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 21.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for poly_web3-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 f881a1819b603c978f51f733cb98c130c5ed7b85bfc0266d2d6d8ea7d5818afa
MD5 fd476b756405c4f02d39c0285e7806ca
BLAKE2b-256 7c85c7a1a9ecfb44fbd71df9d46616e9681bdddf4d21923315a2567749e42a09

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page