多签钱包实现
核心知识点讲解
多签钱包的概念
多签钱包(Multisignature Wallet)是一种需要多个签名才能执行交易的钱包:
- 安全性:单一私钥丢失不会导致资金损失
- 防误操作:需要多个授权才能执行交易
- 权限管理:可以设置不同级别的权限和阈值
- 透明度:所有交易都在链上可见
多签钱包的工作原理
- 创建钱包:设置所有者列表和签名阈值
- 提交交易:任何所有者都可以提交交易提案
- 签名交易:其他所有者对交易进行签名
- 执行交易:当签名数量达到阈值时,交易可以被执行
- 管理操作:如添加/移除所有者、修改阈值等
多签钱包的应用场景
- DAO资金管理:需要多个成员批准才能使用资金
- 企业资金管理:防止单一员工滥用资金
- 个人资产保护:防止单一私钥丢失导致资产损失
- 项目资金管理:团队共同管理项目资金
实用案例分析
基础多签钱包实现
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MultiSigWallet {
// 事件
event Deposit(address indexed sender, uint256 amount);
event Submit(uint256 indexed txId);
event Approve(address indexed owner, uint256 indexed txId);
event Revoke(address indexed owner, uint256 indexed txId);
event Execute(uint256 indexed txId);
// 所有者
address[] public owners;
mapping(address => bool) public isOwner;
uint256 public required;
// 交易结构
struct Transaction {
address to;
uint256 value;
bytes data;
bool executed;
uint256 approvalCount;
}
// 交易映射
Transaction[] public transactions;
mapping(uint256 => mapping(address => bool)) public approvals;
// 修饰器
modifier onlyOwner() {
require(isOwner[msg.sender], "Not owner");
_;
}
modifier txExists(uint256 _txId) {
require(_txId < transactions.length, "Transaction does not exist");
_;
}
modifier notExecuted(uint256 _txId) {
require(!transactions[_txId].executed, "Transaction already executed");
_;
}
modifier notApproved(uint256 _txId) {
require(!approvals[_txId][msg.sender], "Transaction already approved");
_;
}
// 构造函数
constructor(address[] memory _owners, uint256 _required) {
require(_owners.length > 0, "Owners required");
require(_required > 0 && _required <= _owners.length, "Invalid required number of owners");
for (uint256 i = 0; i < _owners.length; i++) {
address owner = _owners[i];
require(owner != address(0), "Invalid owner");
require(!isOwner[owner], "Owner not unique");
isOwner[owner] = true;
owners.push(owner);
}
required = _required;
}
// 接收ETH
receive() external payable {
emit Deposit(msg.sender, msg.value);
}
// 提交交易
function submit(address _to, uint256 _value, bytes calldata _data) external onlyOwner {
uint256 txId = transactions.length;
transactions.push(Transaction({
to: _to,
value: _value,
data: _data,
executed: false,
approvalCount: 0
}));
emit Submit(txId);
}
// 批准交易
function approve(uint256 _txId) external onlyOwner txExists(_txId) notExecuted(_txId) notApproved(_txId) {
Transaction storage transaction = transactions[_txId];
transaction.approvalCount += 1;
approvals[_txId][msg.sender] = true;
emit Approve(msg.sender, _txId);
}
// 撤销批准
function revoke(uint256 _txId) external onlyOwner txExists(_txId) notExecuted(_txId) {
require(approvals[_txId][msg.sender], "Transaction not approved");
Transaction storage transaction = transactions[_txId];
transaction.approvalCount -= 1;
approvals[_txId][msg.sender] = false;
emit Revoke(msg.sender, _txId);
}
// 执行交易
function execute(uint256 _txId) external onlyOwner txExists(_txId) notExecuted(_txId) {
Transaction storage transaction = transactions[_txId];
require(transaction.approvalCount >= required, "Approvals less than required");
transaction.executed = true;
(bool success, ) = transaction.to.call{value: transaction.value}(transaction.data);
require(success, "Transaction failed");
emit Execute(_txId);
}
// 获取所有者
function getOwners() external view returns (address[] memory) {
return owners;
}
// 获取交易数量
function getTransactionCount() external view returns (uint256) {
return transactions.length;
}
// 获取交易信息
function getTransaction(uint256 _txId) external view returns (address to, uint256 value, bytes memory data, bool executed, uint256 approvalCount) {
Transaction storage transaction = transactions[_txId];
return (
transaction.to,
transaction.value,
transaction.data,
transaction.executed,
transaction.approvalCount
);
}
}高级多签钱包实现
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
contract AdvancedMultiSigWallet is AccessControl {
using SafeMath for uint256;
bytes32 public constant OWNER_ROLE = keccak256("OWNER_ROLE");
bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
// 事件
event Deposit(address indexed sender, uint256 amount);
event Submit(uint256 indexed txId);
event Approve(address indexed approver, uint256 indexed txId);
event Revoke(address indexed revoker, uint256 indexed txId);
event Execute(uint256 indexed txId);
event OwnerAdded(address indexed owner);
event OwnerRemoved(address indexed owner);
event ThresholdChanged(uint256 oldThreshold, uint256 newThreshold);
// 交易结构
struct Transaction {
address to;
uint256 value;
bytes data;
bool executed;
uint256 approvalCount;
uint256 creationTime;
uint256 expirationTime;
}
// 状态变量
uint256 public threshold;
Transaction[] public transactions;
mapping(uint256 => mapping(address => bool)) public approvals;
mapping(address => bool) public isOwner;
address[] public owners;
// 修饰器
modifier onlyOwner() {
require(hasRole(OWNER_ROLE, msg.sender), "Not owner");
_;
}
modifier txExists(uint256 _txId) {
require(_txId < transactions.length, "Transaction does not exist");
_;
}
modifier notExecuted(uint256 _txId) {
require(!transactions[_txId].executed, "Transaction already executed");
_;
}
modifier notExpired(uint256 _txId) {
require(block.timestamp <= transactions[_txId].expirationTime, "Transaction expired");
_;
}
modifier notApproved(uint256 _txId) {
require(!approvals[_txId][msg.sender], "Transaction already approved");
_;
}
// 构造函数
constructor(address[] memory _owners, uint256 _threshold, uint256 _defaultExpirationTime) {
require(_owners.length > 0, "Owners required");
require(_threshold > 0 && _threshold <= _owners.length, "Invalid threshold");
for (uint256 i = 0; i < _owners.length; i++) {
address owner = _owners[i];
require(owner != address(0), "Invalid owner");
require(!isOwner[owner], "Owner not unique");
_grantRole(OWNER_ROLE, owner);
_grantRole(PROPOSER_ROLE, owner);
_grantRole(EXECUTOR_ROLE, owner);
isOwner[owner] = true;
owners.push(owner);
}
threshold = _threshold;
}
// 接收ETH
receive() external payable {
emit Deposit(msg.sender, msg.value);
}
// 提交交易
function submit(address _to, uint256 _value, bytes calldata _data, uint256 _expirationTime) external {
require(hasRole(PROPOSER_ROLE, msg.sender), "Not proposer");
uint256 txId = transactions.length;
transactions.push(Transaction({
to: _to,
value: _value,
data: _data,
executed: false,
approvalCount: 0,
creationTime: block.timestamp,
expirationTime: _expirationTime
}));
emit Submit(txId);
}
// 批准交易
function approve(uint256 _txId) external onlyOwner txExists(_txId) notExecuted(_txId) notExpired(_txId) notApproved(_txId) {
Transaction storage transaction = transactions[_txId];
transaction.approvalCount = transaction.approvalCount.add(1);
approvals[_txId][msg.sender] = true;
emit Approve(msg.sender, _txId);
}
// 撤销批准
function revoke(uint256 _txId) external onlyOwner txExists(_txId) notExecuted(_txId) notExpired(_txId) {
require(approvals[_txId][msg.sender], "Transaction not approved");
Transaction storage transaction = transactions[_txId];
transaction.approvalCount = transaction.approvalCount.sub(1);
approvals[_txId][msg.sender] = false;
emit Revoke(msg.sender, _txId);
}
// 执行交易
function execute(uint256 _txId) external {
require(hasRole(EXECUTOR_ROLE, msg.sender), "Not executor");
require(_txId < transactions.length, "Transaction does not exist");
Transaction storage transaction = transactions[_txId];
require(!transaction.executed, "Transaction already executed");
require(block.timestamp <= transaction.expirationTime, "Transaction expired");
require(transaction.approvalCount >= threshold, "Approvals less than required");
transaction.executed = true;
(bool success, ) = transaction.to.call{value: transaction.value}(transaction.data);
require(success, "Transaction failed");
emit Execute(_txId);
}
// 添加所有者
function addOwner(address _owner) external onlyOwner {
require(_owner != address(0), "Invalid owner");
require(!isOwner[_owner], "Already an owner");
_grantRole(OWNER_ROLE, _owner);
_grantRole(PROPOSER_ROLE, _owner);
_grantRole(EXECUTOR_ROLE, _owner);
isOwner[_owner] = true;
owners.push(_owner);
emit OwnerAdded(_owner);
}
// 移除所有者
function removeOwner(address _owner) external onlyOwner {
require(isOwner[_owner], "Not an owner");
require(owners.length > 1, "Cannot remove last owner");
require(threshold <= owners.length - 1, "Threshold would be too high");
_revokeRole(OWNER_ROLE, _owner);
_revokeRole(PROPOSER_ROLE, _owner);
_revokeRole(EXECUTOR_ROLE, _owner);
isOwner[_owner] = false;
// 从owners数组中移除
for (uint256 i = 0; i < owners.length; i++) {
if (owners[i] == _owner) {
owners[i] = owners[owners.length - 1];
owners.pop();
break;
}
}
emit OwnerRemoved(_owner);
}
// 修改阈值
function changeThreshold(uint256 _newThreshold) external onlyOwner {
require(_newThreshold > 0 && _newThreshold <= owners.length, "Invalid threshold");
uint256 oldThreshold = threshold;
threshold = _newThreshold;
emit ThresholdChanged(oldThreshold, _newThreshold);
}
// 获取所有者
function getOwners() external view returns (address[] memory) {
return owners;
}
// 获取交易数量
function getTransactionCount() external view returns (uint256) {
return transactions.length;
}
// 获取交易信息
function getTransaction(uint256 _txId) external view returns (address to, uint256 value, bytes memory data, bool executed, uint256 approvalCount, uint256 creationTime, uint256 expirationTime) {
Transaction storage transaction = transactions[_txId];
return (
transaction.to,
transaction.value,
transaction.data,
transaction.executed,
transaction.approvalCount,
transaction.creationTime,
transaction.expirationTime
);
}
}多签钱包的安全考虑
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SecureMultiSigWallet {
// 事件
event Deposit(address indexed sender, uint256 amount);
event Submit(uint256 indexed txId);
event Approve(address indexed owner, uint256 indexed txId);
event Revoke(address indexed owner, uint256 indexed txId);
event Execute(uint256 indexed txId);
event EmergencyPause();
event EmergencyUnpause();
// 状态变量
address[] public owners;
mapping(address => bool) public isOwner;
uint256 public required;
bool public paused;
// 交易结构
struct Transaction {
address to;
uint256 value;
bytes data;
bool executed;
uint256 approvalCount;
}
// 交易映射
Transaction[] public transactions;
mapping(uint256 => mapping(address => bool)) public approvals;
// 修饰器
modifier onlyOwner() {
require(isOwner[msg.sender], "Not owner");
_;
}
modifier notPaused() {
require(!paused, "Contract paused");
_;
}
modifier txExists(uint256 _txId) {
require(_txId < transactions.length, "Transaction does not exist");
_;
}
modifier notExecuted(uint256 _txId) {
require(!transactions[_txId].executed, "Transaction already executed");
_;
}
modifier notApproved(uint256 _txId) {
require(!approvals[_txId][msg.sender], "Transaction already approved");
_;
}
// 构造函数
constructor(address[] memory _owners, uint256 _required) {
require(_owners.length > 0, "Owners required");
require(_required > 0 && _required <= _owners.length, "Invalid required number of owners");
for (uint256 i = 0; i < _owners.length; i++) {
address owner = _owners[i];
require(owner != address(0), "Invalid owner");
require(!isOwner[owner], "Owner not unique");
isOwner[owner] = true;
owners.push(owner);
}
required = _required;
}
// 接收ETH
receive() external payable {
emit Deposit(msg.sender, msg.value);
}
// 紧急暂停
function emergencyPause() external onlyOwner {
paused = true;
emit EmergencyPause();
}
// 紧急取消暂停
function emergencyUnpause() external onlyOwner {
paused = false;
emit EmergencyUnpause();
}
// 提交交易
function submit(address _to, uint256 _value, bytes calldata _data) external onlyOwner notPaused {
// 防止重入攻击
require(_to != address(this), "Cannot send to self");
uint256 txId = transactions.length;
transactions.push(Transaction({
to: _to,
value: _value,
data: _data,
executed: false,
approvalCount: 0
}));
emit Submit(txId);
}
// 批准交易
function approve(uint256 _txId) external onlyOwner txExists(_txId) notExecuted(_txId) notApproved(_txId) notPaused {
Transaction storage transaction = transactions[_txId];
transaction.approvalCount += 1;
approvals[_txId][msg.sender] = true;
emit Approve(msg.sender, _txId);
}
// 撤销批准
function revoke(uint256 _txId) external onlyOwner txExists(_txId) notExecuted(_txId) notPaused {
require(approvals[_txId][msg.sender], "Transaction not approved");
Transaction storage transaction = transactions[_txId];
transaction.approvalCount -= 1;
approvals[_txId][msg.sender] = false;
emit Revoke(msg.sender, _txId);
}
// 执行交易
function execute(uint256 _txId) external onlyOwner txExists(_txId) notExecuted(_txId) notPaused {
Transaction storage transaction = transactions[_txId];
require(transaction.approvalCount >= required, "Approvals less than required");
// 防止重入攻击
transaction.executed = true;
(bool success, ) = transaction.to.call{value: transaction.value}(transaction.data);
require(success, "Transaction failed");
emit Execute(_txId);
}
}实践练习
部署基础多签钱包:
- 部署
MultiSigWallet合约,设置3个所有者和2个签名阈值 - 向钱包存款ETH
- 提交一笔交易
- 让其他所有者批准交易
- 执行交易并验证结果
- 部署
使用高级多签钱包:
- 部署
AdvancedMultiSigWallet合约 - 添加和移除所有者
- 修改签名阈值
- 测试交易过期功能
- 部署
实现安全多签钱包:
- 部署
SecureMultiSigWallet合约 - 测试紧急暂停功能
- 模拟重入攻击,验证防护措施
- 部署
多签钱包集成:
- 将多签钱包与其他智能合约集成
- 测试复杂交易的执行
- 验证多签钱包的权限管理
总结
多签钱包是Web3开发中的重要安全工具,它通过要求多个签名来执行交易,提高了资金管理的安全性和透明度。在设计和实现多签钱包时,需要考虑以下因素:
- 安全性:防止重入攻击、权限控制等安全问题
- 灵活性:支持添加/移除所有者、修改签名阈值等操作
- 易用性:提供清晰的交易提交、批准和执行流程
- 透明度:所有交易和操作都在链上可见
多签钱包的应用场景非常广泛,从个人资产保护到DAO资金管理,都可以看到多签钱包的身影。通过本集的学习,你应该能够理解多签钱包的基本原理和实现方法,为开发安全可靠的Web3应用提供保障。