以太坊钱包与智能合约服务器

一个基于MCP协议的以太坊钱包管理和智能合约交互服务器,提供完整的钱包管理、ERC20代币操作和Uniswap V2交互功能。

  • by shippomx
  • Rust
  • 本地部署
  • 更新于
在 GitHub 查看 shippomx/mcp-demo

使用说明

Ethereum MCP Server

一个基于 MCP (Model Context Protocol) 协议的以太坊钱包管理和智能合约交互服务器,使用 Rust 开发,提供完整的钱包管理、ERC20 代币操作和 Uniswap V2 交互功能。

📋 目录

✨ 功能特性

钱包管理

  • ✅ 生成新钱包(随机私钥)
  • ✅ 从私钥导入钱包
  • ✅ 从助记词导入钱包(支持自定义派生路径)
  • ✅ 钱包信息存储和检索
  • ✅ 消息签名功能
  • ✅ 多钱包管理

ERC20 代币操作

  • ✅ 部署 ERC20 代币合约
  • ✅ 转账 ERC20 代币
  • ✅ 查询 ERC20 代币余额

Uniswap V2 交互

  • ✅ 查询交易对信息(储备量、价格)
  • ✅ 添加流动性(支持滑点设置)
  • ✅ 代币兑换(支持滑点设置)
  • ✅ 自动计算最优兑换路径

🚀 快速开始

环境要求

  • Rust 1.70+
  • 以太坊节点(如 Anvil、Hardhat 或远程 RPC)
  • Foundry(用于运行 Anvil)

安装

# 克隆仓库
git clone <repository-url>
cd mcp-demo

# 编译项目
cargo build --release

运行

1. 启动本地测试节点(Anvil)

# 使用 Foundry 的 Anvil 启动本地测试网络
anvil

# 或者 fork 主网(测试 Uniswap 功能必需)
anvil --fork-url https://eth-mainnet.alchemyapi.io/v2/YOUR_API_KEY

# 或者使用提供的脚本
./scripts/start_anvil_fork_simple.sh

注意: Uniswap V2 功能需要 fork 主网环境,因为 Uniswap 合约只存在于主网。

2. 启动 MCP 服务器

# 基础运行(仅钱包管理功能)
cargo run

# 启用 ERC20 和 Uniswap 功能(需要提供私钥)
cargo run -- --private-key "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"

# 自定义端口和 RPC
cargo run -- \
  --mcp-port 8081 \
  --rpc-url "http://127.0.0.1:8545" \
  --private-key "YOUR_PRIVATE_KEY" \
  --log-level info

3. 测试 MCP 服务器

# 使用提供的测试脚本
./scripts/test_wallet.sh
./scripts/test_uniswapv2_mcp.sh

# 或使用 Python 测试客户端(更可靠)
python3 scripts/mcp_test_client.py

# 运行 Rust 测试示例
cargo run --example test_uniswap_v2

# 运行完整测试套件
./scripts/test_mcp_all.sh

🔧 MCP 工具列表

MCP 服务器提供以下 13 个工具,可通过 MCP 协议调用:

钱包管理工具

1. create_wallet

创建新的以太坊钱包。

参数:

{
  "wallet_type": "generate | private_key | mnemonic",
  "name": "钱包名称(可选)",
  "private_key": "私钥(private_key 类型时必需)",
  "mnemonic": "助记词(mnemonic 类型时必需)",
  "passphrase": "助记词密码(可选)",
  "derivation_path": "派生路径(可选)",
  "index": 0  // 钱包索引(可选,默认0)
}

返回:

{
  "address": "0x...",
  "wallet_type": "PrivateKey | Mnemonic",
  "name": "钱包名称",
  "created_at": "2024-01-01T00:00:00Z"
}

2. list_wallets

列出所有已保存的钱包。

参数: 无

返回:

[
  {
    "address": "0x...",
    "wallet_type": "PrivateKey",
    "name": "wallet1",
    "created_at": "2024-01-01T00:00:00Z"
  }
]

3. get_wallet

获取指定钱包的详细信息。

参数:

{
  "address": "0x..."
}

返回:

{
  "address": "0x...",
  "wallet_type": "PrivateKey",
  "name": "wallet1",
  "created_at": "2024-01-01T00:00:00Z"
}

4. delete_wallet

删除指定的钱包。

参数:

{
  "address": "0x..."
}

返回:

"Wallet deleted successfully"

5. sign_message

使用钱包签名消息。

参数:

{
  "address": "0x...",
  "message": "要签名的消息"
}

返回:

{
  "signature": "0x...",
  "address": "0x...",
  "message": "要签名的消息"
}

ERC20 代币工具

6. deploy_erc20

部署新的 ERC20 代币合约。

参数:

{
  "name": "代币名称",
  "symbol": "代币符号",
  "initial_supply": "1000000000000000000000"  // 支持大整数(U256)
}

返回:

{
  "contract_address": "0x...",
  "name": "代币名称",
  "symbol": "代币符号",
  "initial_supply": "1000000000000000000000"
}

7. transfer_erc20

转账 ERC20 代币。

参数:

{
  "contract_address": "0x...",
  "to": "0x...",
  "amount": "1000000000000000000"  // 原始值,包含精度
}

返回:

{
  "transaction_hash": "0x...",
  "contract_address": "0x...",
  "to": "0x...",
  "amount": "1000000000000000000"
}

8. balance_erc20

查询 ERC20 代币余额。

参数:

{
  "contract_address": "0x...",
  "address": "0x..."
}

返回:

{
  "balance": "1000000000000000000000",
  "contract_address": "0x...",
  "address": "0x..."
}

Uniswap V2 工具

9. get_uniswap_pair_info

获取 Uniswap V2 交易对的储备量和价格信息。

参数:

{
  "token_a": "0x...",
  "token_b": "0x...",
  "decimals_a": 18,  // 可选,默认18
  "decimals_b": 18   // 可选,默认18
}

返回:

{
  "pair_address": "0x...",
  "token_a": "0x...",
  "token_b": "0x...",
  "reserves": {
    "token_a": {
      "raw": "1000000000000000000000",
      "readable": "1000",
      "decimals": 18
    },
    "token_b": {
      "raw": "2000000000000000000000",
      "readable": "2000",
      "decimals": 18
    }
  },
  "prices": {
    "token_a_to_token_b": "2.0000",
    "token_b_to_token_a": "0.5000"
  }
}

10. uniswap_add_liquidity

向 Uniswap V2 交易对添加流动性。

参数:

{
  "token_a": "0x...",
  "token_b": "0x...",
  "amount_a": "10000000000000000000",  // 原始值,包含精度
  "amount_b": "20000000000000000000",  // 原始值,包含精度
  "slippage_percent": 1  // 可选,默认1%
}

返回:

{
  "transaction_hash": "0x...",
  "token_a": "0x...",
  "token_b": "0x...",
  "amount_a": "10000000000000000000",
  "amount_b": "20000000000000000000",
  "slippage_percent": 1,
  "status": "success",
  "pair_info": {
    "pair_address": "0x...",
    "reserve_a": "10000000000000000000",
    "reserve_b": "20000000000000000000"
  }
}

11. uniswap_swap_tokens

在 Uniswap V2 交易对中兑换代币。

参数:

{
  "token_in": "0x...",
  "token_out": "0x...",
  "amount_in": "1000000000000000000",  // 原始值,包含精度
  "slippage_percent": 5  // 可选,默认5%
}

返回:

{
  "transaction_hash": "0x...",
  "token_in": "0x...",
  "token_out": "0x...",
  "amount_in": "1000000000000000000",
  "slippage_percent": 5,
  "status": "success",
  "pair_info": {
    "pair_address": "0x...",
    "reserve_in": "11000000000000000000",
    "reserve_out": "18187000000000000000"
  }
}

余额和价格查询工具

12. get_balance

查询 ETH 或 ERC20 代币余额。

参数:

{
  "address": "0x...",  // 钱包地址
  "token_address": "0x..."  // 可选,代币合约地址(不提供则查询 ETH 余额)
}

返回(查询 ETH):

{
  "balance": "1000000000000000000",
  "balance_formatted": "1",
  "address": "0x...",
  "decimals": 18,
  "symbol": "ETH"
}

返回(查询 ERC20):

{
  "balance": "1000000000000000000000",
  "balance_formatted": "1000",
  "address": "0x...",
  "token_address": "0x...",
  "decimals": 18,
  "symbol": "ERC20"
}

13. get_token_price

从 Uniswap V2 获取代币价格。

参数:

{
  "token_address": "0x...",  // 代币地址
  "quote_token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",  // 可选,报价代币地址(默认 WETH)
  "decimals_token": 18,  // 可选,代币精度(默认 18)
  "decimals_quote": 18   // 可选,报价代币精度(默认 18)
}

返回:

{
  "token_address": "0x...",
  "quote_token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
  "price": "2000000000000000000",
  "price_formatted": "2",
  "pair_address": "0x...",
  "liquidity": {
    "reserve_token": "1000000000000000000000",
    "reserve_quote": "2000000000000000000000",
    "reserve_token_formatted": "1000",
    "reserve_quote_formatted": "2000"
  }
}

📝 使用示例

示例 1: 创建钱包并签名消息

# 1. 创建一个新钱包
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"create_wallet","arguments":{"wallet_type":"generate","name":"my_wallet"}}}' | nc localhost 8081

# 2. 使用钱包签名消息
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"sign_message","arguments":{"address":"0x...","message":"Hello, Ethereum!"}}}' | nc localhost 8081

示例 2: 部署 ERC20 并转账

# 1. 部署 ERC20 代币(1000 tokens,18位小数)
echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"deploy_erc20","arguments":{"name":"MyToken","symbol":"MTK","initial_supply":"1000000000000000000000"}}}' | nc localhost 8081

# 2. 查询余额
echo '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"balance_erc20","arguments":{"contract_address":"0x...","address":"0x..."}}}' | nc localhost 8081

# 3. 转账代币(1 token)
echo '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"transfer_erc20","arguments":{"contract_address":"0x...","to":"0x...","amount":"1000000000000000000"}}}' | nc localhost 8081

示例 3: Uniswap V2 完整流程

# 1. 查询交易对信息
echo '{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"get_uniswap_pair_info","arguments":{"token_a":"0x...","token_b":"0x..."}}}' | nc localhost 8081

# 2. 添加流动性(10 TokenA + 20 TokenB)
echo '{"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"name":"uniswap_add_liquidity","arguments":{"token_a":"0x...","token_b":"0x...","amount_a":"10000000000000000000","amount_b":"20000000000000000000","slippage_percent":1}}}' | nc localhost 8081

# 3. 兑换代币(1 TokenA -> TokenB)
echo '{"jsonrpc":"2.0","id":8,"method":"tools/call","params":{"name":"uniswap_swap_tokens","arguments":{"token_in":"0x...","token_out":"0x...","amount_in":"1000000000000000000","slippage_percent":5}}}' | nc localhost 8081

使用 Rust 测试示例

项目包含完整的 Rust 测试示例,演示了完整的 Uniswap V2 交互流程:

# 测试 Uniswap V2 交互(部署代币、创建池子、添加流动性、兑换)
cargo run --example test_uniswap_v2

# 或使用测试脚本
./scripts/test_uniswap_rust.sh

使用 Python 测试客户端

Python 客户端提供了更可靠的 MCP 协议通信:

# 运行 Python 测试客户端
python3 scripts/mcp_test_client.py

示例 4: 查询余额和价格

# 1. 查询 ETH 余额
echo '{"jsonrpc":"2.0","id":9,"method":"tools/call","params":{"name":"get_balance","arguments":{"address":"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"}}}' | nc localhost 8081

# 2. 查询 ERC20 代币余额
echo '{"jsonrpc":"2.0","id":10,"method":"tools/call","params":{"name":"get_balance","arguments":{"address":"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266","token_address":"0x..."}}}' | nc localhost 8081

# 3. 查询代币价格(相对于 WETH)
echo '{"jsonrpc":"2.0","id":11,"method":"tools/call","params":{"name":"get_token_price","arguments":{"token_address":"0x..."}}}' | nc localhost 8081

# 4. 查询代币价格(自定义报价代币)
echo '{"jsonrpc":"2.0","id":12,"method":"tools/call","params":{"name":"get_token_price","arguments":{"token_address":"0x...","quote_token":"0x..."}}}' | nc localhost 8081

⚙️ 配置说明

命令行参数

--wallet-path <PATH>     钱包存储路径(默认: ./wallets)
--mcp-port <PORT>        MCP 服务器端口(默认: 8081)
--log-level <LEVEL>      日志级别(默认: info)
--rpc-url <URL>          以太坊 RPC URL(默认: http://127.0.0.1:8545)
--private-key <KEY>      私钥(用于 ERC20 和 Uniswap 操作)
--test-deploy            测试模式:直接部署 ERC20 合约

环境变量

# 设置 RPC URL
export RPC_URL="http://127.0.0.1:8545"

# 设置私钥
export PRIVATE_KEY="0x..."

# 设置日志级别
export RUST_LOG="ethereum_mcp_server=debug"

🛠️ 开发指南

项目结构

mcp-demo/
├── src/
│   ├── main.rs              # 主程序入口
│   ├── lib.rs               # 库入口
│   ├── basic_wallet.rs      # 钱包管理模块
│   ├── mcp_server.rs        # MCP 服务器实现(11个工具)
│   ├── erc20.rs             # ERC20 代币交互
│   ├── uniswapv2.rs         # Uniswap V2 交互
│   └── wallets/             # Foundry 钱包集成
├── examples/
│   └── test_uniswap_v2.rs   # Uniswap V2 测试示例
├── scripts/
│   ├── start_anvil_fork_simple.sh  # 启动 Anvil fork 节点
│   ├── test_wallet.sh              # 钱包测试脚本
│   ├── test_uniswapv2_mcp.sh       # Uniswap MCP 测试(Shell)
│   ├── mcp_test_client.py          # MCP 测试客户端(Python)
│   ├── simple_deploy_test.sh       # 简单部署测试
│   └── test_mcp_all.sh             # 完整测试套件
├── abis/                    # 智能合约 ABI 文件
│   ├── SimpleERC20.json
│   ├── UniswapV2Factory.json
│   └── UniswapV2Router02.json
├── wallets/                 # 钱包存储目录
├── README.md                # 项目文档
└── TESTING.md               # 测试指南

添加新的 MCP 工具

  1. 在 src/mcp_server.rs 的 get_tools() 方法中添加工具定义
  2. 在 execute_tool() 方法中实现工具逻辑
  3. 更新 README.md 文档
  4. 添加测试用例

示例:

// 1. 在 get_tools() 中添加工具定义
McpTool {
    name: "your_tool_name".to_string(),
    description: "工具描述".to_string(),
    input_schema: serde_json::json!({
        "type": "object",
        "properties": {
            "param": {
                "type": "string",
                "description": "参数描述"
            }
        },
        "required": ["param"]
    }),
}

// 2. 在 execute_tool() 中添加处理逻辑
"your_tool_name" => {
    let param = arguments.get("param")
        .and_then(|v| v.as_str())
        .ok_or("Missing param")?;
    
    // 实现工具逻辑
    let result = your_function(param).await?;
    
    Ok(serde_json::to_string_pretty(&result)?)
}

运行测试

# 运行所有单元测试
cargo test

# 运行特定测试
cargo test test_create_wallet_tool

# 运行集成测试
cargo run --example test_uniswap_v2

# 运行完整测试套件
./scripts/test_mcp_all.sh

构建发布版本

# 构建优化版本
cargo build --release

# 运行发布版本
./target/release/ethereum-mcp-server --help

🧪 测试指南

详细的测试指南请参考 TESTING.md 文档。

快速测试

# 1. 启动 Anvil fork 节点
./scripts/start_anvil_fork_simple.sh

# 2. 启动 MCP 服务器
cargo run -- --private-key "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"

# 3. 运行测试
./scripts/test_mcp_all.sh

测试环境说明

  • 本地 Anvil 节点: 适合测试钱包管理和 ERC20 功能
  • Anvil Fork 主网: 必需用于测试 Uniswap V2 功能(因为 Uniswap 合约只存在于主网)

📚 技术栈

  • Rust 1.70+ - 系统编程语言
  • Tokio - 异步运行时
  • Alloy - 以太坊库(合约交互、签名等)
  • Serde - 序列化/反序列化
  • Foundry - 以太坊开发工具链
  • MCP 协议 - JSON-RPC 2.0 based protocol

🎯 设计决策

为什么选择 Alloy 而不是 ethers-rs

  1. 现代化架构: Alloy 是 Paradigm 团队开发的新一代以太坊库,采用更现代的设计理念
  2. 更好的类型系统: 提供更严格的类型检查,减少运行时错误
  3. 性能优化: 在编译时进行更多优化,运行时性能更好
  4. 异步优先: 从设计之初就考虑异步场景,与 Tokio 集成更自然
  5. 社区支持: 得到以太坊社区的广泛支持,是 ethers-rs 的继任者

大整数(U256)处理

  1. 精度保证: 使用 Alloy 的 U256 类型处理所有金额,避免浮点数精度损失
  2. 字符串传递: MCP 工具的输入输出使用字符串格式传递大整数,避免 JSON Number 类型的限制
  3. 计算安全: 使用 saturating_mul、saturating_add 等方法防止溢出
  4. 格式化输出: 提供 balance_formatted 等人类可读字段,便于用户理解

代币价格计算方法

基于 Uniswap V2 的恒定乘积做市商(CPMM)模型:

  1. 价格公式: price = reserve_quote / reserve_token
  2. 精度处理: 为保持精度,先乘以 10^18 再除法计算
  3. 流动性信息: 返回交易对储备量,便于用户评估价格可靠性
  4. 限制: 仅支持直接交易对,不支持多跳路径的价格聚合

交易执行策略

  1. 实际交易: 所有交易操作直接在链上执行,确保结果真实可靠
  2. 滑点保护: 支持用户自定义滑点容忍度(默认 1-5%)
  3. 授权管理: 自动处理 ERC20 代币授权(approve),简化用户操作
  4. 确认等待: 交易发送后等待确认,并返回最新的链上状态
  5. 错误重试: 网络请求失败时自动重试(最多 3 次),提高稳定性

MCP 协议实现

  1. JSON-RPC 2.0: 遵循标准协议规范,确保兼容性
  2. 工具化设计: 每个功能封装为独立的 MCP 工具,便于 AI 理解和调用
  3. 详细文档: 每个工具提供完整的参数和返回值说明
  4. 错误处理: 统一的错误格式,便于调试和问题追踪

🔒 安全注意事项

  1. 私钥安全:

    • ⚠️ 生产环境中永远不要在命令行或环境变量中明文传递私钥
    • 建议使用硬件钱包或 KMS(Key Management Service)
    • 钱包文件使用 JSON 加密存储
    • 测试私钥仅用于本地开发,切勿在主网使用
  2. RPC 安全:

    • 不要将私钥发送给不信任的 RPC 节点
    • 使用 HTTPS 连接远程 RPC 节点
    • 建议使用 Infura、Alchemy 等知名 RPC 提供商
  3. 网络安全:

    • MCP 服务器默认绑定到 0.0.0.0,建议在生产环境使用防火墙限制访问
    • 考虑添加身份验证和授权机制
    • 使用 TLS/SSL 加密网络通信
  4. 金额处理:

    • 所有金额使用 U256 类型,避免溢出
    • 转账前检查余额和授权额度
    • 使用合理的滑点设置(1-5%)防止价格冲击

📊 性能指标

基于本地测试环境(Anvil fork 主网):

  • 钱包创建: < 10ms
  • ERC20 部署: ~2s(包含交易确认)
  • ERC20 转账: ~1s
  • 添加流动性: ~3s(包含两次 approve + addLiquidity)
  • 代币兑换: ~2s(包含 approve + swap)
  • 查询余额: < 100ms
  • 查询交易对信息: < 500ms

🐛 已知问题和限制

  1. Uniswap V2 依赖主网合约

    • Uniswap Factory 和 Router 只存在于主网
    • 测试必须使用 anvil --fork-url fork 主网
    • 无法在纯本地环境测试 Uniswap 功能
  2. 网络稳定性

    • Fork 模式下与主网 RPC 通信可能不稳定
    • 已添加超时和重试机制
    • 建议使用可靠的 RPC 提供商(Infura、Alchemy)
  3. 大整数支持

    • 所有金额必须使用字符串格式传递
    • 支持最大值: U256::MAX (2^256 - 1)
    • JavaScript Number 类型不适合大整数,需使用 BigInt

📄 许可证

MIT License

🤝 贡献

欢迎提交 Issue 和 Pull Request!

贡献指南

  1. Fork 本仓库
  2. 创建功能分支 (git checkout -b feature/AmazingFeature)
  3. 提交更改 (git commit -m 'Add some AmazingFeature')
  4. 推送到分支 (git push origin feature/AmazingFeature)
  5. 开启 Pull Request

📧 联系方式

如有问题或建议,请通过以下方式联系:

  • GitHub Issues: /issues
  • Email: your-email@example.com

注意: 本项目仅用于开发和测试目的。在生产环境使用前,请确保进行充分的安全审计和测试。

测试私钥警告: README 中提到的私钥(0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80)是 Anvil 的默认测试私钥,仅用于本地测试,切勿在主网使用!