Solidity 代币和金融工具 2|多签钱包(Multi Sig Wallet)

📅 发布时间:2026/7/12 21:31:00 👁️ 浏览次数:
Solidity 代币和金融工具 2|多签钱包(Multi Sig Wallet)
多签钱包Multi-Signature Wallet是一种需要多个私钥持有者共同批准才能执行交易的智能合约。它广泛用于提高资金安全性避免单点故障如单个私钥泄露。在 Solidity 中实现多签钱包核心思路是预设一组所有者owners和一个阈值threshold只有当足够多的所有者确认一笔交易后该交易才能被执行。1. 基本原理所有者Owners有权批准交易的地址列表通常不可更改或通过多签本身修改。阈值Threshold执行交易所需的最少确认数通常≤ 所有者数量。交易Transaction:包括i目标地址、发送的以太币数量、调用数据data以及一个唯一标识如交易ID或nonce。确认Confirmations所有者调用合约方法对某笔交易表示同意。当确认数达到阈值时任何人均可触发执行。2. 核心功能与实现要点2.1 数据结构transactions数组存储所有待处理或已执行的交易包含交易目标地址、发送金额、交易数据和执行状态。isConfirmed记录每个地址对每笔交易的确认状态记录每个地址对每笔交易的确认状态。numConfirmations可动态计数也可通过遍历计算但遍历消耗gas。address[] public owners; mapping(address bool) public isOwner; uint public threshold; struct Transaction { address to; uint value; bytes data; bool executed; uint numConfirmations; } mapping(uint mapping(address bool)) public isConfirmed; Transaction[] public transactions;2.2 提交交易通常只允许所有者提交交易但也可开放给任何人需防止垃圾交易。function submitTransaction(address _to, uint _value, bytes memory _data) public onlyOwner returns (uint txIndex) { txIndex transactions.length; transactions.push(Transaction({ to: _to, value: _value, data: _data, executed: false, numConfirmations: 0 })); emit SubmitTransaction(msg.sender, txIndex, _to, _value, _data); }2.3 确认交易function confirmTransaction(uint _txIndex) public onlyOwner { require(_txIndex transactions.length, tx does not exist); Transaction storage txn transactions[_txIndex]; require(!txn.executed, tx already executed); require(!isConfirmed[_txIndex][msg.sender], tx already confirmed); isConfirmed[_txIndex][msg.sender] true; txn.numConfirmations 1; emit ConfirmTransaction(msg.sender, _txIndex); }2.4 执行交易调用call可同时发送 ETH 并调用目标合约函数。注意重入攻击在状态更改后再进行外部调用并使用executed标志防止重复执行。function executeTransaction(uint _txIndex) public { require(_txIndex transactions.length, tx does not exist); Transaction storage txn transactions[_txIndex]; require(!txn.executed, tx already executed); require(txn.numConfirmations threshold, not enough confirmations); txn.executed true; (bool success, ) txn.to.call{value: txn.value}(txn.data); require(success, tx failed); emit ExecuteTransaction(msg.sender, _txIndex); }2.5 撤销确认可选function revokeConfirmation(uint _txIndex) public onlyOwner { require(_txIndex transactions.length, tx does not exist); Transaction storage txn transactions[_txIndex]; require(!txn.executed, tx already executed); require(isConfirmed[_txIndex][msg.sender], tx not confirmed); isConfirmed[_txIndex][msg.sender] false; txn.numConfirmations - 1; emit RevokeConfirmation(msg.sender, _txIndex); }2.6 修改所有者与阈值高级可以通过多签本身来实现即提交一笔调用addOwner、removeOwner或changeThreshold的交易由现有所有者批准后执行。3. 安全注意事项重入攻击执行交易时使用“检查-生效-交互”模式先标记executed true再调用外部地址。Gas 限制交易数组可能很大避免遍历所有确认可使用计数器优化。所有权管理初始所有者需在构造函数中设定后续修改需谨慎。拒绝服务恶意所有者可无限提交垃圾交易但可通过限制提交权限或收取费用缓解。使用事件所有关键操作提交、确认、执行、撤销都应记录事件便于链下监听。4. 简化示例代码以下是一个极简多签钱包的核心合约省略了事件、修饰器等// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract MultiSigWallet { address[] public owners; mapping(address bool) public isOwner; uint public threshold; struct Transaction { address to; uint value; bytes data; bool executed; uint confirmCount; } Transaction[] public transactions; mapping(uint mapping(address bool)) public confirmed; modifier onlyOwner() { require(isOwner[msg.sender], not owner); _; } constructor(address[] memory _owners, uint _threshold) { require(_owners.length 0, owners required); require(_threshold 0 _threshold _owners.length, invalid threshold); for (uint i 0; i _owners.length; i) { address owner _owners[i]; require(owner ! address(0) !isOwner[owner], invalid owner); isOwner[owner] true; owners.push(owner); } threshold _threshold; } function submitTransaction(address _to, uint _value, bytes memory _data) public onlyOwner returns (uint txIndex) { txIndex transactions.length; transactions.push(Transaction({ to: _to, value: _value, data: _data, executed: false, confirmCount: 0 })); } function confirmTransaction(uint _txIndex) public onlyOwner { require(_txIndex transactions.length, invalid tx); Transaction storage txn transactions[_txIndex]; require(!txn.executed, executed); require(!confirmed[_txIndex][msg.sender], already confirmed); confirmed[_txIndex][msg.sender] true; txn.confirmCount 1; } function executeTransaction(uint _txIndex) public { require(_txIndex transactions.length, invalid tx); Transaction storage txn transactions[_txIndex]; require(!txn.executed, executed); require(txn.confirmCount threshold, not enough confirmations); txn.executed true; (bool success, ) txn.to.call{value: txn.value}(txn.data); require(success, tx failed); } // 接收 ETH 的函数 receive() external payable {} }5. 现有成熟方案Gnosis Safe目前最流行的多签钱包支持模块化、多种链、合约交互并有完善的UI。其合约经过严格审计推荐直接使用。其他实现如 OpenZeppelin 也提供MultisigWallet合约示例但已较少维护。6. 总结实现多签钱包的核心是管理交易生命周期提交 → 多方确认 → 达到阈值后执行。关键点在于确保只有所有者可操作防止重入并合理设计数据结构以节省 Gas。对于生产环境建议直接采用经过实战检验的方案如 Gnosis Safe避免自行实现时的安全风险。