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