Governance

This crate includes primitives for on-chain governance.

Multisig

A Multisig module enhances security and decentralization by requiring multiple signers to approve and execute transactions. Features include configurable quorum, signer management, and self-administration, ensuring collective decision-making and transparency for critical operations.

IMultisig

use openzeppelin_governance::multisig::interface::IMultisig;

Interface of a multisig contract.

Functions

get_quorum() → u32 external

Returns the current quorum value. The quorum is the minimum number of confirmations required to approve a transaction.

is_signer(signer: ContractAddress) → bool external

Returns whether the given signer is registered. Only registered signers can submit, confirm, or execute transactions.

get_signers() → Span<ContractAddress> external

Returns the list of all current signers.

is_confirmed(id: TransactionID) → bool external

Returns whether the transaction with the given id has been confirmed.

is_confirmed_by(id: TransactionID, signer: ContractAddress) → bool external

Returns whether the transaction with the given id has been confirmed by the specified signer.

is_executed(id: TransactionID) → bool external

Returns whether the transaction with the given id has been executed.

get_submitted_block(id: TransactionID) → u64 external

Returns the block number when the transaction with the given id was submitted.

get_transaction_state(id: TransactionID) → TransactionState external

Returns the current state of the transaction with the given id.

get_transaction_confirmations(id: TransactionID) → u32 external

Returns the number of confirmations from registered signers for the transaction with the specified id.

hash_transaction(to: ContractAddress, selector: felt252, calldata: Span<felt252>, salt: felt252) → TransactionID external

Returns the computed identifier of a transaction containing a single call.

hash_transaction_batch(calls: Span<Call>, salt: felt252) → TransactionID external

Returns the computed identifier of a transaction containing a batch of calls.

add_signers(new_quorum: u32, signers_to_add: Span<ContractAddress>) external

Adds new signers and updates the quorum.

Requirements:

  • The caller must be the contract itself.

  • new_quorum must be less than or equal to the total number of signers after addition.

Emits a SignerAdded event for each signer added.

Emits a QuorumUpdated event if the quorum changes.

remove_signers(new_quorum: u32, signers_to_remove: Span<ContractAddress>) external

Removes signers and updates the quorum.

Requirements:

  • The caller must be the contract itself.

  • new_quorum must be less than or equal to the total number of signers after removal.

Emits a SignerRemoved event for each signer removed.

Emits a QuorumUpdated event if the quorum changes.

replace_signer(signer_to_remove: ContractAddress, signer_to_add: ContractAddress) external

Replaces an existing signer with a new signer.

Requirements:

  • The caller must be the contract itself.

  • signer_to_remove must be an existing signer.

  • signer_to_add must not be an existing signer.

Emits a SignerRemoved event for the removed signer.

Emits a SignerAdded event for the new signer.

change_quorum(new_quorum: u32) external

Updates the quorum value to new_quorum if it differs from the current quorum.

Requirements:

  • The caller must be the contract itself.

  • new_quorum must be non-zero.

  • new_quorum must be less than or equal to the total number of signers.

Emits a QuorumUpdated event if the quorum changes.

submit_transaction(to: ContractAddress, selector: felt252, calldata: Span<felt252>, salt: felt252) → TransactionID external

Submits a new transaction for confirmation.

Requirements:

  • The caller must be a registered signer.

  • The transaction must not have been submitted before.

Emits a TransactionSubmitted event.

Emits a CallSalt event if salt is not zero.

submit_transaction_batch(calls: Span<Call>, salt: felt252) → TransactionID external

Submits a new batch transaction for confirmation.

Requirements:

  • The caller must be a registered signer.

  • The transaction must not have been submitted before.

Emits a TransactionSubmitted event.

Emits a CallSalt event if salt is not zero.

confirm_transaction(id: TransactionID) external

Confirms a transaction with the given id.

Requirements:

  • The caller must be a registered signer.

  • The transaction must exist and not be executed.

  • The caller must not have already confirmed the transaction.

Emits a TransactionConfirmed event.

revoke_confirmation(id: TransactionID) external

Revokes a previous confirmation for a transaction with the given id.

Requirements:

  • The transaction must exist and not be executed.

  • The caller must have previously confirmed the transaction.

Emits a ConfirmationRevoked event.

execute_transaction(to: ContractAddress, selector: felt252, calldata: Span<felt252>, salt: felt252) external

Executes a confirmed transaction.

Requirements:

  • The caller must be a registered signer.

  • The transaction must be confirmed and not yet executed.

Emits a TransactionExecuted event.

execute_transaction_batch(calls: Span<Call>, salt: felt252) external

Executes a confirmed batch transaction.

Requirements:

  • The caller must be a registered signer.

  • The transaction must be confirmed and not yet executed.

Emits a TransactionExecuted event.

Events

SignerAdded(signer: ContractAddress) event

Emitted when a new signer is added.

SignerRemoved(signer: ContractAddress) event

Emitted when a signer is removed.

QuorumUpdated(old_quorum: u32, new_quorum: u32) event

Emitted when the quorum value is updated.

TransactionSubmitted(id: TransactionID, signer: ContractAddress) event

Emitted when a new transaction is submitted by a signer.

TransactionConfirmed(id: TransactionID, signer: ContractAddress) event

Emitted when a transaction is confirmed by a signer.

ConfirmationRevoked(id: TransactionID, signer: ContractAddress) event

Emitted when a signer revokes his confirmation.

TransactionExecuted(id: TransactionID) event

Emitted when a transaction is executed.

CallSalt(id: felt252, salt: felt252) event

Emitted when a new transaction is submitted with non-zero salt.

MultisigComponent

use openzeppelin_governance::multisig::MultisigComponent;

Component that implements IMultisig and provides functionality for multisignature wallets, including transaction management, quorum handling, and signer operations.

Embeddable functions

get_quorum(self: @ContractState) → u32 external

Returns the current quorum value.

is_signer(self: @ContractState, signer: ContractAddress) → bool external

Checks if a given signer is registered.

get_signers(self: @ContractState) → Span<ContractAddress> external

Returns a list of all current signers.

is_confirmed(self: @ContractState, id: TransactionID) → bool external

Returns whether the transaction with the given id has been confirmed. A confirmed transaction has received the required number of confirmations (quorum).

is_confirmed_by(self: @ContractState, id: TransactionID, signer: ContractAddress) → bool external

Returns whether the transaction with the given id has been confirmed by the specified signer.

is_executed(self: @ContractState, id: TransactionID) → bool external

Returns whether the transaction with the given id has been executed.

get_submitted_block(self: @ContractState, id: TransactionID) → u64 external

Returns the block number when the transaction with the given id was submitted.

get_transaction_state(self: @ContractState, id: TransactionID) → TransactionState external

Returns the current state of the transaction with the given id.

The possible states are:

  • NotFound: the transaction does not exist.

  • Pending: the transaction exists but hasn’t reached the required confirmations.

  • Confirmed: the transaction has reached the required confirmations but hasn’t been executed.

  • Executed: the transaction has been executed.

get_transaction_confirmations(self: @ContractState, id: TransactionID) → u32 external

Returns the number of confirmations from registered signers for the transaction with the specified id.

hash_transaction(self: @ContractState, to: ContractAddress, selector: felt252, calldata: Span<felt252>, salt: felt252) external

Returns the computed identifier of a transaction containing a single call.

hash_transaction_batch(self: @ContractState, calls: Span<Call>, salt: felt252) external

Returns the computed identifier of a transaction containing a batch of calls.

add_signers(ref self: ContractState, new_quorum: u32, signers_to_add: Span<ContractAddress>) external

Adds new signers and updates the quorum.

Requirements:

  • The caller must be the contract itself.

  • new_quorum must be less than or equal to the total number of signers after addition.

Emits a SignerAdded event for each signer added.

Emits a QuorumUpdated event if the quorum changes.

remove_signers(ref self: ContractState, new_quorum: u32, signers_to_remove: Span<ContractAddress>) external

Removes signers and updates the quorum.

Requirements:

  • The caller must be the contract itself.

  • new_quorum must be less than or equal to the total number of signers after removal.

Emits a SignerRemoved event for each signer removed.

Emits a QuorumUpdated event if the quorum changes.

replace_signer(ref self: ContractState, signer_to_remove: ContractAddress, signer_to_add: ContractAddress) external

Replaces an existing signer with a new signer.

Requirements:

  • The caller must be the contract itself.

  • signer_to_remove must be an existing signer.

  • signer_to_add must not be an existing signer.

Emits a SignerRemoved event for the removed signer.

Emits a SignerAdded event for the new signer.

change_quorum(ref self: ContractState, new_quorum: u32) external

Updates the quorum value to new_quorum.

Requirements:

  • The caller must be the contract itself.

  • new_quorum must be non-zero.

  • new_quorum must be less than or equal to the total number of signers.

Emits a QuorumUpdated event if the quorum changes.

submit_transaction(ref self: ContractState, to: ContractAddress, selector: felt252, calldata: Span<felt252>, salt: felt252) external

Submits a new transaction for confirmation.

Requirements:

  • The caller must be a registered signer.

  • The transaction must not have been submitted before.

Emits a TransactionSubmitted event.

Emits a CallSalt event if salt is not zero.

submit_transaction_batch(ref self: ContractState, calls: Span<Call>, salt: felt252) external

Submits a new batch transaction for confirmation.

Requirements:

  • The caller must be a registered signer.

  • The transaction must not have been submitted before.

Emits a TransactionSubmitted event.

Emits a CallSalt event if salt is not zero.

confirm_transaction(ref self: ContractState, id: TransactionID) external

Confirms a transaction with the given id.

Requirements:

  • The caller must be a registered signer.

  • The transaction must exist and not be executed.

  • The caller must not have already confirmed the transaction.

Emits a TransactionConfirmed event.

revoke_confirmation(ref self: ContractState, id: TransactionID) external

Revokes a previous confirmation for a transaction with the given id.

Requirements:

  • The transaction must exist and not be executed.

  • The caller must have previously confirmed the transaction.

Emits a ConfirmationRevoked event.

execute_transaction(ref self: ContractState, to: ContractAddress, selector: felt252, calldata: Span<felt252>, salt: felt252) external

Executes a confirmed transaction.

Requirements:

  • The caller must be a registered signer.

  • The transaction must be confirmed and not yet executed.

Emits a TransactionExecuted event.

execute_transaction_batch(ref self: ContractState, calls: Span<Call>, salt: felt252) external

Executes a confirmed batch transaction.

Requirements:

  • The caller must be a registered signer.

  • The transaction must be confirmed and not yet executed.

Emits a TransactionExecuted event.

Internal functions

initializer(ref self: ContractState, quorum: u32, signers: Span<ContractAddress>) internal

Initializes the Multisig component with the initial quorum and signers. This function must be called during contract initialization to set up the initial state.

Requirements:

  • quorum must be non-zero and less than or equal to the number of signers.

Emits a SignerAdded event for each signer added.

Emits a QuorumUpdated event.

resolve_tx_state(self: @ContractState, id: TransactionID) → TransactionState internal

Resolves and returns the current state of the transaction with the given id.

The possible states are:

  • NotFound: the transaction does not exist.

  • Pending: the transaction exists but hasn’t reached the required confirmations.

  • Confirmed: the transaction has reached the required confirmations but hasn’t been executed.

  • Executed: the transaction has been executed.

assert_one_of_signers(self: @ContractState, caller: ContractAddress) internal

Asserts that the caller is one of the registered signers.

Requirements:

  • The caller must be a registered signer.

assert_tx_exists(self: @ContractState, id: TransactionID) internal

Asserts that a transaction with the given id exists.

Requirements:

  • The transaction with the given id must have been submitted.

assert_only_self(self: @ContractState) internal

Asserts that the caller is the contract itself.

Requirements:

  • The caller must be the contract’s own address.

_add_signers(ref self: ContractState, new_quorum: u32, signers_to_add: Span<ContractAddress>) internal

Adds new signers and updates the quorum.

Requirements:

  • Each signer address must be non-zero.

  • new_quorum must be non-zero and less than or equal to the total number of signers after addition.

Emits a SignerAdded event for each new signer added.

Emits a QuorumUpdated event if the quorum changes.

_remove_signers(ref self: ContractState, new_quorum: u32, signers_to_remove: Span<ContractAddress>) internal

Removes existing signers and updates the quorum.

Requirements:

  • new_quorum must be non-zero and less than or equal to the total number of signers after removal.

Emits a SignerRemoved event for each signer removed.

Emits a QuorumUpdated event if the quorum changes.

_replace_signer(ref self: ContractState, signer_to_remove: ContractAddress, signer_to_add: ContractAddress) internal

Replaces an existing signer with a new signer.

Requirements:

  • signer_to_remove must be an existing signer.

  • signer_to_add must not be an existing signer.

  • signer_to_add must be a non-zero address.

Emits a SignerRemoved event for the removed signer.

Emits a SignerAdded event for the new signer.

_change_quorum(ref self: ContractState, new_quorum: u32) internal

Updates the quorum value to new_quorum if it differs from the current quorum.

Requirements:

  • new_quorum must be non-zero.

  • new_quorum must be less than or equal to the total number of signers.

Emits a QuorumUpdated event if the quorum changes.

Events

SignerAdded(signer: ContractAddress) event

Emitted when a new signer is added.

SignerRemoved(signer: ContractAddress) event

Emitted when a signer is removed.

QuorumUpdated(old_quorum: u32, new_quorum: u32) event

Emitted when the quorum value is updated.

TransactionSubmitted(id: TransactionID, signer: ContractAddress) event

Emitted when a new transaction is submitted by a signer.

TransactionConfirmed(id: TransactionID, signer: ContractAddress) event

Emitted when a transaction is confirmed by a signer.

ConfirmationRevoked(id: TransactionID, signer: ContractAddress) event

Emitted when a signer revokes his confirmation.

TransactionExecuted(id: TransactionID) event

Emitted when a transaction is executed.

CallSalt(id: felt252, salt: felt252) event

Emitted when a new transaction is submitted with non-zero salt.

Timelock

In a governance system, TimelockControllerComponent is in charge of introducing a delay between a proposal and its execution.

ITimelock

use openzeppelin_governance::timelock::interface::ITimelock;

Interface of a timelock contract.

Functions

is_operation(id: felt252) → bool external

Returns whether id corresponds to a registered operation. This includes the OperationStates: Waiting, Ready, and Done.

is_operation_pending(id: felt252) → bool external

Returns whether the id OperationState is pending or not. Note that a pending operation may be either Waiting or Ready.

is_operation_ready(id: felt252) → bool external

Returns whether the id OperationState is Ready or not.

is_operation_done(id: felt252) → bool external

Returns whether the id OperationState is Done or not.

get_timestamp(id: felt252) → u64 external

Returns the timestamp at which id becomes Ready.

0 means the OperationState is Unset and 1 means the OperationState is Done.

get_operation_state(id: felt252) → OperationState external

Returns the OperationState for id.

get_min_delay() → u64 external

Returns the minimum delay in seconds for an operation to become valid. This value can be changed by executing an operation that calls update_delay.

hash_operation(call: Call, predecessor: felt252, salt: felt252) external

Returns the identifier of an operation containing a single transaction.

hash_operation_batch(calls: Span<Call>, predecessor: felt252, salt: felt252) external

Returns the identifier of an operation containing a batch of transactions.

schedule(call: Call, predecessor: felt252, salt: felt252, delay: u64) external

Schedule an operation containing a single transaction.

Requirements:

  • the caller must have the PROPOSER_ROLE role.

Emits CallScheduled event. If salt is not zero, emits CallSalt event.

schedule_batch(calls: Span<Call>, predecessor: felt252, salt: felt252, delay: u64) external

Schedule an operation containing a batch of transactions.

Requirements:

  • The caller must have the PROPOSER_ROLE role.

Emits one CallScheduled event for each transaction in the batch. If salt is not zero, emits CallSalt event.

cancel(id: felt252) external

Cancel an operation.

Requirements:

  • The caller must have the CANCELLER_ROLE role.

  • id must be an operation.

Emits a CallCancelled event.

execute(call: Call, predecessor: felt252, salt: felt252) external

Execute a (Ready) operation containing a single Call.

Requirements:

  • Caller must have EXECUTOR_ROLE.

  • id must be in Ready OperationState.

  • predecessor must either be 0 or in Done OperationState.

Emits a CallExecuted event.

This function can reenter, but it doesn’t pose a risk because _after_call(self: @ContractState, id: felt252) internal checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught.

execute_batch(calls: Span<Call>, predecessor: felt252, salt: felt252) external

Execute a (Ready) operation containing a batch of Calls.

Requirements:

  • Caller must have EXECUTOR_ROLE.

  • id must be in Ready OperationState.

  • predecessor must either be 0 or in Done OperationState.

Emits a CallExecuted event for each Call.

This function can reenter, but it doesn’t pose a risk because _after_call checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught.

update_delay(new_delay: u64) external

Changes the minimum timelock duration for future operations.

Requirements:

  • The caller must be the timelock itself. This can only be achieved by scheduling and later executing an operation where the timelock is the target and the data is the serialized call to this function.

Emits a MinDelayChanged event.

Events

CallScheduled(id: felt252, index: felt252, call: Call, predecessor: felt252, delay: u64) event

Emitted when call is scheduled as part of operation id.

CallExecuted(id: felt252, index: felt252, call: Call) event

Emitted when call is performed as part of operation id.

CallSalt(id: felt252, salt: felt252) event

Emitted when a new proposal is scheduled with non-zero salt.

CallCancelled(id: felt252) event

Emitted when operation id is cancelled.

MinDelayChanged(old_duration: u64, new_duration: u64) event

Emitted when the minimum delay for future operations is modified.

TimelockControllerComponent

use openzeppelin_governance::timelock::TimelockControllerComponent;

Component that implements ITimelock and enables the implementing contract to act as a timelock controller.

Functions

is_operation(self: @ContractState, id: felt252) → bool external

Returns whether id corresponds to a registered operation. This includes the OperationStates: Waiting, Ready, and Done.

is_operation_pending(self: @ContractState, id: felt252) → bool external

Returns whether the id OperationState is pending or not. Note that a pending operation may be either Waiting or Ready.

is_operation_ready(self: @ContractState, id: felt252) → bool external

Returns whether the id OperationState is Ready or not.

is_operation_done(self: @ContractState, id: felt252) → bool external

Returns whether the id OperationState is Done or not.

get_timestamp(self: @ContractState, id: felt252) → u64 external

Returns the timestamp at which id becomes Ready.

0 means the OperationState is Unset and 1 means the OperationState is Done.

get_operation_state(self: @ContractState, id: felt252) → OperationState external

Returns the OperationState for id.

get_min_delay(self: @ContractState) → u64 external

Returns the minimum delay in seconds for an operation to become valid. This value can be changed by executing an operation that calls update_delay.

hash_operation(self: @ContractState, call: Call, predecessor: felt252, salt: felt252) external

Returns the identifier of an operation containing a single transaction.

hash_operation_batch(self: @ContractState, calls: Span<Call>, predecessor: felt252, salt: felt252) external

Returns the identifier of an operation containing a batch of transactions.

schedule(ref self: ContractState, call: Call, predecessor: felt252, salt: felt252, delay: u64) external

Schedule an operation containing a single transaction.

Requirements:

  • the caller must have the PROPOSER_ROLE role.

Emits CallScheduled event. If salt is not zero, emits CallSalt event.

schedule_batch(ref self: ContractState, calls: Span<Call>, predecessor: felt252, salt: felt252, delay: u64) external

Schedule an operation containing a batch of transactions.

Requirements:

  • The caller must have the PROPOSER_ROLE role.

Emits one CallScheduled event for each transaction in the batch. If salt is not zero, emits CallSalt event.

cancel(ref self: ContractState, id: felt252) external

Cancel an operation.

Requirements:

  • The caller must have the CANCELLER_ROLE role.

  • id must be an operation.

Emits a CallCancelled event.

execute(ref self: ContractState, call: Call, predecessor: felt252, salt: felt252) external

Execute a (Ready) operation containing a single Call.

Requirements:

  • Caller must have EXECUTOR_ROLE.

  • id must be in Ready OperationState.

  • predecessor must either be 0 or in Done OperationState.

Emits a CallExecuted event.

This function can reenter, but it doesn’t pose a risk because _after_call(self: @ContractState, id: felt252) internal checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught.

execute_batch(ref self: ContractState, calls: Span<Call>, predecessor: felt252, salt: felt252) external

Execute a (Ready) operation containing a batch of Calls.

Requirements:

  • Caller must have EXECUTOR_ROLE.

  • id must be in Ready OperationState.

  • predecessor must either be 0 or in Done OperationState.

Emits a CallExecuted event for each Call.

This function can reenter, but it doesn’t pose a risk because _after_call checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught.

update_delay(ref self: ContractState, new_delay: u64) external

Changes the minimum timelock duration for future operations.

Requirements:

  • The caller must be the timelock itself. This can only be achieved by scheduling and later executing an operation where the timelock is the target and the data is the serialized call to this function.

Emits a MinDelayChanged event.

Internal functions

initializer(ref self: ContractState, min_delay: u64, proposers: Span<ContractAddress>, executors: Span<ContractState>, admin: ContractAddress) internal

Initializes the contract by registering support for SRC5 and AccessControl.

This function also configures the contract with the following parameters:

  • min_delay: initial minimum delay in seconds for operations.

  • proposers: accounts to be granted proposer and canceller roles.

  • executors: accounts to be granted executor role.

  • admin: optional account to be granted admin role; disable with zero address.

The optional admin can aid with initial configuration of roles after deployment without being subject to delay, but this role should be subsequently renounced in favor of administration through timelocked proposals.

Emits two IAccessControl::RoleGranted events for each account in proposers with PROPOSER_ROLE and CANCELLER_ROLE roles.

Emits a IAccessControl::RoleGranted event for each account in executors with EXECUTOR_ROLE role.

May emit a IAccessControl::RoleGranted event for admin with DEFAULT_ADMIN_ROLE role (if admin is not zero).

Emits MinDelayChanged event.

assert_only_role(self: @ContractState, role: felt252) internal

Validates that the caller has the given role. Otherwise it panics.

assert_only_role_or_open_role(self: @ContractState, role: felt252) internal

Validates that the caller has the given role. If role is granted to the zero address, then this is considered an open role which allows anyone to be the caller.

assert_only_self(self: @ContractState) internal

Validates that the caller is the timelock contract itself. Otherwise it panics.

_before_call(self: @ContractState, id: felt252, predecessor: felt252) internal

Private function that checks before execution of an operation’s calls.

Requirements:

  • id must be in the Ready OperationState.

  • predecessor must either be zero or be in the Done OperationState.

_after_call(self: @ContractState, id: felt252) internal

Private function that checks after execution of an operation’s calls and sets the OperationState of id to Done.

Requirements:

  • id must be in the Ready OperationState.

_schedule(ref self: ContractState, id: felt252, delay: u64) internal

Private function that schedules an operation that is to become valid after a given delay.

_execute(ref self: ContractState, call: Call) internal

Private function that executes an operation’s calls.

Events

CallScheduled(id: felt252, index: felt252, call: Call, predecessor: felt252, delay: u64) event

Emitted when call is scheduled as part of operation id.

CallExecuted(id: felt252, index: felt252, call: Call) event

Emitted when call is performed as part of operation id.

CallSalt(id: felt252, salt: felt252) event

Emitted when a new proposal is scheduled with non-zero salt.

CallCancelled(id: felt252) event

Emitted when operation id is cancelled.

MinDelayChanged(old_duration: u64, new_duration: u64) event

Emitted when the minimum delay for future operations is modified.

Votes

The VotesComponent provides a flexible system for tracking and delegating voting power. This system allows users to delegate their voting power to other addresses, enabling more active participation in governance.

IVotes

use openzeppelin_governance::votes::interface::IVotes;

Common interface for Votes-enabled contracts.

Functions

get_votes(account: ContractAddress) → u256 external

Returns the current amount of votes that account has.

get_past_votes(account: ContractAddress, timepoint: u64) → u256 external

Returns the amount of votes that account had at a specific moment in the past.

get_past_total_supply(timepoint: u64) → u256 external

Returns the total supply of votes available at a specific moment in the past.

This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. Votes that have not been delegated are still part of total supply, even though they would not participate in a vote.

delegates(account: ContractAddress) → ContractAddress external

Returns the delegate that account has chosen.

delegate(delegatee: ContractAddress) external

Delegates votes from the sender to delegatee.

delegate_by_sig(delegator: ContractAddress, delegatee: ContractAddress, nonce: felt252, expiry: u64, signature: Span<felt252>) external

Delegates votes from delegator to delegatee through a SNIP12 message signature validation.

VotesComponent

use openzeppelin_governance::votes::VotesComponent;

Component that implements the IVotes interface and provides a flexible system for tracking and delegating voting power.

By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked.

When using this module, your contract must implement the VotingUnitsTrait. For convenience, this is done automatically for ERC20 and ERC721 tokens.
Voting Units Trait Implementations

ERC20VotesImpl

get_voting_units(self: @ComponentState<TContractState>, account: ContractAddress) → u256 internal

Returns the number of voting units for a given account.

This implementation is specific to ERC20 tokens, where the balance of tokens directly represents the number of voting units.

This implementation will work out of the box if the ERC20 component is implemented in the final contract.
This implementation assumes tokens map to voting units 1:1. Any deviation from this formula when transferring voting units (e.g. by using hooks) may compromise the internal vote accounting.

ERC721VotesImpl

get_voting_units(self: @ComponentState<TContractState>, account: ContractAddress) → u256 internal

Returns the number of voting units for a given account.

This implementation is specific to ERC721 tokens, where each token represents one voting unit. The function returns the balance of ERC721 tokens for the specified account.

This implementation will work out of the box if the ERC721 component is implemented in the final contract.
This implementation assumes tokens map to voting units 1:1. Any deviation from this formula when transferring voting units (e.g. by using hooks) may compromise the internal vote accounting.

Functions

get_votes(self: @ComponentState<TContractState>, account: ContractAddress) → u256 external

Returns the current amount of votes that account has.

get_past_votes(self: @ComponentState<TContractState>, account: ContractAddress, timepoint: u64) → u256 external

Returns the amount of votes that account had at a specific moment in the past.

Requirements:

  • timepoint must be in the past.

get_past_total_supply(self: @ComponentState<TContractState>, timepoint: u64) → u256 external

Returns the total supply of votes available at a specific moment in the past.

This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. Votes that have not been delegated are still part of total supply, even though they would not participate in a vote.

Requirements:

  • timepoint must be in the past.

delegates(self: @ComponentState<TContractState>, account: ContractAddress) → ContractAddress external

Returns the delegate that account has chosen.

delegate(ref self: ComponentState<TContractState>, delegatee: ContractAddress) external

Delegates votes from the sender to delegatee.

Emits a DelegateChanged event.

May emit one or two DelegateVotesChanged events.

delegate_by_sig(ref self: ComponentState<TContractState>, delegator: ContractAddress, delegatee: ContractAddress, nonce: felt252, expiry: u64, signature: Span<felt252>) external

Delegates votes from delegator to delegatee through a SNIP12 message signature validation.

Requirements:

  • expiry must not be in the past.

  • nonce must match the account’s current nonce.

  • delegator must implement SRC6::is_valid_signature.

  • signature should be valid for the message hash.

Emits a DelegateChanged event.

May emit one or two DelegateVotesChanged events.

Internal functions

get_total_supply(self: @ComponentState<TContractState>) → u256 internal

Returns the current total supply of votes.

move_delegate_votes(ref self: ComponentState<TContractState>, from: ContractAddress, to: ContractAddress, amount: u256) internal

Moves delegated votes from one delegate to another.

May emit one or two DelegateVotesChanged events.

transfer_voting_units(ref self: ComponentState<TContractState>, from: ContractAddress, to: ContractAddress, amount: u256) internal

Transfers, mints, or burns voting units.

To register a mint, from should be zero. To register a burn, to should be zero. Total supply of voting units will be adjusted with mints and burns.

If voting units are based on an underlying transferable asset (like a token), you must call this function every time the asset is transferred to keep the internal voting power accounting in sync. For ERC20 and ERC721 tokens, this is typically handled using hooks.

May emit one or two DelegateVotesChanged events.

num_checkpoints(self: @ComponentState<TContractState>, account: ContractAddress) → u64 internal

Returns the number of checkpoints for account.

checkpoints(self: @ComponentState<TContractState>, account: ContractAddress, pos: u64) → Checkpoint internal

Returns the pos-th checkpoint for account.

_delegate(ref self: ComponentState<TContractState>, account: ContractAddress, delegatee: ContractAddress) internal

Delegates all of account's voting units to delegatee.

Emits a DelegateChanged event.

May emit one or two DelegateVotesChanged events.

Events

DelegateChanged(delegator: ContractAddress, from_delegate: ContractAddress, to_delegate: ContractAddress) event

Emitted when an account changes their delegate.

DelegateVotesChanged(delegate: ContractAddress, previous_votes: u256, new_votes: u256) event

Emitted when a token transfer or delegate change results in changes to a delegate’s number of votes.

VotingUnitsTrait

pub trait VotingUnitsTrait<TState> {
    fn get_voting_units(self: @TState, account: ContractAddress) -> u256;
}

A trait that must be implemented when integrating VotesComponent into a contract. It offers a mechanism to retrieve the number of voting units for a given account at the current time.

Functions

get_voting_units(self: @TState, account: ContractAddress) → u256 external

Returns the number of voting units for a given account. For ERC20, this is typically the token balance. For ERC721, this is typically the number of tokens owned.

While any formula can be used as a measure of voting units, the internal vote accounting of the contract may be compromised if voting units are transferred in any external flow by following a different formula.
For example, when implementing the hook for ERC20, the number of voting units transferred should match the formula given by the get_voting_units implementation.