Solidity 练习:多重签名钱包
睡不醒的鲤鱼 2022-12-29 Web3 Solidity
# 一、题目说明
实现一个多重签名钱包。
# 二、任务列表
- 使合约能够接收 Ether,发送 Deposit 事件。
- 编写函数来提交交易,以供其他 owner 批准。
- 函数 submit(address _to, uint _value, bytes calldata _data) external 把输入存储到 transactions 数组。
- 提交 Submit 事件,其中 txId 等于存储在 transactions 中的下标。
- 只有 owners 可以调用该函数。
- 编写函数去批准一个待处理交易。
- 函数 approve(uint _txId) 批准一个 id 为 _txId 的待处理交易。
- 该函数是 external 的。
- 只有 owners 可以调用该函数。
- id 为 _txId 的交易一定存在。
- 交易一定还没有被执行。
- 交易一定还没有被 msg.sender 批准。
- 提交 Approve 事件。
- 编写函数去执行一个批准数量大于等于 required 的待处理交易。
- 函数 execute(uint _txId) 执行交易 transcations[_txId]。
- 该函数是 external 的。
- 只有 owners 可以调用该函数。
- id 为 _txId 的交易一定存在。
- 交易一定还没有被执行。
- 该交易的批准数量大于等于 required。
- 提交 Execute 事件。
- 编写函数去撤销一个批准。
- 函数 revoke(uint _txId) 撤销 msg.sender 对 _txId 交易的批准。
- 该函数是 external 的。
- 只有 owners 可以调用该函数。
- id 为 _txId 的交易一定存在。
- 交易一定还没有被执行。
- 检查交易是否被 msg.sender 批准。
- 提交 Revoke 事件。
# 三、解答代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract MultiSigWallet {
event Deposit(address indexed sender, uint amount);
event Submit(uint indexed txId);
event Approve(address indexed owner, uint indexed txId);
event Revoke(address indexed owner, uint indexed txId);
event Execute(uint indexed txId);
struct Transaction {
address to;
uint value;
bytes data;
bool executed;
}
address[] public owners;
mapping(address => bool) public isOwner;
uint public required;
Transaction[] public transactions;
// mapping from tx id => owner => bool
mapping(uint => mapping(address => bool)) public approved;
modifier onlyOwner() {
require(isOwner[msg.sender], "not owner");
_;
}
modifier txExists(uint _txId) {
require(_txId < transactions.length, "tx does not exist");
_;
}
modifier notApproved(uint _txId) {
require(!approved[_txId][msg.sender], "tx already approved");
_;
}
modifier notExecuted(uint _txId) {
require(!transactions[_txId].executed, "tx already executed");
_;
}
constructor(address[] memory _owners, uint _required) {
require(_owners.length > 0, "owners required");
require(
_required > 0 && _required <= _owners.length,
"invalid required number of owners"
);
for (uint i; i < _owners.length; i++) {
address owner = _owners[i];
require(owner != address(0), "invalid owner");
require(!isOwner[owner], "owner is not unique");
isOwner[owner] = true;
owners.push(owner);
}
required = _required;
}
receive() external payable {
emit Deposit(msg.sender, msg.value);
}
function submit(
address _to,
uint _value,
bytes calldata _data
) external onlyOwner {
transactions.push(
Transaction({to: _to, value: _value, data: _data, executed: false})
);
emit Submit(transactions.length - 1);
}
function approve(uint _txId)
external
onlyOwner
txExists(_txId)
notApproved(_txId)
notExecuted(_txId)
{
approved[_txId][msg.sender] = true;
emit Approve(msg.sender, _txId);
}
function _getApprovalCount(uint _txId) private view returns (uint count) {
for (uint i; i < owners.length; i++) {
if (approved[_txId][owners[i]]) {
count += 1;
}
}
}
function execute(uint _txId)
external
onlyOwner
txExists(_txId)
notExecuted(_txId)
{
require(_getApprovalCount(_txId) >= required, "approvals < required");
Transaction storage transaction = transactions[_txId];
transaction.executed = true;
(bool success, ) = transaction.to.call{value: transaction.value}(
transaction.data
);
require(success, "tx failed");
emit Execute(_txId);
}
function revoke(uint _txId)
external
onlyOwner
txExists(_txId)
notExecuted(_txId)
{
require(approved[_txId][msg.sender], "tx not approved");
approved[_txId][msg.sender] = false;
emit Revoke(msg.sender, _txId);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129