main.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. from fastapi import FastAPI, WebSocket, WebSocketDisconnect
  2. from fastapi.middleware.cors import CORSMiddleware
  3. from fastapi import Body
  4. from pydantic import BaseModel
  5. from typing import Optional
  6. from dotenv import load_dotenv
  7. import os
  8. import asyncio
  9. from typing import List, Dict, Any
  10. from hyperliquid.info import Info
  11. from hyperliquid.exchange import Exchange
  12. from hyperliquid.utils import constants
  13. from hyperliquid.utils.signing import get_timestamp_ms
  14. from eth_account import Account # 导入 Account 用于 wallet 创建
  15. # 加载 .env 文件
  16. load_dotenv()
  17. # Testnet 配置
  18. TESTNET_API_URL = constants.TESTNET_API_URL
  19. # 初始化全局变量
  20. info = None
  21. exchange = None
  22. app = FastAPI(title="Hyperliquid DEX Backend")
  23. class BuilderApproveRequest(BaseModel):
  24. user_address: str
  25. max_fee_rate: Optional[int] = 50 # 默认 50
  26. # 添加 CORS 中间件(允许前端访问)
  27. app.add_middleware(
  28. CORSMiddleware,
  29. allow_origins=["http://localhost:5174", "http://101.32.51.95:5174"], # 前端 URL,根据需要调整
  30. allow_credentials=True,
  31. allow_methods=["*"],
  32. allow_headers=["*"],
  33. )
  34. # 启动事件:初始化 SDK
  35. @app.on_event("startup")
  36. async def startup_event():
  37. global info, exchange
  38. info = Info(TESTNET_API_URL, skip_ws=False)
  39. # 初始化 Exchange,移除 skip_ws 参数(修正)
  40. private_key = os.getenv("PRIVATE_KEY")
  41. if private_key:
  42. wallet = Account.from_key(private_key) # 创建 LocalAccount
  43. exchange = Exchange(wallet, TESTNET_API_URL, account_address=wallet.address)
  44. print(f"Exchange 初始化成功,地址: {wallet.address}")
  45. else:
  46. raise ValueError("PRIVATE_KEY 未设置,请检查 .env 文件")
  47. # WebSocket 管理器
  48. class ConnectionManager:
  49. def __init__(self):
  50. self.active_connections: List[WebSocket] = []
  51. async def connect(self, websocket: WebSocket):
  52. await websocket.accept()
  53. self.active_connections.append(websocket)
  54. def disconnect(self, websocket: WebSocket):
  55. self.active_connections.remove(websocket)
  56. async def broadcast(self, data: Dict[str, Any]):
  57. for connection in self.active_connections:
  58. await connection.send_json(data)
  59. manager = ConnectionManager()
  60. # 根路径
  61. @app.get("/")
  62. def read_root():
  63. return {"message": "Hyperliquid DEX Backend 就绪!"}
  64. # 健康检查
  65. @app.get("/health")
  66. def health_check():
  67. return {
  68. "status": "OK",
  69. "builder_address": os.getenv("BUILDER_ADDRESS"),
  70. "max_fee_rate": os.getenv("MAX_FEE_RATE")
  71. }
  72. # 查询价格
  73. @app.get("/prices")
  74. def get_prices():
  75. global info
  76. if info:
  77. mids = info.all_mids()
  78. return {"prices": mids}
  79. return {"error": "SDK 未初始化"}
  80. @app.post("/approve-builder")
  81. async def approve_builder(request: BuilderApproveRequest):
  82. try:
  83. user_address = request.user_address
  84. max_fee_rate = request.max_fee_rate
  85. if not user_address:
  86. return {"error": "user_address 必填"}
  87. max_fee_bps = max_fee_rate * 10
  88. result = exchange.approve_builder_fee(os.getenv("BUILDER_ADDRESS"), max_fee_bps)
  89. return {
  90. "status": "批准成功",
  91. "result": result,
  92. "fee_rate": max_fee_rate
  93. }
  94. except Exception as e:
  95. return {"error": str(e)}
  96. @app.post("/approve-builder33")
  97. async def approve_builder(request: BuilderApproveRequest):
  98. """
  99. 用户批准 Builder Fee。
  100. Body 示例: {"user_address": "0xUserAddress"}
  101. """
  102. try:
  103. user_address = request.user_address
  104. max_fee_rate = request.max_fee_rate
  105. if not user_address:
  106. return {"error": "user_address 必填"}
  107. # SDK 内置批准
  108. max_fee_bps = max_fee_rate * 10 # bps
  109. result = exchange.approve_builder_fee(os.getenv("BUILDER_ADDRESS"), max_fee_bps)
  110. return {
  111. "status": "批准成功",
  112. "result": result,
  113. "fee_rate": max_fee_rate,
  114. "builder_address": os.getenv("BUILDER_ADDRESS")
  115. }
  116. except Exception as e:
  117. return {"error": str(e)}
  118. @app.post("/approve-builder22")
  119. async def approve_builder(request: BuilderApproveRequest):
  120. """
  121. 用户批准 Builder Fee。
  122. Body 示例: {"user_address": "0xUserAddress", "max_fee_rate": 50}
  123. """
  124. try:
  125. user_address = request.user_address
  126. max_fee_rate = request.max_fee_rate
  127. if not user_address:
  128. return {"error": "user_address 必填"}
  129. # SDK 内置批准
  130. max_fee_bps = max_fee_rate * 10 # bps
  131. result = exchange.approve_builder_fee(os.getenv("BUILDER_ADDRESS"), max_fee_bps)
  132. return {
  133. "status": "批准成功",
  134. "result": result,
  135. "fee_rate": max_fee_rate,
  136. "builder_address": os.getenv("BUILDER_ADDRESS")
  137. }
  138. except Exception as e:
  139. return {"error": str(e)}
  140. # 批准 Builder Fee API
  141. @app.post("/approve-builder111")
  142. async def approve_builder(request: Dict[str, Any] = Body(...)):
  143. """
  144. 用户批准 Builder Fee:批准你的地址的最大费率。
  145. Body 示例: {"user_address": "0xUserAddress", "max_fee_rate": 50}
  146. """
  147. try:
  148. user_address = request.get("user_address")
  149. max_fee_rate = request.get("max_fee_rate", int(os.getenv("MAX_FEE_RATE", 50)))
  150. if not user_address:
  151. return {"error": "user_address 必填"}
  152. # SDK 内置批准(注入费率,自动签名)
  153. max_fee_bps = max_fee_rate * 10 # 转换为 bps (0.05% = 500 bps)
  154. result = exchange.approve_builder_fee(os.getenv("BUILDER_ADDRESS"), max_fee_bps)
  155. return {
  156. "status": "批准成功",
  157. "result": result,
  158. "fee_rate": max_fee_rate,
  159. "builder_address": os.getenv("BUILDER_ADDRESS")
  160. }
  161. except Exception as e:
  162. return {"error": str(e)}
  163. # 下单 API
  164. @app.post("/place-order")
  165. async def place_order(request: Dict[str, Any] = Body(...)):
  166. """
  167. 下单 API:注入 Builder Fee。
  168. Body 示例: {
  169. "asset": "BTC",
  170. "is_buy": true,
  171. "sz": "0.01",
  172. "limit_px": 60000,
  173. "order_type": "Limit",
  174. "reduce_only": false,
  175. "user_address": "0xUserAddress"
  176. }
  177. """
  178. try:
  179. order_data = request
  180. user_address = order_data.get("user_address")
  181. if not user_address:
  182. return {"error": "user_address 必填"}
  183. # 准备 builder
  184. builder = {
  185. "address": os.getenv("BUILDER_ADDRESS"),
  186. "feeBps": int(os.getenv("MAX_FEE_RATE", 50)) * 10, # bps
  187. }
  188. # SDK 内置下单(自动签名,注入 builder)
  189. result = exchange.order(
  190. asset=order_data["asset"],
  191. is_buy=order_data["is_buy"],
  192. sz=order_data["sz"],
  193. limit_px=order_data["limit_px"],
  194. order_type=order_data.get("order_type", "Limit"),
  195. reduce_only=order_data.get("reduce_only", False),
  196. builder=builder
  197. )
  198. # 广播结果(WebSocket)
  199. await manager.broadcast({"order_result": result})
  200. return {"status": "下单成功", "result": result}
  201. except Exception as e:
  202. return {"error": str(e)}
  203. # 查询 Builder 收益
  204. @app.get("/builder-fees")
  205. def get_builder_fees():
  206. """
  207. 查询你的 Builder 费率和估算收益(简化)
  208. """
  209. try:
  210. # 用 Info 查询用户状态
  211. user_state = info.user_state(os.getenv("BUILDER_ADDRESS"))
  212. return {
  213. "max_fee_rate": os.getenv("MAX_FEE_RATE"),
  214. "user_state": user_state,
  215. "estimated_earnings": "下载 fills CSV 查看实际费 (https://stats-data.hyperliquid-testnet.xyz/Testnet/builder_fills/)"
  216. }
  217. except Exception as e:
  218. return {"error": str(e)}
  219. # WebSocket 端点:实时推送订单簿和价格
  220. @app.websocket("/ws")
  221. async def websocket_endpoint(websocket: WebSocket):
  222. await manager.connect(websocket)
  223. try:
  224. while True:
  225. global info
  226. if info:
  227. data = {
  228. "orderbook": info.l2_book("BTC"), # BTC L2 订单簿
  229. "prices": info.all_mids(),
  230. "timestamp": get_timestamp_ms(),
  231. }
  232. await manager.broadcast(data)
  233. await asyncio.sleep(5) # 每 5 秒推送
  234. except WebSocketDisconnect:
  235. manager.disconnect(websocket)
  236. except Exception as e:
  237. print(f"WebSocket 错误: {e}")
  238. if __name__ == "__main__":
  239. import uvicorn
  240. uvicorn.run(app, host="0.0.0.0", port=8000, reload=True)