An Arbitrum Stylus version implementation of Solidity MultiSig wallet.
The wallet owners can
Here is the interface for MultiSig wallet.
1interface IMultiSig {
2 function numConfirmationsRequired() external view returns (uint256);
3
4 function deposit() external payable;
5
6 function submitTransaction(address to, uint256 value, bytes calldata data) external;
7
8 function initialize(address[] memory owners, uint256 num_confirmations_required) external;
9
10 function executeTransaction(uint256 tx_index) external;
11
12 function confirmTransaction(uint256 tx_index) external;
13
14 function revokeConfirmation(uint256 tx_index) external;
15
16 function isOwner(address check_address) external view returns (bool);
17
18 function getTransactionCount() external view returns (uint256);
19
20 error AlreadyInitialized();
21
22 error ZeroOwners();
23
24 error InvaildConfirmationNumber();
25
26 error InvalidOwner();
27
28 error OwnerNotUnique();
29
30 error NotOwner();
31
32 error TxDoesNotExist();
33
34 error TxAlreadyExecuted();
35
36 error TxAlreadyConfirmed();
37
38 error TxNotConfirmed();
39
40 error ConfirmationNumberNotEnough();
41
42 error ExecuteFailed();
43}
1interface IMultiSig {
2 function numConfirmationsRequired() external view returns (uint256);
3
4 function deposit() external payable;
5
6 function submitTransaction(address to, uint256 value, bytes calldata data) external;
7
8 function initialize(address[] memory owners, uint256 num_confirmations_required) external;
9
10 function executeTransaction(uint256 tx_index) external;
11
12 function confirmTransaction(uint256 tx_index) external;
13
14 function revokeConfirmation(uint256 tx_index) external;
15
16 function isOwner(address check_address) external view returns (bool);
17
18 function getTransactionCount() external view returns (uint256);
19
20 error AlreadyInitialized();
21
22 error ZeroOwners();
23
24 error InvaildConfirmationNumber();
25
26 error InvalidOwner();
27
28 error OwnerNotUnique();
29
30 error NotOwner();
31
32 error TxDoesNotExist();
33
34 error TxAlreadyExecuted();
35
36 error TxAlreadyConfirmed();
37
38 error TxNotConfirmed();
39
40 error ConfirmationNumberNotEnough();
41
42 error ExecuteFailed();
43}
Example implementation of a MultiSig Wallet contract written in Rust.
1// Allow `cargo stylus export-abi` to generate a main function.
2#![cfg_attr(not(feature = "export-abi"), no_main)]
3extern crate alloc;
4
5/// Use an efficient WASM allocator.
6#[global_allocator]
7static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT;
8
9/// Import items from the SDK. The prelude contains common traits and macros.
10use stylus_sdk::{contract, evm, msg, prelude::*, call::{Call, call}, alloy_primitives::{Address, U256}, abi::Bytes};
11use alloy_sol_types::sol;
12
13// Define some events using the Solidity ABI.
14sol! {
15 event Deposit(address indexed sender, uint256 amount, uint256 balance);
16 event SubmitTransaction(
17 address indexed owner,
18 uint256 indexed txIndex,
19 address indexed to,
20 uint256 value,
21 bytes data
22 );
23 event ConfirmTransaction(address indexed owner, uint256 indexed txIndex);
24 event RevokeConfirmation(address indexed owner, uint256 indexed txIndex);
25 event ExecuteTransaction(address indexed owner, uint256 indexed txIndex);
26
27 // Error types for the MultiSig contract
28 error AlreadyInitialized();
29 error ZeroOwners(); // The owners number is 0 when init.
30 error InvaildConfirmationNumber();
31 error InvalidOwner(); // The owner address is invalid when init.
32 error OwnerNotUnique(); // The owner address is not unique when init.
33 error NotOwner(); // The sender is not an owner.
34 error TxDoesNotExist();
35 error TxAlreadyExecuted();
36 error TxAlreadyConfirmed();
37 error TxNotConfirmed();
38 error ConfirmationNumberNotEnough();
39 error ExecuteFailed();
40}
41
42// Define some persistent storage using the Solidity ABI.
43// `MultiSig` will be the entrypoint.
44sol_storage! {
45 #[entrypoint]
46 pub struct MultiSig {
47 address[] owners; // The addresses of the owners
48 mapping(address => bool) is_owner; // mapping from owner => bool
49 uint256 num_confirmations_required; // The number of confirmations required to execute a transaction
50 TxStruct[] transactions; // The transactions array
51 // mapping from tx index => owner => bool
52 mapping(uint256 => mapping(address => bool)) is_confirmed;
53 }
54
55 // Define the `TxStruct` struct
56 pub struct TxStruct {
57 address to;
58 uint256 value;
59 bytes data;
60 bool executed; // Whether the transaction has been executed
61 uint256 num_confirmations; // The number of confirmations of the current transaction
62 }
63}
64
65// Error types for the MultiSig contract
66#[derive(SolidityError)]
67pub enum MultiSigError {
68 AlreadyInitialized(AlreadyInitialized),
69 ZeroOwners(ZeroOwners),
70 InvaildConfirmationNumber(InvaildConfirmationNumber),
71 InvalidOwner(InvalidOwner),
72 OwnerNotUnique(OwnerNotUnique),
73 NotOwner(NotOwner),
74 TxDoesNotExist(TxDoesNotExist),
75 TxAlreadyExecuted(TxAlreadyExecuted),
76 TxAlreadyConfirmed(TxAlreadyConfirmed),
77 TxNotConfirmed(TxNotConfirmed),
78 ConfirmationNumberNotEnough(ConfirmationNumberNotEnough),
79 ExecuteFailed(ExecuteFailed),
80}
81
82/// Declare that `MultiSig` is a contract with the following external methods.
83#[external]
84impl MultiSig {
85 pub fn num_confirmations_required(&self) -> Result<U256, MultiSigError> {
86 Ok(self.num_confirmations_required.get())
87 }
88
89 // The `deposit` method is payable, so it can receive funds.
90 #[payable]
91 pub fn deposit(&mut self) {
92 let sender = msg::sender();
93 let amount = msg::value();
94 evm::log(
95 Deposit{
96 sender: sender,
97 amount: amount,
98 balance: contract::balance()
99 });
100 }
101
102 // The `submit_transaction` method submits a new transaction to the contract.
103 pub fn submit_transaction(&mut self, to: Address, value: U256, data: Bytes) -> Result<(), MultiSigError> {
104 // The sender must be an owner.
105 if !self.is_owner.get(msg::sender()) {
106 return Err(MultiSigError::NotOwner(NotOwner{}));
107 }
108
109 let tx_index = U256::from(self.transactions.len());
110
111 // Add the transaction to the transactions array.
112 let mut new_tx = self.transactions.grow();
113 new_tx.to.set(to);
114 new_tx.value.set(value);
115 new_tx.data.set_bytes(data.clone());
116 new_tx.executed.set(false);
117 new_tx.num_confirmations.set(U256::from(0));
118
119 // Emit the `SubmitTransaction` event.
120 evm::log(SubmitTransaction {
121 owner: msg::sender(),
122 txIndex: tx_index,
123 to: to,
124 value: value,
125 data: data.to_vec(),
126 });
127 Ok(())
128 }
129
130
131 // The `initialize` method initializes the contract with the owners and the number of confirmations required.
132 pub fn initialize(&mut self, owners: Vec<Address>, num_confirmations_required: U256) -> Result<(), MultiSigError> {
133 // The owners must not be initialized.
134 if self.owners.len() > 0 {
135 return Err(MultiSigError::AlreadyInitialized(AlreadyInitialized{}));
136 }
137
138 // The owners must not be empty.
139 if owners.len() == 0 {
140 return Err(MultiSigError::ZeroOwners(ZeroOwners{}));
141 }
142
143 // The number of confirmations required must be greater than 0 and less than or equal to the number of owners.
144 if num_confirmations_required == U256::from(0) || num_confirmations_required > U256::from(owners.len()) {
145 return Err(MultiSigError::InvaildConfirmationNumber(InvaildConfirmationNumber{}));
146 }
147
148 // Add the owners to the contract.
149 for owner in owners.iter() {
150 if *owner == Address::default() {
151 return Err(MultiSigError::InvalidOwner(InvalidOwner{}))
152 }
153
154 if self.is_owner.get(*owner) {
155 return Err(MultiSigError::OwnerNotUnique(OwnerNotUnique{}))
156 }
157
158 self.is_owner.setter(*owner).set(true);
159 self.owners.push(*owner);
160 }
161
162 // Set the number of confirmations required.
163 self.num_confirmations_required.set(num_confirmations_required);
164 Ok(())
165 }
166
167 // The `execute_transaction` method executes a transaction.
168 pub fn execute_transaction(&mut self, tx_index: U256) -> Result<(), MultiSigError>{
169 // The sender must be an owner.
170 if !self.is_owner.get(msg::sender()) {
171 return Err(MultiSigError::NotOwner(NotOwner{}));
172 }
173
174 // The transaction must exist.
175 let tx_index = tx_index.to::<usize>();
176 if tx_index >= self.transactions.len() {
177 return Err(MultiSigError::TxDoesNotExist(TxDoesNotExist{}));
178 }
179
180 // Try get transaction and check transaction is valid or not, if valid, execute it, if not, revert tx.
181 if let Some(mut entry) = self.transactions.get_mut(tx_index) {
182 if entry.executed.get() {
183 return Err(MultiSigError::TxAlreadyExecuted(TxAlreadyExecuted{}));
184 }
185
186 if entry.num_confirmations.get() < self.num_confirmations_required.get() {
187 return Err(MultiSigError::ConfirmationNumberNotEnough(ConfirmationNumberNotEnough{}));
188 }
189
190 entry.executed.set(true);
191
192 // Execute the transaction
193 match call(Call::new().value(entry.value.get()), entry.to.get(), &entry.data.get_bytes()) {
194 // If the transaction is successful, emit the `ExecuteTransaction` event.
195 Ok(_) => {
196 evm::log(ExecuteTransaction {
197 owner: msg::sender(),
198 txIndex: U256::from(tx_index),
199 });
200 Ok(())
201 },
202 // If the transaction fails, revert the transaction.
203 Err(_) => {
204 return Err(MultiSigError::ExecuteFailed(ExecuteFailed{}));
205 }
206 }
207
208 } else {
209 return Err(MultiSigError::TxDoesNotExist(TxDoesNotExist{}));
210 }
211 }
212
213 // The `confirm_transaction` method confirms a transaction.
214 pub fn confirm_transaction(&mut self, tx_index: U256) -> Result<(), MultiSigError> {
215 // The sender must be an owner.
216 if !self.is_owner.get(msg::sender()) {
217 return Err(MultiSigError::NotOwner(NotOwner{}));
218 }
219
220 // The transaction must exist.
221 if tx_index >= U256::from(self.transactions.len()) {
222 return Err(MultiSigError::TxDoesNotExist(TxDoesNotExist{}));
223 }
224
225 // Try get transaction and check transaction is valid or not, if valid, confirm it, if not, revert tx.
226 if let Some(mut entry) = self.transactions.get_mut(tx_index) {
227 if entry.executed.get() {
228 return Err(MultiSigError::TxAlreadyExecuted(TxAlreadyExecuted{}));
229 }
230
231 if self.is_confirmed.get(tx_index).get(msg::sender()) {
232 return Err(MultiSigError::TxAlreadyConfirmed(TxAlreadyConfirmed{}));
233 }
234
235 // Confirm the transaction
236 let num_confirmations = entry.num_confirmations.get();
237 entry.num_confirmations.set(num_confirmations + U256::from(1));
238 // Set the transaction as confirmed by the sender.
239 let mut tx_confirmed_info = self.is_confirmed.setter(tx_index);
240 let mut confirmed_by_address = tx_confirmed_info.setter(msg::sender());
241 confirmed_by_address.set(true);
242
243 // Emit the `ConfirmTransaction` event.
244 evm::log(ConfirmTransaction {
245 owner: msg::sender(),
246 txIndex: U256::from(tx_index),
247 });
248 Ok(())
249 } else {
250 return Err(MultiSigError::TxDoesNotExist(TxDoesNotExist{}));
251 }
252 }
253
254 // The `revoke_confirmation` method revokes a confirmation for a transaction.
255 pub fn revoke_confirmation(&mut self, tx_index: U256) -> Result<(), MultiSigError> {
256 // The sender must be an owner.
257 if !self.is_owner.get(msg::sender()) {
258 return Err(MultiSigError::NotOwner(NotOwner{}));
259 }
260 // let tx_index = tx_index.to;
261 if tx_index >= U256::from(self.transactions.len()) {
262 return Err(MultiSigError::TxDoesNotExist(TxDoesNotExist{}));
263 }
264
265 // Try get transaction and check transaction is valid or not, if valid, revoke it, if not, revert tx.
266 if let Some(mut entry) = self.transactions.get_mut(tx_index) {
267 // Check if the transaction has been confirmed or not
268 if !self.is_confirmed.get(tx_index).get(msg::sender()) {
269 // If the transaction has not been confirmed, return an error.
270 return Err(MultiSigError::TxNotConfirmed(TxNotConfirmed{}));
271 }
272
273 // Revoke the transaction
274 let num_confirmations = entry.num_confirmations.get();
275 entry.num_confirmations.set(num_confirmations - U256::from(1));
276 // Set the transaction as not confirmed by the sender.
277 let mut tx_confirmed_info = self.is_confirmed.setter(tx_index);
278 let mut confirmed_by_address = tx_confirmed_info.setter(msg::sender());
279 confirmed_by_address.set(false);
280
281 // Emit the `RevokeConfirmation` event.
282 evm::log(RevokeConfirmation {
283 owner: msg::sender(),
284 txIndex: U256::from(tx_index),
285 });
286 Ok(())
287 } else {
288 return Err(MultiSigError::TxDoesNotExist(TxDoesNotExist{}));
289 }
290 }
291
292 // The `is_owner` method checks if an address is an owner.
293 pub fn is_owner(&self, check_address: Address) -> bool {
294 self.is_owner.get(check_address)
295 }
296
297 // The `get_transaction_count` method returns the number of transactions.
298 pub fn get_transaction_count(&self) -> U256 {
299 U256::from(self.transactions.len())
300 }
301}
1// Allow `cargo stylus export-abi` to generate a main function.
2#![cfg_attr(not(feature = "export-abi"), no_main)]
3extern crate alloc;
4
5/// Use an efficient WASM allocator.
6#[global_allocator]
7static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT;
8
9/// Import items from the SDK. The prelude contains common traits and macros.
10use stylus_sdk::{contract, evm, msg, prelude::*, call::{Call, call}, alloy_primitives::{Address, U256}, abi::Bytes};
11use alloy_sol_types::sol;
12
13// Define some events using the Solidity ABI.
14sol! {
15 event Deposit(address indexed sender, uint256 amount, uint256 balance);
16 event SubmitTransaction(
17 address indexed owner,
18 uint256 indexed txIndex,
19 address indexed to,
20 uint256 value,
21 bytes data
22 );
23 event ConfirmTransaction(address indexed owner, uint256 indexed txIndex);
24 event RevokeConfirmation(address indexed owner, uint256 indexed txIndex);
25 event ExecuteTransaction(address indexed owner, uint256 indexed txIndex);
26
27 // Error types for the MultiSig contract
28 error AlreadyInitialized();
29 error ZeroOwners(); // The owners number is 0 when init.
30 error InvaildConfirmationNumber();
31 error InvalidOwner(); // The owner address is invalid when init.
32 error OwnerNotUnique(); // The owner address is not unique when init.
33 error NotOwner(); // The sender is not an owner.
34 error TxDoesNotExist();
35 error TxAlreadyExecuted();
36 error TxAlreadyConfirmed();
37 error TxNotConfirmed();
38 error ConfirmationNumberNotEnough();
39 error ExecuteFailed();
40}
41
42// Define some persistent storage using the Solidity ABI.
43// `MultiSig` will be the entrypoint.
44sol_storage! {
45 #[entrypoint]
46 pub struct MultiSig {
47 address[] owners; // The addresses of the owners
48 mapping(address => bool) is_owner; // mapping from owner => bool
49 uint256 num_confirmations_required; // The number of confirmations required to execute a transaction
50 TxStruct[] transactions; // The transactions array
51 // mapping from tx index => owner => bool
52 mapping(uint256 => mapping(address => bool)) is_confirmed;
53 }
54
55 // Define the `TxStruct` struct
56 pub struct TxStruct {
57 address to;
58 uint256 value;
59 bytes data;
60 bool executed; // Whether the transaction has been executed
61 uint256 num_confirmations; // The number of confirmations of the current transaction
62 }
63}
64
65// Error types for the MultiSig contract
66#[derive(SolidityError)]
67pub enum MultiSigError {
68 AlreadyInitialized(AlreadyInitialized),
69 ZeroOwners(ZeroOwners),
70 InvaildConfirmationNumber(InvaildConfirmationNumber),
71 InvalidOwner(InvalidOwner),
72 OwnerNotUnique(OwnerNotUnique),
73 NotOwner(NotOwner),
74 TxDoesNotExist(TxDoesNotExist),
75 TxAlreadyExecuted(TxAlreadyExecuted),
76 TxAlreadyConfirmed(TxAlreadyConfirmed),
77 TxNotConfirmed(TxNotConfirmed),
78 ConfirmationNumberNotEnough(ConfirmationNumberNotEnough),
79 ExecuteFailed(ExecuteFailed),
80}
81
82/// Declare that `MultiSig` is a contract with the following external methods.
83#[external]
84impl MultiSig {
85 pub fn num_confirmations_required(&self) -> Result<U256, MultiSigError> {
86 Ok(self.num_confirmations_required.get())
87 }
88
89 // The `deposit` method is payable, so it can receive funds.
90 #[payable]
91 pub fn deposit(&mut self) {
92 let sender = msg::sender();
93 let amount = msg::value();
94 evm::log(
95 Deposit{
96 sender: sender,
97 amount: amount,
98 balance: contract::balance()
99 });
100 }
101
102 // The `submit_transaction` method submits a new transaction to the contract.
103 pub fn submit_transaction(&mut self, to: Address, value: U256, data: Bytes) -> Result<(), MultiSigError> {
104 // The sender must be an owner.
105 if !self.is_owner.get(msg::sender()) {
106 return Err(MultiSigError::NotOwner(NotOwner{}));
107 }
108
109 let tx_index = U256::from(self.transactions.len());
110
111 // Add the transaction to the transactions array.
112 let mut new_tx = self.transactions.grow();
113 new_tx.to.set(to);
114 new_tx.value.set(value);
115 new_tx.data.set_bytes(data.clone());
116 new_tx.executed.set(false);
117 new_tx.num_confirmations.set(U256::from(0));
118
119 // Emit the `SubmitTransaction` event.
120 evm::log(SubmitTransaction {
121 owner: msg::sender(),
122 txIndex: tx_index,
123 to: to,
124 value: value,
125 data: data.to_vec(),
126 });
127 Ok(())
128 }
129
130
131 // The `initialize` method initializes the contract with the owners and the number of confirmations required.
132 pub fn initialize(&mut self, owners: Vec<Address>, num_confirmations_required: U256) -> Result<(), MultiSigError> {
133 // The owners must not be initialized.
134 if self.owners.len() > 0 {
135 return Err(MultiSigError::AlreadyInitialized(AlreadyInitialized{}));
136 }
137
138 // The owners must not be empty.
139 if owners.len() == 0 {
140 return Err(MultiSigError::ZeroOwners(ZeroOwners{}));
141 }
142
143 // The number of confirmations required must be greater than 0 and less than or equal to the number of owners.
144 if num_confirmations_required == U256::from(0) || num_confirmations_required > U256::from(owners.len()) {
145 return Err(MultiSigError::InvaildConfirmationNumber(InvaildConfirmationNumber{}));
146 }
147
148 // Add the owners to the contract.
149 for owner in owners.iter() {
150 if *owner == Address::default() {
151 return Err(MultiSigError::InvalidOwner(InvalidOwner{}))
152 }
153
154 if self.is_owner.get(*owner) {
155 return Err(MultiSigError::OwnerNotUnique(OwnerNotUnique{}))
156 }
157
158 self.is_owner.setter(*owner).set(true);
159 self.owners.push(*owner);
160 }
161
162 // Set the number of confirmations required.
163 self.num_confirmations_required.set(num_confirmations_required);
164 Ok(())
165 }
166
167 // The `execute_transaction` method executes a transaction.
168 pub fn execute_transaction(&mut self, tx_index: U256) -> Result<(), MultiSigError>{
169 // The sender must be an owner.
170 if !self.is_owner.get(msg::sender()) {
171 return Err(MultiSigError::NotOwner(NotOwner{}));
172 }
173
174 // The transaction must exist.
175 let tx_index = tx_index.to::<usize>();
176 if tx_index >= self.transactions.len() {
177 return Err(MultiSigError::TxDoesNotExist(TxDoesNotExist{}));
178 }
179
180 // Try get transaction and check transaction is valid or not, if valid, execute it, if not, revert tx.
181 if let Some(mut entry) = self.transactions.get_mut(tx_index) {
182 if entry.executed.get() {
183 return Err(MultiSigError::TxAlreadyExecuted(TxAlreadyExecuted{}));
184 }
185
186 if entry.num_confirmations.get() < self.num_confirmations_required.get() {
187 return Err(MultiSigError::ConfirmationNumberNotEnough(ConfirmationNumberNotEnough{}));
188 }
189
190 entry.executed.set(true);
191
192 // Execute the transaction
193 match call(Call::new().value(entry.value.get()), entry.to.get(), &entry.data.get_bytes()) {
194 // If the transaction is successful, emit the `ExecuteTransaction` event.
195 Ok(_) => {
196 evm::log(ExecuteTransaction {
197 owner: msg::sender(),
198 txIndex: U256::from(tx_index),
199 });
200 Ok(())
201 },
202 // If the transaction fails, revert the transaction.
203 Err(_) => {
204 return Err(MultiSigError::ExecuteFailed(ExecuteFailed{}));
205 }
206 }
207
208 } else {
209 return Err(MultiSigError::TxDoesNotExist(TxDoesNotExist{}));
210 }
211 }
212
213 // The `confirm_transaction` method confirms a transaction.
214 pub fn confirm_transaction(&mut self, tx_index: U256) -> Result<(), MultiSigError> {
215 // The sender must be an owner.
216 if !self.is_owner.get(msg::sender()) {
217 return Err(MultiSigError::NotOwner(NotOwner{}));
218 }
219
220 // The transaction must exist.
221 if tx_index >= U256::from(self.transactions.len()) {
222 return Err(MultiSigError::TxDoesNotExist(TxDoesNotExist{}));
223 }
224
225 // Try get transaction and check transaction is valid or not, if valid, confirm it, if not, revert tx.
226 if let Some(mut entry) = self.transactions.get_mut(tx_index) {
227 if entry.executed.get() {
228 return Err(MultiSigError::TxAlreadyExecuted(TxAlreadyExecuted{}));
229 }
230
231 if self.is_confirmed.get(tx_index).get(msg::sender()) {
232 return Err(MultiSigError::TxAlreadyConfirmed(TxAlreadyConfirmed{}));
233 }
234
235 // Confirm the transaction
236 let num_confirmations = entry.num_confirmations.get();
237 entry.num_confirmations.set(num_confirmations + U256::from(1));
238 // Set the transaction as confirmed by the sender.
239 let mut tx_confirmed_info = self.is_confirmed.setter(tx_index);
240 let mut confirmed_by_address = tx_confirmed_info.setter(msg::sender());
241 confirmed_by_address.set(true);
242
243 // Emit the `ConfirmTransaction` event.
244 evm::log(ConfirmTransaction {
245 owner: msg::sender(),
246 txIndex: U256::from(tx_index),
247 });
248 Ok(())
249 } else {
250 return Err(MultiSigError::TxDoesNotExist(TxDoesNotExist{}));
251 }
252 }
253
254 // The `revoke_confirmation` method revokes a confirmation for a transaction.
255 pub fn revoke_confirmation(&mut self, tx_index: U256) -> Result<(), MultiSigError> {
256 // The sender must be an owner.
257 if !self.is_owner.get(msg::sender()) {
258 return Err(MultiSigError::NotOwner(NotOwner{}));
259 }
260 // let tx_index = tx_index.to;
261 if tx_index >= U256::from(self.transactions.len()) {
262 return Err(MultiSigError::TxDoesNotExist(TxDoesNotExist{}));
263 }
264
265 // Try get transaction and check transaction is valid or not, if valid, revoke it, if not, revert tx.
266 if let Some(mut entry) = self.transactions.get_mut(tx_index) {
267 // Check if the transaction has been confirmed or not
268 if !self.is_confirmed.get(tx_index).get(msg::sender()) {
269 // If the transaction has not been confirmed, return an error.
270 return Err(MultiSigError::TxNotConfirmed(TxNotConfirmed{}));
271 }
272
273 // Revoke the transaction
274 let num_confirmations = entry.num_confirmations.get();
275 entry.num_confirmations.set(num_confirmations - U256::from(1));
276 // Set the transaction as not confirmed by the sender.
277 let mut tx_confirmed_info = self.is_confirmed.setter(tx_index);
278 let mut confirmed_by_address = tx_confirmed_info.setter(msg::sender());
279 confirmed_by_address.set(false);
280
281 // Emit the `RevokeConfirmation` event.
282 evm::log(RevokeConfirmation {
283 owner: msg::sender(),
284 txIndex: U256::from(tx_index),
285 });
286 Ok(())
287 } else {
288 return Err(MultiSigError::TxDoesNotExist(TxDoesNotExist{}));
289 }
290 }
291
292 // The `is_owner` method checks if an address is an owner.
293 pub fn is_owner(&self, check_address: Address) -> bool {
294 self.is_owner.get(check_address)
295 }
296
297 // The `get_transaction_count` method returns the number of transactions.
298 pub fn get_transaction_count(&self) -> U256 {
299 U256::from(self.transactions.len())
300 }
301}
1[package]
2name = "stylus-multisig-example"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7alloy-primitives = "0.3.1"
8alloy-sol-types = "0.3.1"
9mini-alloc = "0.4.2"
10stylus-sdk = { version = "0.5.0", features = ["docs"] }
11hex = "0.4.3"
12
13[features]
14export-abi = ["stylus-sdk/export-abi"]
15debug = ["stylus-sdk/debug"]
16
17[lib]
18crate-type = ["lib", "cdylib"]
19
20[profile.release]
21codegen-units = 1
22strip = true
23lto = true
24panic = "abort"
25opt-level = "s"
1[package]
2name = "stylus-multisig-example"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7alloy-primitives = "0.3.1"
8alloy-sol-types = "0.3.1"
9mini-alloc = "0.4.2"
10stylus-sdk = { version = "0.5.0", features = ["docs"] }
11hex = "0.4.3"
12
13[features]
14export-abi = ["stylus-sdk/export-abi"]
15debug = ["stylus-sdk/debug"]
16
17[lib]
18crate-type = ["lib", "cdylib"]
19
20[profile.release]
21codegen-units = 1
22strip = true
23lto = true
24panic = "abort"
25opt-level = "s"