Skip to content

Commit

Permalink
Erc721 votes and general Votes component (#1114)
Browse files Browse the repository at this point in the history
* votes first iteration

* consolidate votes and delegation to governace module

* fmt

* add ERC20Votes impl, code docs and remove interface for internal iml

* add basic tests and mocks

* refactor, remove unused usings

* cleanup mocks, remove public impl

* refactor

* export VotesComponent for easier use

* remove erc20votes from token module

* fmt

* add more tests

* fmt

* add changelog entry

* add burn tests

* use hooks in mocks

* use crate for local imports

* refactor account names in tests

* improve docs and remove ERC20Votes

* +

* use vec in trace and remove storage array

* add tests for trace & checkpoint

* refactor tests

* fixes

* fmt

* improve docs

* typos

* Update CHANGELOG.md

Co-authored-by: Eric Nordelo <eric.nordelo39@gmail.com>

* +

* +

* ++

* add more tests and refactor

* add missing internal functions and tests

* add VotesABI and format

* docs: trait implementations

* remove debug and partialeq from checkpoint

* refactor hook and improve docs

* +

* +

* improve tests

* fix test

* fmt

* Update CHANGELOG.md

Co-authored-by: Eric Nordelo <eric.nordelo39@gmail.com>

* move _delegate

---------

Co-authored-by: Eric Nordelo <eric.nordelo39@gmail.com>
  • Loading branch information
ggonzalez94 and ericnordelo authored Oct 15, 2024
1 parent 57e6049 commit 6e60ba9
Show file tree
Hide file tree
Showing 37 changed files with 1,738 additions and 1,029 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- `VotesComponent` with implementation for ERC721 and ERC20 tokens (#1114)
- `IUpgradeAndCall` interface (#1148)
- `upgrade_and_call` function in UpgradeableComponent's InternalImpl (#1148)
- `ERC20Permit` impl for `ERC20Component` facilitating token approvals via off-chain signatures (#1140)
Expand All @@ -23,6 +24,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed (Breaking)

- Remove `ERC20Votes` component in favor of `VotesComponent` (#1114)
- `Trace` is now declared as a `storage_node` and now uses `Vec` instead of `StorageArray`.
- `delegate_by_sig` `signature` param in the `IVotes` interface updated from `Array<felt252>` to `Span<felt252>`.
- Remove `StorageArray` from `openzeppelin_utils` (#1114)
- Bump snforge to 0.31.0
- Remove openzeppelin_utils::selectors (#1163)
- Remove `DualCase dispatchers` (#1163)
Expand Down
4 changes: 3 additions & 1 deletion Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ name = "openzeppelin_governance"
version = "0.17.0"
dependencies = [
"openzeppelin_access",
"openzeppelin_account",
"openzeppelin_introspection",
"openzeppelin_test_common",
"openzeppelin_testing",
"openzeppelin_token",
"openzeppelin_utils",
"snforge_std",
]
Expand Down Expand Up @@ -113,6 +115,7 @@ dependencies = [
"openzeppelin_access",
"openzeppelin_account",
"openzeppelin_finance",
"openzeppelin_governance",
"openzeppelin_introspection",
"openzeppelin_security",
"openzeppelin_testing",
Expand All @@ -134,7 +137,6 @@ name = "openzeppelin_token"
version = "0.17.0"
dependencies = [
"openzeppelin_account",
"openzeppelin_governance",
"openzeppelin_introspection",
"openzeppelin_test_common",
"openzeppelin_testing",
Expand Down
196 changes: 1 addition & 195 deletions docs/modules/ROOT/pages/api/erc20.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -456,200 +456,6 @@ See <<IERC20-Transfer,IERC20::Transfer>>.

See <<IERC20-Approval,IERC20::Approval>>.

== Extensions

[.contract]
[[ERC20VotesComponent]]
=== `++ERC20VotesComponent++` link:https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.17.0/packages/token/src/erc20/extensions/erc20_votes.cairo[{github-icon},role=heading-link]

```cairo
use openzeppelin_token::extensions::ERC20VotesComponent;
```

:DelegateChanged: xref:ERC20VotesComponent-DelegateChanged[DelegateChanged]
:DelegateVotesChanged: xref:ERC20VotesComponent-DelegateVotesChanged[DelegateVotesChanged]

Extension of ERC20 to support voting and delegation.

NOTE: Implementing xref:#ERC20Component[ERC20Component] is a requirement for this component to be implemented.

WARNING: To track voting units, this extension requires that the
xref:#ERC20VotesComponent-transfer_voting_units[transfer_voting_units] function is called after every transfer,
mint, or burn operation. For this, the xref:ERC20Component-ERC20HooksTrait[ERC20HooksTrait] must be used.

This extension keeps a history (checkpoints) of each account’s vote power. Vote power can be delegated either by calling
the xref:#ERC20VotesComponent-delegate[delegate] function directly, or by providing a signature to be used with
xref:#ERC20VotesComponent-delegate_by_sig[delegate_by_sig]. Voting power can be queried through the public accessors
xref:#ERC20VotesComponent-get_votes[get_votes] and xref:#ERC20VotesComponent-get_past_votes[get_past_votes].

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.

[.contract-index#ERC20VotesComponent-Embeddable-Impls]
.Embeddable Implementations
--
[.sub-index#ERC20VotesComponent-Embeddable-Impls-ERC20VotesImpl]
.ERC20VotesImpl
* xref:#ERC20VotesComponent-get_votes[`++get_votes(self, account)++`]
* xref:#ERC20VotesComponent-get_past_votes[`++get_past_votes(self, account, timepoint)++`]
* xref:#ERC20VotesComponent-get_past_total_supply[`++get_past_total_supply(self, timepoint)++`]
* xref:#ERC20VotesComponent-delegates[`++delegates(self, account)++`]
* xref:#ERC20VotesComponent-delegate[`++delegate(self, delegatee)++`]
* xref:#ERC20VotesComponent-delegate_by_sig[`++delegate_by_sig(self, delegator, delegatee, nonce, expiry, signature)++`]
--

[.contract-index]
.Internal implementations
--
.InternalImpl
* xref:#ERC20VotesComponent-get_total_supply[`++get_total_supply(self)++`]
* xref:#ERC20VotesComponent-_delegate[`++_delegate(self, account, delegatee)++`]
* xref:#ERC20VotesComponent-move_delegate_votes[`++move_delegate_votes(self, from, to, amount)++`]
* xref:#ERC20VotesComponent-transfer_voting_units[`++transfer_voting_units(self, from, to, amount)++`]
* xref:#ERC20VotesComponent-num_checkpoints[`++num_checkpoints(self, account)++`]
* xref:#ERC20VotesComponent-checkpoints[`++checkpoints(self, account, pos)++`]
* xref:#ERC20VotesComponent-get_voting_units[`++get_voting_units(self, account)++`]
--

[.contract-index]
.Events
--
* xref:#ERC20VotesComponent-DelegateChanged[`++DelegateChanged(delegator, from_delegate, to_delegate)++`]
* xref:#ERC20VotesComponent-DelegateVotesChanged[`++DelegateVotesChanged(delegate, previous_votes, new_votes)++`]
--

[#ERC20VotesComponent-Embeddable-functions]
==== Embeddable functions

[.contract-item]
[[ERC20VotesComponent-get_votes]]
==== `[.contract-item-name]#++get_votes++#++(self: @ContractState, account: ContractAddress) → u256++` [.item-kind]#external#

Returns the current amount of votes that `account` has.

[.contract-item]
[[ERC20VotesComponent-get_past_votes]]
==== `[.contract-item-name]#++get_past_votes++#++(self: @ContractState, account: ContractAddress, timepoint: u64) → u256++` [.item-kind]#external#

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

Requirements:

- `timepoint` must be in the past.

[.contract-item]
[[ERC20VotesComponent-get_past_total_supply]]
==== `[.contract-item-name]#++get_past_total_supply++#++(self: @ContractState, timepoint: u64) → u256++` [.item-kind]#external#

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

NOTE: 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.

[.contract-item]
[[ERC20VotesComponent-delegates]]
==== `[.contract-item-name]#++delegates++#++(self: @ContractState, account: ContractAddress) → ContractAddress++` [.item-kind]#external#

Returns the delegate that `account` has chosen.

[.contract-item]
[[ERC20VotesComponent-delegate]]
==== `[.contract-item-name]#++delegate++#++(ref self: ContractState, delegatee: ContractAddress)++` [.item-kind]#external#

Delegates votes from the caller to `delegatee`.

Emits a {DelegateChanged} event.

May emit one or two {DelegateVotesChanged} events.

[.contract-item]
[[ERC20VotesComponent-delegate_by_sig]]
==== `[.contract-item-name]#++delegate_by_sig++#++(ref self: ContractState, delegator: ContractAddress, delegatee: ContractAddress, nonce: felt252, expiry: u64, signature: Array<felt252>)++` [.item-kind]#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.

[#ERC20VotesComponent-Internal-functions]
==== Internal functions

[.contract-item]
[[ERC20VotesComponent-get_total_supply]]
==== `[.contract-item-name]#++get_total_supply++#++(self: @ContractState) → u256++` [.item-kind]#internal#

Returns the current total supply of votes.

[.contract-item]
[[ERC20VotesComponent-_delegate]]
==== `[.contract-item-name]#++_delegate++#++(ref self: ContractState, account: ContractAddress, delegatee: ContractAddress)++` [.item-kind]#internal#

Delegates all of ``account``'s voting units to `delegatee`.

Emits a {DelegateChanged} event.

May emit one or two {DelegateVotesChanged} events.

[.contract-item]
[[ERC20VotesComponent-move_delegate_votes]]
==== `[.contract-item-name]#++move_delegate_votes++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, amount: u256)++` [.item-kind]#internal#

Moves `amount` of delegated votes from `from` to `to`.

May emit one or two {DelegateVotesChanged} events.

[.contract-item]
[[ERC20VotesComponent-transfer_voting_units]]
==== `[.contract-item-name]#++transfer_voting_units++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, amount: u256)++` [.item-kind]#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.

May emit one or two {DelegateVotesChanged} events.

[.contract-item]
[[ERC20VotesComponent-num_checkpoints]]
==== `[.contract-item-name]#++num_checkpoints++#++(self: @ContractState, account: ContractAddress) → u32++` [.item-kind]#internal#

Returns the number of checkpoints for `account`.

[.contract-item]
[[ERC20VotesComponent-checkpoints]]
==== `[.contract-item-name]#++checkpoints++#++(self: @ContractState, account: ContractAddress, pos: u32) → Checkpoint++` [.item-kind]#internal#

Returns the `pos`-th checkpoint for `account`.

[.contract-item]
[[ERC20VotesComponent-get_voting_units]]
==== `[.contract-item-name]#++get_voting_units++#++(self: @ContractState, account: ContractAddress) → u256++` [.item-kind]#internal#

Returns the voting units of an `account`.

[#ERC20VotesComponent-Events]
==== Events

[.contract-item]
[[ERC20VotesComponent-DelegateChanged]]
==== `[.contract-item-name]#++DelegateChanged++#++(delegator: ContractAddress, from_delegate: ContractAddress, to_delegate: ContractAddress)++` [.item-kind]#event#

Emitted when `delegator` delegates their votes from `from_delegate` to `to_delegate`.

[.contract-item]
[[ERC20VotesComponent-DelegateVotesChanged]]
==== `[.contract-item-name]#++DelegateVotesChanged++#++(delegate: ContractAddress, previous_votes: u256, new_votes: u256)++` [.item-kind]#event#

Emitted when `delegate` votes are updated from `previous_votes` to `new_votes`.

== Presets

[.contract]
Expand Down Expand Up @@ -716,4 +522,4 @@ Upgrades the contract to a new implementation given by `new_class_hash`.
Requirements:

- The caller is the contract owner.
- `new_class_hash` cannot be zero.
- `new_class_hash` cannot be zero.
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/api/finance.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ use openzeppelin_finance::vesting::VestingComponent;
Vesting component implementing the xref:IVesting[`IVesting`] interface.

[.contract-index]
.Vesting Schedule Trait
.Vesting Schedule Trait Implementations
--
.functions
* xref:#VestingComponent-calculate_vested_amount[`++calculate_vested_amount(self, token, total_allocation,
Expand Down
Loading

0 comments on commit 6e60ba9

Please sign in to comment.