mcpField
W
wallets

wallets

github.com/austintgriffith其他30237 次安装2 天前更新
代码仓库

关于 wallets

面向 Ethereum 钱包与 Agent 开发的安全和架构参考,介绍 EIP-7702、Safe 多签及密钥保护实践。适合构建链上操作的开发者和 AI Agent,内容侧重权限、交易确认与凭据安全。

主要功能

  1. 01说明 EIP-7702 委托执行能力
  2. 02介绍 Safe 多签配置模式
  3. 03提供密钥和凭据保护规则
  4. 04列出 AI Agent 交易安全护栏
  5. 05展示安全发送交易的检查项

适用场景

  1. 01规划 Agent 的钱包与权限
  2. 02为高价值操作设计多签恢复
  3. 03检查提交前的代码是否含密钥
  4. 04实现需人工确认的链上交易

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/austintgriffith/ethskills --skill wallets

安装后在 Claude Code 中输入/wallets确认已加载。

详细介绍

以太坊上的钱包

你可能搞错的地方

EIP-7702 已上线。 自 Pectra(2025 年 5 月 7 日)起,常规 EOA 无需迁移钱包即可将执行权限委托给智能合约代码。这支持批量处理、Gas 赞助和类似会话密钥的用户体验。这不是“即将推出”,而是已经上线。

账户抽象状态: ERC-4337 正在发展,但仍处于早期阶段(2026 年 2 月)。主要实现包括:Kernel(ZeroDev)、Biconomy、Alchemy Account Kit、Pimlico。EntryPoint v0.7:0x0000000071727De22E5E9d8BAf0edAc6f37da032。

最安全的存储方式: 硬件钱包本身是单点故障。经过审计的多签智能合约(例如 Safe)更加安全。多签不需要多个人参与;一个用户可以控制多个设备上的不同密钥。在 2-of-4 设置中,三个签名者是用户在不同设备上的钱包(例如硬件钱包、手机热钱包、笔记本电脑钱包)。第四个签名者是值得信赖的联系人,用于恢复。攻击者必须攻破多个设备,而不是一个。

EIP-7702:智能 EOA(自 2025 年 5 月起上线)

EOA 可以授权执行智能合约代码中的委托代码。这并不会自动“一劳永逸”——委托可以持续生效,直到被替换或明确清除。

工作原理:

  1. 钱包签署一条消息,指定 EOA 可以使用哪个合约代码。
  2. 一笔特殊的 EIP-7702 交易提交该签名消息。
  3. 随后,EOA 可以运行该合约逻辑(批量处理、赞助、权限控制),就像运行账户逻辑一样。
  4. 这并不会自动“一劳永逸”——委托可以持续生效,直到被替换或明确清除。
  5. 如果交易之后失败,委托更新本身仍可能保留。

支持的功能:

  • 将 10 次代币授权合并为一笔交易
  • 为 EOA 用户提供 Gas 赞助/元交易
  • 具有有限权限的会话密钥
  • 自定义授权逻辑
  • 消除“授权疲劳”(授权 + 执行 → 一步完成)

状态(2026 年 2 月): 已部署至主网。MetaMask、Rainbow 正在添加支持。对于生产环境中的 Agent 来说仍处于早期阶段——在工具链成熟之前,请使用标准 EOA 或 Safe。

Safe(Gnosis Safe)多签

关键地址(v1.4.1,跨链确定性一致)
合约地址
Safe Singleton0x41675C099F32341bf84BFc5382aF534df5C7461a
Safe Proxy Factory0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67
MultiSend0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526

主网、Arbitrum、Base 及所有主流链上的地址都相同。

面向 AI Agent 的 Safe

模式: 2-of-3 Safe

  • 所有者 1:Agent 的钱包(热钱包,自动操作)
  • 所有者 2:人类的热钱包(热钱包,手动操作)
  • 所有者 3:人类的冷钱包(冷钱包,用于恢复)
  • 阈值:2(Agent 可以将交易加入队列,由人类执行;反之亦然)

优势:如果 Agent 密钥遭到泄露,人类可以将其移除。人类始终可以恢复资金。Agent 可以批量处理交易。

🚨 切勿将密钥提交到 Git

这是 AI Agent 资金损失和凭据泄露的头号原因。 机器人会实时抓取 GitHub,并在数秒内利用泄露的密钥——即使是私有仓库中的密钥也不例外,即使立即删除也无济于事。提交到 Git 的密钥将永久泄露。

这种情况在 AI 编码 Agent 中不断发生。 Agent 生成部署脚本,将密钥硬编码进去,运行 git add .,然后在下一个提示词到来前,钱包里的资金就被转走了。或者 Agent 把 Alchemy API 密钥粘贴到 scaffold.config.ts 中,结果该密钥最终出现在公开仓库里。

这适用于所有密钥:

  • 钱包私钥——资金会立即被转走
  • API 密钥——Alchemy、Infura、Etherscan、WalletConnect
  • 嵌入密钥的 RPC URL——https://base-mainnet.g.alchemy.com/v2/YOUR_KEY
  • OAuth 令牌、Bearer 令牌、密码
预防措施
# .gitignore (MUST exist in every project)
.env
.env.*
*.key
*.pem
broadcast/
cache/
# Verify before every commit
git diff --cached --name-only | grep -iE '\.env|key|secret|private'
# If this matches ANYTHING, stop and fix it

# Nuclear option: scan entire repo history
git log --all -p | grep -iE 'private.?key|0x[a-fA-F0-9]{64}'
如果你已经提交了密钥
  1. 假设它已泄露。 不要心存侥幸,以为没人看到。
  2. 立即将所有资金转移到新钱包。
  3. 轮换密钥。 生成一个新密钥。旧密钥已永久作废。
  4. 使用 git filter-repo 或 BFG Repo Cleaner 清理 Git 历史记录——但这只是亡羊补牢,并不能防止问题发生。密钥已经泄露。
AI Agent 的安全模式
# Load key from environment (NEVER hardcode)
cast send ... --private-key $DEPLOYER_PRIVATE_KEY

# Or use encrypted keystore
cast send ... --keystore ~/.foundry/keystores/deployer --password-file .password

# Or use hardware wallet
cast send ... --ledger

经验法则: 如果 grep -r "0x[a-fA-F0-9]{64}" . 能在你的源代码中匹配到任何内容,就说明存在问题。grep -r "g.alchemy.com/v2/[A-Za-z0-9]" 和任何嵌入了 API 密钥的 RPC URL 也是如此。

AI Agent 的关键防护措施

密钥安全规则
  1. 未经明确的人类许可,绝不从任何钱包中提取私钥。
  2. **绝不将私钥存储在:**聊天记录、纯文本文件、共享环境中的环境变量、Git 仓库、未加密的数据库中。
  3. 未经人类确认,绝不转移资金。 展示:金额、目的地(带校验和的地址)、Gas 费用及操作内容。等待明确的“是”。
  4. 除非人类明确选择使用命令行或脚本,否则优先使用钱包原生 UI 进行签名。
  5. 使用资金有限的专用钱包执行 Agent 操作。绝不使用人类的主钱包。
  6. 仔细核对地址。 使用 viem.getAddress() 或等效方法验证校验和。仅一个字符错误,就可能造成永久损失。
  7. 先在测试网上进行测试。 或使用本地 Anvil 分叉。
  8. 设置支出限额。 超过阈值时须获得人类批准。高价值操作使用 Safe 多签。
  9. 记录所有交易(绝不记录密钥)。 保留审计记录。
  10. 假设密钥会泄露。 做好设计,确保 Agent 密钥泄露不会导致全部损失。
存储选项(从最差到最佳)

❌ 将纯文本存放在代码或日志中——绝不允许 ❌ 在共享环境中使用环境变量——绝不允许 ❌ 提交到 Git——绝不允许 ⚠️ 本地 .env 文件——仅用于测试 ✅ 加密密钥库(受密码保护) ✅ 硬件钱包 / Cloud KMS / TEE

安全交易模式
async function sendSafely(publicClient, walletClient, to, value) {
  const checksummedTo = viem.getAddress(to);
  const { account } = walletClient;
  const gas = await publicClient.estimateGas({ account, to: checksummedTo, value });
  const fees = await publicClient.estimateFeesPerGas();
  const totalCostUSD = Number(viem.formatEther(value + gas * fees.maxFeePerGas)) * 2000;

  if (totalCostUSD > 10) {
    // human approval
  }

  const hash = await walletClient.sendTransaction({
    account,
    to: checksummedTo,
    value,
    gas: (gas * 120n) / 100n,
    maxFeePerGas: fees.maxFeePerGas,
    maxPriorityFeePerGas: fees.maxPriorityFeePerGas,
  });
  const receipt = await publicClient.waitForTransactionReceipt({ hash });
  logTransaction({ hash, to: checksummedTo, value, block: receipt.blockNumber });
  return receipt;
}

延伸阅读

评论

支持 Markdown