Skip to content

Commit

Permalink
Add Multisig doc page
Browse files Browse the repository at this point in the history
  • Loading branch information
immrsd committed Dec 10, 2024
1 parent 0acb875 commit c962eef
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
** Governance
*** xref:/governance/governor.adoc[Governor]
*** xref:/governance/multisig.adoc[Multisig]
*** xref:/governance/timelock.adoc[Timelock Controller]
*** xref:/governance/votes.adoc[Votes]
*** xref:/api/governance.adoc[API Reference]
Expand Down
147 changes: 147 additions & 0 deletions docs/modules/ROOT/pages/governance/multisig.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
= Multisig

:multisig-component: xref:api/governance.adoc#MultisigComponent[MultisigComponent]
:snip12-metadata: xref:api/utilities.adoc#snip12[SNIP12Metadata]

The Multisig component implements a multi-signature mechanism to enhance the security and
governance of smart contract transactions. It ensures that no single signer can unilaterally
execute critical actions, requiring multiple registered signers to approve and collectively
execute transactions.

This component is designed to secure operations such as fund management or protocol governance,
where collective decision-making is essential. The Multisig Component is self-administered,
meaning that changes to signers or quorum must be approved through the multisig process itself.

== Key Features

- *Multi-Signature Security*: Transactions must be approved by multiple signers, ensuring
distributed governance.

- *Quorum Enforcement*: Defines the minimum number of approvals required for transaction execution.

- *Self-Administration*: All modifications to the component (e.g., adding or removing signers)
must pass through the multisig process.

- *Event Logging*: Provides comprehensive event logging for transparency and auditability.

== Signer Management

The Multisig component introduces the concept of signers and quorum:

- *Signers*: Only registered signers can submit, confirm, revoke, or execute transactions. The Multisig
Component supports adding, removing, or replacing signers.
- *Quorum*: The quorum defines the minimum number of confirmations required to approve a transaction.

NOTE: To prevent unauthorized modifications, only the contract itself can add, remove, or replace signers or change the quorum.
This ensures that all modifications pass through the multisig approval process.

== Transaction Lifecycle

A transaction in the Multisig component follows a specific lifecycle:

`NotFound` → `Pending` → `Confirmed` → `Executed`

The state of a transaction is respresented by `TransactionState` enum can be checked

Check warning on line 44 in docs/modules/ROOT/pages/governance/multisig.adoc

View workflow job for this annotation

GitHub Actions / check-for-typos

"respresented" should be "represented".
by calling the `get_transaction_state` function.

- *NotFound*: The transaction does not exist.
- *Pending*: The transaction exists but has not reached the required confirmations.
- *Confirmed*: The transaction has reached the quorum but has not yet been executed.
- *Executed*: The transaction has been successfully executed.

== Usage

Integrating the Multisig functionality into a contract requires implementing {multisig-component}.
The contract's constructor should initialize the component with a quorum value and a list of initial signers.

Here's an example of a simple wallet contract featuring the Multisig functionality:

[,cairo]
----
#[starknet::contract]
mod MultisigWallet {
use openzeppelin_governance::multisig::MultisigComponent;
use starknet::ContractAddress;
component!(path: MultisigComponent, storage: multisig, event: MultisigEvent);
#[abi(embed_v0)]
impl MultisigImpl = MultisigComponent::MultisigImpl<ContractState>;
impl MultisigInternalImpl = MultisigComponent::InternalImpl<ContractState>;
#[storage]
struct Storage {
#[substorage(v0)]
multisig: MultisigComponent::Storage,
}
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
MultisigEvent: MultisigComponent::Event,
}
#[constructor]
fn constructor(ref self: ContractState, quorum: u32, signers: Span<ContractAddress>) {
self.multisig.initializer(quorum, signers);
}
}
----

== Interface

This is the interface of a contract implementing the {multisig-component}:

[,cairo]
----
#[starknet::interface]
pub trait MultisigABI<TState> {
// Read functions
fn get_quorum(self: @TState) -> u32;
fn is_signer(self: @TState, signer: ContractAddress) -> bool;
fn get_signers(self: @TState) -> Span<ContractAddress>;
fn is_confirmed(self: @TState, id: TransactionID) -> bool;
fn is_confirmed_by(self: @TState, id: TransactionID, signer: ContractAddress) -> bool;
fn is_executed(self: @TState, id: TransactionID) -> bool;
fn get_submitted_block(self: @TState, id: TransactionID) -> u64;
fn get_transaction_state(self: @TState, id: TransactionID) -> TransactionState;
fn get_transaction_confirmations(self: @TState, id: TransactionID) -> u32;
fn hash_transaction(
self: @TState,
to: ContractAddress,
selector: felt252,
calldata: Span<felt252>,
salt: felt252,
) -> TransactionID;
fn hash_transaction_batch(self: @TState, calls: Span<Call>, salt: felt252) -> TransactionID;
// Write functions
fn add_signers(ref self: TState, new_quorum: u32, signers_to_add: Span<ContractAddress>);
fn remove_signers(ref self: TState, new_quorum: u32, signers_to_remove: Span<ContractAddress>);
fn replace_signer(
ref self: TState, signer_to_remove: ContractAddress, signer_to_add: ContractAddress,
);
fn change_quorum(ref self: TState, new_quorum: u32);
fn submit_transaction(
ref self: TState,
to: ContractAddress,
selector: felt252,
calldata: Span<felt252>,
salt: felt252,
) -> TransactionID;
fn submit_transaction_batch(
ref self: TState, calls: Span<Call>, salt: felt252,
) -> TransactionID;
fn confirm_transaction(ref self: TState, id: TransactionID);
fn revoke_confirmation(ref self: TState, id: TransactionID);
fn execute_transaction(
ref self: TState,
to: ContractAddress,
selector: felt252,
calldata: Span<felt252>,
salt: felt252,
);
fn execute_transaction_batch(ref self: TState, calls: Span<Call>, salt: felt252);
}
----

0 comments on commit c962eef

Please sign in to comment.