3. 修改确认阈值
任务说明
本案例为多签钱包增加修改确认阈值功能:允许已批准的多签事务在达到当前阈值后,将所需确认数从旧值变更为新值。核心约束是——只有通过多签批准流程(而非单个所有者直接调用)才能完成修改,以此保证修改行为本身受到多签治理约束。
实现方案
在既有MultiSigWallet合约中新增changeRequirement函数,该函数内部调用_execute执行逻辑。由于_execute仅允许已提交且确认数达阈值的事务执行,因此修改确认数的操作天然被多签流程保护。新阈值必须满足1 <= newRequired <= ownerCount,否则回滚。
合约代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract MultiSigWallet {
struct Transaction {
address destination;
uint256 value;
bytes data;
bool executed;
uint256 confirmations;
}
address[] public owners;
mapping(address => bool) public isOwner;
uint256 public required;
Transaction[] public transactions;
mapping(uint256 => mapping(address => bool)) public confirmations;
event Confirmation(address indexed sender, uint256 indexed txId);
event Execution(uint256 indexed txId);
event RequirementChanged(uint256 newRequired);
modifier onlyOwner() {
require(isOwner[msg.sender], "not owner");
_;
}
modifier txExists(uint256 txId) {
require(txId < transactions.length, "tx not exist");
_;
}
modifier notExecuted(uint256 txId) {
require(!transactions[txId].executed, "tx executed");
_;
}
modifier confirmed(uint256 txId) {
require(transactions[txId].confirmations >= required, "not enough confirmations");
_;
}
constructor(address[] memory _owners, uint256 _required) {
require(_owners.length > 0, "owners required");
require(_required > 0 && _required <= _owners.length, "invalid required");
for (uint256 i = 0; i < _owners.length; i++) {
address owner = _owners[i];
require(owner != address(0), "invalid owner");
require(!isOwner[owner], "duplicate owner");
isOwner[owner] = true;
owners.push(owner);
}
required = _required;
}
/// @notice 提交修改确认数的多签事务(data 编码 changeRequirement 调用)
function submitChangeRequirement(uint256 newRequired) external onlyOwner returns (uint256 txId) {
bytes memory data = abi.encodeWithSignature("changeRequirement(uint256)", newRequired);
txId = _addTransaction(address(this), 0, data);
}
function confirmTransaction(uint256 txId)
external
onlyOwner
txExists(txId)
notExecuted(txId)
{
require(!confirmations[txId][msg.sender], "tx already confirmed");
confirmations[txId][msg.sender] = true;
transactions[txId].confirmations++;
emit Confirmation(msg.sender, txId);
// 达到阈值后自动执行
if (transactions[txId].confirmations >= required) {
_execute(txId);
}
}
/// @notice 内部执行函数,仅由多签流程触发
function _execute(uint256 txId) internal txExists(txId) notExecuted(txId) confirmed(txId) {
Transaction storage txn = transactions[txId];
txn.executed = true;
(bool success, ) = txn.destination.call{value: txn.value}(txn.data);
require(success, "tx failed");
emit Execution(txId);
}
/// @notice 修改确认阈值(只能通过多签事务调用)
function changeRequirement(uint256 newRequired) external {
require(msg.sender == address(this), "only via multisig");
require(newRequired > 0 && newRequired <= owners.length, "invalid new required");
required = newRequired;
emit RequirementChanged(newRequired);
}
function _addTransaction(address destination, uint256 value, bytes memory data)
internal
returns (uint256 txId)
{
txId = transactions.length;
transactions.push(Transaction({
destination: destination,
value: value,
data: data,
executed: false,
confirmations: 0
}));
}
// 查询辅助
function getOwners() external view returns (address[] memory) { return owners; }
function getTransactionCount() external view returns (uint256) { return transactions.length; }
function getRequired() external view returns (uint256) { return required; }
}
验证方法
部署与测试流程:
- 部署合约:
owners = [0xA, 0xB, 0xC],required = 2。 - 所有者A调用
submitChangeRequirement(3),获得txId = 0。 - 所有者B调用
confirmTransaction(0)——此时确认数=2,达到旧阈值,事务自动执行,required变为3。 - 调用
getRequired(),预期返回3。
判定标准:
- 若所有者A直接调用
changeRequirement(1),因msg.sender != address(this),交易回滚——证明修改只能通过多签批准。 - 若提交
newRequired = 0或newRequired = 4(超出所有者数),执行时回滚——证明阈值范围校验生效。 - 确认数未达旧阈值(如仅A一人确认)时,
required保持不变——证明修改未被提前执行。
预期输出:上述步骤3执行后,getRequired()返回3;步骤4中直接调用changeRequirement的尝试会触发revert异常。