Python

LimitOrders 0x v4 伺服器錯誤 500

  • March 31, 2022

我正在嘗試使用 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”屬性時似乎出錯。您可以記錄“數據”和“呼叫數據”以檢查它們是否設置正確?

引用自:https://ethereum.stackexchange.com/questions/124846