Skip to content

Commit d66c805

Browse files
authored
Updating multisig-wallet.md (#208)
1 parent 2f96573 commit d66c805

File tree

1 file changed

+98
-178
lines changed

1 file changed

+98
-178
lines changed

docs/examples/DeFi/multisig-wallet.md

Lines changed: 98 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ To illustrate, think of a bank vault that demands multiple keys to unlock; this
1313

1414
Advocates of multisignature wallets argue that they offer the most secure and fail-proof method for storing cryptocurrency. Even if a thief were to obtain one of the wallet keys, they would still be unable to access the account without the keys associated with the other wallets in the setup.
1515

16-
This article provides an explanation of the programming interface, data structure, basic functions, and their respective purposes. It can be used as-is or customized to fit specific needs. Anyone can create an application and deploy it on the Gear Network. The source code is accessible on [GitHub](https://github.com/gear-foundation/dapps/tree/master/contracts/multisig-wallet).
16+
This article provides an explanation of the programming interface, data structure, basic functions, and their respective purposes. It can be used as-is or customized to fit specific needs. Anyone can create an application and deploy it on the Gear Network. The source code, developed using the [Sails](../../build/sails/sails.mdx) framework, is available on [GitHub](https://github.com/gear-foundation/dapps/tree/master/contracts/multisig-wallet).
1717

1818
## Logic
1919

@@ -35,55 +35,103 @@ The transaction approval logic is complex, for example:
3535
3636
## Interface
3737

38-
### Init Config
38+
### Program description
3939

40-
```rust title="multisig-wallet/io/src/lib.rs"
41-
pub struct MWInitConfig {
42-
pub owners: Vec<ActorId>,
40+
The program contains the following information
41+
42+
```rust title="multisig-wallet/app/src/utils.rs"
43+
pub struct MultisigWallet {
44+
pub transactions: HashMap<TransactionId, Transaction>,
45+
pub confirmations: HashMap<TransactionId, HashSet<ActorId>>,
46+
pub owners: HashSet<ActorId>,
4347
pub required: u32,
48+
pub transaction_count: U256,
4449
}
4550
```
4651

47-
Describes the initial state of the wallet.
48-
- `owners` - a list of owners of the new wallet
49-
- `required` - required confirmations count to approve and execute the transaction
52+
* `transactions` - a mapping of `TransactionId` to `Transaction` that stores all transactions in the wallet
53+
* `confirmations` - a mapping of `TransactionId` to a set of `ActorIds`, representing confirmations by each owner for specific transactions
54+
* `owners` - a set of `ActorIds` representing the authorized owners of the wallet
55+
* `required` - the number of confirmations required to execute a transaction
56+
* `transaction_count` - a counter tracking the number of transactions
57+
58+
Where `Transaction` is defined as follows:
59+
60+
```rust title="multisig-wallet/app/src/utils.rs"
61+
pub struct Transaction {
62+
pub destination: ActorId,
63+
pub payload: Vec<u8>,
64+
pub value: u128,
65+
pub description: Option<String>,
66+
pub executed: bool,
67+
}
68+
```
5069

51-
### Actions
70+
* `destination` - the account where the transaction funds or actions are directed
71+
* `payload` - data related to the transaction, potentially containing specific instructions or actions
72+
* `value` - the amount of funds or value to transfer
73+
* `description` - an optional description providing context for the transaction
74+
* `executed` - a boolean indicating if the transaction has been executed
5275

53-
```rust title="multisig-wallet/io/src/lib.rs"
54-
pub enum MWAction {
55-
AddOwner(ActorId),
56-
RemoveOwner(ActorId),
57-
ReplaceOwner {
58-
old_owner: ActorId,
59-
new_owner: ActorId,
60-
},
61-
ChangeRequiredConfirmationsCount(u32),
62-
SubmitTransaction {
63-
destination: ActorId,
64-
data: Vec<u8>,
65-
value: u128,
66-
description: Option<String>,
67-
},
68-
ConfirmTransaction(U256),
69-
RevokeConfirmation(U256),
70-
ExecuteTransaction(U256),
76+
### Initialization
77+
78+
During program initialization, the following variables are set: the list of wallet owners and the minimum number of confirmations required to execute a transaction.
79+
80+
```rust title="multisig-wallet/app/src/lib.rs"
81+
fn init(owners: Vec<ActorId>, required: u32) -> Self {
82+
let owners_count = owners.len();
83+
84+
validate_requirement(owners_count, required);
85+
86+
let mut wallet = MultisigWallet::default();
87+
88+
for owner in &owners {
89+
if wallet.owners.contains(owner) {
90+
panic!("The same owner contained twice")
91+
} else {
92+
wallet.owners.insert(*owner);
93+
}
94+
}
95+
96+
wallet.required = required;
97+
98+
unsafe { STORAGE = Some(wallet) };
99+
Self(())
71100
}
72101
```
73102

74-
- `AddOwner` is an action to add a new owner. The action has to be used through `SubmitTransaction`.
75-
- `RemoveOwner` is an action to remove an owner. The action has to be used through `SubmitTransaction`.
76-
- `ReplaceOwner` is an action to replace an owner with a new owner. The action has to be used through `SubmitTransaction`.
77-
- `ChangeRequiredConfirmationsCount` is an action to change the number of required confirmations. The action has to be used through `SubmitTransaction`.
78-
- `SubmitTransaction` is an action that allows an owner to submit and automatically confirm a transaction.
79-
- `ConfirmTransaction` is an action that allows an owner to confirm a transaction. If this is the last confirmation, the transaction is automatically executed.
80-
- `RevokeConfirmation` is an action that allows an owner to revoke a confirmation for a transaction.
81-
- `ExecuteTransaction` is an action that allows anyone to execute a confirmed transaction.
103+
### Actions
104+
105+
```rust title="multisig-wallet/app/src/lib.rs"
106+
pub fn add_owner(&mut self, owner: ActorId);
107+
pub fn remove_owner(&mut self, owner: ActorId);
108+
pub fn replace_owner(&mut self, old_owner: ActorId, new_owner: ActorId);
109+
pub fn change_required_confirmations_count(&mut self, count: u32);
110+
pub fn submit_transaction(
111+
&mut self,
112+
destination: ActorId,
113+
data: Vec<u8>,
114+
value: u128,
115+
description: Option<String>,
116+
);
117+
pub fn confirm_transaction(&mut self, transaction_id: U256);
118+
pub fn revoke_confirmation(&mut self, transaction_id: U256);
119+
pub fn execute_transaction(&mut self, transaction_id: U256);
120+
```
121+
122+
- `add_owner` is an action to add a new owner. The action has to be used through `submit_transaction`.
123+
- `remove_owner` is an action to remove an owner. The action has to be used through `submit_transaction`.
124+
- `replace_owner` is an action to replace an owner with a new owner. The action has to be used through `submit_transaction`.
125+
- `change_required_confirmations_count` is an action to change the number of required confirmations. The action has to be used through `submit_transaction`.
126+
- `submit_transaction` is an action that allows an owner to submit and automatically confirm a transaction.
127+
- `confirm_transaction` is an action that allows an owner to confirm a transaction. If this is the last confirmation, the transaction is automatically executed.
128+
- `revoke_confirmation` is an action that allows an owner to revoke a confirmation for a transaction.
129+
- `execute_transaction` is an action that allows anyone to execute a confirmed transaction.
82130

83131
### Events
84132

85-
```rust title="multisig-wallet/io/src/lib.rs"
86-
pub enum MWEvent {
133+
```rust title="multisig-wallet/app/src/lib.rs"
134+
pub enum Event {
87135
Confirmation {
88136
sender: ActorId,
89137
transaction_id: U256,
@@ -121,150 +169,22 @@ pub enum MWEvent {
121169
- `OwnerReplace` is an event that occurs when the wallet uses the `ReplaceOwner` action successfully.
122170
- `RequirementChange` is an event that occurs when the wallet uses the `ChangeRequiredConfirmationsCount` action successfully.
123171

124-
### Program Metadata and State
125-
126-
Metadata interface description:
172+
### Query
127173

128-
```rust title="multisig-wallet/io/src/lib.rs"
129-
pub struct ContractMetadata;
130-
131-
impl Metadata for ContractMetadata {
132-
type Init = In<MWInitConfig>;
133-
type Handle = InOut<MWAction, MWEvent>;
134-
type Reply = ();
135-
type Others = ();
136-
type Signal = ();
137-
type State = Out<State>;
138-
}
139-
```
140-
To display the full contract state information, the `state()` function is used:
141-
142-
```rust title="multisig-wallet/src/lib.rs"
143-
#[no_mangle]
144-
extern fn state() {
145-
let contract = unsafe { WALLET.take().expect("Unexpected error in taking state") };
146-
msg::reply::<State>(contract.into(), 0)
147-
.expect("Failed to encode or reply with `State` from `state()`");
174+
```rust title="multisig-wallet/app/src/lib.rs"
175+
pub fn get_state(&self) -> State {
176+
self.get().clone().into()
148177
}
149178
```
150-
To display only specific values from the state, write a separate crate. In this crate, specify functions that will return the desired values from the `State` struct. For example, see [gear-foundation/dapps/multisig-wallet/state](https://github.com/gear-foundation/dapps/tree/master/contracts/multisig-wallet/state):
151-
152-
```rust title="multisig-wallet/state/src/lib.rs"
153-
#[gmeta::metawasm]
154-
pub mod metafns {
155-
pub type State = multisig_wallet_io::State;
156-
157-
/// Returns number of confirmations of a transaction.
158-
/// `transaction_id` Transaction ID.
159-
/// Number of confirmations.
160-
pub fn confirmations_count(state: State, transaction_id: TransactionId) -> Option<u32> {
161-
common_confirmations_count(&state, transaction_id)
162-
}
163-
164-
/// Returns total number of transactions after filters are applied.
165-
/// `pending` Include pending transactions.
166-
/// `executed` Include executed transactions.
167-
/// Total number of transactions after filters are applied.
168-
pub fn transactions_count(state: State, pending: bool, executed: bool) -> u32 {
169-
state
170-
.transactions
171-
.into_iter()
172-
.filter(|(_, tx)| (pending && !tx.executed) || (executed && tx.executed))
173-
.count() as _
174-
}
175-
176-
/// Returns list of owners.
177-
/// List of owner addresses.
178-
pub fn owners(state: State) -> Vec<ActorId> {
179-
state.owners
180-
}
181-
182-
/// Returns array with owner addresses, which confirmed transaction.
183-
/// `transaction_id` Transaction ID.
184-
/// Returns array of owner addresses.
185-
pub fn confirmations(state: State, transaction_id: TransactionId) -> Option<Vec<ActorId>> {
186-
state
187-
.confirmations
188-
.into_iter()
189-
.find_map(|(tx_id, confirmations)| (tx_id == transaction_id).then_some(confirmations))
190-
}
191-
192-
/// Returns list of transaction IDs in defined range.
193-
/// `from` Index start position of transaction array.
194-
/// `to` Index end position of transaction array (not included).
195-
/// `pending` Include pending transactions.
196-
/// `executed` Include executed transactions.
197-
/// `Returns` array of transaction IDs.
198-
pub fn transaction_ids(
199-
state: State,
200-
from: u32,
201-
to: u32,
202-
pending: bool,
203-
executed: bool,
204-
) -> Vec<TransactionId> {
205-
state
206-
.transactions
207-
.into_iter()
208-
.filter(|(_, tx)| (pending && !tx.executed) || (executed && tx.executed))
209-
.map(|(
210-
211-
id, _)| id)
212-
.take(to as _)
213-
.skip(from as _)
214-
.collect()
215-
}
179+
Where `State` is defined as follows:
216180

217-
/// Returns the confirmation status of a transaction.
218-
/// `transaction_id` Transaction ID.
219-
pub fn is_confirmed(state: State, transaction_id: TransactionId) -> bool {
220-
let required = state.required;
221-
222-
if let Some(count) = common_confirmations_count(&state, transaction_id) {
223-
count >= required
224-
} else {
225-
false
226-
}
227-
}
228-
229-
/// Returns the description of a transaction.
230-
/// `transaction_id` Transaction ID.
231-
pub fn transaction_description(
232-
state: State,
233-
transaction_id: TransactionId,
234-
) -> Option<Option<String>> {
235-
state
236-
.transactions
237-
.into_iter()
238-
.find_map(|(tx_id, tx)| (tx_id == transaction_id).then_some(tx.description))
239-
}
240-
}
241-
```
242-
243-
- `ConfirmationsCount` returns the number of confirmations of a transaction whose ID is a parameter.
244-
- `TransactionsCount` returns the total number of transactions after filters are applied. `pending` includes transactions that have not been executed yet, `executed` includes transactions that have been completed.
245-
- `Owners` returns the list of owners.
246-
- `Confirmations` returns an array with owner addresses that confirmed the transaction whose ID is a parameter.
247-
- `TransactionIds` returns a list of transaction IDs in a defined range.
248-
- `from` index start position of the transaction array.
249-
- `to` index end position of the transaction array (not included).
250-
- `pending` include pending transactions.
251-
- `executed` include executed transactions.
252-
- `IsConfirmed` returns the confirmation status of the transaction whose ID is a parameter.
253-
- `Description` returns the description of the transaction whose ID is a parameter.
254-
255-
Each state request has a corresponding reply with the same name.
256-
257-
*Replies:*
258-
259-
```rust
260-
pub enum StateReply {
261-
ConfirmationCount(u64),
262-
TransactionsCount(u64),
263-
Owners(Vec<ActorId>),
264-
Confirmations(Vec<ActorId>),
265-
TransactionIds(Vec<U256>),
266-
IsConfirmed(bool),
267-
Description(Option<String>)
181+
```rust title="multisig-wallet/app/src/utils.rs"
182+
pub struct State {
183+
pub transactions: Vec<(TransactionId, Transaction)>,
184+
pub confirmations: Vec<(TransactionId, Vec<ActorId>)>,
185+
pub owners: Vec<ActorId>,
186+
pub required: u32,
187+
pub transaction_count: U256,
268188
}
269189
```
270190

@@ -274,4 +194,4 @@ The source code of this example of a Multisig Wallet program and an implementati
274194

275195
See also an example of the program testing implementation based on `gtest`: [multisig-wallet/tests](https://github.com/gear-foundation/dapps/tree/master/contracts/multisig-wallet/tests).
276196

277-
For more details about testing program written on Vara, refer to the [Program Testing](/docs/build/testing) article.
197+
For more details about testing program written on Vara, refer to the [Program Testing](/docs/build/testing) article.

0 commit comments

Comments
 (0)