diff --git a/packages/core-cairo/CHANGELOG.md b/packages/core-cairo/CHANGELOG.md index 99beb9141..a8eef6056 100644 --- a/packages/core-cairo/CHANGELOG.md +++ b/packages/core-cairo/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.17.0 (2024-10-22) + +- Add ERC721 votes. ([#399](https://github.com/OpenZeppelin/contracts-wizard/pull/399)) + +- **Breaking changes**: + - Use OpenZeppelin Contracts for Cairo v0.18.0. ([#399](https://github.com/OpenZeppelin/contracts-wizard/pull/399)) + - Use `VotesComponent` for ERC20 votes. + ## 0.16.0 (2024-09-26) - Add ERC721Enumerable. ([#391](https://github.com/OpenZeppelin/contracts-wizard/pull/391)) diff --git a/packages/core-cairo/package.json b/packages/core-cairo/package.json index 091e86c84..a751256fd 100644 --- a/packages/core-cairo/package.json +++ b/packages/core-cairo/package.json @@ -1,6 +1,6 @@ { "name": "@openzeppelin/wizard-cairo", - "version": "0.16.0", + "version": "0.17.0", "description": "A boilerplate generator to get started with OpenZeppelin Contracts for Cairo", "license": "MIT", "repository": "github:OpenZeppelin/contracts-wizard", diff --git a/packages/core-cairo/src/account.test.ts.md b/packages/core-cairo/src/account.test.ts.md index 40024995a..1db9ad657 100644 --- a/packages/core-cairo/src/account.test.ts.md +++ b/packages/core-cairo/src/account.test.ts.md @@ -9,7 +9,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -70,7 +70,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -114,7 +114,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -175,7 +175,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -219,7 +219,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -284,7 +284,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -332,7 +332,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -399,7 +399,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -466,7 +466,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -535,7 +535,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -604,7 +604,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -675,7 +675,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -746,7 +746,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -808,7 +808,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -853,7 +853,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -915,7 +915,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -960,7 +960,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -1026,7 +1026,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -1075,7 +1075,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -1143,7 +1143,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -1211,7 +1211,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -1281,7 +1281,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -1351,7 +1351,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ @@ -1423,7 +1423,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract(account)]␊ mod MyAccount {␊ diff --git a/packages/core-cairo/src/account.test.ts.snap b/packages/core-cairo/src/account.test.ts.snap index 845b0fcc0..9175052f9 100644 Binary files a/packages/core-cairo/src/account.test.ts.snap and b/packages/core-cairo/src/account.test.ts.snap differ diff --git a/packages/core-cairo/src/common-components.ts b/packages/core-cairo/src/common-components.ts index dc90090a0..207e62125 100644 --- a/packages/core-cairo/src/common-components.ts +++ b/packages/core-cairo/src/common-components.ts @@ -1,6 +1,9 @@ -import type { ContractBuilder } from "./contract"; +import type { BaseImplementedTrait, ContractBuilder } from './contract'; import { defineComponents } from "./utils/define-components"; +export const tokenTypes = ['ERC20', 'ERC721'] as const; +export type Token = typeof tokenTypes[number]; + const components = defineComponents( { SRC5Component: { path: 'openzeppelin::introspection::src5', @@ -14,6 +17,41 @@ const components = defineComponents( { }, impls: [], }, + + VotesComponent: { + path: 'openzeppelin::governance::votes', + substorage: { + name: 'votes', + type: 'VotesComponent::Storage', + }, + event: { + name: 'VotesEvent', + type: 'VotesComponent::Event', + }, + impls: [], + internalImpl: { + name: 'VotesInternalImpl', + value: 'VotesComponent::InternalImpl', + }, + }, + + NoncesComponent: { + path: 'openzeppelin::utils::cryptography::nonces', + substorage: { + name: 'nonces', + type: 'NoncesComponent::Storage', + }, + event: { + name: 'NoncesEvent', + type: 'NoncesComponent::Event', + }, + impls: [ + { + name: 'NoncesImpl', + value: 'NoncesComponent::NoncesImpl', + }, + ], + }, }) export function addSRC5Component(c: ContractBuilder) { @@ -26,4 +64,40 @@ export function addSRC5Component(c: ContractBuilder) { }); c.addInterfaceFlag('ISRC5'); } -} \ No newline at end of file +} + +export function addVotesComponent(c: ContractBuilder, name: string, version: string) { + c.addStandaloneImport('openzeppelin::utils::cryptography::snip12::SNIP12Metadata'); + c.addComponent(components.NoncesComponent, [], false); + c.addComponent(components.VotesComponent, [], false); + c.addImplToComponent(components.VotesComponent, { + name: 'VotesImpl', + value: `VotesComponent::VotesImpl`, + }); + + const SNIP12Metadata: BaseImplementedTrait = { + name: 'SNIP12MetadataImpl', + of: 'SNIP12Metadata', + tags: [], + priority: 0, + }; + c.addImplementedTrait(SNIP12Metadata); + + c.addFunction(SNIP12Metadata, { + name: 'name', + args: [], + returns: 'felt252', + code: [ + `'${name}'`, + ], + }); + + c.addFunction(SNIP12Metadata, { + name: 'version', + args: [], + returns: 'felt252', + code: [ + `'${version}'`, + ], + }); +} diff --git a/packages/core-cairo/src/contract.test.ts.md b/packages/core-cairo/src/contract.test.ts.md index e23e3719e..06030e966 100644 --- a/packages/core-cairo/src/contract.test.ts.md +++ b/packages/core-cairo/src/contract.test.ts.md @@ -9,7 +9,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod Foo {␊ @@ -24,7 +24,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod Foo {␊ @@ -44,7 +44,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod Foo {␊ @@ -64,7 +64,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod Foo {␊ @@ -89,7 +89,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod Foo {␊ @@ -114,7 +114,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod Foo {␊ @@ -152,7 +152,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod Foo {␊ diff --git a/packages/core-cairo/src/contract.test.ts.snap b/packages/core-cairo/src/contract.test.ts.snap index 735418f31..4e9e34fe1 100644 Binary files a/packages/core-cairo/src/contract.test.ts.snap and b/packages/core-cairo/src/contract.test.ts.snap differ diff --git a/packages/core-cairo/src/custom.test.ts.md b/packages/core-cairo/src/custom.test.ts.md index 70208bce2..29bd5a14c 100644 --- a/packages/core-cairo/src/custom.test.ts.md +++ b/packages/core-cairo/src/custom.test.ts.md @@ -9,7 +9,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyContract {␊ @@ -24,7 +24,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyContract {␊ @@ -80,7 +80,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyContract {␊ @@ -161,7 +161,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyContract {␊ @@ -217,7 +217,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyContract {␊ @@ -232,7 +232,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyContract {␊ @@ -288,7 +288,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ ␊ @@ -361,7 +361,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyContract {␊ diff --git a/packages/core-cairo/src/custom.test.ts.snap b/packages/core-cairo/src/custom.test.ts.snap index 06d0a86ec..3e07866d4 100644 Binary files a/packages/core-cairo/src/custom.test.ts.snap and b/packages/core-cairo/src/custom.test.ts.snap differ diff --git a/packages/core-cairo/src/erc1155.test.ts.md b/packages/core-cairo/src/erc1155.test.ts.md index deb87d326..fee595d16 100644 --- a/packages/core-cairo/src/erc1155.test.ts.md +++ b/packages/core-cairo/src/erc1155.test.ts.md @@ -9,7 +9,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -80,7 +80,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -168,7 +168,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ @@ -272,7 +272,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -345,7 +345,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -467,7 +467,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -541,18 +541,9 @@ Generated by [AVA](https://avajs.dev). token_ids: Span,␊ values: Span,␊ ) {␊ - let contract_state = ERC1155Component::HasComponent::get_contract(@self);␊ + let contract_state = self.get_contract();␊ contract_state.pausable.assert_not_paused();␊ }␊ - ␊ - fn after_update(␊ - ref self: ERC1155Component::ComponentState,␊ - from: ContractAddress,␊ - to: ContractAddress,␊ - token_ids: Span,␊ - values: Span,␊ - ) {␊ - }␊ }␊ ␊ #[abi(embed_v0)]␊ @@ -597,7 +588,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -720,7 +711,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ const URI_SETTER_ROLE: felt252 = selector!("URI_SETTER_ROLE");␊ @@ -862,7 +853,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ @@ -947,18 +938,9 @@ Generated by [AVA](https://avajs.dev). token_ids: Span,␊ values: Span,␊ ) {␊ - let contract_state = ERC1155Component::HasComponent::get_contract(@self);␊ + let contract_state = self.get_contract();␊ contract_state.pausable.assert_not_paused();␊ }␊ - ␊ - fn after_update(␊ - ref self: ERC1155Component::ComponentState,␊ - from: ContractAddress,␊ - to: ContractAddress,␊ - token_ids: Span,␊ - values: Span,␊ - ) {␊ - }␊ }␊ ␊ #[generate_trait]␊ @@ -1063,7 +1045,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ @@ -1160,18 +1142,9 @@ Generated by [AVA](https://avajs.dev). token_ids: Span,␊ values: Span,␊ ) {␊ - let contract_state = ERC1155Component::HasComponent::get_contract(@self);␊ + let contract_state = self.get_contract();␊ contract_state.pausable.assert_not_paused();␊ }␊ - ␊ - fn after_update(␊ - ref self: ERC1155Component::ComponentState,␊ - from: ContractAddress,␊ - to: ContractAddress,␊ - token_ids: Span,␊ - values: Span,␊ - ) {␊ - }␊ }␊ ␊ #[abi(embed_v0)]␊ diff --git a/packages/core-cairo/src/erc1155.test.ts.snap b/packages/core-cairo/src/erc1155.test.ts.snap index 4cb1ea059..2f1de6cf8 100644 Binary files a/packages/core-cairo/src/erc1155.test.ts.snap and b/packages/core-cairo/src/erc1155.test.ts.snap differ diff --git a/packages/core-cairo/src/erc1155.ts b/packages/core-cairo/src/erc1155.ts index 8ccacbbce..bfb421e6a 100644 --- a/packages/core-cairo/src/erc1155.ts +++ b/packages/core-cairo/src/erc1155.ts @@ -62,9 +62,6 @@ export function buildERC1155(opts: ERC1155Options): Contract { if (allOpts.pausable) { addPausable(c, allOpts.access); - addPausableHook(c); - } else { - c.addStandaloneImport('openzeppelin::token::erc1155::ERC1155HooksEmptyImpl'); } if (allOpts.burnable) { @@ -79,6 +76,8 @@ export function buildERC1155(opts: ERC1155Options): Contract { addSetBaseUri(c, allOpts.access); } + addHooks(c, allOpts); + setAccessControl(c, allOpts.access); setUpgradeable(c, allOpts.upgradeable, allOpts.access); setInfo(c, allOpts.info); @@ -86,43 +85,35 @@ export function buildERC1155(opts: ERC1155Options): Contract { return c; } -function addPausableHook(c: ContractBuilder) { - const ERC1155HooksTrait: BaseImplementedTrait = { - name: `ERC1155HooksImpl`, - of: 'ERC1155Component::ERC1155HooksTrait', - tags: [], - priority: 0, - }; - c.addImplementedTrait(ERC1155HooksTrait); - - c.addStandaloneImport('starknet::ContractAddress'); - - c.addFunction(ERC1155HooksTrait, { - name: 'before_update', - args: [ - { name: 'ref self', type: `ERC1155Component::ComponentState` }, - { name: 'from', type: 'ContractAddress' }, - { name: 'to', type: 'ContractAddress' }, - { name: 'token_ids', type: 'Span' }, - { name: 'values', type: 'Span' }, - ], - code: [ - 'let contract_state = ERC1155Component::HasComponent::get_contract(@self)', - 'contract_state.pausable.assert_not_paused()', - ], - }); +function addHooks(c: ContractBuilder, allOpts: Required) { + const usesCustomHooks = allOpts.pausable; + if (usesCustomHooks) { + const hooksTrait = { + name: 'ERC1155HooksImpl', + of: 'ERC1155Component::ERC1155HooksTrait', + tags: [], + priority: 1, + }; + c.addImplementedTrait(hooksTrait); + c.addStandaloneImport('starknet::ContractAddress'); - c.addFunction(ERC1155HooksTrait, { - name: 'after_update', - args: [ - { name: 'ref self', type: `ERC1155Component::ComponentState` }, - { name: 'from', type: 'ContractAddress' }, - { name: 'to', type: 'ContractAddress' }, - { name: 'token_ids', type: 'Span' }, - { name: 'values', type: 'Span' }, - ], - code: [], - }); + c.addFunction(hooksTrait, { + name: 'before_update', + args: [ + { name: 'ref self', type: `ERC1155Component::ComponentState` }, + { name: 'from', type: 'ContractAddress' }, + { name: 'to', type: 'ContractAddress' }, + { name: 'token_ids', type: 'Span' }, + { name: 'values', type: 'Span' }, + ], + code: [ + 'let contract_state = self.get_contract()', + 'contract_state.pausable.assert_not_paused()', + ], + }); + } else { + c.addStandaloneImport('openzeppelin::token::erc1155::ERC1155HooksEmptyImpl'); + } } function addERC1155Mixin(c: ContractBuilder) { diff --git a/packages/core-cairo/src/erc20.test.ts.md b/packages/core-cairo/src/erc20.test.ts.md index 0ab43d08f..d9fa7a7f9 100644 --- a/packages/core-cairo/src/erc20.test.ts.md +++ b/packages/core-cairo/src/erc20.test.ts.md @@ -9,7 +9,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -48,7 +48,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -115,7 +115,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -192,7 +192,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -259,17 +259,9 @@ Generated by [AVA](https://avajs.dev). recipient: ContractAddress,␊ amount: u256,␊ ) {␊ - let contract_state = ERC20Component::HasComponent::get_contract(@self);␊ + 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,␊ - ) {␊ - }␊ }␊ ␊ #[abi(embed_v0)]␊ @@ -303,7 +295,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ @@ -390,17 +382,9 @@ Generated by [AVA](https://avajs.dev). recipient: ContractAddress,␊ amount: u256,␊ ) {␊ - let contract_state = ERC20Component::HasComponent::get_contract(@self);␊ + 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,␊ - ) {␊ - }␊ }␊ ␊ #[abi(embed_v0)]␊ @@ -434,7 +418,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -502,17 +486,9 @@ Generated by [AVA](https://avajs.dev). recipient: ContractAddress,␊ amount: u256,␊ ) {␊ - let contract_state = ERC20Component::HasComponent::get_contract(@self);␊ + 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,␊ - ) {␊ - }␊ }␊ ␊ #[abi(embed_v0)]␊ @@ -551,7 +527,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -620,7 +596,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -687,7 +663,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -764,7 +740,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ @@ -861,14 +837,13 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::governance::votes::VotesComponent;␊ use openzeppelin::token::erc20::ERC20Component;␊ - use openzeppelin::token::erc20::extensions::ERC20VotesComponent;␊ - use openzeppelin::token::erc20::extensions::ERC20VotesComponent::InternalTrait as ERC20VotesInternalTrait;␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use openzeppelin::upgrades::interface::IUpgradeable;␊ use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ @@ -877,21 +852,22 @@ Generated by [AVA](https://avajs.dev). use starknet::ContractAddress;␊ ␊ component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ - component!(path: ERC20VotesComponent, storage: erc20_votes, event: ERC20VotesEvent);␊ 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);␊ ␊ #[abi(embed_v0)]␊ impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;␊ #[abi(embed_v0)]␊ - impl ERC20VotesComponentImpl = ERC20VotesComponent::ERC20VotesImpl;␊ - #[abi(embed_v0)]␊ impl NoncesImpl = NoncesComponent::NoncesImpl;␊ #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ + #[abi(embed_v0)]␊ impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ ␊ impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ ␊ @@ -900,10 +876,10 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ erc20: ERC20Component::Storage,␊ #[substorage(v0)]␊ - erc20_votes: ERC20VotesComponent::Storage,␊ - #[substorage(v0)]␊ nonces: NoncesComponent::Storage,␊ #[substorage(v0)]␊ + votes: VotesComponent::Storage,␊ + #[substorage(v0)]␊ upgradeable: UpgradeableComponent::Storage,␊ #[substorage(v0)]␊ ownable: OwnableComponent::Storage,␊ @@ -915,10 +891,10 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ ERC20Event: ERC20Component::Event,␊ #[flat]␊ - ERC20VotesEvent: ERC20VotesComponent::Event,␊ - #[flat]␊ NoncesEvent: NoncesComponent::Event,␊ #[flat]␊ + VotesEvent: VotesComponent::Event,␊ + #[flat]␊ UpgradeableEvent: UpgradeableComponent::Event,␊ #[flat]␊ OwnableEvent: OwnableComponent::Event,␊ @@ -941,22 +917,14 @@ Generated by [AVA](https://avajs.dev). }␊ ␊ impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait {␊ - fn before_update(␊ - ref self: ERC20Component::ComponentState,␊ - from: ContractAddress,␊ - recipient: ContractAddress,␊ - amount: u256,␊ - ) {␊ - }␊ - ␊ fn after_update(␊ ref self: ERC20Component::ComponentState,␊ from: ContractAddress,␊ recipient: ContractAddress,␊ amount: u256,␊ ) {␊ - let mut contract_state = ERC20Component::HasComponent::get_contract_mut(ref self);␊ - contract_state.erc20_votes.transfer_voting_units(from, recipient, amount);␊ + let mut contract_state = self.get_contract_mut();␊ + contract_state.votes.transfer_voting_units(from, recipient, amount);␊ }␊ }␊ ␊ @@ -975,14 +943,13 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::governance::votes::VotesComponent;␊ use openzeppelin::token::erc20::ERC20Component;␊ - use openzeppelin::token::erc20::extensions::ERC20VotesComponent;␊ - use openzeppelin::token::erc20::extensions::ERC20VotesComponent::InternalTrait as ERC20VotesInternalTrait;␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use openzeppelin::upgrades::interface::IUpgradeable;␊ use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ @@ -991,21 +958,22 @@ Generated by [AVA](https://avajs.dev). use starknet::ContractAddress;␊ ␊ component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ - component!(path: ERC20VotesComponent, storage: erc20_votes, event: ERC20VotesEvent);␊ 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);␊ ␊ #[abi(embed_v0)]␊ impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;␊ #[abi(embed_v0)]␊ - impl ERC20VotesComponentImpl = ERC20VotesComponent::ERC20VotesImpl;␊ - #[abi(embed_v0)]␊ impl NoncesImpl = NoncesComponent::NoncesImpl;␊ #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ + #[abi(embed_v0)]␊ impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ ␊ impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ ␊ @@ -1014,10 +982,10 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ erc20: ERC20Component::Storage,␊ #[substorage(v0)]␊ - erc20_votes: ERC20VotesComponent::Storage,␊ - #[substorage(v0)]␊ nonces: NoncesComponent::Storage,␊ #[substorage(v0)]␊ + votes: VotesComponent::Storage,␊ + #[substorage(v0)]␊ upgradeable: UpgradeableComponent::Storage,␊ #[substorage(v0)]␊ ownable: OwnableComponent::Storage,␊ @@ -1029,10 +997,10 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ ERC20Event: ERC20Component::Event,␊ #[flat]␊ - ERC20VotesEvent: ERC20VotesComponent::Event,␊ - #[flat]␊ NoncesEvent: NoncesComponent::Event,␊ #[flat]␊ + VotesEvent: VotesComponent::Event,␊ + #[flat]␊ UpgradeableEvent: UpgradeableComponent::Event,␊ #[flat]␊ OwnableEvent: OwnableComponent::Event,␊ @@ -1055,22 +1023,14 @@ Generated by [AVA](https://avajs.dev). }␊ ␊ impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait {␊ - fn before_update(␊ - ref self: ERC20Component::ComponentState,␊ - from: ContractAddress,␊ - recipient: ContractAddress,␊ - amount: u256,␊ - ) {␊ - }␊ - ␊ fn after_update(␊ ref self: ERC20Component::ComponentState,␊ from: ContractAddress,␊ recipient: ContractAddress,␊ amount: u256,␊ ) {␊ - let mut contract_state = ERC20Component::HasComponent::get_contract_mut(ref self);␊ - contract_state.erc20_votes.transfer_voting_units(from, recipient, amount);␊ + let mut contract_state = self.get_contract_mut();␊ + contract_state.votes.transfer_voting_units(from, recipient, amount);␊ }␊ }␊ ␊ @@ -1089,38 +1049,37 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ + use openzeppelin::governance::votes::VotesComponent;␊ use openzeppelin::token::erc20::ERC20Component;␊ - use openzeppelin::token::erc20::extensions::ERC20VotesComponent;␊ - use openzeppelin::token::erc20::extensions::ERC20VotesComponent::InternalTrait as ERC20VotesInternalTrait;␊ use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ - use starknet::ContractAddress;␊ ␊ component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ - component!(path: ERC20VotesComponent, storage: erc20_votes, event: ERC20VotesEvent);␊ component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ + component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ ␊ #[abi(embed_v0)]␊ impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;␊ #[abi(embed_v0)]␊ - impl ERC20VotesComponentImpl = ERC20VotesComponent::ERC20VotesImpl;␊ - #[abi(embed_v0)]␊ impl NoncesImpl = NoncesComponent::NoncesImpl;␊ + #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ ␊ impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ ␊ #[storage]␊ struct Storage {␊ #[substorage(v0)]␊ erc20: ERC20Component::Storage,␊ #[substorage(v0)]␊ - erc20_votes: ERC20VotesComponent::Storage,␊ - #[substorage(v0)]␊ nonces: NoncesComponent::Storage,␊ + #[substorage(v0)]␊ + votes: VotesComponent::Storage,␊ }␊ ␊ #[event]␊ @@ -1129,9 +1088,9 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ ERC20Event: ERC20Component::Event,␊ #[flat]␊ - ERC20VotesEvent: ERC20VotesComponent::Event,␊ - #[flat]␊ NoncesEvent: NoncesComponent::Event,␊ + #[flat]␊ + VotesEvent: VotesComponent::Event,␊ }␊ ␊ #[constructor]␊ @@ -1150,22 +1109,14 @@ Generated by [AVA](https://avajs.dev). }␊ ␊ impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait {␊ - fn before_update(␊ - ref self: ERC20Component::ComponentState,␊ - from: ContractAddress,␊ - recipient: ContractAddress,␊ - amount: u256,␊ - ) {␊ - }␊ - ␊ fn after_update(␊ ref self: ERC20Component::ComponentState,␊ from: ContractAddress,␊ recipient: ContractAddress,␊ amount: u256,␊ ) {␊ - let mut contract_state = ERC20Component::HasComponent::get_contract_mut(ref self);␊ - contract_state.erc20_votes.transfer_voting_units(from, recipient, amount);␊ + let mut contract_state = self.get_contract_mut();␊ + contract_state.votes.transfer_voting_units(from, recipient, amount);␊ }␊ }␊ }␊ @@ -1176,15 +1127,14 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::governance::votes::VotesComponent;␊ use openzeppelin::security::pausable::PausableComponent;␊ use openzeppelin::token::erc20::ERC20Component;␊ - use openzeppelin::token::erc20::extensions::ERC20VotesComponent;␊ - use openzeppelin::token::erc20::extensions::ERC20VotesComponent::InternalTrait as ERC20VotesInternalTrait;␊ use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ use starknet::ContractAddress;␊ @@ -1193,8 +1143,8 @@ Generated by [AVA](https://avajs.dev). component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ - component!(path: ERC20VotesComponent, storage: erc20_votes, event: ERC20VotesEvent);␊ component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ + component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ ␊ #[abi(embed_v0)]␊ impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;␊ @@ -1203,13 +1153,14 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ #[abi(embed_v0)]␊ - impl ERC20VotesComponentImpl = ERC20VotesComponent::ERC20VotesImpl;␊ - #[abi(embed_v0)]␊ impl NoncesImpl = NoncesComponent::NoncesImpl;␊ + #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ ␊ impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ impl PausableInternalImpl = PausableComponent::InternalImpl;␊ impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ ␊ #[storage]␊ struct Storage {␊ @@ -1220,9 +1171,9 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ ownable: OwnableComponent::Storage,␊ #[substorage(v0)]␊ - erc20_votes: ERC20VotesComponent::Storage,␊ - #[substorage(v0)]␊ nonces: NoncesComponent::Storage,␊ + #[substorage(v0)]␊ + votes: VotesComponent::Storage,␊ }␊ ␊ #[event]␊ @@ -1235,9 +1186,9 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ OwnableEvent: OwnableComponent::Event,␊ #[flat]␊ - ERC20VotesEvent: ERC20VotesComponent::Event,␊ - #[flat]␊ NoncesEvent: NoncesComponent::Event,␊ + #[flat]␊ + VotesEvent: VotesComponent::Event,␊ }␊ ␊ #[constructor]␊ @@ -1265,7 +1216,7 @@ Generated by [AVA](https://avajs.dev). recipient: ContractAddress,␊ amount: u256,␊ ) {␊ - let contract_state = ERC20Component::HasComponent::get_contract(@self);␊ + let contract_state = self.get_contract();␊ contract_state.pausable.assert_not_paused();␊ }␊ ␊ @@ -1275,8 +1226,8 @@ Generated by [AVA](https://avajs.dev). recipient: ContractAddress,␊ amount: u256,␊ ) {␊ - let mut contract_state = ERC20Component::HasComponent::get_contract_mut(ref self);␊ - contract_state.erc20_votes.transfer_voting_units(from, recipient, amount);␊ + let mut contract_state = self.get_contract_mut();␊ + contract_state.votes.transfer_voting_units(from, recipient, amount);␊ }␊ }␊ ␊ @@ -1314,15 +1265,14 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::governance::votes::VotesComponent;␊ use openzeppelin::security::pausable::PausableComponent;␊ use openzeppelin::token::erc20::ERC20Component;␊ - use openzeppelin::token::erc20::extensions::ERC20VotesComponent;␊ - use openzeppelin::token::erc20::extensions::ERC20VotesComponent::InternalTrait as ERC20VotesInternalTrait;␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use openzeppelin::upgrades::interface::IUpgradeable;␊ use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ @@ -1334,8 +1284,8 @@ Generated by [AVA](https://avajs.dev). component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ - component!(path: ERC20VotesComponent, storage: erc20_votes, event: ERC20VotesEvent);␊ component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ + component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ ␊ #[abi(embed_v0)]␊ @@ -1345,13 +1295,14 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ #[abi(embed_v0)]␊ - impl ERC20VotesComponentImpl = ERC20VotesComponent::ERC20VotesImpl;␊ - #[abi(embed_v0)]␊ impl NoncesImpl = NoncesComponent::NoncesImpl;␊ + #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ ␊ impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ impl PausableInternalImpl = PausableComponent::InternalImpl;␊ impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ ␊ #[storage]␊ @@ -1363,10 +1314,10 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ ownable: OwnableComponent::Storage,␊ #[substorage(v0)]␊ - erc20_votes: ERC20VotesComponent::Storage,␊ - #[substorage(v0)]␊ nonces: NoncesComponent::Storage,␊ #[substorage(v0)]␊ + votes: VotesComponent::Storage,␊ + #[substorage(v0)]␊ upgradeable: UpgradeableComponent::Storage,␊ }␊ ␊ @@ -1380,10 +1331,10 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ OwnableEvent: OwnableComponent::Event,␊ #[flat]␊ - ERC20VotesEvent: ERC20VotesComponent::Event,␊ - #[flat]␊ NoncesEvent: NoncesComponent::Event,␊ #[flat]␊ + VotesEvent: VotesComponent::Event,␊ + #[flat]␊ UpgradeableEvent: UpgradeableComponent::Event,␊ }␊ ␊ @@ -1412,7 +1363,7 @@ Generated by [AVA](https://avajs.dev). recipient: ContractAddress,␊ amount: u256,␊ ) {␊ - let contract_state = ERC20Component::HasComponent::get_contract(@self);␊ + let contract_state = self.get_contract();␊ contract_state.pausable.assert_not_paused();␊ }␊ ␊ @@ -1422,8 +1373,8 @@ Generated by [AVA](https://avajs.dev). recipient: ContractAddress,␊ amount: u256,␊ ) {␊ - let mut contract_state = ERC20Component::HasComponent::get_contract_mut(ref self);␊ - contract_state.erc20_votes.transfer_voting_units(from, recipient, amount);␊ + let mut contract_state = self.get_contract_mut();␊ + contract_state.votes.transfer_voting_units(from, recipient, amount);␊ }␊ }␊ ␊ @@ -1469,7 +1420,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ @@ -1479,11 +1430,10 @@ Generated by [AVA](https://avajs.dev). mod MyToken {␊ use openzeppelin::access::accesscontrol::AccessControlComponent;␊ use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE;␊ + use openzeppelin::governance::votes::VotesComponent;␊ use openzeppelin::introspection::src5::SRC5Component;␊ use openzeppelin::security::pausable::PausableComponent;␊ use openzeppelin::token::erc20::ERC20Component;␊ - use openzeppelin::token::erc20::extensions::ERC20VotesComponent;␊ - use openzeppelin::token::erc20::extensions::ERC20VotesComponent::InternalTrait as ERC20VotesInternalTrait;␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use openzeppelin::upgrades::interface::IUpgradeable;␊ use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ @@ -1497,8 +1447,8 @@ Generated by [AVA](https://avajs.dev). component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ - component!(path: ERC20VotesComponent, storage: erc20_votes, event: ERC20VotesEvent);␊ component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ + component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ ␊ #[abi(embed_v0)]␊ @@ -1508,13 +1458,14 @@ Generated by [AVA](https://avajs.dev). #[abi(embed_v0)]␊ impl AccessControlMixinImpl = AccessControlComponent::AccessControlMixinImpl;␊ #[abi(embed_v0)]␊ - impl ERC20VotesComponentImpl = ERC20VotesComponent::ERC20VotesImpl;␊ - #[abi(embed_v0)]␊ impl NoncesImpl = NoncesComponent::NoncesImpl;␊ + #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ ␊ impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ impl PausableInternalImpl = PausableComponent::InternalImpl;␊ impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ ␊ #[storage]␊ @@ -1528,10 +1479,10 @@ Generated by [AVA](https://avajs.dev). #[substorage(v0)]␊ src5: SRC5Component::Storage,␊ #[substorage(v0)]␊ - erc20_votes: ERC20VotesComponent::Storage,␊ - #[substorage(v0)]␊ nonces: NoncesComponent::Storage,␊ #[substorage(v0)]␊ + votes: VotesComponent::Storage,␊ + #[substorage(v0)]␊ upgradeable: UpgradeableComponent::Storage,␊ }␊ ␊ @@ -1547,10 +1498,10 @@ Generated by [AVA](https://avajs.dev). #[flat]␊ SRC5Event: SRC5Component::Event,␊ #[flat]␊ - ERC20VotesEvent: ERC20VotesComponent::Event,␊ - #[flat]␊ NoncesEvent: NoncesComponent::Event,␊ #[flat]␊ + VotesEvent: VotesComponent::Event,␊ + #[flat]␊ UpgradeableEvent: UpgradeableComponent::Event,␊ }␊ ␊ @@ -1590,7 +1541,7 @@ Generated by [AVA](https://avajs.dev). recipient: ContractAddress,␊ amount: u256,␊ ) {␊ - let contract_state = ERC20Component::HasComponent::get_contract(@self);␊ + let contract_state = self.get_contract();␊ contract_state.pausable.assert_not_paused();␊ }␊ ␊ @@ -1600,8 +1551,8 @@ Generated by [AVA](https://avajs.dev). recipient: ContractAddress,␊ amount: u256,␊ ) {␊ - let mut contract_state = ERC20Component::HasComponent::get_contract_mut(ref self);␊ - contract_state.erc20_votes.transfer_voting_units(from, recipient, amount);␊ + let mut contract_state = self.get_contract_mut();␊ + contract_state.votes.transfer_voting_units(from, recipient, amount);␊ }␊ }␊ ␊ diff --git a/packages/core-cairo/src/erc20.test.ts.snap b/packages/core-cairo/src/erc20.test.ts.snap index 13ade779e..2c1e03f09 100644 Binary files a/packages/core-cairo/src/erc20.test.ts.snap and b/packages/core-cairo/src/erc20.test.ts.snap differ diff --git a/packages/core-cairo/src/erc20.ts b/packages/core-cairo/src/erc20.ts index 690db04d9..3e13b6119 100644 --- a/packages/core-cairo/src/erc20.ts +++ b/packages/core-cairo/src/erc20.ts @@ -11,6 +11,8 @@ import { contractDefaults as commonDefaults } from './common-options'; import { printContract } from './print'; import { externalTrait } from './external-trait'; import { toByteArray, toFelt252 } from './utils/convert-strings'; +import { addVotesComponent } from './common-components'; + export const defaults: Required = { name: 'MyToken', @@ -53,7 +55,7 @@ function withDefaults(opts: ERC20Options): Required { mintable: opts.mintable ?? defaults.mintable, votes: opts.votes ?? defaults.votes, appName: opts.appName ?? defaults.appName, - appVersion: opts.appVersion ?? defaults.appVersion, + appVersion: opts.appVersion ?? defaults.appVersion }; } @@ -95,7 +97,8 @@ export function buildERC20(opts: ERC20Options): Contract { } function addHooks(c: ContractBuilder, allOpts: Required) { - if (allOpts.votes || allOpts.pausable) { + const usesCustomHooks = allOpts.pausable || allOpts.votes; + if (usesCustomHooks) { const hooksTrait = { name: 'ERC20HooksImpl', of: 'ERC20Component::ERC20HooksTrait', @@ -104,31 +107,20 @@ function addHooks(c: ContractBuilder, allOpts: Required) { }; c.addImplementedTrait(hooksTrait); - const beforeUpdateFn = c.addFunction(hooksTrait, { - name: 'before_update', - args: [ - { name: 'ref self', type: 'ERC20Component::ComponentState' }, - { name: 'from', type: 'ContractAddress' }, - { name: 'recipient', type: 'ContractAddress' }, - { name: 'amount', type: 'u256' }, - ], - code: [], - }); - - const afterUpdateFn = c.addFunction(hooksTrait, { - name: 'after_update', - args: [ - { name: 'ref self', type: 'ERC20Component::ComponentState' }, - { name: 'from', type: 'ContractAddress' }, - { name: 'recipient', type: 'ContractAddress' }, - { name: 'amount', type: 'u256' }, - ], - code: [], - }); - if (allOpts.pausable) { + const beforeUpdateFn = c.addFunction(hooksTrait, { + name: 'before_update', + args: [ + { name: 'ref self', type: 'ERC20Component::ComponentState' }, + { name: 'from', type: 'ContractAddress' }, + { name: 'recipient', type: 'ContractAddress' }, + { name: 'amount', type: 'u256' }, + ], + code: [], + }); + beforeUpdateFn.code.push( - 'let contract_state = ERC20Component::HasComponent::get_contract(@self);', + 'let contract_state = self.get_contract();', 'contract_state.pausable.assert_not_paused();', ); } @@ -148,9 +140,20 @@ function addHooks(c: ContractBuilder, allOpts: Required) { addVotesComponent(c, toFelt252(allOpts.appName, 'appName'), toFelt252(allOpts.appVersion, 'appVersion')); + const afterUpdateFn = c.addFunction(hooksTrait, { + name: 'after_update', + args: [ + { name: 'ref self', type: 'ERC20Component::ComponentState' }, + { name: 'from', type: 'ContractAddress' }, + { name: 'recipient', type: 'ContractAddress' }, + { name: 'amount', type: 'u256' }, + ], + code: [], + }); + afterUpdateFn.code.push( - 'let mut contract_state = ERC20Component::HasComponent::get_contract_mut(ref self);', - 'contract_state.erc20_votes.transfer_voting_units(from, recipient, amount);', + 'let mut contract_state = self.get_contract_mut();', + 'contract_state.votes.transfer_voting_units(from, recipient, amount);', ); } } else { @@ -194,7 +197,7 @@ function addPremint(c: ContractBuilder, amount: string) { c.addStandaloneImport('starknet::ContractAddress'); c.addConstructorArgument({ name:'recipient', type:'ContractAddress' }); - c.addConstructorCode(`self.erc20.mint(recipient, ${premintAbsolute})`); + c.addConstructorCode(`self.erc20.mint(recipient, ${premintAbsolute})`); } } @@ -244,40 +247,6 @@ function addMintable(c: ContractBuilder, access: Access) { requireAccessControl(c, externalTrait, functions.mint, access, 'MINTER', 'minter'); } -function addVotesComponent(c: ContractBuilder, name: string, version: string) { - c.addComponent(components.ERC20VotesComponent, [], false); - c.addComponent(components.NoncesComponent, [], false); - c.addStandaloneImport('openzeppelin::token::erc20::extensions::ERC20VotesComponent::InternalTrait as ERC20VotesInternalTrait'); - c.addStandaloneImport('openzeppelin::utils::cryptography::snip12::SNIP12Metadata'); - c.addStandaloneImport('starknet::ContractAddress'); - - const SNIP12Metadata: BaseImplementedTrait = { - name: 'SNIP12MetadataImpl', - of: 'SNIP12Metadata', - tags: [], - priority: 0, - }; - c.addImplementedTrait(SNIP12Metadata); - - c.addFunction(SNIP12Metadata, { - name: 'name', - args: [], - returns: 'felt252', - code: [ - `'${name}'`, - ], - }); - - c.addFunction(SNIP12Metadata, { - name: 'version', - args: [], - returns: 'felt252', - code: [ - `'${version}'`, - ], - }); -} - const components = defineComponents( { ERC20Component: { path: 'openzeppelin::token::erc20', @@ -295,40 +264,6 @@ const components = defineComponents( { value: 'ERC20Component::InternalImpl', }, }, - ERC20VotesComponent: { - path: 'openzeppelin::token::erc20::extensions', - substorage: { - name: 'erc20_votes', - type: 'ERC20VotesComponent::Storage', - }, - event: { - name: 'ERC20VotesEvent', - type: 'ERC20VotesComponent::Event', - }, - impls: [ - { - name: 'ERC20VotesComponentImpl', - value: 'ERC20VotesComponent::ERC20VotesImpl', - }, - ], - }, - NoncesComponent: { - path: 'openzeppelin::utils::cryptography::nonces', - substorage: { - name: 'nonces', - type: 'NoncesComponent::Storage', - }, - event: { - name: 'NoncesEvent', - type: 'NoncesComponent::Event', - }, - impls: [ - { - name: 'NoncesImpl', - value: 'NoncesComponent::NoncesImpl', - }, - ], - }, }); const functions = defineFunctions({ diff --git a/packages/core-cairo/src/erc721.test.ts b/packages/core-cairo/src/erc721.test.ts index 36e05cc6a..d59400092 100644 --- a/packages/core-cairo/src/erc721.test.ts +++ b/packages/core-cairo/src/erc721.test.ts @@ -3,13 +3,16 @@ import test from 'ava'; import { buildERC721, ERC721Options } from './erc721'; import { printContract } from './print'; -import { erc721 } from '.'; +import { erc721, OptionsError } from '.'; const allFeaturesON: Partial = { mintable: true, burnable: true, pausable: true, enumerable: true, + votes: true, + appName: 'MY_DAPP_NAME', + appVersion: 'MY_DAPP_VERSION', upgradeable: true } as const; @@ -78,6 +81,54 @@ testERC721('full non-upgradeable', { upgradeable: false, }); +testERC721('erc721 votes', { + votes: true, + appName: 'MY_DAPP_NAME', +}); + +testERC721('erc721 votes, version', { + votes: true, + appName: 'MY_DAPP_NAME', + appVersion: 'MY_DAPP_VERSION', +}); + +test('erc721 votes, no name', async t => { + let error = t.throws(() => buildERC721({ + name: 'MyToken', + symbol: 'MTK', + votes: true, + })); + t.is((error as OptionsError).messages.appName, 'Application Name is required when Votes are enabled'); +}); + +test('erc721 votes, no version', async t => { + let error = t.throws(() => buildERC721({ + name: 'MyToken', + symbol: 'MTK', + votes: true, + appName: 'MY_DAPP_NAME', + appVersion: '' + })); + t.is((error as OptionsError).messages.appVersion, 'Application Version is required when Votes are enabled'); +}); + +test('erc721 votes, empty version', async t => { + let error = t.throws(() => buildERC721({ + name: 'MyToken', + symbol: 'MTK', + votes: true, + appName: 'MY_DAPP_NAME', + appVersion: '', // avoids default value of v1 + })); + t.is((error as OptionsError).messages.appVersion, 'Application Version is required when Votes are enabled'); +}); + +testERC721('erc721 votes, non-upgradeable', { + votes: true, + appName: 'MY_DAPP_NAME', + upgradeable: false, +}); + testERC721('full upgradeable', allFeaturesON); testAPIEquivalence('API default'); diff --git a/packages/core-cairo/src/erc721.test.ts.md b/packages/core-cairo/src/erc721.test.ts.md index 7ef79008a..58d847cff 100644 --- a/packages/core-cairo/src/erc721.test.ts.md +++ b/packages/core-cairo/src/erc721.test.ts.md @@ -9,7 +9,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -54,7 +54,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -127,7 +127,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -200,7 +200,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -284,7 +284,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -393,7 +393,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -491,7 +491,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -585,7 +585,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ @@ -705,7 +705,7 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ const UPGRADER_ROLE: felt252 = selector!("UPGRADER_ROLE");␊ @@ -819,16 +819,19 @@ Generated by [AVA](https://avajs.dev). > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ use core::num::traits::Zero;␊ use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::governance::votes::VotesComponent;␊ use openzeppelin::introspection::src5::SRC5Component;␊ use openzeppelin::security::pausable::PausableComponent;␊ use openzeppelin::token::erc721::ERC721Component;␊ use openzeppelin::token::erc721::extensions::ERC721EnumerableComponent;␊ + use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ use starknet::ContractAddress;␊ use starknet::get_caller_address;␊ ␊ @@ -837,6 +840,8 @@ Generated by [AVA](https://avajs.dev). component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ component!(path: ERC721EnumerableComponent, storage: erc721_enumerable, event: ERC721EnumerableEvent);␊ + component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ + component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ ␊ #[abi(embed_v0)]␊ impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ @@ -846,11 +851,16 @@ Generated by [AVA](https://avajs.dev). impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ #[abi(embed_v0)]␊ impl ERC721EnumerableImpl = ERC721EnumerableComponent::ERC721EnumerableImpl;␊ + #[abi(embed_v0)]␊ + impl NoncesImpl = NoncesComponent::NoncesImpl;␊ + #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ ␊ impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ impl PausableInternalImpl = PausableComponent::InternalImpl;␊ impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ impl ERC721EnumerableInternalImpl = ERC721EnumerableComponent::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ ␊ #[storage]␊ struct Storage {␊ @@ -864,6 +874,10 @@ Generated by [AVA](https://avajs.dev). ownable: OwnableComponent::Storage,␊ #[substorage(v0)]␊ erc721_enumerable: ERC721EnumerableComponent::Storage,␊ + #[substorage(v0)]␊ + nonces: NoncesComponent::Storage,␊ + #[substorage(v0)]␊ + votes: VotesComponent::Storage,␊ }␊ ␊ #[event]␊ @@ -879,6 +893,10 @@ Generated by [AVA](https://avajs.dev). OwnableEvent: OwnableComponent::Event,␊ #[flat]␊ ERC721EnumerableEvent: ERC721EnumerableComponent::Event,␊ + #[flat]␊ + NoncesEvent: NoncesComponent::Event,␊ + #[flat]␊ + VotesEvent: VotesComponent::Event,␊ }␊ ␊ #[constructor]␊ @@ -898,6 +916,18 @@ Generated by [AVA](https://avajs.dev). let mut contract_state = self.get_contract_mut();␊ contract_state.pausable.assert_not_paused();␊ contract_state.erc721_enumerable.before_update(to, token_id);␊ + let previous_owner = self._owner_of(token_id);␊ + contract_state.votes.transfer_voting_units(previous_owner, to, 1);␊ + }␊ + }␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'MY_DAPP_NAME'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'MY_DAPP_VERSION'␊ }␊ }␊ ␊ @@ -945,23 +975,338 @@ Generated by [AVA](https://avajs.dev). }␊ ` +## erc721 votes + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::governance::votes::VotesComponent;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::erc721::ERC721Component;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::ClassHash;␊ + use starknet::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);␊ + component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ + component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + #[abi(embed_v0)]␊ + impl NoncesImpl = NoncesComponent::NoncesImpl;␊ + #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ + ␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + #[substorage(v0)]␊ + nonces: NoncesComponent::Storage,␊ + #[substorage(v0)]␊ + votes: VotesComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + #[flat]␊ + NoncesEvent: NoncesComponent::Event,␊ + #[flat]␊ + VotesEvent: VotesComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.erc721.initializer("MyToken", "MTK", "");␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + impl ERC721HooksImpl of ERC721Component::ERC721HooksTrait {␊ + fn before_update(␊ + ref self: ERC721Component::ComponentState,␊ + to: ContractAddress,␊ + token_id: u256,␊ + auth: ContractAddress,␊ + ) {␊ + let mut contract_state = self.get_contract_mut();␊ + let previous_owner = self._owner_of(token_id);␊ + contract_state.votes.transfer_voting_units(previous_owner, to, 1);␊ + }␊ + }␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'MY_DAPP_NAME'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'v1'␊ + }␊ + }␊ + ␊ + #[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);␊ + }␊ + }␊ + }␊ + ` + +## erc721 votes, version + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::governance::votes::VotesComponent;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::erc721::ERC721Component;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::ClassHash;␊ + use starknet::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);␊ + component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ + component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + #[abi(embed_v0)]␊ + impl NoncesImpl = NoncesComponent::NoncesImpl;␊ + #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ + ␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + #[substorage(v0)]␊ + nonces: NoncesComponent::Storage,␊ + #[substorage(v0)]␊ + votes: VotesComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + #[flat]␊ + NoncesEvent: NoncesComponent::Event,␊ + #[flat]␊ + VotesEvent: VotesComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.erc721.initializer("MyToken", "MTK", "");␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + impl ERC721HooksImpl of ERC721Component::ERC721HooksTrait {␊ + fn before_update(␊ + ref self: ERC721Component::ComponentState,␊ + to: ContractAddress,␊ + token_id: u256,␊ + auth: ContractAddress,␊ + ) {␊ + let mut contract_state = self.get_contract_mut();␊ + let previous_owner = self._owner_of(token_id);␊ + contract_state.votes.transfer_voting_units(previous_owner, to, 1);␊ + }␊ + }␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'MY_DAPP_NAME'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'MY_DAPP_VERSION'␊ + }␊ + }␊ + ␊ + #[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);␊ + }␊ + }␊ + }␊ + ` + +## erc721 votes, non-upgradeable + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::governance::votes::VotesComponent;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::token::erc721::ERC721Component;␊ + use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ + use starknet::ContractAddress;␊ + ␊ + component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ + component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ + #[abi(embed_v0)]␊ + impl NoncesImpl = NoncesComponent::NoncesImpl;␊ + #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ + ␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + nonces: NoncesComponent::Storage,␊ + #[substorage(v0)]␊ + votes: VotesComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + NoncesEvent: NoncesComponent::Event,␊ + #[flat]␊ + VotesEvent: VotesComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState) {␊ + self.erc721.initializer("MyToken", "MTK", "");␊ + }␊ + ␊ + impl ERC721HooksImpl of ERC721Component::ERC721HooksTrait {␊ + fn before_update(␊ + ref self: ERC721Component::ComponentState,␊ + to: ContractAddress,␊ + token_id: u256,␊ + auth: ContractAddress,␊ + ) {␊ + let mut contract_state = self.get_contract_mut();␊ + let previous_owner = self._owner_of(token_id);␊ + contract_state.votes.transfer_voting_units(previous_owner, to, 1);␊ + }␊ + }␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'MY_DAPP_NAME'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'v1'␊ + }␊ + }␊ + }␊ + ` + ## full upgradeable > Snapshot 1 `// SPDX-License-Identifier: MIT␊ - // Compatible with OpenZeppelin Contracts for Cairo ^0.17.0␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.18.0␊ ␊ #[starknet::contract]␊ mod MyToken {␊ use core::num::traits::Zero;␊ use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::governance::votes::VotesComponent;␊ use openzeppelin::introspection::src5::SRC5Component;␊ use openzeppelin::security::pausable::PausableComponent;␊ use openzeppelin::token::erc721::ERC721Component;␊ use openzeppelin::token::erc721::extensions::ERC721EnumerableComponent;␊ use openzeppelin::upgrades::UpgradeableComponent;␊ use openzeppelin::upgrades::interface::IUpgradeable;␊ + use openzeppelin::utils::cryptography::nonces::NoncesComponent;␊ + use openzeppelin::utils::cryptography::snip12::SNIP12Metadata;␊ use starknet::ClassHash;␊ use starknet::ContractAddress;␊ use starknet::get_caller_address;␊ @@ -972,6 +1317,8 @@ Generated by [AVA](https://avajs.dev). component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ component!(path: ERC721EnumerableComponent, storage: erc721_enumerable, event: ERC721EnumerableEvent);␊ component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + component!(path: NoncesComponent, storage: nonces, event: NoncesEvent);␊ + component!(path: VotesComponent, storage: votes, event: VotesEvent);␊ ␊ #[abi(embed_v0)]␊ impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl;␊ @@ -981,12 +1328,17 @@ Generated by [AVA](https://avajs.dev). impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ #[abi(embed_v0)]␊ impl ERC721EnumerableImpl = ERC721EnumerableComponent::ERC721EnumerableImpl;␊ + #[abi(embed_v0)]␊ + impl NoncesImpl = NoncesComponent::NoncesImpl;␊ + #[abi(embed_v0)]␊ + impl VotesImpl = VotesComponent::VotesImpl;␊ ␊ impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ impl PausableInternalImpl = PausableComponent::InternalImpl;␊ impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ impl ERC721EnumerableInternalImpl = ERC721EnumerableComponent::InternalImpl;␊ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl VotesInternalImpl = VotesComponent::InternalImpl;␊ ␊ #[storage]␊ struct Storage {␊ @@ -1002,6 +1354,10 @@ Generated by [AVA](https://avajs.dev). erc721_enumerable: ERC721EnumerableComponent::Storage,␊ #[substorage(v0)]␊ upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + nonces: NoncesComponent::Storage,␊ + #[substorage(v0)]␊ + votes: VotesComponent::Storage,␊ }␊ ␊ #[event]␊ @@ -1019,6 +1375,10 @@ Generated by [AVA](https://avajs.dev). ERC721EnumerableEvent: ERC721EnumerableComponent::Event,␊ #[flat]␊ UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + NoncesEvent: NoncesComponent::Event,␊ + #[flat]␊ + VotesEvent: VotesComponent::Event,␊ }␊ ␊ #[constructor]␊ @@ -1038,6 +1398,18 @@ Generated by [AVA](https://avajs.dev). let mut contract_state = self.get_contract_mut();␊ contract_state.pausable.assert_not_paused();␊ contract_state.erc721_enumerable.before_update(to, token_id);␊ + let previous_owner = self._owner_of(token_id);␊ + contract_state.votes.transfer_voting_units(previous_owner, to, 1);␊ + }␊ + }␊ + ␊ + impl SNIP12MetadataImpl of SNIP12Metadata {␊ + fn name() -> felt252 {␊ + 'MY_DAPP_NAME'␊ + }␊ + ␊ + fn version() -> felt252 {␊ + 'MY_DAPP_VERSION'␊ }␊ }␊ ␊ diff --git a/packages/core-cairo/src/erc721.test.ts.snap b/packages/core-cairo/src/erc721.test.ts.snap index 1d88f8e64..3593522ae 100644 Binary files a/packages/core-cairo/src/erc721.test.ts.snap and b/packages/core-cairo/src/erc721.test.ts.snap differ diff --git a/packages/core-cairo/src/erc721.ts b/packages/core-cairo/src/erc721.ts index eb661d8d3..9d0a32c1a 100644 --- a/packages/core-cairo/src/erc721.ts +++ b/packages/core-cairo/src/erc721.ts @@ -8,9 +8,10 @@ import { setInfo } from './set-info'; import { defineComponents } from './utils/define-components'; import { contractDefaults as commonDefaults } from './common-options'; import { printContract } from './print'; -import { addSRC5Component } from './common-components'; +import { addSRC5Component, addVotesComponent } from './common-components'; import { externalTrait } from './external-trait'; -import { toByteArray } from './utils/convert-strings'; +import { toByteArray, toFelt252 } from './utils/convert-strings'; +import { OptionsError } from './error'; export const defaults: Required = { name: 'MyToken', @@ -20,6 +21,9 @@ export const defaults: Required = { pausable: false, mintable: false, enumerable: false, + votes: false, + appName: '', // Defaults to empty string, but user must provide a non-empty value if votes are enabled + appVersion: 'v1', access: commonDefaults.access, upgradeable: commonDefaults.upgradeable, info: commonDefaults.info @@ -37,6 +41,9 @@ export interface ERC721Options extends CommonContractOptions { pausable?: boolean; mintable?: boolean; enumerable?: boolean; + votes?: boolean; + appName?: string; + appVersion?: string; } function withDefaults(opts: ERC721Options): Required { @@ -47,7 +54,10 @@ function withDefaults(opts: ERC721Options): Required { burnable: opts.burnable ?? defaults.burnable, pausable: opts.pausable ?? defaults.pausable, mintable: opts.mintable ?? defaults.mintable, - enumerable: opts.enumerable ?? defaults.enumerable + enumerable: opts.enumerable ?? defaults.enumerable, + votes: opts.votes ?? defaults.votes, + appName: opts.appName ?? defaults.appName, + appVersion: opts.appVersion ?? defaults.appVersion }; } @@ -87,8 +97,8 @@ export function buildERC721(opts: ERC721Options): Contract { return c; } -function addHooks(c: ContractBuilder, opts: ERC721Options) { - const usesCustomHooks = opts.pausable || opts.enumerable; +function addHooks(c: ContractBuilder, opts: Required) { + const usesCustomHooks = opts.pausable || opts.enumerable || opts.votes; if (usesCustomHooks) { const ERC721HooksTrait: BaseImplementedTrait = { name: `ERC721HooksImpl`, @@ -98,10 +108,10 @@ function addHooks(c: ContractBuilder, opts: ERC721Options) { }; c.addImplementedTrait(ERC721HooksTrait); c.addStandaloneImport('starknet::ContractAddress'); - - const requiresMutState = opts.enumerable; + + const requiresMutState = opts.enumerable || opts.votes; const initStateLine = requiresMutState - ? 'let mut contract_state = self.get_contract_mut()' + ? 'let mut contract_state = self.get_contract_mut()' : 'let contract_state = self.get_contract()'; const beforeUpdateCode = [initStateLine]; if (opts.pausable) { @@ -110,6 +120,23 @@ function addHooks(c: ContractBuilder, opts: ERC721Options) { if (opts.enumerable) { beforeUpdateCode.push('contract_state.erc721_enumerable.before_update(to, token_id)'); } + if (opts.votes) { + if (!opts.appName) { + throw new OptionsError({ + appName: 'Application Name is required when Votes are enabled', + }); + } + + if (!opts.appVersion) { + throw new OptionsError({ + appVersion: 'Application Version is required when Votes are enabled', + }); + } + + addVotesComponent(c, toFelt252(opts.appName, 'appName'), toFelt252(opts.appVersion, 'appVersion')); + beforeUpdateCode.push('let previous_owner = self._owner_of(token_id);'); + beforeUpdateCode.push('contract_state.votes.transfer_voting_units(previous_owner, to, 1);'); + } c.addFunction(ERC721HooksTrait, { name: 'before_update', args: [ diff --git a/packages/core-cairo/src/generate/erc721.ts b/packages/core-cairo/src/generate/erc721.ts index 143ff6595..99f97a120 100644 --- a/packages/core-cairo/src/generate/erc721.ts +++ b/packages/core-cairo/src/generate/erc721.ts @@ -12,6 +12,9 @@ const blueprint = { baseUri: ['https://example.com/'], burnable: booleans, enumerable: booleans, + votes: booleans, + appName: ['MyApp'], + appVersion: ['v1'], pausable: booleans, mintable: booleans, access: accessOptions, diff --git a/packages/core-cairo/src/utils/version.ts b/packages/core-cairo/src/utils/version.ts index 524caaaf2..5a87cb4a6 100644 --- a/packages/core-cairo/src/utils/version.ts +++ b/packages/core-cairo/src/utils/version.ts @@ -1,17 +1,17 @@ /** * The actual latest version to use in links. */ -export const contractsVersion = '0.17.0'; +export const contractsVersion = '0.18.0'; export const contractsVersionTag = `v${contractsVersion}`; /** * Cairo compiler versions. */ export const edition = '2024_07'; -export const cairoVersion = '2.8.2'; -export const scarbVersion = '2.8.2'; +export const cairoVersion = '2.8.4'; +export const scarbVersion = '2.8.4'; /** * Semantic version string representing of the minimum compatible version of Contracts to display in output. */ -export const compatibleContractsSemver = '^0.17.0'; +export const compatibleContractsSemver = '^0.18.0'; diff --git a/packages/core-cairo/test_project/Scarb.lock b/packages/core-cairo/test_project/Scarb.lock index 7e9efb8ce..ab78ab5ef 100644 --- a/packages/core-cairo/test_project/Scarb.lock +++ b/packages/core-cairo/test_project/Scarb.lock @@ -3,9 +3,9 @@ version = 1 [[package]] name = "openzeppelin" -version = "0.17.0" +version = "0.18.0" source = "registry+https://scarbs.xyz/" -checksum = "sha256:7e77855aaba0825a2a12cad72d52d85380a9fab732007754b3c5d98908918ce7" +checksum = "sha256:878cc6da762e90f0f586f426b16d6c4c087e41de24a9f7b2f5c30b7ac9f96171" dependencies = [ "openzeppelin_access", "openzeppelin_account", @@ -22,9 +22,9 @@ dependencies = [ [[package]] name = "openzeppelin_access" -version = "0.17.0" +version = "0.18.0" source = "registry+https://scarbs.xyz/" -checksum = "sha256:541bb8fdf1ad17fe0d275b00acb9f0d7f56ea5534741e21535ac3fda2c600281" +checksum = "sha256:424314072ae27d5b6f4264472a5c403711448ea62763a661b89e6ff5f23297fd" dependencies = [ "openzeppelin_introspection", "openzeppelin_utils", @@ -32,9 +32,9 @@ dependencies = [ [[package]] name = "openzeppelin_account" -version = "0.17.0" +version = "0.18.0" source = "registry+https://scarbs.xyz/" -checksum = "sha256:c4e11609fdd1f4c3d3004cd1468711bd2ea664739c9e59a4b270567fe4c23ee3" +checksum = "sha256:83e6571cac4c67049c8d0ab4e3c7ad146d582d7605e7354248835833e1d26c4a" dependencies = [ "openzeppelin_introspection", "openzeppelin_utils", @@ -42,9 +42,9 @@ dependencies = [ [[package]] name = "openzeppelin_finance" -version = "0.17.0" +version = "0.18.0" source = "registry+https://scarbs.xyz/" -checksum = "sha256:9adcbec76ee8ed08be8d87c6af6014aa7080d67578816f5ba77f4376b25bc165" +checksum = "sha256:231eb9ff52dc9a5d0708ed14bf091cecf221e8c7a8bc015067d262f52564b2d4" dependencies = [ "openzeppelin_access", "openzeppelin_token", @@ -52,31 +52,33 @@ dependencies = [ [[package]] name = "openzeppelin_governance" -version = "0.17.0" +version = "0.18.0" source = "registry+https://scarbs.xyz/" -checksum = "sha256:b7e0142d88d69a8c367aea8c9dc7f659f27372551efc23f39a0cf71a189c1302" +checksum = "sha256:88ef4007fb9665971f10a5fd4e8320ca20570808cb2c9ca5d8f7f95805834d5d" dependencies = [ "openzeppelin_access", + "openzeppelin_account", "openzeppelin_introspection", + "openzeppelin_token", ] [[package]] name = "openzeppelin_introspection" -version = "0.17.0" +version = "0.18.0" source = "registry+https://scarbs.xyz/" -checksum = "sha256:892433a4a1ea0fc9cf7cdb01e06ddc2782182abcc188e4ea5dd480906d006cf8" +checksum = "sha256:46c4cc6c95c9baa4c7d5cc0ed2bdaf334f46c25a8c92b3012829fff936e3042b" [[package]] name = "openzeppelin_merkle_tree" -version = "0.17.0" +version = "0.18.0" source = "registry+https://scarbs.xyz/" -checksum = "sha256:3c338fa07cbaba8034051a42967816800abe535ef7d709a929175616603dccf9" +checksum = "sha256:19b7baac7de27aaf997847c86ab331873dcebcaee74873778256b36f488de192" [[package]] name = "openzeppelin_presets" -version = "0.17.0" +version = "0.18.0" source = "registry+https://scarbs.xyz/" -checksum = "sha256:0a39e0effff133ab7fb003961ee2986438ee09b53608ce0d71aca24459879597" +checksum = "sha256:b95723242e7aeba4145b4f259168f751d4886c1a1195d2e0a52e0a0761b6c26d" dependencies = [ "openzeppelin_access", "openzeppelin_account", @@ -84,36 +86,37 @@ dependencies = [ "openzeppelin_introspection", "openzeppelin_token", "openzeppelin_upgrades", + "openzeppelin_utils", ] [[package]] name = "openzeppelin_security" -version = "0.17.0" +version = "0.18.0" source = "registry+https://scarbs.xyz/" -checksum = "sha256:6e2dee39d87f9ddec2ad37e33e80cf0d8b6c6927fd7950f220dbc2baea658d43" +checksum = "sha256:1db3a41e02ed48806587981340ed01ee7d552c3ad52cb33a6d81c1ed5cba9ee0" [[package]] name = "openzeppelin_token" -version = "0.17.0" +version = "0.18.0" source = "registry+https://scarbs.xyz/" -checksum = "sha256:77997a7e217b69674c34b402dc0c7b2210540db66a56087572679c31896eaabb" +checksum = "sha256:eafbe13f6a0487ce212459e25a81ae07f340ba76208ad4616626eb2d25a9625e" dependencies = [ "openzeppelin_account", - "openzeppelin_governance", "openzeppelin_introspection", + "openzeppelin_utils", ] [[package]] name = "openzeppelin_upgrades" -version = "0.17.0" +version = "0.18.0" source = "registry+https://scarbs.xyz/" -checksum = "sha256:a0fa5934f2924e1e85ec8f8c5b7dcd95c25295c029d3a745ba87b3191146004d" +checksum = "sha256:33c9d0865364fc18a5e7b471fe53c3b0f3e0aec56a94f435089638fad2a4a35b" [[package]] name = "openzeppelin_utils" -version = "0.17.0" +version = "0.18.0" source = "registry+https://scarbs.xyz/" -checksum = "sha256:36d93e353f42fd6b824abcd8b4b51c3f5d02c893c5f886ae81403b0368aa5fde" +checksum = "sha256:725b212839f3eddc32791408609099c5e808c167ca0cf331d8c1d778b07a4e21" [[package]] name = "test_project" diff --git a/packages/core-cairo/test_project/Scarb.toml b/packages/core-cairo/test_project/Scarb.toml index 1d8ec2861..a8a162c4e 100644 --- a/packages/core-cairo/test_project/Scarb.toml +++ b/packages/core-cairo/test_project/Scarb.toml @@ -2,12 +2,12 @@ name = "test_project" version = "0.1.0" edition = "2024_07" -cairo-version = "2.8.2" -scarb-version = "2.8.2" +cairo-version = "2.8.4" +scarb-version = "2.8.4" [dependencies] -starknet = "2.8.2" -openzeppelin = "0.17.0" +starknet = "2.8.4" +openzeppelin = "0.18.0" [lib] diff --git a/packages/ui/src/cairo/ERC20Controls.svelte b/packages/ui/src/cairo/ERC20Controls.svelte index a01109b4a..ec9e5b687 100644 --- a/packages/ui/src/cairo/ERC20Controls.svelte +++ b/packages/ui/src/cairo/ERC20Controls.svelte @@ -86,7 +86,7 @@ - + Keeps track of historical balances for voting in on-chain governance, with a way to delegate one's voting power to a trusted account. diff --git a/packages/ui/src/cairo/ERC721Controls.svelte b/packages/ui/src/cairo/ERC721Controls.svelte index 90076be2a..1d78ffe78 100644 --- a/packages/ui/src/cairo/ERC721Controls.svelte +++ b/packages/ui/src/cairo/ERC721Controls.svelte @@ -3,12 +3,12 @@ import type { KindedOptions, OptionsErrorMessages } from '@openzeppelin/wizard-cairo'; import { erc721, infoDefaults } from '@openzeppelin/wizard-cairo'; - + import AccessControlSection from './AccessControlSection.svelte'; import UpgradeabilityField from './UpgradeabilityField.svelte'; import InfoSection from './InfoSection.svelte'; import { error } from '../error-tooltip'; - + export const opts: Required = { kind: 'ERC721', ...erc721.defaults, @@ -79,6 +79,37 @@ +
+

+ + +

+ + + + +
+ \ No newline at end of file