OpenSea API V2创建Listing时签名无效,如何正确签名订单?
解决OpenSea Seaport订单签名无效问题
调用OpenSea测试网API创建Seaport挂单时,返回错误:
{"errors":["['Invalid order signature']"]}
以下是错误的签名实现分析及正确解决方案:
错误原因分析
- 第一种签名方式:直接将Order对象转为字符串后签名,完全不符合Seaport要求的EIP-712结构化签名规范,Seaport不会认可这种简单的字符串哈希签名。
- 第二种签名方式:虽然尝试引入EIP-712的domain和类型定义,但错误地将domain、类型和order对象转为字符串拼接后签名,这不是EIP-712的正确哈希方式,必须使用结构化数据编码生成签名哈希。
正确的Python签名实现
Seaport订单签名必须严格遵循EIP-712规范,使用web3库的结构化数据编码功能生成正确的消息哈希后再签名。同时要注意:order参数中所有数值类型(如startTime、salt、各类amount等)必须是整数类型,不能是字符串,否则编码会出错。
修正后的完整代码
import requests from web3.auto import w3 from eth_account.messages import encode_structured_data import time def create_order_model(): current_time = int(time.time()) order = { "offerer": "0xd107AC5e5830BB95f880cA3b1270ccCdAA0A3c5e", "zone": "0x00000000e88fe2628ebc5da81d2b3cead633e89e", "zoneHash": "0x3000000000000000000000000000000000000000000000000000000000000000", "startTime": current_time, "endTime": current_time + 2629743, # 当前时间+1个月(秒) "orderType": 2, "offer": [ { "itemType": 2, "token": "0x45aeb17Db4dd06FF9Ca6cB1E37455AAF761Bdc14", "identifierOrCriteria": 1, "startAmount": 1, "endAmount": 1, }, ], "consideration": [ { "itemType": 0, "token": "0x0000000000000000000000000000000000000000", "identifierOrCriteria": 0, "startAmount": 9750000000000000000, "endAmount": 9750000000000000000, "recipient": "0xd107AC5e5830BB95f880cA3b1270ccCdAA0A3c5e", }, { "itemType": 0, "token": "0x0000000000000000000000000000000000000000", "identifierOrCriteria": 0, "startAmount": 250000000000000000, "endAmount": 250000000000000000, "recipient": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", }, ], "totalOriginalConsiderationItems": 2, "salt": 12686911856931635052326433555881236148, "conduitKey": "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000", "nonce": 0, "counter": 0 } return order def sign_order(order): private_key = "YOUR_PRIVATE_KEY" # EIP-712 domain数据 domain = { "name": "Seaport", "version": "1.1", "chainId": 5, # Goerli测试网链ID "verifyingContract": "0x00000000e88fe2628ebc5da81d2b3cead633e89e", # Goerli的Seaport合约地址 } # EIP-712类型定义 types = { "OrderComponents": [ {"name": "offerer", "type": "address"}, {"name": "zone", "type": "address"}, {"name": "offer", "type": "OfferItem[]"}, {"name": "consideration", "type": "ConsiderationItem[]"}, {"name": "orderType", "type": "uint8"}, {"name": "startTime", "type": "uint256"}, {"name": "endTime", "type": "uint256"}, {"name": "zoneHash", "type": "bytes32"}, {"name": "salt", "type": "uint256"}, {"name": "conduitKey", "type": "bytes32"}, {"name": "counter", "type": "uint256"}, ], "OfferItem": [ {"name": "itemType", "type": "uint8"}, {"name": "token", "type": "address"}, {"name": "identifierOrCriteria", "type": "uint256"}, {"name": "startAmount", "type": "uint256"}, {"name": "endAmount", "type": "uint256"}, ], "ConsiderationItem": [ {"name": "itemType", "type": "uint8"}, {"name": "token", "type": "address"}, {"name": "identifierOrCriteria", "type": "uint256"}, {"name": "startAmount", "type": "uint256"}, {"name": "endAmount", "type": "uint256"}, {"name": "recipient", "type": "address"}, ], } # 生成EIP-712结构化消息 structured_msg = { "domain": domain, "types": types, "primaryType": "OrderComponents", "message": order } # 编码并签名 encoded_msg = encode_structured_data(structured_msg) signed_message = w3.eth.account.sign_message(encoded_msg, private_key=private_key) return signed_message.signature.hex() def main(): url = "https://testnets-api.opensea.io/v2/orders/goerli/seaport/listings" headers = { "accept": "application/json", "content-type": "application/json" } order_parameters = create_order_model() signature = sign_order(order_parameters) params = {"parameters": order_parameters, "signature": signature} response = requests.post(url, headers=headers, json=params) print(response.text) if __name__ == '__main__': main()
关键修正点
- 使用
encode_structured_data替代encode_defunct,严格按照EIP-712规范编码订单数据 - 将order中所有数值类型字段从字符串改为整数,确保编码类型匹配
- 正确构建EIP-712的domain、types和message结构,而非字符串拼接
内容的提问来源于stack exchange,提问作者0xkornel
相关产品推荐
相关产品推荐

