Python
LimitOrders 0x v4 伺服器錯誤 500
我正在嘗試使用 0x 參考來獲得限價訂單: https ://docs.0x.org/market-makers/guides/0x-v4-rfq-orders-code-examples
希望通過限價訂單將一些 WETH 換成一些 ZRX。
我收到錯誤:
500 {'reason': "Cannot read properties of undefined (reading 'data')"}
這是我的要求:
{ "makerToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "takerToken": "0xe41d2489571d322189246dafa5ebde1f4699f498", "makerAmount": "50000000000000000", "takerAmount": "220000000000000000000", "takerTokenFeeAmount": "0", "maker": "0xMYADDRESSS", "taker": "0x0000000000000000000000000000000000000000", "sender": "0x0000000000000000000000000000000000000000", "feeRecipient": "0x0000000000000000000000000000000000000000", "pool": "0x0000000000000000000000000000000000000000000000000000000000000000", "expiry": "1648687063", "salt": "1648686703", "chainId": 1, "verifyingContract": "0xDef1C0ded9bec7F1a1670819833240f027b25EfF", "signature": { "signatureType": 3, "v": 27, "r": "0xbec1e45ecba34aa17158ebfd672d6c6fdd418cc4ba576efe23aad24af35d85d6", "s": "0x7cbd78a796c3fad5b87e75cc96c6e5f2963dd78265724e700e30049af41cd927" } }
我將其發佈到:
https://api.0x.org/orderbook/v1/order
下面是完整的程式碼,對不起,它很長,這是我能在沒有包裝類的情況下弄清楚的唯一方法:
import eth_abi import web3 from eth_utils import keccak, remove_0x_prefix, to_bytes import hashlib from bitcoin import ecdsa_raw_sign from typing import NamedTuple, Tuple import time import os import personal # Create 2 private keys. These keys have been funded with some Ropsten ETH. For these scripts to work, the accounts below need to have some Ropsten ETH private_key_maker = web3.Web3.toBytes(hexstr=personal.MY_PK) private_key_maker_address = personal.MY_ETH_ADY # Fetch the RPC URL and the Exchange Proxy address MAIN_URL = personal.MYINFURA ZERO_EX_EP = '0xdef1c0ded9bec7f1a1670819833240f027b25eff' ZERO_EX_EP_CS = web3.Web3.toChecksumAddress('0xdef1c0ded9bec7f1a1670819833240f027b25eff') TOKEN_A = web3.Web3.toChecksumAddress('0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2') #WETH TOKEN_B = web3.Web3.toChecksumAddress('0xe41d2489571d322189246dafa5ebde1f4699f498') #ZRX print(TOKEN_A) provider = web3.HTTPProvider(MAIN_URL) client = web3.Web3(provider) print(f"Latest block is: {client.eth.getBlock('latest')['number']}") import sha3 private_key_address = web3.Web3.toChecksumAddress(personal.MY_ETH_ADY) private_key = personal.MY_PK null_eth_address = '0x0000000000000000000000000000000000000000' ORDER_URL = 'https://api.0x.org/orderbook/v1/order' from eip712_structs import EIP712Struct, Address, String, Bytes, Address, Uint from eip712_structs import Bytes def pad_20_bytes_to_32(twenty_bytes: bytes): return bytes(12) + twenty_bytes def int_to_32_big_endian_bytes(i: int): return i.to_bytes(32, byteorder="big") order_field_types = [ "address", "address", "uint128", "uint128", "uint128", "address", "address", "address", "address", "bytes32", "uint64", "uint256", ] class LimitOrder(NamedTuple): makerToken: str takerToken: str makerAmount: int takerAmount: int takerTokenFeeAmount: int maker: str taker: str sender: str feeRecipient: str pool: int expiry: int salt: int order = LimitOrder( makerToken= TOKEN_A.lower(), takerToken= TOKEN_B.lower(), makerAmount= int(0.05 * 1e18), takerAmount= int(220 * 1e18), takerTokenFeeAmount = 0, maker= private_key_maker_address, taker= null_eth_address, sender = null_eth_address, feeRecipient = null_eth_address, pool = 0, expiry=int(time.time() + 360), #6 mins salt=int(time.time()) ) eip191_header = b"\x19\x01" eip712_domain_separator_schema_hash = client.keccak( b"EIP712Domain(" + b"string name," + b"string version," + b"uint256 chainId," + b"address verifyingContract" + b")" ) eip712_order_schema_hash = client.keccak( b"LimitOrder(" + b"address makerToken," + b"address takerToken," + b"uint128 makerAmount," + b"uint128 takerAmount," + b"uint128 takerTokenFeeAmount," + b"address maker," + b"address taker," + b"address sender," + b"address feeRecipient," + b"bytes32 pool," + b"uint64 expiry," + b"uint256 salt" + b")" ) def pad_20_bytes_to_32(twenty_bytes: bytes): return bytes(12) + twenty_bytes def int_to_32_big_endian_bytes(i: int): return i.to_bytes(32, byteorder="big") def make_eip712_domain_struct_header_hash(chain_id: int, verifying_contract: str) -> str: return keccak( eip712_domain_separator_schema_hash + keccak(b"ZeroEx") + keccak(b"1.0.0") + int_to_32_big_endian_bytes(int(chain_id)) + pad_20_bytes_to_32(to_bytes(hexstr=verifying_contract)) ) def get_order_hash_0xv4(order: LimitOrder, chain_id: int, exchange_proxy_address: str): """ Returns an order hash for a given LIMIT order. The order hash will not contain the `0x` prefix. :params order: a RFQ order :params chain_id: the Chain ID. Ropsten is 3, while Mainnet is 1 :params exchange_proxy_address: the address of the Exchange Proxy. This address must be checksummed. """ order_struct = eip712_order_schema_hash + pad_20_bytes_to_32(to_bytes(hexstr=order.makerToken)) + pad_20_bytes_to_32(to_bytes(hexstr=order.takerToken))+ int_to_32_big_endian_bytes(int(order.makerAmount))+ int_to_32_big_endian_bytes(int(order.takerAmount))+ int_to_32_big_endian_bytes(int(order.takerTokenFeeAmount))+ pad_20_bytes_to_32(to_bytes(hexstr=order.maker))+ pad_20_bytes_to_32(to_bytes(hexstr=order.taker))+ pad_20_bytes_to_32(to_bytes(hexstr=order.sender))+ pad_20_bytes_to_32(to_bytes(hexstr=order.feeRecipient))+ int_to_32_big_endian_bytes(int(order.pool))+ int_to_32_big_endian_bytes(int(order.expiry))+ int_to_32_big_endian_bytes(int(order.salt)) eip712_order_struct_hash = client.keccak(order_struct) eip712_domain_struct_hash = make_eip712_domain_struct_header_hash( chain_id=chain_id, verifying_contract=exchange_proxy_address ) #print(f"Domain hash: {eip712_domain_struct_hash.hex()}") #print(f"Order schema hash: {eip712_order_schema_hash.hex()}") return keccak( eip191_header + eip712_domain_struct_hash + eip712_order_struct_hash ) def get_order_info(order: LimitOrder) -> Tuple[bytes, int, int]: function_signature = web3.Web3.keccak(text=f'getLimitOrderInfo(({",".join(order_field_types)}))').hex()[:10] encoded_args = eth_abi.encode_abi(order_field_types, [ order.makerToken, order.takerToken, order.makerAmount, order.takerAmount, order.takerTokenFeeAmount, order.maker, order.taker, order.sender, order.feeRecipient, int_to_32_big_endian_bytes(order.pool), # Pool is represented as an unsigned integer, however it is encoded as a `bytes32` order.expiry, order.salt ]) calldata = function_signature + encoded_args.hex() tx = { "to": client.toChecksumAddress(ZERO_EX_EP), "data": calldata, } # Perform an `eth_call` to read the transaction data result. The response structure can be found here: # https://0xprotocol.readthedocs.io/en/latest/basics/functions.htbml#getrfqorderinfo response = client.eth.call(tx) on_chain_order_hash, fillable_status, amount_filled = eth_abi.decode_abi(['bytes32', 'uint8', 'uint128'], bytes.fromhex(response.hex()[2:])) print(f"ONLINE Hash hex: {on_chain_order_hash.hex()}") #print(f"Fillable status: {fillable_status}") #print(f"Amount filled: {amount_filled / 1e18}") return on_chain_order_hash, fillable_status, amount_filled import json def order_to_sra(order: LimitOrder, chain_id: int, verifying_contract: str, v: int, r: bytes, s: bytes): obj = { "makerToken": order.makerToken, "takerToken": order.takerToken, "makerAmount": str(order.makerAmount), "takerAmount": str(order.takerAmount), "takerTokenFeeAmount": str(order.takerTokenFeeAmount), "maker": order.maker, "taker": order.taker, "sender": order.sender, "feeRecipient": order.feeRecipient, "pool": '0x' + order.pool.to_bytes(32, 'big').hex(), "expiry": str(order.expiry), "salt": str(order.salt), "chainId": int(chain_id), "verifyingContract": str(verifying_contract), "signature": { "signatureType": 3, "v": v, "r": f"0x{r.hex()}", "s": f"0x{s.hex()}", } } return json.dumps(obj, indent=4) # Indent is added simply for prettifying def SignHashNew(hash_bytesArray, privateKey): from eth_account.messages import encode_defunct message = encode_defunct(hash_bytesArray) signed_message = client.eth.account.sign_message(message, private_key=privateKey) return ( signed_message.v, '0x' + signed_message.r.to_bytes(length=32, byteorder='big').hex(), '0x' + signed_message.s.to_bytes(length=32, byteorder='big').hex() ) off_chain_order_hash = get_order_hash_0xv4(order, 1, ZERO_EX_EP) off_chain_order_hash.hex() print('OFFCHAIN Hash hex: ', off_chain_order_hash.hex()) on_chain_order_hash = get_order_info(order) v, r, s = SignHashNew(off_chain_order_hash, private_key_maker) r = bytes.fromhex(r[2:]) s = bytes.fromhex(s[2:]) json_order = order_to_sra(order=order, chain_id=1, verifying_contract=ZERO_EX_EP_CS, v=v, r=r, s=s) json_order = json.loads(json_order) print(json_order) import requests response = requests.post( url=ORDER_URL, json=json_order, headers = { "Content-Type": "application/json" } ) if response.status_code != 200: try: print(response.status_code) response_json = response.json() print(response_json) except: print('error getting response')
上面的程式碼不再返回 500 錯誤。
它現在返回 200,這意味著它是成功的。
對於希望在 python 中下限價單的任何人,我希望這將是一個很好的參考。
讀取設置為“calldata”的“data”屬性時似乎出錯。您可以記錄“數據”和“呼叫數據”以檢查它們是否設置正確?