diff --git a/app/domains/trading/service.py b/app/domains/trading/service.py index b635f92..9bbc2af 100644 --- a/app/domains/trading/service.py +++ b/app/domains/trading/service.py @@ -47,7 +47,7 @@ class TradingService: order_id = str(uuid.uuid4()) fee = amount * Decimal("0.006") # 0.6% protocol fee - # 1. 记账并扣款/冻结 + # 1. 记账并扣款/冻结 (若提供 vault_address 则关联金库) await self.ledger_service.record_trade( address=user_address, market_address=market_address, diff --git a/app/infrastructure/blockchain/relayer.py b/app/infrastructure/blockchain/relayer.py new file mode 100644 index 0000000..9e84569 --- /dev/null +++ b/app/infrastructure/blockchain/relayer.py @@ -0,0 +1,74 @@ +import logging +from decimal import Decimal +from typing import Optional, Dict, Any +from web3 import AsyncWeb3, AsyncHTTPProvider +from eth_account import Account +from app.config import settings + +logger = logging.getLogger(__name__) + +class RelayerService: + """ + 【WTFX Relayer 链上代付撮合服务】 + 代表持有 Session Key 授权的用户,将其交易免 Gas 费提交至链上执行 + """ + + def __init__(self): + self.w3 = AsyncWeb3(AsyncHTTPProvider(settings.ROBINHOOD_TESTNET_RPC)) + # 管理员/Relayer 私钥 + self.relayer_private_key = "0x02aef1247dbddab8e8d6b0962f086f47874626e94988541ca21c9c2ea2475e62" + self.relayer_account = Account.from_key(self.relayer_private_key) + + async def execute_vault_transaction( + self, + vault_address: str, + target_market: str, + call_data: str, + value: int = 0 + ) -> str: + """ + 通过 Relayer 调用用户 Vault 的 execute() 函数 + """ + try: + vault_checksum = self.w3.to_checksum_address(vault_address) + target_checksum = self.w3.to_checksum_address(target_market) + + # 构建 execute(address target, uint256 value, bytes calldata data) ABI 调用 + execute_abi = [{ + "inputs": [ + {"name": "target", "type": "address"}, + {"name": "value", "type": "uint256"}, + {"name": "data", "type": "bytes"} + ], + "name": "execute", + "outputs": [{"name": "", "type": "bytes"}], + "stateMutability": "payable", + "type": "function" + }] + + vault_contract = self.w3.eth.contract(address=vault_checksum, abi=execute_abi) + nonce = await self.w3.eth.get_transaction_count(self.relayer_account.address) + gas_price = await self.w3.eth.gas_price + + data_bytes = bytes.fromhex(call_data[2:]) if call_data.startswith("0x") else bytes.fromhex(call_data) + + tx = await vault_contract.functions.execute( + target_checksum, + value, + data_bytes + ).build_transaction({ + "from": self.relayer_account.address, + "nonce": nonce, + "gasPrice": gas_price, + "chainId": 46630 + }) + + signed_tx = self.relayer_account.sign_transaction(tx) + tx_hash = await self.w3.eth.send_raw_transaction(signed_tx.rawTransaction) + logger.info(f"Relayer tx submitted: {tx_hash.hex()} for Vault {vault_address}") + return tx_hash.hex() + except Exception as e: + logger.error(f"Failed to execute relayer tx: {e}") + raise e + +relayer_service = RelayerService() diff --git a/app/schemas/dtos.py b/app/schemas/dtos.py index f1652d2..525035d 100644 --- a/app/schemas/dtos.py +++ b/app/schemas/dtos.py @@ -52,10 +52,12 @@ class MarketDTO(BaseModel): class CreateOrderRequest(BaseModel): market_address: str + vault_address: Optional[str] = None outcome_index: int = Field(..., ge=0, le=7) side: str = Field(..., pattern="^(BUY|SELL)$") amount: Decimal = Field(..., gt=0) price: Optional[Decimal] = None + session_signature: Optional[str] = None class OrderDTO(BaseModel): order_id: str