skyfffire 4 сар өмнө
parent
commit
c56d68fc4d

+ 12 - 13
checker/config.py.sample

@@ -17,16 +17,15 @@ okchain_api = {
 
 # 交易相關的配置
 arb = {
-  "IN_AMOUNT_TO_QUERY": decimal.Decimal(0),
-  "EXCHANGE_OUT_AMOUNT": decimal.Decimal(0),
-  "PROFIT_LIMIT": 0.01,
-  "IN_TOKEN_ADDRESS": "0xdAC17F958D2ee523a2206206994597C13D831ec7", # 這是ERC20的USDT
-  "IN_TOKEN_DECIMALS": 6,
-  "OUT_TOKEN_ADDRESS": "",
-  "SLIPPAGE": 0.05,                                                 # 0.01代表1%哦
-  "MEXC_TARGET_PAIR_USDT": "_USDT",                                 # 交易所的交易對
-  "CHAIN_ID": 1,                                                    # 一般不會改這個
-  "ARB_EXECUTOR_URL": "http://localhost:188/submit_process",        # arb地址
-  "PORT": 7777,                                                     # checker服務運行端口
-  "STRATEGY": "erc20_to_mexc",                                      # 该配置对应的策略,防止配置写错了
-}
+  "STRATEGY": "erc20_to_mexc",                                          # 该配置对应的策略,防止配置写错了
+  "BASE_TOKEN_TRADE_AMOUNT": decimal.Decimal(0),
+  "COIN_TOKEN_TRADE_AMOUNT": decimal.Decimal(0),
+  "PROFIT_LIMIT": 1,
+  "BASE_TOKEN_ADDRESS": "0xdAC17F958D2ee523a2206206994597C13D831ec7",   # 這是ERC20的USDT
+  "COIN_TOKEN_ADDRESS": "",
+  "SLIPPAGE": 0.05,                                                     # 0.01代表1%哦
+  "CEX_PAIR": "_USDT",                                                  # 交易所的交易對
+  "CHAIN_ID": 1,                                                        # 一般不會改這個
+  "ARB_EXECUTOR_URL": "http://localhost:188/submit_process",            # arb地址
+  "PORT": 7777,                                                         # checker服務運行端口
+}

+ 18 - 13
checker/erc20_to_mexc_checker.py

@@ -13,6 +13,7 @@ from flask import Flask, render_template, jsonify
 from collections import deque
 from plotly.utils import PlotlyJSONEncoder
 
+
 # configs
 from config import wallet
 from config import okchain_api
@@ -22,6 +23,10 @@ from config import arb
 from logger_config import get_logger
 logger = get_logger('as')
 
+# lite客户端
+from web3_py_client_lite import EthClient
+web3_client = EthClient()
+
 # ok web3的配置
 ok_chain_client.api_config = okchain_api # 假设ok_chain_client有此配置方式
 
@@ -30,13 +35,13 @@ ARB_EXECUTOR_URL = arb["ARB_EXECUTOR_URL"]
 
 # --- 配置部分 ---
 # IN_AMOUNT_TO_QUERY 将在循环中动态确定
-EXCHANGE_OUT_AMOUNT = decimal.Decimal(str(arb["EXCHANGE_OUT_AMOUNT"])) # 确保是Decimal
+EXCHANGE_OUT_AMOUNT = decimal.Decimal(str(arb["COIN_TOKEN_TRADE_AMOUNT"])) # 确保是Decimal
 PROFIT_LIMIT = decimal.Decimal(str(arb["PROFIT_LIMIT"])) # 确保是Decimal
-IN_TOKEN_ADDRESS = arb["IN_TOKEN_ADDRESS"]
-IN_TOKEN_DECIMALS = arb["IN_TOKEN_DECIMALS"]
-OUT_TOKEN_ADDRESS = arb["OUT_TOKEN_ADDRESS"]
+IN_TOKEN_ADDRESS = arb["BASE_TOKEN_ADDRESS"]
+IN_TOKEN_DECIMALS = web3_client.get_erc20_decimals(IN_TOKEN_ADDRESS)
+OUT_TOKEN_ADDRESS = arb["COIN_TOKEN_ADDRESS"]
 SLIPPAGE = arb["SLIPPAGE"]
-MEXC_TARGET_PAIR_USDT = arb["MEXC_TARGET_PAIR_USDT"]
+MEXC_TARGET_PAIR_USDT = arb["CEX_PAIR"]
 CHAIN_ID = arb["CHAIN_ID"]
 STRATEGY = arb["STRATEGY"]
 
@@ -89,7 +94,7 @@ def get_chain_price_vs_target_currency(chain_id, in_token_addr, out_token_addr,
         return {"error": f"Okx ({chain_id})请求错误: {e}"}, None
 
 # MEXC 现货 (获取 目标代币/USDT 的 bid 价格)
-# 返回: price_target_per_usdt_bid1 (例如 RATO per USDT)
+# 返回: price_target_per_usdt (例如 RATO per USDT)
 def get_mexc_spot_price_target_usdt_bid(pair_symbol):
     url = "https://api.mexc.com/api/v3/depth"
     params = {'symbol': pair_symbol.replace('_', ''), 'limit': 1000} # 减少limit,5000可能过大且非必要
@@ -126,7 +131,7 @@ def get_mexc_spot_price_target_usdt_bid(pair_symbol):
 
             # trade_value 代表卖出 accumulated_volume 个 TARGET_ASSET 能得到的 USDT 总量
             return {
-                "price_target_per_usdt_bid1": sell_price # 这个名字其实是 RATO/USDT,所以可以叫 price_target_per_base
+                "price_target_per_usdt": sell_price # 这个名字其实是 RATO/USDT,所以可以叫 price_target_per_base
             }, trade_value # 返回的是实际能卖出 EXCHANGE_OUT_AMOUNT (或更少,如果深度不足) 所得的 USDT 总额
         else:
             # logger.warning(f"MEXC现货({pair_symbol}) bids 数据不存在或为空: {data}")
@@ -223,18 +228,18 @@ def update_data_for_plotly_and_table():
         fetch_time_full = time.strftime("%Y-%m-%d %H:%M:%S")
         fetch_time_chart = time.strftime("%H:%M:%S")
 
-        # 1. MEXC: 获取 price_target_per_usdt_bid1 (例如 RATO/USDT) 和相应的 trade_value_usdt
+        # 1. MEXC: 获取 price_target_per_usdt (例如 RATO/USDT) 和相应的 trade_value_usdt
         # trade_value_usdt 是指如果以 EXCHANGE_OUT_AMOUNT 的目标代币在MEXC上砸盘卖出,能获得的USDT估值
         mexc_data, trade_value_usdt = get_mexc_spot_price_target_usdt_bid(MEXC_TARGET_PAIR_USDT)
-        mexc_price_target_per_usdt_bid1 = mexc_data.get("price_target_per_usdt_bid1") # TARGET/USDT
+        mexc_price_target_per_usdt = mexc_data.get("price_target_per_usdt") # TARGET/USDT
         mexc_err = mexc_data.get("error")
 
-        # price_target_per_usdt_bid1: 这是1个目标币能卖多少USDT, 即 USDT/TARGET
+        # price_target_per_usdt: 这是1个目标币能卖多少USDT, 即 USDT/TARGET
         # 所以可以直接用,不需要转换,变量名应为 mexc_price_target_per_base
         mexc_price_target_per_base = None
-        if mexc_price_target_per_usdt_bid1 is not None and mexc_price_target_per_usdt_bid1 > 0:
-            mexc_price_target_per_base = mexc_price_target_per_usdt_bid1 # RATO/USDT => USDT/TARGET (命名约定)
-        elif not mexc_err and mexc_price_target_per_usdt_bid1 is not None:
+        if mexc_price_target_per_usdt is not None and mexc_price_target_per_usdt > 0:
+            mexc_price_target_per_base = mexc_price_target_per_usdt # RATO/USDT => USDT/TARGET (命名约定)
+        elif not mexc_err and mexc_price_target_per_usdt is not None:
              mexc_err = mexc_err or "MEXC价格为0或无效"
         
         if mexc_err or trade_value_usdt == decimal.Decimal('0'): # 如果MEXC有问题或无法确定砸盘价值,则跳过本次循环

+ 9 - 5
checker/mexc_to_erc20_checker.py

@@ -22,6 +22,10 @@ from config import arb
 from logger_config import get_logger
 logger = get_logger('as')
 
+# lite客户端
+from web3_py_client_lite import EthClient
+web3_client = EthClient()
+
 # ok web3的配置
 ok_chain_client.api_config = okchain_api # 假设ok_chain_client有此配置方式
 
@@ -30,14 +34,14 @@ ARB_EXECUTOR_URL = arb["ARB_EXECUTOR_URL"]
 
 # --- 配置部分 ---
 # EXCHANGE_OUT_AMOUNT 将在循环中动态确定
-EXCHANGE_OUT_AMOUNT = decimal.Decimal(str(arb["EXCHANGE_OUT_AMOUNT"])) # 确保是Decimal
+IN_AMOUNT_TO_QUERY = decimal.Decimal(str(arb["COIN_TOKEN_TRADE_AMOUNT"]))
+EXCHANGE_OUT_AMOUNT = decimal.Decimal(str(arb["BASE_TOKEN_TRADE_AMOUNT"])) # 确保是Decimal
 PROFIT_LIMIT = decimal.Decimal(str(arb["PROFIT_LIMIT"])) # 确保是Decimal
-IN_TOKEN_ADDRESS = arb["IN_TOKEN_ADDRESS"]
-IN_TOKEN_DECIMALS = arb["IN_TOKEN_DECIMALS"]
-IN_AMOUNT_TO_QUERY = arb["IN_AMOUNT_TO_QUERY"]
+IN_TOKEN_ADDRESS = arb["COIN_TOKEN_ADDRESS"]
+IN_TOKEN_DECIMALS = web3_client.get_erc20_decimals(IN_TOKEN_ADDRESS)
 OUT_TOKEN_ADDRESS = arb["OUT_TOKEN_ADDRESS"]
 SLIPPAGE = arb["SLIPPAGE"]
-MEXC_TARGET_PAIR_USDT = arb["MEXC_TARGET_PAIR_USDT"]
+MEXC_TARGET_PAIR_USDT = arb["CEX_PAIR"]
 CHAIN_ID = arb["CHAIN_ID"]
 STRATEGY = arb["STRATEGY"]
 

+ 265 - 0
checker/web3_py_client_lite.py

@@ -0,0 +1,265 @@
+import os
+import json
+import logging
+import re
+from decimal import Decimal, ROUND_DOWN
+
+from web3 import Web3
+from web3.middleware import ExtraDataToPOAMiddleware # For PoA networks like Goerli, Sepolia, BSC etc.
+from eth_account import Account
+from dotenv import load_dotenv
+from logger_config import get_logger
+
+
+# 配置日志
+logger = get_logger('as')
+
+# 加载环境变量
+load_dotenv()
+
+# 标准 IERC20 ABI (只包含常用函数)
+IERC20_ABI = json.loads('''
+[
+    {
+        "constant": true,
+        "inputs": [],
+        "name": "name",
+        "outputs": [{"name": "", "type": "string"}],
+        "payable": false,
+        "stateMutability": "view",
+        "type": "function"
+    },
+    {
+        "constant": false,
+        "inputs": [
+            {"name": "_spender", "type": "address"},
+            {"name": "_value", "type": "uint256"}
+        ],
+        "name": "approve",
+        "outputs": [{"name": "", "type": "bool"}],
+        "payable": false,
+        "stateMutability": "nonpayable",
+        "type": "function"
+    },
+    {
+        "constant": true,
+        "inputs": [],
+        "name": "totalSupply",
+        "outputs": [{"name": "", "type": "uint256"}],
+        "payable": false,
+        "stateMutability": "view",
+        "type": "function"
+    },
+    {
+        "constant": false,
+        "inputs": [
+            {"name": "_from", "type": "address"},
+            {"name": "_to", "type": "address"},
+            {"name": "_value", "type": "uint256"}
+        ],
+        "name": "transferFrom",
+        "outputs": [{"name": "", "type": "bool"}],
+        "payable": false,
+        "stateMutability": "nonpayable",
+        "type": "function"
+    },
+    {
+        "constant": true,
+        "inputs": [],
+        "name": "decimals",
+        "outputs": [{"name": "", "type": "uint8"}],
+        "payable": false,
+        "stateMutability": "view",
+        "type": "function"
+    },
+    {
+        "constant": true,
+        "inputs": [{"name": "_owner", "type": "address"}],
+        "name": "balanceOf",
+        "outputs": [{"name": "balance", "type": "uint256"}],
+        "payable": false,
+        "stateMutability": "view",
+        "type": "function"
+    },
+    {
+        "constant": true,
+        "inputs": [],
+        "name": "symbol",
+        "outputs": [{"name": "", "type": "string"}],
+        "payable": false,
+        "stateMutability": "view",
+        "type": "function"
+    },
+    {
+        "constant": false,
+        "inputs": [
+            {"name": "_to", "type": "address"},
+            {"name": "_value", "type": "uint256"}
+        ],
+        "name": "transfer",
+        "outputs": [{"name": "", "type": "bool"}],
+        "payable": false,
+        "stateMutability": "nonpayable",
+        "type": "function"
+    },
+    {
+        "constant": true,
+        "inputs": [
+            {"name": "_owner", "type": "address"},
+            {"name": "_spender", "type": "address"}
+        ],
+        "name": "allowance",
+        "outputs": [{"name": "", "type": "uint256"}],
+        "payable": false,
+        "stateMutability": "view",
+        "type": "function"
+    }
+]
+''')
+
+class EthClient:
+    def __init__(self, rpc_url: str = None):
+        self.rpc_url = rpc_url or os.getenv("RPC_URL")
+
+        if not self.rpc_url:
+            raise ValueError("RPC_URL not provided or found in environment variables.")
+
+        self.w3 = Web3(Web3.HTTPProvider(self.rpc_url))
+
+        # 如果连接的是 PoA 网络 (如 Goerli, Sepolia, BSC, Polygon), 需要注入中间件
+        # 对于主网,不需要此操作。可以根据 chain_id 动态判断,或者让用户明确。
+        # 例如:if self.w3.eth.chain_id in [5, 11155111, 56, 137]: # Goerli, Sepolia, BSC, Polygon
+        self.w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
+
+        if not self.w3.is_connected():
+            raise ConnectionError(f"Failed to connect to Ethereum node at {self.rpc_url}")
+
+        logger.info(f"EthClient initialized. RPC: {self.rpc_url}, Connected: {self.w3.is_connected()}")
+
+    
+
+    def _get_erc20_contract(self, token_address: str):
+        """获取 ERC20 合约实例"""
+        if not self.w3.is_address(token_address):
+            raise ValueError(f"Invalid token address: {token_address}")
+        return self.w3.eth.contract(address=self.w3.to_checksum_address(token_address), abi=IERC20_ABI)
+
+    def get_erc20_decimals(self, token_address: str) -> int:
+        """获取 ERC20 代币的精度"""
+        contract = self._get_erc20_contract(token_address)
+        return contract.functions.decimals().call()
+
+    def _to_token_units(self, token_address: str, amount_readable: float) -> int:
+        """将可读的代币数量转换为最小单位 (例如 1.0 USDT -> 1000000 if decimals is 6)"""
+        decimals = self.get_erc20_decimals(token_address)
+        factor = Decimal(10) ** Decimal(decimals)
+        return int(Decimal(str(amount_readable)) * factor)
+
+    def _from_token_units(self, token_address: str, amount_units: int) -> Decimal:
+        """将最小单位的代币数量转换为可读数量"""
+        decimals = self.get_erc20_decimals(token_address)
+        factor = Decimal(10) ** Decimal(decimals)
+        return (Decimal(amount_units) / factor).quantize(Decimal('0.1') ** decimals, rounding=ROUND_DOWN)
+
+    def get_erc20_balance(self, token_address: str, owner_address: str = None) -> Decimal:
+        """获取指定地址的 ERC20 代币余额 (可读数量)"""
+        target_address = owner_address or self.address
+        if not self.w3.is_address(target_address):
+            raise ValueError(f"Invalid owner address: {target_address}")
+
+        contract = self._get_erc20_contract(token_address)
+        balance_units = contract.functions.balanceOf(self.w3.to_checksum_address(target_address)).call()
+        return self._from_token_units(token_address, balance_units)
+
+    def get_erc20_allowance(self, token_address: str, spender_address: str, owner_address: str = None) -> Decimal:
+        """获取 owner 授权给 spender 的 ERC20 代币数量 (可读数量)"""
+        target_owner = owner_address or self.address
+        if not self.w3.is_address(target_owner):
+            raise ValueError(f"Invalid owner address: {target_owner}")
+        if not self.w3.is_address(spender_address):
+            raise ValueError(f"Invalid spender address: {spender_address}")
+
+        contract = self._get_erc20_contract(token_address)
+        allowance_units = contract.functions.allowance(
+            self.w3.to_checksum_address(target_owner),
+            self.w3.to_checksum_address(spender_address)
+        ).call()
+        return self._from_token_units(token_address, allowance_units)
+
+    def get_erc20_total_supply(self, token_address: str) -> Decimal:
+        """获取 ERC20 代币的总供应量 (可读数量)"""
+        contract = self._get_erc20_contract(token_address)
+        total_supply_units = contract.functions.totalSupply().call()
+        return self._from_token_units(token_address, total_supply_units)
+
+    def get_erc20_name(self, token_address: str) -> str:
+        """获取 ERC20 代币的名称"""
+        contract = self._get_erc20_contract(token_address)
+        return contract.functions.name().call()
+
+    def get_erc20_symbol(self, token_address: str) -> str:
+        """获取 ERC20 代币的符号"""
+        contract = self._get_erc20_contract(token_address)
+        return contract.functions.symbol().call()
+
+    def get_eth_balance(self, address: str = None) -> Decimal:
+        """获取ETH余额 (单位 Ether)"""
+        target_address = address or self.address
+        if not self.w3.is_address(target_address):
+            raise ValueError(f"Invalid address: {target_address}")
+        balance_wei = self.w3.eth.get_balance(self.w3.to_checksum_address(target_address))
+        return self.w3.from_wei(balance_wei, 'ether')
+
+if __name__ == "__main__":
+    import traceback
+    import time
+    from pprint import pprint
+    from decimal import Decimal
+    from config import wallet
+
+    client = EthClient()
+
+    # --- 使用示例 ---
+    # 确保你的 .env 文件配置正确
+    # 并且你的账户中有足够的 ETH 来支付 Gas 费
+
+    # 替换为实际的ERC20代币地址和接收者地址 (例如USDT on Sepolia testnet)
+    # Sepolia USDT: 0xaA8E23Fb1079EA71e0a56F48S1a3ET28wpD1RLf
+    # Sepolia WETH: 0x7b79995e5f793A07Bc00c21412e50Ecn10yt2e_ (错误,应为 0x7b79995e5f793A07Bc00c21412e50Ecn10yt2eH)
+    # 更正: Sepolia WETH: 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14 (常用)
+    # 有些测试网可能没有标准的USDT,你可以找一个存在的ERC20代币进行测试,或者自己部署一个
+    # 为了演示,这里假设使用的是 Sepolia 测试网
+
+    # !!重要!!: 以下地址和代币地址仅为示例, 请替换为您测试网络上的真实地址和代币
+    # 如果您在主网操作,请务必小心,并使用小额资金测试。
+    TEST_RECIPIENT_ADDRESS = "0xb1f33026db86a86372493a3b124d7123e9045bb4" # 替换为你的测试接收地址
+    # Sepolia 上的一个示例 ERC20 token (你可以找一个你有的测试币)
+    # 用于测试的代币地址
+    TEST_ERC20_TOKEN_ADDRESS_SEPOLIA_LINK = "0xdAC17F958D2ee523a2206206994597C13D831ec7"
+
+    try:
+        client = EthClient() # RPC_URL 和 HASH 会从 .env 文件加载
+
+        # --- ERC20 操作示例 ---
+        # 使用 Sepolia LINK 代币进行演示
+        token_address = TEST_ERC20_TOKEN_ADDRESS_SEPOLIA_LINK
+        if not client.w3.is_address(token_address): # 简单检查
+             logger.info(f"Warning: {token_address} does not look like a valid address. Skipping ERC20 tests.")
+        else:
+            logger.info(f"\n--- ERC20 Token Operations for: {token_address} ---")
+            token_name = client.get_erc20_name(token_address)
+            token_symbol = client.get_erc20_symbol(token_address)
+            token_decimals = client.get_erc20_decimals(token_address)
+            logger.info(f"Token: {token_name} ({token_symbol}), Decimals: {token_decimals}")
+
+            # ERC20 总供应量
+            total_supply = client.get_erc20_total_supply(token_address)
+            logger.info(f"Total Supply of {token_symbol}: {total_supply} {token_symbol}")
+    except ValueError as ve:
+        logger.info(f"Configuration Error: {ve}")
+    except ConnectionError as ce:
+        logger.info(f"Connection Error: {ce}")
+    except Exception as e:
+        logger.info(f"An unexpected error occurred: {e}")
+        import traceback
+        traceback.logger.info_exc()

+ 6 - 1
toto.readme

@@ -38,7 +38,12 @@
 [-] 【重要】沒有占用USDT的情況才能提現*STATE判斷*
 
 2025-06-19
-[ ] 另一个方向的监听
+[-] 另一个方向的监听
+
+2025-06-24
+[-] config重新描述一下,乱七八糟的
+[ ] zeus昨晚上有一个状态识别bug,导致单边
+[ ] 测试延迟开单
 [ ] 另一個方向
 
 有時間再做