Web3.py对接Polymarket CLOB API时EIP-712签名报错:无效域键ClobAuth
问题描述
使用Web3.py与Polymarket的CLOB API交互,尝试签署EIP-712结构化消息创建API密钥时,触发错误:
[ERROR] Failed to sign typed data: Invalid domain key:
ClobAuth
已尝试操作
- 确认ClobAuth域和EIP-712签名类型配置正确
- 检查Web3.py已通过Alchemy正确连接Polygon主网
环境信息
- Python版本:3.12
- Web3.py版本:5.x.x
- 使用
py_clob_client与Polymarket API交互
相关代码
import requests from web3 import Web3 from dotenv import load_dotenv import os import time # 原代码遗漏该模块导入 # Load the .env file to access private keys and other environment variables load_dotenv() # Function to implement manual PoA middleware injection def geth_poa_middleware(make_request, web3): def middleware(method, params): if method == "eth_getBlockByNumber": params = list(params) if params[0] == "pending": params[0] = "latest" return make_request(method, params) return middleware from py_clob_client.client import ClobClient from py_clob_client.clob_types import OrderArgs, OrderType, ApiCreds from py_clob_client.order_builder.constants import BUY, SELL # --- Configuration --- CLOB_ENDPOINT = "https://clob.polymarket.com/" # API Endpoint CHAIN_ID = 137 # Polygon Mainnet. Use 80001 for testnet (Mumbai) # --- Authentication --- def create_api_key(private_key: str) -> ApiCreds: """Creates API key using private key authentication (L1).""" print("[DEBUG] Initializing Web3 with Polygon mainnet.") w3 = Web3(Web3.HTTPProvider(f"https://polygon-mainnet.g.alchemy.com/v2/{os.getenv('YOUR_ALCHEMY_KEY')}")) # Inject the PoA middleware manually w3.middleware_onion.inject(geth_poa_middleware, layer=0) # Create account from private key account = w3.eth.account.from_key(private_key) address = account.address print(f"[DEBUG] Created account with address: {address}") ts = str(int(time.time())) nonce = 0 # Set to 0 initially message = "This message attests that I control the given wallet" print("[DEBUG] Constructing domain and types for EIP-712 signature.") # Define domain and types according to Polymarket specs domain = { "name": "ClobAuthDomain", "version": "1", "chainId": CHAIN_ID, } # The types for the signature, as per EIP-712 types = { "ClobAuth": [ {"name": "address", "type": "address"}, {"name": "timestamp", "type": "string"}, {"name": "nonce", "type": "uint256"}, {"name": "message", "type": "string"}, ], } # Values for the signature value = { "address": address, "timestamp": ts, "nonce": nonce, "message": message, } print("[DEBUG] Value for signing:") print(value) try: # Encode the structured data for EIP-712 signed_message = w3.eth.account.sign_typed_data(domain, types, value) signature = signed_message.signature.hex() print(f"[DEBUG] Signature: {signature}") except Exception as e: print(f"[ERROR] Failed to sign typed data: {e}") return None # Prepare headers with the signature headers = { 'POLY_ADDRESS': address, 'POLY_SIGNATURE': signature, 'POLY_TIMESTAMP': ts, 'POLY_NONCE': str(nonce) } print(f"[DEBUG] Sending POST request to {CLOB_ENDPOINT}auth/api-key") response = requests.post(f"{CLOB_ENDPOINT}auth/api-key", headers=headers) # Print the response status print(f"[DEBUG] Response Status Code: {response.status_code}") try: response.raise_for_status() # Raise an error for bad responses creds = ApiCreds(**response.json()) print(f"[DEBUG] API Key created: {creds.apiKey}") return creds except requests.exceptions.HTTPError as e: print(f"[ERROR] HTTPError: {e.response.text}") except Exception as e: print(f"[ERROR] Unexpected error: {e}") return None # --- Trading --- def execute_trade(client: ClobClient, token_id: str, side: str, size: float, price: float, order_type: OrderType = OrderType.GTC) -> dict: """Executes a trade on Polymarket.""" print(f"[DEBUG] Preparing order: Side={side}, Size={size}, Price={price}, Token ID={token_id}") order_args = OrderArgs( price=price, size=size, side=BUY if side.upper() == "BUY" else SELL, # Ensure side is correctly formatted token_id=token_id ) print("[DEBUG] Signing the order.") try: signed_order = client.create_order(order_args) print(f"[DEBUG] Signed Order: {signed_order}") except Exception as e: print(f"[ERROR] Error signing order: {e}") return {} print("[DEBUG] Posting the order to the Polymarket CLOB.") try: response = client.post_order(signed_order, order_type) print(f"[DEBUG] Order Response: {response}") return response except Exception as e: print(f"[ERROR] Error posting order: {e}") return {} # Example usage (replace with your actual values): if __name__ == "__main__": private_key = os.getenv('private_key') # Your Polygon private key, ensure it is loaded correctly # Check if private_key is loaded if not private_key: print("[ERROR] Private key not loaded from .env file.") exit(1) # 1. Create/Get API Keys try: creds = create_api_key(private_key) if not creds: print("[ERROR] Failed to create API Key.") exit(1) print(f"[INFO] Created API Key: {creds.apiKey}") print(f"[INFO] Secret: {creds.secret}") # Be careful not to expose this! print(f"[INFO] Passphrase: {creds.passphrase}") # Be careful not to expose this! except requests.exceptions.HTTPError as e: if e.response.status_code == 409: # Key already exists print("[INFO] API Key already exists, deriving it.") client_l1 = ClobClient(CLOB_ENDPOINT, key=private_key, chain_id=CHAIN_ID) creds = client_l1.derive_api_key() # Retrieve existing key print(f"[INFO] Derived existing API Key: {creds.apiKey}") else: print(f"[ERROR] HTTP Error during API key creation: {e}") exit(1) # 2. Initialize Trading Client (using API Key authentication now) print("[DEBUG] Initializing the ClobClient with the API credentials.") client = ClobClient(CLOB_ENDPOINT, key=creds.apiKey, secret=creds.secret, passphrase=creds.passphrase, chain_id=CHAIN_ID) # 3. Example Trade token_id = os.getenv('YOUR_TOKEN_ID') # Replace with the actual token ID # Check if token_id is loaded if not token_id: print("[ERROR] Token ID not loaded from environment variables.") exit(1) side = "BUY" size = 10.0 price = 0.6 # Example price. Get this from market data. order_type = OrderType.GTC # Or OrderType.FOK or OrderType.GTD try: print("[INFO] Executing trade.") trade_response = execute_trade(client, token_id, side, size, price, order_type) print(f"[INFO] Trade Response: {trade_response}") except Exception as e: print(f"[ERROR] Error executing trade: {e}")
解决方案
错误的核心原因是Web3.py v5的sign_typed_data方法要求在types字典中必须显式定义EIP712Domain类型,代码中仅定义了ClobAuth类型,导致签名时无法正确识别域结构。
修改步骤
- 补充
EIP712Domain类型定义
在types字典中添加与domain字段对应的EIP712Domain类型描述:
types = { "EIP712Domain": [ {"name": "name", "type": "string"}, {"name": "version", "type": "string"}, {"name": "chainId", "type": "uint256"}, ], "ClobAuth": [ {"name": "address", "type": "address"}, {"name": "timestamp", "type": "string"}, {"name": "nonce", "type": "uint256"}, {"name": "message", "type": "string"}, ], }
- 修复模块导入问题
在代码顶部添加time模块导入,避免time.time()调用报错:
import time
- 可选:使用
py_clob_client内置方法简化流程
可以直接用py_clob_client提供的方法生成API密钥,无需手动构建EIP-712结构:
client_l1 = ClobClient(CLOB_ENDPOINT, key=private_key, chain_id=CHAIN_ID) creds = client_l1.create_api_key()
内容的提问来源于stack exchange,提问作者ahron
相关产品推荐
相关产品推荐

