diff --git a/.changeset/sixty-turkeys-pay.md b/.changeset/sixty-turkeys-pay.md new file mode 100644 index 000000000..d53b98ef8 --- /dev/null +++ b/.changeset/sixty-turkeys-pay.md @@ -0,0 +1,5 @@ +--- +'@openzeppelin/wizard-common': patch +--- + +Add AI descriptions for AccessControl in Cairo-alpha diff --git a/.github/workflows/compile-cairo-alpha-project.yml b/.github/workflows/compile-cairo-alpha-project.yml index 09cf419cd..d14a6f840 100644 --- a/.github/workflows/compile-cairo-alpha-project.yml +++ b/.github/workflows/compile-cairo-alpha-project.yml @@ -47,15 +47,31 @@ jobs: set -e declare -a all_kinds=("ERC20" "ERC721" "ERC1155" "Account" "Multisig" "Governor" "Vesting" "Custom") - declare -a all_royalty_info_options=("disabled" "enabled_default" "enabled_custom") + declare -a all_access_options=("disabled" "ownable" "roles" "roles-dar-default" "roles-dar-custom") + declare -a all_royalty_options=("disabled" "enabled-default" "enabled-custom") for kind in "${all_kinds[@]}"; do scarb clean if [[ "$kind" == "ERC721" || "$kind" == "ERC1155" ]]; then - for royalty_info_option in "${all_royalty_info_options[@]}"; do - proj_name="'$kind (royalty info: $royalty_info_option)' test project" + for access_option in "${all_access_options[@]}"; do + for royalty_option in "${all_royalty_options[@]}"; do + proj_name="'$kind, access: $access_option, royalty: $royalty_option' test project" + echo "Generating $proj_name..." + yarn run update_scarb_project --kind=$kind --access=$access_option --royalty=$royalty_option + + echo "Compiling $proj_name..." + scarb build + + echo "✅ Compiled $proj_name!" + echo "---------------------------------" + done + done + + elif [[ "$kind" == "ERC20" || "$kind" == "Custom" ]]; then + for access_option in "${all_access_options[@]}"; do + proj_name="'$kind, access: $access_option' test project" echo "Generating $proj_name..." - yarn run update_scarb_project $kind $royalty_info_option + yarn run update_scarb_project --kind=$kind --access=$access_option echo "Compiling $proj_name..." scarb build @@ -68,7 +84,7 @@ jobs: proj_name="'$kind' test project" echo "Generating $proj_name..." - yarn run update_scarb_project $kind + yarn run update_scarb_project --kind=$kind echo "Compiling $proj_name..." scarb build diff --git a/packages/common/src/ai/descriptions/cairo.ts b/packages/common/src/ai/descriptions/cairo.ts index f26b91c8b..7d9e03485 100644 --- a/packages/common/src/ai/descriptions/cairo.ts +++ b/packages/common/src/ai/descriptions/cairo.ts @@ -27,6 +27,14 @@ export const cairoCommonDescriptions = { 'Provides information for how much royalty is owed and to whom, based on a sale price. Follows ERC-2981 standard.', }; +export const cairoAlphaAccessDescriptions = { + accessType: + 'The type of access control to provision. Ownable is a simple mechanism with a single account authorized for all privileged actions. Roles is a flexible mechanism with a separate role for each privileged action. A role can have many authorized accounts. Roles (Default Admin Rules) provides additional enforced security measures on top of standard Roles mechanism for managing the most privileged role: default admin.', + darInitialDelay: 'The initial delay for the default admin role (in case Roles (Default Admin Rules) is used).', + darDefaultDelayIncrease: + 'The default delay increase for the default admin role (in case Roles (Default Admin Rules) is used).', +}; + export const cairoRoyaltyInfoDescriptions = { enabled: 'Whether to enable royalty feature for the contract', defaultRoyaltyFraction: diff --git a/packages/core/cairo_alpha/CHANGELOG.md b/packages/core/cairo_alpha/CHANGELOG.md index 49ae1ef43..f87ec5960 100644 --- a/packages/core/cairo_alpha/CHANGELOG.md +++ b/packages/core/cairo_alpha/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Add AccessControlDefaultAdminRules. ([#698](https://github.com/OpenZeppelin/contracts-wizard/pull/698)) + - **Breaking changes**: - Use OpenZeppelin Contracts for Cairo v3.0.0-alpha.3. ([#688](https://github.com/OpenZeppelin/contracts-wizard/pull/688)) - Use OpenZeppelin Contracts for Cairo v3.0.0-alpha.2. ([#663](https://github.com/OpenZeppelin/contracts-wizard/pull/663)) diff --git a/packages/core/cairo_alpha/src/common-options.ts b/packages/core/cairo_alpha/src/common-options.ts index 5cdee43e0..a179fcffe 100644 --- a/packages/core/cairo_alpha/src/common-options.ts +++ b/packages/core/cairo_alpha/src/common-options.ts @@ -1,5 +1,6 @@ import type { Argument } from './contract'; import type { Access } from './set-access-control'; +import { AccessControl } from './set-access-control'; import type { Info } from './set-info'; import { defaults as infoDefaults } from './set-info'; import type { Upgradeable } from './set-upgradeable'; @@ -11,7 +12,7 @@ export const defaults: Required = { export const contractDefaults: Required = { ...defaults, - access: false, + access: AccessControl.None(), } as const; export interface CommonOptions { diff --git a/packages/core/cairo_alpha/src/custom.test.ts b/packages/core/cairo_alpha/src/custom.test.ts index 9b3059094..81037f5b0 100644 --- a/packages/core/cairo_alpha/src/custom.test.ts +++ b/packages/core/cairo_alpha/src/custom.test.ts @@ -4,6 +4,7 @@ import { custom } from '.'; import type { CustomOptions } from './custom'; import { buildCustom } from './custom'; import { printContract } from './print'; +import { AccessControl, darDefaultOpts, darCustomOpts } from './set-access-control'; function testCustom(title: string, opts: Partial) { test(title, t => { @@ -48,20 +49,28 @@ testCustom('upgradeable', { testCustom('access control disabled', { upgradeable: false, - access: false, + access: AccessControl.None(), }); testCustom('access control ownable', { - access: 'ownable', + access: AccessControl.Ownable(), }); testCustom('access control roles', { - access: 'roles', + access: AccessControl.Roles(), +}); + +testCustom('access control roles default admin rules (default opts)', { + access: AccessControl.RolesDefaultAdminRules(darDefaultOpts), +}); + +testCustom('access control roles default admin rules (custom opts)', { + access: AccessControl.RolesDefaultAdminRules(darCustomOpts), }); testCustom('pausable with access control disabled', { // API should override access to true since it is required for pausable - access: false, + access: AccessControl.None(), pausable: true, upgradeable: false, }); @@ -70,7 +79,7 @@ testAPIEquivalence('custom API default'); testAPIEquivalence('custom API full upgradeable', { name: 'CustomContract', - access: 'roles', + access: AccessControl.Roles(), pausable: true, upgradeable: true, }); diff --git a/packages/core/cairo_alpha/src/custom.test.ts.md b/packages/core/cairo_alpha/src/custom.test.ts.md index c930822c2..c9f9ea133 100644 --- a/packages/core/cairo_alpha/src/custom.test.ts.md +++ b/packages/core/cairo_alpha/src/custom.test.ts.md @@ -380,6 +380,169 @@ Generated by [AVA](https://avajs.dev). }␊ ` +## access control roles default admin rules (default opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyContract {␊ + use openzeppelin::access::accesscontrol::extensions::{␊ + AccessControlDefaultAdminRulesComponent,␊ + DefaultConfig as AccessControlDefaultAdminRulesDefaultConfig␊ + };␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + use super::UPGRADER_ROLE;␊ + ␊ + const INITIAL_DELAY: u64 = 86400; // 1 day␊ + ␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl AccessControlMixinImpl = AccessControlDefaultAdminRulesComponent::AccessControlMixinImpl;␊ + ␊ + // Internal␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + upgrader: ContractAddress,␊ + ) {␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + ␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## access control roles default admin rules (custom opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyContract {␊ + use openzeppelin::access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + use super::UPGRADER_ROLE;␊ + ␊ + const INITIAL_DELAY: u64 = 172800; // 2 days␊ + ␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl AccessControlMixinImpl = AccessControlDefaultAdminRulesComponent::AccessControlMixinImpl;␊ + ␊ + // Internal␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + upgrader: ContractAddress,␊ + ) {␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + ␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + }␊ + ␊ + impl AccessControlDefaultAdminRulesImmutableConfig of AccessControlDefaultAdminRulesComponent::ImmutableConfig {␊ + const DEFAULT_ADMIN_DELAY_INCREASE_WAIT: u64 = 604800; // 1 week␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + ## pausable with access control disabled > Snapshot 1 diff --git a/packages/core/cairo_alpha/src/custom.test.ts.snap b/packages/core/cairo_alpha/src/custom.test.ts.snap index 0735df50d..8762df1bb 100644 Binary files a/packages/core/cairo_alpha/src/custom.test.ts.snap and b/packages/core/cairo_alpha/src/custom.test.ts.snap differ diff --git a/packages/core/cairo_alpha/src/erc1155.test.ts b/packages/core/cairo_alpha/src/erc1155.test.ts index 066e86c4b..96fac1703 100644 --- a/packages/core/cairo_alpha/src/erc1155.test.ts +++ b/packages/core/cairo_alpha/src/erc1155.test.ts @@ -5,6 +5,7 @@ import type { ERC1155Options } from './erc1155'; import { buildERC1155 } from './erc1155'; import { printContract } from './print'; import { royaltyInfoOptions } from './set-royalty-info'; +import { AccessControl, darDefaultOpts, darCustomOpts } from './set-access-control'; const NAME = 'MyToken'; const CUSTOM_NAME = 'CustomToken'; @@ -54,7 +55,7 @@ testERC1155('basic non-upgradeable', { testERC1155('basic', {}); testERC1155('basic + roles', { - access: 'roles', + access: AccessControl.Roles(), }); testERC1155('no updatable uri', { @@ -75,7 +76,17 @@ testERC1155('mintable', { testERC1155('mintable + roles', { mintable: true, - access: 'roles', + access: AccessControl.Roles(), +}); + +testERC1155('mintable + roles DAR (default opts)', { + mintable: true, + access: AccessControl.RolesDefaultAdminRules(darDefaultOpts), +}); + +testERC1155('mintable + roles DAR (custom opts)', { + mintable: true, + access: AccessControl.RolesDefaultAdminRules(darCustomOpts), }); testERC1155('royalty info disabled', { @@ -84,33 +95,72 @@ testERC1155('royalty info disabled', { testERC1155('royalty info enabled default + ownable', { royaltyInfo: royaltyInfoOptions.enabledDefault, - access: 'ownable', + access: AccessControl.Ownable(), }); testERC1155('royalty info enabled default + roles', { royaltyInfo: royaltyInfoOptions.enabledDefault, - access: 'roles', + access: AccessControl.Roles(), +}); + +testERC1155('royalty info enabled default + roles-DAR (custom opts)', { + royaltyInfo: royaltyInfoOptions.enabledDefault, + access: AccessControl.RolesDefaultAdminRules(darCustomOpts), }); testERC1155('royalty info enabled custom + ownable', { royaltyInfo: royaltyInfoOptions.enabledCustom, - access: 'ownable', + access: AccessControl.Ownable(), }); testERC1155('royalty info enabled custom + roles', { royaltyInfo: royaltyInfoOptions.enabledCustom, - access: 'roles', + access: AccessControl.Roles(), +}); + +testERC1155('royalty info enabled custom + roles-DAR (default opts)', { + royaltyInfo: royaltyInfoOptions.enabledCustom, + access: AccessControl.RolesDefaultAdminRules(darDefaultOpts), }); -testERC1155('full non-upgradeable', { +testERC1155('royalty info enabled custom + roles-DAR (custom opts)', { + royaltyInfo: royaltyInfoOptions.enabledCustom, + access: AccessControl.RolesDefaultAdminRules(darCustomOpts), +}); + +testERC1155('full non-upgradeable roles', { ...allFeaturesON, - access: 'roles', + access: AccessControl.Roles(), upgradeable: false, }); -testERC1155('full upgradeable', { +testERC1155('full upgradeable roles', { + ...allFeaturesON, + access: AccessControl.Roles(), + upgradeable: true, +}); + +testERC1155('full non-upgradeable roles-DAR (default opts)', { + ...allFeaturesON, + access: AccessControl.RolesDefaultAdminRules(darDefaultOpts), + upgradeable: false, +}); + +testERC1155('full non-upgradeable roles-DAR (custom opts)', { + ...allFeaturesON, + access: AccessControl.RolesDefaultAdminRules(darCustomOpts), + upgradeable: false, +}); + +testERC1155('full upgradeable roles-DAR (default opts)', { + ...allFeaturesON, + access: AccessControl.RolesDefaultAdminRules(darDefaultOpts), + upgradeable: true, +}); + +testERC1155('full upgradeable roles-DAR (custom opts)', { ...allFeaturesON, - access: 'roles', + access: AccessControl.RolesDefaultAdminRules(darCustomOpts), upgradeable: true, }); @@ -122,7 +172,7 @@ testAPIEquivalence('API full upgradeable', { ...allFeaturesON, name: CUSTOM_NAME, baseUri: BASE_URI, - access: 'roles', + access: AccessControl.Roles(), upgradeable: true, }); diff --git a/packages/core/cairo_alpha/src/erc1155.test.ts.md b/packages/core/cairo_alpha/src/erc1155.test.ts.md index 4dd14cbd8..edb0a57e8 100644 --- a/packages/core/cairo_alpha/src/erc1155.test.ts.md +++ b/packages/core/cairo_alpha/src/erc1155.test.ts.md @@ -879,36 +879,52 @@ Generated by [AVA](https://avajs.dev). }␊ ` -## royalty info disabled +## mintable + roles DAR (default opts) > Snapshot 1 `// SPDX-License-Identifier: MIT␊ // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ ␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ #[starknet::contract]␊ mod MyToken {␊ - use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::access::accesscontrol::extensions::{␊ + AccessControlDefaultAdminRulesComponent,␊ + DefaultConfig as AccessControlDefaultAdminRulesDefaultConfig␊ + };␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use starknet::{ClassHash, ContractAddress};␊ + use super::{MINTER_ROLE, UPGRADER_ROLE, URI_SETTER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 86400; // 1 day␊ ␊ component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ ␊ // External␊ #[abi(embed_v0)]␊ impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl;␊ #[abi(embed_v0)]␊ - impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ ␊ // Internal␊ impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ - impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ ␊ #[storage]␊ @@ -918,7 +934,7 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ src5: SRC5Component::Storage,␊ #[substorage(v0)]␊ - ownable: OwnableComponent::Storage,␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ #[substorage(v0)]␊ upgradeable: UpgradeableComponent::Storage,␊ }␊ @@ -931,23 +947,68 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ SRC5Event: SRC5Component::Event,␊ #[flat]␊ - OwnableEvent: OwnableComponent::Event,␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ #[flat]␊ UpgradeableEvent: UpgradeableComponent::Event,␊ }␊ ␊ #[constructor]␊ - fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + minter: ContractAddress,␊ + uri_setter: ContractAddress,␊ + upgrader: ContractAddress,␊ + ) {␊ self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ - self.ownable.initializer(owner);␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + ␊ + self.accesscontrol_dar._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol_dar._grant_role(URI_SETTER_ROLE, uri_setter);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ }␊ ␊ #[generate_trait]␊ #[abi(per_item)]␊ impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn mint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_id: u256,␊ + value: u256,␊ + data: Span,␊ + ) {␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ + self.erc1155.mint_with_acceptance_check(account, token_id, value, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batch_mint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + data: Span,␊ + ) {␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ + self.erc1155.batch_mint_with_acceptance_check(account, token_ids, values, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batchMint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + tokenIds: Span,␊ + values: Span,␊ + data: Span,␊ + ) {␊ + self.batch_mint(account, tokenIds, values, data);␊ + }␊ + ␊ #[external(v0)]␊ fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ - self.ownable.assert_only_owner();␊ + self.accesscontrol_dar.assert_only_role(URI_SETTER_ROLE);␊ self.erc1155._set_base_uri(base_uri);␊ }␊ ␊ @@ -964,53 +1025,57 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl UpgradeableImpl of IUpgradeable {␊ fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ - self.ownable.assert_only_owner();␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ self.upgradeable.upgrade(new_class_hash);␊ }␊ }␊ }␊ ` -## royalty info enabled default + ownable +## mintable + roles DAR (custom opts) > Snapshot 1 `// SPDX-License-Identifier: MIT␊ // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ ␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ #[starknet::contract]␊ mod MyToken {␊ - use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent;␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ - use openzeppelin::token::common::erc2981::{DefaultConfig, ERC2981Component};␊ use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use starknet::{ClassHash, ContractAddress};␊ + use super::{MINTER_ROLE, UPGRADER_ROLE, URI_SETTER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 172800; // 2 days␊ ␊ component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ - component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ ␊ // External␊ #[abi(embed_v0)]␊ impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl;␊ #[abi(embed_v0)]␊ - impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ #[abi(embed_v0)]␊ - impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ #[abi(embed_v0)]␊ - impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ #[abi(embed_v0)]␊ - impl ERC2981AdminOwnableImpl = ERC2981Component::ERC2981AdminOwnableImpl;␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ ␊ // Internal␊ impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ - impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ - impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ ␊ #[storage]␊ struct Storage {␊ @@ -1019,11 +1084,9 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ src5: SRC5Component::Storage,␊ #[substorage(v0)]␊ - ownable: OwnableComponent::Storage,␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ #[substorage(v0)]␊ upgradeable: UpgradeableComponent::Storage,␊ - #[substorage(v0)]␊ - erc2981: ERC2981Component::Storage,␊ }␊ ␊ #[event]␊ @@ -1034,30 +1097,72 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ SRC5Event: SRC5Component::Event,␊ #[flat]␊ - OwnableEvent: OwnableComponent::Event,␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ #[flat]␊ UpgradeableEvent: UpgradeableComponent::Event,␊ - #[flat]␊ - ERC2981Event: ERC2981Component::Event,␊ }␊ ␊ #[constructor]␊ fn constructor(␊ ref self: ContractState,␊ - owner: ContractAddress,␊ - default_royalty_receiver: ContractAddress,␊ + initial_default_admin: ContractAddress,␊ + minter: ContractAddress,␊ + uri_setter: ContractAddress,␊ + upgrader: ContractAddress,␊ ) {␊ self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ - self.ownable.initializer(owner);␊ - self.erc2981.initializer(default_royalty_receiver, 500);␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + ␊ + self.accesscontrol_dar._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol_dar._grant_role(URI_SETTER_ROLE, uri_setter);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ }␊ ␊ + impl AccessControlDefaultAdminRulesImmutableConfig of AccessControlDefaultAdminRulesComponent::ImmutableConfig {␊ + const DEFAULT_ADMIN_DELAY_INCREASE_WAIT: u64 = 604800; // 1 week␊ + }␊ + ␊ #[generate_trait]␊ #[abi(per_item)]␊ impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn mint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_id: u256,␊ + value: u256,␊ + data: Span,␊ + ) {␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ + self.erc1155.mint_with_acceptance_check(account, token_id, value, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batch_mint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + data: Span,␊ + ) {␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ + self.erc1155.batch_mint_with_acceptance_check(account, token_ids, values, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batchMint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + tokenIds: Span,␊ + values: Span,␊ + data: Span,␊ + ) {␊ + self.batch_mint(account, tokenIds, values, data);␊ + }␊ + ␊ #[external(v0)]␊ fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ - self.ownable.assert_only_owner();␊ + self.accesscontrol_dar.assert_only_role(URI_SETTER_ROLE);␊ self.erc1155._set_base_uri(base_uri);␊ }␊ ␊ @@ -1074,61 +1179,44 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl UpgradeableImpl of IUpgradeable {␊ fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ - self.ownable.assert_only_owner();␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ self.upgradeable.upgrade(new_class_hash);␊ }␊ }␊ }␊ ` -## royalty info enabled default + roles +## royalty info disabled > Snapshot 1 `// SPDX-License-Identifier: MIT␊ // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ ␊ - const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ - const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ - ␊ #[starknet::contract]␊ mod MyToken {␊ - use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE};␊ + use openzeppelin::access::ownable::OwnableComponent;␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ - use openzeppelin::token::common::erc2981::{DefaultConfig, ERC2981Component};␊ use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use starknet::{ClassHash, ContractAddress};␊ - use super::{UPGRADER_ROLE, URI_SETTER_ROLE};␊ ␊ component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ - component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ - component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ ␊ // External␊ #[abi(embed_v0)]␊ impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl;␊ #[abi(embed_v0)]␊ - impl AccessControlImpl = AccessControlComponent::AccessControlImpl;␊ - #[abi(embed_v0)]␊ - impl AccessControlCamelImpl = AccessControlComponent::AccessControlCamelImpl;␊ - #[abi(embed_v0)]␊ - impl AccessControlWithDelayImpl = AccessControlComponent::AccessControlWithDelayImpl;␊ - #[abi(embed_v0)]␊ - impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ - #[abi(embed_v0)]␊ - impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ - #[abi(embed_v0)]␊ - impl ERC2981AdminAccessControlImpl = ERC2981Component::ERC2981AdminAccessControlImpl;␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ ␊ // Internal␊ impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ - impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ - impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ ␊ #[storage]␊ struct Storage {␊ @@ -1137,11 +1225,9 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ src5: SRC5Component::Storage,␊ #[substorage(v0)]␊ - accesscontrol: AccessControlComponent::Storage,␊ + ownable: OwnableComponent::Storage,␊ #[substorage(v0)]␊ upgradeable: UpgradeableComponent::Storage,␊ - #[substorage(v0)]␊ - erc2981: ERC2981Component::Storage,␊ }␊ ␊ #[event]␊ @@ -1152,30 +1238,15 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ SRC5Event: SRC5Component::Event,␊ #[flat]␊ - AccessControlEvent: AccessControlComponent::Event,␊ + OwnableEvent: OwnableComponent::Event,␊ #[flat]␊ UpgradeableEvent: UpgradeableComponent::Event,␊ - #[flat]␊ - ERC2981Event: ERC2981Component::Event,␊ }␊ ␊ #[constructor]␊ - fn constructor(␊ - ref self: ContractState,␊ - default_admin: ContractAddress,␊ - uri_setter: ContractAddress,␊ - upgrader: ContractAddress,␊ - default_royalty_receiver: ContractAddress,␊ - royalty_admin: ContractAddress,␊ - ) {␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ - self.accesscontrol.initializer();␊ - self.erc2981.initializer(default_royalty_receiver, 500);␊ - ␊ - self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ - self.accesscontrol._grant_role(URI_SETTER_ROLE, uri_setter);␊ - self.accesscontrol._grant_role(UPGRADER_ROLE, upgrader);␊ - self.accesscontrol._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + self.ownable.initializer(owner);␊ }␊ ␊ #[generate_trait]␊ @@ -1183,7 +1254,7 @@ Generated by [AVA](https://avajs.dev). impl ExternalImpl of ExternalTrait {␊ #[external(v0)]␊ fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ - self.accesscontrol.assert_only_role(URI_SETTER_ROLE);␊ + self.ownable.assert_only_owner();␊ self.erc1155._set_base_uri(base_uri);␊ }␊ ␊ @@ -1200,14 +1271,14 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl UpgradeableImpl of IUpgradeable {␊ fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ - self.accesscontrol.assert_only_role(UPGRADER_ROLE);␊ + self.ownable.assert_only_owner();␊ self.upgradeable.upgrade(new_class_hash);␊ }␊ }␊ }␊ ` -## royalty info enabled custom + ownable +## royalty info enabled default + ownable > Snapshot 1 @@ -1219,7 +1290,9 @@ Generated by [AVA](https://avajs.dev). use openzeppelin::access::ownable::OwnableComponent;␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ - use openzeppelin::token::common::erc2981::ERC2981Component;␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use starknet::{ClassHash, ContractAddress};␊ @@ -1285,13 +1358,9 @@ Generated by [AVA](https://avajs.dev). ) {␊ self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ self.ownable.initializer(owner);␊ - self.erc2981.initializer(default_royalty_receiver, 15125);␊ + self.erc2981.initializer(default_royalty_receiver, 500);␊ }␊ ␊ - impl ERC2981ImmutableConfig of ERC2981Component::ImmutableConfig {␊ - const FEE_DENOMINATOR: u128 = 100000;␊ - }␊ - ␊ #[generate_trait]␊ #[abi(per_item)]␊ impl ExternalImpl of ExternalTrait {␊ @@ -1321,7 +1390,7 @@ Generated by [AVA](https://avajs.dev). }␊ ` -## royalty info enabled custom + roles +## royalty info enabled default + roles > Snapshot 1 @@ -1336,7 +1405,9 @@ Generated by [AVA](https://avajs.dev). use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE};␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ - use openzeppelin::token::common::erc2981::ERC2981Component;␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use starknet::{ClassHash, ContractAddress};␊ @@ -1410,7 +1481,7 @@ Generated by [AVA](https://avajs.dev). ) {␊ self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ self.accesscontrol.initializer();␊ - self.erc2981.initializer(default_royalty_receiver, 15125);␊ + self.erc2981.initializer(default_royalty_receiver, 500);␊ ␊ self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ self.accesscontrol._grant_role(URI_SETTER_ROLE, uri_setter);␊ @@ -1418,10 +1489,6 @@ Generated by [AVA](https://avajs.dev). self.accesscontrol._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ }␊ ␊ - impl ERC2981ImmutableConfig of ERC2981Component::ImmutableConfig {␊ - const FEE_DENOMINATOR: u128 = 100000;␊ - }␊ - ␊ #[generate_trait]␊ #[abi(per_item)]␊ impl ExternalImpl of ExternalTrait {␊ @@ -1451,55 +1518,59 @@ Generated by [AVA](https://avajs.dev). }␊ ` -## full non-upgradeable +## royalty info enabled default + roles-DAR (custom opts) > Snapshot 1 `// SPDX-License-Identifier: MIT␊ // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ ␊ - const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ - const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ ␊ #[starknet::contract]␊ mod MyToken {␊ - use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE};␊ + use openzeppelin::access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ - use openzeppelin::security::pausable::PausableComponent;␊ - use openzeppelin::token::common::erc2981::{DefaultConfig, ERC2981Component};␊ - use openzeppelin::token::erc1155::ERC1155Component;␊ - use starknet::{ContractAddress, get_caller_address};␊ - use super::{MINTER_ROLE, PAUSER_ROLE, URI_SETTER_ROLE};␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ + use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + use super::{UPGRADER_ROLE, URI_SETTER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 172800; // 2 days␊ ␊ component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ - component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ - component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ ␊ // External␊ #[abi(embed_v0)]␊ impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl;␊ #[abi(embed_v0)]␊ - impl PausableImpl = PausableComponent::PausableImpl;␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ #[abi(embed_v0)]␊ - impl AccessControlImpl = AccessControlComponent::AccessControlImpl;␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ #[abi(embed_v0)]␊ - impl AccessControlCamelImpl = AccessControlComponent::AccessControlCamelImpl;␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ #[abi(embed_v0)]␊ - impl AccessControlWithDelayImpl = AccessControlComponent::AccessControlWithDelayImpl;␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ #[abi(embed_v0)]␊ impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ #[abi(embed_v0)]␊ impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ #[abi(embed_v0)]␊ - impl ERC2981AdminAccessControlImpl = ERC2981Component::ERC2981AdminAccessControlImpl;␊ + impl ERC2981AdminAccessControlDefaultAdminRulesImpl = ERC2981Component::ERC2981AdminAccessControlDefaultAdminRulesImpl;␊ ␊ // Internal␊ impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ - impl PausableInternalImpl = PausableComponent::InternalImpl;␊ - impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ ␊ #[storage]␊ @@ -1509,9 +1580,9 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ src5: SRC5Component::Storage,␊ #[substorage(v0)]␊ - pausable: PausableComponent::Storage,␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ #[substorage(v0)]␊ - accesscontrol: AccessControlComponent::Storage,␊ + upgradeable: UpgradeableComponent::Storage,␊ #[substorage(v0)]␊ erc2981: ERC2981Component::Storage,␊ }␊ @@ -1524,9 +1595,9 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ SRC5Event: SRC5Component::Event,␊ #[flat]␊ - PausableEvent: PausableComponent::Event,␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ #[flat]␊ - AccessControlEvent: AccessControlComponent::Event,␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ #[flat]␊ ERC2981Event: ERC2981Component::Event,␊ }␊ @@ -1534,49 +1605,1374 @@ Generated by [AVA](https://avajs.dev). #[constructor]␊ fn constructor(␊ ref self: ContractState,␊ - default_admin: ContractAddress,␊ - pauser: ContractAddress,␊ - minter: ContractAddress,␊ + initial_default_admin: ContractAddress,␊ uri_setter: ContractAddress,␊ + upgrader: ContractAddress,␊ default_royalty_receiver: ContractAddress,␊ royalty_admin: ContractAddress,␊ ) {␊ self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ - self.accesscontrol.initializer();␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ self.erc2981.initializer(default_royalty_receiver, 500);␊ ␊ - self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ - self.accesscontrol._grant_role(PAUSER_ROLE, pauser);␊ - self.accesscontrol._grant_role(MINTER_ROLE, minter);␊ - self.accesscontrol._grant_role(URI_SETTER_ROLE, uri_setter);␊ - self.accesscontrol._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + self.accesscontrol_dar._grant_role(URI_SETTER_ROLE, uri_setter);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + self.accesscontrol_dar._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ }␊ ␊ - impl ERC1155HooksImpl of ERC1155Component::ERC1155HooksTrait {␊ - fn before_update(␊ - ref self: ERC1155Component::ComponentState,␊ - from: ContractAddress,␊ - to: ContractAddress,␊ - token_ids: Span,␊ - values: Span,␊ - ) {␊ - let contract_state = self.get_contract();␊ - contract_state.pausable.assert_not_paused();␊ - }␊ + impl AccessControlDefaultAdminRulesImmutableConfig of AccessControlDefaultAdminRulesComponent::ImmutableConfig {␊ + const DEFAULT_ADMIN_DELAY_INCREASE_WAIT: u64 = 604800; // 1 week␊ }␊ ␊ #[generate_trait]␊ #[abi(per_item)]␊ impl ExternalImpl of ExternalTrait {␊ #[external(v0)]␊ - fn pause(ref self: ContractState) {␊ - self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ - self.pausable.pause();␊ - }␊ + fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ + self.accesscontrol_dar.assert_only_role(URI_SETTER_ROLE);␊ + self.erc1155._set_base_uri(base_uri);␊ + }␊ + ␊ + #[external(v0)]␊ + fn setBaseUri(ref self: ContractState, baseUri: ByteArray) {␊ + self.set_base_uri(baseUri);␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## royalty info enabled custom + ownable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::common::erc2981::ERC2981Component;␊ + use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + ␊ + component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + #[abi(embed_v0)]␊ + impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981AdminOwnableImpl = ERC2981Component::ERC2981AdminOwnableImpl;␊ + ␊ + // Internal␊ + impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc1155: ERC1155Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + erc2981: ERC2981Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC1155Event: ERC1155Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + ERC2981Event: ERC2981Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + owner: ContractAddress,␊ + default_royalty_receiver: ContractAddress,␊ + ) {␊ + self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ + self.ownable.initializer(owner);␊ + self.erc2981.initializer(default_royalty_receiver, 15125);␊ + }␊ + ␊ + impl ERC2981ImmutableConfig of ERC2981Component::ImmutableConfig {␊ + const FEE_DENOMINATOR: u128 = 100000;␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ + self.ownable.assert_only_owner();␊ + self.erc1155._set_base_uri(base_uri);␊ + }␊ + ␊ + #[external(v0)]␊ + fn setBaseUri(ref self: ContractState, baseUri: ByteArray) {␊ + self.set_base_uri(baseUri);␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.ownable.assert_only_owner();␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## royalty info enabled custom + roles + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE};␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::common::erc2981::ERC2981Component;␊ + use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + use super::{UPGRADER_ROLE, URI_SETTER_ROLE};␊ + ␊ + component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlComponent::AccessControlWithDelayImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + #[abi(embed_v0)]␊ + impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981AdminAccessControlImpl = ERC2981Component::ERC2981AdminAccessControlImpl;␊ + ␊ + // Internal␊ + impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc1155: ERC1155Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + accesscontrol: AccessControlComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + erc2981: ERC2981Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC1155Event: ERC1155Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + AccessControlEvent: AccessControlComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + ERC2981Event: ERC2981Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + default_admin: ContractAddress,␊ + uri_setter: ContractAddress,␊ + upgrader: ContractAddress,␊ + default_royalty_receiver: ContractAddress,␊ + royalty_admin: ContractAddress,␊ + ) {␊ + self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ + self.accesscontrol.initializer();␊ + self.erc2981.initializer(default_royalty_receiver, 15125);␊ + ␊ + self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ + self.accesscontrol._grant_role(URI_SETTER_ROLE, uri_setter);␊ + self.accesscontrol._grant_role(UPGRADER_ROLE, upgrader);␊ + self.accesscontrol._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + }␊ + ␊ + impl ERC2981ImmutableConfig of ERC2981Component::ImmutableConfig {␊ + const FEE_DENOMINATOR: u128 = 100000;␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ + self.accesscontrol.assert_only_role(URI_SETTER_ROLE);␊ + self.erc1155._set_base_uri(base_uri);␊ + }␊ + ␊ + #[external(v0)]␊ + fn setBaseUri(ref self: ContractState, baseUri: ByteArray) {␊ + self.set_base_uri(baseUri);␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## royalty info enabled custom + roles-DAR (default opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::extensions::{␊ + AccessControlDefaultAdminRulesComponent,␊ + DefaultConfig as AccessControlDefaultAdminRulesDefaultConfig␊ + };␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::common::erc2981::ERC2981Component;␊ + use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + use super::{UPGRADER_ROLE, URI_SETTER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 86400; // 1 day␊ + ␊ + component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + #[abi(embed_v0)]␊ + impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981AdminAccessControlDefaultAdminRulesImpl = ERC2981Component::ERC2981AdminAccessControlDefaultAdminRulesImpl;␊ + ␊ + // Internal␊ + impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc1155: ERC1155Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + erc2981: ERC2981Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC1155Event: ERC1155Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + ERC2981Event: ERC2981Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + uri_setter: ContractAddress,␊ + upgrader: ContractAddress,␊ + default_royalty_receiver: ContractAddress,␊ + royalty_admin: ContractAddress,␊ + ) {␊ + self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + self.erc2981.initializer(default_royalty_receiver, 15125);␊ + ␊ + self.accesscontrol_dar._grant_role(URI_SETTER_ROLE, uri_setter);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + self.accesscontrol_dar._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + }␊ + ␊ + impl ERC2981ImmutableConfig of ERC2981Component::ImmutableConfig {␊ + const FEE_DENOMINATOR: u128 = 100000;␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ + self.accesscontrol_dar.assert_only_role(URI_SETTER_ROLE);␊ + self.erc1155._set_base_uri(base_uri);␊ + }␊ + ␊ + #[external(v0)]␊ + fn setBaseUri(ref self: ContractState, baseUri: ByteArray) {␊ + self.set_base_uri(baseUri);␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## royalty info enabled custom + roles-DAR (custom opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::common::erc2981::ERC2981Component;␊ + use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + use super::{UPGRADER_ROLE, URI_SETTER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 172800; // 2 days␊ + ␊ + component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + #[abi(embed_v0)]␊ + impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981AdminAccessControlDefaultAdminRulesImpl = ERC2981Component::ERC2981AdminAccessControlDefaultAdminRulesImpl;␊ + ␊ + // Internal␊ + impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc1155: ERC1155Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + erc2981: ERC2981Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC1155Event: ERC1155Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + ERC2981Event: ERC2981Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + uri_setter: ContractAddress,␊ + upgrader: ContractAddress,␊ + default_royalty_receiver: ContractAddress,␊ + royalty_admin: ContractAddress,␊ + ) {␊ + self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + self.erc2981.initializer(default_royalty_receiver, 15125);␊ + ␊ + self.accesscontrol_dar._grant_role(URI_SETTER_ROLE, uri_setter);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + self.accesscontrol_dar._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + }␊ + ␊ + impl AccessControlDefaultAdminRulesImmutableConfig of AccessControlDefaultAdminRulesComponent::ImmutableConfig {␊ + const DEFAULT_ADMIN_DELAY_INCREASE_WAIT: u64 = 604800; // 1 week␊ + }␊ + ␊ + impl ERC2981ImmutableConfig of ERC2981Component::ImmutableConfig {␊ + const FEE_DENOMINATOR: u128 = 100000;␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ + self.accesscontrol_dar.assert_only_role(URI_SETTER_ROLE);␊ + self.erc1155._set_base_uri(base_uri);␊ + }␊ + ␊ + #[external(v0)]␊ + fn setBaseUri(ref self: ContractState, baseUri: ByteArray) {␊ + self.set_base_uri(baseUri);␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## full non-upgradeable roles + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE};␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ + use openzeppelin::token::erc1155::ERC1155Component;␊ + use starknet::{ContractAddress, get_caller_address};␊ + use super::{MINTER_ROLE, PAUSER_ROLE, URI_SETTER_ROLE};␊ + ␊ + component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl;␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlComponent::AccessControlWithDelayImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + #[abi(embed_v0)]␊ + impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981AdminAccessControlImpl = ERC2981Component::ERC2981AdminAccessControlImpl;␊ + ␊ + // Internal␊ + impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc1155: ERC1155Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + accesscontrol: AccessControlComponent::Storage,␊ + #[substorage(v0)]␊ + erc2981: ERC2981Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC1155Event: ERC1155Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + AccessControlEvent: AccessControlComponent::Event,␊ + #[flat]␊ + ERC2981Event: ERC2981Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + default_admin: ContractAddress,␊ + pauser: ContractAddress,␊ + minter: ContractAddress,␊ + uri_setter: ContractAddress,␊ + default_royalty_receiver: ContractAddress,␊ + royalty_admin: ContractAddress,␊ + ) {␊ + self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ + self.accesscontrol.initializer();␊ + self.erc2981.initializer(default_royalty_receiver, 500);␊ + ␊ + self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ + self.accesscontrol._grant_role(PAUSER_ROLE, pauser);␊ + self.accesscontrol._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol._grant_role(URI_SETTER_ROLE, uri_setter);␊ + self.accesscontrol._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + }␊ + ␊ + impl ERC1155HooksImpl of ERC1155Component::ERC1155HooksTrait {␊ + fn before_update(␊ + ref self: ERC1155Component::ComponentState,␊ + from: ContractAddress,␊ + to: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + ) {␊ + let contract_state = self.get_contract();␊ + contract_state.pausable.assert_not_paused();␊ + }␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn pause(ref self: ContractState) {␊ + self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ + self.pausable.pause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn unpause(ref self: ContractState) {␊ + self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ + self.pausable.unpause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn burn(ref self: ContractState, account: ContractAddress, token_id: u256, value: u256) {␊ + let caller = get_caller_address();␊ + if account != caller {␊ + assert(self.erc1155.is_approved_for_all(account, caller), ERC1155Component::Errors::UNAUTHORIZED);␊ + }␊ + self.erc1155.burn(account, token_id, value);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batch_burn(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + ) {␊ + let caller = get_caller_address();␊ + if account != caller {␊ + assert(self.erc1155.is_approved_for_all(account, caller), ERC1155Component::Errors::UNAUTHORIZED);␊ + }␊ + self.erc1155.batch_burn(account, token_ids, values);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batchBurn(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + tokenIds: Span,␊ + values: Span,␊ + ) {␊ + self.batch_burn(account, tokenIds, values);␊ + }␊ + ␊ + #[external(v0)]␊ + fn mint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_id: u256,␊ + value: u256,␊ + data: Span,␊ + ) {␊ + self.accesscontrol.assert_only_role(MINTER_ROLE);␊ + self.erc1155.mint_with_acceptance_check(account, token_id, value, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batch_mint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + data: Span,␊ + ) {␊ + self.accesscontrol.assert_only_role(MINTER_ROLE);␊ + self.erc1155.batch_mint_with_acceptance_check(account, token_ids, values, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batchMint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + tokenIds: Span,␊ + values: Span,␊ + data: Span,␊ + ) {␊ + self.batch_mint(account, tokenIds, values, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ + self.accesscontrol.assert_only_role(URI_SETTER_ROLE);␊ + self.erc1155._set_base_uri(base_uri);␊ + }␊ + ␊ + #[external(v0)]␊ + fn setBaseUri(ref self: ContractState, baseUri: ByteArray) {␊ + self.set_base_uri(baseUri);␊ + }␊ + }␊ + }␊ + ` + +## full upgradeable roles + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE};␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ + use openzeppelin::token::erc1155::ERC1155Component;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress, get_caller_address};␊ + use super::{MINTER_ROLE, PAUSER_ROLE, UPGRADER_ROLE, URI_SETTER_ROLE};␊ + ␊ + component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl;␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlComponent::AccessControlWithDelayImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + #[abi(embed_v0)]␊ + impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981AdminAccessControlImpl = ERC2981Component::ERC2981AdminAccessControlImpl;␊ + ␊ + // Internal␊ + impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc1155: ERC1155Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + accesscontrol: AccessControlComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + erc2981: ERC2981Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC1155Event: ERC1155Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + AccessControlEvent: AccessControlComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + ERC2981Event: ERC2981Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + default_admin: ContractAddress,␊ + pauser: ContractAddress,␊ + minter: ContractAddress,␊ + uri_setter: ContractAddress,␊ + upgrader: ContractAddress,␊ + default_royalty_receiver: ContractAddress,␊ + royalty_admin: ContractAddress,␊ + ) {␊ + self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ + self.accesscontrol.initializer();␊ + self.erc2981.initializer(default_royalty_receiver, 500);␊ + ␊ + self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ + self.accesscontrol._grant_role(PAUSER_ROLE, pauser);␊ + self.accesscontrol._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol._grant_role(URI_SETTER_ROLE, uri_setter);␊ + self.accesscontrol._grant_role(UPGRADER_ROLE, upgrader);␊ + self.accesscontrol._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + }␊ + ␊ + impl ERC1155HooksImpl of ERC1155Component::ERC1155HooksTrait {␊ + fn before_update(␊ + ref self: ERC1155Component::ComponentState,␊ + from: ContractAddress,␊ + to: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + ) {␊ + let contract_state = self.get_contract();␊ + contract_state.pausable.assert_not_paused();␊ + }␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn pause(ref self: ContractState) {␊ + self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ + self.pausable.pause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn unpause(ref self: ContractState) {␊ + self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ + self.pausable.unpause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn burn(ref self: ContractState, account: ContractAddress, token_id: u256, value: u256) {␊ + let caller = get_caller_address();␊ + if account != caller {␊ + assert(self.erc1155.is_approved_for_all(account, caller), ERC1155Component::Errors::UNAUTHORIZED);␊ + }␊ + self.erc1155.burn(account, token_id, value);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batch_burn(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + ) {␊ + let caller = get_caller_address();␊ + if account != caller {␊ + assert(self.erc1155.is_approved_for_all(account, caller), ERC1155Component::Errors::UNAUTHORIZED);␊ + }␊ + self.erc1155.batch_burn(account, token_ids, values);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batchBurn(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + tokenIds: Span,␊ + values: Span,␊ + ) {␊ + self.batch_burn(account, tokenIds, values);␊ + }␊ + ␊ + #[external(v0)]␊ + fn mint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_id: u256,␊ + value: u256,␊ + data: Span,␊ + ) {␊ + self.accesscontrol.assert_only_role(MINTER_ROLE);␊ + self.erc1155.mint_with_acceptance_check(account, token_id, value, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batch_mint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + data: Span,␊ + ) {␊ + self.accesscontrol.assert_only_role(MINTER_ROLE);␊ + self.erc1155.batch_mint_with_acceptance_check(account, token_ids, values, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batchMint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + tokenIds: Span,␊ + values: Span,␊ + data: Span,␊ + ) {␊ + self.batch_mint(account, tokenIds, values, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ + self.accesscontrol.assert_only_role(URI_SETTER_ROLE);␊ + self.erc1155._set_base_uri(base_uri);␊ + }␊ + ␊ + #[external(v0)]␊ + fn setBaseUri(ref self: ContractState, baseUri: ByteArray) {␊ + self.set_base_uri(baseUri);␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## full non-upgradeable roles-DAR (default opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::extensions::{␊ + AccessControlDefaultAdminRulesComponent,␊ + DefaultConfig as AccessControlDefaultAdminRulesDefaultConfig␊ + };␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ + use openzeppelin::token::erc1155::ERC1155Component;␊ + use starknet::{ContractAddress, get_caller_address};␊ + use super::{MINTER_ROLE, PAUSER_ROLE, URI_SETTER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 86400; // 1 day␊ + ␊ + component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl;␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + #[abi(embed_v0)]␊ + impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981AdminAccessControlDefaultAdminRulesImpl = ERC2981Component::ERC2981AdminAccessControlDefaultAdminRulesImpl;␊ + ␊ + // Internal␊ + impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc1155: ERC1155Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + erc2981: ERC2981Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC1155Event: ERC1155Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + ERC2981Event: ERC2981Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + pauser: ContractAddress,␊ + minter: ContractAddress,␊ + uri_setter: ContractAddress,␊ + default_royalty_receiver: ContractAddress,␊ + royalty_admin: ContractAddress,␊ + ) {␊ + self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + self.erc2981.initializer(default_royalty_receiver, 500);␊ + ␊ + self.accesscontrol_dar._grant_role(PAUSER_ROLE, pauser);␊ + self.accesscontrol_dar._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol_dar._grant_role(URI_SETTER_ROLE, uri_setter);␊ + self.accesscontrol_dar._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + }␊ + ␊ + impl ERC1155HooksImpl of ERC1155Component::ERC1155HooksTrait {␊ + fn before_update(␊ + ref self: ERC1155Component::ComponentState,␊ + from: ContractAddress,␊ + to: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + ) {␊ + let contract_state = self.get_contract();␊ + contract_state.pausable.assert_not_paused();␊ + }␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn pause(ref self: ContractState) {␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ + self.pausable.pause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn unpause(ref self: ContractState) {␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ + self.pausable.unpause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn burn(ref self: ContractState, account: ContractAddress, token_id: u256, value: u256) {␊ + let caller = get_caller_address();␊ + if account != caller {␊ + assert(self.erc1155.is_approved_for_all(account, caller), ERC1155Component::Errors::UNAUTHORIZED);␊ + }␊ + self.erc1155.burn(account, token_id, value);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batch_burn(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + ) {␊ + let caller = get_caller_address();␊ + if account != caller {␊ + assert(self.erc1155.is_approved_for_all(account, caller), ERC1155Component::Errors::UNAUTHORIZED);␊ + }␊ + self.erc1155.batch_burn(account, token_ids, values);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batchBurn(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + tokenIds: Span,␊ + values: Span,␊ + ) {␊ + self.batch_burn(account, tokenIds, values);␊ + }␊ + ␊ + #[external(v0)]␊ + fn mint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_id: u256,␊ + value: u256,␊ + data: Span,␊ + ) {␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ + self.erc1155.mint_with_acceptance_check(account, token_id, value, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batch_mint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + data: Span,␊ + ) {␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ + self.erc1155.batch_mint_with_acceptance_check(account, token_ids, values, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batchMint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + tokenIds: Span,␊ + values: Span,␊ + data: Span,␊ + ) {␊ + self.batch_mint(account, tokenIds, values, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ + self.accesscontrol_dar.assert_only_role(URI_SETTER_ROLE);␊ + self.erc1155._set_base_uri(base_uri);␊ + }␊ + ␊ + #[external(v0)]␊ + fn setBaseUri(ref self: ContractState, baseUri: ByteArray) {␊ + self.set_base_uri(baseUri);␊ + }␊ + }␊ + }␊ + ` + +## full non-upgradeable roles-DAR (custom opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ + use openzeppelin::token::erc1155::ERC1155Component;␊ + use starknet::{ContractAddress, get_caller_address};␊ + use super::{MINTER_ROLE, PAUSER_ROLE, URI_SETTER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 172800; // 2 days␊ + ␊ + component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl;␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + #[abi(embed_v0)]␊ + impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981AdminAccessControlDefaultAdminRulesImpl = ERC2981Component::ERC2981AdminAccessControlDefaultAdminRulesImpl;␊ + ␊ + // Internal␊ + impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc1155: ERC1155Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + erc2981: ERC2981Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC1155Event: ERC1155Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + ERC2981Event: ERC2981Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + pauser: ContractAddress,␊ + minter: ContractAddress,␊ + uri_setter: ContractAddress,␊ + default_royalty_receiver: ContractAddress,␊ + royalty_admin: ContractAddress,␊ + ) {␊ + self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + self.erc2981.initializer(default_royalty_receiver, 500);␊ + ␊ + self.accesscontrol_dar._grant_role(PAUSER_ROLE, pauser);␊ + self.accesscontrol_dar._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol_dar._grant_role(URI_SETTER_ROLE, uri_setter);␊ + self.accesscontrol_dar._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + }␊ + ␊ + impl ERC1155HooksImpl of ERC1155Component::ERC1155HooksTrait {␊ + fn before_update(␊ + ref self: ERC1155Component::ComponentState,␊ + from: ContractAddress,␊ + to: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + ) {␊ + let contract_state = self.get_contract();␊ + contract_state.pausable.assert_not_paused();␊ + }␊ + }␊ + ␊ + impl AccessControlDefaultAdminRulesImmutableConfig of AccessControlDefaultAdminRulesComponent::ImmutableConfig {␊ + const DEFAULT_ADMIN_DELAY_INCREASE_WAIT: u64 = 604800; // 1 week␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn pause(ref self: ContractState) {␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ + self.pausable.pause();␊ + }␊ ␊ #[external(v0)]␊ fn unpause(ref self: ContractState) {␊ - self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ self.pausable.unpause();␊ }␊ ␊ @@ -1621,7 +3017,7 @@ Generated by [AVA](https://avajs.dev). value: u256,␊ data: Span,␊ ) {␊ - self.accesscontrol.assert_only_role(MINTER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ self.erc1155.mint_with_acceptance_check(account, token_id, value, data);␊ }␊ ␊ @@ -1633,7 +3029,7 @@ Generated by [AVA](https://avajs.dev). values: Span,␊ data: Span,␊ ) {␊ - self.accesscontrol.assert_only_role(MINTER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ self.erc1155.batch_mint_with_acceptance_check(account, token_ids, values, data);␊ }␊ ␊ @@ -1650,7 +3046,7 @@ Generated by [AVA](https://avajs.dev). ␊ #[external(v0)]␊ fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ - self.accesscontrol.assert_only_role(URI_SETTER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(URI_SETTER_ROLE);␊ self.erc1155._set_base_uri(base_uri);␊ }␊ ␊ @@ -1662,7 +3058,7 @@ Generated by [AVA](https://avajs.dev). }␊ ` -## full upgradeable +## full upgradeable roles-DAR (default opts) > Snapshot 1 @@ -1676,20 +3072,27 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyToken {␊ - use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE};␊ + use openzeppelin::access::accesscontrol::extensions::{␊ + AccessControlDefaultAdminRulesComponent,␊ + DefaultConfig as AccessControlDefaultAdminRulesDefaultConfig␊ + };␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ use openzeppelin::security::pausable::PausableComponent;␊ - use openzeppelin::token::common::erc2981::{DefaultConfig, ERC2981Component};␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ use openzeppelin::token::erc1155::ERC1155Component;␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use starknet::{ClassHash, ContractAddress, get_caller_address};␊ use super::{MINTER_ROLE, PAUSER_ROLE, UPGRADER_ROLE, URI_SETTER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 86400; // 1 day␊ ␊ component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ - component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ ␊ @@ -1699,22 +3102,24 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl PausableImpl = PausableComponent::PausableImpl;␊ #[abi(embed_v0)]␊ - impl AccessControlImpl = AccessControlComponent::AccessControlImpl;␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ #[abi(embed_v0)]␊ - impl AccessControlCamelImpl = AccessControlComponent::AccessControlCamelImpl;␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ #[abi(embed_v0)]␊ - impl AccessControlWithDelayImpl = AccessControlComponent::AccessControlWithDelayImpl;␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ #[abi(embed_v0)]␊ impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ #[abi(embed_v0)]␊ impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ #[abi(embed_v0)]␊ - impl ERC2981AdminAccessControlImpl = ERC2981Component::ERC2981AdminAccessControlImpl;␊ + impl ERC2981AdminAccessControlDefaultAdminRulesImpl = ERC2981Component::ERC2981AdminAccessControlDefaultAdminRulesImpl;␊ ␊ // Internal␊ impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ impl PausableInternalImpl = PausableComponent::InternalImpl;␊ - impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ ␊ @@ -1727,7 +3132,7 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ pausable: PausableComponent::Storage,␊ #[substorage(v0)]␊ - accesscontrol: AccessControlComponent::Storage,␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ #[substorage(v0)]␊ upgradeable: UpgradeableComponent::Storage,␊ #[substorage(v0)]␊ @@ -1744,7 +3149,7 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ PausableEvent: PausableComponent::Event,␊ #[flat]␊ - AccessControlEvent: AccessControlComponent::Event,␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ #[flat]␊ UpgradeableEvent: UpgradeableComponent::Event,␊ #[flat]␊ @@ -1754,7 +3159,7 @@ Generated by [AVA](https://avajs.dev). #[constructor]␊ fn constructor(␊ ref self: ContractState,␊ - default_admin: ContractAddress,␊ + initial_default_admin: ContractAddress,␊ pauser: ContractAddress,␊ minter: ContractAddress,␊ uri_setter: ContractAddress,␊ @@ -1763,15 +3168,14 @@ Generated by [AVA](https://avajs.dev). royalty_admin: ContractAddress,␊ ) {␊ self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ - self.accesscontrol.initializer();␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ self.erc2981.initializer(default_royalty_receiver, 500);␊ ␊ - self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ - self.accesscontrol._grant_role(PAUSER_ROLE, pauser);␊ - self.accesscontrol._grant_role(MINTER_ROLE, minter);␊ - self.accesscontrol._grant_role(URI_SETTER_ROLE, uri_setter);␊ - self.accesscontrol._grant_role(UPGRADER_ROLE, upgrader);␊ - self.accesscontrol._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + self.accesscontrol_dar._grant_role(PAUSER_ROLE, pauser);␊ + self.accesscontrol_dar._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol_dar._grant_role(URI_SETTER_ROLE, uri_setter);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + self.accesscontrol_dar._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ }␊ ␊ impl ERC1155HooksImpl of ERC1155Component::ERC1155HooksTrait {␊ @@ -1792,13 +3196,13 @@ Generated by [AVA](https://avajs.dev). impl ExternalImpl of ExternalTrait {␊ #[external(v0)]␊ fn pause(ref self: ContractState) {␊ - self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ self.pausable.pause();␊ }␊ ␊ #[external(v0)]␊ fn unpause(ref self: ContractState) {␊ - self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ self.pausable.unpause();␊ }␊ ␊ @@ -1843,7 +3247,7 @@ Generated by [AVA](https://avajs.dev). value: u256,␊ data: Span,␊ ) {␊ - self.accesscontrol.assert_only_role(MINTER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ self.erc1155.mint_with_acceptance_check(account, token_id, value, data);␊ }␊ ␊ @@ -1855,7 +3259,7 @@ Generated by [AVA](https://avajs.dev). values: Span,␊ data: Span,␊ ) {␊ - self.accesscontrol.assert_only_role(MINTER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ self.erc1155.batch_mint_with_acceptance_check(account, token_ids, values, data);␊ }␊ ␊ @@ -1872,7 +3276,7 @@ Generated by [AVA](https://avajs.dev). ␊ #[external(v0)]␊ fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ - self.accesscontrol.assert_only_role(URI_SETTER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(URI_SETTER_ROLE);␊ self.erc1155._set_base_uri(base_uri);␊ }␊ ␊ @@ -1889,7 +3293,250 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl UpgradeableImpl of IUpgradeable {␊ fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ - self.accesscontrol.assert_only_role(UPGRADER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## full upgradeable roles-DAR (custom opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ + use openzeppelin::token::erc1155::ERC1155Component;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress, get_caller_address};␊ + use super::{MINTER_ROLE, PAUSER_ROLE, UPGRADER_ROLE, URI_SETTER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 172800; // 2 days␊ + ␊ + component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl;␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + #[abi(embed_v0)]␊ + impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981AdminAccessControlDefaultAdminRulesImpl = ERC2981Component::ERC2981AdminAccessControlDefaultAdminRulesImpl;␊ + ␊ + // Internal␊ + impl ERC1155InternalImpl = ERC1155Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc1155: ERC1155Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + erc2981: ERC2981Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC1155Event: ERC1155Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + ERC2981Event: ERC2981Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + pauser: ContractAddress,␊ + minter: ContractAddress,␊ + uri_setter: ContractAddress,␊ + upgrader: ContractAddress,␊ + default_royalty_receiver: ContractAddress,␊ + royalty_admin: ContractAddress,␊ + ) {␊ + self.erc1155.initializer("https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/");␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + self.erc2981.initializer(default_royalty_receiver, 500);␊ + ␊ + self.accesscontrol_dar._grant_role(PAUSER_ROLE, pauser);␊ + self.accesscontrol_dar._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol_dar._grant_role(URI_SETTER_ROLE, uri_setter);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + self.accesscontrol_dar._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + }␊ + ␊ + impl ERC1155HooksImpl of ERC1155Component::ERC1155HooksTrait {␊ + fn before_update(␊ + ref self: ERC1155Component::ComponentState,␊ + from: ContractAddress,␊ + to: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + ) {␊ + let contract_state = self.get_contract();␊ + contract_state.pausable.assert_not_paused();␊ + }␊ + }␊ + ␊ + impl AccessControlDefaultAdminRulesImmutableConfig of AccessControlDefaultAdminRulesComponent::ImmutableConfig {␊ + const DEFAULT_ADMIN_DELAY_INCREASE_WAIT: u64 = 604800; // 1 week␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn pause(ref self: ContractState) {␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ + self.pausable.pause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn unpause(ref self: ContractState) {␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ + self.pausable.unpause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn burn(ref self: ContractState, account: ContractAddress, token_id: u256, value: u256) {␊ + let caller = get_caller_address();␊ + if account != caller {␊ + assert(self.erc1155.is_approved_for_all(account, caller), ERC1155Component::Errors::UNAUTHORIZED);␊ + }␊ + self.erc1155.burn(account, token_id, value);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batch_burn(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + ) {␊ + let caller = get_caller_address();␊ + if account != caller {␊ + assert(self.erc1155.is_approved_for_all(account, caller), ERC1155Component::Errors::UNAUTHORIZED);␊ + }␊ + self.erc1155.batch_burn(account, token_ids, values);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batchBurn(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + tokenIds: Span,␊ + values: Span,␊ + ) {␊ + self.batch_burn(account, tokenIds, values);␊ + }␊ + ␊ + #[external(v0)]␊ + fn mint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_id: u256,␊ + value: u256,␊ + data: Span,␊ + ) {␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ + self.erc1155.mint_with_acceptance_check(account, token_id, value, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batch_mint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + token_ids: Span,␊ + values: Span,␊ + data: Span,␊ + ) {␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ + self.erc1155.batch_mint_with_acceptance_check(account, token_ids, values, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn batchMint(␊ + ref self: ContractState,␊ + account: ContractAddress,␊ + tokenIds: Span,␊ + values: Span,␊ + data: Span,␊ + ) {␊ + self.batch_mint(account, tokenIds, values, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn set_base_uri(ref self: ContractState, base_uri: ByteArray) {␊ + self.accesscontrol_dar.assert_only_role(URI_SETTER_ROLE);␊ + self.erc1155._set_base_uri(base_uri);␊ + }␊ + ␊ + #[external(v0)]␊ + fn setBaseUri(ref self: ContractState, baseUri: ByteArray) {␊ + self.set_base_uri(baseUri);␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ self.upgradeable.upgrade(new_class_hash);␊ }␊ }␊ diff --git a/packages/core/cairo_alpha/src/erc1155.test.ts.snap b/packages/core/cairo_alpha/src/erc1155.test.ts.snap index 9038e79a8..83cc4a12c 100644 Binary files a/packages/core/cairo_alpha/src/erc1155.test.ts.snap and b/packages/core/cairo_alpha/src/erc1155.test.ts.snap differ diff --git a/packages/core/cairo_alpha/src/erc20.test.ts b/packages/core/cairo_alpha/src/erc20.test.ts index 760cc8b53..b7f224ba5 100644 --- a/packages/core/cairo_alpha/src/erc20.test.ts +++ b/packages/core/cairo_alpha/src/erc20.test.ts @@ -3,6 +3,7 @@ import test from 'ava'; import type { ERC20Options } from './erc20'; import { buildERC20, getInitialSupply } from './erc20'; import { printContract } from './print'; +import { AccessControl, darDefaultOpts, darCustomOpts } from './set-access-control'; import type { OptionsError } from '.'; import { erc20 } from '.'; @@ -48,12 +49,22 @@ testERC20('erc20 burnable', { testERC20('erc20 pausable', { pausable: true, - access: 'ownable', + access: AccessControl.Ownable(), }); testERC20('erc20 pausable with roles', { pausable: true, - access: 'roles', + access: AccessControl.Roles(), +}); + +testERC20('erc20 pausable with roles-DAR (default opts)', { + pausable: true, + access: AccessControl.RolesDefaultAdminRules(darDefaultOpts), +}); + +testERC20('erc20 pausable with roles-DAR (custom opts)', { + pausable: true, + access: AccessControl.RolesDefaultAdminRules(darCustomOpts), }); testERC20('erc20 burnable pausable', { @@ -86,12 +97,22 @@ test('erc20 votes, decimals too high', async t => { testERC20('erc20 mintable', { mintable: true, - access: 'ownable', + access: AccessControl.Ownable(), }); testERC20('erc20 mintable with roles', { mintable: true, - access: 'roles', + access: AccessControl.Roles(), +}); + +testERC20('erc20 mintable with roles-DAR (default opts)', { + mintable: true, + access: AccessControl.RolesDefaultAdminRules(darDefaultOpts), +}); + +testERC20('erc20 mintable with roles-DAR (custom opts)', { + mintable: true, + access: AccessControl.RolesDefaultAdminRules(darCustomOpts), }); testERC20('erc20 votes', { @@ -137,7 +158,7 @@ testERC20('erc20 votes, non-upgradeable', { testERC20('erc20 full, non-upgradeable', { premint: '2000', - access: 'ownable', + access: AccessControl.Ownable(), burnable: true, mintable: true, votes: true, @@ -149,7 +170,7 @@ testERC20('erc20 full, non-upgradeable', { testERC20('erc20 full upgradeable', { premint: '2000', - access: 'ownable', + access: AccessControl.Ownable(), burnable: true, mintable: true, votes: true, @@ -161,7 +182,31 @@ testERC20('erc20 full upgradeable', { testERC20('erc20 full upgradeable with roles', { premint: '2000', - access: 'roles', + access: AccessControl.Roles(), + burnable: true, + mintable: true, + votes: true, + pausable: true, + upgradeable: true, + appName: 'MY_DAPP_NAME', + appVersion: 'MY_DAPP_VERSION', +}); + +testERC20('erc20 full upgradeable with roles-DAR (default opts)', { + premint: '2000', + access: AccessControl.RolesDefaultAdminRules(darDefaultOpts), + burnable: true, + mintable: true, + votes: true, + pausable: true, + upgradeable: true, + appName: 'MY_DAPP_NAME', + appVersion: 'MY_DAPP_VERSION', +}); + +testERC20('erc20 full upgradeable with roles-DAR (custom opts)', { + premint: '2000', + access: AccessControl.RolesDefaultAdminRules(darCustomOpts), burnable: true, mintable: true, votes: true, @@ -180,7 +225,7 @@ testAPIEquivalence('erc20 API full upgradeable', { symbol: 'CTK', decimals: '6', premint: '2000', - access: 'roles', + access: AccessControl.Roles(), burnable: true, mintable: true, votes: true, diff --git a/packages/core/cairo_alpha/src/erc20.test.ts.md b/packages/core/cairo_alpha/src/erc20.test.ts.md index d4d1325fd..389b9f184 100644 --- a/packages/core/cairo_alpha/src/erc20.test.ts.md +++ b/packages/core/cairo_alpha/src/erc20.test.ts.md @@ -436,6 +436,269 @@ Generated by [AVA](https://avajs.dev). }␊ ` +## erc20 pausable with roles-DAR (default opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::extensions::{␊ + AccessControlDefaultAdminRulesComponent,␊ + DefaultConfig as AccessControlDefaultAdminRulesDefaultConfig␊ + };␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::token::erc20::{DefaultConfig as ERC20DefaultConfig, ERC20Component};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + use super::{PAUSER_ROLE, UPGRADER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 86400; // 1 day␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlMixinImpl = AccessControlDefaultAdminRulesComponent::AccessControlMixinImpl;␊ + ␊ + // Internal␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + pauser: ContractAddress,␊ + upgrader: ContractAddress,␊ + ) {␊ + self.erc20.initializer("MyToken", "MTK");␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + ␊ + self.accesscontrol_dar._grant_role(PAUSER_ROLE, pauser);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + }␊ + ␊ + impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait {␊ + fn before_update(␊ + ref self: ERC20Component::ComponentState,␊ + from: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) {␊ + let contract_state = self.get_contract();␊ + contract_state.pausable.assert_not_paused();␊ + }␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn pause(ref self: ContractState) {␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ + self.pausable.pause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn unpause(ref self: ContractState) {␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ + self.pausable.unpause();␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## erc20 pausable with roles-DAR (custom opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::token::erc20::{DefaultConfig as ERC20DefaultConfig, ERC20Component};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + use super::{PAUSER_ROLE, UPGRADER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 172800; // 2 days␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlMixinImpl = AccessControlDefaultAdminRulesComponent::AccessControlMixinImpl;␊ + ␊ + // Internal␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + pauser: ContractAddress,␊ + upgrader: ContractAddress,␊ + ) {␊ + self.erc20.initializer("MyToken", "MTK");␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + ␊ + self.accesscontrol_dar._grant_role(PAUSER_ROLE, pauser);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + }␊ + ␊ + impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait {␊ + fn before_update(␊ + ref self: ERC20Component::ComponentState,␊ + from: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) {␊ + let contract_state = self.get_contract();␊ + contract_state.pausable.assert_not_paused();␊ + }␊ + }␊ + ␊ + impl AccessControlDefaultAdminRulesImmutableConfig of AccessControlDefaultAdminRulesComponent::ImmutableConfig {␊ + const DEFAULT_ADMIN_DELAY_INCREASE_WAIT: u64 = 604800; // 1 week␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn pause(ref self: ContractState) {␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ + self.pausable.pause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn unpause(ref self: ContractState) {␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ + self.pausable.unpause();␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + ## erc20 burnable pausable > Snapshot 1 @@ -957,44 +1220,257 @@ Generated by [AVA](https://avajs.dev). }␊ ` -## erc20 votes +## erc20 mintable with roles-DAR (default opts) > Snapshot 1 `// SPDX-License-Identifier: MIT␊ // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ ␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ #[starknet::contract]␊ mod MyToken {␊ - use openzeppelin::access::ownable::OwnableComponent;␊ - use openzeppelin::governance::votes::VotesComponent;␊ + use openzeppelin::access::accesscontrol::extensions::{␊ + AccessControlDefaultAdminRulesComponent,␊ + DefaultConfig as AccessControlDefaultAdminRulesDefaultConfig␊ + };␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ - use openzeppelin::token::erc20::{DefaultConfig as ERC20DefaultConfig, ERC20Component};␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::erc20::{␊ + DefaultConfig as ERC20DefaultConfig, ERC20Component, ERC20HooksEmptyImpl␊ + };␊ use openzeppelin::upgrades::UpgradeableComponent;␊ - use openzeppelin::utils::contract_clock::ERC6372TimestampClock;␊ - use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ - use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ use starknet::{ClassHash, ContractAddress};␊ + use super::{MINTER_ROLE, UPGRADER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 86400; // 1 day␊ ␊ component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ - component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ - component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ ␊ // External␊ #[abi(embed_v0)]␊ impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;␊ #[abi(embed_v0)]␊ - impl NoncesImpl = NoncesComponent::NoncesImpl;␊ - #[abi(embed_v0)]␊ - impl VotesImpl = VotesComponent::VotesImpl;␊ - #[abi(embed_v0)]␊ - impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + impl AccessControlMixinImpl = AccessControlDefaultAdminRulesComponent::AccessControlMixinImpl;␊ ␊ // Internal␊ impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ - impl VotesInternalImpl = VotesComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + minter: ContractAddress,␊ + upgrader: ContractAddress,␊ + ) {␊ + self.erc20.initializer("MyToken", "MTK");␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + ␊ + self.accesscontrol_dar._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ + self.erc20.mint(recipient, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## erc20 mintable with roles-DAR (custom opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::erc20::{␊ + DefaultConfig as ERC20DefaultConfig, ERC20Component, ERC20HooksEmptyImpl␊ + };␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + use super::{MINTER_ROLE, UPGRADER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 172800; // 2 days␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlMixinImpl = AccessControlDefaultAdminRulesComponent::AccessControlMixinImpl;␊ + ␊ + // Internal␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + minter: ContractAddress,␊ + upgrader: ContractAddress,␊ + ) {␊ + self.erc20.initializer("MyToken", "MTK");␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + ␊ + self.accesscontrol_dar._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + }␊ + ␊ + impl AccessControlDefaultAdminRulesImmutableConfig of AccessControlDefaultAdminRulesComponent::ImmutableConfig {␊ + const DEFAULT_ADMIN_DELAY_INCREASE_WAIT: u64 = 604800; // 1 week␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ + self.erc20.mint(recipient, amount);␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## erc20 votes + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::governance::votes::VotesComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::token::erc20::{DefaultConfig as ERC20DefaultConfig, ERC20Component};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::utils::contract_clock::ERC6372TimestampClock;␊ + use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::{ClassHash, ContractAddress};␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ + component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;␊ + #[abi(embed_v0)]␊ + impl NoncesImpl = NoncesComponent::NoncesImpl;␊ + #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + ␊ + // Internal␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ ␊ @@ -1174,22 +1650,252 @@ Generated by [AVA](https://avajs.dev). 'MY_DAPP_VERSION'␊ }␊ }␊ - ␊ - //␊ - // Upgradeable␊ - //␊ - ␊ - #[abi(embed_v0)]␊ - impl UpgradeableImpl of IUpgradeable {␊ - fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ - self.ownable.assert_only_owner();␊ - self.upgradeable.upgrade(new_class_hash);␊ - }␊ - }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.ownable.assert_only_owner();␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## erc20 votes, non-upgradeable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::governance::votes::VotesComponent;␊ + use openzeppelin::token::erc20::{DefaultConfig as ERC20DefaultConfig, ERC20Component};␊ + use openzeppelin::utils::contract_clock::ERC6372TimestampClock;␊ + use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::ContractAddress;␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ + component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;␊ + #[abi(embed_v0)]␊ + impl NoncesImpl = NoncesComponent::NoncesImpl;␊ + #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ + ␊ + // Internal␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + #[substorage(v0)]␊ + nonces: NoncesComponent::Storage,␊ + #[substorage(v0)]␊ + votes: VotesComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + #[flat]␊ + NoncesEvent: NoncesComponent::Event,␊ + #[flat]␊ + VotesEvent: VotesComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState) {␊ + self.erc20.initializer("MyToken", "MTK");␊ + }␊ + ␊ + impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait {␊ + fn after_update(␊ + ref self: ERC20Component::ComponentState,␊ + from: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) {␊ + let mut contract_state = self.get_contract_mut();␊ + contract_state.votes.transfer_voting_units(from, recipient, amount);␊ + }␊ + }␊ + ␊ + //␊ + // SNIP12 Metadata␊ + //␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'MY_DAPP_NAME'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'v1'␊ + }␊ + }␊ + }␊ + ` + +## erc20 full, non-upgradeable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::governance::votes::VotesComponent;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::token::erc20::{DefaultConfig as ERC20DefaultConfig, ERC20Component};␊ + use openzeppelin::utils::contract_clock::ERC6372TimestampClock;␊ + use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::{ContractAddress, get_caller_address};␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ + component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + #[abi(embed_v0)]␊ + impl NoncesImpl = NoncesComponent::NoncesImpl;␊ + #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ + ␊ + // Internal␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + #[substorage(v0)]␊ + nonces: NoncesComponent::Storage,␊ + #[substorage(v0)]␊ + votes: VotesComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + #[flat]␊ + NoncesEvent: NoncesComponent::Event,␊ + #[flat]␊ + VotesEvent: VotesComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, recipient: ContractAddress, owner: ContractAddress) {␊ + self.erc20.initializer("MyToken", "MTK");␊ + self.ownable.initializer(owner);␊ + ␊ + self.erc20.mint(recipient, 2000000000000000000000);␊ + }␊ + ␊ + impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait {␊ + fn before_update(␊ + ref self: ERC20Component::ComponentState,␊ + from: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) {␊ + let contract_state = self.get_contract();␊ + contract_state.pausable.assert_not_paused();␊ + }␊ + ␊ + fn after_update(␊ + ref self: ERC20Component::ComponentState,␊ + from: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) {␊ + let mut contract_state = self.get_contract_mut();␊ + contract_state.votes.transfer_voting_units(from, recipient, amount);␊ + }␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn pause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable.pause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn unpause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable.unpause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn burn(ref self: ContractState, value: u256) {␊ + self.erc20.burn(get_caller_address(), value);␊ + }␊ + ␊ + #[external(v0)]␊ + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {␊ + self.ownable.assert_only_owner();␊ + self.erc20.mint(recipient, amount);␊ + }␊ + }␊ + ␊ + //␊ + // SNIP12 Metadata␊ + //␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'MY_DAPP_NAME'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'MY_DAPP_VERSION'␊ + }␊ + }␊ }␊ ` -## erc20 votes, non-upgradeable +## erc20 full upgradeable > Snapshot 1 @@ -1198,37 +1904,57 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyToken {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ use openzeppelin::governance::votes::VotesComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::security::pausable::PausableComponent;␊ use openzeppelin::token::erc20::{DefaultConfig as ERC20DefaultConfig, ERC20Component};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ use openzeppelin::utils::contract_clock::ERC6372TimestampClock;␊ use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ - use starknet::ContractAddress;␊ + use starknet::{ClassHash, ContractAddress, get_caller_address};␊ ␊ component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ ␊ // External␊ #[abi(embed_v0)]␊ impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;␊ #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + #[abi(embed_v0)]␊ impl NoncesImpl = NoncesComponent::NoncesImpl;␊ #[abi(embed_v0)]␊ impl VotesImpl = VotesComponent::VotesImpl;␊ ␊ // Internal␊ impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ impl VotesInternalImpl = VotesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ ␊ #[storage]␊ struct Storage {␊ #[substorage(v0)]␊ erc20: ERC20Component::Storage,␊ #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + #[substorage(v0)]␊ nonces: NoncesComponent::Storage,␊ #[substorage(v0)]␊ votes: VotesComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ }␊ ␊ #[event]␊ @@ -1237,17 +1963,36 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ ERC20Event: ERC20Component::Event,␊ #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + #[flat]␊ NoncesEvent: NoncesComponent::Event,␊ #[flat]␊ VotesEvent: VotesComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ }␊ ␊ #[constructor]␊ - fn constructor(ref self: ContractState) {␊ + fn constructor(ref self: ContractState, recipient: ContractAddress, owner: ContractAddress) {␊ self.erc20.initializer("MyToken", "MTK");␊ + self.ownable.initializer(owner);␊ + ␊ + self.erc20.mint(recipient, 2000000000000000000000);␊ }␊ ␊ impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait {␊ + fn before_update(␊ + ref self: ERC20Component::ComponentState,␊ + from: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) {␊ + let contract_state = self.get_contract();␊ + contract_state.pausable.assert_not_paused();␊ + }␊ + ␊ fn after_update(␊ ref self: ERC20Component::ComponentState,␊ from: ContractAddress,␊ @@ -1258,6 +2003,33 @@ Generated by [AVA](https://avajs.dev). contract_state.votes.transfer_voting_units(from, recipient, amount);␊ }␊ }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn pause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable.pause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn unpause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable.unpause();␊ + }␊ + ␊ + #[external(v0)]␊ + fn burn(ref self: ContractState, value: u256) {␊ + self.erc20.burn(get_caller_address(), value);␊ + }␊ + ␊ + #[external(v0)]␊ + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {␊ + self.ownable.assert_only_owner();␊ + self.erc20.mint(recipient, amount);␊ + }␊ + }␊ ␊ //␊ // SNIP12 Metadata␊ @@ -1269,35 +2041,57 @@ Generated by [AVA](https://avajs.dev). }␊ ␊ fn version() -> felt252 {␊ - 'v1'␊ + 'MY_DAPP_VERSION'␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.ownable.assert_only_owner();␊ + self.upgradeable.upgrade(new_class_hash);␊ }␊ }␊ }␊ ` -## erc20 full, non-upgradeable +## erc20 full upgradeable with roles > Snapshot 1 `// SPDX-License-Identifier: MIT␊ // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ ␊ + const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ #[starknet::contract]␊ mod MyToken {␊ - use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE};␊ use openzeppelin::governance::votes::VotesComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ use openzeppelin::security::pausable::PausableComponent;␊ use openzeppelin::token::erc20::{DefaultConfig as ERC20DefaultConfig, ERC20Component};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ use openzeppelin::utils::contract_clock::ERC6372TimestampClock;␊ use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ - use starknet::{ContractAddress, get_caller_address};␊ + use starknet::{ClassHash, ContractAddress, get_caller_address};␊ + use super::{MINTER_ROLE, PAUSER_ROLE, UPGRADER_ROLE};␊ ␊ component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ ␊ // External␊ #[abi(embed_v0)]␊ @@ -1305,7 +2099,7 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl PausableImpl = PausableComponent::PausableImpl;␊ #[abi(embed_v0)]␊ - impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + impl AccessControlMixinImpl = AccessControlComponent::AccessControlMixinImpl;␊ #[abi(embed_v0)]␊ impl NoncesImpl = NoncesComponent::NoncesImpl;␊ #[abi(embed_v0)]␊ @@ -1314,8 +2108,9 @@ Generated by [AVA](https://avajs.dev). // Internal␊ impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ impl PausableInternalImpl = PausableComponent::InternalImpl;␊ - impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ impl VotesInternalImpl = VotesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ ␊ #[storage]␊ struct Storage {␊ @@ -1324,11 +2119,15 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ pausable: PausableComponent::Storage,␊ #[substorage(v0)]␊ - ownable: OwnableComponent::Storage,␊ + accesscontrol: AccessControlComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ #[substorage(v0)]␊ nonces: NoncesComponent::Storage,␊ #[substorage(v0)]␊ votes: VotesComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ }␊ ␊ #[event]␊ @@ -1339,19 +2138,34 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ PausableEvent: PausableComponent::Event,␊ #[flat]␊ - OwnableEvent: OwnableComponent::Event,␊ + AccessControlEvent: AccessControlComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ #[flat]␊ NoncesEvent: NoncesComponent::Event,␊ #[flat]␊ VotesEvent: VotesComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ }␊ ␊ #[constructor]␊ - fn constructor(ref self: ContractState, recipient: ContractAddress, owner: ContractAddress) {␊ + fn constructor(␊ + ref self: ContractState,␊ + recipient: ContractAddress,␊ + default_admin: ContractAddress,␊ + pauser: ContractAddress,␊ + minter: ContractAddress,␊ + upgrader: ContractAddress,␊ + ) {␊ self.erc20.initializer("MyToken", "MTK");␊ - self.ownable.initializer(owner);␊ + self.accesscontrol.initializer();␊ ␊ self.erc20.mint(recipient, 2000000000000000000000);␊ + self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ + self.accesscontrol._grant_role(PAUSER_ROLE, pauser);␊ + self.accesscontrol._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol._grant_role(UPGRADER_ROLE, upgrader);␊ }␊ ␊ impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait {␊ @@ -1381,13 +2195,13 @@ Generated by [AVA](https://avajs.dev). impl ExternalImpl of ExternalTrait {␊ #[external(v0)]␊ fn pause(ref self: ContractState) {␊ - self.ownable.assert_only_owner();␊ + self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ self.pausable.pause();␊ }␊ ␊ #[external(v0)]␊ fn unpause(ref self: ContractState) {␊ - self.ownable.assert_only_owner();␊ + self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ self.pausable.unpause();␊ }␊ ␊ @@ -1398,7 +2212,7 @@ Generated by [AVA](https://avajs.dev). ␊ #[external(v0)]␊ fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {␊ - self.ownable.assert_only_owner();␊ + self.accesscontrol.assert_only_role(MINTER_ROLE);␊ self.erc20.mint(recipient, amount);␊ }␊ }␊ @@ -1416,21 +2230,41 @@ Generated by [AVA](https://avajs.dev). 'MY_DAPP_VERSION'␊ }␊ }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ }␊ ` -## erc20 full upgradeable +## erc20 full upgradeable with roles-DAR (default opts) > Snapshot 1 `// SPDX-License-Identifier: MIT␊ // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ ␊ + const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ #[starknet::contract]␊ mod MyToken {␊ - use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::access::accesscontrol::extensions::{␊ + AccessControlDefaultAdminRulesComponent,␊ + DefaultConfig as AccessControlDefaultAdminRulesDefaultConfig␊ + };␊ use openzeppelin::governance::votes::VotesComponent;␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ use openzeppelin::security::pausable::PausableComponent;␊ use openzeppelin::token::erc20::{DefaultConfig as ERC20DefaultConfig, ERC20Component};␊ use openzeppelin::upgrades::UpgradeableComponent;␊ @@ -1438,10 +2272,14 @@ Generated by [AVA](https://avajs.dev). use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ use starknet::{ClassHash, ContractAddress, get_caller_address};␊ + use super::{MINTER_ROLE, PAUSER_ROLE, UPGRADER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 86400; // 1 day␊ ␊ component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ @@ -1452,7 +2290,7 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl PausableImpl = PausableComponent::PausableImpl;␊ #[abi(embed_v0)]␊ - impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + impl AccessControlMixinImpl = AccessControlDefaultAdminRulesComponent::AccessControlMixinImpl;␊ #[abi(embed_v0)]␊ impl NoncesImpl = NoncesComponent::NoncesImpl;␊ #[abi(embed_v0)]␊ @@ -1461,7 +2299,7 @@ Generated by [AVA](https://avajs.dev). // Internal␊ impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ impl PausableInternalImpl = PausableComponent::InternalImpl;␊ - impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ impl VotesInternalImpl = VotesComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ ␊ @@ -1472,7 +2310,9 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ pausable: PausableComponent::Storage,␊ #[substorage(v0)]␊ - ownable: OwnableComponent::Storage,␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ #[substorage(v0)]␊ nonces: NoncesComponent::Storage,␊ #[substorage(v0)]␊ @@ -1489,7 +2329,9 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ PausableEvent: PausableComponent::Event,␊ #[flat]␊ - OwnableEvent: OwnableComponent::Event,␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ #[flat]␊ NoncesEvent: NoncesComponent::Event,␊ #[flat]␊ @@ -1499,11 +2341,21 @@ Generated by [AVA](https://avajs.dev). }␊ ␊ #[constructor]␊ - fn constructor(ref self: ContractState, recipient: ContractAddress, owner: ContractAddress) {␊ + fn constructor(␊ + ref self: ContractState,␊ + recipient: ContractAddress,␊ + initial_default_admin: ContractAddress,␊ + pauser: ContractAddress,␊ + minter: ContractAddress,␊ + upgrader: ContractAddress,␊ + ) {␊ self.erc20.initializer("MyToken", "MTK");␊ - self.ownable.initializer(owner);␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ ␊ self.erc20.mint(recipient, 2000000000000000000000);␊ + self.accesscontrol_dar._grant_role(PAUSER_ROLE, pauser);␊ + self.accesscontrol_dar._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ }␊ ␊ impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait {␊ @@ -1533,13 +2385,13 @@ Generated by [AVA](https://avajs.dev). impl ExternalImpl of ExternalTrait {␊ #[external(v0)]␊ fn pause(ref self: ContractState) {␊ - self.ownable.assert_only_owner();␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ self.pausable.pause();␊ }␊ ␊ #[external(v0)]␊ fn unpause(ref self: ContractState) {␊ - self.ownable.assert_only_owner();␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ self.pausable.unpause();␊ }␊ ␊ @@ -1550,7 +2402,7 @@ Generated by [AVA](https://avajs.dev). ␊ #[external(v0)]␊ fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {␊ - self.ownable.assert_only_owner();␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ self.erc20.mint(recipient, amount);␊ }␊ }␊ @@ -1576,14 +2428,14 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl UpgradeableImpl of IUpgradeable {␊ fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ - self.ownable.assert_only_owner();␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ self.upgradeable.upgrade(new_class_hash);␊ }␊ }␊ }␊ ` -## erc20 full upgradeable with roles +## erc20 full upgradeable with roles-DAR (custom opts) > Snapshot 1 @@ -1596,7 +2448,7 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyToken {␊ - use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE};␊ + use openzeppelin::access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent;␊ use openzeppelin::governance::votes::VotesComponent;␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ @@ -1608,10 +2460,12 @@ Generated by [AVA](https://avajs.dev). use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ use starknet::{ClassHash, ContractAddress, get_caller_address};␊ use super::{MINTER_ROLE, PAUSER_ROLE, UPGRADER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 172800; // 2 days␊ ␊ component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ - component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ @@ -1623,7 +2477,7 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl PausableImpl = PausableComponent::PausableImpl;␊ #[abi(embed_v0)]␊ - impl AccessControlMixinImpl = AccessControlComponent::AccessControlMixinImpl;␊ + impl AccessControlMixinImpl = AccessControlDefaultAdminRulesComponent::AccessControlMixinImpl;␊ #[abi(embed_v0)]␊ impl NoncesImpl = NoncesComponent::NoncesImpl;␊ #[abi(embed_v0)]␊ @@ -1632,7 +2486,7 @@ Generated by [AVA](https://avajs.dev). // Internal␊ impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ impl PausableInternalImpl = PausableComponent::InternalImpl;␊ - impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ impl VotesInternalImpl = VotesComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ ␊ @@ -1643,7 +2497,7 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ pausable: PausableComponent::Storage,␊ #[substorage(v0)]␊ - accesscontrol: AccessControlComponent::Storage,␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ #[substorage(v0)]␊ src5: SRC5Component::Storage,␊ #[substorage(v0)]␊ @@ -1662,7 +2516,7 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ PausableEvent: PausableComponent::Event,␊ #[flat]␊ - AccessControlEvent: AccessControlComponent::Event,␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ #[flat]␊ SRC5Event: SRC5Component::Event,␊ #[flat]␊ @@ -1677,19 +2531,18 @@ Generated by [AVA](https://avajs.dev). fn constructor(␊ ref self: ContractState,␊ recipient: ContractAddress,␊ - default_admin: ContractAddress,␊ + initial_default_admin: ContractAddress,␊ pauser: ContractAddress,␊ minter: ContractAddress,␊ upgrader: ContractAddress,␊ ) {␊ self.erc20.initializer("MyToken", "MTK");␊ - self.accesscontrol.initializer();␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ ␊ self.erc20.mint(recipient, 2000000000000000000000);␊ - self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ - self.accesscontrol._grant_role(PAUSER_ROLE, pauser);␊ - self.accesscontrol._grant_role(MINTER_ROLE, minter);␊ - self.accesscontrol._grant_role(UPGRADER_ROLE, upgrader);␊ + self.accesscontrol_dar._grant_role(PAUSER_ROLE, pauser);␊ + self.accesscontrol_dar._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ }␊ ␊ impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait {␊ @@ -1714,18 +2567,22 @@ Generated by [AVA](https://avajs.dev). }␊ }␊ ␊ + impl AccessControlDefaultAdminRulesImmutableConfig of AccessControlDefaultAdminRulesComponent::ImmutableConfig {␊ + const DEFAULT_ADMIN_DELAY_INCREASE_WAIT: u64 = 604800; // 1 week␊ + }␊ + ␊ #[generate_trait]␊ #[abi(per_item)]␊ impl ExternalImpl of ExternalTrait {␊ #[external(v0)]␊ fn pause(ref self: ContractState) {␊ - self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ self.pausable.pause();␊ }␊ ␊ #[external(v0)]␊ fn unpause(ref self: ContractState) {␊ - self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(PAUSER_ROLE);␊ self.pausable.unpause();␊ }␊ ␊ @@ -1736,7 +2593,7 @@ Generated by [AVA](https://avajs.dev). ␊ #[external(v0)]␊ fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {␊ - self.accesscontrol.assert_only_role(MINTER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ self.erc20.mint(recipient, amount);␊ }␊ }␊ @@ -1762,7 +2619,7 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl UpgradeableImpl of IUpgradeable {␊ fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ - self.accesscontrol.assert_only_role(UPGRADER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ self.upgradeable.upgrade(new_class_hash);␊ }␊ }␊ diff --git a/packages/core/cairo_alpha/src/erc20.test.ts.snap b/packages/core/cairo_alpha/src/erc20.test.ts.snap index 73bd80c0d..08f1ada47 100644 Binary files a/packages/core/cairo_alpha/src/erc20.test.ts.snap and b/packages/core/cairo_alpha/src/erc20.test.ts.snap differ diff --git a/packages/core/cairo_alpha/src/erc721.test.ts b/packages/core/cairo_alpha/src/erc721.test.ts index b5f4583e9..bfd04c49a 100644 --- a/packages/core/cairo_alpha/src/erc721.test.ts +++ b/packages/core/cairo_alpha/src/erc721.test.ts @@ -4,6 +4,7 @@ import type { ERC721Options } from './erc721'; import { buildERC721 } from './erc721'; import { printContract } from './print'; import { royaltyInfoOptions } from './set-royalty-info'; +import { AccessControl, darDefaultOpts, darCustomOpts } from './set-access-control'; import type { OptionsError } from '.'; import { erc721 } from '.'; @@ -90,7 +91,17 @@ testERC721('pausable + enumerable', { testERC721('mintable + roles', { mintable: true, - access: 'roles', + access: AccessControl.Roles(), +}); + +testERC721('mintable + roles-DAR (default opts)', { + mintable: true, + access: AccessControl.RolesDefaultAdminRules(darDefaultOpts), +}); + +testERC721('mintable + roles-DAR (custom opts)', { + mintable: true, + access: AccessControl.RolesDefaultAdminRules(darCustomOpts), }); testERC721('royalty info disabled', { @@ -99,22 +110,42 @@ testERC721('royalty info disabled', { testERC721('royalty info enabled default + ownable', { royaltyInfo: royaltyInfoOptions.enabledDefault, - access: 'ownable', + access: AccessControl.Ownable(), }); testERC721('royalty info enabled default + roles', { royaltyInfo: royaltyInfoOptions.enabledDefault, - access: 'roles', + access: AccessControl.Roles(), +}); + +testERC721('royalty info enabled default + roles-DAR (default opts)', { + royaltyInfo: royaltyInfoOptions.enabledDefault, + access: AccessControl.RolesDefaultAdminRules(darDefaultOpts), +}); + +testERC721('royalty info enabled default + roles-DAR (custom opts)', { + royaltyInfo: royaltyInfoOptions.enabledDefault, + access: AccessControl.RolesDefaultAdminRules(darCustomOpts), }); testERC721('royalty info enabled custom + ownable', { royaltyInfo: royaltyInfoOptions.enabledCustom, - access: 'ownable', + access: AccessControl.Ownable(), }); testERC721('royalty info enabled custom + roles', { royaltyInfo: royaltyInfoOptions.enabledCustom, - access: 'roles', + access: AccessControl.Roles(), +}); + +testERC721('royalty info enabled custom + roles-DAR (default opts)', { + royaltyInfo: royaltyInfoOptions.enabledCustom, + access: AccessControl.RolesDefaultAdminRules(darDefaultOpts), +}); + +testERC721('royalty info enabled custom + roles-DAR (custom opts)', { + royaltyInfo: royaltyInfoOptions.enabledCustom, + access: AccessControl.RolesDefaultAdminRules(darCustomOpts), }); testERC721('full non-upgradeable', { diff --git a/packages/core/cairo_alpha/src/erc721.test.ts.md b/packages/core/cairo_alpha/src/erc721.test.ts.md index 5749ae397..1ab18758a 100644 --- a/packages/core/cairo_alpha/src/erc721.test.ts.md +++ b/packages/core/cairo_alpha/src/erc721.test.ts.md @@ -850,6 +850,257 @@ Generated by [AVA](https://avajs.dev). }␊ ` +## mintable + roles-DAR (default opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::extensions::{␊ + AccessControlDefaultAdminRulesComponent,␊ + DefaultConfig as AccessControlDefaultAdminRulesDefaultConfig␊ + };␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + use super::{MINTER_ROLE, UPGRADER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 86400; // 1 day␊ + ␊ + component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ + ␊ + // Internal␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + minter: ContractAddress,␊ + upgrader: ContractAddress,␊ + ) {␊ + self.erc721.initializer("MyToken", "MTK", "");␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + ␊ + self.accesscontrol_dar._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn safe_mint(␊ + ref self: ContractState,␊ + recipient: ContractAddress,␊ + token_id: u256,␊ + data: Span,␊ + ) {␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ + self.erc721.safe_mint(recipient, token_id, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn safeMint(␊ + ref self: ContractState,␊ + recipient: ContractAddress,␊ + tokenId: u256,␊ + data: Span,␊ + ) {␊ + self.safe_mint(recipient, tokenId, data);␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## mintable + roles-DAR (custom opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + use super::{MINTER_ROLE, UPGRADER_ROLE};␊ + ␊ + const INITIAL_DELAY: u64 = 172800; // 2 days␊ + ␊ + component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ + ␊ + // Internal␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + minter: ContractAddress,␊ + upgrader: ContractAddress,␊ + ) {␊ + self.erc721.initializer("MyToken", "MTK", "");␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + ␊ + self.accesscontrol_dar._grant_role(MINTER_ROLE, minter);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + }␊ + ␊ + impl AccessControlDefaultAdminRulesImmutableConfig of AccessControlDefaultAdminRulesComponent::ImmutableConfig {␊ + const DEFAULT_ADMIN_DELAY_INCREASE_WAIT: u64 = 604800; // 1 week␊ + }␊ + ␊ + #[generate_trait]␊ + #[abi(per_item)]␊ + impl ExternalImpl of ExternalTrait {␊ + #[external(v0)]␊ + fn safe_mint(␊ + ref self: ContractState,␊ + recipient: ContractAddress,␊ + token_id: u256,␊ + data: Span,␊ + ) {␊ + self.accesscontrol_dar.assert_only_role(MINTER_ROLE);␊ + self.erc721.safe_mint(recipient, token_id, data);␊ + }␊ + ␊ + #[external(v0)]␊ + fn safeMint(␊ + ref self: ContractState,␊ + recipient: ContractAddress,␊ + tokenId: u256,␊ + data: Span,␊ + ) {␊ + self.safe_mint(recipient, tokenId, data);␊ + }␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + ## royalty info disabled > Snapshot 1 @@ -859,28 +1110,337 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyToken {␊ - use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + ␊ + component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + ␊ + // Internal␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.erc721.initializer("MyToken", "MTK", "");␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.ownable.assert_only_owner();␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## royalty info enabled default + ownable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + ␊ + component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + #[abi(embed_v0)]␊ + impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981AdminOwnableImpl = ERC2981Component::ERC2981AdminOwnableImpl;␊ + ␊ + // Internal␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + erc2981: ERC2981Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + ERC2981Event: ERC2981Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + owner: ContractAddress,␊ + default_royalty_receiver: ContractAddress,␊ + ) {␊ + self.erc721.initializer("MyToken", "MTK", "");␊ + self.ownable.initializer(owner);␊ + self.erc2981.initializer(default_royalty_receiver, 500);␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.ownable.assert_only_owner();␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## royalty info enabled default + roles + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE};␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + use super::UPGRADER_ROLE;␊ + ␊ + component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlComponent::AccessControlWithDelayImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + #[abi(embed_v0)]␊ + impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981AdminAccessControlImpl = ERC2981Component::ERC2981AdminAccessControlImpl;␊ + ␊ + // Internal␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + accesscontrol: AccessControlComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + erc2981: ERC2981Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + AccessControlEvent: AccessControlComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + ERC2981Event: ERC2981Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + default_admin: ContractAddress,␊ + upgrader: ContractAddress,␊ + default_royalty_receiver: ContractAddress,␊ + royalty_admin: ContractAddress,␊ + ) {␊ + self.erc721.initializer("MyToken", "MTK", "");␊ + self.accesscontrol.initializer();␊ + self.erc2981.initializer(default_royalty_receiver, 500);␊ + ␊ + self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ + self.accesscontrol._grant_role(UPGRADER_ROLE, upgrader);␊ + self.accesscontrol._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## royalty info enabled default + roles-DAR (default opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::extensions::{␊ + AccessControlDefaultAdminRulesComponent,␊ + DefaultConfig as AccessControlDefaultAdminRulesDefaultConfig␊ + };␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl};␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use starknet::{ClassHash, ContractAddress};␊ + use super::UPGRADER_ROLE;␊ + ␊ + const INITIAL_DELAY: u64 = 86400; // 1 day␊ ␊ component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ ␊ // External␊ #[abi(embed_v0)]␊ impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ #[abi(embed_v0)]␊ - impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + #[abi(embed_v0)]␊ + impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981AdminAccessControlDefaultAdminRulesImpl = ERC2981Component::ERC2981AdminAccessControlDefaultAdminRulesImpl;␊ ␊ // Internal␊ impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ - impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ ␊ #[storage]␊ struct Storage {␊ @@ -889,9 +1449,11 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ src5: SRC5Component::Storage,␊ #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ upgradeable: UpgradeableComponent::Storage,␊ #[substorage(v0)]␊ - ownable: OwnableComponent::Storage,␊ + erc2981: ERC2981Component::Storage,␊ }␊ ␊ #[event]␊ @@ -902,15 +1464,27 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ SRC5Event: SRC5Component::Event,␊ #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ UpgradeableEvent: UpgradeableComponent::Event,␊ #[flat]␊ - OwnableEvent: OwnableComponent::Event,␊ + ERC2981Event: ERC2981Component::Event,␊ }␊ ␊ #[constructor]␊ - fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + upgrader: ContractAddress,␊ + default_royalty_receiver: ContractAddress,␊ + royalty_admin: ContractAddress,␊ + ) {␊ self.erc721.initializer("MyToken", "MTK", "");␊ - self.ownable.initializer(owner);␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + self.erc2981.initializer(default_royalty_receiver, 500);␊ + ␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + self.accesscontrol_dar._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ }␊ ␊ //␊ @@ -920,14 +1494,131 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl UpgradeableImpl of IUpgradeable {␊ fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ - self.ownable.assert_only_owner();␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ self.upgradeable.upgrade(new_class_hash);␊ }␊ }␊ }␊ ` -## royalty info enabled default + ownable +## royalty info enabled default + roles-DAR (custom opts) + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ + ␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent;␊ + use openzeppelin::interfaces::upgrades::IUpgradeable;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ + use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl};␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use starknet::{ClassHash, ContractAddress};␊ + use super::UPGRADER_ROLE;␊ + ␊ + const INITIAL_DELAY: u64 = 172800; // 2 days␊ + ␊ + component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ + #[abi(embed_v0)]␊ + impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ + #[abi(embed_v0)]␊ + impl ERC2981AdminAccessControlDefaultAdminRulesImpl = ERC2981Component::ERC2981AdminAccessControlDefaultAdminRulesImpl;␊ + ␊ + // Internal␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + erc2981: ERC2981Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + ERC2981Event: ERC2981Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + initial_default_admin: ContractAddress,␊ + upgrader: ContractAddress,␊ + default_royalty_receiver: ContractAddress,␊ + royalty_admin: ContractAddress,␊ + ) {␊ + self.erc721.initializer("MyToken", "MTK", "");␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ + self.erc2981.initializer(default_royalty_receiver, 500);␊ + ␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + self.accesscontrol_dar._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + }␊ + ␊ + impl AccessControlDefaultAdminRulesImmutableConfig of AccessControlDefaultAdminRulesComponent::ImmutableConfig {␊ + const DEFAULT_ADMIN_DELAY_INCREASE_WAIT: u64 = 604800; // 1 week␊ + }␊ + ␊ + //␊ + // Upgradeable␊ + //␊ + ␊ + #[abi(embed_v0)]␊ + impl UpgradeableImpl of IUpgradeable {␊ + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ + self.upgradeable.upgrade(new_class_hash);␊ + }␊ + }␊ + }␊ + ` + +## royalty info enabled custom + ownable > Snapshot 1 @@ -939,7 +1630,7 @@ Generated by [AVA](https://avajs.dev). use openzeppelin::access::ownable::OwnableComponent;␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ - use openzeppelin::token::common::erc2981::{DefaultConfig, ERC2981Component};␊ + use openzeppelin::token::common::erc2981::ERC2981Component;␊ use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl};␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use starknet::{ClassHash, ContractAddress};␊ @@ -1005,7 +1696,11 @@ Generated by [AVA](https://avajs.dev). ) {␊ self.erc721.initializer("MyToken", "MTK", "");␊ self.ownable.initializer(owner);␊ - self.erc2981.initializer(default_royalty_receiver, 500);␊ + self.erc2981.initializer(default_royalty_receiver, 15125);␊ + }␊ + ␊ + impl ERC2981ImmutableConfig of ERC2981Component::ImmutableConfig {␊ + const FEE_DENOMINATOR: u128 = 100000;␊ }␊ ␊ //␊ @@ -1022,7 +1717,7 @@ Generated by [AVA](https://avajs.dev). }␊ ` -## royalty info enabled default + roles +## royalty info enabled custom + roles > Snapshot 1 @@ -1036,7 +1731,7 @@ Generated by [AVA](https://avajs.dev). use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE};␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ - use openzeppelin::token::common::erc2981::{DefaultConfig, ERC2981Component};␊ + use openzeppelin::token::common::erc2981::ERC2981Component;␊ use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl};␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use starknet::{ClassHash, ContractAddress};␊ @@ -1109,12 +1804,16 @@ Generated by [AVA](https://avajs.dev). ) {␊ self.erc721.initializer("MyToken", "MTK", "");␊ self.accesscontrol.initializer();␊ - self.erc2981.initializer(default_royalty_receiver, 500);␊ + self.erc2981.initializer(default_royalty_receiver, 15125);␊ ␊ self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ self.accesscontrol._grant_role(UPGRADER_ROLE, upgrader);␊ self.accesscontrol._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ }␊ + ␊ + impl ERC2981ImmutableConfig of ERC2981Component::ImmutableConfig {␊ + const FEE_DENOMINATOR: u128 = 100000;␊ + }␊ ␊ //␊ // Upgradeable␊ @@ -1130,26 +1829,34 @@ Generated by [AVA](https://avajs.dev). }␊ ` -## royalty info enabled custom + ownable +## royalty info enabled custom + roles-DAR (default opts) > Snapshot 1 `// SPDX-License-Identifier: MIT␊ // Compatible with OpenZeppelin Contracts for Cairo 3.0.0-alpha.3␊ ␊ + const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ + ␊ #[starknet::contract]␊ mod MyToken {␊ - use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::access::accesscontrol::extensions::{␊ + AccessControlDefaultAdminRulesComponent,␊ + DefaultConfig as AccessControlDefaultAdminRulesDefaultConfig␊ + };␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ use openzeppelin::token::common::erc2981::ERC2981Component;␊ use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl};␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use starknet::{ClassHash, ContractAddress};␊ + use super::UPGRADER_ROLE;␊ + ␊ + const INITIAL_DELAY: u64 = 86400; // 1 day␊ ␊ component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ ␊ @@ -1157,17 +1864,23 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ #[abi(embed_v0)]␊ - impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ #[abi(embed_v0)]␊ impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ #[abi(embed_v0)]␊ impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ #[abi(embed_v0)]␊ - impl ERC2981AdminOwnableImpl = ERC2981Component::ERC2981AdminOwnableImpl;␊ + impl ERC2981AdminAccessControlDefaultAdminRulesImpl = ERC2981Component::ERC2981AdminAccessControlDefaultAdminRulesImpl;␊ ␊ // Internal␊ impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ - impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ ␊ @@ -1178,7 +1891,7 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ src5: SRC5Component::Storage,␊ #[substorage(v0)]␊ - ownable: OwnableComponent::Storage,␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ #[substorage(v0)]␊ upgradeable: UpgradeableComponent::Storage,␊ #[substorage(v0)]␊ @@ -1193,7 +1906,7 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ SRC5Event: SRC5Component::Event,␊ #[flat]␊ - OwnableEvent: OwnableComponent::Event,␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ #[flat]␊ UpgradeableEvent: UpgradeableComponent::Event,␊ #[flat]␊ @@ -1203,12 +1916,17 @@ Generated by [AVA](https://avajs.dev). #[constructor]␊ fn constructor(␊ ref self: ContractState,␊ - owner: ContractAddress,␊ + initial_default_admin: ContractAddress,␊ + upgrader: ContractAddress,␊ default_royalty_receiver: ContractAddress,␊ + royalty_admin: ContractAddress,␊ ) {␊ self.erc721.initializer("MyToken", "MTK", "");␊ - self.ownable.initializer(owner);␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ self.erc2981.initializer(default_royalty_receiver, 15125);␊ + ␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + self.accesscontrol_dar._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ }␊ ␊ impl ERC2981ImmutableConfig of ERC2981Component::ImmutableConfig {␊ @@ -1222,14 +1940,14 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl UpgradeableImpl of IUpgradeable {␊ fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ - self.ownable.assert_only_owner();␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ self.upgradeable.upgrade(new_class_hash);␊ }␊ }␊ }␊ ` -## royalty info enabled custom + roles +## royalty info enabled custom + roles-DAR (custom opts) > Snapshot 1 @@ -1240,7 +1958,7 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyToken {␊ - use openzeppelin::access::accesscontrol::{AccessControlComponent, DEFAULT_ADMIN_ROLE};␊ + use openzeppelin::access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent;␊ use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ use openzeppelin::token::common::erc2981::ERC2981Component;␊ @@ -1248,10 +1966,12 @@ Generated by [AVA](https://avajs.dev). use openzeppelin::upgrades::UpgradeableComponent;␊ use starknet::{ClassHash, ContractAddress};␊ use super::UPGRADER_ROLE;␊ + ␊ + const INITIAL_DELAY: u64 = 172800; // 2 days␊ ␊ component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ - component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: AccessControlDefaultAdminRulesComponent, storage: accesscontrol_dar, event: AccessControlDefaultAdminRulesEvent);␊ component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);␊ ␊ @@ -1259,21 +1979,23 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ #[abi(embed_v0)]␊ - impl AccessControlImpl = AccessControlComponent::AccessControlImpl;␊ + impl AccessControlImpl = AccessControlDefaultAdminRulesComponent::AccessControlImpl;␊ #[abi(embed_v0)]␊ - impl AccessControlCamelImpl = AccessControlComponent::AccessControlCamelImpl;␊ + impl AccessControlDefaultAdminRulesImpl = AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl;␊ #[abi(embed_v0)]␊ - impl AccessControlWithDelayImpl = AccessControlComponent::AccessControlWithDelayImpl;␊ + impl AccessControlCamelImpl = AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlWithDelayImpl = AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl;␊ #[abi(embed_v0)]␊ impl ERC2981Impl = ERC2981Component::ERC2981Impl;␊ #[abi(embed_v0)]␊ impl ERC2981InfoImpl = ERC2981Component::ERC2981InfoImpl;␊ #[abi(embed_v0)]␊ - impl ERC2981AdminAccessControlImpl = ERC2981Component::ERC2981AdminAccessControlImpl;␊ + impl ERC2981AdminAccessControlDefaultAdminRulesImpl = ERC2981Component::ERC2981AdminAccessControlDefaultAdminRulesImpl;␊ ␊ // Internal␊ impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ - impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + impl AccessControlDefaultAdminRulesInternalImpl = AccessControlDefaultAdminRulesComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ impl ERC2981InternalImpl = ERC2981Component::InternalImpl;␊ ␊ @@ -1284,7 +2006,7 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ src5: SRC5Component::Storage,␊ #[substorage(v0)]␊ - accesscontrol: AccessControlComponent::Storage,␊ + accesscontrol_dar: AccessControlDefaultAdminRulesComponent::Storage,␊ #[substorage(v0)]␊ upgradeable: UpgradeableComponent::Storage,␊ #[substorage(v0)]␊ @@ -1299,7 +2021,7 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ SRC5Event: SRC5Component::Event,␊ #[flat]␊ - AccessControlEvent: AccessControlComponent::Event,␊ + AccessControlDefaultAdminRulesEvent: AccessControlDefaultAdminRulesComponent::Event,␊ #[flat]␊ UpgradeableEvent: UpgradeableComponent::Event,␊ #[flat]␊ @@ -1309,20 +2031,23 @@ Generated by [AVA](https://avajs.dev). #[constructor]␊ fn constructor(␊ ref self: ContractState,␊ - default_admin: ContractAddress,␊ + initial_default_admin: ContractAddress,␊ upgrader: ContractAddress,␊ default_royalty_receiver: ContractAddress,␊ royalty_admin: ContractAddress,␊ ) {␊ self.erc721.initializer("MyToken", "MTK", "");␊ - self.accesscontrol.initializer();␊ + self.accesscontrol_dar.initializer(INITIAL_DELAY, initial_default_admin);␊ self.erc2981.initializer(default_royalty_receiver, 15125);␊ ␊ - self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ - self.accesscontrol._grant_role(UPGRADER_ROLE, upgrader);␊ - self.accesscontrol._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ + self.accesscontrol_dar._grant_role(UPGRADER_ROLE, upgrader);␊ + self.accesscontrol_dar._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin);␊ }␊ ␊ + impl AccessControlDefaultAdminRulesImmutableConfig of AccessControlDefaultAdminRulesComponent::ImmutableConfig {␊ + const DEFAULT_ADMIN_DELAY_INCREASE_WAIT: u64 = 604800; // 1 week␊ + }␊ + ␊ impl ERC2981ImmutableConfig of ERC2981Component::ImmutableConfig {␊ const FEE_DENOMINATOR: u128 = 100000;␊ }␊ @@ -1334,7 +2059,7 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl UpgradeableImpl of IUpgradeable {␊ fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {␊ - self.accesscontrol.assert_only_role(UPGRADER_ROLE);␊ + self.accesscontrol_dar.assert_only_role(UPGRADER_ROLE);␊ self.upgradeable.upgrade(new_class_hash);␊ }␊ }␊ @@ -1355,7 +2080,9 @@ Generated by [AVA](https://avajs.dev). use openzeppelin::governance::votes::VotesComponent;␊ use openzeppelin::introspection::src5::SRC5Component;␊ use openzeppelin::security::pausable::PausableComponent;␊ - use openzeppelin::token::common::erc2981::{DefaultConfig, ERC2981Component};␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ use openzeppelin::token::erc721::ERC721Component;␊ use openzeppelin::token::erc721::extensions::ERC721EnumerableComponent;␊ use openzeppelin::utils::contract_clock::ERC6372TimestampClock;␊ @@ -1880,7 +2607,9 @@ Generated by [AVA](https://avajs.dev). use openzeppelin::interfaces::upgrades::IUpgradeable;␊ use openzeppelin::introspection::src5::SRC5Component;␊ use openzeppelin::security::pausable::PausableComponent;␊ - use openzeppelin::token::common::erc2981::{DefaultConfig, ERC2981Component};␊ + use openzeppelin::token::common::erc2981::{␊ + DefaultConfig as ERC2981DefaultConfig, ERC2981Component␊ + };␊ use openzeppelin::token::erc721::ERC721Component;␊ use openzeppelin::token::erc721::extensions::ERC721EnumerableComponent;␊ use openzeppelin::upgrades::UpgradeableComponent;␊ diff --git a/packages/core/cairo_alpha/src/erc721.test.ts.snap b/packages/core/cairo_alpha/src/erc721.test.ts.snap index 55d8fb717..f6409227c 100644 Binary files a/packages/core/cairo_alpha/src/erc721.test.ts.snap and b/packages/core/cairo_alpha/src/erc721.test.ts.snap differ diff --git a/packages/core/cairo_alpha/src/generate/custom.ts b/packages/core/cairo_alpha/src/generate/custom.ts index 40207666a..9e7772503 100644 --- a/packages/core/cairo_alpha/src/generate/custom.ts +++ b/packages/core/cairo_alpha/src/generate/custom.ts @@ -1,19 +1,27 @@ import type { CustomOptions } from '../custom'; -import { accessOptions } from '../set-access-control'; +import type { AccessSubset } from '../set-access-control'; +import { resolveAccessControlOptions } from '../set-access-control'; import { infoOptions } from '../set-info'; import { upgradeableOptions } from '../set-upgradeable'; import { generateAlternatives } from './alternatives'; const booleans = [true, false]; -const blueprint = { - name: ['MyContract'], - pausable: booleans, - access: accessOptions, - upgradeable: upgradeableOptions, - info: infoOptions, +type GeneratorOptions = { + access: AccessSubset; }; -export function* generateCustomOptions(): Generator> { +function prepareBlueprint(opts: GeneratorOptions) { + return { + name: ['MyContract'], + pausable: booleans, + access: resolveAccessControlOptions(opts.access), + upgradeable: upgradeableOptions, + info: infoOptions, + }; +} + +export function* generateCustomOptions(opts: GeneratorOptions): Generator> { + const blueprint = prepareBlueprint(opts); yield* generateAlternatives(blueprint); } diff --git a/packages/core/cairo_alpha/src/generate/erc1155.ts b/packages/core/cairo_alpha/src/generate/erc1155.ts index bc2d3dcd9..74cc016a3 100644 --- a/packages/core/cairo_alpha/src/generate/erc1155.ts +++ b/packages/core/cairo_alpha/src/generate/erc1155.ts @@ -1,5 +1,6 @@ import type { ERC1155Options } from '../erc1155'; -import { accessOptions } from '../set-access-control'; +import type { AccessSubset } from '../set-access-control'; +import { resolveAccessControlOptions } from '../set-access-control'; import { infoOptions } from '../set-info'; import { upgradeableOptions } from '../set-upgradeable'; import type { RoyaltyInfoSubset } from '../set-royalty-info'; @@ -9,6 +10,7 @@ import { generateAlternatives } from './alternatives'; const booleans = [true, false]; type GeneratorOptions = { + access: AccessSubset; royaltyInfo: RoyaltyInfoSubset; }; @@ -23,7 +25,7 @@ function prepareBlueprint(opts: GeneratorOptions) { updatableUri: booleans, upgradeable: upgradeableOptions, royaltyInfo, - access: accessOptions, + access: resolveAccessControlOptions(opts.access), info: infoOptions, }; } diff --git a/packages/core/cairo_alpha/src/generate/erc20.ts b/packages/core/cairo_alpha/src/generate/erc20.ts index 6d73a7e9d..c652aaf27 100644 --- a/packages/core/cairo_alpha/src/generate/erc20.ts +++ b/packages/core/cairo_alpha/src/generate/erc20.ts @@ -1,27 +1,35 @@ import type { ERC20Options } from '../erc20'; -import { accessOptions } from '../set-access-control'; +import type { AccessSubset } from '../set-access-control'; +import { resolveAccessControlOptions } from '../set-access-control'; import { infoOptions } from '../set-info'; import { upgradeableOptions } from '../set-upgradeable'; import { generateAlternatives } from './alternatives'; const booleans = [true, false]; -const blueprint = { - name: ['MyToken'], - symbol: ['MTK'], - decimals: ['6', '18'], - burnable: booleans, - pausable: booleans, - mintable: booleans, - premint: ['1'], - votes: booleans, - appName: ['MyApp'], - appVersion: ['v1'], - access: accessOptions, - upgradeable: upgradeableOptions, - info: infoOptions, +type GeneratorOptions = { + access: AccessSubset; }; -export function* generateERC20Options(): Generator> { +function prepareBlueprint(opts: GeneratorOptions) { + return { + name: ['MyToken'], + symbol: ['MTK'], + decimals: ['6', '18'], + burnable: booleans, + pausable: booleans, + mintable: booleans, + premint: ['1'], + votes: booleans, + appName: ['MyApp'], + appVersion: ['v1'], + access: resolveAccessControlOptions(opts.access), + upgradeable: upgradeableOptions, + info: infoOptions, + }; +} + +export function* generateERC20Options(opts: GeneratorOptions): Generator> { + const blueprint = prepareBlueprint(opts); yield* generateAlternatives(blueprint); } diff --git a/packages/core/cairo_alpha/src/generate/erc721.ts b/packages/core/cairo_alpha/src/generate/erc721.ts index 2911e642d..461fe0e09 100644 --- a/packages/core/cairo_alpha/src/generate/erc721.ts +++ b/packages/core/cairo_alpha/src/generate/erc721.ts @@ -1,5 +1,6 @@ import type { ERC721Options } from '../erc721'; -import { accessOptions } from '../set-access-control'; +import type { AccessSubset } from '../set-access-control'; +import { resolveAccessControlOptions } from '../set-access-control'; import { defaults as infoDefaults } from '../set-info'; import { upgradeableOptions } from '../set-upgradeable'; import type { RoyaltyInfoSubset } from '../set-royalty-info'; @@ -9,6 +10,7 @@ import { generateAlternatives } from './alternatives'; const booleans = [true, false]; type GeneratorOptions = { + access: AccessSubset; royaltyInfo: RoyaltyInfoSubset; }; @@ -26,7 +28,7 @@ function prepareBlueprint(opts: GeneratorOptions) { pausable: booleans, mintable: booleans, royaltyInfo, - access: accessOptions, + access: resolveAccessControlOptions(opts.access), upgradeable: upgradeableOptions, info: [infoDefaults], }; diff --git a/packages/core/cairo_alpha/src/generate/sources.ts b/packages/core/cairo_alpha/src/generate/sources.ts index 756315225..4a407e4ea 100644 --- a/packages/core/cairo_alpha/src/generate/sources.ts +++ b/packages/core/cairo_alpha/src/generate/sources.ts @@ -17,6 +17,7 @@ import { OptionsError } from '../error'; import { findCover } from '../utils/find-cover'; import type { Contract } from '../contract'; import type { RoyaltyInfoSubset } from '../set-royalty-info'; +import type { AccessSubset } from '../set-access-control'; export type Subset = 'all' | 'minimal-cover'; @@ -24,23 +25,24 @@ export type KindSubset = 'all' | keyof KindedOptions; export function* generateOptions(params: { kind: KindSubset; + access: AccessSubset; royaltyInfo: RoyaltyInfoSubset; }): Generator { - const { kind, royaltyInfo } = params; + const { kind, access, royaltyInfo } = params; if (kind === 'all' || kind === 'ERC20') { - for (const kindOpts of generateERC20Options()) { + for (const kindOpts of generateERC20Options({ access })) { yield { kind: 'ERC20', ...kindOpts }; } } if (kind === 'all' || kind === 'ERC721') { - for (const kindOpts of generateERC721Options({ royaltyInfo })) { + for (const kindOpts of generateERC721Options({ access, royaltyInfo })) { yield { kind: 'ERC721', ...kindOpts }; } } if (kind === 'all' || kind === 'ERC1155') { - for (const kindOpts of generateERC1155Options({ royaltyInfo })) { + for (const kindOpts of generateERC1155Options({ access, royaltyInfo })) { yield { kind: 'ERC1155', ...kindOpts }; } } @@ -70,7 +72,7 @@ export function* generateOptions(params: { } if (kind === 'all' || kind === 'Custom') { - for (const kindOpts of generateCustomOptions()) { + for (const kindOpts of generateCustomOptions({ access })) { yield { kind: 'Custom', ...kindOpts }; } } @@ -89,12 +91,13 @@ interface GeneratedSource extends GeneratedContract { function generateContractSubset(params: { subset: Subset; kind: KindSubset; + access: AccessSubset; royaltyInfo: RoyaltyInfoSubset; }): GeneratedContract[] { - const { subset, kind, royaltyInfo } = params; + const { subset, kind, access, royaltyInfo } = params; const contracts = []; - for (const options of generateOptions({ kind, royaltyInfo })) { + for (const options of generateOptions({ kind, access, royaltyInfo })) { const id = crypto.createHash('sha1').update(JSON.stringify(options)).digest().toString('hex'); try { const contract = buildGeneric(options); @@ -143,11 +146,12 @@ export function* generateSources(params: { subset: Subset; uniqueName: boolean; kind: KindSubset; + access: AccessSubset; royaltyInfo: RoyaltyInfoSubset; }): Generator { - const { subset, uniqueName, kind, royaltyInfo } = params; + const { subset, uniqueName, kind, access, royaltyInfo } = params; let counter = 1; - for (const c of generateContractSubset({ subset, kind, royaltyInfo })) { + for (const c of generateContractSubset({ subset, kind, access, royaltyInfo })) { if (uniqueName) { c.contract.name = `Contract${counter++}`; } @@ -161,41 +165,91 @@ export async function writeGeneratedSources(params: { subset: Subset; uniqueName: boolean; kind: KindSubset; + access: AccessSubset; royaltyInfo: RoyaltyInfoSubset; logsEnabled: boolean; }): Promise { - const { dir, subset, uniqueName, kind, royaltyInfo, logsEnabled } = params; + const { dir, subset, uniqueName, kind, access, royaltyInfo, logsEnabled } = params; await fs.mkdir(dir, { recursive: true }); const contractNames = []; - for (const { id, contract, source } of generateSources({ subset, uniqueName, kind, royaltyInfo })) { + for (const { id, contract, source } of generateSources({ subset, uniqueName, kind, access, royaltyInfo })) { const name = uniqueName ? contract.name : id; await fs.writeFile(path.format({ dir, name, ext: '.cairo' }), source); contractNames.push(name); } if (logsEnabled) { - const sourceLabel = resolveSourceLabel({ kind, royaltyInfo }); + const sourceLabel = resolveSourceLabel({ kind, access, royaltyInfo }); console.log(`Generated ${contractNames.length} contracts for ${sourceLabel}`); } return contractNames; } -function resolveSourceLabel(params: { kind: KindSubset; royaltyInfo: RoyaltyInfoSubset }): string { - const { kind, royaltyInfo } = params; +function resolveSourceLabel(params: { + kind: KindSubset; + access: AccessSubset; + royaltyInfo: RoyaltyInfoSubset; +}): string { + const { kind, access, royaltyInfo } = params; + return [resolveKindLabel(kind), resolveAccessLabel(kind, access), resolveRoyaltyInfoLabel(kind, royaltyInfo)] + .filter(elem => elem !== undefined) + .join(', '); +} + +function resolveKindLabel(kind: KindSubset): string { switch (kind) { + case 'all': + return 'All kinds'; + case 'ERC20': case 'ERC721': case 'ERC1155': - return `${kind} (royaltyInfo: ${royaltyInfo})`; + case 'Account': + case 'Multisig': + case 'Governor': + case 'Vesting': + case 'Custom': + return kind; + default: { + const _: never = kind; + throw new Error('Unknown kind'); + } + } +} + +function resolveAccessLabel(kind: KindSubset, access: AccessSubset): string | undefined { + switch (kind) { case 'all': - return 'All contract kinds'; + case 'Custom': case 'ERC20': + case 'ERC721': + case 'ERC1155': + return `access: ${access}`; case 'Account': case 'Multisig': case 'Governor': case 'Vesting': + return undefined; + default: { + const _: never = kind; + throw new Error('Unknown kind'); + } + } +} + +function resolveRoyaltyInfoLabel(kind: KindSubset, royaltyInfo: RoyaltyInfoSubset): string | undefined { + switch (kind) { + case 'all': + case 'ERC721': + case 'ERC1155': + return `royalty: ${royaltyInfo}`; + case 'ERC20': + case 'Account': case 'Custom': - return kind; + case 'Multisig': + case 'Governor': + case 'Vesting': + return undefined; default: { const _: never = kind; throw new Error('Unknown kind'); diff --git a/packages/core/cairo_alpha/src/governor.test.ts.md b/packages/core/cairo_alpha/src/governor.test.ts.md index 701390ad3..46f2acb65 100644 --- a/packages/core/cairo_alpha/src/governor.test.ts.md +++ b/packages/core/cairo_alpha/src/governor.test.ts.md @@ -13,7 +13,9 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyGovernor {␊ - use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::{␊ + DefaultConfig as GovernorDefaultConfig, GovernorComponent␊ + };␊ use openzeppelin::governance::governor::extensions::{␊ GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ @@ -155,7 +157,9 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyGovernor {␊ - use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::{␊ + DefaultConfig as GovernorDefaultConfig, GovernorComponent␊ + };␊ use openzeppelin::governance::governor::extensions::{␊ GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ @@ -273,7 +277,9 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyGovernor {␊ - use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::{␊ + DefaultConfig as GovernorDefaultConfig, GovernorComponent␊ + };␊ use openzeppelin::governance::governor::extensions::{␊ GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ @@ -415,7 +421,9 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyGovernor {␊ - use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::{␊ + DefaultConfig as GovernorDefaultConfig, GovernorComponent␊ + };␊ use openzeppelin::governance::governor::extensions::{␊ GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ @@ -557,7 +565,9 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod CustomGovernor {␊ - use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::{␊ + DefaultConfig as GovernorDefaultConfig, GovernorComponent␊ + };␊ use openzeppelin::governance::governor::extensions::{␊ GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ @@ -699,7 +709,9 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyGovernor {␊ - use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::{␊ + DefaultConfig as GovernorDefaultConfig, GovernorComponent␊ + };␊ use openzeppelin::governance::governor::extensions::{␊ GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ @@ -841,7 +853,9 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyGovernor {␊ - use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::{␊ + DefaultConfig as GovernorDefaultConfig, GovernorComponent␊ + };␊ use openzeppelin::governance::governor::extensions::{␊ GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ GovernorTimelockExecutionComponent, GovernorVotesComponent␊ @@ -992,7 +1006,9 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyGovernor {␊ - use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::{␊ + DefaultConfig as GovernorDefaultConfig, GovernorComponent␊ + };␊ use openzeppelin::governance::governor::extensions::{␊ GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ @@ -1134,7 +1150,9 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyGovernor {␊ - use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::{␊ + DefaultConfig as GovernorDefaultConfig, GovernorComponent␊ + };␊ use openzeppelin::governance::governor::extensions::{␊ GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ GovernorTimelockExecutionComponent, GovernorVotesQuorumFractionComponent␊ @@ -1276,7 +1294,9 @@ Generated by [AVA](https://avajs.dev). ␊ #[starknet::contract]␊ mod MyGovernor {␊ - use openzeppelin::governance::governor::{DefaultConfig, GovernorComponent};␊ + use openzeppelin::governance::governor::{␊ + DefaultConfig as GovernorDefaultConfig, GovernorComponent␊ + };␊ use openzeppelin::governance::governor::extensions::{␊ GovernorCountingSimpleComponent, GovernorSettingsComponent,␊ GovernorTimelockExecutionComponent, GovernorVotesComponent␊ diff --git a/packages/core/cairo_alpha/src/governor.test.ts.snap b/packages/core/cairo_alpha/src/governor.test.ts.snap index 41881efad..7ed14d42c 100644 Binary files a/packages/core/cairo_alpha/src/governor.test.ts.snap and b/packages/core/cairo_alpha/src/governor.test.ts.snap differ diff --git a/packages/core/cairo_alpha/src/governor.ts b/packages/core/cairo_alpha/src/governor.ts index 12b46b9d3..7268fd387 100644 --- a/packages/core/cairo_alpha/src/governor.ts +++ b/packages/core/cairo_alpha/src/governor.ts @@ -7,7 +7,7 @@ import { printContract } from './print'; import { setInfo } from './set-info'; import { setUpgradeableGovernor } from './set-upgradeable'; import { defineComponents } from './utils/define-components'; -import { durationToTimestamp } from './utils/duration'; +import { durationToSeconds } from './utils/duration'; import { addSNIP12Metadata, addSRC5Component } from './common-components'; import { toUint, isNaturalNumber } from './utils/convert-strings'; export const clockModeOptions = ['timestamp'] as const; @@ -269,7 +269,7 @@ const components = defineComponents({ function addBase(c: ContractBuilder, _: GovernorOptions) { c.addUseClause('starknet', 'ContractAddress'); - c.addUseClause('openzeppelin::governance::governor', 'DefaultConfig'); + c.addUseClause('openzeppelin::governance::governor', 'DefaultConfig', { alias: 'GovernorDefaultConfig' }); c.addConstructorArgument({ name: 'votes_token', type: 'ContractAddress' }); c.addUseClause('openzeppelin::governance::governor::GovernorComponent', 'InternalTrait', { alias: 'GovernorInternalTrait', @@ -316,7 +316,7 @@ function addSettings(c: ContractBuilder, allOpts: Required) { function getVotingDelay(opts: Required): number { try { if (opts.clockMode === 'timestamp') { - return durationToTimestamp(opts.delay); + return durationToSeconds(opts.delay); } else { throw new Error('Invalid clock mode'); } @@ -334,7 +334,7 @@ function getVotingDelay(opts: Required): number { function getVotingPeriod(opts: Required): number { try { if (opts.clockMode === 'timestamp') { - return durationToTimestamp(opts.period); + return durationToSeconds(opts.period); } else { throw new Error('Invalid clock mode'); } diff --git a/packages/core/cairo_alpha/src/index.ts b/packages/core/cairo_alpha/src/index.ts index 91eee658a..25d51a165 100644 --- a/packages/core/cairo_alpha/src/index.ts +++ b/packages/core/cairo_alpha/src/index.ts @@ -6,7 +6,7 @@ export { ContractBuilder } from './contract'; export { printContract } from './print'; -export type { Access } from './set-access-control'; +export type { Access, AccessType } from './set-access-control'; export type { Account } from './account'; export type { Upgradeable } from './set-upgradeable'; export type { Info } from './set-info'; diff --git a/packages/core/cairo_alpha/src/multisig.ts b/packages/core/cairo_alpha/src/multisig.ts index 8b017d39e..757ba2b7e 100644 --- a/packages/core/cairo_alpha/src/multisig.ts +++ b/packages/core/cairo_alpha/src/multisig.ts @@ -2,7 +2,7 @@ import type { Contract } from './contract'; import { ContractBuilder } from './contract'; import type { CommonOptions } from './common-options'; import { contractDefaults as commonDefaults } from './common-options'; -import { setAccessControl } from './set-access-control'; +import { setAccessControl, AccessControl } from './set-access-control'; import { setUpgradeableMultisig } from './set-upgradeable'; import { setInfo } from './set-info'; import { defineComponents } from './utils/define-components'; @@ -46,7 +46,7 @@ export function buildMultisig(opts: MultisigOptions): Contract { // A Multisig contract is exclusively governed by its own multisig process. // The collective approval of the designated signers determines all actions. // No other access-control mechanism should override or bypass this process. - setAccessControl(c, false); + setAccessControl(c, AccessControl.None()); return c; } diff --git a/packages/core/cairo_alpha/src/print.ts b/packages/core/cairo_alpha/src/print.ts index 7853f1319..ecac44fa2 100644 --- a/packages/core/cairo_alpha/src/print.ts +++ b/packages/core/cairo_alpha/src/print.ts @@ -7,6 +7,7 @@ import type { ContractFunction, ImplementedTrait, UseClause, + Variable, } from './contract'; import { formatLines, spaceBetween } from './utils/format-lines'; @@ -34,7 +35,7 @@ export function printContract(contract: Contract): string { `mod ${contract.name} {`, spaceBetween( printUseClauses(contract), - printConstants(contract), + printConstants(contract.constants), printComponentDeclarations(contract), printImpls(contract), printStorage(contract), @@ -135,9 +136,9 @@ function sortUseClauses(contract: Contract): UseClause[] { }); } -function printConstants(contract: Contract): Lines[] { +function printConstants(constants: Variable[]): Lines[] { const lines = []; - for (const constant of contract.constants) { + for (const constant of constants) { // inlineComment is optional, default to false const inlineComment = constant.inlineComment ?? false; const commented = !!constant.comment; @@ -284,7 +285,7 @@ function printImplementedTrait(trait: ImplementedTrait): Lines[] { implLines.push(...trait.tags.map(t => `#[${t}]`)); implLines.push(`impl ${trait.name} of ${trait.of} {`); - const superVars = withSemicolons(trait.superVariables.map(v => `const ${v.name}: ${v.type} = ${v.value}`)); + const superVars = printConstants(trait.superVariables); implLines.push(superVars); const fns = trait.functions.map(fn => printFunction(fn)); diff --git a/packages/core/cairo_alpha/src/scripts/update-scarb-project.ts b/packages/core/cairo_alpha/src/scripts/update-scarb-project.ts index ae4db934d..26f894f99 100644 --- a/packages/core/cairo_alpha/src/scripts/update-scarb-project.ts +++ b/packages/core/cairo_alpha/src/scripts/update-scarb-project.ts @@ -2,33 +2,31 @@ import { promises as fs } from 'fs'; import path from 'path'; import type { KindSubset } from '../generate/sources'; +import type { AccessSubset } from '../set-access-control'; +import type { RoyaltyInfoSubset } from '../set-royalty-info'; import { writeGeneratedSources } from '../generate/sources'; import { contractsVersion, edition, cairoVersion, scarbVersion } from '../utils/version'; -import type { RoyaltyInfoSubset } from '../set-royalty-info'; type Arguments = { kind: KindSubset; + access: AccessSubset; royaltyInfo: RoyaltyInfoSubset; }; +const defaults: Arguments = { + kind: 'all', + access: 'all', + royaltyInfo: 'all', +} as const; + export function resolveArguments(): Arguments { const cliArgs = process.argv.slice(2); - switch (cliArgs.length) { - case 0: - return { kind: 'all', royaltyInfo: 'all' }; - case 1: - return { - kind: parseKindSubset(cliArgs[0]), - royaltyInfo: 'all', - }; - case 2: - return { - kind: parseKindSubset(cliArgs[0]), - royaltyInfo: parseRoyaltyInfoSubset(cliArgs[1]), - }; - default: - throw new Error(`Too many CLI arguments provided: ${cliArgs.length}.`); - } + const args = parseCliArgs(cliArgs); + return { + kind: parseKindSubset(args.kind ?? defaults.kind), + access: parseAccessSubset(args.access ?? defaults.access), + royaltyInfo: parseRoyaltyInfoSubset(args.royalty ?? defaults.royaltyInfo), + }; } export async function updateScarbProject() { @@ -36,12 +34,13 @@ export async function updateScarbProject() { await fs.rm(generatedSourcesPath, { force: true, recursive: true }); // Generate the contracts source code - const { kind, royaltyInfo } = resolveArguments(); + const { kind, access, royaltyInfo } = resolveArguments(); const contractNames = await writeGeneratedSources({ dir: generatedSourcesPath, subset: 'all', uniqueName: true, kind, + access, royaltyInfo, logsEnabled: true, }); @@ -79,6 +78,25 @@ async function updateScarbToml() { await fs.writeFile(scarbTomlPath, updatedContent, 'utf8'); } +function parseCliArgs(args: string[]): Record { + const result: Record = {}; + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + if (!arg) continue; + if (arg.startsWith('--') && arg.slice(2).includes('=')) { + const [key, value] = arg.slice(2).split('=', 2); + if (key && value !== undefined) { + result[key] = value; + } else { + throw new Error(`Invalid argument format: ${arg}`); + } + } else { + throw new Error(`Invalid argument format: ${arg}. Expected --key=value`); + } + } + return result; +} + function parseKindSubset(value: string | undefined): KindSubset { if (value === undefined) { throw new Error(`Failed to resolve contract kind subset from 'undefined'.`); @@ -107,6 +125,30 @@ function parseKindSubset(value: string | undefined): KindSubset { } } +function parseAccessSubset(value: string | undefined): AccessSubset { + if (value === undefined) { + throw new Error(`Failed to resolve access subset from 'undefined'.`); + } + switch (value.toLowerCase()) { + case 'all': + return 'all'; + case 'disabled': + return 'disabled'; + case 'ownable': + return 'ownable'; + case 'roles': + return 'roles'; + case 'roles-dar-default': + case 'roles_dar_default': + return 'roles-dar-default'; + case 'roles-dar-custom': + case 'roles_dar_custom': + return 'roles-dar-custom'; + default: + throw new Error(`Failed to resolve access subset from '${value}' value.`); + } +} + function parseRoyaltyInfoSubset(value: string | undefined): RoyaltyInfoSubset { if (value === undefined) { throw new Error(`Failed to resolve royalty info subset from 'undefined'.`); @@ -116,10 +158,12 @@ function parseRoyaltyInfoSubset(value: string | undefined): RoyaltyInfoSubset { return 'all'; case 'disabled': return 'disabled'; + case 'enabled-default': case 'enabled_default': - return 'enabled_default'; + return 'enabled-default'; + case 'enabled-custom': case 'enabled_custom': - return 'enabled_custom'; + return 'enabled-custom'; default: throw new Error(`Failed to resolve royalty info subset from '${value}' value.`); } diff --git a/packages/core/cairo_alpha/src/set-access-control.ts b/packages/core/cairo_alpha/src/set-access-control.ts index 33014de5b..b912ef50a 100644 --- a/packages/core/cairo_alpha/src/set-access-control.ts +++ b/packages/core/cairo_alpha/src/set-access-control.ts @@ -1,23 +1,92 @@ import type { BaseFunction, BaseImplementedTrait, ContractBuilder } from './contract'; import { defineComponents } from './utils/define-components'; import { addSRC5Component } from './common-components'; +import { durationToSeconds } from './utils/duration'; +import { OptionsError } from './error'; +import { toUint } from './utils/convert-strings'; + +export type AccessType = + | false + | 'ownable' + | 'roles' // AccessControl + | 'roles-dar'; // AccessControlDefaultAdminRules + +export type RolesDefaultAdminRulesOptions = { + darInitialDelay: string; + darDefaultDelayIncrease: string; +}; + +export type Access = { type: AccessType } & RolesDefaultAdminRulesOptions; + +export const darDefaultOpts: RolesDefaultAdminRulesOptions = { + darInitialDelay: '1 day', + darDefaultDelayIncrease: '5 days', +}; + +export const darCustomOpts: RolesDefaultAdminRulesOptions = { + darInitialDelay: '2 days', + darDefaultDelayIncrease: '1 week', +}; + +type AccessControlFactory = { + readonly None: () => Readonly<{ type: false } & typeof darDefaultOpts>; + readonly Ownable: () => Readonly<{ type: 'ownable' } & typeof darDefaultOpts>; + readonly Roles: () => Readonly<{ type: 'roles' } & typeof darDefaultOpts>; + readonly RolesDefaultAdminRules: ( + opts: RolesDefaultAdminRulesOptions, + ) => Readonly<{ type: 'roles-dar' } & RolesDefaultAdminRulesOptions>; +}; + +export const AccessControl = { + None: () => ({ type: false, ...darDefaultOpts }) as const, + Ownable: () => ({ type: 'ownable', ...darDefaultOpts }) as const, + Roles: () => ({ type: 'roles', ...darDefaultOpts }) as const, + RolesDefaultAdminRules: (opts: RolesDefaultAdminRulesOptions) => ({ type: 'roles-dar', ...opts }) as const, +} as const satisfies AccessControlFactory; -export const accessOptions = [false, 'ownable', 'roles'] as const; export const DEFAULT_ACCESS_CONTROL = 'ownable'; -export type Access = (typeof accessOptions)[number]; +export type AccessSubset = 'all' | 'disabled' | 'ownable' | 'roles' | 'roles-dar-default' | 'roles-dar-custom'; -/** - * Sets access control for the contract by adding inheritance. - */ +export function resolveAccessControlOptions(subset: AccessSubset): Access[] { + switch (subset) { + case 'all': + return [ + AccessControl.None(), + AccessControl.Ownable(), + AccessControl.Roles(), + AccessControl.RolesDefaultAdminRules(darDefaultOpts), + AccessControl.RolesDefaultAdminRules(darCustomOpts), + ]; + case 'disabled': + return [AccessControl.None()]; + case 'ownable': + return [AccessControl.Ownable()]; + case 'roles': + return [AccessControl.Roles()]; + case 'roles-dar-default': + return [AccessControl.RolesDefaultAdminRules(darDefaultOpts)]; + case 'roles-dar-custom': + return [AccessControl.RolesDefaultAdminRules(darCustomOpts)]; + default: { + const _: never = subset; + throw new Error('Unknown AccessControlSubset'); + } + } +} + +const DEFAULT_ADMIN_DELAY_INCREASE_WAIT = BigInt(5 * 24 * 60 * 60); // 5 days + +/// Sets access control for the contract by adding inheritance. export function setAccessControl(c: ContractBuilder, access: Access): void { - switch (access) { + switch (access.type) { + case false: { + break; + } case 'ownable': { c.addComponent(components.OwnableComponent, [{ lit: 'owner' }], true); - c.addUseClause('starknet', 'ContractAddress'); c.addConstructorArgument({ name: 'owner', type: 'ContractAddress' }); - break; } case 'roles': { @@ -55,6 +124,76 @@ export function setAccessControl(c: ContractBuilder, access: Access): void { } break; } + case 'roles-dar': { + const initParams = [{ lit: 'INITIAL_DELAY' }, { lit: 'initial_default_admin' }]; + if (c.addComponent(components.AccessControlDefaultAdminRulesComponent, initParams, true)) { + if (c.interfaceFlags.has('ISRC5')) { + c.addImplToComponent(components.AccessControlDefaultAdminRulesComponent, { + name: 'AccessControlImpl', + value: 'AccessControlDefaultAdminRulesComponent::AccessControlImpl', + }); + c.addImplToComponent(components.AccessControlDefaultAdminRulesComponent, { + name: 'AccessControlDefaultAdminRulesImpl', + value: 'AccessControlDefaultAdminRulesComponent::AccessControlDefaultAdminRulesImpl', + }); + c.addImplToComponent(components.AccessControlDefaultAdminRulesComponent, { + name: 'AccessControlCamelImpl', + value: 'AccessControlDefaultAdminRulesComponent::AccessControlCamelImpl', + }); + c.addImplToComponent(components.AccessControlDefaultAdminRulesComponent, { + name: 'AccessControlWithDelayImpl', + value: 'AccessControlDefaultAdminRulesComponent::AccessControlWithDelayImpl', + }); + } else { + c.addImplToComponent(components.AccessControlDefaultAdminRulesComponent, { + name: 'AccessControlMixinImpl', + value: 'AccessControlDefaultAdminRulesComponent::AccessControlMixinImpl', + }); + c.addInterfaceFlag('ISRC5'); + } + addSRC5Component(c); + const initialDelay = toUint(getInitialDelay(access), 'darInitialDelay', 'u64'); + c.addConstant({ + name: 'INITIAL_DELAY', + type: 'u64', + value: initialDelay.toString(), + comment: access.darInitialDelay, + inlineComment: true, + }); + const defaultAdminDelayIncreaseWait = toUint( + getDefaultAdminDelayIncreaseWait(access), + 'darDefaultDelayIncrease', + 'u64', + ); + if (defaultAdminDelayIncreaseWait === DEFAULT_ADMIN_DELAY_INCREASE_WAIT) { + c.addUseClause('openzeppelin::access::accesscontrol::extensions', 'DefaultConfig', { + alias: 'AccessControlDefaultAdminRulesDefaultConfig', + }); + } else { + const trait: BaseImplementedTrait = { + name: 'AccessControlDefaultAdminRulesImmutableConfig', + of: 'AccessControlDefaultAdminRulesComponent::ImmutableConfig', + tags: [], + }; + c.addImplementedTrait(trait); + c.addSuperVariableToTrait(trait, { + name: 'DEFAULT_ADMIN_DELAY_INCREASE_WAIT', + type: 'u64', + value: defaultAdminDelayIncreaseWait.toString(), + comment: access.darDefaultDelayIncrease, + inlineComment: true, + }); + } + + c.addUseClause('starknet', 'ContractAddress'); + c.addConstructorArgument({ name: 'initial_default_admin', type: 'ContractAddress' }); + } + break; + } + default: { + const _: never = access.type; + throw new Error('Unknown access type'); + } } } @@ -65,39 +204,77 @@ export function requireAccessControl( c: ContractBuilder, trait: BaseImplementedTrait, fn: BaseFunction, - access: Access, + accessObj: Access, roleIdPrefix: string, roleOwner: string | undefined, ): void { - if (access === false) { - access = DEFAULT_ACCESS_CONTROL; + const access = { ...accessObj }; // make a copy to avoid mutating caller-supplied object + if (access.type === false) { + access.type = DEFAULT_ACCESS_CONTROL; } setAccessControl(c, access); - switch (access) { + switch (access.type) { case 'ownable': { c.addFunctionCodeBefore(trait, fn, 'self.ownable.assert_only_owner()'); break; } - case 'roles': { + case 'roles': + case 'roles-dar': { const roleId = roleIdPrefix + '_ROLE'; const addedSuper = c.addSuperVariable({ name: roleId, type: 'felt252', value: `selector!("${roleId}")`, }); + const substorageName = + access.type === 'roles-dar' + ? components.AccessControlDefaultAdminRulesComponent.substorage.name + : components.AccessControlComponent.substorage.name; if (roleOwner !== undefined) { c.addUseClause('starknet', 'ContractAddress'); c.addConstructorArgument({ name: roleOwner, type: 'ContractAddress' }); if (addedSuper) { - c.addConstructorCode(`self.accesscontrol._grant_role(${roleId}, ${roleOwner})`); + c.addConstructorCode(`self.${substorageName}._grant_role(${roleId}, ${roleOwner})`); } } - c.addFunctionCodeBefore(trait, fn, `self.accesscontrol.assert_only_role(${roleId})`); + c.addFunctionCodeBefore(trait, fn, `self.${substorageName}.assert_only_role(${roleId})`); break; } + default: { + const _: never = access.type; + throw new Error('Unknown access type'); + } + } +} + +function getInitialDelay(opts: RolesDefaultAdminRulesOptions): number { + try { + return durationToSeconds(opts.darInitialDelay); + } catch (e) { + if (e instanceof Error) { + throw new OptionsError({ + darInitialDelay: e.message, + }); + } else { + throw e; + } + } +} + +function getDefaultAdminDelayIncreaseWait(opts: RolesDefaultAdminRulesOptions): number { + try { + return durationToSeconds(opts.darDefaultDelayIncrease); + } catch (e) { + if (e instanceof Error) { + throw new OptionsError({ + darDefaultDelayIncrease: e.message, + }); + } else { + throw e; + } } } @@ -142,4 +319,22 @@ const components = defineComponents({ }, ], }, + AccessControlDefaultAdminRulesComponent: { + path: 'openzeppelin::access::accesscontrol::extensions', + substorage: { + name: 'accesscontrol_dar', + type: 'AccessControlDefaultAdminRulesComponent::Storage', + }, + event: { + name: 'AccessControlDefaultAdminRulesEvent', + type: 'AccessControlDefaultAdminRulesComponent::Event', + }, + impls: [ + { + name: 'AccessControlDefaultAdminRulesInternalImpl', + embed: false, + value: 'AccessControlDefaultAdminRulesComponent::InternalImpl', + }, + ], + }, }); diff --git a/packages/core/cairo_alpha/src/set-royalty-info.ts b/packages/core/cairo_alpha/src/set-royalty-info.ts index ae13ee2a0..c6024aa8e 100644 --- a/packages/core/cairo_alpha/src/set-royalty-info.ts +++ b/packages/core/cairo_alpha/src/set-royalty-info.ts @@ -13,7 +13,7 @@ export const defaults: RoyaltyInfoOptions = { feeDenominator: DEFAULT_FEE_DENOMINATOR.toString(), }; -export type RoyaltyInfoSubset = 'all' | 'disabled' | 'enabled_default' | 'enabled_custom'; +export type RoyaltyInfoSubset = 'all' | 'disabled' | 'enabled-default' | 'enabled-custom'; export const royaltyInfoOptions = { disabled: defaults, @@ -36,9 +36,9 @@ export function resolveRoyaltyOptionsSubset(subset: RoyaltyInfoSubset): RoyaltyI return [disabled, enabledDefault, enabledCustom]; case 'disabled': return [disabled]; - case 'enabled_default': + case 'enabled-default': return [enabledDefault]; - case 'enabled_custom': + case 'enabled-custom': return [enabledCustom]; default: { const _: never = subset; @@ -53,18 +53,18 @@ export type RoyaltyInfoOptions = { feeDenominator: string; }; -export function setRoyaltyInfo(c: ContractBuilder, options: RoyaltyInfoOptions, access: Access): void { +export function setRoyaltyInfo(c: ContractBuilder, options: RoyaltyInfoOptions, accessObj: Access): void { if (!options.enabled) { return; } - if (access === false) { - access = DEFAULT_ACCESS_CONTROL; + const access = { ...accessObj }; // make a copy to avoid mutating caller-supplied object + if (access.type === false) { + access.type = DEFAULT_ACCESS_CONTROL; } setAccessControl(c, access); const { defaultRoyaltyFraction, feeDenominator } = getRoyaltyParameters(options); const initParams = [{ lit: 'default_royalty_receiver' }, defaultRoyaltyFraction]; - c.addComponent(components.ERC2981Component, initParams, true); c.addUseClause('starknet', 'ContractAddress'); c.addConstructorArgument({ @@ -72,14 +72,15 @@ export function setRoyaltyInfo(c: ContractBuilder, options: RoyaltyInfoOptions, type: 'ContractAddress', }); - switch (access) { - case 'ownable': + switch (access.type) { + case 'ownable': { c.addImplToComponent(components.ERC2981Component, { name: 'ERC2981AdminOwnableImpl', value: `ERC2981Component::ERC2981AdminOwnableImpl`, }); break; - case 'roles': + } + case 'roles': { c.addImplToComponent(components.ERC2981Component, { name: 'ERC2981AdminAccessControlImpl', value: `ERC2981Component::ERC2981AdminAccessControlImpl`, @@ -90,10 +91,26 @@ export function setRoyaltyInfo(c: ContractBuilder, options: RoyaltyInfoOptions, }); c.addConstructorCode('self.accesscontrol._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin)'); break; + } + case 'roles-dar': { + c.addImplToComponent(components.ERC2981Component, { + name: 'ERC2981AdminAccessControlDefaultAdminRulesImpl', + value: `ERC2981Component::ERC2981AdminAccessControlDefaultAdminRulesImpl`, + }); + c.addConstructorArgument({ + name: 'royalty_admin', + type: 'ContractAddress', + }); + c.addConstructorCode('self.accesscontrol_dar._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin)'); + break; + } + default: { + const _: never = access.type; + throw new Error('Unknown access type'); + } } - if (feeDenominator === DEFAULT_FEE_DENOMINATOR) { - c.addUseClause('openzeppelin::token::common::erc2981', 'DefaultConfig'); + c.addUseClause('openzeppelin::token::common::erc2981', 'DefaultConfig', { alias: 'ERC2981DefaultConfig' }); } else { const trait: BaseImplementedTrait = { name: 'ERC2981ImmutableConfig', diff --git a/packages/core/cairo_alpha/src/test.ts b/packages/core/cairo_alpha/src/test.ts index f5a99332c..7eb96152b 100644 --- a/packages/core/cairo_alpha/src/test.ts +++ b/packages/core/cairo_alpha/src/test.ts @@ -5,10 +5,11 @@ import _test from 'ava'; import path from 'path'; import type { KindSubset } from './generate/sources'; -import { generateSources, writeGeneratedSources } from './generate/sources'; +import type { AccessSubset } from './set-access-control'; +import type { RoyaltyInfoSubset } from './set-royalty-info'; import type { GenericOptions } from './build-generic'; +import { generateSources, writeGeneratedSources } from './generate/sources'; import { custom, erc20, erc721, erc1155 } from './api'; -import type { RoyaltyInfoSubset } from './set-royalty-info'; interface Context { generatedSourcesPath: string; @@ -17,15 +18,15 @@ interface Context { const test = _test as TestFn; test.serial('erc20 results generated', async ctx => { - await testGenerate({ ctx, kind: 'ERC20' }); + await testGenerate({ ctx, kind: 'ERC20', access: 'all' }); }); test.serial('erc721 results generated', async ctx => { - await testGenerate({ ctx, kind: 'ERC721', royaltyInfo: 'all' }); + await testGenerate({ ctx, kind: 'ERC721', access: 'all', royaltyInfo: 'all' }); }); test.serial('erc1155 results generated', async ctx => { - await testGenerate({ ctx, kind: 'ERC1155', royaltyInfo: 'all' }); + await testGenerate({ ctx, kind: 'ERC1155', access: 'all', royaltyInfo: 'all' }); }); test.serial('account results generated', async ctx => { @@ -45,15 +46,16 @@ test.serial('vesting results generated', async ctx => { }); test.serial('custom results generated', async ctx => { - await testGenerate({ ctx, kind: 'Custom' }); + await testGenerate({ ctx, kind: 'Custom', access: 'all' }); }); async function testGenerate(params: { ctx: ExecutionContext; kind: KindSubset; + access?: AccessSubset; royaltyInfo?: RoyaltyInfoSubset; }) { - const { ctx, kind, royaltyInfo } = params; + const { ctx, kind, access, royaltyInfo } = params; const generatedSourcesPath = path.join(os.tmpdir(), 'oz-wizard-cairo-alpha'); await fs.rm(generatedSourcesPath, { force: true, recursive: true }); await writeGeneratedSources({ @@ -61,6 +63,7 @@ async function testGenerate(params: { subset: 'all', uniqueName: true, kind, + access: access || 'all', royaltyInfo: royaltyInfo || 'all', logsEnabled: false, }); @@ -96,6 +99,7 @@ test('is access control required', async t => { subset: 'all', uniqueName: false, kind: 'all', + access: 'all', royaltyInfo: 'all', }); for (const contract of allSources) { @@ -112,7 +116,7 @@ test('is access control required', async t => { case 'ERC721': case 'ERC1155': case 'Custom': - if (!contract.options.access) { + if (!contract.options.access?.type) { if (isAccessControlRequired(contract.options)) { t.regex(contract.source, regexOwnable, JSON.stringify(contract.options)); } else { diff --git a/packages/core/cairo_alpha/src/utils/duration.ts b/packages/core/cairo_alpha/src/utils/duration.ts index cfbaf279e..254e805c2 100644 --- a/packages/core/cairo_alpha/src/utils/duration.ts +++ b/packages/core/cairo_alpha/src/utils/duration.ts @@ -11,7 +11,7 @@ const month = 30 * day; const year = 365 * day; const secondsForUnit = { second, minute, hour, day, week, month, year }; -export function durationToTimestamp(duration: string): number { +export function durationToSeconds(duration: string): number { const match = duration.trim().match(durationPattern); if (!match || match.length < 2) { diff --git a/packages/core/cairo_alpha/src/vesting.ts b/packages/core/cairo_alpha/src/vesting.ts index b5fa9cc85..9365aca35 100644 --- a/packages/core/cairo_alpha/src/vesting.ts +++ b/packages/core/cairo_alpha/src/vesting.ts @@ -1,14 +1,14 @@ import type { BaseImplementedTrait, Contract } from './contract'; import { ContractBuilder } from './contract'; import { contractDefaults as commonDefaults } from './common-options'; -import { setAccessControl } from './set-access-control'; +import { AccessControl, setAccessControl } from './set-access-control'; import { setUpgradeable } from './set-upgradeable'; import type { Info } from './set-info'; import { setInfo } from './set-info'; import { defineComponents } from './utils/define-components'; import { printContract } from './print'; import { OptionsError } from './error'; -import { durationToTimestamp } from './utils/duration'; +import { durationToSeconds } from './utils/duration'; import { toUint, validateUint } from './utils/convert-strings'; export type VestingSchedule = 'linear' | 'custom'; @@ -55,7 +55,7 @@ export function buildVesting(opts: VestingOptions): Contract { setInfo(c, allOpts.info); // Vesting component depends on Ownable component - const access = 'ownable'; + const access = AccessControl.Ownable(); setAccessControl(c, access); // Must be non-upgradable to guarantee vesting according to the schedule @@ -159,7 +159,7 @@ function getVestingStart(opts: VestingOptions): { timestampInSec: bigint; format function getVestingDuration(opts: VestingOptions): number { try { - return durationToTimestamp(opts.duration); + return durationToSeconds(opts.duration); } catch (e) { if (e instanceof Error) { throw new OptionsError({ @@ -173,7 +173,7 @@ function getVestingDuration(opts: VestingOptions): number { function getCliffDuration(opts: VestingOptions): number { try { - return durationToTimestamp(opts.cliffDuration); + return durationToSeconds(opts.cliffDuration); } catch (e) { if (e instanceof Error) { throw new OptionsError({ diff --git a/packages/ui/api/ai-assistant/function-definitions/cairo-alpha-shared.ts b/packages/ui/api/ai-assistant/function-definitions/cairo-alpha-shared.ts index c05b58a9f..0112678a8 100644 --- a/packages/ui/api/ai-assistant/function-definitions/cairo-alpha-shared.ts +++ b/packages/ui/api/ai-assistant/function-definitions/cairo-alpha-shared.ts @@ -1,7 +1,11 @@ import type { AiFunctionPropertyDefinition } from '../types/function-definition.ts'; import type { CairoAlphaCommonContractOptions, CairoAlphaRoyaltyInfoOptions } from '../types/languages.ts'; import { infoDescriptions } from '../../../../common/src/ai/descriptions/common.ts'; -import { cairoCommonDescriptions, cairoRoyaltyInfoDescriptions } from '../../../../common/src/ai/descriptions/cairo.ts'; +import { + cairoCommonDescriptions, + cairoAlphaAccessDescriptions, + cairoRoyaltyInfoDescriptions, +} from '../../../../common/src/ai/descriptions/cairo.ts'; const commonContractFunctionDescription = { upgradeable: { @@ -26,10 +30,24 @@ const commonContractFunctionDescription = { }, access: { - anyOf: [ - { type: 'boolean', enum: [false] }, - { type: 'string', enum: ['ownable', 'roles'] }, - ], + type: 'object', + properties: { + type: { + anyOf: [ + { type: 'boolean', enum: [false] }, + { type: 'string', enum: ['ownable', 'roles', 'roles-dar'] }, + ], + description: cairoAlphaAccessDescriptions.accessType, + }, + darInitialDelay: { + type: 'string', + description: cairoAlphaAccessDescriptions.darInitialDelay, + }, + darDefaultDelayIncrease: { + type: 'string', + description: cairoAlphaAccessDescriptions.darDefaultDelayIncrease, + }, + }, description: cairoCommonDescriptions.access, }, } as const satisfies AiFunctionPropertyDefinition['properties']; diff --git a/packages/ui/src/cairo_alpha/AccessControlSection.svelte b/packages/ui/src/cairo_alpha/AccessControlSection.svelte index e761d8c6c..89e000429 100644 --- a/packages/ui/src/cairo_alpha/AccessControlSection.svelte +++ b/packages/ui/src/cairo_alpha/AccessControlSection.svelte @@ -1,55 +1,86 @@
-
+ {#if accessType === 'roles-dar'} + + + {/if}
diff --git a/packages/ui/src/cairo_alpha/AccountControls.svelte b/packages/ui/src/cairo_alpha/AccountControls.svelte index 6bd4aaf4f..3c55ad975 100644 --- a/packages/ui/src/cairo_alpha/AccountControls.svelte +++ b/packages/ui/src/cairo_alpha/AccountControls.svelte @@ -1,7 +1,7 @@