diff --git a/packages/core-cairo/src/generate/governor.ts b/packages/core-cairo/src/generate/governor.ts index 8e93d6ac..8ffee9ae 100644 --- a/packages/core-cairo/src/generate/governor.ts +++ b/packages/core-cairo/src/generate/governor.ts @@ -7,8 +7,8 @@ const booleans = [true, false]; const blueprint = { name: ['MyGovernor'], - delay: ['1'], - period: ['1'], + delay: ['1 day'], + period: ['1 week'], proposalThreshold: ['1'], decimals: [18], quorumMode: quorumModeOptions, diff --git a/packages/core-cairo/src/governor.test.ts b/packages/core-cairo/src/governor.test.ts new file mode 100644 index 00000000..5b0e0422 --- /dev/null +++ b/packages/core-cairo/src/governor.test.ts @@ -0,0 +1,180 @@ +import test from 'ava'; +import { governor } from '.'; + +import { buildGovernor, GovernorOptions } from './governor'; +import { printContract } from './print'; + +const NAME = 'MyGovernor'; + +function testGovernor(title: string, opts: Partial) { + test(title, t => { + const c = buildGovernor({ + name: NAME, + delay: '1 day', + period: '1 week', + ...opts, + }); + t.snapshot(printContract(c)); + }); +} + +/** + * Tests external API for equivalence with internal API + */ +function testAPIEquivalence(title: string, opts?: GovernorOptions) { + test(title, t => { + t.is(governor.print(opts), printContract(buildGovernor({ + name: NAME, + delay: '1 day', + period: '1 week', + ...opts, + }))); + }); +} + +testGovernor('basic + upgradeable', { + upgradeable: true +}); + +testGovernor('basic non-upgradeable', { + upgradeable: false +}); + +testGovernor('erc20 votes + timelock', { + votes: 'erc20votes', + timelock: 'openzeppelin', +}); + +testGovernor('erc721 votes + timelock', { + votes: 'erc721votes', + timelock: 'openzeppelin', +}); + +testGovernor('custom name', { + name: 'CustomGovernor', +}); + +testGovernor('custom settings', { + delay: '2 hours', + period: '1 year', + proposalThreshold: '300', + settings: true, +}); + +testGovernor('quorum mode absolute', { + quorumMode: 'absolute', + quorumAbsolute: '200', +}); + +testGovernor('quorum mode percent', { + quorumMode: 'percent', + quorumPercent: 40, +}); + +testGovernor('custom snip12 metadata', { + appName: 'Governor', + appVersion: 'v3', +}); + +testGovernor('all options', { + name: NAME, + delay: '4 day', + period: '4 week', + proposalThreshold: '500', + decimals: 10, + quorumMode: 'absolute', + quorumPercent: 50, + quorumAbsolute: '200', + votes: 'erc721votes', + clockMode: 'timestamp', + timelock: 'openzeppelin', + settings: true, + appName: 'MyApp2', + appVersion: 'v5', + upgradeable: true, +}); + +testAPIEquivalence('API basic + upgradeable', { + name: NAME, + delay: '1 day', + period: '1 week', + upgradeable: true +}); + +testAPIEquivalence('API basic non-upgradeable', { + name: NAME, + delay: '1 day', + period: '1 week', + upgradeable: false +}); + +testAPIEquivalence('API erc20 votes + timelock', { + name: NAME, + delay: '1 day', + period: '1 week', + votes: 'erc20votes', + timelock: 'openzeppelin', +}); + +testAPIEquivalence('API erc721 votes + timelock', { + name: NAME, + delay: '1 day', + period: '1 week', + votes: 'erc721votes', + timelock: 'openzeppelin', +}); + +testAPIEquivalence('API custom name', { + delay: '1 day', + period: '1 week', + name: 'CustomGovernor', +}); + +testAPIEquivalence('API custom settings', { + name: NAME, + delay: '2 hours', + period: '1 year', + proposalThreshold: '300', + settings: true, +}); + +testAPIEquivalence('API quorum mode absolute', { + name: NAME, + delay: '1 day', + period: '1 week', + quorumMode: 'absolute', + quorumAbsolute: '200', +}); + +testAPIEquivalence('API quorum mode percent', { name: NAME, + delay: '1 day', + period: '1 week', + quorumMode: 'percent', + quorumPercent: 40, +}); + +testAPIEquivalence('API custom snip12 metadata', { + name: NAME, + delay: '1 day', + period: '1 week', + appName: 'Governor', + appVersion: 'v3', +}); + +testAPIEquivalence('API all options', { + name: NAME, + delay: '4 day', + period: '4 week', + proposalThreshold: '500', + decimals: 10, + quorumMode: 'absolute', + quorumPercent: 50, + quorumAbsolute: '200', + votes: 'erc721votes', + clockMode: 'timestamp', + timelock: 'openzeppelin', + settings: true, + appName: 'MyApp2', + appVersion: 'v5', + upgradeable: true, +}); diff --git a/packages/core-cairo/src/governor.test.ts.md b/packages/core-cairo/src/governor.test.ts.md new file mode 100644 index 00000000..e4ef8935 --- /dev/null +++ b/packages/core-cairo/src/governor.test.ts.md @@ -0,0 +1,1383 @@ +# Snapshot report for `src/governor.test.ts` + +The actual snapshot is saved in `governor.test.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## basic + upgradeable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.19.0␊ + ␊ + #[starknet::contract]␊ + mod MyGovernor {␊ + use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::extensions::{␊ + GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ + GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ + };␊ + use openzeppelin::governance::governor::extensions::GovernorSettingsComponent::InternalTrait as GovernorSettingsInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorTimelockExecutionComponent::InternalTrait as GovernorTimelockExecutionInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorVotesQuorumFractionComponent::InternalTrait as GovernorVotesQuorumFractionInternalTrait;␊ + use openzeppelin::governance::governor::GovernorComponent::{␊ + InternalExtendedImpl, InternalTrait as GovernorInternalTrait␊ + };␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::{ClassHash, ContractAddress};␊ + ␊ + const QUORUM_NUMERATOR: u256 = 40; /* 4% */␊ + const VOTING_DELAY: u64 = 86400; /* 1 day */␊ + const VOTING_PERIOD: u64 = 604800; /* 1 week */␊ + const PROPOSAL_THRESHOLD: u256 = 0;␊ + ␊ + component!(path: GovernorComponent, storage: governor, event: GovernorEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: GovernorCountingSimpleComponent, storage: governor_counting, event: GovernorCountingSimpleEvent);␊ + component!(path: GovernorVotesQuorumFractionComponent, storage: governor_votes, event: GovernorVotesEvent);␊ + component!(path: GovernorSettingsComponent, storage: governor_settings, event: GovernorSettingsEvent);␊ + component!(path: GovernorTimelockExecutionComponent, storage: governor_timelock_execution, event: GovernorTimelockExecutionEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // Extensions (external)␊ + #[abi(embed_v0)]␊ + impl QuorumFractionImpl = GovernorVotesQuorumFractionComponent::QuorumFractionImpl;␊ + #[abi(embed_v0)]␊ + impl GovernorSettingsAdminImpl = GovernorSettingsComponent::GovernorSettingsAdminImpl;␊ + #[abi(embed_v0)]␊ + impl TimelockedImpl = GovernorTimelockExecutionComponent::TimelockedImpl;␊ + ␊ + // Extensions (internal)␊ + impl GovernorCountingSimpleImpl = GovernorCountingSimpleComponent::GovernorCounting;␊ + impl GovernorQuorumImpl = GovernorVotesQuorumFractionComponent::GovernorQuorum;␊ + impl GovernorVotesImpl = GovernorVotesQuorumFractionComponent::GovernorVotes;␊ + impl GovernorSettingsImpl = GovernorSettingsComponent::GovernorSettings;␊ + impl GovernorTimelockExecutionImpl = GovernorTimelockExecutionComponent::GovernorExecution;␊ + ␊ + // Governor Core␊ + #[abi(embed_v0)]␊ + impl GovernorImpl = GovernorComponent::GovernorImpl;␊ + ␊ + // Internal␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + // SRC5␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + governor: GovernorComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + governor_counting: GovernorCountingSimpleComponent::Storage,␊ + #[substorage(v0)]␊ + governor_votes: GovernorVotesQuorumFractionComponent::Storage,␊ + #[substorage(v0)]␊ + governor_settings: GovernorSettingsComponent::Storage,␊ + #[substorage(v0)]␊ + governor_timelock_execution: GovernorTimelockExecutionComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + GovernorEvent: GovernorComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event,␊ + #[flat]␊ + GovernorVotesEvent: GovernorVotesQuorumFractionComponent::Event,␊ + #[flat]␊ + GovernorSettingsEvent: GovernorSettingsComponent::Event,␊ + #[flat]␊ + GovernorTimelockExecutionEvent: GovernorTimelockExecutionComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + votes_token: ContractAddress,␊ + timelock_controller: ContractAddress,␊ + ) {␊ + self.governor.initializer();␊ + self.governor_votes.initializer(votes_token, QUORUM_NUMERATOR);␊ + self.governor_settings.initializer(VOTING_DELAY, VOTING_PERIOD, PROPOSAL_THRESHOLD);␊ + self.governor_timelock_execution.initializer(timelock_controller);␊ + }␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.governor.assert_only_governance();␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + ␊ + //␊ + // SNIP12 Metadata␊ + //␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'OpenZeppelin Governor'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'v1'␊ + }␊ + }␊ + }␊ + ` + +## basic non-upgradeable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.19.0␊ + ␊ + #[starknet::contract]␊ + mod MyGovernor {␊ + use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::extensions::{␊ + GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ + GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ + };␊ + use openzeppelin::governance::governor::extensions::GovernorSettingsComponent::InternalTrait as GovernorSettingsInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorTimelockExecutionComponent::InternalTrait as GovernorTimelockExecutionInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorVotesQuorumFractionComponent::InternalTrait as GovernorVotesQuorumFractionInternalTrait;␊ + use openzeppelin::governance::governor::GovernorComponent::InternalTrait as GovernorInternalTrait;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::ContractAddress;␊ + ␊ + const QUORUM_NUMERATOR: u256 = 40; /* 4% */␊ + const VOTING_DELAY: u64 = 86400; /* 1 day */␊ + const VOTING_PERIOD: u64 = 604800; /* 1 week */␊ + const PROPOSAL_THRESHOLD: u256 = 0;␊ + ␊ + component!(path: GovernorComponent, storage: governor, event: GovernorEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: GovernorCountingSimpleComponent, storage: governor_counting, event: GovernorCountingSimpleEvent);␊ + component!(path: GovernorVotesQuorumFractionComponent, storage: governor_votes, event: GovernorVotesEvent);␊ + component!(path: GovernorSettingsComponent, storage: governor_settings, event: GovernorSettingsEvent);␊ + component!(path: GovernorTimelockExecutionComponent, storage: governor_timelock_execution, event: GovernorTimelockExecutionEvent);␊ + ␊ + // Extensions (external)␊ + #[abi(embed_v0)]␊ + impl QuorumFractionImpl = GovernorVotesQuorumFractionComponent::QuorumFractionImpl;␊ + #[abi(embed_v0)]␊ + impl GovernorSettingsAdminImpl = GovernorSettingsComponent::GovernorSettingsAdminImpl;␊ + #[abi(embed_v0)]␊ + impl TimelockedImpl = GovernorTimelockExecutionComponent::TimelockedImpl;␊ + ␊ + // Extensions (internal)␊ + impl GovernorCountingSimpleImpl = GovernorCountingSimpleComponent::GovernorCounting;␊ + impl GovernorQuorumImpl = GovernorVotesQuorumFractionComponent::GovernorQuorum;␊ + impl GovernorVotesImpl = GovernorVotesQuorumFractionComponent::GovernorVotes;␊ + impl GovernorSettingsImpl = GovernorSettingsComponent::GovernorSettings;␊ + impl GovernorTimelockExecutionImpl = GovernorTimelockExecutionComponent::GovernorExecution;␊ + ␊ + // Governor Core␊ + #[abi(embed_v0)]␊ + impl GovernorImpl = GovernorComponent::GovernorImpl;␊ + ␊ + // SRC5␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + governor: GovernorComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + governor_counting: GovernorCountingSimpleComponent::Storage,␊ + #[substorage(v0)]␊ + governor_votes: GovernorVotesQuorumFractionComponent::Storage,␊ + #[substorage(v0)]␊ + governor_settings: GovernorSettingsComponent::Storage,␊ + #[substorage(v0)]␊ + governor_timelock_execution: GovernorTimelockExecutionComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + GovernorEvent: GovernorComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event,␊ + #[flat]␊ + GovernorVotesEvent: GovernorVotesQuorumFractionComponent::Event,␊ + #[flat]␊ + GovernorSettingsEvent: GovernorSettingsComponent::Event,␊ + #[flat]␊ + GovernorTimelockExecutionEvent: GovernorTimelockExecutionComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + votes_token: ContractAddress,␊ + timelock_controller: ContractAddress,␊ + ) {␊ + self.governor.initializer();␊ + self.governor_votes.initializer(votes_token, QUORUM_NUMERATOR);␊ + self.governor_settings.initializer(VOTING_DELAY, VOTING_PERIOD, PROPOSAL_THRESHOLD);␊ + self.governor_timelock_execution.initializer(timelock_controller);␊ + }␊ + ␊ + //␊ + // SNIP12 Metadata␊ + //␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'OpenZeppelin Governor'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'v1'␊ + }␊ + }␊ + }␊ + ` + +## erc20 votes + timelock + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.19.0␊ + ␊ + #[starknet::contract]␊ + mod MyGovernor {␊ + use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::extensions::{␊ + GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ + GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ + };␊ + use openzeppelin::governance::governor::extensions::GovernorSettingsComponent::InternalTrait as GovernorSettingsInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorTimelockExecutionComponent::InternalTrait as GovernorTimelockExecutionInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorVotesQuorumFractionComponent::InternalTrait as GovernorVotesQuorumFractionInternalTrait;␊ + use openzeppelin::governance::governor::GovernorComponent::{␊ + InternalExtendedImpl, InternalTrait as GovernorInternalTrait␊ + };␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::{ClassHash, ContractAddress};␊ + ␊ + const QUORUM_NUMERATOR: u256 = 40; /* 4% */␊ + const VOTING_DELAY: u64 = 86400; /* 1 day */␊ + const VOTING_PERIOD: u64 = 604800; /* 1 week */␊ + const PROPOSAL_THRESHOLD: u256 = 0;␊ + ␊ + component!(path: GovernorComponent, storage: governor, event: GovernorEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: GovernorCountingSimpleComponent, storage: governor_counting, event: GovernorCountingSimpleEvent);␊ + component!(path: GovernorVotesQuorumFractionComponent, storage: governor_votes, event: GovernorVotesEvent);␊ + component!(path: GovernorSettingsComponent, storage: governor_settings, event: GovernorSettingsEvent);␊ + component!(path: GovernorTimelockExecutionComponent, storage: governor_timelock_execution, event: GovernorTimelockExecutionEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // Extensions (external)␊ + #[abi(embed_v0)]␊ + impl QuorumFractionImpl = GovernorVotesQuorumFractionComponent::QuorumFractionImpl;␊ + #[abi(embed_v0)]␊ + impl GovernorSettingsAdminImpl = GovernorSettingsComponent::GovernorSettingsAdminImpl;␊ + #[abi(embed_v0)]␊ + impl TimelockedImpl = GovernorTimelockExecutionComponent::TimelockedImpl;␊ + ␊ + // Extensions (internal)␊ + impl GovernorCountingSimpleImpl = GovernorCountingSimpleComponent::GovernorCounting;␊ + impl GovernorQuorumImpl = GovernorVotesQuorumFractionComponent::GovernorQuorum;␊ + impl GovernorVotesImpl = GovernorVotesQuorumFractionComponent::GovernorVotes;␊ + impl GovernorSettingsImpl = GovernorSettingsComponent::GovernorSettings;␊ + impl GovernorTimelockExecutionImpl = GovernorTimelockExecutionComponent::GovernorExecution;␊ + ␊ + // Governor Core␊ + #[abi(embed_v0)]␊ + impl GovernorImpl = GovernorComponent::GovernorImpl;␊ + ␊ + // Internal␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + // SRC5␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + governor: GovernorComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + governor_counting: GovernorCountingSimpleComponent::Storage,␊ + #[substorage(v0)]␊ + governor_votes: GovernorVotesQuorumFractionComponent::Storage,␊ + #[substorage(v0)]␊ + governor_settings: GovernorSettingsComponent::Storage,␊ + #[substorage(v0)]␊ + governor_timelock_execution: GovernorTimelockExecutionComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + GovernorEvent: GovernorComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event,␊ + #[flat]␊ + GovernorVotesEvent: GovernorVotesQuorumFractionComponent::Event,␊ + #[flat]␊ + GovernorSettingsEvent: GovernorSettingsComponent::Event,␊ + #[flat]␊ + GovernorTimelockExecutionEvent: GovernorTimelockExecutionComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + votes_token: ContractAddress,␊ + timelock_controller: ContractAddress,␊ + ) {␊ + self.governor.initializer();␊ + self.governor_votes.initializer(votes_token, QUORUM_NUMERATOR);␊ + self.governor_settings.initializer(VOTING_DELAY, VOTING_PERIOD, PROPOSAL_THRESHOLD);␊ + self.governor_timelock_execution.initializer(timelock_controller);␊ + }␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.governor.assert_only_governance();␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + ␊ + //␊ + // SNIP12 Metadata␊ + //␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'OpenZeppelin Governor'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'v1'␊ + }␊ + }␊ + }␊ + ` + +## erc721 votes + timelock + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.19.0␊ + ␊ + #[starknet::contract]␊ + mod MyGovernor {␊ + use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::extensions::{␊ + GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ + GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ + };␊ + use openzeppelin::governance::governor::extensions::GovernorSettingsComponent::InternalTrait as GovernorSettingsInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorTimelockExecutionComponent::InternalTrait as GovernorTimelockExecutionInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorVotesQuorumFractionComponent::InternalTrait as GovernorVotesQuorumFractionInternalTrait;␊ + use openzeppelin::governance::governor::GovernorComponent::{␊ + InternalExtendedImpl, InternalTrait as GovernorInternalTrait␊ + };␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::{ClassHash, ContractAddress};␊ + ␊ + const QUORUM_NUMERATOR: u256 = 40; /* 4% */␊ + const VOTING_DELAY: u64 = 86400; /* 1 day */␊ + const VOTING_PERIOD: u64 = 604800; /* 1 week */␊ + const PROPOSAL_THRESHOLD: u256 = 0;␊ + ␊ + component!(path: GovernorComponent, storage: governor, event: GovernorEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: GovernorCountingSimpleComponent, storage: governor_counting, event: GovernorCountingSimpleEvent);␊ + component!(path: GovernorVotesQuorumFractionComponent, storage: governor_votes, event: GovernorVotesEvent);␊ + component!(path: GovernorSettingsComponent, storage: governor_settings, event: GovernorSettingsEvent);␊ + component!(path: GovernorTimelockExecutionComponent, storage: governor_timelock_execution, event: GovernorTimelockExecutionEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // Extensions (external)␊ + #[abi(embed_v0)]␊ + impl QuorumFractionImpl = GovernorVotesQuorumFractionComponent::QuorumFractionImpl;␊ + #[abi(embed_v0)]␊ + impl GovernorSettingsAdminImpl = GovernorSettingsComponent::GovernorSettingsAdminImpl;␊ + #[abi(embed_v0)]␊ + impl TimelockedImpl = GovernorTimelockExecutionComponent::TimelockedImpl;␊ + ␊ + // Extensions (internal)␊ + impl GovernorCountingSimpleImpl = GovernorCountingSimpleComponent::GovernorCounting;␊ + impl GovernorQuorumImpl = GovernorVotesQuorumFractionComponent::GovernorQuorum;␊ + impl GovernorVotesImpl = GovernorVotesQuorumFractionComponent::GovernorVotes;␊ + impl GovernorSettingsImpl = GovernorSettingsComponent::GovernorSettings;␊ + impl GovernorTimelockExecutionImpl = GovernorTimelockExecutionComponent::GovernorExecution;␊ + ␊ + // Governor Core␊ + #[abi(embed_v0)]␊ + impl GovernorImpl = GovernorComponent::GovernorImpl;␊ + ␊ + // Internal␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + // SRC5␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + governor: GovernorComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + governor_counting: GovernorCountingSimpleComponent::Storage,␊ + #[substorage(v0)]␊ + governor_votes: GovernorVotesQuorumFractionComponent::Storage,␊ + #[substorage(v0)]␊ + governor_settings: GovernorSettingsComponent::Storage,␊ + #[substorage(v0)]␊ + governor_timelock_execution: GovernorTimelockExecutionComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + GovernorEvent: GovernorComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event,␊ + #[flat]␊ + GovernorVotesEvent: GovernorVotesQuorumFractionComponent::Event,␊ + #[flat]␊ + GovernorSettingsEvent: GovernorSettingsComponent::Event,␊ + #[flat]␊ + GovernorTimelockExecutionEvent: GovernorTimelockExecutionComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + votes_token: ContractAddress,␊ + timelock_controller: ContractAddress,␊ + ) {␊ + self.governor.initializer();␊ + self.governor_votes.initializer(votes_token, QUORUM_NUMERATOR);␊ + self.governor_settings.initializer(VOTING_DELAY, VOTING_PERIOD, PROPOSAL_THRESHOLD);␊ + self.governor_timelock_execution.initializer(timelock_controller);␊ + }␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.governor.assert_only_governance();␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + ␊ + //␊ + // SNIP12 Metadata␊ + //␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'OpenZeppelin Governor'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'v1'␊ + }␊ + }␊ + }␊ + ` + +## custom name + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.19.0␊ + ␊ + #[starknet::contract]␊ + mod CustomGovernor {␊ + use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::extensions::{␊ + GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ + GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ + };␊ + use openzeppelin::governance::governor::extensions::GovernorSettingsComponent::InternalTrait as GovernorSettingsInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorTimelockExecutionComponent::InternalTrait as GovernorTimelockExecutionInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorVotesQuorumFractionComponent::InternalTrait as GovernorVotesQuorumFractionInternalTrait;␊ + use openzeppelin::governance::governor::GovernorComponent::{␊ + InternalExtendedImpl, InternalTrait as GovernorInternalTrait␊ + };␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::{ClassHash, ContractAddress};␊ + ␊ + const QUORUM_NUMERATOR: u256 = 40; /* 4% */␊ + const VOTING_DELAY: u64 = 86400; /* 1 day */␊ + const VOTING_PERIOD: u64 = 604800; /* 1 week */␊ + const PROPOSAL_THRESHOLD: u256 = 0;␊ + ␊ + component!(path: GovernorComponent, storage: governor, event: GovernorEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: GovernorCountingSimpleComponent, storage: governor_counting, event: GovernorCountingSimpleEvent);␊ + component!(path: GovernorVotesQuorumFractionComponent, storage: governor_votes, event: GovernorVotesEvent);␊ + component!(path: GovernorSettingsComponent, storage: governor_settings, event: GovernorSettingsEvent);␊ + component!(path: GovernorTimelockExecutionComponent, storage: governor_timelock_execution, event: GovernorTimelockExecutionEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // Extensions (external)␊ + #[abi(embed_v0)]␊ + impl QuorumFractionImpl = GovernorVotesQuorumFractionComponent::QuorumFractionImpl;␊ + #[abi(embed_v0)]␊ + impl GovernorSettingsAdminImpl = GovernorSettingsComponent::GovernorSettingsAdminImpl;␊ + #[abi(embed_v0)]␊ + impl TimelockedImpl = GovernorTimelockExecutionComponent::TimelockedImpl;␊ + ␊ + // Extensions (internal)␊ + impl GovernorCountingSimpleImpl = GovernorCountingSimpleComponent::GovernorCounting;␊ + impl GovernorQuorumImpl = GovernorVotesQuorumFractionComponent::GovernorQuorum;␊ + impl GovernorVotesImpl = GovernorVotesQuorumFractionComponent::GovernorVotes;␊ + impl GovernorSettingsImpl = GovernorSettingsComponent::GovernorSettings;␊ + impl GovernorTimelockExecutionImpl = GovernorTimelockExecutionComponent::GovernorExecution;␊ + ␊ + // Governor Core␊ + #[abi(embed_v0)]␊ + impl GovernorImpl = GovernorComponent::GovernorImpl;␊ + ␊ + // Internal␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + // SRC5␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + governor: GovernorComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + governor_counting: GovernorCountingSimpleComponent::Storage,␊ + #[substorage(v0)]␊ + governor_votes: GovernorVotesQuorumFractionComponent::Storage,␊ + #[substorage(v0)]␊ + governor_settings: GovernorSettingsComponent::Storage,␊ + #[substorage(v0)]␊ + governor_timelock_execution: GovernorTimelockExecutionComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + GovernorEvent: GovernorComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event,␊ + #[flat]␊ + GovernorVotesEvent: GovernorVotesQuorumFractionComponent::Event,␊ + #[flat]␊ + GovernorSettingsEvent: GovernorSettingsComponent::Event,␊ + #[flat]␊ + GovernorTimelockExecutionEvent: GovernorTimelockExecutionComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + votes_token: ContractAddress,␊ + timelock_controller: ContractAddress,␊ + ) {␊ + self.governor.initializer();␊ + self.governor_votes.initializer(votes_token, QUORUM_NUMERATOR);␊ + self.governor_settings.initializer(VOTING_DELAY, VOTING_PERIOD, PROPOSAL_THRESHOLD);␊ + self.governor_timelock_execution.initializer(timelock_controller);␊ + }␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.governor.assert_only_governance();␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + ␊ + //␊ + // SNIP12 Metadata␊ + //␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'OpenZeppelin Governor'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'v1'␊ + }␊ + }␊ + }␊ + ` + +## custom settings + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.19.0␊ + ␊ + #[starknet::contract]␊ + mod MyGovernor {␊ + use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::extensions::{␊ + GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ + GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ + };␊ + use openzeppelin::governance::governor::extensions::GovernorSettingsComponent::InternalTrait as GovernorSettingsInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorTimelockExecutionComponent::InternalTrait as GovernorTimelockExecutionInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorVotesQuorumFractionComponent::InternalTrait as GovernorVotesQuorumFractionInternalTrait;␊ + use openzeppelin::governance::governor::GovernorComponent::{␊ + InternalExtendedImpl, InternalTrait as GovernorInternalTrait␊ + };␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::{ClassHash, ContractAddress};␊ + ␊ + const QUORUM_NUMERATOR: u256 = 40; /* 4% */␊ + const VOTING_DELAY: u64 = 7200; /* 2 hours */␊ + const VOTING_PERIOD: u64 = 31536000; /* 1 year */␊ + const PROPOSAL_THRESHOLD: u256 = 300e18;␊ + ␊ + component!(path: GovernorComponent, storage: governor, event: GovernorEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: GovernorCountingSimpleComponent, storage: governor_counting, event: GovernorCountingSimpleEvent);␊ + component!(path: GovernorVotesQuorumFractionComponent, storage: governor_votes, event: GovernorVotesEvent);␊ + component!(path: GovernorSettingsComponent, storage: governor_settings, event: GovernorSettingsEvent);␊ + component!(path: GovernorTimelockExecutionComponent, storage: governor_timelock_execution, event: GovernorTimelockExecutionEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // Extensions (external)␊ + #[abi(embed_v0)]␊ + impl QuorumFractionImpl = GovernorVotesQuorumFractionComponent::QuorumFractionImpl;␊ + #[abi(embed_v0)]␊ + impl GovernorSettingsAdminImpl = GovernorSettingsComponent::GovernorSettingsAdminImpl;␊ + #[abi(embed_v0)]␊ + impl TimelockedImpl = GovernorTimelockExecutionComponent::TimelockedImpl;␊ + ␊ + // Extensions (internal)␊ + impl GovernorCountingSimpleImpl = GovernorCountingSimpleComponent::GovernorCounting;␊ + impl GovernorQuorumImpl = GovernorVotesQuorumFractionComponent::GovernorQuorum;␊ + impl GovernorVotesImpl = GovernorVotesQuorumFractionComponent::GovernorVotes;␊ + impl GovernorSettingsImpl = GovernorSettingsComponent::GovernorSettings;␊ + impl GovernorTimelockExecutionImpl = GovernorTimelockExecutionComponent::GovernorExecution;␊ + ␊ + // Governor Core␊ + #[abi(embed_v0)]␊ + impl GovernorImpl = GovernorComponent::GovernorImpl;␊ + ␊ + // Internal␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + // SRC5␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + governor: GovernorComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + governor_counting: GovernorCountingSimpleComponent::Storage,␊ + #[substorage(v0)]␊ + governor_votes: GovernorVotesQuorumFractionComponent::Storage,␊ + #[substorage(v0)]␊ + governor_settings: GovernorSettingsComponent::Storage,␊ + #[substorage(v0)]␊ + governor_timelock_execution: GovernorTimelockExecutionComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + GovernorEvent: GovernorComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event,␊ + #[flat]␊ + GovernorVotesEvent: GovernorVotesQuorumFractionComponent::Event,␊ + #[flat]␊ + GovernorSettingsEvent: GovernorSettingsComponent::Event,␊ + #[flat]␊ + GovernorTimelockExecutionEvent: GovernorTimelockExecutionComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + votes_token: ContractAddress,␊ + timelock_controller: ContractAddress,␊ + ) {␊ + self.governor.initializer();␊ + self.governor_votes.initializer(votes_token, QUORUM_NUMERATOR);␊ + self.governor_settings.initializer(VOTING_DELAY, VOTING_PERIOD, PROPOSAL_THRESHOLD);␊ + self.governor_timelock_execution.initializer(timelock_controller);␊ + }␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.governor.assert_only_governance();␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + ␊ + //␊ + // SNIP12 Metadata␊ + //␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'OpenZeppelin Governor'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'v1'␊ + }␊ + }␊ + }␊ + ` + +## quorum mode absolute + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.19.0␊ + ␊ + #[starknet::contract]␊ + mod MyGovernor {␊ + use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::extensions::{␊ + GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ + GovernorTimelockExecutionComponent, GovernorVotesComponent␊ + };␊ + use openzeppelin::governance::governor::extensions::GovernorSettingsComponent::InternalTrait as GovernorSettingsInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorTimelockExecutionComponent::InternalTrait as GovernorTimelockExecutionInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorVotesComponent::InternalTrait as GovernorVotesInternalTrait;␊ + use openzeppelin::governance::governor::GovernorComponent::{␊ + InternalExtendedImpl, InternalTrait as GovernorInternalTrait␊ + };␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::{ClassHash, ContractAddress};␊ + ␊ + const QUORUM: u256 = 200e18;␊ + const VOTING_DELAY: u64 = 86400; /* 1 day */␊ + const VOTING_PERIOD: u64 = 604800; /* 1 week */␊ + const PROPOSAL_THRESHOLD: u256 = 0;␊ + ␊ + component!(path: GovernorComponent, storage: governor, event: GovernorEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: GovernorCountingSimpleComponent, storage: governor_counting, event: GovernorCountingSimpleEvent);␊ + component!(path: GovernorVotesComponent, storage: governor_votes, event: GovernorVotesEvent);␊ + component!(path: GovernorSettingsComponent, storage: governor_settings, event: GovernorSettingsEvent);␊ + component!(path: GovernorTimelockExecutionComponent, storage: governor_timelock_execution, event: GovernorTimelockExecutionEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // Extensions (external)␊ + #[abi(embed_v0)]␊ + impl VotesTokenImpl = GovernorVotesComponent::VotesTokenImpl;␊ + #[abi(embed_v0)]␊ + impl GovernorSettingsAdminImpl = GovernorSettingsComponent::GovernorSettingsAdminImpl;␊ + #[abi(embed_v0)]␊ + impl TimelockedImpl = GovernorTimelockExecutionComponent::TimelockedImpl;␊ + ␊ + // Extensions (internal)␊ + impl GovernorCountingSimpleImpl = GovernorCountingSimpleComponent::GovernorCounting;␊ + impl GovernorVotesImpl = GovernorVotesComponent::GovernorVotes;␊ + impl GovernorSettingsImpl = GovernorSettingsComponent::GovernorSettings;␊ + impl GovernorTimelockExecutionImpl = GovernorTimelockExecutionComponent::GovernorExecution;␊ + ␊ + // Governor Core␊ + #[abi(embed_v0)]␊ + impl GovernorImpl = GovernorComponent::GovernorImpl;␊ + ␊ + // Internal␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + // SRC5␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + governor: GovernorComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + governor_counting: GovernorCountingSimpleComponent::Storage,␊ + #[substorage(v0)]␊ + governor_votes: GovernorVotesComponent::Storage,␊ + #[substorage(v0)]␊ + governor_settings: GovernorSettingsComponent::Storage,␊ + #[substorage(v0)]␊ + governor_timelock_execution: GovernorTimelockExecutionComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + GovernorEvent: GovernorComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event,␊ + #[flat]␊ + GovernorVotesEvent: GovernorVotesComponent::Event,␊ + #[flat]␊ + GovernorSettingsEvent: GovernorSettingsComponent::Event,␊ + #[flat]␊ + GovernorTimelockExecutionEvent: GovernorTimelockExecutionComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + votes_token: ContractAddress,␊ + timelock_controller: ContractAddress,␊ + ) {␊ + self.governor.initializer();␊ + self.governor_votes.initializer(votes_token);␊ + self.governor_settings.initializer(VOTING_DELAY, VOTING_PERIOD, PROPOSAL_THRESHOLD);␊ + self.governor_timelock_execution.initializer(timelock_controller);␊ + }␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.governor.assert_only_governance();␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + ␊ + //␊ + // Locally implemented extensions␊ + //␊ + ␊ + impl GovernorQuorum of GovernorComponent::GovernorQuorumTrait {␊ + fn quorum(self: @GovernorComponent::ComponentState, timepoint: u64) -> u256 {␊ + QUORUM␊ + }␊ + }␊ + ␊ + //␊ + // SNIP12 Metadata␊ + //␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'OpenZeppelin Governor'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'v1'␊ + }␊ + }␊ + }␊ + ` + +## quorum mode percent + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.19.0␊ + ␊ + #[starknet::contract]␊ + mod MyGovernor {␊ + use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::extensions::{␊ + GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ + GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ + };␊ + use openzeppelin::governance::governor::extensions::GovernorSettingsComponent::InternalTrait as GovernorSettingsInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorTimelockExecutionComponent::InternalTrait as GovernorTimelockExecutionInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorVotesQuorumFractionComponent::InternalTrait as GovernorVotesQuorumFractionInternalTrait;␊ + use openzeppelin::governance::governor::GovernorComponent::{␊ + InternalExtendedImpl, InternalTrait as GovernorInternalTrait␊ + };␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::{ClassHash, ContractAddress};␊ + ␊ + const QUORUM_NUMERATOR: u256 = 400; /* 40% */␊ + const VOTING_DELAY: u64 = 86400; /* 1 day */␊ + const VOTING_PERIOD: u64 = 604800; /* 1 week */␊ + const PROPOSAL_THRESHOLD: u256 = 0;␊ + ␊ + component!(path: GovernorComponent, storage: governor, event: GovernorEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: GovernorCountingSimpleComponent, storage: governor_counting, event: GovernorCountingSimpleEvent);␊ + component!(path: GovernorVotesQuorumFractionComponent, storage: governor_votes, event: GovernorVotesEvent);␊ + component!(path: GovernorSettingsComponent, storage: governor_settings, event: GovernorSettingsEvent);␊ + component!(path: GovernorTimelockExecutionComponent, storage: governor_timelock_execution, event: GovernorTimelockExecutionEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // Extensions (external)␊ + #[abi(embed_v0)]␊ + impl QuorumFractionImpl = GovernorVotesQuorumFractionComponent::QuorumFractionImpl;␊ + #[abi(embed_v0)]␊ + impl GovernorSettingsAdminImpl = GovernorSettingsComponent::GovernorSettingsAdminImpl;␊ + #[abi(embed_v0)]␊ + impl TimelockedImpl = GovernorTimelockExecutionComponent::TimelockedImpl;␊ + ␊ + // Extensions (internal)␊ + impl GovernorCountingSimpleImpl = GovernorCountingSimpleComponent::GovernorCounting;␊ + impl GovernorQuorumImpl = GovernorVotesQuorumFractionComponent::GovernorQuorum;␊ + impl GovernorVotesImpl = GovernorVotesQuorumFractionComponent::GovernorVotes;␊ + impl GovernorSettingsImpl = GovernorSettingsComponent::GovernorSettings;␊ + impl GovernorTimelockExecutionImpl = GovernorTimelockExecutionComponent::GovernorExecution;␊ + ␊ + // Governor Core␊ + #[abi(embed_v0)]␊ + impl GovernorImpl = GovernorComponent::GovernorImpl;␊ + ␊ + // Internal␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + // SRC5␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + governor: GovernorComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + governor_counting: GovernorCountingSimpleComponent::Storage,␊ + #[substorage(v0)]␊ + governor_votes: GovernorVotesQuorumFractionComponent::Storage,␊ + #[substorage(v0)]␊ + governor_settings: GovernorSettingsComponent::Storage,␊ + #[substorage(v0)]␊ + governor_timelock_execution: GovernorTimelockExecutionComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + GovernorEvent: GovernorComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event,␊ + #[flat]␊ + GovernorVotesEvent: GovernorVotesQuorumFractionComponent::Event,␊ + #[flat]␊ + GovernorSettingsEvent: GovernorSettingsComponent::Event,␊ + #[flat]␊ + GovernorTimelockExecutionEvent: GovernorTimelockExecutionComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + votes_token: ContractAddress,␊ + timelock_controller: ContractAddress,␊ + ) {␊ + self.governor.initializer();␊ + self.governor_votes.initializer(votes_token, QUORUM_NUMERATOR);␊ + self.governor_settings.initializer(VOTING_DELAY, VOTING_PERIOD, PROPOSAL_THRESHOLD);␊ + self.governor_timelock_execution.initializer(timelock_controller);␊ + }␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.governor.assert_only_governance();␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + ␊ + //␊ + // SNIP12 Metadata␊ + //␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'OpenZeppelin Governor'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'v1'␊ + }␊ + }␊ + }␊ + ` + +## custom snip12 metadata + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.19.0␊ + ␊ + #[starknet::contract]␊ + mod MyGovernor {␊ + use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::extensions::{␊ + GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ + GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ + };␊ + use openzeppelin::governance::governor::extensions::GovernorSettingsComponent::InternalTrait as GovernorSettingsInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorTimelockExecutionComponent::InternalTrait as GovernorTimelockExecutionInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorVotesQuorumFractionComponent::InternalTrait as GovernorVotesQuorumFractionInternalTrait;␊ + use openzeppelin::governance::governor::GovernorComponent::{␊ + InternalExtendedImpl, InternalTrait as GovernorInternalTrait␊ + };␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::{ClassHash, ContractAddress};␊ + ␊ + const QUORUM_NUMERATOR: u256 = 40; /* 4% */␊ + const VOTING_DELAY: u64 = 86400; /* 1 day */␊ + const VOTING_PERIOD: u64 = 604800; /* 1 week */␊ + const PROPOSAL_THRESHOLD: u256 = 0;␊ + ␊ + component!(path: GovernorComponent, storage: governor, event: GovernorEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: GovernorCountingSimpleComponent, storage: governor_counting, event: GovernorCountingSimpleEvent);␊ + component!(path: GovernorVotesQuorumFractionComponent, storage: governor_votes, event: GovernorVotesEvent);␊ + component!(path: GovernorSettingsComponent, storage: governor_settings, event: GovernorSettingsEvent);␊ + component!(path: GovernorTimelockExecutionComponent, storage: governor_timelock_execution, event: GovernorTimelockExecutionEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // Extensions (external)␊ + #[abi(embed_v0)]␊ + impl QuorumFractionImpl = GovernorVotesQuorumFractionComponent::QuorumFractionImpl;␊ + #[abi(embed_v0)]␊ + impl GovernorSettingsAdminImpl = GovernorSettingsComponent::GovernorSettingsAdminImpl;␊ + #[abi(embed_v0)]␊ + impl TimelockedImpl = GovernorTimelockExecutionComponent::TimelockedImpl;␊ + ␊ + // Extensions (internal)␊ + impl GovernorCountingSimpleImpl = GovernorCountingSimpleComponent::GovernorCounting;␊ + impl GovernorQuorumImpl = GovernorVotesQuorumFractionComponent::GovernorQuorum;␊ + impl GovernorVotesImpl = GovernorVotesQuorumFractionComponent::GovernorVotes;␊ + impl GovernorSettingsImpl = GovernorSettingsComponent::GovernorSettings;␊ + impl GovernorTimelockExecutionImpl = GovernorTimelockExecutionComponent::GovernorExecution;␊ + ␊ + // Governor Core␊ + #[abi(embed_v0)]␊ + impl GovernorImpl = GovernorComponent::GovernorImpl;␊ + ␊ + // Internal␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + // SRC5␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + governor: GovernorComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + governor_counting: GovernorCountingSimpleComponent::Storage,␊ + #[substorage(v0)]␊ + governor_votes: GovernorVotesQuorumFractionComponent::Storage,␊ + #[substorage(v0)]␊ + governor_settings: GovernorSettingsComponent::Storage,␊ + #[substorage(v0)]␊ + governor_timelock_execution: GovernorTimelockExecutionComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + GovernorEvent: GovernorComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event,␊ + #[flat]␊ + GovernorVotesEvent: GovernorVotesQuorumFractionComponent::Event,␊ + #[flat]␊ + GovernorSettingsEvent: GovernorSettingsComponent::Event,␊ + #[flat]␊ + GovernorTimelockExecutionEvent: GovernorTimelockExecutionComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + votes_token: ContractAddress,␊ + timelock_controller: ContractAddress,␊ + ) {␊ + self.governor.initializer();␊ + self.governor_votes.initializer(votes_token, QUORUM_NUMERATOR);␊ + self.governor_settings.initializer(VOTING_DELAY, VOTING_PERIOD, PROPOSAL_THRESHOLD);␊ + self.governor_timelock_execution.initializer(timelock_controller);␊ + }␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.governor.assert_only_governance();␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + ␊ + //␊ + // SNIP12 Metadata␊ + //␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'Governor'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'v3'␊ + }␊ + }␊ + }␊ + ` + +## all options + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.19.0␊ + ␊ + #[starknet::contract]␊ + mod MyGovernor {␊ + use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::extensions::{␊ + GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ + GovernorTimelockExecutionComponent, GovernorVotesComponent␊ + };␊ + use openzeppelin::governance::governor::extensions::GovernorSettingsComponent::InternalTrait as GovernorSettingsInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorTimelockExecutionComponent::InternalTrait as GovernorTimelockExecutionInternalTrait;␊ + use openzeppelin::governance::governor::extensions::GovernorVotesComponent::InternalTrait as GovernorVotesInternalTrait;␊ + use openzeppelin::governance::governor::GovernorComponent::{␊ + InternalExtendedImpl, InternalTrait as GovernorInternalTrait␊ + };␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::{ClassHash, ContractAddress};␊ + ␊ + const QUORUM: u256 = 200;␊ + const VOTING_DELAY: u64 = 345600; /* 4 day */␊ + const VOTING_PERIOD: u64 = 2419200; /* 4 week */␊ + const PROPOSAL_THRESHOLD: u256 = 500;␊ + ␊ + component!(path: GovernorComponent, storage: governor, event: GovernorEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: GovernorCountingSimpleComponent, storage: governor_counting, event: GovernorCountingSimpleEvent);␊ + component!(path: GovernorVotesComponent, storage: governor_votes, event: GovernorVotesEvent);␊ + component!(path: GovernorSettingsComponent, storage: governor_settings, event: GovernorSettingsEvent);␊ + component!(path: GovernorTimelockExecutionComponent, storage: governor_timelock_execution, event: GovernorTimelockExecutionEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // Extensions (external)␊ + #[abi(embed_v0)]␊ + impl VotesTokenImpl = GovernorVotesComponent::VotesTokenImpl;␊ + #[abi(embed_v0)]␊ + impl GovernorSettingsAdminImpl = GovernorSettingsComponent::GovernorSettingsAdminImpl;␊ + #[abi(embed_v0)]␊ + impl TimelockedImpl = GovernorTimelockExecutionComponent::TimelockedImpl;␊ + ␊ + // Extensions (internal)␊ + impl GovernorCountingSimpleImpl = GovernorCountingSimpleComponent::GovernorCounting;␊ + impl GovernorVotesImpl = GovernorVotesComponent::GovernorVotes;␊ + impl GovernorSettingsImpl = GovernorSettingsComponent::GovernorSettings;␊ + impl GovernorTimelockExecutionImpl = GovernorTimelockExecutionComponent::GovernorExecution;␊ + ␊ + // Governor Core␊ + #[abi(embed_v0)]␊ + impl GovernorImpl = GovernorComponent::GovernorImpl;␊ + ␊ + // Internal␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + // SRC5␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + governor: GovernorComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + governor_counting: GovernorCountingSimpleComponent::Storage,␊ + #[substorage(v0)]␊ + governor_votes: GovernorVotesComponent::Storage,␊ + #[substorage(v0)]␊ + governor_settings: GovernorSettingsComponent::Storage,␊ + #[substorage(v0)]␊ + governor_timelock_execution: GovernorTimelockExecutionComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + GovernorEvent: GovernorComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event,␊ + #[flat]␊ + GovernorVotesEvent: GovernorVotesComponent::Event,␊ + #[flat]␊ + GovernorSettingsEvent: GovernorSettingsComponent::Event,␊ + #[flat]␊ + GovernorTimelockExecutionEvent: GovernorTimelockExecutionComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + votes_token: ContractAddress,␊ + timelock_controller: ContractAddress,␊ + ) {␊ + self.governor.initializer();␊ + self.governor_votes.initializer(votes_token);␊ + self.governor_settings.initializer(VOTING_DELAY, VOTING_PERIOD, PROPOSAL_THRESHOLD);␊ + self.governor_timelock_execution.initializer(timelock_controller);␊ + }␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.governor.assert_only_governance();␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + ␊ + //␊ + // Locally implemented extensions␊ + //␊ + ␊ + impl GovernorQuorum of GovernorComponent::GovernorQuorumTrait {␊ + fn quorum(self: @GovernorComponent::ComponentState, timepoint: u64) -> u256 {␊ + QUORUM␊ + }␊ + }␊ + ␊ + //␊ + // SNIP12 Metadata␊ + //␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'MyApp2'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'v5'␊ + }␊ + }␊ + }␊ + ` diff --git a/packages/core-cairo/src/governor.test.ts.snap b/packages/core-cairo/src/governor.test.ts.snap new file mode 100644 index 00000000..484c0722 Binary files /dev/null and b/packages/core-cairo/src/governor.test.ts.snap differ diff --git a/packages/core-cairo/src/governor.ts b/packages/core-cairo/src/governor.ts index e8752048..26257d08 100644 --- a/packages/core-cairo/src/governor.ts +++ b/packages/core-cairo/src/governor.ts @@ -72,10 +72,12 @@ function withDefaults(opts: GovernorOptions): Required { return { ...opts, ...withCommonDefaults(opts), + delay: opts.delay ?? defaults.delay, + period: opts.period ?? defaults.period, + proposalThreshold: opts.proposalThreshold || defaults.proposalThreshold, decimals: opts.decimals ?? defaults.decimals, quorumPercent: opts.quorumPercent ?? defaults.quorumPercent, quorumAbsolute: opts.quorumAbsolute ?? defaults.quorumAbsolute, - proposalThreshold: opts.proposalThreshold || defaults.proposalThreshold, settings: opts.settings ?? defaults.settings, quorumMode: opts.quorumMode ?? defaults.quorumMode, votes: opts.votes ?? defaults.votes, diff --git a/packages/core-cairo/src/test.ts b/packages/core-cairo/src/test.ts index e8651979..a6d7fce7 100644 --- a/packages/core-cairo/src/test.ts +++ b/packages/core-cairo/src/test.ts @@ -63,7 +63,7 @@ test('is access control required', async t => { for (const contract of generateSources('all')) { const regexOwnable = /(use openzeppelin::access::ownable::OwnableComponent)/gm; - if (contract.options.kind !== 'Account' && !contract.options.access) { + if (contract.options.kind !== 'Account' && contract.options.kind !== 'Governor' && !contract.options.access) { if (isAccessControlRequired(contract.options)) { t.regex(contract.source, regexOwnable, JSON.stringify(contract.options)); } else {