以太坊交易服务器

一个提供以太坊主网余额查询、代币价格获取和代币交换模拟功能的JSON-RPC服务器。

  • by JungleLiu-LHJ
  • Rust
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 JungleLiu-LHJ/ETH-trading-MCP

使用说明

WalletMcp——以太坊交易MCP服务器

Stdio JSON-RPC服务器为以太坊主网提供了三种工具:

  • get_balance --ETH或ERC-20余额查询
  • get_token_price --采用Uniswap V3回退的链式首价
  • swap_tokens --构建真正的Uniswap V3呼叫数据并模拟(无广播)

设计决策

编码结构

  • 三层:MCP(标准输入/JSON-RPC)→ 服务(编排)→ 实施(平衡/价格/交换)。这隔离了关注点,使处理程序保持精简,并使核心逻辑可以在没有I/O的情况下进行测试。
  • 共享上下文(ServiceContext)持有 provider,以及一个令牌 registry 后面 Arc/RwLock。这使得在发现新令牌时能够进行安全的并发读取和偶尔的写入。
  • **stdio上的JSON-RPC保持二进制主机无关性和MCP兼容;stdout保留用于协议有效负载,日志通过以下方式发送到stderr**tracing.

核心流程

  • 令牌元数据和注册表
    • 默认元数据保存在(config/token_defaults.json)用于确定性行为和快速启动。
    • 按需发现:如果令牌不在注册表中,但提供了地址,服务器将获取最小的ERC-20元数据并缓存,从而避免对静态配置的硬依赖。
    • 我将注册表和主流程解耦,以方便以后的维护。
  • 定价政策
    • 首先向Chainlink索取价格,以确保完整性和弹性
    • 如果不存在,则回退到Uniswap V3 Quoter
  • 按构造交换模拟
    • 调用Uniswap V3 QuoterV2单跳输出,然后计算滑点。
    • 构建链上精确的SwapRouter调用数据发送,然后使用私钥通过节点(eth_estimateGas+eth_call)进行模拟

设置

  • 依赖项
    • 锈蚀(稳定)和货物
    • HTTPS以太坊RPC端点(例如Alchemy/Infura/Public)
  • 克隆和构建
    • cargo build --release
  • 配置
    • 选项A:环境变量(支持dotenv)
      • ETH_RPC_URL --HTTPS RPC URL(必需)
      • PRIVATE_KEY --十六进制私钥,有或没有 0x (可选;交换模拟需要)
      • DEFAULT_CHAIN_ID --默认为 1 (主网)
    • **选项B:**Config.toml (生产中首选)。例子:
      eth_rpc_url = "https://..."
      private_key = "0xabc..."
      default_chain_id = 1
      
  • 令牌注册表默认值
    • **in**config/token_defaults.json (符号、地址、小数、链式馈送、默认Uniswap费用等级)。

跑

  • 通过stdio启动MCP服务器(从stdin读取JSON-RPC行,将响应写入stdout):
    • cargo run --release
  • 或者运行编译后的二进制文件:
    • target/release/walletmcp
  • **服务器通过以下方式登录到stderr**tracingstdout是为JSON-RPC有效负载保留的。

MCP调用示例(stdio上的JSON-RPC)

  • **获得平衡**
    • 参数

      • address:持有人地址(支票加和或小写) 0x...).
      • token (可选):ERC-20地址或已知符号;省略ETH。
    • 以太坊

      • 请求:
      {"jsonrpc":"2.0","id":"1","method":"get_balance","params":{"address":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}}
      
      • 示例响应:
      {"jsonrpc":"2.0","result":{"decimals":18,"formatted":"3.758181447334319014","raw":"3758181447334319014","symbol":"ETH"},"id":"1"}
      
    • 泰达币

      • 请求:
      {"jsonrpc":"2.0","id":"bal-usdt","method":"get_balance","params":{"address":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","token":"0xdAC17F958D2ee523a2206206994597C13D831ec7"}}
      
      
      • 示例响应:
      {"jsonrpc":"2.0","id":"bal-usdc","method":"get_balance","params":{"address":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","token":"USDC"}}
      
      
  • **获取代币价格**
    • 参数

      • base:令牌到价格(地址或符号)。
      • quote (可选): "USD" 或 "ETH" (默认为 "USD").
    • USDC → USD

      • 请求:
      {"jsonrpc":"2.0","id":"price-1","method":"get_token_price","params":{"base":"USDC"}}
      
      • 响应:
      {"jsonrpc":"2.0","result":{"base":"USDC","decimals":8,"price":"0.99981815","quote":"USD","source":"chainlink"},"id":"price-1"}
      
  • **模拟交换**
    • 参数
      • from_token / to_token:地址或已知符号;符号通过注册表解析。
      • amount_in_wei:在wei中以十进制字符串形式输入金额。
      • slippage_bps (可选):基点公差(默认100=1%)。
      • fee (可选):Uniswap V3收费等级。
      • recipient (可选):输出接收器;默认为签名者地址。
      • sqrt_price_limit (可选):X96价格边界; "0" 或者无限制地省略。
    • 请求:
      {"jsonrpc":"2.0","id":"swap-1","method":"swap_tokens","params":{"from_token":"....","to_token":"0x6B175474E89094C44Da98b954EedeAC495271d0F","amount_in_wei":"10000000000000","slippage_bps":100,"fee":3000,"recipient":"0xYourAddressHere"}}
      
    • 示例响应:
      {"jsonrpc":"2.0","result":{"amount_out_estimate":"0.033810900284009015","amount_out_min":"0.033472791281168924","calldata_hex":"0x414bf389000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000bb80000000000000000000000004fc74ba63ddd9c685f8bb59f25d2c9345d3c72e600000000000000000000000000000000000000000000000000000000690c4e19000000000000000000000000000000000000000000000000000009184e72a0000000000000000000000000000000000000000000000000000076eb5389f4721c0000000000000000000000000000000000000000000000000000000000000000","gas_estimate":"121109","router":"0xe592427a0aece92de3edee1f18e0157c05861564"},"id":"swap-1"}
      

备注

  • 确切数字因市场状况和RPC提供商状态而异。
  • **交换模拟需要**PRIVATE_KEY 被配置为导出发送者并构建真实的事务上下文。

描述

  • get_balance
    • 参数
      • address string--持有者地址(0x +40个十六进制字符)。
      • token string | null--可选ERC-20地址或已知符号(每个 config/token_defaults.json).忽略获取本地ETH余额。
    • **退货**BalanceOut — { symbol, raw, decimals, formatted } 哪里 formatted = raw / 10^decimals.
    • 错误--地址/符号无效,RPC失败。
  • get_token_price
    • 参数
      • base string——令牌地址或符号(注册表已知或可通过链上ERC-20元数据发现)。
      • quote string(可选,默认 "USD")--其中之一 "USD" 或 "ETH".
    • **退货**PriceOut — { base, quote, price, source, decimals } 哪里 source 是 chainlink, chainlink (via USD/ETH),或 uniswap_v3 (fee N).
    • 注——先用链节;使用令牌注册表中的默认费用退回Uniswap V3 Quoter。
    • 错误--不支持的令牌、缺少引号令牌配置、RPC失败。
  • swap_tokens
    • 参数
      • from_token/to_token string——地址或已知符号。
      • amount_in_wei string——wei中输入金额的十进制字符串。
      • slippage_bps 整数(默认值 100)--基点(最大 10000).
      • fee 整数(默认值 3000)--Uniswap V3收费等级(例如500/3000/10000)。
      • recipient string(可选)——接收输出的地址;默认为签名者地址。
      • sqrt_price_limit string(可选,高级)--raw X96 极限;无限制省略。
    • **退货**SwapSimOut — { amount_out_estimate, amount_out_min, gas_estimate, calldata_hex, router }.
    • **要求--**PRIVATE_KEY 必须配置为导出发送器,以进行真实的通话数据和气体估计。
    • 错误-无效的数字输入,滑点>10000,报价返回0,气体估算/eth_call失败,RPC问题。

错误代码

  • -32602 无效参数; -32601 未找到方法; -32603 内部/序列化。
  • -32001 配置; -32002 RPC; -32010 价格; -32020 互换; -32030 钱包; -32040 输入/输出。

已知限制和假设

  • 注册表是静态的,没有更新
  • 仅限单跳Uniswap V3;没有路由搜索或多跳路径。
  • 在流动性不足的情况下,下跌的DEX价格可能会很嘈杂/容易操纵;视为指示性。
  • **有限的Chainlink覆盖范围:仅在**token_defaults.json (除非在代码中扩展)。
  • 真正的执行需要ERC-20的批准和平衡;这个项目只模拟,从不广播。
  • 主网合约地址是Uniswap V3 QuoterV2和SwapRouter的硬编码。

核心功能逻辑(高级)

get_balance(余额查找)

  • 接受地址和可选的令牌标识符(地址或符号)。
  • 如果没有提供令牌,请读取本地ETH余额,并使用18位小数进行格式化。
  • 如果提供了令牌,请通过内存中的注册表解析符号(或使用给定的地址),根据需要获取最小的令牌元数据,读取持有者的令牌余额,并使用该令牌的小数进行格式化。
  • 返回符号、原始金额、小数和人类可读的格式化字符串。无效输入或网络问题表现为错误。

get_token_price(定价)

  • 解析基础资产(地址或符号)。如果还不知道,则发现链上的最小元数据并将其添加到注册表缓存中;如果仍然无法识别,则返回“不支持的令牌”。
  • 应用链式优先策略:
    • 如果存在直接的基础/引用oracle提要,请使用它。
    • 如果没有,请尝试使用众所周知的提要(base/USD+WETH/USD或base/ETH+WETH/USD)通过美元或以太坊进行枢轴转换。
  • 如果没有可用的oracle路径,则使用默认费用级别和代表性报价代币(USDC代表美元,WETH代表ETH)退回单跳DEX现货报价。
  • 返回带有源标签的十进制字符串价格。如果缺少所需的引号令牌配置,则返回错误。

swap_tomates(模拟,无广播)

  • 解析输入和输出资产,并要求签名者构建一个真实的事务上下文(发送方和执行域)。
  • 验证金额和滑点;未提供时,默认收件人为签名者。
  • 获取所选费用级别的只读输出估计值。
  • 从滑动容差中得出可接受的最小输出,并将真实的路由器呼叫数据与截止日期和收件人组装在一起。
  • 估算天然气并模拟交易;使用输出标记的小数格式化输出。
  • 返回估计输出、最小输出、气体估计、调用数据和路由器地址。真正的执行需要备抵和足够的余额;此工具仅模拟。

网络呼叫和数据源

  • get_balance
    • 仅限以太坊RPC。
    • **ETH:**eth_getBalance(address, latest).
    • **ERC-20:**eth_call 代币合约 balanceOf(address),加上可选的元数据读取 decimals() 和 symbol() 用于格式化。
  • get_token_price
    • 以太坊RPC+链上数据源。
    • **链环(首选):**eth_call AggregatorV3合同 decimals() 和 latestRoundData() 在主网馈送上(例如,WETH/USD、USDC/USD)。
    • 数据透视:当缺少直接数据源时,将两个链式数据源(base/USD与WETH/USD,或base/ETH与WETH/USD)组合在一起。
    • **Uniswap V3回退:**eth_call 到QuoterV2 0x61fFE014bA17989E743c5F6cB21bF9697530B21e 使用 quoteExactInputSingle(...) 获取单跳现货报价。
    • **注册表确保步骤(根据需要):**eth_call 代币合约 decimals()/symbol() 当首次看到令牌时。
  • swap_takes(模拟)
    • 以太坊RPC+Uniswap V3合约。
    • **报价:**eth_call 使用Uniswap QuoterV2进行单跳输出估计。
    • **呼叫数据:构建Uniswap V3 SwapRouter**exactInputSingle(...) 交易目标 0xE592427A0AEce92De3Edee1F18E0157C05861564.
    • **模拟:**eth_estimateGas 对于路由器事务,则 eth_call 对其进行干燥处理;不 eth_sendRawTransaction (从不广播)。
    • **元数据:如果需要**eth_call 对小数合约进行标记,以格式化输出金额。

测试

汇款:一些单元测试需要完整的配置(ETH_RPC_URL、PRIVATE_KEY)!!!

  • **运行快速单元测试:**cargo test
  • **标记实时网络测试**#[ignore] 并需要env变量(请参见 tests/ 和评论)。如果您有真实的RPC和资金密钥,请手动启用它们。

安全说明

  • 永远不要提交真正的私钥。在本地使用环境变量,在生产中使用安全的秘密管理器。
  • **服务器从不广播交易;模拟用途**eth_estimateGas 和 eth_call 只有。