From e0eabc9c5a7b13b713798e2890806023c8d8153e Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 11 Dec 2023 08:10:07 -0500 Subject: [PATCH] Use Cairo 1+ and OpenZeppelin Contracts for Cairo v0.8.0 (#305) --- .github/workflows/test.yml | 1 + packages/core-cairo/CHANGELOG.md | 10 + packages/core-cairo/package.json | 4 +- packages/core-cairo/src/add-pausable.ts | 94 +- packages/core-cairo/src/api.ts | 21 +- packages/core-cairo/src/build-generic.ts | 5 - packages/core-cairo/src/common-components.ts | 26 + packages/core-cairo/src/common-functions.ts | 45 - packages/core-cairo/src/common-options.ts | 12 +- packages/core-cairo/src/contract.test.ts | 117 +- packages/core-cairo/src/contract.test.ts.md | 159 +- packages/core-cairo/src/contract.test.ts.snap | Bin 519 -> 663 bytes packages/core-cairo/src/contract.ts | 248 +- packages/core-cairo/src/custom.test.ts | 5 +- packages/core-cairo/src/custom.test.ts.md | 486 ++-- packages/core-cairo/src/custom.test.ts.snap | Bin 920 -> 1132 bytes packages/core-cairo/src/custom.ts | 15 +- packages/core-cairo/src/erc1155.test.ts | 88 - packages/core-cairo/src/erc1155.test.ts.md | 1239 -------- packages/core-cairo/src/erc1155.test.ts.snap | Bin 2183 -> 0 bytes packages/core-cairo/src/erc1155.ts | 330 --- packages/core-cairo/src/erc20.test.ts | 33 +- packages/core-cairo/src/erc20.test.ts.md | 2497 +++++++---------- packages/core-cairo/src/erc20.test.ts.snap | Bin 2136 -> 2408 bytes packages/core-cairo/src/erc20.ts | 477 ++-- packages/core-cairo/src/erc721.test.ts | 2 +- packages/core-cairo/src/erc721.test.ts.md | 1508 +++++----- packages/core-cairo/src/erc721.test.ts.snap | Bin 1724 -> 2017 bytes packages/core-cairo/src/erc721.ts | 434 +-- packages/core-cairo/src/external-trait.ts | 10 + .../core-cairo/src/generate/alternatives.ts | 2 - packages/core-cairo/src/generate/erc20.ts | 8 +- packages/core-cairo/src/generate/erc721.ts | 4 - packages/core-cairo/src/generate/sources.ts | 2 +- packages/core-cairo/src/index.ts | 2 +- packages/core-cairo/src/kind.ts | 1 - packages/core-cairo/src/options.ts | 12 - packages/core-cairo/src/print.ts | 312 +- packages/core-cairo/src/set-access-control.ts | 226 +- packages/core-cairo/src/set-upgradeable.ts | 59 +- packages/core-cairo/src/test.ts | 2 +- .../src/utils/convert-strings.test.ts | 70 + .../core-cairo/src/utils/convert-strings.ts | 38 + .../core-cairo/src/utils/define-components.ts | 18 + .../core-cairo/src/utils/define-modules.ts | 18 - packages/core-cairo/src/utils/hash-builtin.ts | 14 - packages/core-cairo/src/utils/imports-map.ts | 57 - packages/core-cairo/src/utils/map-values.ts | 10 - .../core-cairo/src/utils/module-prefix.ts | 27 - .../src/utils/to-identifier.test.ts | 20 - .../core-cairo/src/utils/to-identifier.ts | 7 - packages/core-cairo/src/utils/uint256.test.ts | 32 - packages/core-cairo/src/utils/uint256.ts | 36 - packages/core-cairo/src/utils/version.ts | 2 +- packages/core/package.json | 2 +- packages/ui/package.json | 1 + packages/ui/src/HelpTooltip.svelte | 2 +- .../ui/src/cairo/AccessControlSection.svelte | 4 +- packages/ui/src/cairo/App.svelte | 16 +- packages/ui/src/cairo/CustomControls.svelte | 23 +- packages/ui/src/cairo/ERC1155Controls.svelte | 72 - packages/ui/src/cairo/ERC20Controls.svelte | 29 +- packages/ui/src/cairo/ERC721Controls.svelte | 22 +- .../ui/src/cairo/UpgradeabilityField.svelte | 15 + .../ui/src/cairo/UpgradeabilitySection.svelte | 34 - packages/ui/src/cairo/highlightjs.ts | 7 +- packages/ui/src/cairo/inject-hyperlinks.ts | 25 +- yarn.lock | 10 +- 68 files changed, 3422 insertions(+), 5685 deletions(-) create mode 100644 packages/core-cairo/src/common-components.ts delete mode 100644 packages/core-cairo/src/common-functions.ts delete mode 100644 packages/core-cairo/src/erc1155.test.ts delete mode 100644 packages/core-cairo/src/erc1155.test.ts.md delete mode 100644 packages/core-cairo/src/erc1155.test.ts.snap delete mode 100644 packages/core-cairo/src/erc1155.ts create mode 100644 packages/core-cairo/src/external-trait.ts delete mode 100644 packages/core-cairo/src/options.ts create mode 100644 packages/core-cairo/src/utils/convert-strings.test.ts create mode 100644 packages/core-cairo/src/utils/convert-strings.ts create mode 100644 packages/core-cairo/src/utils/define-components.ts delete mode 100644 packages/core-cairo/src/utils/define-modules.ts delete mode 100644 packages/core-cairo/src/utils/hash-builtin.ts delete mode 100644 packages/core-cairo/src/utils/imports-map.ts delete mode 100644 packages/core-cairo/src/utils/map-values.ts delete mode 100644 packages/core-cairo/src/utils/module-prefix.ts delete mode 100644 packages/core-cairo/src/utils/to-identifier.test.ts delete mode 100644 packages/core-cairo/src/utils/to-identifier.ts delete mode 100644 packages/core-cairo/src/utils/uint256.test.ts delete mode 100644 packages/core-cairo/src/utils/uint256.ts delete mode 100644 packages/ui/src/cairo/ERC1155Controls.svelte create mode 100644 packages/ui/src/cairo/UpgradeabilityField.svelte delete mode 100644 packages/ui/src/cairo/UpgradeabilitySection.svelte diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 267b551d..6845f84d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,6 +21,7 @@ jobs: node-version: 18.x cache: 'yarn' - name: Install Foundry + if: matrix.package == 'core' uses: foundry-rs/foundry-toolchain@v1 - name: Install dependencies run: yarn install diff --git a/packages/core-cairo/CHANGELOG.md b/packages/core-cairo/CHANGELOG.md index 70899a71..7d6cb218 100644 --- a/packages/core-cairo/CHANGELOG.md +++ b/packages/core-cairo/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## Unreleased + +- **Breaking changes**: + - Use Cairo 1+ and OpenZeppelin Contracts for Cairo v0.8.0. + - Remove functions for `getInitialSupply` and `toUint256`. + - Remove ERC1155. + - Role-Based Access Control adds separate constructor arguments to assign different users for different roles. + - Throws error if `name` results in an identifer that is empty or does not have valid characters. + - Throws error if `name` or `symbol` result in strings longer than 31 characters. + ## 0.6.0 (2023-01-11) - Add ERC1155. ([#167](https://github.com/OpenZeppelin/contracts-wizard/pull/167)) diff --git a/packages/core-cairo/package.json b/packages/core-cairo/package.json index db5d6b4c..fe228a67 100644 --- a/packages/core-cairo/package.json +++ b/packages/core-cairo/package.json @@ -1,6 +1,6 @@ { "name": "@openzeppelin/wizard-cairo", - "version": "0.6.0", + "version": "0.7.0", "description": "A boilerplate generator to get started with OpenZeppelin Contracts for Cairo", "license": "MIT", "repository": "github:OpenZeppelin/contracts-wizard", @@ -20,7 +20,7 @@ }, "devDependencies": { "@types/bn.js": "^5.1.0", - "@types/node": "^10.17.51", + "@types/node": "^18.0.0", "array.prototype.flat": "^1.2.4", "ava": "^5.0.0", "rimraf": "^5.0.0", diff --git a/packages/core-cairo/src/add-pausable.ts b/packages/core-cairo/src/add-pausable.ts index edd99722..e6f1e0f2 100644 --- a/packages/core-cairo/src/add-pausable.ts +++ b/packages/core-cairo/src/add-pausable.ts @@ -1,66 +1,62 @@ -import { withImplicitArgs } from './common-options'; -import type { ContractBuilder, BaseFunction } from './contract'; +import { getSelfArg } from './common-options'; +import type { BaseImplementedTrait, ContractBuilder, ContractFunction } from './contract'; import { Access, requireAccessControl } from './set-access-control'; import { defineFunctions } from './utils/define-functions'; -import { defineModules } from './utils/define-modules'; +import { defineComponents } from './utils/define-components'; +import { externalTrait } from './external-trait'; -export function addPausable(c: ContractBuilder, access: Access, pausableFns: BaseFunction[]) { - c.addModule(modules.Pausable, [], [functions.pause, functions.unpause], false); +export function addPausable(c: ContractBuilder, access: Access) { + c.addComponent(components.PausableComponent, [], false); - for (const fn of pausableFns) { - setPausable(c, fn); - } - - c.addFunction(functions.paused); - - requireAccessControl(c, functions.pause, access, 'PAUSER'); - requireAccessControl(c, functions.unpause, access, 'PAUSER'); + c.addFunction(externalTrait, functions.pause); + c.addFunction(externalTrait, functions.unpause); + requireAccessControl(c, externalTrait, functions.pause, access, 'PAUSER', 'pauser'); + requireAccessControl(c, externalTrait, functions.unpause, access, 'PAUSER', 'pauser'); } -const modules = defineModules( { - Pausable: { - path: 'openzeppelin.security.pausable.library', - useNamespace: true +const components = defineComponents( { + PausableComponent: { + path: 'openzeppelin::security::pausable', + substorage: { + name: 'pausable', + type: 'PausableComponent::Storage', + }, + event: { + name: 'PausableEvent', + type: 'PausableComponent::Event', + }, + impls: [ + { + name: 'PausableImpl', + value: 'PausableComponent::PausableImpl', + }, + ], + internalImpl: { + name: 'PausableInternalImpl', + value: 'PausableComponent::InternalImpl', + }, }, }); const functions = defineFunctions({ - - paused: { - module: modules.Pausable, - kind: 'view', - implicitArgs: withImplicitArgs(), - args: [], - returns: [{ name: 'paused', type: 'felt' }], - passthrough: true, - parentFunctionName: 'is_paused', - }, - pause: { - module: modules.Pausable, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [], - parentFunctionName: '_pause', + args: [ + getSelfArg(), + ], + code: [ + 'self.pausable._pause()' + ] }, - unpause: { - module: modules.Pausable, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [], - parentFunctionName: '_unpause', - }, - - // --- library-only calls --- - - assert_not_paused: { - module: modules.Pausable, - args: [], + args: [ + getSelfArg(), + ], + code: [ + 'self.pausable._unpause()' + ] }, - }); -export function setPausable(c: ContractBuilder, fn: BaseFunction) { - c.addLibraryCall(functions.assert_not_paused, fn); +export function setPausable(c: ContractBuilder, t: BaseImplementedTrait, fn: ContractFunction) { + c.addFunctionCodeBefore(t, fn, 'self.pausable.assert_not_paused()'); } diff --git a/packages/core-cairo/src/api.ts b/packages/core-cairo/src/api.ts index 5a1bbc54..26fdcb7d 100644 --- a/packages/core-cairo/src/api.ts +++ b/packages/core-cairo/src/api.ts @@ -1,9 +1,7 @@ import type { CommonOptions } from './common-options'; -import { printERC20, defaults as erc20defaults, isAccessControlRequired as erc20IsAccessControlRequired, ERC20Options, getInitialSupply } from './erc20'; +import { printERC20, defaults as erc20defaults, isAccessControlRequired as erc20IsAccessControlRequired, ERC20Options } from './erc20'; import { printERC721, defaults as erc721defaults, isAccessControlRequired as erc721IsAccessControlRequired, ERC721Options } from './erc721'; -import { printERC1155, defaults as erc1155defaults, isAccessControlRequired as erc1155IsAccessControlRequired, ERC1155Options } from './erc1155'; import { printCustom, defaults as customDefaults, isAccessControlRequired as customIsAccessControlRequired, CustomOptions } from './custom'; -import { toUint256 } from './utils/uint256'; export interface WizardContractAPI { /** @@ -23,37 +21,22 @@ export interface WizardContractAPI { isAccessControlRequired: (opts: Partial) => boolean, } -export type ERC20 = WizardContractAPI & { - /** - * Calculates the initial supply that would be used in an ERC20 contract based on a given premint amount and number of decimals. - */ - getInitialSupply: (premint: string, decimals: number) => string; -} +export type ERC20 = WizardContractAPI; export type ERC721 = WizardContractAPI; -export type ERC1155 = WizardContractAPI; export type Custom = WizardContractAPI; export const erc20: ERC20 = { print: printERC20, defaults: erc20defaults, isAccessControlRequired: erc20IsAccessControlRequired, - getInitialSupply } export const erc721: ERC721 = { print: printERC721, defaults: erc721defaults, isAccessControlRequired: erc721IsAccessControlRequired } -export const erc1155: ERC1155 = { - print: printERC1155, - defaults: erc1155defaults, - isAccessControlRequired: erc1155IsAccessControlRequired -} export const custom: Custom = { print: printCustom, defaults: customDefaults, isAccessControlRequired: customIsAccessControlRequired } -export const utils = { - toUint256 -} \ No newline at end of file diff --git a/packages/core-cairo/src/build-generic.ts b/packages/core-cairo/src/build-generic.ts index b15eadbe..3a28e208 100644 --- a/packages/core-cairo/src/build-generic.ts +++ b/packages/core-cairo/src/build-generic.ts @@ -1,12 +1,10 @@ import { ERC20Options, buildERC20 } from './erc20'; import { ERC721Options, buildERC721 } from './erc721'; -import { ERC1155Options, buildERC1155 } from './erc1155'; import { CustomOptions, buildCustom } from './custom'; export interface KindedOptions { ERC20: { kind: 'ERC20' } & ERC20Options; ERC721: { kind: 'ERC721' } & ERC721Options; - ERC1155: { kind: 'ERC1155' } & ERC1155Options; Custom: { kind: 'Custom' } & CustomOptions; } @@ -20,9 +18,6 @@ export function buildGeneric(opts: GenericOptions) { case 'ERC721': return buildERC721(opts); - case 'ERC1155': - return buildERC1155(opts); - case 'Custom': return buildCustom(opts); diff --git a/packages/core-cairo/src/common-components.ts b/packages/core-cairo/src/common-components.ts new file mode 100644 index 00000000..dff26e48 --- /dev/null +++ b/packages/core-cairo/src/common-components.ts @@ -0,0 +1,26 @@ +import type { ContractBuilder } from "./contract"; +import { defineComponents } from "./utils/define-components"; + +const components = defineComponents( { + SRC5Component: { + path: 'openzeppelin::introspection::src5', + substorage: { + name: 'src5', + type: 'SRC5Component::Storage', + }, + event: { + name: 'SRC5Event', + type: 'SRC5Component::Event', + }, + impls: [ + { + name: 'SRC5Impl', + value: 'SRC5Component::SRC5Impl', + }, + ], + }, +}) + +export function addSRC5Component(c: ContractBuilder) { + c.addComponent(components.SRC5Component, [], false); +} \ No newline at end of file diff --git a/packages/core-cairo/src/common-functions.ts b/packages/core-cairo/src/common-functions.ts deleted file mode 100644 index 61f4882d..00000000 --- a/packages/core-cairo/src/common-functions.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { withImplicitArgs } from './common-options'; -import type { ContractBuilder } from './contract'; -import { defineFunctions } from './utils/define-functions'; -import { defineModules } from './utils/define-modules'; - -export function addSupportsInterface(c: ContractBuilder) { - c.addModule( - modules.ERC165, [], [], false - ); - c.addFunction(functions.supportsInterface); -} - -export function importGetCallerAddress(c: ContractBuilder) { - c.addModule( - modules.syscalls, [], [], false - ); - c.addModuleFunction(modules.syscalls, 'get_caller_address'); -} - -const modules = defineModules({ - ERC165: { - path: 'openzeppelin.introspection.erc165.library', - useNamespace: true - }, - - syscalls: { - path: 'starkware.starknet.common.syscalls', - useNamespace: false - }, -}) - -const functions = defineFunctions({ - // --- view functions --- - supportsInterface: { - module: modules.ERC165, - kind: 'view', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'interfaceId', type: 'felt' }, - ], - returns: [{ name: 'success', type: 'felt' }], - passthrough: true, - parentFunctionName: 'supports_interface', - }, -}); \ No newline at end of file diff --git a/packages/core-cairo/src/common-options.ts b/packages/core-cairo/src/common-options.ts index 05ff871a..6961816d 100644 --- a/packages/core-cairo/src/common-options.ts +++ b/packages/core-cairo/src/common-options.ts @@ -24,10 +24,10 @@ export function withCommonDefaults(opts: CommonOptions): Required }; } -export function withImplicitArgs(): Argument[] { - return [ - { name: 'syscall_ptr', type: 'felt*' }, - { name: 'pedersen_ptr', type: 'HashBuiltin*' }, - { name: 'range_check_ptr' } - ]; +export function getSelfArg(scope: 'external' | 'view' = 'external'): Argument { + if (scope === 'view') { + return { name: 'self', type: '@ContractState' }; + } else { + return { name: 'ref self', type: 'ContractState' }; + } } \ No newline at end of file diff --git a/packages/core-cairo/src/contract.test.ts b/packages/core-cairo/src/contract.test.ts index 80cbba5b..ef3c0757 100644 --- a/packages/core-cairo/src/contract.test.ts +++ b/packages/core-cairo/src/contract.test.ts @@ -1,75 +1,110 @@ import test from 'ava'; -import { BaseFunction, ContractBuilder } from './contract'; +import { ContractBuilder, BaseFunction, BaseImplementedTrait, Component } from './contract'; import { printContract } from './print'; +const FOO_COMPONENT: Component = { + name: 'FooComponent', + path: 'some::path', + substorage: { + name: 'foo', + type: 'FooComponent::Storage', + }, + event: { + name: 'FooEvent', + type: 'FooComponent::Event', + }, + impls: [ + { + name: 'FooImpl', + value: 'FooComponent::FooImpl', + }, + ], + internalImpl: { + name: 'FooInternalImpl', + value: 'FooComponent::InternalImpl', + }, +}; + test('contract basics', t => { - const Foo = new ContractBuilder(); + const Foo = new ContractBuilder('Foo'); t.snapshot(printContract(Foo)); }); test('contract with constructor code', t => { - const Foo = new ContractBuilder(); + const Foo = new ContractBuilder('Foo'); Foo.addConstructorCode('someFunction()'); t.snapshot(printContract(Foo)); }); test('contract with constructor code with semicolon', t => { - const Foo = new ContractBuilder(); + const Foo = new ContractBuilder('Foo'); Foo.addConstructorCode('someFunction();'); t.snapshot(printContract(Foo)); }); -test('contract with function code', t => { - const Foo = new ContractBuilder(); - Foo.addFunctionCode('someFunction()', _otherFunction); +test('contract with function code before', t => { + const Foo = new ContractBuilder('Foo'); + const trait: BaseImplementedTrait = { + name: 'External', + of: 'ExternalTrait', + tags: [ + '#[generate_trait]', + '#[external(v0)]' + ], + }; + Foo.addImplementedTrait(trait); + const fn: BaseFunction = { + name: 'someFunction', + args: [], + code: [ + 'someFunction()' + ] + }; + Foo.addFunction(trait, fn); + Foo.addFunctionCodeBefore(trait, fn, 'before()'); t.snapshot(printContract(Foo)); }); -test('contract with function code with semicolon', t => { - const Foo = new ContractBuilder(); - Foo.addFunctionCode('someFunction();', _otherFunction); +test('contract with function code before with semicolons', t => { + const Foo = new ContractBuilder('Foo'); + const trait: BaseImplementedTrait = { + name: 'External', + of: 'ExternalTrait', + tags: [ + '#[generate_trait]', + '#[external(v0)]' + ], + }; + Foo.addImplementedTrait(trait); + const fn: BaseFunction = { + name: 'someFunction', + args: [], + code: [ + 'someFunction();' + ] + }; + Foo.addFunction(trait, fn); + Foo.addFunctionCodeBefore(trait, fn, 'before();'); t.snapshot(printContract(Foo)); }); test('contract with initializer params', t => { - const Foo = new ContractBuilder(); - Foo.addModule( - someModule, + const Foo = new ContractBuilder('Foo'); + + Foo.addComponent( + FOO_COMPONENT, ['param1'], - [], true ); t.snapshot(printContract(Foo)); }); -test('contract with library call', t => { - const Foo = new ContractBuilder(); - Foo.addModule( - someModule, - [], - [], - false +test('contract with standalone import', t => { + const Foo = new ContractBuilder('Foo'); + Foo.addComponent( + FOO_COMPONENT, ); - Foo.addFunction(_libraryFunction); + Foo.addStandaloneImport('some::library::SomeLibrary'); t.snapshot(printContract(Foo)); }); - -const someModule = { - name: 'SomeLibrary', - path: 'contracts/some/library', - useNamespace: true -}; - -const _otherFunction: BaseFunction = { - name: 'otherFunction', - kind: 'external', - args: [], -}; - -const _libraryFunction: BaseFunction = { - module: someModule, - name: 'libraryFunction', - kind: 'external', - args: [], -}; \ No newline at end of file diff --git a/packages/core-cairo/src/contract.test.ts.md b/packages/core-cairo/src/contract.test.ts.md index 1510ca5a..d33d3b48 100644 --- a/packages/core-cairo/src/contract.test.ts.md +++ b/packages/core-cairo/src/contract.test.ts.md @@ -10,7 +10,12 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ + #[starknet::contract]␊ + mod Foo {␊ + #[storage]␊ + struct Storage {␊ + }␊ + }␊ ` ## contract with constructor code @@ -19,14 +24,16 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ + #[starknet::contract]␊ + mod Foo {␊ + #[storage]␊ + struct Storage {␊ + }␊ ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - someFunction();␊ - return ();␊ + #[constructor]␊ + fn constructor(ref self: ContractState) {␊ + someFunction();␊ + }␊ }␊ ` @@ -36,56 +43,62 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ + #[starknet::contract]␊ + mod Foo {␊ + #[storage]␊ + struct Storage {␊ + }␊ ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - someFunction();␊ - return ();␊ + #[constructor]␊ + fn constructor(ref self: ContractState) {␊ + someFunction();␊ + }␊ }␊ ` -## contract with function code +## contract with function code before > Snapshot 1 `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func otherFunction() {␊ - someFunction();␊ - return ();␊ + #[starknet::contract]␊ + mod Foo {␊ + #[storage]␊ + struct Storage {␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl External of ExternalTrait {␊ + fn someFunction() {␊ + before();␊ + someFunction();␊ + }␊ + }␊ }␊ ` -## contract with function code with semicolon +## contract with function code before with semicolons > Snapshot 1 `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func otherFunction() {␊ - someFunction();␊ - return ();␊ + #[starknet::contract]␊ + mod Foo {␊ + #[storage]␊ + struct Storage {␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl External of ExternalTrait {␊ + fn someFunction() {␊ + before();␊ + someFunction();␊ + }␊ + }␊ }␊ ` @@ -95,38 +108,66 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ + #[starknet::contract]␊ + mod Foo {␊ + use some::path::FooComponent;␊ + ␊ + component!(path: FooComponent, storage: foo, event: FooEvent);␊ ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ + #[abi(embed_v0)]␊ + impl FooImpl = FooComponent::FooImpl;␊ ␊ - from contracts.some.library import SomeLibrary␊ + impl FooInternalImpl = FooComponent::InternalImpl;␊ ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - SomeLibrary.initializer('param1');␊ - return ();␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + foo: FooComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + FooEvent: FooComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState) {␊ + self.foo.initializer('param1');␊ + }␊ }␊ ` -## contract with library call +## contract with standalone import > Snapshot 1 `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ + #[starknet::contract]␊ + mod Foo {␊ + use some::path::FooComponent;␊ + use some::library::SomeLibrary;␊ + ␊ + component!(path: FooComponent, storage: foo, event: FooEvent);␊ ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ + #[abi(embed_v0)]␊ + impl FooImpl = FooComponent::FooImpl;␊ ␊ - from contracts.some.library import SomeLibrary␊ + impl FooInternalImpl = FooComponent::InternalImpl;␊ ␊ - //␊ - // Externals␊ - //␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + foo: FooComponent::Storage,␊ + }␊ ␊ - @external␊ - func libraryFunction() {␊ - SomeLibrary.libraryFunction();␊ - return ();␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + FooEvent: FooComponent::Event,␊ + }␊ }␊ ` diff --git a/packages/core-cairo/src/contract.test.ts.snap b/packages/core-cairo/src/contract.test.ts.snap index 63cc588c5aa62c8a3f125842dc4622b722062223..1a82b9125c57cfd1373c44b66e0bea07ee82d7f5 100644 GIT binary patch literal 663 zcmV;I0%-j~RzVEbpT2u(e}d`bNL_47BM_u|VWOffo@U%r82 zDg}&?D?uYgLMmA#ndCH=6IAl^WgJsQk1p=C?zM{1zT3Lr&bGG5=|e zaK#b^A|MCDL(hBqUMeb1IjA70l8?O%W3q=xExG9WMWn`_vW58 zdH!(W@{Fko(KE9Jg?^7=)%4H0>CTVV8eLmy&7+YgyzbiSzCoGTEVrvB`C*Th(sr)1ZL2_}p8!(v|zlt$~as;w2nu&5AThu8RA2L1B) zpqDyw^|HAI{}gQ`R{WIkje#K&$mv~pNHUpr|_BSrj` zbBVmM=@vV=DPLoT`eJjMKj|^6n}$|;)l6ldAO=gv+U-5JLrX4IW5yuZ4E!B|dBLx$ zks)S8z^5501CEa*rRwVRit@6BGPq>mCa!J0ys5uv>J?d;m&-;CK^6|}}cZ>!cL+>aBCzv77 zOZDOCQyf3D+I?0mn#K+!p6Q%Fy%jn`$yKmeXkx@ucu!$L_cYpQ3P1SSxvz4ZkuPfV zOcNwVBl*PmqVTRZhNB5uLB|S<(vzq|&H3xv7sm&U9OG=_Of%_AzF?!R9hMU0GqAF| zr%FhH7+mdaTo(2y = new Set(); - private librariesMap: Map = new Map(); - private functionMap: Map = new Map(); - readonly constructorImplicitArgs: Argument[] = withImplicitArgs(); + private componentsMap: Map = new Map(); + private implementedTraitsMap: Map = new Map(); + private superVariablesMap: Map = new Map(); + private standaloneImportsSet: Set = new Set(); - get libraries(): Library[] { - return [...this.librariesMap.values()]; + constructor(name: string) { + this.name = toIdentifier(name, true); } - get functions(): ContractFunction[] { - return [...this.functionMap.values()]; + get components(): Component[] { + return [...this.componentsMap.values()]; } - get variables(): string[] { - return [...this.variableSet]; + get implementedTraits(): ImplementedTrait[] { + return [...this.implementedTraitsMap.values()]; } - addModule(module: Module, params: Value[] = [], functions: BaseFunction[] = [], initializable: boolean = true): boolean { - const key = module; - const present = this.librariesMap.has(key); - const initializer = initializable ? { params } : undefined; + get superVariables(): Variable[] { + return [...this.superVariablesMap.values()]; + } - if (initializer !== undefined && initializer.params.length > 0) { - // presence of initializer params implies initializer code will be written, so implicit args must be included - importHashBuiltin(this); - } + get standaloneImports(): string[] { + return [...this.standaloneImportsSet]; + } - const functionStrings: string[] = []; - functions.forEach(fn => { - functionStrings.push(getImportName(fn)); - }); - if (initializable) { - functionStrings.push(getImportName({ - module: module, - name: 'initializer', - args: [] - })) - } + addStandaloneImport(fullyQualified: string) { + this.standaloneImportsSet.add(fullyQualified); + } - this.librariesMap.set(module, { module, functions: functionStrings, initializer }); + addComponent(component: Component, params: Value[] = [], initializable: boolean = true): boolean { + const key = component.name; + const present = this.componentsMap.has(key); + if (!present) { + const initializer = initializable ? { params } : undefined; + const cp: Component = { initializer, ...component, impls: [ ...component.impls ] }; // spread impls to deep copy from original component + this.componentsMap.set(key, cp); + } return !present; } - addModuleFunction(module: Module, addFunction: string) { - const existing = this.librariesMap.get(module); - if (existing === undefined) { - throw new Error(`Module ${module} has not been added yet`); + addImplToComponent(component: Component, impl: Impl) { + this.addComponent(component); + let c = this.componentsMap.get(component.name); + if (c == undefined) { + throw new Error(`Component ${component.name} has not been added yet`); } - if (!existing.functions.includes(addFunction)) { - existing.functions.push(addFunction); + + if (!c.impls.some(i => i.name === impl.name)) { + c.impls.push(impl); } } - addLibraryCall(callFn: BaseFunction, baseFn: BaseFunction, args: string[] = []) { - const fn = this.addFunction(baseFn); - if (callFn.module !== undefined) { - this.addModuleFunction(callFn.module, getImportName(callFn)); + addSuperVariable(variable: Variable) { + if (this.superVariablesMap.has(variable.name)) { + return false; + } else { + this.superVariablesMap.set(variable.name, variable); + return true; } - const libraryCall: LibraryCall = { callFn, args }; - fn.libraryCalls.push(libraryCall); } - addFunction(baseFn: BaseFunction): ContractFunction { - importHashBuiltin(this); - - const signature = [baseFn.name, '(', ...baseFn.args.map(a => a.name), ')'].join(''); - const got = this.functionMap.get(signature); - if (got !== undefined) { - return got; + addImplementedTrait(baseTrait: BaseImplementedTrait) { + const key = baseTrait.name; + const existingTrait = this.implementedTraitsMap.get(key); + if (existingTrait !== undefined) { + return existingTrait; } else { - const fn: ContractFunction = { - libraryCalls: [], - code: [], - final: false, - ...baseFn, + const t: ImplementedTrait = { + name: baseTrait.name, + of: baseTrait.of, + tags: [ ...baseTrait.tags ], + functions: [], }; - this.functionMap.set(signature, fn); - return fn; + this.implementedTraitsMap.set(key, t); + return t; } } - addConstructorArgument(arg: Argument) { - for (const existingArg of this.constructorArgs) { - if (existingArg.name == arg.name) { - return; + addFunction(baseTrait: BaseImplementedTrait, fn: BaseFunction) { + const t = this.addImplementedTrait(baseTrait); + + const signature = this.getFunctionSignature(fn); + + // Look for the existing function with the same signature and return it if found + for (let i = 0; i < t.functions.length; i++) { + const existingFn = t.functions[i]; + if (existingFn !== undefined && this.getFunctionSignature(existingFn) === signature) { + return existingFn; } } - this.constructorArgs.push(arg); + + // Otherwise, add the function + const contractFn: ContractFunction = { + ...fn, + codeBefore: [], + }; + t.functions.push(contractFn); + return contractFn; } - addConstructorCode(code: string) { - importHashBuiltin(this); - - this.constructorCode.push(code); + private getFunctionSignature(fn: BaseFunction) { + return [fn.name, '(', ...fn.args.map(a => a.name), ')'].join(''); } - addFunctionCode(code: string, baseFn: BaseFunction) { - const fn = this.addFunction(baseFn); - if (fn.final) { - throw new Error(`Function ${baseFn.name} is already finalized`); - } - fn.code.push(code); + addFunctionCodeBefore(baseTrait: BaseImplementedTrait, fn: BaseFunction, codeBefore: string) { + this.addImplementedTrait(baseTrait); + const existingFn = this.addFunction(baseTrait, fn); + existingFn.codeBefore = [ ...existingFn.codeBefore ?? [], codeBefore ]; } - setFunctionBody(code: string[], baseFn: BaseFunction) { - const fn = this.addFunction(baseFn); - if (fn.code.length > 0) { - throw new Error(`Function ${baseFn.name} has additional code`); + addConstructorArgument(arg: Argument) { + for (const existingArg of this.constructorArgs) { + if (existingArg.name == arg.name) { + return; + } } - fn.code.push(...code); - fn.final = true; + this.constructorArgs.push(arg); } - addVariable(code: string): boolean { - const present = this.variableSet.has(code); - this.variableSet.add(code); - return !present; + addConstructorCode(code: string) { + this.constructorCode.push(code); } } diff --git a/packages/core-cairo/src/custom.test.ts b/packages/core-cairo/src/custom.test.ts index bf729fd5..1b3addab 100644 --- a/packages/core-cairo/src/custom.test.ts +++ b/packages/core-cairo/src/custom.test.ts @@ -7,6 +7,7 @@ import { printContract } from './print'; function testCustom(title: string, opts: Partial) { test(title, t => { const c = buildCustom({ + name: 'MyContract', ...opts, }); t.snapshot(printContract(c)); @@ -19,6 +20,7 @@ function testCustom(title: string, opts: Partial) { function testAPIEquivalence(title: string, opts?: CustomOptions) { test(title, t => { t.is(custom.print(opts), printContract(buildCustom({ + name: 'MyContract', ...opts, }))); }); @@ -55,6 +57,7 @@ testCustom('pausable with access control disabled', { testAPIEquivalence('custom API default'); testAPIEquivalence('custom API full upgradeable', { + name: 'CustomContract', access: 'roles', pausable: true, upgradeable: true, @@ -66,5 +69,5 @@ test('custom API assert defaults', async t => { test('API isAccessControlRequired', async t => { t.is(custom.isAccessControlRequired({ pausable: true }), true); - t.is(custom.isAccessControlRequired({ upgradeable: true }), false); + t.is(custom.isAccessControlRequired({ upgradeable: true }), true); }); \ No newline at end of file diff --git a/packages/core-cairo/src/custom.test.ts.md b/packages/core-cairo/src/custom.test.ts.md index 14169887..47234ec1 100644 --- a/packages/core-cairo/src/custom.test.ts.md +++ b/packages/core-cairo/src/custom.test.ts.md @@ -10,7 +10,12 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ + #[starknet::contract]␊ + mod MyContract {␊ + #[storage]␊ + struct Storage {␊ + }␊ + }␊ ` ## pausable @@ -19,65 +24,60 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - ␊ - from openzeppelin.security.pausable.library import Pausable␊ - from openzeppelin.access.ownable.library import Ownable␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) {␊ - Ownable.initializer(owner);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (paused: felt) {␊ - return Pausable.is_paused();␊ - }␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ - }␊ - ␊ - @external␊ - func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._pause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._unpause();␊ - return ();␊ + #[starknet::contract]␊ + mod MyContract {␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use starknet::ContractAddress;␊ + ␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableImpl = OwnableComponent::OwnableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableCamelOnlyImpl = OwnableComponent::OwnableCamelOnlyImpl;␊ + ␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn pause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._pause();␊ + }␊ + ␊ + fn unpause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._unpause();␊ + }␊ + }␊ }␊ ` @@ -87,31 +87,54 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - ␊ - from openzeppelin.upgrades.library import Proxy␊ - ␊ - @external␊ - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - proxy_admin: felt␊ - ) {␊ - Proxy.initializer(proxy_admin);␊ - return ();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func upgrade{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - new_implementation: felt␊ - ) -> () {␊ - Proxy.assert_only_admin();␊ - Proxy._set_implementation_hash(new_implementation);␊ - return ();␊ + #[starknet::contract]␊ + mod MyContract {␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use starknet::ClassHash;␊ + use starknet::ContractAddress;␊ + ␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl OwnableImpl = OwnableComponent::OwnableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableCamelOnlyImpl = OwnableComponent::OwnableCamelOnlyImpl;␊ + ␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + #[external(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);␊ + }␊ + }␊ }␊ ` @@ -121,7 +144,12 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ + #[starknet::contract]␊ + mod MyContract {␊ + #[storage]␊ + struct Storage {␊ + }␊ + }␊ ` ## access control ownable @@ -130,45 +158,37 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - ␊ - from openzeppelin.access.ownable.library import Ownable␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) {␊ - Ownable.initializer(owner);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ + #[starknet::contract]␊ + mod MyContract {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use starknet::ContractAddress;␊ + ␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl OwnableImpl = OwnableComponent::OwnableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableCamelOnlyImpl = OwnableComponent::OwnableCamelOnlyImpl;␊ + ␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.ownable.initializer(owner);␊ + }␊ }␊ ` @@ -178,67 +198,48 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - ␊ - from openzeppelin.access.accesscontrol.library import AccessControl␊ - from openzeppelin.utils.constants.library import DEFAULT_ADMIN_ROLE␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - admin: felt␊ - ) {␊ - AccessControl.initializer();␊ - ␊ - AccessControl._grant_role(DEFAULT_ADMIN_ROLE, admin);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func hasRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) -> (has_role: felt) {␊ - return AccessControl.has_role(role, user);␊ - }␊ - ␊ - @view␊ - func getRoleAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt␊ - ) -> (admin: felt) {␊ - return AccessControl.get_role_admin(role);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func grantRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.grant_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func revokeRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.revoke_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.renounce_role(role, user);␊ - return ();␊ + #[starknet::contract]␊ + mod MyContract {␊ + use openzeppelin::access::accesscontrol::AccessControlComponent;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE;␊ + use starknet::ContractAddress;␊ + ␊ + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + ␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + accesscontrol: AccessControlComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + AccessControlEvent: AccessControlComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, default_admin: ContractAddress) {␊ + self.accesscontrol.initializer();␊ + ␊ + self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ + }␊ }␊ ` @@ -248,64 +249,59 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - ␊ - from openzeppelin.security.pausable.library import Pausable␊ - from openzeppelin.access.ownable.library import Ownable␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) {␊ - Ownable.initializer(owner);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (paused: felt) {␊ - return Pausable.is_paused();␊ - }␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ - }␊ - ␊ - @external␊ - func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._pause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._unpause();␊ - return ();␊ + #[starknet::contract]␊ + mod MyContract {␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use starknet::ContractAddress;␊ + ␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableImpl = OwnableComponent::OwnableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableCamelOnlyImpl = OwnableComponent::OwnableCamelOnlyImpl;␊ + ␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn pause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._pause();␊ + }␊ + ␊ + fn unpause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._unpause();␊ + }␊ + }␊ }␊ ` diff --git a/packages/core-cairo/src/custom.test.ts.snap b/packages/core-cairo/src/custom.test.ts.snap index 5acfe6d0dcce8aa3fe6e740da233e48c2e3b77bb..bd8639bca8cb61a82b2e80a8e1ef269c4597063c 100644 GIT binary patch literal 1132 zcmV-y1e5zgRzV z)8BXl2Oo)~p%e(Fms0X-k-JRb$%G*EhOcG|o0EH3I4-iEV5Ng{D+$1La#I|!t zw*6ZD*9iwvNJX@jhRxV@fic?-k7%}YEBmKr;}BvFY1Q*$v3sSJ+PL|a`&NW`-$oS> zZKO(Uqa11*!J)V^G+vj>#g%%GWpJ89l|bQ7nda`)Eyg`!!Jr48bNFO2yU}D21x56( zTs*5HA68UaJk1NR%`u1PDIA|`5 zZcz)~sDSCN3iC{FV^68FOM1%GDvBmw#gNpcqa3xz@RLtWO&W%m(;Rr(n0spBK}-(8 zT8qP|txZC-%|WF?)dtN%1ApVlb#O*Mu3S8nvC#QtX$ zY|uNsp_Sz6Y*p$qQvg+~H0MC!$Z;jNIekHH;&RS&6ObgWPlYNeBxb;~H0mg0c$Df% z7$A?N0_WZ2Pm_lw%&K5jiJkvuNbJ}7tCiTR{CGIio(kfdpb|HO*Qb+^i+Sdyc@{sw zs?)si$C&0{3)eHv*IgKa zHR3c~w0|X^iPE+QK28GeU~eGkWh8wYx6B~%d`cTk9dQDs+>sn^RmrM^qpAhR5A*L> y&=KK%H16?q_8ejM9AV1OwX^34qy5~B^Y-jH!t6Q1grJ=H9N{mGPL?6+9{>R6b~Vrd literal 920 zcmV;J184j}RzV`5%i200000000B+Sle#XFc{uJ6H@MY1K&)_(3Wxy(zL+_3~h88L)xw^H~G6- zb?wM@)^*b^xZ(wPfx(O7#Lkwap%e%!gH}EH_<#TD`yJnOJ4}Rq_2~;}su^^{L}@Xg zij!FNgjOGKqKIg6vbntQXd!(qt}HxWeA;TE_lG+t8~Zc_u3%#?0K`f+3Z;0x>uzha1h=E^190Q{~P!J}P>QRuR4jAo7B1if9L)TxkmV_ZF6^J3P zTiCYUp0^bWuCz=-Eu_~?c!&xeS8Aj}!WfRV(YXsuKUqgHL?9J#+gZ@Sj7ehZd&s;D25{@WHFTBrIRF9WR8zv}B}hM;xAr_6m!K2Lk#CyUp4V!bH+l_P8_{XY z+d8K(w189NK=k`q*U-i$^6hSRha9W}lUqBW3fpN8UI9#|LsM7^hO0oy&6mw68EN;z z*oYJ6%ml7!(P<+{bwRe%WvWNxhCIw+*q-3zTr3v3Tt8n?>+T9_30#<8Xb^Q3VJ@P8 za2jM;6f}>9CwWm3F766JRWkJy#CI = { + name: 'MyContract', pausable: false, access: commonDefaults.access, upgradeable: commonDefaults.upgradeable, @@ -19,6 +20,7 @@ export function printCustom(opts: CustomOptions = defaults): string { } export interface CustomOptions extends CommonOptions { + name: string; pausable?: boolean; } @@ -31,21 +33,22 @@ function withDefaults(opts: CustomOptions): Required { } export function isAccessControlRequired(opts: Partial): boolean { - return opts.pausable === true; + return opts.pausable === true || opts.upgradeable === true; } export function buildCustom(opts: CustomOptions): Contract { - const allOpts = withDefaults(opts); + const c = new ContractBuilder(opts.name); - const c = new ContractBuilder(); + const allOpts = withDefaults(opts); if (allOpts.pausable) { - addPausable(c, allOpts.access, []); + addPausable(c, allOpts.access); } setAccessControl(c, allOpts.access); - setUpgradeable(c, allOpts.upgradeable); + setUpgradeable(c, allOpts.upgradeable, allOpts.access); setInfo(c, allOpts.info); return c; } + diff --git a/packages/core-cairo/src/erc1155.test.ts b/packages/core-cairo/src/erc1155.test.ts deleted file mode 100644 index 630c005f..00000000 --- a/packages/core-cairo/src/erc1155.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import test from 'ava'; -import { erc1155 } from '.'; - -import { buildERC1155, ERC1155Options } from './erc1155'; -import { printContract } from './print'; - -function testERC1155(title: string, opts: Partial) { - test(title, t => { - const c = buildERC1155({ - uri: 'https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/', - ...opts, - }); - t.snapshot(printContract(c)); - }); -} - -/** - * Tests external API for equivalence with internal API - */ - function testAPIEquivalence(title: string, opts?: ERC1155Options) { - test(title, t => { - t.is(erc1155.print(opts), printContract(buildERC1155({ - uri: '', - ...opts, - }))); - }); -} - -testERC1155('basic', {}); - -testERC1155('basic + roles', { - access: 'roles', -}); - -testERC1155('no updatable uri', { - updatableUri: false, -}); - -testERC1155('burnable', { - burnable: true, -}); - -testERC1155('pausable', { - pausable: true, -}); - -testERC1155('mintable', { - mintable: true, -}); - -testERC1155('mintable + roles', { - mintable: true, - access: 'roles', -}); - -testERC1155('full upgradeable', { - mintable: true, - access: 'roles', - burnable: true, - pausable: true, - upgradeable: true, -}); - -testAPIEquivalence('API default'); - -testAPIEquivalence('API basic', { uri: 'https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/' }); - -testAPIEquivalence('API full upgradeable', { - uri: 'https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/', - mintable: true, - access: 'roles', - burnable: true, - pausable: true, - upgradeable: true, -}); - -test('API assert defaults', async t => { - t.is(erc1155.print(erc1155.defaults), erc1155.print()); -}); - -test('API isAccessControlRequired', async t => { - t.is(erc1155.isAccessControlRequired({ updatableUri: false, mintable: true }), true); - t.is(erc1155.isAccessControlRequired({ updatableUri: false, pausable: true }), true); - t.is(erc1155.isAccessControlRequired({ updatableUri: false, upgradeable: true }), false); - t.is(erc1155.isAccessControlRequired({ updatableUri: true }), true); - t.is(erc1155.isAccessControlRequired({ updatableUri: false}), false); - t.is(erc1155.isAccessControlRequired({}), true); // updatableUri is true by default -}); \ No newline at end of file diff --git a/packages/core-cairo/src/erc1155.test.ts.md b/packages/core-cairo/src/erc1155.test.ts.md deleted file mode 100644 index a2f44b38..00000000 --- a/packages/core-cairo/src/erc1155.test.ts.md +++ /dev/null @@ -1,1239 +0,0 @@ -# Snapshot report for `src/erc1155.test.ts` - -The actual snapshot is saved in `erc1155.test.ts.snap`. - -Generated by [AVA](https://avajs.dev). - -## basic - -> Snapshot 1 - - `// SPDX-License-Identifier: MIT␊ - ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc1155.library import ERC1155␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - from openzeppelin.access.ownable.library import Ownable␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) {␊ - ERC1155.initializer('https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/');␊ - Ownable.initializer(owner);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - id: Uint256␊ - ) -> (uri: felt) {␊ - return ERC1155.uri(id);␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, id: Uint256␊ - ) -> (balance: Uint256) {␊ - return ERC1155.balance_of(account, id);␊ - }␊ - ␊ - @view␊ - func balanceOfBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - accounts_len: felt, accounts: felt*, ids_len: felt, ids: Uint256*␊ - ) -> (balances_len: felt, balances: Uint256*) {␊ - return ERC1155.balance_of_batch(accounts_len, accounts, ids_len, ids);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC1155.is_approved_for_all(account, operator);␊ - }␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - ERC1155.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt*␊ - ) {␊ - ERC1155.safe_transfer_from(from_, to, id, value, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeBatchTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt,␊ - to: felt,␊ - ids_len: felt,␊ - ids: Uint256*,␊ - values_len: felt,␊ - values: Uint256*,␊ - data_len: felt,␊ - data: felt*,␊ - ) {␊ - ERC1155.safe_batch_transfer_from(from_, to, ids_len, ids, values_len, values, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func setURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - uri: felt␊ - ) {␊ - Ownable.assert_only_owner();␊ - ERC1155._set_uri(uri);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ - }␊ - ` - -## basic + roles - -> Snapshot 1 - - `// SPDX-License-Identifier: MIT␊ - ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc1155.library import ERC1155␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - from openzeppelin.access.accesscontrol.library import AccessControl␊ - from openzeppelin.utils.constants.library import DEFAULT_ADMIN_ROLE␊ - ␊ - const URI_SETTER_ROLE = 0x7804d923f43a17d325d77e781528e0793b2edd9890ab45fc64efd7b4b427744; // keccak256('URI_SETTER_ROLE')[0:251 bits]␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - admin: felt␊ - ) {␊ - ERC1155.initializer('https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/');␊ - AccessControl.initializer();␊ - ␊ - AccessControl._grant_role(DEFAULT_ADMIN_ROLE, admin);␊ - AccessControl._grant_role(URI_SETTER_ROLE, admin);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - id: Uint256␊ - ) -> (uri: felt) {␊ - return ERC1155.uri(id);␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, id: Uint256␊ - ) -> (balance: Uint256) {␊ - return ERC1155.balance_of(account, id);␊ - }␊ - ␊ - @view␊ - func balanceOfBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - accounts_len: felt, accounts: felt*, ids_len: felt, ids: Uint256*␊ - ) -> (balances_len: felt, balances: Uint256*) {␊ - return ERC1155.balance_of_batch(accounts_len, accounts, ids_len, ids);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC1155.is_approved_for_all(account, operator);␊ - }␊ - ␊ - @view␊ - func hasRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) -> (has_role: felt) {␊ - return AccessControl.has_role(role, user);␊ - }␊ - ␊ - @view␊ - func getRoleAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt␊ - ) -> (admin: felt) {␊ - return AccessControl.get_role_admin(role);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - ERC1155.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt*␊ - ) {␊ - ERC1155.safe_transfer_from(from_, to, id, value, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeBatchTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt,␊ - to: felt,␊ - ids_len: felt,␊ - ids: Uint256*,␊ - values_len: felt,␊ - values: Uint256*,␊ - data_len: felt,␊ - data: felt*,␊ - ) {␊ - ERC1155.safe_batch_transfer_from(from_, to, ids_len, ids, values_len, values, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func setURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - uri: felt␊ - ) {␊ - AccessControl.assert_only_role(URI_SETTER_ROLE);␊ - ERC1155._set_uri(uri);␊ - return ();␊ - }␊ - ␊ - @external␊ - func grantRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.grant_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func revokeRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.revoke_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.renounce_role(role, user);␊ - return ();␊ - }␊ - ` - -## no updatable uri - -> Snapshot 1 - - `// SPDX-License-Identifier: MIT␊ - ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc1155.library import ERC1155␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - ERC1155.initializer('https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/');␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - id: Uint256␊ - ) -> (uri: felt) {␊ - return ERC1155.uri(id);␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, id: Uint256␊ - ) -> (balance: Uint256) {␊ - return ERC1155.balance_of(account, id);␊ - }␊ - ␊ - @view␊ - func balanceOfBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - accounts_len: felt, accounts: felt*, ids_len: felt, ids: Uint256*␊ - ) -> (balances_len: felt, balances: Uint256*) {␊ - return ERC1155.balance_of_batch(accounts_len, accounts, ids_len, ids);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC1155.is_approved_for_all(account, operator);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - ERC1155.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt*␊ - ) {␊ - ERC1155.safe_transfer_from(from_, to, id, value, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeBatchTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt,␊ - to: felt,␊ - ids_len: felt,␊ - ids: Uint256*,␊ - values_len: felt,␊ - values: Uint256*,␊ - data_len: felt,␊ - data: felt*,␊ - ) {␊ - ERC1155.safe_batch_transfer_from(from_, to, ids_len, ids, values_len, values, data_len, data);␊ - return ();␊ - }␊ - ` - -## burnable - -> Snapshot 1 - - `// SPDX-License-Identifier: MIT␊ - ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc1155.library import ERC1155␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - from openzeppelin.access.ownable.library import Ownable␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) {␊ - ERC1155.initializer('https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/');␊ - Ownable.initializer(owner);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - id: Uint256␊ - ) -> (uri: felt) {␊ - return ERC1155.uri(id);␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, id: Uint256␊ - ) -> (balance: Uint256) {␊ - return ERC1155.balance_of(account, id);␊ - }␊ - ␊ - @view␊ - func balanceOfBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - accounts_len: felt, accounts: felt*, ids_len: felt, ids: Uint256*␊ - ) -> (balances_len: felt, balances: Uint256*) {␊ - return ERC1155.balance_of_batch(accounts_len, accounts, ids_len, ids);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC1155.is_approved_for_all(account, operator);␊ - }␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - ERC1155.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt*␊ - ) {␊ - ERC1155.safe_transfer_from(from_, to, id, value, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeBatchTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt,␊ - to: felt,␊ - ids_len: felt,␊ - ids: Uint256*,␊ - values_len: felt,␊ - values: Uint256*,␊ - data_len: felt,␊ - data: felt*,␊ - ) {␊ - ERC1155.safe_batch_transfer_from(from_, to, ids_len, ids, values_len, values, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, id: Uint256, value: Uint256␊ - ) {␊ - ERC1155.assert_owner_or_approved(owner=from_);␊ - ERC1155._burn(from_, id, value);␊ - return ();␊ - }␊ - ␊ - @external␊ - func burnBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, ids_len: felt, ids: Uint256*, values_len: felt, values: Uint256*␊ - ) {␊ - ERC1155.assert_owner_or_approved(owner=from_);␊ - ERC1155._burn_batch(from_, ids_len, ids, values_len, values);␊ - return ();␊ - }␊ - ␊ - @external␊ - func setURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - uri: felt␊ - ) {␊ - Ownable.assert_only_owner();␊ - ERC1155._set_uri(uri);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ - }␊ - ` - -## pausable - -> Snapshot 1 - - `// SPDX-License-Identifier: MIT␊ - ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc1155.library import ERC1155␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - from openzeppelin.security.pausable.library import Pausable␊ - from openzeppelin.access.ownable.library import Ownable␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) {␊ - ERC1155.initializer('https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/');␊ - Ownable.initializer(owner);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - id: Uint256␊ - ) -> (uri: felt) {␊ - return ERC1155.uri(id);␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, id: Uint256␊ - ) -> (balance: Uint256) {␊ - return ERC1155.balance_of(account, id);␊ - }␊ - ␊ - @view␊ - func balanceOfBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - accounts_len: felt, accounts: felt*, ids_len: felt, ids: Uint256*␊ - ) -> (balances_len: felt, balances: Uint256*) {␊ - return ERC1155.balance_of_batch(accounts_len, accounts, ids_len, ids);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC1155.is_approved_for_all(account, operator);␊ - }␊ - ␊ - @view␊ - func paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (paused: felt) {␊ - return Pausable.is_paused();␊ - }␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC1155.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt*␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC1155.safe_transfer_from(from_, to, id, value, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeBatchTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt,␊ - to: felt,␊ - ids_len: felt,␊ - ids: Uint256*,␊ - values_len: felt,␊ - values: Uint256*,␊ - data_len: felt,␊ - data: felt*,␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC1155.safe_batch_transfer_from(from_, to, ids_len, ids, values_len, values, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ - }␊ - ␊ - @external␊ - func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._pause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._unpause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func setURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - uri: felt␊ - ) {␊ - Ownable.assert_only_owner();␊ - ERC1155._set_uri(uri);␊ - return ();␊ - }␊ - ` - -## mintable - -> Snapshot 1 - - `// SPDX-License-Identifier: MIT␊ - ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc1155.library import ERC1155␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - from openzeppelin.access.ownable.library import Ownable␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) {␊ - ERC1155.initializer('https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/');␊ - Ownable.initializer(owner);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - id: Uint256␊ - ) -> (uri: felt) {␊ - return ERC1155.uri(id);␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, id: Uint256␊ - ) -> (balance: Uint256) {␊ - return ERC1155.balance_of(account, id);␊ - }␊ - ␊ - @view␊ - func balanceOfBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - accounts_len: felt, accounts: felt*, ids_len: felt, ids: Uint256*␊ - ) -> (balances_len: felt, balances: Uint256*) {␊ - return ERC1155.balance_of_batch(accounts_len, accounts, ids_len, ids);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC1155.is_approved_for_all(account, operator);␊ - }␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - ERC1155.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt*␊ - ) {␊ - ERC1155.safe_transfer_from(from_, to, id, value, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeBatchTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt,␊ - to: felt,␊ - ids_len: felt,␊ - ids: Uint256*,␊ - values_len: felt,␊ - values: Uint256*,␊ - data_len: felt,␊ - data: felt*,␊ - ) {␊ - ERC1155.safe_batch_transfer_from(from_, to, ids_len, ids, values_len, values, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt*␊ - ) {␊ - Ownable.assert_only_owner();␊ - ERC1155._mint(to, id, value, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ - }␊ - ␊ - @external␊ - func mintBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt,␊ - ids_len: felt,␊ - ids: Uint256*,␊ - values_len: felt,␊ - values: Uint256*,␊ - data_len: felt,␊ - data: felt*,␊ - ) {␊ - Ownable.assert_only_owner();␊ - ERC1155._mint_batch(to, ids_len, ids, values_len, values, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func setURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - uri: felt␊ - ) {␊ - Ownable.assert_only_owner();␊ - ERC1155._set_uri(uri);␊ - return ();␊ - }␊ - ` - -## mintable + roles - -> Snapshot 1 - - `// SPDX-License-Identifier: MIT␊ - ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc1155.library import ERC1155␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - from openzeppelin.access.accesscontrol.library import AccessControl␊ - from openzeppelin.utils.constants.library import DEFAULT_ADMIN_ROLE␊ - ␊ - const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5; // keccak256('MINTER_ROLE')[0:251 bits]␊ - const URI_SETTER_ROLE = 0x7804d923f43a17d325d77e781528e0793b2edd9890ab45fc64efd7b4b427744; // keccak256('URI_SETTER_ROLE')[0:251 bits]␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - admin: felt␊ - ) {␊ - ERC1155.initializer('https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/');␊ - AccessControl.initializer();␊ - ␊ - AccessControl._grant_role(DEFAULT_ADMIN_ROLE, admin);␊ - AccessControl._grant_role(MINTER_ROLE, admin);␊ - AccessControl._grant_role(URI_SETTER_ROLE, admin);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - id: Uint256␊ - ) -> (uri: felt) {␊ - return ERC1155.uri(id);␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, id: Uint256␊ - ) -> (balance: Uint256) {␊ - return ERC1155.balance_of(account, id);␊ - }␊ - ␊ - @view␊ - func balanceOfBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - accounts_len: felt, accounts: felt*, ids_len: felt, ids: Uint256*␊ - ) -> (balances_len: felt, balances: Uint256*) {␊ - return ERC1155.balance_of_batch(accounts_len, accounts, ids_len, ids);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC1155.is_approved_for_all(account, operator);␊ - }␊ - ␊ - @view␊ - func hasRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) -> (has_role: felt) {␊ - return AccessControl.has_role(role, user);␊ - }␊ - ␊ - @view␊ - func getRoleAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt␊ - ) -> (admin: felt) {␊ - return AccessControl.get_role_admin(role);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - ERC1155.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt*␊ - ) {␊ - ERC1155.safe_transfer_from(from_, to, id, value, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeBatchTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt,␊ - to: felt,␊ - ids_len: felt,␊ - ids: Uint256*,␊ - values_len: felt,␊ - values: Uint256*,␊ - data_len: felt,␊ - data: felt*,␊ - ) {␊ - ERC1155.safe_batch_transfer_from(from_, to, ids_len, ids, values_len, values, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt*␊ - ) {␊ - AccessControl.assert_only_role(MINTER_ROLE);␊ - ERC1155._mint(to, id, value, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func grantRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.grant_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func revokeRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.revoke_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.renounce_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func mintBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt,␊ - ids_len: felt,␊ - ids: Uint256*,␊ - values_len: felt,␊ - values: Uint256*,␊ - data_len: felt,␊ - data: felt*,␊ - ) {␊ - AccessControl.assert_only_role(MINTER_ROLE);␊ - ERC1155._mint_batch(to, ids_len, ids, values_len, values, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func setURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - uri: felt␊ - ) {␊ - AccessControl.assert_only_role(URI_SETTER_ROLE);␊ - ERC1155._set_uri(uri);␊ - return ();␊ - }␊ - ` - -## full upgradeable - -> Snapshot 1 - - `// SPDX-License-Identifier: MIT␊ - ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc1155.library import ERC1155␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - from openzeppelin.security.pausable.library import Pausable␊ - from openzeppelin.access.accesscontrol.library import AccessControl␊ - from openzeppelin.utils.constants.library import DEFAULT_ADMIN_ROLE␊ - from openzeppelin.upgrades.library import Proxy␊ - ␊ - const PAUSER_ROLE = 0x65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862; // keccak256('PAUSER_ROLE')[0:251 bits]␊ - const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5; // keccak256('MINTER_ROLE')[0:251 bits]␊ - const URI_SETTER_ROLE = 0x7804d923f43a17d325d77e781528e0793b2edd9890ab45fc64efd7b4b427744; // keccak256('URI_SETTER_ROLE')[0:251 bits]␊ - ␊ - @external␊ - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - admin: felt, proxy_admin: felt␊ - ) {␊ - ERC1155.initializer('https://gateway.pinata.cloud/ipfs/QmcP9hxrnC1T5ATPmq2saFeAM1ypFX9BnAswCdHB9JCjLA/');␊ - AccessControl.initializer();␊ - Proxy.initializer(proxy_admin);␊ - ␊ - AccessControl._grant_role(DEFAULT_ADMIN_ROLE, admin);␊ - AccessControl._grant_role(PAUSER_ROLE, admin);␊ - AccessControl._grant_role(MINTER_ROLE, admin);␊ - AccessControl._grant_role(URI_SETTER_ROLE, admin);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func uri{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - id: Uint256␊ - ) -> (uri: felt) {␊ - return ERC1155.uri(id);␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, id: Uint256␊ - ) -> (balance: Uint256) {␊ - return ERC1155.balance_of(account, id);␊ - }␊ - ␊ - @view␊ - func balanceOfBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - accounts_len: felt, accounts: felt*, ids_len: felt, ids: Uint256*␊ - ) -> (balances_len: felt, balances: Uint256*) {␊ - return ERC1155.balance_of_batch(accounts_len, accounts, ids_len, ids);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC1155.is_approved_for_all(account, operator);␊ - }␊ - ␊ - @view␊ - func paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (paused: felt) {␊ - return Pausable.is_paused();␊ - }␊ - ␊ - @view␊ - func hasRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) -> (has_role: felt) {␊ - return AccessControl.has_role(role, user);␊ - }␊ - ␊ - @view␊ - func getRoleAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt␊ - ) -> (admin: felt) {␊ - return AccessControl.get_role_admin(role);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC1155.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt*␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC1155.safe_transfer_from(from_, to, id, value, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeBatchTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt,␊ - to: felt,␊ - ids_len: felt,␊ - ids: Uint256*,␊ - values_len: felt,␊ - values: Uint256*,␊ - data_len: felt,␊ - data: felt*,␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC1155.safe_batch_transfer_from(from_, to, ids_len, ids, values_len, values, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func grantRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.grant_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func revokeRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.revoke_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.renounce_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - AccessControl.assert_only_role(PAUSER_ROLE);␊ - Pausable._pause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - AccessControl.assert_only_role(PAUSER_ROLE);␊ - Pausable._unpause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, id: Uint256, value: Uint256␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC1155.assert_owner_or_approved(owner=from_);␊ - ERC1155._burn(from_, id, value);␊ - return ();␊ - }␊ - ␊ - @external␊ - func burnBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, ids_len: felt, ids: Uint256*, values_len: felt, values: Uint256*␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC1155.assert_owner_or_approved(owner=from_);␊ - ERC1155._burn_batch(from_, ids_len, ids, values_len, values);␊ - return ();␊ - }␊ - ␊ - @external␊ - func mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, id: Uint256, value: Uint256, data_len: felt, data: felt*␊ - ) {␊ - Pausable.assert_not_paused();␊ - AccessControl.assert_only_role(MINTER_ROLE);␊ - ERC1155._mint(to, id, value, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func mintBatch{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt,␊ - ids_len: felt,␊ - ids: Uint256*,␊ - values_len: felt,␊ - values: Uint256*,␊ - data_len: felt,␊ - data: felt*,␊ - ) {␊ - Pausable.assert_not_paused();␊ - AccessControl.assert_only_role(MINTER_ROLE);␊ - ERC1155._mint_batch(to, ids_len, ids, values_len, values, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func setURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - uri: felt␊ - ) {␊ - AccessControl.assert_only_role(URI_SETTER_ROLE);␊ - ERC1155._set_uri(uri);␊ - return ();␊ - }␊ - ␊ - @external␊ - func upgrade{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - new_implementation: felt␊ - ) -> () {␊ - Proxy.assert_only_admin();␊ - Proxy._set_implementation_hash(new_implementation);␊ - return ();␊ - }␊ - ` diff --git a/packages/core-cairo/src/erc1155.test.ts.snap b/packages/core-cairo/src/erc1155.test.ts.snap deleted file mode 100644 index 8568ac070ccf336d274b3447a2d2ca72db007e8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2183 zcmV;22zd8FRzVXDc6XqeH@s^w@^}P_) zxNz-jTOW%E00000000B+T-|QmL=-Nd3b8`1;F2qIfx0c}{&>CK-BzG((}V`KB&4YX zLdY81%+)Zb3*q0Pn&ZAl`s$W^9kwe@QltH@j(1)Sq}fbLKl| zW{%Hy#-86S-SLcl^3(5_+SI{VN5nR4;v%0+JW77}%`_23U%vhG8<*a^#D8A9eCeIn zpTFI1OOJN$eR1=FZD5z+n_DJ!scqRfxFtQ@+G{jEaFF{%A`}JtE~br!6?oJ5@&E<6 zWgt87T81~BdTxB7>7ngV+a;1c^}T>f_Ys-gjmwU09ojCH`>JU6IKE7J^nC1ohkYMA zw%elKK6YC;FuL7-zvb9^fPzEOZew?y7gsc60t1iu*r2u-H&-iLt5uDWVPHa9-hqpB z2N#=d$Hk4t9m8`84MKx@LBk4NL&_99Cx^s9j-&aMnb*P&y|E_w*u(+BZd{l#7P}WP z+hfg`U}K-({=V6e*bl>illG14((|Y+(VFRKQyV$yF96x_STZ=+pIY z9*o=9uHTM&OVrKv7NJV+2bhL|D>d2W_l-uo&HhNAU`m;y{Hk&1sf`a}^N`Tz79?9P zyK5l>*JLDH$lEegQ{DVnYLYM-$hb^cFTooReoH{oQW?#Rv{_Ma zWFLu?4>!Btw9Alm#8$0=x2>9hvB~d+E{!FuRp3lIOpA|zn^e<0tC@)8!j6W27g1wU zuMR|WuqzZ1WvN)(%-v^~sY*BUs^;p6vRUO54WQ{3itH^2|&u*sfxH7Ud&otn^wdKErnO=93foVn()Np?N)>jXmC0t~iB6Wki_tr>O6JLBCX z(q}^B-{!SQ2lmZ%^#P2&2q4n-8r{7GCCX;!Kjmlo?5Y?_t{9jXl*d8OmbJ9A5G z893ti7qU90k9W6fGIRRCOw|+X9T9>9s(G$+sHJN^tG~wA7Js0!zw_W0M%w1W$i((* zQo+Rs+Z7R>&?6liPxRXqE#^TTV3+w}_HeBabrGTHB7?9BSwdB&5mk{slmgGeq)b_^ z{+XjJzg`8(ayrTq|Ho(u6Xg^ekE5IG@y!YuA+;USiZ~2nfl0CXy^YQB;|F`%_};^< z&$Qj`2OEt>R9Rw6T6?syx3{qy6-ysUoo9n#M=?io&r*7*J1~24-y96^VA$=;L);mR zdb*5Fb2J=vkgoJCLshV44s=CV!0s8~?cPjQb zL1dSCmYS5|PD*GJ5}en9b*0TJ6tu|uJW;jD|A|GFkUzncBOh}QSUY@S_}O#jSiB5N zgjb6iM5uw`0>cG{`!X}!ybH+k+^SgZjI1{b0?)%!SLv3CKywax4%YjW5wK;!j(dMG z+%c729`c0B&w)MR^S#Q`qG&R-lJK4}l&6 zJv^Bn0%-x#0;J`fNJ|pFO>|boL$`9+cIB^J*!IU22;1Q0F?e|l;zNiJAwGnc#~@0D zC>5gAb55=EXmt^D@0b}^(p_ZSxlhpes!G;T>_6FxXveALF=QR zU~AbNtPErn(7$`ANVxYu8iO-P|bEk~Wx;SwxtrDj2@*HPetqJ`6H2gd@U=lsL9?cjM z82l-11RTF!j(Foy2(P$qrPh4Z2$9t2UI zGAD7w{_lS|qWmuqW!V1(vI}Gv$S#oGWs_atm$3f}`@gT={x6SKYcg_;({28soa1kv zdF}!Kt(X7uQK>V0SefN|x`~;D85i`k$hw)aFPQPsXVtv>PmUq~{Vp)%i*QyAyUv|c zqgW%=8V)RVr1o@OR#ZzidY!(ks`5}#)uGZcR7Ez8zR~ZPeWVW9kD(ey2P+EF`^9r= zGU5wo)WA_WFaJ?FnbC*Sbs#+g=@Cee!09@0x(=j(AO!>|pqDoVbi$!LnW%6vDIqwg z=k%~&jdOZlnPk`5mf*!sel7G&Fp&_mLdTIppRINiSHCjyUMH6jd;dL1h@IcJ73Z8x ztB?^-jZ)*Cz)zJBkfvOIAiqbKMs$9%`WyDt|-yrvw_e+_ySpA7V)|?N$w&J-!ULl&Jxf>bi zi_8~hCM={FA;kzOMo2MUh!o>WeQpH#&R0C&37=HKCsiw)GhO{psvv&|`NR3J*ZdjQ z37M-5JzAxY$IgKIG#`^K^SGn=eHI*uR5}o8@y4tF$fVWWSUcI0%iel>X*X0BZF<;p znfq9 = { - uri: '', - burnable: false, - pausable: false, - mintable: false, - updatableUri: true, - access: commonDefaults.access, - upgradeable: commonDefaults.upgradeable, - info: commonDefaults.info -} as const; - -export function printERC1155(opts: ERC1155Options = defaults): string { - return printContract(buildERC1155(opts)); -} - -export interface ERC1155Options extends CommonOptions { - uri: string; - burnable?: boolean; - pausable?: boolean; - mintable?: boolean; - updatableUri?: boolean; -} - -function withDefaults(opts: ERC1155Options): Required { - return { - ...opts, - ...withCommonDefaults(opts), - burnable: opts.burnable ?? defaults.burnable, - pausable: opts.pausable ?? defaults.pausable, - mintable: opts.mintable ?? defaults.mintable, - updatableUri: opts.updatableUri ?? defaults.updatableUri, - }; -} - -export function isAccessControlRequired(opts: Partial): boolean { - return opts.mintable || opts.pausable || opts.updatableUri !== false; -} - -export function buildERC1155(opts: ERC1155Options): Contract { - const c = new ContractBuilder(); - - const allOpts = withDefaults(opts); - - addBase(c, allOpts.uri); - addSupportsInterface(c); - - c.addFunction(functions.uri); - c.addFunction(functions.balanceOf); - c.addFunction(functions.balanceOfBatch); - c.addFunction(functions.isApprovedForAll); - - c.addFunction(functions.setApprovalForAll); - c.addFunction(functions.safeTransferFrom); - c.addFunction(functions.safeBatchTransferFrom); - - importUint256(c); - - if (allOpts.pausable) { - addPausable(c, allOpts.access, [ - functions.setApprovalForAll, - functions.safeTransferFrom, - functions.safeBatchTransferFrom, - ]); - if (allOpts.burnable) { - setPausable(c, functions.burn); - setPausable(c, functions.burnBatch); - } - if (allOpts.mintable) { - setPausable(c, functions.mint); - setPausable(c, functions.mintBatch); - } - } - - if (allOpts.burnable) { - addBurnable(c); - } - - if (allOpts.mintable) { - addMintable(c, allOpts.access); - } - - if (allOpts.updatableUri) { - addSetUri(c, allOpts.access); - } - - setAccessControl(c, allOpts.access); - setUpgradeable(c, allOpts.upgradeable); - - setInfo(c, allOpts.info); - - return c; -} - -function addBase(c: ContractBuilder, uri: string) { - c.addModule( - modules.ERC1155, - [uri], - [ - functions.safeTransferFrom, - functions.safeBatchTransferFrom, - ], - true, - ); -} - -function addBurnable(c: ContractBuilder) { - c.addFunction(functions.burn); - c.addLibraryCall(functions.assert_owner_or_approved, functions.burn, [ 'owner=from_' ]); - - c.addFunction(functions.burnBatch); - c.addLibraryCall(functions.assert_owner_or_approved, functions.burnBatch, [ 'owner=from_' ]); -} - -function addMintable(c: ContractBuilder, access: Access) { - c.addFunction(functions.mint); - requireAccessControl(c, functions.mint, access, 'MINTER'); - - c.addFunction(functions.mintBatch); - requireAccessControl(c, functions.mintBatch, access, 'MINTER'); -} - -function addSetUri(c: ContractBuilder, access: Access) { - c.addFunction(functions.setURI); - requireAccessControl(c, functions.setURI, access, 'URI_SETTER'); -} - -const modules = defineModules( { - ERC1155: { - path: 'openzeppelin.token.erc1155.library', - useNamespace: true - }, - - math: { - path: 'starkware.cairo.common.math', - useNamespace: false - } -}); - -const functions = defineFunctions({ - - // --- view functions --- - - uri: { - module: modules.ERC1155, - kind: 'view', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'id', type: 'Uint256' }, - ], - returns: [{ name: 'uri', type: 'felt' }], - passthrough: true, - }, - - balanceOf: { - module: modules.ERC1155, - kind: 'view', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'account', type: 'felt' }, - { name: 'id', type: 'Uint256' }, - ], - returns: [{ name: 'balance', type: 'Uint256' }], - passthrough: true, - parentFunctionName: 'balance_of', - }, - - balanceOfBatch: { - module: modules.ERC1155, - kind: 'view', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'accounts_len', type: 'felt' }, - { name: 'accounts', type: 'felt*' }, - { name: 'ids_len', type: 'felt' }, - { name: 'ids', type: 'Uint256*' }, - ], - returns: [ - { name: 'balances_len', type: 'felt' }, - { name: 'balances', type: 'Uint256*' }], - passthrough: true, - parentFunctionName: 'balance_of_batch', - }, - - isApprovedForAll: { - module: modules.ERC1155, - kind: 'view', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'account', type: 'felt' }, - { name: 'operator', type: 'felt' }, - ], - returns: [{ name: 'approved', type: 'felt' }], - passthrough: true, - parentFunctionName: 'is_approved_for_all', - }, - - // --- external functions --- - - setURI: { - module: modules.ERC1155, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'uri', type: 'felt' }, - ], - parentFunctionName: '_set_uri', - }, - - setApprovalForAll: { - module: modules.ERC1155, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'operator', type: 'felt' }, - { name: 'approved', type: 'felt' }, - ], - parentFunctionName: 'set_approval_for_all', - }, - - safeTransferFrom: { - module: modules.ERC1155, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'from_', type: 'felt' }, - { name: 'to', type: 'felt' }, - { name: 'id', type: 'Uint256' }, - { name: 'value', type: 'Uint256' }, - { name: 'data_len', type: 'felt' }, - { name: 'data', type: 'felt*' }, - ], - parentFunctionName: 'safe_transfer_from', - }, - - safeBatchTransferFrom: { - module: modules.ERC1155, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'from_', type: 'felt' }, - { name: 'to', type: 'felt' }, - { name: 'ids_len', type: 'felt' }, - { name: 'ids', type: 'Uint256*' }, - { name: 'values_len', type: 'felt' }, - { name: 'values', type: 'Uint256*' }, - { name: 'data_len', type: 'felt' }, - { name: 'data', type: 'felt*' }, - ], - parentFunctionName: 'safe_batch_transfer_from', - }, - - mint: { - module: modules.ERC1155, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'to', type: 'felt' }, - { name: 'id', type: 'Uint256' }, - { name: 'value', type: 'Uint256' }, - { name: 'data_len', type: 'felt' }, - { name: 'data', type: 'felt*' }, - ], - parentFunctionName: '_mint', - }, - - mintBatch: { - module: modules.ERC1155, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'to', type: 'felt' }, - { name: 'ids_len', type: 'felt' }, - { name: 'ids', type: 'Uint256*' }, - { name: 'values_len', type: 'felt' }, - { name: 'values', type: 'Uint256*' }, - { name: 'data_len', type: 'felt' }, - { name: 'data', type: 'felt*' }, - ], - parentFunctionName: '_mint_batch', - }, - - burn: { - module: modules.ERC1155, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'from_', type: 'felt' }, - { name: 'id', type: 'Uint256' }, - { name: 'value', type: 'Uint256' }, - ], - parentFunctionName: '_burn', - }, - - burnBatch: { - module: modules.ERC1155, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'from_', type: 'felt' }, - { name: 'ids_len', type: 'felt' }, - { name: 'ids', type: 'Uint256*' }, - { name: 'values_len', type: 'felt' }, - { name: 'values', type: 'Uint256*' }, - ], - parentFunctionName: '_burn_batch', - }, - - // --- library-only calls --- - - assert_owner_or_approved: { - module: modules.ERC1155, - args: [ - { name: 'owner', type: 'felt' }, - ], - }, - -}); \ No newline at end of file diff --git a/packages/core-cairo/src/erc20.test.ts b/packages/core-cairo/src/erc20.test.ts index 1b78da22..27404f99 100644 --- a/packages/core-cairo/src/erc20.test.ts +++ b/packages/core-cairo/src/erc20.test.ts @@ -1,10 +1,9 @@ import test from 'ava'; -import { buildERC20, ERC20Options } from './erc20'; +import { buildERC20, ERC20Options, getInitialSupply } from './erc20'; import { printContract } from './print'; -import { erc20 } from '.'; -import type { OptionsError } from './error'; +import { erc20, OptionsError } from '.'; function testERC20(title: string, opts: Partial) { test(title, t => { @@ -69,23 +68,27 @@ testERC20('erc20 mintable with roles', { access: 'roles', }); +testERC20('erc20 safe allowance', { + safeAllowance: true, +}); + testERC20('erc20 full upgradeable', { premint: '2000', - decimals: '9', access: 'ownable', burnable: true, mintable: true, pausable: true, + safeAllowance: true, upgradeable: true, }); testERC20('erc20 full upgradeable with roles', { premint: '2000', - decimals: '9', access: 'roles', burnable: true, mintable: true, pausable: true, + safeAllowance: true, upgradeable: true, }); @@ -97,11 +100,11 @@ testAPIEquivalence('erc20 API full upgradeable', { name: 'CustomToken', symbol: 'CTK', premint: '2000', - decimals: '9', access: 'roles', burnable: true, mintable: true, pausable: true, + safeAllowance: true, upgradeable: true, }); @@ -112,20 +115,20 @@ test('erc20 API assert defaults', async t => { test('erc20 API isAccessControlRequired', async t => { t.is(erc20.isAccessControlRequired({ mintable: true }), true); t.is(erc20.isAccessControlRequired({ pausable: true }), true); - t.is(erc20.isAccessControlRequired({ upgradeable: true }), false); + t.is(erc20.isAccessControlRequired({ upgradeable: true }), true); }); -test('erc20 API getInitialSupply', async t => { - t.is(erc20.getInitialSupply('1000', 18), '1000000000000000000000'); - t.is(erc20.getInitialSupply('1000.1', 18), '1000100000000000000000'); - t.is(erc20.getInitialSupply('.1', 18), '100000000000000000'); - t.is(erc20.getInitialSupply('.01', 2), '1'); - - let error = t.throws(() => erc20.getInitialSupply('.01', 1)); +test('erc20 getInitialSupply', async t => { + t.is(getInitialSupply('1000', 18), '1000000000000000000000'); + t.is(getInitialSupply('1000.1', 18), '1000100000000000000000'); + t.is(getInitialSupply('.1', 18), '100000000000000000'); + t.is(getInitialSupply('.01', 2), '1'); + + let error = t.throws(() => getInitialSupply('.01', 1)); t.not(error, undefined); t.is((error as OptionsError).messages.premint, 'Too many decimals'); - error = t.throws(() => erc20.getInitialSupply('1.1.1', 18)); + error = t.throws(() => getInitialSupply('1.1.1', 18)); t.not(error, undefined); t.is((error as OptionsError).messages.premint, 'Not a valid number'); }); \ No newline at end of file diff --git a/packages/core-cairo/src/erc20.test.ts.md b/packages/core-cairo/src/erc20.test.ts.md index c476d220..f9ce8e88 100644 --- a/packages/core-cairo/src/erc20.test.ts.md +++ b/packages/core-cairo/src/erc20.test.ts.md @@ -10,95 +10,38 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc20.library import ERC20␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - ERC20.initializer('MyToken', 'MTK', 18);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC20.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC20.symbol();␊ - }␊ - ␊ - @view␊ - func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (totalSupply: Uint256) {␊ - let (totalSupply) = ERC20.total_supply();␊ - return (totalSupply=totalSupply);␊ - }␊ - ␊ - @view␊ - func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (decimals: felt) {␊ - return ERC20.decimals();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt␊ - ) -> (balance: Uint256) {␊ - return ERC20.balance_of(account);␊ - }␊ - ␊ - @view␊ - func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, spender: felt␊ - ) -> (remaining: Uint256) {␊ - return ERC20.allowance(owner, spender);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.transfer(recipient, amount);␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - sender: felt, recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.transfer_from(sender, recipient, amount);␊ - }␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.approve(spender, amount);␊ - }␊ - ␊ - @external␊ - func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, added_value: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.increase_allowance(spender, added_value);␊ - }␊ - ␊ - @external␊ - func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, subtracted_value: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.decrease_allowance(spender, subtracted_value);␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc20::ERC20Component;␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl ERC20Impl = ERC20Component::ERC20Impl;␊ + #[abi(embed_v0)]␊ + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl;␊ + ␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState) {␊ + self.erc20.initializer('MyToken', 'MTK');␊ + }␊ }␊ ` @@ -108,105 +51,48 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - from starkware.starknet.common.syscalls import get_caller_address␊ - ␊ - from openzeppelin.token.erc20.library import ERC20␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - ERC20.initializer('MyToken', 'MTK', 18);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC20.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC20.symbol();␊ - }␊ - ␊ - @view␊ - func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (totalSupply: Uint256) {␊ - let (totalSupply) = ERC20.total_supply();␊ - return (totalSupply=totalSupply);␊ - }␊ - ␊ - @view␊ - func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (decimals: felt) {␊ - return ERC20.decimals();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt␊ - ) -> (balance: Uint256) {␊ - return ERC20.balance_of(account);␊ - }␊ - ␊ - @view␊ - func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, spender: felt␊ - ) -> (remaining: Uint256) {␊ - return ERC20.allowance(owner, spender);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.transfer(recipient, amount);␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - sender: felt, recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.transfer_from(sender, recipient, amount);␊ - }␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.approve(spender, amount);␊ - }␊ - ␊ - @external␊ - func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, added_value: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.increase_allowance(spender, added_value);␊ - }␊ - ␊ - @external␊ - func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, subtracted_value: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.decrease_allowance(spender, subtracted_value);␊ - }␊ - ␊ - @external␊ - func burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - amount: Uint256␊ - ) {␊ - let (owner) = get_caller_address();␊ - ERC20._burn(owner, amount);␊ - return ();␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc20::ERC20Component;␊ + use starknet::get_caller_address;␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl ERC20Impl = ERC20Component::ERC20Impl;␊ + #[abi(embed_v0)]␊ + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl;␊ + ␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState) {␊ + self.erc20.initializer('MyToken', 'MTK');␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn burn(ref self: ContractState, value: u256) {␊ + let caller = get_caller_address();␊ + self.erc20._burn(caller, value);␊ + }␊ + }␊ }␊ ` @@ -216,143 +102,127 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc20.library import ERC20␊ - from openzeppelin.security.pausable.library import Pausable␊ - from openzeppelin.access.ownable.library import Ownable␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) {␊ - ERC20.initializer('MyToken', 'MTK', 18);␊ - Ownable.initializer(owner);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC20.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC20.symbol();␊ - }␊ - ␊ - @view␊ - func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (totalSupply: Uint256) {␊ - let (totalSupply) = ERC20.total_supply();␊ - return (totalSupply=totalSupply);␊ - }␊ - ␊ - @view␊ - func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (decimals: felt) {␊ - return ERC20.decimals();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt␊ - ) -> (balance: Uint256) {␊ - return ERC20.balance_of(account);␊ - }␊ - ␊ - @view␊ - func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, spender: felt␊ - ) -> (remaining: Uint256) {␊ - return ERC20.allowance(owner, spender);␊ - }␊ - ␊ - @view␊ - func paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (paused: felt) {␊ - return Pausable.is_paused();␊ - }␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.transfer(recipient, amount);␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - sender: felt, recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.transfer_from(sender, recipient, amount);␊ - }␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.approve(spender, amount);␊ - }␊ - ␊ - @external␊ - func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, added_value: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.increase_allowance(spender, added_value);␊ - }␊ - ␊ - @external␊ - func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, subtracted_value: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.decrease_allowance(spender, subtracted_value);␊ - }␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ - }␊ - ␊ - @external␊ - func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._pause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._unpause();␊ - return ();␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc20::ERC20Component;␊ + use openzeppelin::token::erc20::interface;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use starknet::ContractAddress;␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableImpl = OwnableComponent::OwnableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableCamelOnlyImpl = OwnableComponent::OwnableCamelOnlyImpl;␊ + ␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.erc20.initializer('MyToken', 'MTK');␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC20Impl of interface::IERC20 {␊ + fn total_supply(self: @ContractState) -> u256 {␊ + self.erc20.total_supply()␊ + }␊ + ␊ + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.erc20.balance_of(account)␊ + }␊ + ␊ + fn allowance(self: @ContractState, owner: ContractAddress, spender: ContractAddress) -> u256 {␊ + self.erc20.allowance(owner, spender)␊ + }␊ + ␊ + fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.transfer(recipient, amount)␊ + }␊ + ␊ + fn transfer_from(␊ + ref self: ContractState,␊ + sender: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.transfer_from(sender, recipient, amount)␊ + }␊ + ␊ + fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.approve(spender, amount)␊ + }␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC20CamelOnlyImpl of interface::IERC20CamelOnly {␊ + fn totalSupply(self: @ContractState) -> u256 {␊ + self.total_supply()␊ + }␊ + ␊ + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.balance_of(account)␊ + }␊ + ␊ + fn transferFrom(␊ + ref self: ContractState,␊ + sender: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.transfer_from(sender, recipient, amount)␊ + }␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn pause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._pause();␊ + }␊ + ␊ + fn unpause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._unpause();␊ + }␊ + }␊ }␊ ` @@ -362,168 +232,142 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc20.library import ERC20␊ - from openzeppelin.security.pausable.library import Pausable␊ - from openzeppelin.access.accesscontrol.library import AccessControl␊ - from openzeppelin.utils.constants.library import DEFAULT_ADMIN_ROLE␊ - ␊ - const PAUSER_ROLE = 0x65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862; // keccak256('PAUSER_ROLE')[0:251 bits]␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - admin: felt␊ - ) {␊ - ERC20.initializer('MyToken', 'MTK', 18);␊ - AccessControl.initializer();␊ - ␊ - AccessControl._grant_role(DEFAULT_ADMIN_ROLE, admin);␊ - AccessControl._grant_role(PAUSER_ROLE, admin);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC20.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC20.symbol();␊ - }␊ - ␊ - @view␊ - func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (totalSupply: Uint256) {␊ - let (totalSupply) = ERC20.total_supply();␊ - return (totalSupply=totalSupply);␊ - }␊ - ␊ - @view␊ - func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (decimals: felt) {␊ - return ERC20.decimals();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt␊ - ) -> (balance: Uint256) {␊ - return ERC20.balance_of(account);␊ - }␊ - ␊ - @view␊ - func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, spender: felt␊ - ) -> (remaining: Uint256) {␊ - return ERC20.allowance(owner, spender);␊ - }␊ - ␊ - @view␊ - func paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (paused: felt) {␊ - return Pausable.is_paused();␊ - }␊ - ␊ - @view␊ - func hasRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) -> (has_role: felt) {␊ - return AccessControl.has_role(role, user);␊ - }␊ - ␊ - @view␊ - func getRoleAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt␊ - ) -> (admin: felt) {␊ - return AccessControl.get_role_admin(role);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.transfer(recipient, amount);␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - sender: felt, recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.transfer_from(sender, recipient, amount);␊ - }␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.approve(spender, amount);␊ - }␊ - ␊ - @external␊ - func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, added_value: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.increase_allowance(spender, added_value);␊ - }␊ - ␊ - @external␊ - func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, subtracted_value: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.decrease_allowance(spender, subtracted_value);␊ - }␊ - ␊ - @external␊ - func grantRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.grant_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func revokeRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.revoke_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.renounce_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - AccessControl.assert_only_role(PAUSER_ROLE);␊ - Pausable._pause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - AccessControl.assert_only_role(PAUSER_ROLE);␊ - Pausable._unpause();␊ - return ();␊ + const PAUSER_ROLE: felt252 = selector!("PAUSER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc20::ERC20Component;␊ + use openzeppelin::token::erc20::interface;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::access::accesscontrol::AccessControlComponent;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE;␊ + use starknet::ContractAddress;␊ + use super::{PAUSER_ROLE};␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl;␊ + #[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 SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + accesscontrol: AccessControlComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + AccessControlEvent: AccessControlComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, default_admin: ContractAddress, pauser: ContractAddress) {␊ + self.erc20.initializer('MyToken', 'MTK');␊ + self.accesscontrol.initializer();␊ + ␊ + self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ + self.accesscontrol._grant_role(PAUSER_ROLE, pauser);␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC20Impl of interface::IERC20 {␊ + fn total_supply(self: @ContractState) -> u256 {␊ + self.erc20.total_supply()␊ + }␊ + ␊ + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.erc20.balance_of(account)␊ + }␊ + ␊ + fn allowance(self: @ContractState, owner: ContractAddress, spender: ContractAddress) -> u256 {␊ + self.erc20.allowance(owner, spender)␊ + }␊ + ␊ + fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.transfer(recipient, amount)␊ + }␊ + ␊ + fn transfer_from(␊ + ref self: ContractState,␊ + sender: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.transfer_from(sender, recipient, amount)␊ + }␊ + ␊ + fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.approve(spender, amount)␊ + }␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC20CamelOnlyImpl of interface::IERC20CamelOnly {␊ + fn totalSupply(self: @ContractState) -> u256 {␊ + self.total_supply()␊ + }␊ + ␊ + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.balance_of(account)␊ + }␊ + ␊ + fn transferFrom(␊ + ref self: ContractState,␊ + sender: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.transfer_from(sender, recipient, amount)␊ + }␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn pause(ref self: ContractState) {␊ + self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ + self.pausable._pause();␊ + }␊ + ␊ + fn unpause(ref self: ContractState) {␊ + self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ + self.pausable._unpause();␊ + }␊ + }␊ }␊ ` @@ -533,154 +377,134 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - from starkware.starknet.common.syscalls import get_caller_address␊ - ␊ - from openzeppelin.token.erc20.library import ERC20␊ - from openzeppelin.security.pausable.library import Pausable␊ - from openzeppelin.access.ownable.library import Ownable␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) {␊ - ERC20.initializer('MyToken', 'MTK', 18);␊ - Ownable.initializer(owner);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC20.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC20.symbol();␊ - }␊ - ␊ - @view␊ - func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (totalSupply: Uint256) {␊ - let (totalSupply) = ERC20.total_supply();␊ - return (totalSupply=totalSupply);␊ - }␊ - ␊ - @view␊ - func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (decimals: felt) {␊ - return ERC20.decimals();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt␊ - ) -> (balance: Uint256) {␊ - return ERC20.balance_of(account);␊ - }␊ - ␊ - @view␊ - func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, spender: felt␊ - ) -> (remaining: Uint256) {␊ - return ERC20.allowance(owner, spender);␊ - }␊ - ␊ - @view␊ - func paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (paused: felt) {␊ - return Pausable.is_paused();␊ - }␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.transfer(recipient, amount);␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - sender: felt, recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.transfer_from(sender, recipient, amount);␊ - }␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.approve(spender, amount);␊ - }␊ - ␊ - @external␊ - func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, added_value: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.increase_allowance(spender, added_value);␊ - }␊ - ␊ - @external␊ - func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, subtracted_value: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.decrease_allowance(spender, subtracted_value);␊ - }␊ - ␊ - @external␊ - func burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - amount: Uint256␊ - ) {␊ - Pausable.assert_not_paused();␊ - let (owner) = get_caller_address();␊ - ERC20._burn(owner, amount);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ - }␊ - ␊ - @external␊ - func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._pause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._unpause();␊ - return ();␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc20::ERC20Component;␊ + use openzeppelin::token::erc20::interface;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use starknet::ContractAddress;␊ + use starknet::get_caller_address;␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableImpl = OwnableComponent::OwnableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableCamelOnlyImpl = OwnableComponent::OwnableCamelOnlyImpl;␊ + ␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.erc20.initializer('MyToken', 'MTK');␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC20Impl of interface::IERC20 {␊ + fn total_supply(self: @ContractState) -> u256 {␊ + self.erc20.total_supply()␊ + }␊ + ␊ + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.erc20.balance_of(account)␊ + }␊ + ␊ + fn allowance(self: @ContractState, owner: ContractAddress, spender: ContractAddress) -> u256 {␊ + self.erc20.allowance(owner, spender)␊ + }␊ + ␊ + fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.transfer(recipient, amount)␊ + }␊ + ␊ + fn transfer_from(␊ + ref self: ContractState,␊ + sender: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.transfer_from(sender, recipient, amount)␊ + }␊ + ␊ + fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.approve(spender, amount)␊ + }␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC20CamelOnlyImpl of interface::IERC20CamelOnly {␊ + fn totalSupply(self: @ContractState) -> u256 {␊ + self.total_supply()␊ + }␊ + ␊ + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.balance_of(account)␊ + }␊ + ␊ + fn transferFrom(␊ + ref self: ContractState,␊ + sender: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.transfer_from(sender, recipient, amount)␊ + }␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn pause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._pause();␊ + }␊ + ␊ + fn unpause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._unpause();␊ + }␊ + ␊ + fn burn(ref self: ContractState, value: u256) {␊ + self.pausable.assert_not_paused();␊ + let caller = get_caller_address();␊ + self.erc20._burn(caller, value);␊ + }␊ + }␊ }␊ ` @@ -690,99 +514,41 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc20.library import ERC20␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - recipient: felt␊ - ) {␊ - ERC20.initializer('MyToken', 'MTK', 18);␊ - ␊ - ERC20._mint(recipient, Uint256(1000000000000000000000, 0));␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC20.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC20.symbol();␊ - }␊ - ␊ - @view␊ - func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (totalSupply: Uint256) {␊ - let (totalSupply) = ERC20.total_supply();␊ - return (totalSupply=totalSupply);␊ - }␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc20::ERC20Component;␊ + use starknet::ContractAddress;␊ ␊ - @view␊ - func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (decimals: felt) {␊ - return ERC20.decimals();␊ - }␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt␊ - ) -> (balance: Uint256) {␊ - return ERC20.balance_of(account);␊ - }␊ + #[abi(embed_v0)]␊ + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl ERC20Impl = ERC20Component::ERC20Impl;␊ + #[abi(embed_v0)]␊ + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl;␊ ␊ - @view␊ - func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, spender: felt␊ - ) -> (remaining: Uint256) {␊ - return ERC20.allowance(owner, spender);␊ - }␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ ␊ - //␊ - // Externals␊ - //␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + }␊ ␊ - @external␊ - func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.transfer(recipient, amount);␊ - }␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + }␊ ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - sender: felt, recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.transfer_from(sender, recipient, amount);␊ - }␊ + #[constructor]␊ + fn constructor(ref self: ContractState, recipient: ContractAddress) {␊ + self.erc20.initializer('MyToken', 'MTK');␊ ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.approve(spender, amount);␊ - }␊ - ␊ - @external␊ - func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, added_value: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.increase_allowance(spender, added_value);␊ - }␊ - ␊ - @external␊ - func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, subtracted_value: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.decrease_allowance(spender, subtracted_value);␊ + self.erc20._mint(recipient, 1000000000000000000000);␊ + }␊ }␊ ` @@ -792,95 +558,38 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc20::ERC20Component;␊ ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ ␊ - from openzeppelin.token.erc20.library import ERC20␊ + #[abi(embed_v0)]␊ + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl ERC20Impl = ERC20Component::ERC20Impl;␊ + #[abi(embed_v0)]␊ + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl;␊ ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - ERC20.initializer('MyToken', 'MTK', 18);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC20.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC20.symbol();␊ - }␊ - ␊ - @view␊ - func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (totalSupply: Uint256) {␊ - let (totalSupply) = ERC20.total_supply();␊ - return (totalSupply=totalSupply);␊ - }␊ - ␊ - @view␊ - func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (decimals: felt) {␊ - return ERC20.decimals();␊ - }␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + }␊ ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt␊ - ) -> (balance: Uint256) {␊ - return ERC20.balance_of(account);␊ - }␊ - ␊ - @view␊ - func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, spender: felt␊ - ) -> (remaining: Uint256) {␊ - return ERC20.allowance(owner, spender);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.transfer(recipient, amount);␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - sender: felt, recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.transfer_from(sender, recipient, amount);␊ - }␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.approve(spender, amount);␊ - }␊ - ␊ - @external␊ - func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, added_value: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.increase_allowance(spender, added_value);␊ - }␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + }␊ ␊ - @external␊ - func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, subtracted_value: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.decrease_allowance(spender, subtracted_value);␊ + #[constructor]␊ + fn constructor(ref self: ContractState) {␊ + self.erc20.initializer('MyToken', 'MTK');␊ + }␊ }␊ ` @@ -890,127 +599,60 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc20.library import ERC20␊ - from openzeppelin.access.ownable.library import Ownable␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) {␊ - ERC20.initializer('MyToken', 'MTK', 18);␊ - Ownable.initializer(owner);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC20.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC20.symbol();␊ - }␊ - ␊ - @view␊ - func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (totalSupply: Uint256) {␊ - let (totalSupply) = ERC20.total_supply();␊ - return (totalSupply=totalSupply);␊ - }␊ - ␊ - @view␊ - func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (decimals: felt) {␊ - return ERC20.decimals();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt␊ - ) -> (balance: Uint256) {␊ - return ERC20.balance_of(account);␊ - }␊ - ␊ - @view␊ - func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, spender: felt␊ - ) -> (remaining: Uint256) {␊ - return ERC20.allowance(owner, spender);␊ - }␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.transfer(recipient, amount);␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - sender: felt, recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.transfer_from(sender, recipient, amount);␊ - }␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.approve(spender, amount);␊ - }␊ - ␊ - @external␊ - func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, added_value: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.increase_allowance(spender, added_value);␊ - }␊ - ␊ - @external␊ - func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, subtracted_value: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.decrease_allowance(spender, subtracted_value);␊ - }␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ - }␊ - ␊ - @external␊ - func mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, amount: Uint256␊ - ) {␊ - Ownable.assert_only_owner();␊ - ERC20._mint(to, amount);␊ - return ();␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc20::ERC20Component;␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use starknet::ContractAddress;␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl ERC20Impl = ERC20Component::ERC20Impl;␊ + #[abi(embed_v0)]␊ + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableImpl = OwnableComponent::OwnableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableCamelOnlyImpl = OwnableComponent::OwnableCamelOnlyImpl;␊ + ␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.erc20.initializer('MyToken', 'MTK');␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {␊ + self.ownable.assert_only_owner();␊ + self.erc20._mint(recipient, amount);␊ + }␊ + }␊ }␊ ` @@ -1020,152 +662,120 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc20.library import ERC20␊ - from openzeppelin.access.accesscontrol.library import AccessControl␊ - from openzeppelin.utils.constants.library import DEFAULT_ADMIN_ROLE␊ - ␊ - const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5; // keccak256('MINTER_ROLE')[0:251 bits]␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - admin: felt␊ - ) {␊ - ERC20.initializer('MyToken', 'MTK', 18);␊ - AccessControl.initializer();␊ - ␊ - AccessControl._grant_role(DEFAULT_ADMIN_ROLE, admin);␊ - AccessControl._grant_role(MINTER_ROLE, admin);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC20.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC20.symbol();␊ - }␊ - ␊ - @view␊ - func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (totalSupply: Uint256) {␊ - let (totalSupply) = ERC20.total_supply();␊ - return (totalSupply=totalSupply);␊ - }␊ - ␊ - @view␊ - func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (decimals: felt) {␊ - return ERC20.decimals();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt␊ - ) -> (balance: Uint256) {␊ - return ERC20.balance_of(account);␊ - }␊ - ␊ - @view␊ - func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, spender: felt␊ - ) -> (remaining: Uint256) {␊ - return ERC20.allowance(owner, spender);␊ - }␊ - ␊ - @view␊ - func hasRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) -> (has_role: felt) {␊ - return AccessControl.has_role(role, user);␊ - }␊ - ␊ - @view␊ - func getRoleAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt␊ - ) -> (admin: felt) {␊ - return AccessControl.get_role_admin(role);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.transfer(recipient, amount);␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - sender: felt, recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.transfer_from(sender, recipient, amount);␊ - }␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.approve(spender, amount);␊ - }␊ - ␊ - @external␊ - func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, added_value: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.increase_allowance(spender, added_value);␊ - }␊ - ␊ - @external␊ - func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, subtracted_value: Uint256␊ - ) -> (success: felt) {␊ - return ERC20.decrease_allowance(spender, subtracted_value);␊ - }␊ - ␊ - @external␊ - func grantRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.grant_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func revokeRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.revoke_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.renounce_role(role, user);␊ - return ();␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc20::ERC20Component;␊ + use openzeppelin::access::accesscontrol::AccessControlComponent;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE;␊ + use starknet::ContractAddress;␊ + use super::{MINTER_ROLE};␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl ERC20Impl = ERC20Component::ERC20Impl;␊ + #[abi(embed_v0)]␊ + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlComponent::AccessControlCamelImpl;␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + #[substorage(v0)]␊ + accesscontrol: AccessControlComponent::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + #[flat]␊ + AccessControlEvent: AccessControlComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, default_admin: ContractAddress, minter: ContractAddress) {␊ + self.erc20.initializer('MyToken', 'MTK');␊ + self.accesscontrol.initializer();␊ + ␊ + self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ + self.accesscontrol._grant_role(MINTER_ROLE, minter);␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {␊ + self.accesscontrol.assert_only_role(MINTER_ROLE);␊ + self.erc20._mint(recipient, amount);␊ + }␊ + }␊ }␊ + ` + +## erc20 safe allowance + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ ␊ - @external␊ - func mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, amount: Uint256␊ - ) {␊ - AccessControl.assert_only_role(MINTER_ROLE);␊ - ERC20._mint(to, amount);␊ - return ();␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc20::ERC20Component;␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl ERC20Impl = ERC20Component::ERC20Impl;␊ + #[abi(embed_v0)]␊ + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl;␊ + #[abi(embed_v0)]␊ + impl SafeAllowanceImpl = ERC20Component::SafeAllowanceImpl;␊ + #[abi(embed_v0)]␊ + impl SafeAllowanceCamelImpl = ERC20Component::SafeAllowanceCamelImpl;␊ + ␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState) {␊ + self.erc20.initializer('MyToken', 'MTK');␊ + }␊ }␊ ` @@ -1175,176 +785,185 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - from starkware.starknet.common.syscalls import get_caller_address␊ - ␊ - from openzeppelin.token.erc20.library import ERC20␊ - from openzeppelin.security.pausable.library import Pausable␊ - from openzeppelin.access.ownable.library import Ownable␊ - from openzeppelin.upgrades.library import Proxy␊ - ␊ - @external␊ - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, recipient: felt, proxy_admin: felt␊ - ) {␊ - ERC20.initializer('MyToken', 'MTK', 9);␊ - Ownable.initializer(owner);␊ - Proxy.initializer(proxy_admin);␊ - ␊ - ERC20._mint(recipient, Uint256(2000000000000, 0));␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC20.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC20.symbol();␊ - }␊ - ␊ - @view␊ - func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (totalSupply: Uint256) {␊ - let (totalSupply) = ERC20.total_supply();␊ - return (totalSupply=totalSupply);␊ - }␊ - ␊ - @view␊ - func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (decimals: felt) {␊ - return ERC20.decimals();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt␊ - ) -> (balance: Uint256) {␊ - return ERC20.balance_of(account);␊ - }␊ - ␊ - @view␊ - func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, spender: felt␊ - ) -> (remaining: Uint256) {␊ - return ERC20.allowance(owner, spender);␊ - }␊ - ␊ - @view␊ - func paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (paused: felt) {␊ - return Pausable.is_paused();␊ - }␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.transfer(recipient, amount);␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - sender: felt, recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.transfer_from(sender, recipient, amount);␊ - }␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.approve(spender, amount);␊ - }␊ - ␊ - @external␊ - func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, added_value: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.increase_allowance(spender, added_value);␊ - }␊ - ␊ - @external␊ - func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, subtracted_value: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.decrease_allowance(spender, subtracted_value);␊ - }␊ - ␊ - @external␊ - func burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - amount: Uint256␊ - ) {␊ - Pausable.assert_not_paused();␊ - let (owner) = get_caller_address();␊ - ERC20._burn(owner, amount);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ - }␊ - ␊ - @external␊ - func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._pause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._unpause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, amount: Uint256␊ - ) {␊ - Ownable.assert_only_owner();␊ - ERC20._mint(to, amount);␊ - return ();␊ - }␊ - ␊ - @external␊ - func upgrade{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - new_implementation: felt␊ - ) -> () {␊ - Proxy.assert_only_admin();␊ - Proxy._set_implementation_hash(new_implementation);␊ - return ();␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc20::ERC20Component;␊ + use openzeppelin::token::erc20::interface;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use starknet::ContractAddress;␊ + use starknet::get_caller_address;␊ + use starknet::ClassHash;␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableImpl = OwnableComponent::OwnableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableCamelOnlyImpl = OwnableComponent::OwnableCamelOnlyImpl;␊ + ␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::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)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC20Event: ERC20Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::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);␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC20Impl of interface::IERC20 {␊ + fn total_supply(self: @ContractState) -> u256 {␊ + self.erc20.total_supply()␊ + }␊ + ␊ + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.erc20.balance_of(account)␊ + }␊ + ␊ + fn allowance(self: @ContractState, owner: ContractAddress, spender: ContractAddress) -> u256 {␊ + self.erc20.allowance(owner, spender)␊ + }␊ + ␊ + fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.transfer(recipient, amount)␊ + }␊ + ␊ + fn transfer_from(␊ + ref self: ContractState,␊ + sender: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.transfer_from(sender, recipient, amount)␊ + }␊ + ␊ + fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.approve(spender, amount)␊ + }␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC20CamelOnlyImpl of interface::IERC20CamelOnly {␊ + fn totalSupply(self: @ContractState) -> u256 {␊ + self.total_supply()␊ + }␊ + ␊ + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.balance_of(account)␊ + }␊ + ␊ + fn transferFrom(␊ + ref self: ContractState,␊ + sender: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.transfer_from(sender, recipient, amount)␊ + }␊ + }␊ + ␊ + #[external(v0)]␊ + impl SafeAllowanceImpl of interface::ISafeAllowance {␊ + fn increase_allowance(ref self: ContractState, spender: ContractAddress, added_value: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.increase_allowance(spender, added_value)␊ + }␊ + ␊ + fn decrease_allowance(ref self: ContractState, spender: ContractAddress, subtracted_value: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.decrease_allowance(spender, subtracted_value)␊ + }␊ + }␊ + ␊ + #[external(v0)]␊ + impl SafeAllowanceCamelImpl of interface::ISafeAllowanceCamel {␊ + fn increaseAllowance(ref self: ContractState, spender: ContractAddress, addedValue: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.increase_allowance(spender, addedValue)␊ + }␊ + ␊ + fn decreaseAllowance(ref self: ContractState, spender: ContractAddress, subtractedValue: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.decrease_allowance(spender, subtractedValue)␊ + }␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn pause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._pause();␊ + }␊ + ␊ + fn unpause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._unpause();␊ + }␊ + ␊ + fn burn(ref self: ContractState, value: u256) {␊ + self.pausable.assert_not_paused();␊ + let caller = get_caller_address();␊ + self.erc20._burn(caller, value);␊ + }␊ + ␊ + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {␊ + self.ownable.assert_only_owner();␊ + self.pausable.assert_not_paused();␊ + self.erc20._mint(recipient, amount);␊ + }␊ + }␊ + ␊ + #[external(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);␊ + }␊ + }␊ }␊ ` @@ -1354,201 +973,209 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - from starkware.starknet.common.syscalls import get_caller_address␊ - ␊ - from openzeppelin.token.erc20.library import ERC20␊ - from openzeppelin.security.pausable.library import Pausable␊ - from openzeppelin.access.accesscontrol.library import AccessControl␊ - from openzeppelin.utils.constants.library import DEFAULT_ADMIN_ROLE␊ - from openzeppelin.upgrades.library import Proxy␊ - ␊ - const PAUSER_ROLE = 0x65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862; // keccak256('PAUSER_ROLE')[0:251 bits]␊ - const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5; // keccak256('MINTER_ROLE')[0:251 bits]␊ - ␊ - @external␊ - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - admin: felt, recipient: felt, proxy_admin: felt␊ - ) {␊ - ERC20.initializer('MyToken', 'MTK', 9);␊ - AccessControl.initializer();␊ - Proxy.initializer(proxy_admin);␊ - ␊ - AccessControl._grant_role(DEFAULT_ADMIN_ROLE, admin);␊ - AccessControl._grant_role(PAUSER_ROLE, admin);␊ - ERC20._mint(recipient, Uint256(2000000000000, 0));␊ - AccessControl._grant_role(MINTER_ROLE, admin);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC20.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC20.symbol();␊ - }␊ - ␊ - @view␊ - func totalSupply{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (totalSupply: Uint256) {␊ - let (totalSupply) = ERC20.total_supply();␊ - return (totalSupply=totalSupply);␊ - }␊ - ␊ - @view␊ - func decimals{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (decimals: felt) {␊ - return ERC20.decimals();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - account: felt␊ - ) -> (balance: Uint256) {␊ - return ERC20.balance_of(account);␊ - }␊ - ␊ - @view␊ - func allowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, spender: felt␊ - ) -> (remaining: Uint256) {␊ - return ERC20.allowance(owner, spender);␊ - }␊ - ␊ - @view␊ - func paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (paused: felt) {␊ - return Pausable.is_paused();␊ - }␊ - ␊ - @view␊ - func hasRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) -> (has_role: felt) {␊ - return AccessControl.has_role(role, user);␊ - }␊ - ␊ - @view␊ - func getRoleAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt␊ - ) -> (admin: felt) {␊ - return AccessControl.get_role_admin(role);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func transfer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.transfer(recipient, amount);␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - sender: felt, recipient: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.transfer_from(sender, recipient, amount);␊ - }␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, amount: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.approve(spender, amount);␊ - }␊ - ␊ - @external␊ - func increaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, added_value: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.increase_allowance(spender, added_value);␊ - }␊ - ␊ - @external␊ - func decreaseAllowance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - spender: felt, subtracted_value: Uint256␊ - ) -> (success: felt) {␊ - Pausable.assert_not_paused();␊ - return ERC20.decrease_allowance(spender, subtracted_value);␊ - }␊ - ␊ - @external␊ - func burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - amount: Uint256␊ - ) {␊ - Pausable.assert_not_paused();␊ - let (owner) = get_caller_address();␊ - ERC20._burn(owner, amount);␊ - return ();␊ - }␊ - ␊ - @external␊ - func grantRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.grant_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func revokeRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.revoke_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.renounce_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - AccessControl.assert_only_role(PAUSER_ROLE);␊ - Pausable._pause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - AccessControl.assert_only_role(PAUSER_ROLE);␊ - Pausable._unpause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func mint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, amount: Uint256␊ - ) {␊ - AccessControl.assert_only_role(MINTER_ROLE);␊ - ERC20._mint(to, amount);␊ - return ();␊ - }␊ - ␊ - @external␊ - func upgrade{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - new_implementation: felt␊ - ) -> () {␊ - Proxy.assert_only_admin();␊ - Proxy._set_implementation_hash(new_implementation);␊ - return ();␊ + 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::token::erc20::ERC20Component;␊ + use openzeppelin::token::erc20::interface;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::access::accesscontrol::AccessControlComponent;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use starknet::ContractAddress;␊ + use starknet::get_caller_address;␊ + use starknet::ClassHash;␊ + use super::{PAUSER_ROLE, MINTER_ROLE, UPGRADER_ROLE};␊ + ␊ + component!(path: ERC20Component, storage: erc20, event: ERC20Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl;␊ + #[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 SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + impl ERC20InternalImpl = ERC20Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc20: ERC20Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + accesscontrol: AccessControlComponent::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]␊ + AccessControlEvent: AccessControlComponent::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(␊ + ref self: ContractState,␊ + recipient: ContractAddress,␊ + default_admin: ContractAddress,␊ + pauser: ContractAddress,␊ + minter: ContractAddress,␊ + upgrader: ContractAddress,␊ + ) {␊ + self.erc20.initializer('MyToken', 'MTK');␊ + 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);␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC20Impl of interface::IERC20 {␊ + fn total_supply(self: @ContractState) -> u256 {␊ + self.erc20.total_supply()␊ + }␊ + ␊ + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.erc20.balance_of(account)␊ + }␊ + ␊ + fn allowance(self: @ContractState, owner: ContractAddress, spender: ContractAddress) -> u256 {␊ + self.erc20.allowance(owner, spender)␊ + }␊ + ␊ + fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.transfer(recipient, amount)␊ + }␊ + ␊ + fn transfer_from(␊ + ref self: ContractState,␊ + sender: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.transfer_from(sender, recipient, amount)␊ + }␊ + ␊ + fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.approve(spender, amount)␊ + }␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC20CamelOnlyImpl of interface::IERC20CamelOnly {␊ + fn totalSupply(self: @ContractState) -> u256 {␊ + self.total_supply()␊ + }␊ + ␊ + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.balance_of(account)␊ + }␊ + ␊ + fn transferFrom(␊ + ref self: ContractState,␊ + sender: ContractAddress,␊ + recipient: ContractAddress,␊ + amount: u256,␊ + ) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.transfer_from(sender, recipient, amount)␊ + }␊ + }␊ + ␊ + #[external(v0)]␊ + impl SafeAllowanceImpl of interface::ISafeAllowance {␊ + fn increase_allowance(ref self: ContractState, spender: ContractAddress, added_value: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.increase_allowance(spender, added_value)␊ + }␊ + ␊ + fn decrease_allowance(ref self: ContractState, spender: ContractAddress, subtracted_value: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.erc20.decrease_allowance(spender, subtracted_value)␊ + }␊ + }␊ + ␊ + #[external(v0)]␊ + impl SafeAllowanceCamelImpl of interface::ISafeAllowanceCamel {␊ + fn increaseAllowance(ref self: ContractState, spender: ContractAddress, addedValue: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.increase_allowance(spender, addedValue)␊ + }␊ + ␊ + fn decreaseAllowance(ref self: ContractState, spender: ContractAddress, subtractedValue: u256) -> bool {␊ + self.pausable.assert_not_paused();␊ + self.decrease_allowance(spender, subtractedValue)␊ + }␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn pause(ref self: ContractState) {␊ + self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ + self.pausable._pause();␊ + }␊ + ␊ + fn unpause(ref self: ContractState) {␊ + self.accesscontrol.assert_only_role(PAUSER_ROLE);␊ + self.pausable._unpause();␊ + }␊ + ␊ + fn burn(ref self: ContractState, value: u256) {␊ + self.pausable.assert_not_paused();␊ + let caller = get_caller_address();␊ + self.erc20._burn(caller, value);␊ + }␊ + ␊ + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {␊ + self.accesscontrol.assert_only_role(MINTER_ROLE);␊ + self.pausable.assert_not_paused();␊ + self.erc20._mint(recipient, amount);␊ + }␊ + }␊ + ␊ + #[external(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);␊ + }␊ + }␊ }␊ ` diff --git a/packages/core-cairo/src/erc20.test.ts.snap b/packages/core-cairo/src/erc20.test.ts.snap index 1954a8db5f8d9cce8169091c0b3ebae7ed38af08..becbd01c4f07977fd6b56be5ca21e26870bb9066 100644 GIT binary patch literal 2408 zcmV-u377UkRzV-4?)BN^3K2IHR5+5v{4E! z*_tT1kspf)00000000B+9m{UpMlfws1kfdEdue(InB-s?P!iXGi-Ku`3bvCPjbp=) zphXc7D{^HMrbvQZ`jOO!o_gsY^pqa^2mOQoML(cGZ@u)?U2;k7l1qxB9;T$#C6=^1 zubr74&O>?E?O2X};{Wm+L?*J}NeB3*u0T&;T`_$dxc;$&{GZ+#20-BZ>%U*WcJmr} zz4yVj58waw`s2sS{_e(i4_}x%w0-z+%YZgAyC(EhWqa$O(RlEKkAQb#L!_#@VFdrcRhV^!f-wG~)}_ovtju4nH(| zt`)7_h6oS=Y>`jCN}-{|v1`6ur>yQHfZ)?7WpEV1t_pJ<^q{q4TNg#ZxkVW)X)0|5 zJsVg#0NJm_pol_*hZ521BQMaAvLAg$m4U7cI&^6>VU4^ZjmV&;AX?$N%NSXB;;-o& z1A68uY;JgtOLQaF%SaQVMbHjRR`lHY-$A{h?zfTqd%5cNLig`}J#fu9ernzJQQb{E6=+YWp z8Zn0yd`3F`U}1W~`6T(p(*FhXez0KmEwkr>7pNwkQ#$vu|x+0`SAWCuM*Zcm6T9g5@y z9ZBqqBaY;j!`2Xfk+K6m2tf`4mgWbpYh5%WuD?zNP!FGmQAjEXVOZs0EHmK*7wrHG z*gDjlt`JR2!S|;VkWp@%fvK^plC8U^tqLmMR_ z#yE%@t?N0xCI>fI~xdP$VvBK$pGdVFuOh+s*$2Lf2z;!(bd*)mP$kWD2 zAAd|3#V_K6;ilMK@xhD8ZzU@RJFxwV{%aV|wKG=7Ro-RA%Jgg@45)(Xa4I*chxL%; zGD(JnllW4nd^=c?weSWI@|}n&5>AtQEN2M`?CFsb<0{G#oZsP-1j?C-jup?bpr6eb ze0+n;7ySFtzye*2Ldt4GVI26WIBW(PhZ2&414QqRnc#3K5HMoIMCV~+gmTgV)WM)ie#%AcjUS`G=nHiIrvFeI8KbbN9qGyv*>DzwlnX++_Rwi?H#`hBz zwAtMLhfACNbz8RCjM!$AT{77QNg*KFj3b+IW-)amn{i|_jtGEk#*rxpnR1XRhq^p5 z$&`a4Qw}oaP+igHC*?4&%{ZKCRGlXpQZ=SFe&o>3A`kY6KQaUpmrGn?-SZPAE7V8hqW1sh_!?#9>4``AHl(H#1ieiyX?C zQg}4KLo{;uWv*ve!i4@p%&su^Xw3E;gqZ{SWErN%8g`;6>hwVu_Pa*}WuYIrfU|dX zWg(fttwlhxj}@jJ>q`!j%$iYfuz^S+qWl14jPb%0Z}nQ-p|!|KpGLl_8(5YSxcEje zpg1>u=V#7ao!_ZhZn~!3{rpBMVZ8OQFY6(8u?$(`iv_~U~p)gKB>@eyWiioyObI?Mx zJ?%O3q&cO&pew5Mh+t zVP?-*Zfet?SIOs=ud5at1)I~NcEP5`3*9Kq)#MA&hG&{i^55Dq@Q?A=aV~reQezTD zSam5SJi@}>#(Uz*O<|AY-DR$b^Xc75{jzz**2rkv;qLRj_D1nWZjJmc^+|ScRx4Un z==-c`)hqcj`|Iy5g=?JQlv4gO{5U#Xg-~Q2PWHFfsK2%LKRPcAmSh324&A)P`(-gP zen0KzV$sG)RwNT71M{+F(yap;n>ouV%qd9n{{(l6N*qq;_ zU1BpAo3@g3lSN`#B$h?u8LDx~BC#Ti#Ii_SUD0HbSQd$8kysXqWsz9^pC|&-WD&U@ a)3>syTuO@EWBoYG%>M(kps=_plmGyf4U>of literal 2136 zcmV-e2&eZ!RzV1lRWoF9t_00000000B+U0rY6R2Uv0gl0{fwu?!-GZ&~X%F@(H+;-E@%GSZeN~^Au zKnPjK$H%Qz$F_Wq+h)@)xaL<3#2pe6;szlhF1g!3;E!;_v5%eki!4purb*stT4m>a zJn#AP^SpWU=9OhQ9zLMo|3H|{9CBbFYGauMSZ&$VMLym482#>*X(EQczWm#Jmp-@@ zz215M()D+Ly1cX`Kiz)#)$J!XCN3qnADhHwwq=vxj=cGJSCT$*kh>>ShJpi^FiElk zuO}{#P(Yd(*@4%@Ua#l5;zBn<+hMj#WxMBl0h2#RbpKOvZ)ok%cA47M(rVAdWzwVP z6Zbjsed5?|lX(ZkZN?@x9oq;{&`)b^>^x9gl5`(?E@eT8nHNY_=wdl@;|1+gj2uVz z8Mo3R4*R$v`@|#xC9b%c!OY(a_z(~s?-P6wz5P;IlwU|Pf5p8uZP#Wta_r|MP!=}( zyAisDhP<%3`vw2Ayt;TdE*uaR2Cl5|%a@Y0w8a0(pAp7*x6ze!|Ij8!0<4RAqz;s~ zKarKFEm1#3od6Nwqqh@C83453Gd!m*2%@ziAVh&O4CXQ9JPm!{>DPrN)8w6Wjis0z z!g2)`<$DRh_@++dyGmg`GZpS-D;JG13ATI4p>Ff-q>U0*j?O!Z3e|+wJ90@`!-2up&_H5n3MDhjO^Qnyw3xP)%uTf!kN81dlPNSaDUJFK)1zmKB09-5Kh#R(`+;{@*BZ<%yYUT5 zKq*OcLk+N991ujwdd=IZXo_hPQ$IvbNKS4~icx>Fx89icrr}Ea_+fV*_R-Mb%Luda zjZA@78Ch&Swltx3Zp?UOgSUhXhm_f!^kS8dMh*AzP-j|yjA{BbE0hzOJOdi$gci@L$~oaM zoLUE^n`J9H80o(4*EAL`IohhAu<1EG*rQ(BuaefN)J2487a4@3 zAdBa!el*Ey@LZ>_51wJU^&(cuNEkzRawwC&D{&^;_V`v_+{#Bb+m{2+A+)S>ufNXe z+`q1a&K+6jia%VtGCsJ)>g(~F55$`lZG_BrXfqbR$Yr!x{o%%=^=D6Z_4S9FkH6G+ zww`QAQe0TxUVrvzXS_T_tT*>l*D9q;*z0YwDWSZfjk2*=V;_ysWJ* z8*A;hrC}p_V@1i|@d6bk^gRxQ>CyI3%7fkzfGXS0*367rz6!Vrz)b*d0&o*d*iEpH=ng-p z>eAu7sdOlLfBA9bUFmNM6tLz`(6p!${U@3#A>Sh`LcSiIskOsTAZH#rL-9tRK}41H zxI&B?Tp{2J0apmPLckRw$8`8So-!{g$asc}=N={_xIzxOl9&{1(ffF~g@0vo$P@q0 z7}#repX7mMohU_Zc9EPur$9Y|6X+-($U7v`;o3hr576st7vBRknTTG9m4H|YaDspn z1e_pK>I8vU32+dAg8&=^;2@ZcgWw!u#@}wznM9RCR0u?cT=1w6F9{0a43F;_Vpe>$ zSIRj8{)NOXwXv_0%j)bI6irx`TP4;j%dM(cLvAgC?grfrx_jPq_jHNOnU#<;ClF^% z{>_EBt0f&wkRy$@zCp&2o*{hbp$zfZyUA!-#olgVnquXnRqEjrekX05JR+M3oj47Hwa7FNSuIA+YLOW=$Z7$45%ePH zMMxx334cBlNq{B>O$?fNN;EN~XE-_R`ApAnu4?}+X%I3JWL))sD8#=NI*uIrd?lKs z`lHSpcXP_WapU4E|M|&;P~mG3b6IjSYzN+Pzv4kQ(9CDSy-RGvk5K623jehcKe&x; z&AprHcf_xesPOF(5cweTLF7Z$hN=F98nQM()&|JhF!Npk$l3r|8>S;_0J1iWpS59D zA}~thEkt{LJ4@%P-yuEz*hb9u+#D|+oG-CT&RQcrcgcxpo$^y9-;R?hJx-2}(e?EZ|)i5*!J{@Is_$LE~2A1~vhJ2OAt(D;1V`iaIT|6f2|}B|nF`KSaHfJY6`ZN3b*4hN8HAg`uK`W^*MMd = { name: 'MyToken', @@ -18,8 +18,8 @@ export const defaults: Required = { burnable: false, pausable: false, premint: '0', - decimals: '18', mintable: false, + safeAllowance: false, access: commonDefaults.access, upgradeable: commonDefaults.upgradeable, info: commonDefaults.info @@ -36,19 +36,7 @@ export interface ERC20Options extends CommonOptions { pausable?: boolean; premint?: string; mintable?: boolean; - decimals?: string; -} - -function checkDecimals(decimals: string) { - if (!/^\d+$/.test(decimals)) { - throw new OptionsError({ - decimals: 'Not a valid number', - }); - } else if (parseInt(decimals) >= 256) { // 8 bits - throw new OptionsError({ - decimals: 'Number too large', - }); - } + safeAllowance?: boolean; } function withDefaults(opts: ERC20Options): Required { @@ -59,116 +47,168 @@ function withDefaults(opts: ERC20Options): Required { pausable: opts.pausable ?? defaults.pausable, premint: opts.premint || defaults.premint, mintable: opts.mintable ?? defaults.mintable, - decimals: opts.decimals || defaults.decimals, + safeAllowance: opts.safeAllowance ?? defaults.safeAllowance, }; } export function isAccessControlRequired(opts: Partial): boolean { - return opts.mintable === true || opts.pausable === true; + return opts.mintable === true || opts.pausable === true || opts.upgradeable === true; } export function buildERC20(opts: ERC20Options): Contract { - const c = new ContractBuilder(); + const c = new ContractBuilder(opts.name); const allOpts = withDefaults(opts); - checkDecimals(allOpts.decimals); - - addBase(c, allOpts.name, allOpts.symbol, allOpts.decimals); + addBase(c, toShortString(allOpts.name, 'name'), toShortString(allOpts.symbol, 'symbol')); + addERC20ImplAndCamelOnlyImpl(c, allOpts.pausable); - c.addFunction(functions.name); - c.addFunction(functions.symbol); - c.addFunction(functions.totalSupply); - c.addFunction(functions.decimals); - c.addFunction(functions.balanceOf); - c.addFunction(functions.allowance); - - c.addFunction(functions.transfer); - c.addFunction(functions.transferFrom); - c.addFunction(functions.approve); - c.addFunction(functions.increaseAllowance); - c.addFunction(functions.decreaseAllowance); - - importUint256(c); + if (allOpts.safeAllowance) { + addSafeAllowance(c, allOpts.pausable); + } - if (allOpts.burnable) { - addBurnable(c); + if (allOpts.premint) { + addPremint(c, allOpts.premint); } if (allOpts.pausable) { - addPausable(c, allOpts.access, [functions.transfer, functions.transferFrom, functions.approve, functions.increaseAllowance, functions.decreaseAllowance]); - if (allOpts.burnable) { - setPausable(c, functions.burn); - } + addPausable(c, allOpts.access); } - if (allOpts.premint) { - addPremint(c, allOpts.premint, allOpts.decimals); + if (allOpts.burnable) { + addBurnable(c); + if (allOpts.pausable) { + setPausable(c, externalTrait, functions.burn); + } } if (allOpts.mintable) { addMintable(c, allOpts.access); + if (allOpts.pausable) { + setPausable(c, externalTrait, functions.mint); + } } setAccessControl(c, allOpts.access); - setUpgradeable(c, allOpts.upgradeable); - + setUpgradeable(c, allOpts.upgradeable, allOpts.access); setInfo(c, allOpts.info); return c; } -function addBase(c: ContractBuilder, name: string, symbol: string, decimals: string) { - c.addModule( - modules.ERC20, - [ - name, symbol, { lit: decimals } - ], +function addERC20Interface(c: ContractBuilder) { + c.addStandaloneImport('openzeppelin::token::erc20::interface'); +} + +function addERC20ImplAndCamelOnlyImpl(c: ContractBuilder, pausable: boolean) { + if (pausable) { + addERC20Interface(c); + + const ERC20Impl: BaseImplementedTrait = { + name: 'ERC20Impl', + of: 'interface::IERC20', + tags: [ + '#[external(v0)]' + ], + } + c.addFunction(ERC20Impl, functions.total_supply); + c.addFunction(ERC20Impl, functions.balance_of); + c.addFunction(ERC20Impl, functions.allowance); + setPausable(c, ERC20Impl, functions.transfer); + setPausable(c, ERC20Impl, functions.transfer_from); + setPausable(c, ERC20Impl, functions.approve); + + const ERC20CamelOnlyImpl: BaseImplementedTrait = { + name: 'ERC20CamelOnlyImpl', + of: 'interface::IERC20CamelOnly', + tags: [ + '#[external(v0)]' + ], + } + c.addFunction(ERC20CamelOnlyImpl, functions.totalSupply); + c.addFunction(ERC20CamelOnlyImpl, functions.balanceOf); + setPausable(c, ERC20CamelOnlyImpl, functions.transferFrom); + } else { + c.addImplToComponent(components.ERC20Component, { + name: 'ERC20Impl', + value: 'ERC20Component::ERC20Impl', + }); + c.addImplToComponent(components.ERC20Component, { + name: 'ERC20CamelOnlyImpl', + value: 'ERC20Component::ERC20CamelOnlyImpl', + }); + } +} + +function addBase(c: ContractBuilder, name: string, symbol: string) { + c.addComponent( + components.ERC20Component, [ - functions.transfer, functions.transferFrom, functions.approve, functions.increaseAllowance, functions.decreaseAllowance + name, symbol ], true, ); } +function addSafeAllowance(c: ContractBuilder, pausable: boolean) { + if (pausable) { + addERC20Interface(c); + + const SafeAllowanceImpl: BaseImplementedTrait = { + name: 'SafeAllowanceImpl', + of: 'interface::ISafeAllowance', + tags: [ + '#[external(v0)]' + ], + } + setPausable(c, SafeAllowanceImpl, functions.increase_allowance); + setPausable(c, SafeAllowanceImpl, functions.decrease_allowance); + + const SafeAllowanceCamelImpl: BaseImplementedTrait = { + name: 'SafeAllowanceCamelImpl', + of: 'interface::ISafeAllowanceCamel', + tags: [ + '#[external(v0)]' + ], + } + setPausable(c, SafeAllowanceCamelImpl, functions.increaseAllowance); + setPausable(c, SafeAllowanceCamelImpl, functions.decreaseAllowance); + } else { + c.addImplToComponent(components.ERC20Component, + { + name: 'SafeAllowanceImpl', + value: 'ERC20Component::SafeAllowanceImpl', + }, + ); + c.addImplToComponent(components.ERC20Component, + { + name: 'SafeAllowanceCamelImpl', + value: 'ERC20Component::SafeAllowanceCamelImpl', + }, + ); + } +} + function addBurnable(c: ContractBuilder) { - importGetCallerAddress(c); - c.addFunction(functions.burn); - c.setFunctionBody( - [ - 'let (owner) = get_caller_address()', - 'ERC20._burn(owner, amount)' - ], - functions.burn - ); + c.addStandaloneImport('starknet::get_caller_address'); + c.addFunction(externalTrait, functions.burn); } export const premintPattern = /^(\d*\.?\d*)$/; -function addPremint(c: ContractBuilder, amount: string, decimals: string) { +function addPremint(c: ContractBuilder, amount: string) { if (amount !== undefined && amount !== '0') { - if (!premintPattern.test(amount)) { throw new OptionsError({ premint: 'Not a valid number', }); - } - - const premintAbsolute = getInitialSupply(amount, parseInt(decimals)); + } - try { - const premintUint256 = toUint256(premintAbsolute); + const premintAbsolute = getInitialSupply(amount, 18); - c.addConstructorArgument({ name:'recipient', type:'felt' }); - c.addConstructorCode(`ERC20._mint(recipient, Uint256(${premintUint256.lowBits}, ${premintUint256.highBits}))`); - } catch (e: any) { - if (e instanceof NumberTooLarge) { - throw new OptionsError({ - premint: 'Premint argument too large', - decimals: 'Premint argument too large', - }); - } - } + c.addStandaloneImport('starknet::ContractAddress'); + c.addConstructorArgument({ name:'recipient', type:'ContractAddress' }); + c.addConstructorCode(`self.erc20._mint(recipient, ${premintAbsolute})`); } } @@ -194,9 +234,9 @@ export function getInitialSupply(premint: string, decimals: number): string { try { lastSegment += "0".repeat(decimals - lastSegment.length); } catch (e) { - // .repeat gives an error if number is too large, although this should not happen since decimals is limited to 256 + // .repeat gives an error if decimals number is too large throw new OptionsError({ - decimals: 'Number too large', + premint: 'Decimals number too large', }); } } else if (decimals < lastSegment.length) { @@ -214,175 +254,200 @@ export function getInitialSupply(premint: string, decimals: number): string { } function addMintable(c: ContractBuilder, access: Access) { - requireAccessControl(c, functions.mint, access, 'MINTER'); + c.addStandaloneImport('starknet::ContractAddress'); + requireAccessControl(c, externalTrait, functions.mint, access, 'MINTER', 'minter'); } -const modules = defineModules( { - ERC20: { - path: 'openzeppelin.token.erc20.library', - useNamespace: true +const components = defineComponents( { + ERC20Component: { + path: 'openzeppelin::token::erc20', + substorage: { + name: 'erc20', + type: 'ERC20Component::Storage', + }, + event: { + name: 'ERC20Event', + type: 'ERC20Component::Event', + }, + impls: [ + { + name: 'ERC20MetadataImpl', + value: 'ERC20Component::ERC20MetadataImpl', + }, + ], + internalImpl: { + name: 'ERC20InternalImpl', + value: 'ERC20Component::InternalImpl', + }, }, - - bool: { - path: 'starkware.cairo.common.bool', - useNamespace: false - } -}) +}); const functions = defineFunctions({ - - // --- view functions --- - - name: { - module: modules.ERC20, - kind: 'view', - implicitArgs: withImplicitArgs(), + burn: { args: [ + getSelfArg(), + { name: 'value', type: 'u256' } ], - returns: [{ name: 'name', type: 'felt' }], - passthrough: true, + code: [ + 'let caller = get_caller_address();', + 'self.erc20._burn(caller, value);' + ] }, - - symbol: { - module: modules.ERC20, - kind: 'view', - implicitArgs: withImplicitArgs(), + mint: { args: [ + getSelfArg(), + { name: 'recipient', type: 'ContractAddress' }, + { name: 'amount', type: 'u256' } ], - returns: [{ name: 'symbol', type: 'felt' }], - passthrough: true, + code: [ + 'self.erc20._mint(recipient, amount);' + ] }, - totalSupply: { - module: modules.ERC20, - kind: 'view', - implicitArgs: withImplicitArgs(), + // Re-implements ERC20Impl + total_supply: { args: [ + getSelfArg('view') ], - returns: [{ name: 'totalSupply', type: 'Uint256' }], - passthrough: 'strict', - parentFunctionName: 'total_supply', - }, - - decimals: { - module: modules.ERC20, - kind: 'view', - implicitArgs: withImplicitArgs(), - args: [ + code: [ + 'self.erc20.total_supply()' ], - returns: [{ name: 'decimals', type: 'felt' }], - passthrough: true, + returns : 'u256', }, - - balanceOf: { - module: modules.ERC20, - kind: 'view', - implicitArgs: withImplicitArgs(), + balance_of: { args: [ - { name: 'account', type: 'felt' }, + getSelfArg('view'), + { name: 'account', type: 'ContractAddress' }, ], - returns: [{ name: 'balance', type: 'Uint256' }], - passthrough: true, - parentFunctionName: 'balance_of', + code: [ + 'self.erc20.balance_of(account)' + ], + returns : 'u256', }, - allowance: { - module: modules.ERC20, - kind: 'view', - implicitArgs: withImplicitArgs(), args: [ - { name: 'owner', type: 'felt' }, - { name: 'spender', type: 'felt' }, + getSelfArg('view'), + { name: 'owner', type: 'ContractAddress' }, + { name: 'spender', type: 'ContractAddress' }, ], - returns: [{ name: 'remaining', type: 'Uint256' }], - passthrough: true, + code: [ + 'self.erc20.allowance(owner, spender)' + ], + returns : 'u256', }, - - // --- external functions --- - transfer: { - module: modules.ERC20, - kind: 'external', - implicitArgs: withImplicitArgs(), args: [ - { name: 'recipient', type: 'felt' }, - { name: 'amount', type: 'Uint256' }, + getSelfArg(), + { name: 'recipient', type: 'ContractAddress' }, + { name: 'amount', type: 'u256' }, + ], + code: [ + 'self.erc20.transfer(recipient, amount)', ], - returns: [{ name: 'success', type: 'felt' }], - passthrough: true, + returns : 'bool', }, - - transferFrom: { - module: modules.ERC20, - kind: 'external', - implicitArgs: withImplicitArgs(), + transfer_from: { args: [ - { name: 'sender', type: 'felt' }, - { name: 'recipient', type: 'felt' }, - { name: 'amount', type: 'Uint256' }, + getSelfArg(), + { name: 'sender', type: 'ContractAddress' }, + { name: 'recipient', type: 'ContractAddress' }, + { name: 'amount', type: 'u256' }, + ], + code: [ + 'self.erc20.transfer_from(sender, recipient, amount)', ], - returns: [{ name: 'success', type: 'felt' }], - passthrough: true, - parentFunctionName: 'transfer_from', + returns : 'bool', }, - approve: { - module: modules.ERC20, - kind: 'external', - implicitArgs: withImplicitArgs(), args: [ - { name: 'spender', type: 'felt' }, - { name: 'amount', type: 'Uint256' }, + getSelfArg(), + { name: 'spender', type: 'ContractAddress' }, + { name: 'amount', type: 'u256' }, ], - returns: [{ name: 'success', type: 'felt' }], - passthrough: true, + code: [ + 'self.erc20.approve(spender, amount)', + ], + returns : 'bool', }, - increaseAllowance: { - module: modules.ERC20, - kind: 'external', - implicitArgs: withImplicitArgs(), + // Re-implements ERC20CamelOnlyImpl + totalSupply: { args: [ - { name: 'spender', type: 'felt' }, - { name: 'added_value', type: 'Uint256' }, + getSelfArg('view') ], - returns: [{ name: 'success', type: 'felt' }], - passthrough: true, - parentFunctionName: 'increase_allowance', + code: [ + 'self.total_supply()' + ], + returns : 'u256', }, - - decreaseAllowance: { - module: modules.ERC20, - kind: 'external', - implicitArgs: withImplicitArgs(), + balanceOf: { args: [ - { name: 'spender', type: 'felt' }, - { name: 'subtracted_value', type: 'Uint256' }, + getSelfArg('view'), + { name: 'account', type: 'ContractAddress' }, ], - returns: [{ name: 'success', type: 'felt' }], - passthrough: true, - parentFunctionName: 'decrease_allowance', + code: [ + 'self.balance_of(account)' + ], + returns : 'u256', }, - - mint: { - module: modules.ERC20, - kind: 'external', - implicitArgs: withImplicitArgs(), + transferFrom: { args: [ - { name: 'to', type: 'felt' }, - { name: 'amount', type: 'Uint256' }, + getSelfArg(), + { name: 'sender', type: 'ContractAddress' }, + { name: 'recipient', type: 'ContractAddress' }, + { name: 'amount', type: 'u256' }, + ], + code: [ + 'self.transfer_from(sender, recipient, amount)', ], - parentFunctionName: '_mint' + returns : 'bool', }, - burn: { - module: modules.ERC20, - kind: 'external', - implicitArgs: withImplicitArgs(), + // Re-implements SafeAllowanceImpl + increase_allowance: { args: [ - { name: 'amount', type: 'Uint256' }, + getSelfArg(), + { name: 'spender', type: 'ContractAddress' }, + { name: 'added_value', type: 'u256' }, ], - parentFunctionName: '_burn' + code: [ + 'self.erc20.increase_allowance(spender, added_value)' + ], + returns : 'bool', + }, + decrease_allowance: { + args: [ + getSelfArg(), + { name: 'spender', type: 'ContractAddress' }, + { name: 'subtracted_value', type: 'u256' }, + ], + code: [ + 'self.erc20.decrease_allowance(spender, subtracted_value)' + ], + returns : 'bool', }, + // Re-implements SafeAllowanceCamelImpl + increaseAllowance: { + args: [ + getSelfArg(), + { name: 'spender', type: 'ContractAddress' }, + { name: 'addedValue', type: 'u256' }, + ], + code: [ + 'self.increase_allowance(spender, addedValue)' + ], + returns : 'bool', + }, + decreaseAllowance: { + args: [ + getSelfArg(), + { name: 'spender', type: 'ContractAddress' }, + { name: 'subtractedValue', type: 'u256' }, + ], + code: [ + 'self.decrease_allowance(spender, subtractedValue)' + ], + returns : 'bool', + }, }); diff --git a/packages/core-cairo/src/erc721.test.ts b/packages/core-cairo/src/erc721.test.ts index ed79d152..248d5bf0 100644 --- a/packages/core-cairo/src/erc721.test.ts +++ b/packages/core-cairo/src/erc721.test.ts @@ -75,5 +75,5 @@ test('API assert defaults', async t => { test('API isAccessControlRequired', async t => { t.is(erc721.isAccessControlRequired({ mintable: true }), true); t.is(erc721.isAccessControlRequired({ pausable: true }), true); - t.is(erc721.isAccessControlRequired({ upgradeable: true }), false); + t.is(erc721.isAccessControlRequired({ upgradeable: true }), true); }); \ No newline at end of file diff --git a/packages/core-cairo/src/erc721.test.ts.md b/packages/core-cairo/src/erc721.test.ts.md index 5a987f27..dccc4c34 100644 --- a/packages/core-cairo/src/erc721.test.ts.md +++ b/packages/core-cairo/src/erc721.test.ts.md @@ -10,111 +10,48 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc721.library import ERC721␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - ERC721.initializer('MyToken', 'MTK');␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC721.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC721.symbol();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) -> (balance: Uint256) {␊ - return ERC721.balance_of(owner);␊ - }␊ - ␊ - @view␊ - func ownerOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - token_id: Uint256␊ - ) -> (owner: felt) {␊ - return ERC721.owner_of(token_id);␊ - }␊ - ␊ - @view␊ - func getApproved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - token_id: Uint256␊ - ) -> (approved: felt) {␊ - return ERC721.get_approved(token_id);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC721.is_approved_for_all(owner, operator);␊ - }␊ - ␊ - @view␊ - func tokenURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - tokenId: Uint256␊ - ) -> (tokenURI: felt) {␊ - let (tokenURI) = ERC721.token_uri(tokenId);␊ - return (tokenURI=tokenURI);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, tokenId: Uint256␊ - ) {␊ - ERC721.approve(to, tokenId);␊ - return ();␊ - }␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - ERC721.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, tokenId: Uint256␊ - ) {␊ - ERC721.transfer_from(from_, to, tokenId);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*␊ - ) {␊ - ERC721.safe_transfer_from(from_, to, tokenId, data_len, data);␊ - return ();␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc721::ERC721Component;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + ␊ + component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl ERC721MetadataCamelOnly = ERC721Component::ERC721MetadataCamelOnlyImpl;␊ + #[abi(embed_v0)]␊ + impl ERC721Impl = ERC721Component::ERC721Impl;␊ + #[abi(embed_v0)]␊ + impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl;␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState) {␊ + self.erc721.initializer('MyToken', 'MTK');␊ + }␊ }␊ ` @@ -124,120 +61,59 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc721.library import ERC721␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - ERC721.initializer('MyToken', 'MTK');␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC721.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC721.symbol();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) -> (balance: Uint256) {␊ - return ERC721.balance_of(owner);␊ - }␊ - ␊ - @view␊ - func ownerOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - token_id: Uint256␊ - ) -> (owner: felt) {␊ - return ERC721.owner_of(token_id);␊ - }␊ - ␊ - @view␊ - func getApproved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - token_id: Uint256␊ - ) -> (approved: felt) {␊ - return ERC721.get_approved(token_id);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC721.is_approved_for_all(owner, operator);␊ - }␊ - ␊ - @view␊ - func tokenURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - tokenId: Uint256␊ - ) -> (tokenURI: felt) {␊ - let (tokenURI) = ERC721.token_uri(tokenId);␊ - return (tokenURI=tokenURI);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, tokenId: Uint256␊ - ) {␊ - ERC721.approve(to, tokenId);␊ - return ();␊ - }␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - ERC721.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, tokenId: Uint256␊ - ) {␊ - ERC721.transfer_from(from_, to, tokenId);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*␊ - ) {␊ - ERC721.safe_transfer_from(from_, to, tokenId, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - tokenId: Uint256␊ - ) {␊ - ERC721.assert_only_token_owner(tokenId);␊ - ERC721._burn(tokenId);␊ - return ();␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc721::ERC721Component;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use starknet::get_caller_address;␊ + ␊ + component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl ERC721MetadataCamelOnly = ERC721Component::ERC721MetadataCamelOnlyImpl;␊ + #[abi(embed_v0)]␊ + impl ERC721Impl = ERC721Component::ERC721Impl;␊ + #[abi(embed_v0)]␊ + impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl;␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + ␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState) {␊ + self.erc721.initializer('MyToken', 'MTK');␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn burn(ref self: ContractState, token_id: u256) {␊ + let caller = get_caller_address();␊ + assert(self.erc721._is_approved_or_owner(caller, token_id), ERC721Component::Errors::UNAUTHORIZED);␊ + self.erc721._burn(token_id);␊ + }␊ + }␊ }␊ ` @@ -247,158 +123,176 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc721.library import ERC721␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - from openzeppelin.security.pausable.library import Pausable␊ - from openzeppelin.access.ownable.library import Ownable␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) {␊ - ERC721.initializer('MyToken', 'MTK');␊ - Ownable.initializer(owner);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC721.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC721.symbol();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) -> (balance: Uint256) {␊ - return ERC721.balance_of(owner);␊ - }␊ - ␊ - @view␊ - func ownerOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - token_id: Uint256␊ - ) -> (owner: felt) {␊ - return ERC721.owner_of(token_id);␊ - }␊ - ␊ - @view␊ - func getApproved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - token_id: Uint256␊ - ) -> (approved: felt) {␊ - return ERC721.get_approved(token_id);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC721.is_approved_for_all(owner, operator);␊ - }␊ - ␊ - @view␊ - func tokenURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - tokenId: Uint256␊ - ) -> (tokenURI: felt) {␊ - let (tokenURI) = ERC721.token_uri(tokenId);␊ - return (tokenURI=tokenURI);␊ - }␊ - ␊ - @view␊ - func paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (paused: felt) {␊ - return Pausable.is_paused();␊ - }␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, tokenId: Uint256␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC721.approve(to, tokenId);␊ - return ();␊ - }␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC721.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, tokenId: Uint256␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC721.transfer_from(from_, to, tokenId);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC721.safe_transfer_from(from_, to, tokenId, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ - }␊ - ␊ - @external␊ - func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._pause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._unpause();␊ - return ();␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc721::ERC721Component;␊ + use openzeppelin::token::erc721::interface;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use starknet::ContractAddress;␊ + ␊ + component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl ERC721MetadataCamelOnly = ERC721Component::ERC721MetadataCamelOnlyImpl;␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableImpl = OwnableComponent::OwnableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableCamelOnlyImpl = OwnableComponent::OwnableCamelOnlyImpl;␊ + ␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.erc721.initializer('MyToken', 'MTK');␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC721Impl of interface::IERC721 {␊ + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.erc721.balance_of(account)␊ + }␊ + ␊ + fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress {␊ + self.erc721.owner_of(token_id)␊ + }␊ + ␊ + fn safe_transfer_from(␊ + ref self: ContractState,␊ + from: ContractAddress,␊ + to: ContractAddress,␊ + token_id: u256,␊ + data: Span,␊ + ) {␊ + self.pausable.assert_not_paused();␊ + self.erc721.safe_transfer_from(from, to, token_id, data);␊ + }␊ + ␊ + fn transfer_from(␊ + ref self: ContractState,␊ + from: ContractAddress,␊ + to: ContractAddress,␊ + token_id: u256,␊ + ) {␊ + self.pausable.assert_not_paused();␊ + self.erc721.transfer_from(from, to, token_id);␊ + }␊ + ␊ + fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) {␊ + self.pausable.assert_not_paused();␊ + self.erc721.approve(to, token_id);␊ + }␊ + ␊ + fn set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool) {␊ + self.pausable.assert_not_paused();␊ + self.erc721.set_approval_for_all(operator, approved);␊ + }␊ + ␊ + fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress {␊ + self.erc721.get_approved(token_id)␊ + }␊ + ␊ + fn is_approved_for_all(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool {␊ + self.erc721.is_approved_for_all(owner, operator)␊ + }␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC721CamelOnlyImpl of interface::IERC721CamelOnly {␊ + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.balance_of(account)␊ + }␊ + ␊ + fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress {␊ + self.owner_of(tokenId)␊ + }␊ + ␊ + fn safeTransferFrom(␊ + ref self: ContractState,␊ + from: ContractAddress,␊ + to: ContractAddress,␊ + tokenId: u256,␊ + data: Span,␊ + ) {␊ + self.pausable.assert_not_paused();␊ + self.safe_transfer_from(from, to, tokenId, data);␊ + }␊ + ␊ + fn transferFrom(␊ + ref self: ContractState,␊ + from: ContractAddress,␊ + to: ContractAddress,␊ + tokenId: u256,␊ + ) {␊ + self.pausable.assert_not_paused();␊ + self.transfer_from(from, to, tokenId);␊ + }␊ + ␊ + fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) {␊ + self.pausable.assert_not_paused();␊ + self.set_approval_for_all(operator, approved);␊ + }␊ + ␊ + fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress {␊ + self.get_approved(tokenId)␊ + }␊ + ␊ + fn isApprovedForAll(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool {␊ + self.is_approved_for_all(owner, operator)␊ + }␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn pause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._pause();␊ + }␊ + ␊ + fn unpause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._unpause();␊ + }␊ + }␊ }␊ ` @@ -408,144 +302,87 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc721.library import ERC721␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - from openzeppelin.access.ownable.library import Ownable␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) {␊ - ERC721.initializer('MyToken', 'MTK');␊ - Ownable.initializer(owner);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC721.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC721.symbol();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) -> (balance: Uint256) {␊ - return ERC721.balance_of(owner);␊ - }␊ - ␊ - @view␊ - func ownerOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - token_id: Uint256␊ - ) -> (owner: felt) {␊ - return ERC721.owner_of(token_id);␊ - }␊ - ␊ - @view␊ - func getApproved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - token_id: Uint256␊ - ) -> (approved: felt) {␊ - return ERC721.get_approved(token_id);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC721.is_approved_for_all(owner, operator);␊ - }␊ - ␊ - @view␊ - func tokenURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - tokenId: Uint256␊ - ) -> (tokenURI: felt) {␊ - let (tokenURI) = ERC721.token_uri(tokenId);␊ - return (tokenURI=tokenURI);␊ - }␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, tokenId: Uint256␊ - ) {␊ - ERC721.approve(to, tokenId);␊ - return ();␊ - }␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - ERC721.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, tokenId: Uint256␊ - ) {␊ - ERC721.transfer_from(from_, to, tokenId);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*␊ - ) {␊ - ERC721.safe_transfer_from(from_, to, tokenId, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeMint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, tokenId: Uint256, data_len: felt, data: felt*, tokenURI: felt␊ - ) {␊ - Ownable.assert_only_owner();␊ - ERC721._safe_mint(to, tokenId, data_len, data);␊ - ERC721._set_token_uri(tokenId, tokenURI);␊ - return ();␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc721::ERC721Component;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use starknet::ContractAddress;␊ + ␊ + component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl ERC721MetadataCamelOnly = ERC721Component::ERC721MetadataCamelOnlyImpl;␊ + #[abi(embed_v0)]␊ + impl ERC721Impl = ERC721Component::ERC721Impl;␊ + #[abi(embed_v0)]␊ + impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl;␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + #[abi(embed_v0)]␊ + impl OwnableImpl = OwnableComponent::OwnableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableCamelOnlyImpl = OwnableComponent::OwnableCamelOnlyImpl;␊ + ␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.erc721.initializer('MyToken', 'MTK');␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn safe_mint(␊ + ref self: ContractState,␊ + recipient: ContractAddress,␊ + token_id: u256,␊ + data: Span,␊ + token_uri: felt252,␊ + ) {␊ + self.ownable.assert_only_owner();␊ + self.erc721._safe_mint(recipient, token_id, data);␊ + self.erc721._set_token_uri(token_id, token_uri);␊ + }␊ + ␊ + fn safeMint(␊ + ref self: ContractState,␊ + recipient: ContractAddress,␊ + tokenId: u256,␊ + data: Span,␊ + tokenURI: felt252,␊ + ) {␊ + self.safe_mint(recipient, tokenId, data, tokenURI);␊ + }␊ + }␊ }␊ ` @@ -555,169 +392,94 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc721.library import ERC721␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - from openzeppelin.access.accesscontrol.library import AccessControl␊ - from openzeppelin.utils.constants.library import DEFAULT_ADMIN_ROLE␊ - ␊ - const MINTER_ROLE = 0x4f96f87f6963bb246f2c30526628466840c642dc5c50d5a67777c6cc0e44ab5; // keccak256('MINTER_ROLE')[0:251 bits]␊ - ␊ - @constructor␊ - func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - admin: felt␊ - ) {␊ - ERC721.initializer('MyToken', 'MTK');␊ - AccessControl.initializer();␊ - ␊ - AccessControl._grant_role(DEFAULT_ADMIN_ROLE, admin);␊ - AccessControl._grant_role(MINTER_ROLE, admin);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC721.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC721.symbol();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) -> (balance: Uint256) {␊ - return ERC721.balance_of(owner);␊ - }␊ - ␊ - @view␊ - func ownerOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - token_id: Uint256␊ - ) -> (owner: felt) {␊ - return ERC721.owner_of(token_id);␊ - }␊ - ␊ - @view␊ - func getApproved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - token_id: Uint256␊ - ) -> (approved: felt) {␊ - return ERC721.get_approved(token_id);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC721.is_approved_for_all(owner, operator);␊ - }␊ - ␊ - @view␊ - func tokenURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - tokenId: Uint256␊ - ) -> (tokenURI: felt) {␊ - let (tokenURI) = ERC721.token_uri(tokenId);␊ - return (tokenURI=tokenURI);␊ - }␊ - ␊ - @view␊ - func hasRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) -> (has_role: felt) {␊ - return AccessControl.has_role(role, user);␊ - }␊ - ␊ - @view␊ - func getRoleAdmin{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt␊ - ) -> (admin: felt) {␊ - return AccessControl.get_role_admin(role);␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, tokenId: Uint256␊ - ) {␊ - ERC721.approve(to, tokenId);␊ - return ();␊ - }␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - ERC721.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, tokenId: Uint256␊ - ) {␊ - ERC721.transfer_from(from_, to, tokenId);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*␊ - ) {␊ - ERC721.safe_transfer_from(from_, to, tokenId, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func grantRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.grant_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func revokeRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.revoke_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceRole{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - role: felt, user: felt␊ - ) {␊ - AccessControl.renounce_role(role, user);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeMint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, tokenId: Uint256, data_len: felt, data: felt*, tokenURI: felt␊ - ) {␊ - AccessControl.assert_only_role(MINTER_ROLE);␊ - ERC721._safe_mint(to, tokenId, data_len, data);␊ - ERC721._set_token_uri(tokenId, tokenURI);␊ - return ();␊ + const MINTER_ROLE: felt252 = selector!("MINTER_ROLE");␊ + ␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc721::ERC721Component;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::access::accesscontrol::AccessControlComponent;␊ + use openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE;␊ + use starknet::ContractAddress;␊ + use super::{MINTER_ROLE};␊ + ␊ + component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl ERC721MetadataCamelOnly = ERC721Component::ERC721MetadataCamelOnlyImpl;␊ + #[abi(embed_v0)]␊ + impl ERC721Impl = ERC721Component::ERC721Impl;␊ + #[abi(embed_v0)]␊ + impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl;␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + #[abi(embed_v0)]␊ + impl AccessControlImpl = AccessControlComponent::AccessControlImpl;␊ + #[abi(embed_v0)]␊ + impl AccessControlCamelImpl = AccessControlComponent::AccessControlCamelImpl;␊ + ␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + accesscontrol: AccessControlComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + AccessControlEvent: AccessControlComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, default_admin: ContractAddress, minter: ContractAddress) {␊ + self.erc721.initializer('MyToken', 'MTK');␊ + self.accesscontrol.initializer();␊ + ␊ + self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin);␊ + self.accesscontrol._grant_role(MINTER_ROLE, minter);␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn safe_mint(␊ + ref self: ContractState,␊ + recipient: ContractAddress,␊ + token_id: u256,␊ + data: Span,␊ + token_uri: felt252,␊ + ) {␊ + self.accesscontrol.assert_only_role(MINTER_ROLE);␊ + self.erc721._safe_mint(recipient, token_id, data);␊ + self.erc721._set_token_uri(token_id, token_uri);␊ + }␊ + ␊ + fn safeMint(␊ + ref self: ContractState,␊ + recipient: ContractAddress,␊ + tokenId: u256,␊ + data: Span,␊ + tokenURI: felt252,␊ + ) {␊ + self.safe_mint(recipient, tokenId, data, tokenURI);␊ + }␊ + }␊ }␊ ` @@ -727,189 +489,223 @@ Generated by [AVA](https://avajs.dev). `// SPDX-License-Identifier: MIT␊ ␊ - %lang starknet␊ - ␊ - from starkware.cairo.common.cairo_builtins import HashBuiltin␊ - from starkware.cairo.common.uint256 import Uint256␊ - ␊ - from openzeppelin.token.erc721.library import ERC721␊ - from openzeppelin.introspection.erc165.library import ERC165␊ - from openzeppelin.security.pausable.library import Pausable␊ - from openzeppelin.access.ownable.library import Ownable␊ - from openzeppelin.upgrades.library import Proxy␊ - ␊ - @external␊ - func initializer{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, proxy_admin: felt␊ - ) {␊ - ERC721.initializer('MyToken', 'MTK');␊ - Ownable.initializer(owner);␊ - Proxy.initializer(proxy_admin);␊ - return ();␊ - }␊ - ␊ - //␊ - // Getters␊ - //␊ - ␊ - @view␊ - func supportsInterface{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - interfaceId: felt␊ - ) -> (success: felt) {␊ - return ERC165.supports_interface(interfaceId);␊ - }␊ - ␊ - @view␊ - func name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (name: felt) {␊ - return ERC721.name();␊ - }␊ - ␊ - @view␊ - func symbol{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (symbol: felt) {␊ - return ERC721.symbol();␊ - }␊ - ␊ - @view␊ - func balanceOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt␊ - ) -> (balance: Uint256) {␊ - return ERC721.balance_of(owner);␊ - }␊ - ␊ - @view␊ - func ownerOf{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - token_id: Uint256␊ - ) -> (owner: felt) {␊ - return ERC721.owner_of(token_id);␊ - }␊ - ␊ - @view␊ - func getApproved{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - token_id: Uint256␊ - ) -> (approved: felt) {␊ - return ERC721.get_approved(token_id);␊ - }␊ - ␊ - @view␊ - func isApprovedForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - owner: felt, operator: felt␊ - ) -> (approved: felt) {␊ - return ERC721.is_approved_for_all(owner, operator);␊ - }␊ - ␊ - @view␊ - func tokenURI{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - tokenId: Uint256␊ - ) -> (tokenURI: felt) {␊ - let (tokenURI) = ERC721.token_uri(tokenId);␊ - return (tokenURI=tokenURI);␊ - }␊ - ␊ - @view␊ - func paused{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (paused: felt) {␊ - return Pausable.is_paused();␊ - }␊ - ␊ - @view␊ - func owner{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() -> (owner: felt) {␊ - return Ownable.owner();␊ - }␊ - ␊ - //␊ - // Externals␊ - //␊ - ␊ - @external␊ - func approve{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, tokenId: Uint256␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC721.approve(to, tokenId);␊ - return ();␊ - }␊ - ␊ - @external␊ - func setApprovalForAll{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - operator: felt, approved: felt␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC721.set_approval_for_all(operator, approved);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, tokenId: Uint256␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC721.transfer_from(from_, to, tokenId);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeTransferFrom{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - from_: felt, to: felt, tokenId: Uint256, data_len: felt, data: felt*␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC721.safe_transfer_from(from_, to, tokenId, data_len, data);␊ - return ();␊ - }␊ - ␊ - @external␊ - func transferOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - newOwner: felt␊ - ) {␊ - Ownable.transfer_ownership(newOwner);␊ - return ();␊ - }␊ - ␊ - @external␊ - func renounceOwnership{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.renounce_ownership();␊ - return ();␊ - }␊ - ␊ - @external␊ - func pause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._pause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func unpause{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}() {␊ - Ownable.assert_only_owner();␊ - Pausable._unpause();␊ - return ();␊ - }␊ - ␊ - @external␊ - func burn{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - tokenId: Uint256␊ - ) {␊ - Pausable.assert_not_paused();␊ - ERC721.assert_only_token_owner(tokenId);␊ - ERC721._burn(tokenId);␊ - return ();␊ - }␊ - ␊ - @external␊ - func safeMint{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - to: felt, tokenId: Uint256, data_len: felt, data: felt*, tokenURI: felt␊ - ) {␊ - Pausable.assert_not_paused();␊ - Ownable.assert_only_owner();␊ - ERC721._safe_mint(to, tokenId, data_len, data);␊ - ERC721._set_token_uri(tokenId, tokenURI);␊ - return ();␊ - }␊ - ␊ - @external␊ - func upgrade{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(␊ - new_implementation: felt␊ - ) -> () {␊ - Proxy.assert_only_admin();␊ - Proxy._set_implementation_hash(new_implementation);␊ - return ();␊ + #[starknet::contract]␊ + mod MyToken {␊ + use openzeppelin::token::erc721::ERC721Component;␊ + use openzeppelin::token::erc721::interface;␊ + use openzeppelin::introspection::src5::SRC5Component;␊ + use openzeppelin::security::pausable::PausableComponent;␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::upgrades::UpgradeableComponent;␊ + use openzeppelin::upgrades::interface::IUpgradeable;␊ + use starknet::ContractAddress;␊ + use starknet::get_caller_address;␊ + use starknet::ClassHash;␊ + ␊ + component!(path: ERC721Component, storage: erc721, event: ERC721Event);␊ + component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ + component!(path: PausableComponent, storage: pausable, event: PausableEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);␊ + ␊ + #[abi(embed_v0)]␊ + impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl;␊ + #[abi(embed_v0)]␊ + impl ERC721MetadataCamelOnly = ERC721Component::ERC721MetadataCamelOnlyImpl;␊ + #[abi(embed_v0)]␊ + impl SRC5Impl = SRC5Component::SRC5Impl;␊ + #[abi(embed_v0)]␊ + impl PausableImpl = PausableComponent::PausableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableImpl = OwnableComponent::OwnableImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableCamelOnlyImpl = OwnableComponent::OwnableCamelOnlyImpl;␊ + ␊ + impl ERC721InternalImpl = ERC721Component::InternalImpl;␊ + impl PausableInternalImpl = PausableComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + erc721: ERC721Component::Storage,␊ + #[substorage(v0)]␊ + src5: SRC5Component::Storage,␊ + #[substorage(v0)]␊ + pausable: PausableComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + #[substorage(v0)]␊ + upgradeable: UpgradeableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + ERC721Event: ERC721Component::Event,␊ + #[flat]␊ + SRC5Event: SRC5Component::Event,␊ + #[flat]␊ + PausableEvent: PausableComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + #[flat]␊ + UpgradeableEvent: UpgradeableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.erc721.initializer('MyToken', 'MTK');␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC721Impl of interface::IERC721 {␊ + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.erc721.balance_of(account)␊ + }␊ + ␊ + fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress {␊ + self.erc721.owner_of(token_id)␊ + }␊ + ␊ + fn safe_transfer_from(␊ + ref self: ContractState,␊ + from: ContractAddress,␊ + to: ContractAddress,␊ + token_id: u256,␊ + data: Span,␊ + ) {␊ + self.pausable.assert_not_paused();␊ + self.erc721.safe_transfer_from(from, to, token_id, data);␊ + }␊ + ␊ + fn transfer_from(␊ + ref self: ContractState,␊ + from: ContractAddress,␊ + to: ContractAddress,␊ + token_id: u256,␊ + ) {␊ + self.pausable.assert_not_paused();␊ + self.erc721.transfer_from(from, to, token_id);␊ + }␊ + ␊ + fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) {␊ + self.pausable.assert_not_paused();␊ + self.erc721.approve(to, token_id);␊ + }␊ + ␊ + fn set_approval_for_all(ref self: ContractState, operator: ContractAddress, approved: bool) {␊ + self.pausable.assert_not_paused();␊ + self.erc721.set_approval_for_all(operator, approved);␊ + }␊ + ␊ + fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress {␊ + self.erc721.get_approved(token_id)␊ + }␊ + ␊ + fn is_approved_for_all(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool {␊ + self.erc721.is_approved_for_all(owner, operator)␊ + }␊ + }␊ + ␊ + #[external(v0)]␊ + impl ERC721CamelOnlyImpl of interface::IERC721CamelOnly {␊ + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 {␊ + self.balance_of(account)␊ + }␊ + ␊ + fn ownerOf(self: @ContractState, tokenId: u256) -> ContractAddress {␊ + self.owner_of(tokenId)␊ + }␊ + ␊ + fn safeTransferFrom(␊ + ref self: ContractState,␊ + from: ContractAddress,␊ + to: ContractAddress,␊ + tokenId: u256,␊ + data: Span,␊ + ) {␊ + self.pausable.assert_not_paused();␊ + self.safe_transfer_from(from, to, tokenId, data);␊ + }␊ + ␊ + fn transferFrom(␊ + ref self: ContractState,␊ + from: ContractAddress,␊ + to: ContractAddress,␊ + tokenId: u256,␊ + ) {␊ + self.pausable.assert_not_paused();␊ + self.transfer_from(from, to, tokenId);␊ + }␊ + ␊ + fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) {␊ + self.pausable.assert_not_paused();␊ + self.set_approval_for_all(operator, approved);␊ + }␊ + ␊ + fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress {␊ + self.get_approved(tokenId)␊ + }␊ + ␊ + fn isApprovedForAll(self: @ContractState, owner: ContractAddress, operator: ContractAddress) -> bool {␊ + self.is_approved_for_all(owner, operator)␊ + }␊ + }␊ + ␊ + #[generate_trait]␊ + #[external(v0)]␊ + impl ExternalImpl of ExternalTrait {␊ + fn pause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._pause();␊ + }␊ + ␊ + fn unpause(ref self: ContractState) {␊ + self.ownable.assert_only_owner();␊ + self.pausable._unpause();␊ + }␊ + ␊ + fn burn(ref self: ContractState, token_id: u256) {␊ + self.pausable.assert_not_paused();␊ + let caller = get_caller_address();␊ + assert(self.erc721._is_approved_or_owner(caller, token_id), ERC721Component::Errors::UNAUTHORIZED);␊ + self.erc721._burn(token_id);␊ + }␊ + ␊ + fn safe_mint(␊ + ref self: ContractState,␊ + recipient: ContractAddress,␊ + token_id: u256,␊ + data: Span,␊ + token_uri: felt252,␊ + ) {␊ + self.ownable.assert_only_owner();␊ + self.pausable.assert_not_paused();␊ + self.erc721._safe_mint(recipient, token_id, data);␊ + self.erc721._set_token_uri(token_id, token_uri);␊ + }␊ + ␊ + fn safeMint(␊ + ref self: ContractState,␊ + recipient: ContractAddress,␊ + tokenId: u256,␊ + data: Span,␊ + tokenURI: felt252,␊ + ) {␊ + self.safe_mint(recipient, tokenId, data, tokenURI);␊ + }␊ + }␊ + ␊ + #[external(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);␊ + }␊ + }␊ }␊ ` diff --git a/packages/core-cairo/src/erc721.test.ts.snap b/packages/core-cairo/src/erc721.test.ts.snap index 0eb9b9792fe5b60f07b7004b7d42614b0ce0467e..549562d886df78dc69b5cba7592a62441ca553a2 100644 GIT binary patch literal 2017 zcmV<72OjuARzVQ&w)H^)VZmCj%k zQ^yq(0w0SA00000000B+TuX1;HXQFZbOnrFcIs(`)&h%yx$PDp8=!W@j3h;kHBFH? zz_y|gXqifc$f7_|=?gjRvg5wN4&4XaH`sm0krGLh5+y(4q_!g;l1SqJ^&|f;%FGA9 zA0qE0`QvX$0un;l2T9-sNd)5L7?I@n4}$?9;MeQ#KD&0~8vA^5{n{6wo?L(Mz&d!b z{mZ>)fd``m-s=uvL;^p6*s*rIy;ke)%Y*=Y5<%iP9*PJC9(mOoqJg#hwueq&WW8@$ z^phsgLNScq!8nFt5IGKEPaOwh?}v@=9A{^Li~in1!x%+W#={D#fQmp#3_TJcdYNEv z({T>=w>A~ZjMNj?U)eDr$BtDH(6$nSFgSvam8;OU;3>T?2JWzn^@nC&nm~mUQ%CY) zg9Mk1&E1!vAJ}l%hXeQY+x1u3&0rXZT;wh!fK5Y}-TJYpuz*M%^=OMjen0?$j~`AY zW(y2qxEFT@$Q=3?3U z$`wlpPCa5BVo|2|*J=qxBlnjJyndLRrTcEMnPVUjd4youpW3~yria>Z~i`Upl4Q(i7* z8%PY{n|zJf1s8auEG^{A>mH+3l%eXg#89Po+sXpA8w?yP-PrtIwS#>^NUS`>()C-8 zw>Gyyd_a;wOl%baH%MF%#~7Vb4@1~Ruc?)G&SoTiy=|CRj1f*8=kR&wu=jLtzx&(H zc0rydD6>;C91T1xrOFtji8)o?`dUksw{9(5sw_|iEQI*LgY!tOI%NXA6bIz3;|RPQ z=LP?3EHUsrx;`9-PIpeZm;X_l(8bd1EO0hAaw);0a1!82ZenmP$0A$`Lk_9r$A>oM z#w;njo}rj3&a^bmBZruQIiEMqyEx&}V`k^9L0KoF9`m_YHDzdZZU%@a<0sJ4*_BOR zotmqjUdC(_q*L$_0o(xPN>zX+LM>7ofgHm?4b3J5=U9QZt*o*d-Q`L29F?gg;cGyn z$)pua*s5Z6)CSU0RhBH&8S^IvKP;55AOw*IUF6%j**_KRrlkf-8NMaU?>)|n+CojM zqM$J6Y}S>!QB@>eBx8w?dS0~qWF zn~TwWv@>x-mWFIqVSXOr(24+tr)^gsx9pPug?V zM9-Kt->4iw+X4h?P9LFgo-5d-p-^Nbx6;BRNl)cmKwQ%JkBd!epD%1N>ts$;HB*qO z&9JZ0)g(R>G~%mjuIN7|ZRu6CF@MP+lO(dex#qDc6IiA2Y<9a-E)zX(b!*dBkB6Wq zA34x=KTggXpRBr*=BmX*a2^1S!KN3Us@qLCcah>%?*6#+q72X(#jWrf-iWMI- zG(s49K^#Csr#+`V&Jl5n1IOY6#%O3S#RU3!@Mw<-+*pUs_@O=gqN&apvsw&*#0J zeRqHF*^ab|<`JqsWMSYd`;Ic=4y(SFub|F(=Tls}#N}RlgpUE8$rA4xk)btHGFSH!y%cNJHX+0j*IC+qJ{&z1D@a3)TSRY;ott~ofcM6Xj8^5B5vrubEXqsd9HPWyM+#}VqeC=t;d zJ+D9`dYOkCOytE3R5cN+_DZZdBhj3ZXwFD9XC#_463rQj=8QyhMnZUEb4H>$Bhj3Z zXwFD9XC#*VjD(iY&hfvOru3-Q_*YG(PHv^%mCm@%rYe}5SE-BfDz!ecs{6z|xT>$< zYKvsPds!Vs-C+{`Ta- z?^hmShzLVh1|vi`4l$y)-L1i{<9r(d^4w({&^>~<gi9I6>qEN)i-d(&q6VB5g$BjqbH} zgohN+sTSMZxi4C4goHX8vjjno<4pLP->(rCt&VdK#)NU2LLO5mObK)g4KLZ0K@dej z!iAGU6!Gs`Zh}ULGDK8k!DR7{3J;(*NC+Scx`YwLHio)qzv64w)tuN*OHa^8MMoQpB#lXGt$qXUJMrHQnK4TyLf0*Kxn zZw3KteS?wG;;g$@esDdOLWmesZQ8ONukvSGLkea(yn;4mQ^5iPCJlgA=%lWrph@4$ z_g)17n@)yt)D#A#?Kl*wM-|5qh}{R#cG%Dy#s>sZWA^e9ZtLw@@kR0Guc8Y-aHi1jDRQ)#wdSO*5>YM%ve2THD(*fNb1 znv~!&y+c6M>~b?!Sj#mRR~bB;?Fxg z1z7?slZQINZPk3{q@X52ds~)}Lp8eUe9f{5%OWg`uq?u|h!tcJ1*_!w)x@e&=#{ow z&+7wG`d)RbMviO>n!)N5h$J}z>$u-TsqeOr7T zoE5>-I=5v&U}Ky#3;9hI6|z-E-(FPu*=RR%P!xBU8-M#F$z^tS$HFRd0)M( z(UEc-v35o`0pYAH{$TUr#?wc;!N!BF!Q)_O`_ZQ3WSzx4Jl@^h$(lrx(mCvfxBT$t zjnKd4Ump&;JwNQi>z#hr_q#WHzJIgVfqt($f_>QUjQYU8AwJNDu!DL%FznxP#iZ{c z2*IB4ub`oG^=s>Yc5Zk3*W4lI?AiNa%U~py?Wx(a;dDblbg1bOJQpZ|C6 zsnuPSEV#@RvE&b?PX17Tu6+K z;I%*jVqciNEZA!F)8STCjpZTFt173!zT%}7)6imprA)k*TWSf@D00(8Ed3Fx|04hL zx>0KR`m!yxtjOj2n$nD#$VyG-35^e@l`T}LGMq( zEyLC_Y%RmqGFHBpVK4ZvZqdPB@c$SK7xsdGskC7)_X(_ipk#(x)B{Ge&{ z{||g%m8Fxtv})ISDr;U = { name: 'MyToken', @@ -45,251 +46,342 @@ function withDefaults(opts: ERC721Options): Required { } export function isAccessControlRequired(opts: Partial): boolean { - return opts.mintable === true || opts.pausable === true; + return opts.mintable === true || opts.pausable === true || opts.upgradeable === true; } export function buildERC721(opts: ERC721Options): Contract { - const c = new ContractBuilder(); + const c = new ContractBuilder(opts.name); const allOpts = withDefaults(opts); - addBase(c, allOpts.name, allOpts.symbol); - addSupportsInterface(c); - - c.addFunction(functions.name); - c.addFunction(functions.symbol); - c.addFunction(functions.balanceOf); - c.addFunction(functions.ownerOf); - c.addFunction(functions.getApproved); - c.addFunction(functions.isApprovedForAll); - c.addFunction(functions.tokenURI); - - c.addFunction(functions.approve); - c.addFunction(functions.setApprovalForAll); - c.addFunction(functions.transferFrom); - c.addFunction(functions.safeTransferFrom); - - importUint256(c); + addBase(c, toShortString(allOpts.name, 'name'), toShortString(allOpts.symbol, 'symbol')); + addERC721ImplAndCamelOnlyImpl(c, allOpts.pausable); if (allOpts.pausable) { - addPausable(c, allOpts.access, [functions.approve, functions.setApprovalForAll, functions.transferFrom, functions.safeTransferFrom]); - if (allOpts.burnable) { - setPausable(c, functions.burn); - } - if (allOpts.mintable) { - setPausable(c, functions.safeMint); - } + addPausable(c, allOpts.access); } if (allOpts.burnable) { addBurnable(c); + if (allOpts.pausable) { + setPausable(c, externalTrait, functions.burn); + } } if (allOpts.mintable) { addMintable(c, allOpts.access); + if (allOpts.pausable) { + setPausable(c, externalTrait, functions.safe_mint); + } } setAccessControl(c, allOpts.access); - setUpgradeable(c, allOpts.upgradeable); - + setUpgradeable(c, allOpts.upgradeable, allOpts.access); setInfo(c, allOpts.info); return c; } +function addERC721Interface(c: ContractBuilder) { + c.addStandaloneImport('openzeppelin::token::erc721::interface'); +} + +function addERC721ImplAndCamelOnlyImpl(c: ContractBuilder, pausable: boolean) { + if (pausable) { + addERC721Interface(c); + + const ERC721Impl: BaseImplementedTrait = { + name: 'ERC721Impl', + of: 'interface::IERC721', + tags: [ + '#[external(v0)]' + ], + } + c.addFunction(ERC721Impl, functions.balance_of); + c.addFunction(ERC721Impl, functions.owner_of); + setPausable(c, ERC721Impl, functions.safe_transfer_from); + setPausable(c, ERC721Impl, functions.transfer_from); + setPausable(c, ERC721Impl, functions.approve); + setPausable(c, ERC721Impl, functions.set_approval_for_all); + c.addFunction(ERC721Impl, functions.get_approved); + c.addFunction(ERC721Impl, functions.is_approved_for_all); + + const ERC721CamelOnlyImpl: BaseImplementedTrait = { + name: 'ERC721CamelOnlyImpl', + of: 'interface::IERC721CamelOnly', + tags: [ + '#[external(v0)]' + ], + } + c.addFunction(ERC721CamelOnlyImpl, functions.balanceOf); + c.addFunction(ERC721CamelOnlyImpl, functions.ownerOf); + setPausable(c, ERC721CamelOnlyImpl, functions.safeTransferFrom); + setPausable(c, ERC721CamelOnlyImpl, functions.transferFrom); + setPausable(c, ERC721CamelOnlyImpl, functions.setApprovalForAll); + c.addFunction(ERC721CamelOnlyImpl, functions.getApproved); + c.addFunction(ERC721CamelOnlyImpl, functions.isApprovedForAll); + } else { + c.addImplToComponent(components.ERC721Component, { + name: 'ERC721Impl', + value: 'ERC721Component::ERC721Impl', + }); + c.addImplToComponent(components.ERC721Component, { + name: 'ERC721CamelOnly', + value: 'ERC721Component::ERC721CamelOnlyImpl', + }); + } +} + function addBase(c: ContractBuilder, name: string, symbol: string) { - c.addModule( - modules.ERC721, - [name, symbol], - [functions.approve, functions.setApprovalForAll, functions.transferFrom, functions.safeTransferFrom], + c.addComponent( + components.ERC721Component, + [ + name, symbol + ], true, ); + addSRC5Component(c); } function addBurnable(c: ContractBuilder) { - c.addFunction(functions.burn); - c.setFunctionBody( - [ - 'ERC721.assert_only_token_owner(tokenId)', - 'ERC721._burn(tokenId)' - ], - functions.burn - ); + c.addStandaloneImport('starknet::get_caller_address'); + c.addFunction(externalTrait, functions.burn); } function addMintable(c: ContractBuilder, access: Access) { - requireAccessControl(c, functions.safeMint, access, 'MINTER'); - c.setFunctionBody( - [ - 'ERC721._safe_mint(to, tokenId, data_len, data)', - 'ERC721._set_token_uri(tokenId, tokenURI)' - ], - functions.safeMint - ); + c.addStandaloneImport('starknet::ContractAddress'); + requireAccessControl(c, externalTrait, functions.safe_mint, access, 'MINTER', 'minter'); + + // Camel case version of safe_mint. Access control and pausable are already set on safe_mint. + c.addFunction(externalTrait, functions.safeMint); } -const modules = defineModules( { - ERC721: { - path: 'openzeppelin.token.erc721.library', - useNamespace: true +const components = defineComponents( { + ERC721Component: { + path: 'openzeppelin::token::erc721', + substorage: { + name: 'erc721', + type: 'ERC721Component::Storage', + }, + event: { + name: 'ERC721Event', + type: 'ERC721Component::Event', + }, + impls: [ + { + name: 'ERC721MetadataImpl', + value: 'ERC721Component::ERC721MetadataImpl', + }, + { + name: 'ERC721MetadataCamelOnly', + value: 'ERC721Component::ERC721MetadataCamelOnlyImpl', + } + ], + internalImpl: { + name: 'ERC721InternalImpl', + value: 'ERC721Component::InternalImpl', + }, }, }); const functions = defineFunctions({ - - // --- view functions --- - - name: { - module: modules.ERC721, - kind: 'view', - implicitArgs: withImplicitArgs(), + burn: { args: [ + getSelfArg(), + { name: 'token_id', type: 'u256' } ], - returns: [{ name: 'name', type: 'felt' }], - passthrough: true, + code: [ + 'let caller = get_caller_address();', + 'assert(self.erc721._is_approved_or_owner(caller, token_id), ERC721Component::Errors::UNAUTHORIZED)', + 'self.erc721._burn(token_id);' + ] }, - - symbol: { - module: modules.ERC721, - kind: 'view', - implicitArgs: withImplicitArgs(), + safe_mint: { args: [ + getSelfArg(), + { name: 'recipient', type: 'ContractAddress' }, + { name: 'token_id', type: 'u256' }, + { name: 'data', type: 'Span' }, + { name: 'token_uri', type: 'felt252' }, ], - returns: [{ name: 'symbol', type: 'felt' }], - passthrough: true, + code: [ + 'self.erc721._safe_mint(recipient, token_id, data);', + 'self.erc721._set_token_uri(token_id, token_uri);', + ] }, - - balanceOf: { - module: modules.ERC721, - kind: 'view', - implicitArgs: withImplicitArgs(), + safeMint: { args: [ - { name: 'owner', type: 'felt' }, + getSelfArg(), + { name: 'recipient', type: 'ContractAddress' }, + { name: 'tokenId', type: 'u256' }, + { name: 'data', type: 'Span' }, + { name: 'tokenURI', type: 'felt252' }, ], - returns: [{ name: 'balance', type: 'Uint256' }], - passthrough: true, - parentFunctionName: 'balance_of', + code: [ + 'self.safe_mint(recipient, tokenId, data, tokenURI);', + ] }, - ownerOf: { - module: modules.ERC721, - kind: 'view', - implicitArgs: withImplicitArgs(), + // Re-implements ERC721Impl + balance_of: { args: [ - { name: 'token_id', type: 'Uint256' }, + getSelfArg('view'), + { name: 'account', type: 'ContractAddress' }, ], - returns: [{ name: 'owner', type: 'felt' }], - passthrough: true, - parentFunctionName: 'owner_of', + returns: 'u256', + code: [ + 'self.erc721.balance_of(account)' + ] }, - - getApproved: { - module: modules.ERC721, - kind: 'view', - implicitArgs: withImplicitArgs(), + owner_of: { args: [ - { name: 'token_id', type: 'Uint256' }, + getSelfArg('view'), + { name: 'token_id', type: 'u256' }, ], - returns: [{ name: 'approved', type: 'felt' }], - passthrough: true, - parentFunctionName: 'get_approved', + returns: 'ContractAddress', + code: [ + 'self.erc721.owner_of(token_id)' + ] }, - - isApprovedForAll: { - module: modules.ERC721, - kind: 'view', - implicitArgs: withImplicitArgs(), + safe_transfer_from: { args: [ - { name: 'owner', type: 'felt' }, - { name: 'operator', type: 'felt' }, + getSelfArg(), + { name: 'from', type: 'ContractAddress' }, + { name: 'to', type: 'ContractAddress' }, + { name: 'token_id', type: 'u256' }, + { name: 'data', type: 'Span' }, ], - returns: [{ name: 'approved', type: 'felt' }], - passthrough: true, - parentFunctionName: 'is_approved_for_all', + code: [ + 'self.erc721.safe_transfer_from(from, to, token_id, data)' + ] }, - - tokenURI: { - module: modules.ERC721, - kind: 'view', - implicitArgs: withImplicitArgs(), + transfer_from: { args: [ - { name: 'tokenId', type: 'Uint256' }, + getSelfArg(), + { name: 'from', type: 'ContractAddress' }, + { name: 'to', type: 'ContractAddress' }, + { name: 'token_id', type: 'u256' }, ], - returns: [{ name: 'tokenURI', type: 'felt' }], - passthrough: 'strict', - parentFunctionName: 'token_uri', + code: [ + 'self.erc721.transfer_from(from, to, token_id)' + ] }, - - // --- external functions --- - approve: { - module: modules.ERC721, - kind: 'external', - implicitArgs: withImplicitArgs(), args: [ - { name: 'to', type: 'felt' }, - { name: 'tokenId', type: 'Uint256' }, + getSelfArg(), + { name: 'to', type: 'ContractAddress' }, + { name: 'token_id', type: 'u256' }, ], + code: [ + 'self.erc721.approve(to, token_id)' + ] }, - - setApprovalForAll: { - module: modules.ERC721, - kind: 'external', - implicitArgs: withImplicitArgs(), + set_approval_for_all: { args: [ - { name: 'operator', type: 'felt' }, - { name: 'approved', type: 'felt' }, + getSelfArg(), + { name: 'operator', type: 'ContractAddress' }, + { name: 'approved', type: 'bool' }, ], - parentFunctionName: 'set_approval_for_all', + code: [ + 'self.erc721.set_approval_for_all(operator, approved)' + ] }, - - transferFrom: { - module: modules.ERC721, - kind: 'external', - implicitArgs: withImplicitArgs(), + get_approved: { args: [ - { name: 'from_', type: 'felt' }, - { name: 'to', type: 'felt' }, - { name: 'tokenId', type: 'Uint256' }, + getSelfArg('view'), + { name: 'token_id', type: 'u256' }, ], - parentFunctionName: 'transfer_from', + returns: 'ContractAddress', + code: [ + 'self.erc721.get_approved(token_id)' + ] + }, + is_approved_for_all: { + args: [ + getSelfArg('view'), + { name: 'owner', type: 'ContractAddress' }, + { name: 'operator', type: 'ContractAddress' }, + ], + returns: 'bool', + code: [ + 'self.erc721.is_approved_for_all(owner, operator)' + ] }, + // Re-implements ERC721CamelOnlyImpl + + balanceOf: { + args: [ + getSelfArg('view'), + { name: 'account', type: 'ContractAddress' }, + ], + returns: 'u256', + code: [ + 'self.balance_of(account)' + ] + }, + ownerOf: { + args: [ + getSelfArg('view'), + { name: 'tokenId', type: 'u256' }, + ], + returns: 'ContractAddress', + code: [ + 'self.owner_of(tokenId)' + ] + }, safeTransferFrom: { - module: modules.ERC721, - kind: 'external', - implicitArgs: withImplicitArgs(), args: [ - { name: 'from_', type: 'felt' }, - { name: 'to', type: 'felt' }, - { name: 'tokenId', type: 'Uint256' }, - { name: 'data_len', type: 'felt' }, - { name: 'data', type: 'felt*' }, + getSelfArg(), + { name: 'from', type: 'ContractAddress' }, + { name: 'to', type: 'ContractAddress' }, + { name: 'tokenId', type: 'u256' }, + { name: 'data', type: 'Span' }, ], - parentFunctionName: 'safe_transfer_from', + code: [ + 'self.safe_transfer_from(from, to, tokenId, data)' + ] }, - - safeMint: { - module: modules.ERC721, - kind: 'external', - implicitArgs: withImplicitArgs(), + transferFrom: { args: [ - { name: 'to', type: 'felt' }, - { name: 'tokenId', type: 'Uint256' }, - { name: 'data_len', type: 'felt' }, - { name: 'data', type: 'felt*' }, - { name: 'tokenURI', type: 'felt' }, + getSelfArg(), + { name: 'from', type: 'ContractAddress' }, + { name: 'to', type: 'ContractAddress' }, + { name: 'tokenId', type: 'u256' }, ], - parentFunctionName: '_safe_mint', + code: [ + 'self.transfer_from(from, to, tokenId)' + ] }, - - burn: { - module: modules.ERC721, - kind: 'external', - implicitArgs: withImplicitArgs(), + setApprovalForAll: { args: [ - { name: 'tokenId', type: 'Uint256' }, + getSelfArg(), + { name: 'operator', type: 'ContractAddress' }, + { name: 'approved', type: 'bool' }, ], + code: [ + 'self.set_approval_for_all(operator, approved)' + ] }, - -}); \ No newline at end of file + getApproved: { + args: [ + getSelfArg('view'), + { name: 'tokenId', type: 'u256' }, + ], + returns: 'ContractAddress', + code: [ + 'self.get_approved(tokenId)' + ] + }, + isApprovedForAll: { + args: [ + getSelfArg('view'), + { name: 'owner', type: 'ContractAddress' }, + { name: 'operator', type: 'ContractAddress' }, + ], + returns: 'bool', + code: [ + 'self.is_approved_for_all(owner, operator)' + ] + }, +}); diff --git a/packages/core-cairo/src/external-trait.ts b/packages/core-cairo/src/external-trait.ts new file mode 100644 index 00000000..4645d78e --- /dev/null +++ b/packages/core-cairo/src/external-trait.ts @@ -0,0 +1,10 @@ +import type { BaseImplementedTrait } from "./contract"; + +export const externalTrait: BaseImplementedTrait = { + name: 'ExternalImpl', + of: 'ExternalTrait', + tags: [ + '#[generate_trait]', + '#[external(v0)]' + ], +} diff --git a/packages/core-cairo/src/generate/alternatives.ts b/packages/core-cairo/src/generate/alternatives.ts index e2660353..afd89ab6 100644 --- a/packages/core-cairo/src/generate/alternatives.ts +++ b/packages/core-cairo/src/generate/alternatives.ts @@ -1,5 +1,3 @@ -import { mapValues } from "../utils/map-values"; - type Blueprint = Record; type Alternatives = { diff --git a/packages/core-cairo/src/generate/erc20.ts b/packages/core-cairo/src/generate/erc20.ts index 50a80305..5621199e 100644 --- a/packages/core-cairo/src/generate/erc20.ts +++ b/packages/core-cairo/src/generate/erc20.ts @@ -10,17 +10,13 @@ const blueprint = { name: ['MyToken'], symbol: ['MTK'], burnable: booleans, - snapshots: booleans, pausable: booleans, mintable: booleans, - permit: booleans, - votes: booleans, - flashmint: booleans, + safeAllowance: booleans, premint: ['1'], access: accessOptions, upgradeable: upgradeableOptions, - info: infoOptions, - decimals: ['18'] + info: infoOptions }; export function* generateERC20Options(): Generator> { diff --git a/packages/core-cairo/src/generate/erc721.ts b/packages/core-cairo/src/generate/erc721.ts index 2d60c848..c420495f 100644 --- a/packages/core-cairo/src/generate/erc721.ts +++ b/packages/core-cairo/src/generate/erc721.ts @@ -9,13 +9,9 @@ const booleans = [true, false]; const blueprint = { name: ['MyToken'], symbol: ['MTK'], - baseUri: ['https://example.com/'], - enumerable: booleans, - uriStorage: booleans, burnable: booleans, pausable: booleans, mintable: booleans, - incremental: booleans, access: accessOptions, upgradeable: upgradeableOptions, info: infoOptions, diff --git a/packages/core-cairo/src/generate/sources.ts b/packages/core-cairo/src/generate/sources.ts index 319ea77f..2846be40 100644 --- a/packages/core-cairo/src/generate/sources.ts +++ b/packages/core-cairo/src/generate/sources.ts @@ -62,7 +62,7 @@ function generateContractSubset(subset: Subset): GeneratedContract[] { if (subset === 'all') { return contracts; } else { - const getParents = (c: GeneratedContract) => c.contract.libraries.map(p => p.module.path); + const getParents = (c: GeneratedContract) => c.contract.components.map(p => p.path); return [ ...findCover(contracts.filter(c => c.options.upgradeable), getParents), ...findCover(contracts.filter(c => !c.options.upgradeable), getParents), diff --git a/packages/core-cairo/src/index.ts b/packages/core-cairo/src/index.ts index 4d386280..ea2f8341 100644 --- a/packages/core-cairo/src/index.ts +++ b/packages/core-cairo/src/index.ts @@ -22,4 +22,4 @@ export { sanitizeKind } from './kind'; export { contractsVersion, contractsVersionTag } from './utils/version'; -export { erc20, erc721, erc1155, custom, utils } from './api'; +export { erc20, erc721, custom } from './api'; diff --git a/packages/core-cairo/src/kind.ts b/packages/core-cairo/src/kind.ts index 30544f33..8f816e99 100644 --- a/packages/core-cairo/src/kind.ts +++ b/packages/core-cairo/src/kind.ts @@ -16,7 +16,6 @@ function isKind(value: Kind | T): value is Kind { switch (value) { case 'ERC20': case 'ERC721': - case 'ERC1155': case 'Custom': return true; diff --git a/packages/core-cairo/src/options.ts b/packages/core-cairo/src/options.ts deleted file mode 100644 index 67e7b2e9..00000000 --- a/packages/core-cairo/src/options.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { Contract } from './contract'; - -export interface Helpers { - upgradeable: boolean; -} - -export function withHelpers(contract: Contract): Helpers { - const upgradeable = contract.upgradeable; - return { - upgradeable - }; -} diff --git a/packages/core-cairo/src/print.ts b/packages/core-cairo/src/print.ts index 8985b4e4..37e370b7 100644 --- a/packages/core-cairo/src/print.ts +++ b/packages/core-cairo/src/print.ts @@ -1,62 +1,31 @@ import 'array.prototype.flatmap/auto'; -import type { Contract, Library, ContractFunction, Argument, Value, } from './contract'; -import { Helpers, withHelpers } from './options'; +import type { Contract, Component, Argument, Value, Impl, ContractFunction, } from './contract'; import { formatLines, spaceBetween, Lines } from './utils/format-lines'; -import { getImportsMap } from './utils/imports-map'; -import { mapValues } from './utils/map-values'; -import { getFunctionName } from './utils/module-prefix'; +import { getSelfArg } from './common-options'; export function printContract(contract: Contract): string { - const helpers = withHelpers(contract); - - const fns = mapValues( - sortedFunctions(contract), - fns => fns.map(fn => printFunction(fn)), - ); - - const hasViews = fns.views.some(l => l.length > 0); - const hasExternals = fns.externals.some(l => l.length > 0); - - const { starkwareImports, ozImports } = printImports(contract); - return formatLines( ...spaceBetween( [ `// SPDX-License-Identifier: ${contract.license}`, ], - - [ - `%lang starknet` - ], - + printSuperVariables(contract), [ - ...starkwareImports, + `#[starknet::contract]`, + `mod ${contract.name} {`, + spaceBetween( + printImports(contract), + printComponentDeclarations(contract), + printImpls(contract), + printStorage(contract), + printEvents(contract), + printConstructor(contract), + printImplementedTraits(contract), + ), + `}`, ], - - ozImports, - - spaceBetween( - contract.variables, - printConstructor(contract, helpers), - ...fns.code, - ...fns.modifiers, - hasViews ? - [ - `//`, - `// Getters`, - `//` - ] : [], - ...fns.views, - hasExternals ? - [ - `//`, - `// Externals`, - `//` - ] : [], - ...fns.externals, - ), ), ); } @@ -65,53 +34,145 @@ function withSemicolons(lines: string[]): string[] { return lines.map(line => line.endsWith(';') ? line : line + ';'); } -function printImports(contract: Contract) { - const modulesToLibraryFunctions = getImportsMap(contract); - const { starkwareImportsMap, ozImportsMap } = getVendoredImports(modulesToLibraryFunctions); +function printSuperVariables(contract: Contract) { + return withSemicolons(contract.superVariables.map(v => `const ${v.name}: ${v.type} = ${v.value}`)); +} - const starkwareImports = printImportLines(starkwareImportsMap); - const ozImports = printImportLines(ozImportsMap); - return { starkwareImports, ozImports }; +function printImports(contract: Contract) { + const lines: string[] = []; + getCategorizedImports(contract) + .forEach(category => category.forEach(i => lines.push(`use ${i}`))); + return withSemicolons(lines); } -function getVendoredImports(parentImportsMap: Map>) { - const starkwareImportsMap: Map> = new Map>(); - const ozImportsMap: Map> = new Map>(); - for (let [key, value] of parentImportsMap) { - if (key.startsWith('starkware')) { - starkwareImportsMap.set(key, value); +function getCategorizedImports(contract: Contract) { + const componentImports = contract.components.flatMap(c => `${c.path}::${c.name}`); + const combined = componentImports.concat(contract.standaloneImports); + + const ozTokenImports = []; + const ozImports = []; + const otherImports = []; + const superImports = []; + + for (const importStatement of combined) { + if (importStatement.startsWith('openzeppelin::token')) { + ozTokenImports.push(importStatement); + } else if (importStatement.startsWith('openzeppelin')) { + ozImports.push(importStatement); } else { - ozImportsMap.set(key, value); + otherImports.push(importStatement); } } - return { starkwareImportsMap, ozImportsMap }; + if (contract.superVariables.length > 0) { + superImports.push(`super::{${contract.superVariables.map(v => v.name).join(', ')}}`); + } + return [ ozTokenImports, ozImports, otherImports, superImports ]; +} + +function printComponentDeclarations(contract: Contract) { + const lines = []; + for (const component of contract.components) { + lines.push(`component!(path: ${component.name}, storage: ${component.substorage.name}, event: ${component.event.name});`); + } + return lines; +} + +function printImpls(contract: Contract) { + const externalImpls = contract.components.flatMap(c => c.impls); + const internalImpls = contract.components.flatMap(c => c.internalImpl ? [c.internalImpl] : []); + + return spaceBetween( + externalImpls.flatMap(impl => printImpl(impl)), + internalImpls.flatMap(impl => printImpl(impl, true)) + ); +} + +function printImpl(impl: Impl, internal = false) { + const lines = []; + if (!internal) { + lines.push('#[abi(embed_v0)]'); + } + lines.push(`impl ${impl.name} = ${impl.value};`); + return lines; +} + +function printStorage(contract: Contract) { + const lines = []; + // storage is required regardless of whether there are components + lines.push('#[storage]'); + lines.push('struct Storage {'); + const storageLines = []; + for (const component of contract.components) { + storageLines.push(`#[substorage(v0)]`); + storageLines.push(`${component.substorage.name}: ${component.substorage.type},`); + } + lines.push(storageLines); + lines.push('}'); + return lines; } -function printImportLines(importStatements: Map>) { +function printEvents(contract: Contract) { const lines = []; - for (const [module, fns] of importStatements.entries()) { - if (fns.size > 1) { - lines.push(`from ${module} import (`); - lines.push(Array.from(fns).map(p => `${p},`)); - lines.push(`)`); - } else if (fns.size === 1) { - lines.push(`from ${module} import ${Array.from(fns)[0]}`); + if (contract.components.length > 0) { + lines.push('#[event]'); + lines.push('#[derive(Drop, starknet::Event)]'); + lines.push('enum Event {') + const eventLines = []; + for (const component of contract.components) { + eventLines.push('#[flat]'); + eventLines.push(`${component.event.name}: ${component.event.type},`); } + lines.push(eventLines); + lines.push('}'); } return lines; } -function printConstructor(contract: Contract, helpers: Helpers): Lines[] { - const hasParentParams = contract.libraries.some(p => p.initializer !== undefined && p.initializer.params.length > 0); +function printImplementedTraits(contract: Contract) { + const impls = []; + for (const trait of contract.implementedTraits) { + const implLines = []; + implLines.push(...trait.tags.map(t => `${t}`)); + implLines.push(`impl ${trait.name} of ${trait.of} {`); + const fns = trait.functions.map(fn => printFunction(fn)); + implLines.push(spaceBetween(...fns)); + implLines.push('}'); + impls.push(implLines); + } + return spaceBetween(...impls); +} + +function printFunction(fn: ContractFunction) { + const head = `fn ${fn.name}`; + const args = fn.args.map(a => printArgument(a)); + + const codeLines = fn.codeBefore?.concat(fn.code) ?? fn.code; + for (let i = 0; i < codeLines.length; i++) { + const line = codeLines[i]; + const shouldEndWithSemicolon = i < codeLines.length - 1 || fn.returns === undefined; + if (line !== undefined) { + if (shouldEndWithSemicolon && !line.endsWith(';')) { + codeLines[i] += ';'; + } else if (!shouldEndWithSemicolon && line.endsWith(';')) { + codeLines[i] = line.slice(0, line.length - 1); + } + } + } + + return printFunction2(head, args, undefined, fn.returns, undefined, codeLines); +} + +function printConstructor(contract: Contract): Lines[] { + const hasParentParams = contract.components.some(p => p.initializer !== undefined && p.initializer.params.length > 0); const hasConstructorCode = contract.constructorCode.length > 0; if (hasParentParams || hasConstructorCode) { - const parents = contract.libraries + const parents = contract.components .filter(hasInitializer) .flatMap(p => printParentConstructor(p)); - const modifier = helpers.upgradeable ? 'external' : 'constructor'; - const head = helpers.upgradeable ? 'func initializer' : 'func constructor'; - const args = contract.constructorArgs.map(a => printArgument(a)); - const implicitArgs = contract.constructorImplicitArgs?.map(a => printArgument(a)); + const tag = 'constructor'; + const head = 'fn constructor'; + const args = [ getSelfArg(), ...contract.constructorArgs ]; + const body = spaceBetween( withSemicolons(parents), withSemicolons(contract.constructorCode), @@ -119,11 +180,10 @@ function printConstructor(contract: Contract, helpers: Helpers): Lines[] { const constructor = printFunction2( head, - implicitArgs ?? [], - args, - modifier, + args.map(a => printArgument(a)), + tag, + undefined, undefined, - 'return ();', body, ); return constructor; @@ -132,33 +192,15 @@ function printConstructor(contract: Contract, helpers: Helpers): Lines[] { } } -function hasInitializer(parent: Library) { - return parent.initializer !== undefined && parent.module.name !== undefined; +function hasInitializer(parent: Component) { + return parent.initializer !== undefined && parent.substorage?.name !== undefined; } -type SortedFunctions = Record<'code' | 'modifiers' | 'views' | 'externals', ContractFunction[]>; - -function sortedFunctions(contract: Contract): SortedFunctions { - const fns: SortedFunctions = { code: [], modifiers: [], views: [], externals: [] }; - - for (const fn of contract.functions) { - if (fn.kind === undefined && fn.code.length > 0) { // fallback case, not sure if anything fits in this category - fns.code.push(fn); - } else if (fn.kind === 'view') { - fns.views.push(fn); - } else { - fns.externals.push(fn); - } - } - - return fns; -} - -function printParentConstructor({ module, initializer }: Library): [] | [string] { - if (initializer === undefined || module.name === undefined || !module.useNamespace) { +function printParentConstructor({ substorage, initializer }: Component): [] | [string] { + if (initializer === undefined || substorage?.name === undefined) { return []; } - const fn = `${module.name}.initializer`; + const fn = `self.${substorage.name}.initializer`; return [ fn + '(' + initializer.params.map(printValue).join(', ') + ')', ]; @@ -184,80 +226,34 @@ export function printValue(value: Value): string { } } -function printFunction(fn: ContractFunction): Lines[] { - const code = []; - - const returnArgs = fn.returns?.map(a => typeof a === 'string' ? a : a.name); - - fn.libraryCalls.forEach(libraryCall => { - const libraryCallString = `${getFunctionName(libraryCall.callFn)}(${libraryCall.args.join(', ')})`; - code.push(libraryCallString); - }); - - let returnLine = 'return ();'; - - if (!fn.final && fn.module !== undefined) { - const fnName = getFunctionName(fn); - const parentFunctionCall = fn.read ? - `${fnName}.read()` : - `${fnName}(${fn.args.map(a => a.name).join(', ')})`; - if (!fn.passthrough || returnArgs === undefined || returnArgs.length === 0) { - code.push(parentFunctionCall); - } else if (fn.passthrough === 'strict') { - code.push(`let (${returnArgs}) = ${parentFunctionCall}`); - const namedReturnVars = returnArgs.map(v => `${v}=${v}`).join(', '); - returnLine = `return (${namedReturnVars});`; - } else if (fn.passthrough === true) { - returnLine = `return ${parentFunctionCall};`; - } - } - - code.push(...fn.code); - - return printFunction2( - 'func ' + fn.name, - fn.implicitArgs?.map(a => printArgument(a)) ?? [], - fn.args.map(a => printArgument(a)), - fn.kind, - fn.returns?.map(a => typeof a === 'string' ? a : printArgument(a)), - returnLine, - withSemicolons(code), - ); -} - // generic for functions and constructors -// kindedName = 'func foo' -function printFunction2(kindedName: string, implicitArgs: string[], args: string[], kind: string | undefined, returns: string[] | undefined, returnLine: string, code: Lines[]): Lines[] { +// kindedName = 'fn foo' +function printFunction2(kindedName: string, args: string[], tag: string | undefined, returns: string | undefined, returnLine: string | undefined, code: Lines[]): Lines[] { const fn = []; - if (kind !== undefined) { - fn.push(`@${kind}`); + if (tag !== undefined) { + fn.push(`#[${tag}]`); } - let accum = kindedName; + let accum = `${kindedName}(`; - if (implicitArgs.length > 0) { - accum += '{' + implicitArgs.join(', ') + '}'; - } - if (args.length > 0) { - fn.push(`${accum}(`); let formattedArgs = args.join(', '); if (formattedArgs.length > 80) { + fn.push(accum); + accum = ''; // print each arg in a separate line fn.push(args.map(arg => `${arg},`)); } else { - fn.push([formattedArgs]); + accum += `${formattedArgs}`; } - accum = ')'; - } else { - accum += '()'; } + accum += ')'; if (returns === undefined) { accum += ' {'; } else { - accum += ` -> (${returns.join(', ')}) {`; + accum += ` -> ${returns} {`; } fn.push(accum); diff --git a/packages/core-cairo/src/set-access-control.ts b/packages/core-cairo/src/set-access-control.ts index ed2d321d..26be1e80 100644 --- a/packages/core-cairo/src/set-access-control.ts +++ b/packages/core-cairo/src/set-access-control.ts @@ -1,9 +1,6 @@ -import { withImplicitArgs } from './common-options'; -import type { ContractBuilder, BaseFunction } from './contract'; -import { defineFunctions } from './utils/define-functions'; -import { defineModules } from './utils/define-modules'; -import { keccak256 } from 'ethereum-cryptography/keccak'; -import { utf8ToBytes, bytesToHex } from 'ethereum-cryptography/utils'; +import type { BaseFunction, BaseImplementedTrait, ContractBuilder } from './contract'; +import { defineComponents } from './utils/define-components'; +import { addSRC5Component } from './common-components'; export const accessOptions = [false, 'ownable', 'roles'] as const; @@ -15,26 +12,22 @@ export type Access = typeof accessOptions[number]; export function setAccessControl(c: ContractBuilder, access: Access) { switch (access) { case 'ownable': { - c.addModule(modules.Ownable, [{ lit:'owner' }], [], true); - c.addConstructorArgument({ name: 'owner', type: 'felt'}); - - c.addFunction(functions.owner); - c.addFunction(functions.transferOwnership); - c.addFunction(functions.renounceOwnership); + c.addComponent(components.OwnableComponent, [{ lit:'owner' }], true); + + c.addStandaloneImport('starknet::ContractAddress'); + c.addConstructorArgument({ name: 'owner', type: 'ContractAddress'}); + break; } case 'roles': { - if (c.addModule(modules.AccessControl)) { - importDefaultAdminRole(c); + if (c.addComponent(components.AccessControlComponent, [], true)) { + addSRC5Component(c); - c.addConstructorArgument({ name: 'admin', type: 'felt'}); - c.addConstructorCode('AccessControl._grant_role(DEFAULT_ADMIN_ROLE, admin)'); + c.addStandaloneImport('starknet::ContractAddress'); + c.addConstructorArgument({ name: 'default_admin', type: 'ContractAddress'}); - c.addFunction(functions.hasRole); - c.addFunction(functions.getRoleAdmin); - c.addFunction(functions.grantRole); - c.addFunction(functions.revokeRole); - c.addFunction(functions.renounceRole); + c.addStandaloneImport('openzeppelin::access::accesscontrol::DEFAULT_ADMIN_ROLE'); + c.addConstructorCode('self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, default_admin)'); } break; } @@ -44,7 +37,7 @@ export type Access = typeof accessOptions[number]; /** * Enables access control for the contract and restricts the given function with access control. */ -export function requireAccessControl(c: ContractBuilder, fn: BaseFunction, access: Access, role: string) { +export function requireAccessControl(c: ContractBuilder, trait: BaseImplementedTrait, fn: BaseFunction, access: Access, roleIdPrefix: string, roleOwner: string | undefined) { if (access === false) { access = 'ownable'; } @@ -53,147 +46,76 @@ export function requireAccessControl(c: ContractBuilder, fn: BaseFunction, acces switch (access) { case 'ownable': { - c.addLibraryCall(functions.assert_only_owner, fn); + c.addFunctionCodeBefore(trait, fn, 'self.ownable.assert_only_owner()'); break; } case 'roles': { - const roleId = role + '_ROLE'; - if (c.addVariable(`const ${roleId} = ${to251BitHash(roleId)}; // keccak256('${roleId}')[0:251 bits]`)) { - c.addConstructorCode(`AccessControl._grant_role(${roleId}, admin)`); + const roleId = roleIdPrefix + '_ROLE'; + const addedSuper = c.addSuperVariable({ name: roleId, type: 'felt252', value: `selector!("${roleId}")` }) + if (roleOwner !== undefined) { + c.addStandaloneImport('starknet::ContractAddress'); + c.addConstructorArgument({ name: roleOwner, type: 'ContractAddress'}); + if (addedSuper) { + c.addConstructorCode(`self.accesscontrol._grant_role(${roleId}, ${roleOwner})`); + } } - c.addLibraryCall(functions.assert_only_role, fn, [roleId]); + c.addFunctionCodeBefore(trait, fn, `self.accesscontrol.assert_only_role(${roleId})`); + break; } } } -export function to251BitHash(label: string): string { - const hash = bytesToHex(keccak256(utf8ToBytes(label))); - const bin = BigInt('0x' + hash).toString(2).substring(0, 251); - const hex = BigInt('0b' + bin).toString(16); - return '0x' + hex; -} - -function importDefaultAdminRole(c: ContractBuilder) { - c.addModule(modules.constants, [], [], false); - c.addModuleFunction(modules.constants, 'DEFAULT_ADMIN_ROLE'); -} - -const modules = defineModules( { - Ownable: { - path: 'openzeppelin.access.ownable.library', - useNamespace: true - }, - AccessControl: { - path: 'openzeppelin.access.accesscontrol.library', - useNamespace: true - }, - constants: { - path: 'openzeppelin.utils.constants.library', - useNamespace: false - } -}) - -const functions = defineFunctions({ - // --- library-only calls --- - assert_only_owner: { - module: modules.Ownable, - args: [], - }, - - assert_only_role: { - module: modules.AccessControl, - args: [] - }, - - // --- view functions --- - - owner: { - module: modules.Ownable, - kind: 'view', - implicitArgs: withImplicitArgs(), - args: [], - returns: [{ name: 'owner', type: 'felt' }], - passthrough: true, - }, - - // --- external functions --- - - transferOwnership: { - module: modules.Ownable, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'newOwner', type: 'felt' }, - ], - parentFunctionName: 'transfer_ownership', - }, - - renounceOwnership: { - module: modules.Ownable, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [], - parentFunctionName: 'renounce_ownership', - }, - - hasRole: { - module: modules.AccessControl, - kind: 'view', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'role', type: 'felt' }, - { name: 'user', type: 'felt' }, - ], - parentFunctionName: 'has_role', - returns: [{ name: 'has_role', type: 'felt' }], - passthrough: true, - }, - - getRoleAdmin: { - module: modules.AccessControl, - kind: 'view', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'role', type: 'felt' }, +const components = defineComponents( { + OwnableComponent: { + path: 'openzeppelin::access::ownable', + substorage: { + name: 'ownable', + type: 'OwnableComponent::Storage', + }, + event: { + name: 'OwnableEvent', + type: 'OwnableComponent::Event', + }, + impls: [ + { + name: 'OwnableImpl', + value: 'OwnableComponent::OwnableImpl', + }, + { + name: 'OwnableCamelOnlyImpl', + value: 'OwnableComponent::OwnableCamelOnlyImpl', + }, ], - parentFunctionName: 'get_role_admin', - returns: [{ name: 'admin', type: 'felt' }], - passthrough: true, + internalImpl: { + name: 'OwnableInternalImpl', + value: 'OwnableComponent::InternalImpl', + }, }, - - grantRole: { - module: modules.AccessControl, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'role', type: 'felt' }, - { name: 'user', type: 'felt' }, - ], - parentFunctionName: 'grant_role', - }, - - revokeRole: { - module: modules.AccessControl, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'role', type: 'felt' }, - { name: 'user', type: 'felt' }, + AccessControlComponent: { + path: 'openzeppelin::access::accesscontrol', + substorage: { + name: 'accesscontrol', + type: 'AccessControlComponent::Storage', + }, + event: { + name: 'AccessControlEvent', + type: 'AccessControlComponent::Event', + }, + impls: [ + { + name: 'AccessControlImpl', + value: 'AccessControlComponent::AccessControlImpl', + }, + { + name: 'AccessControlCamelImpl', + value: 'AccessControlComponent::AccessControlCamelImpl', + }, ], - parentFunctionName: 'revoke_role', + internalImpl: { + name: 'AccessControlInternalImpl', + value: 'AccessControlComponent::InternalImpl', + }, }, - - renounceRole: { - module: modules.AccessControl, - kind: 'external', - implicitArgs: withImplicitArgs(), - args: [ - { name: 'role', type: 'felt' }, - { name: 'user', type: 'felt' }, - ], - parentFunctionName: 'renounce_role', - }, - -}); \ No newline at end of file +}); diff --git a/packages/core-cairo/src/set-upgradeable.ts b/packages/core-cairo/src/set-upgradeable.ts index 10f9be74..a77cd53f 100644 --- a/packages/core-cairo/src/set-upgradeable.ts +++ b/packages/core-cairo/src/set-upgradeable.ts @@ -1,46 +1,63 @@ -import { withImplicitArgs } from './common-options'; -import type { ContractBuilder } from './contract'; -//import { Access, setAccessControl } from './set-access-control'; +import { getSelfArg } from './common-options'; +import type { BaseImplementedTrait, ContractBuilder } from './contract'; +import { Access, requireAccessControl } from './set-access-control'; +import { defineComponents } from './utils/define-components'; import { defineFunctions } from './utils/define-functions'; -import { defineModules } from './utils/define-modules'; export const upgradeableOptions = [false, true] as const; export type Upgradeable = typeof upgradeableOptions[number]; -export function setUpgradeable(c: ContractBuilder, upgradeable: Upgradeable) { +export function setUpgradeable(c: ContractBuilder, upgradeable: Upgradeable, access: Access) { if (upgradeable === false) { return; } c.upgradeable = true; - c.addModule(modules.Proxy, [ {lit: 'proxy_admin' }], [], true); + c.addComponent(components.UpgradeableComponent, [], false); - c.addConstructorArgument({ name:'proxy_admin', type:'felt' }); + c.addStandaloneImport('openzeppelin::upgrades::interface::IUpgradeable'); + c.addStandaloneImport('starknet::ClassHash'); - c.setFunctionBody([ - 'Proxy.assert_only_admin()', - 'Proxy._set_implementation_hash(new_implementation)' - ], functions.upgrade); + const t: BaseImplementedTrait = { + name: 'UpgradeableImpl', + of: 'IUpgradeable', + tags: [ + '#[external(v0)]' + ], + }; + c.addImplementedTrait(t); + requireAccessControl(c, t, functions.upgrade, access, 'UPGRADER', 'upgrader'); } -const modules = defineModules( { - Proxy: { - path: 'openzeppelin.upgrades.library', - useNamespace: true +const components = defineComponents( { + UpgradeableComponent: { + path: 'openzeppelin::upgrades', + substorage: { + name: 'upgradeable', + type: 'UpgradeableComponent::Storage', + }, + event: { + name: 'UpgradeableEvent', + type: 'UpgradeableComponent::Event', + }, + impls: [], + internalImpl: { + name: 'UpgradeableInternalImpl', + value: 'UpgradeableComponent::InternalImpl', + }, }, }); const functions = defineFunctions({ - upgrade: { - kind: 'external', - implicitArgs: withImplicitArgs(), args: [ - { name: 'new_implementation', type: 'felt' }, + getSelfArg(), + { name: 'new_class_hash', type: 'ClassHash' }, ], - returns: [], + code: [ + 'self.upgradeable._upgrade(new_class_hash)' + ] }, - }); diff --git a/packages/core-cairo/src/test.ts b/packages/core-cairo/src/test.ts index 0fef5a08..723fe109 100644 --- a/packages/core-cairo/src/test.ts +++ b/packages/core-cairo/src/test.ts @@ -19,7 +19,7 @@ function isAccessControlRequired(opts: GenericOptions) { test('is access control required', async t => { for (const contract of generateSources('all')) { - const regexOwnable = /(from openzeppelin.access.ownable.library import Ownable)/gm; + const regexOwnable = /(use openzeppelin::access::ownable::OwnableComponent)/gm; if (!contract.options.access) { if (isAccessControlRequired(contract.options)) { diff --git a/packages/core-cairo/src/utils/convert-strings.test.ts b/packages/core-cairo/src/utils/convert-strings.test.ts new file mode 100644 index 00000000..75aeb7a2 --- /dev/null +++ b/packages/core-cairo/src/utils/convert-strings.test.ts @@ -0,0 +1,70 @@ +import test from 'ava'; + +import { toIdentifier, toShortString } from './convert-strings'; +import type { OptionsError } from '../error'; + +test('identifier - unmodified', t => { + t.is(toIdentifier('abc'), 'abc'); +}); + +test('identifier - remove leading specials', t => { + t.is(toIdentifier('--abc'), 'abc'); +}); + +test('identifier - remove specials and upcase next char', t => { + t.is(toIdentifier('abc-def'), 'abcDef'); + t.is(toIdentifier('abc--def'), 'abcDef'); +}); + +test('identifier - capitalize', t => { + t.is(toIdentifier('abc', true), 'Abc'); +}); + +test('identifier - remove accents', t => { + t.is(toIdentifier('ábc'), 'abc'); +}); + +test('identifier - underscores', t => { + t.is(toIdentifier('_abc_'), '_abc_'); +}); + +test('identifier - remove starting numbers', t => { + t.is(toIdentifier('123abc456'), 'abc456'); +}); + +test('identifier - empty string', t => { + let error: OptionsError | undefined = t.throws(() => toIdentifier('')); + t.is(error?.messages.name, 'Identifier is empty or does not have valid characters'); +}); + +test('identifier - no valid chars', t => { + let error: OptionsError | undefined = t.throws(() => toIdentifier('123')); + t.is(error?.messages.name, 'Identifier is empty or does not have valid characters'); +}); + +test('short string - unmodified', t => { + t.is(toShortString('abc', 'foo'), 'abc'); +}); + +test('short string - remove accents', t => { + t.is(toShortString('ábc', 'foo'), 'abc'); +}); + +test('short string - remove non-ascii-printable characters', t => { + t.is(toShortString('abc😀', 'foo'), 'abc'); +}); + +test('short string - escape single quote', t => { + t.is(toShortString("abc'def", 'foo'), "abc\\'def"); +}); + +test('short string - escape backslash', t => { + t.is(toShortString('abc\\def', 'foo'), 'abc\\\\def'); +}); + +test('short string - max 31 characters', t => { + t.is(toShortString('A234567890123456789012345678901', 'foo'), 'A234567890123456789012345678901'); + + let error: OptionsError | undefined = t.throws(() => toShortString('A2345678901234567890123456789012', 'foo')); + t.is(error?.messages.foo, 'String is longer than 31 characters'); +}); \ No newline at end of file diff --git a/packages/core-cairo/src/utils/convert-strings.ts b/packages/core-cairo/src/utils/convert-strings.ts new file mode 100644 index 00000000..e0966e9e --- /dev/null +++ b/packages/core-cairo/src/utils/convert-strings.ts @@ -0,0 +1,38 @@ +import { OptionsError } from "../error"; + +/** + * Converts to an identifier according to the rules in https://docs.cairo-lang.org/language_constructs/identifiers.html + */ +export function toIdentifier(str: string, capitalize = false): string { + const result = str + .normalize('NFD').replace(/[\u0300-\u036f]/g, '') // remove accents + .replace(/^[^a-zA-Z_]+/, '') + .replace(/^(.)/, c => capitalize ? c.toUpperCase() : c) + .replace(/[^\w]+(.?)/g, (_, c) => c.toUpperCase()); + + if (result.length === 0) { + throw new OptionsError({ + name: 'Identifier is empty or does not have valid characters', + }); + } else { + return result; + } +} + +/** + * Converts to a felt252-compatible short string according to the rules in https://docs.cairo-lang.org/language_constructs/literal-expressions.html#short_string_literals + */ +export function toShortString(str: string, field: string): string { + const result = str + .normalize('NFD').replace(/[\u0300-\u036f]/g, '') // remove accents + .replace(/[^\x20-\x7E]+/g, '') // remove non-ascii-printable characters + .replace(/(\\|')/g, (_, c) => '\\' + c); // escape backslash or single quote + + if (result.length > 31) { + throw new OptionsError({ + [field]: 'String is longer than 31 characters', + }); + } else { + return result; + } +} \ No newline at end of file diff --git a/packages/core-cairo/src/utils/define-components.ts b/packages/core-cairo/src/utils/define-components.ts new file mode 100644 index 00000000..62ba3ca6 --- /dev/null +++ b/packages/core-cairo/src/utils/define-components.ts @@ -0,0 +1,18 @@ +import type { Component } from '../contract'; + +type ImplicitNameComponent = Omit; + +export function defineComponents( + fns: Record, +): Record; + +export function defineComponents( + modules: Record, +): Record { + return Object.fromEntries( + Object.entries(modules).map(([name, module]) => [ + name, + Object.assign({ name }, module), + ]), + ); +} diff --git a/packages/core-cairo/src/utils/define-modules.ts b/packages/core-cairo/src/utils/define-modules.ts deleted file mode 100644 index ac84d3eb..00000000 --- a/packages/core-cairo/src/utils/define-modules.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Module } from '../contract'; - -type ImplicitNameModule = Omit; - -export function defineModules( - fns: Record, -): Record; - -export function defineModules( - modules: Record, -): Record { - return Object.fromEntries( - Object.entries(modules).map(([name, module]) => [ - name, - Object.assign({ name }, module), - ]), - ); -} diff --git a/packages/core-cairo/src/utils/hash-builtin.ts b/packages/core-cairo/src/utils/hash-builtin.ts deleted file mode 100644 index bced024e..00000000 --- a/packages/core-cairo/src/utils/hash-builtin.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { ContractBuilder } from "../contract"; -import { defineModules } from "./define-modules"; - -const modules = defineModules( { - cairo_builtins: { - path: 'starkware.cairo.common.cairo_builtins', - useNamespace: false - }, -}) - -export function importHashBuiltin(c: ContractBuilder) { - c.addModule(modules.cairo_builtins, [], [], false); - c.addModuleFunction(modules.cairo_builtins, 'HashBuiltin'); -} \ No newline at end of file diff --git a/packages/core-cairo/src/utils/imports-map.ts b/packages/core-cairo/src/utils/imports-map.ts deleted file mode 100644 index 8d434afd..00000000 --- a/packages/core-cairo/src/utils/imports-map.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { Contract } from "../contract"; -import { getFunctionName, getImportName } from "./module-prefix"; - -export function getImportsMap(contract: Contract) { - const modulesToParentFunctions = getModulesToParentFunctions(contract); - const modulesToLibraryFunctions = getModulesToLibraryFunctions(contract); - mergeToLibraryFunctions(modulesToParentFunctions, modulesToLibraryFunctions); - return modulesToLibraryFunctions; -} - -function mergeToLibraryFunctions(modulesToParentFunctions: Map>, modulesToLibraryFunctions: Map>) { - modulesToParentFunctions.forEach((value, key) => { - const functionsToMerge = modulesToLibraryFunctions.get(key); - if (functionsToMerge !== undefined) { - functionsToMerge.forEach(fn => { value.add(fn) }); - modulesToLibraryFunctions.set(key, value); - } - }); -} - -function getModulesToLibraryFunctions(contract: Contract) { - const modulesToLibraryFunctions: Map> = new Map(); - for (const parent of contract.libraries) { - if (parent.functions !== undefined) { - modulesToLibraryFunctions.set(convertPathToImport(parent.module.path), new Set(parent.functions)); - } - } - return modulesToLibraryFunctions; -} - -function getModulesToParentFunctions(contract: Contract) { - const functionsToModules: Map = new Map(); - for (const fn of contract.functions) { - if (fn.module !== undefined) { - functionsToModules.set(getImportName(fn), convertPathToImport(fn.module.path)); - } - } - const modulesToFunctions = invertMapToSet(functionsToModules); - return modulesToFunctions; -} - -function convertPathToImport(relativePath: any): string { - return relativePath.split('/').join('.'); -} - -function invertMapToSet(functionsToModules: Map): Map> { - const modulesToFunctions: Map> = new Map(); - for (const [functionName, moduleName] of functionsToModules.entries()) { - const moduleFunctions = modulesToFunctions.get(moduleName); - if (moduleFunctions === undefined) { - modulesToFunctions.set(moduleName, new Set().add(functionName)); - } else { - moduleFunctions.add(functionName); - } - } - return modulesToFunctions; -} \ No newline at end of file diff --git a/packages/core-cairo/src/utils/map-values.ts b/packages/core-cairo/src/utils/map-values.ts deleted file mode 100644 index 56d598de..00000000 --- a/packages/core-cairo/src/utils/map-values.ts +++ /dev/null @@ -1,10 +0,0 @@ -export function mapValues( - obj: Record, - fn: (val: V) => W, -): Record { - const res = {} as Record; - for (const key in obj) { - res[key] = fn(obj[key]); - } - return res; -} diff --git a/packages/core-cairo/src/utils/module-prefix.ts b/packages/core-cairo/src/utils/module-prefix.ts deleted file mode 100644 index d58d09e5..00000000 --- a/packages/core-cairo/src/utils/module-prefix.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { BaseFunction } from "../contract"; - -/** - * If the function's module has a namespace, returns the namespace. - * Otherwise returns the function name itself. - */ -export function getImportName(fn: BaseFunction): string { - if (fn.module?.useNamespace) { - return fn.module.name; - } else { - return getFunctionName(fn); - } -} - -/** - * Returns the function name with either namespace or module prefix based on extensibility pattern. - */ -export function getFunctionName(fn: BaseFunction): string { - const suffix = fn.parentFunctionName ?? fn.name; - let prefix: string; - if (fn.module !== undefined && fn.module.useNamespace) { - prefix = `${fn.module.name}.` - } else { - prefix = ''; - } - return `${prefix}${suffix}`; -} \ No newline at end of file diff --git a/packages/core-cairo/src/utils/to-identifier.test.ts b/packages/core-cairo/src/utils/to-identifier.test.ts deleted file mode 100644 index 6af3b3a1..00000000 --- a/packages/core-cairo/src/utils/to-identifier.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import test from 'ava'; - -import { toIdentifier } from './to-identifier'; - -test('unmodified', t => { - t.is(toIdentifier('abc'), 'abc'); -}); - -test('remove leading specials', t => { - t.is(toIdentifier('--abc'), 'abc'); -}); - -test('remove specials and upcase next char', t => { - t.is(toIdentifier('abc-def'), 'abcDef'); - t.is(toIdentifier('abc--def'), 'abcDef'); -}); - -test('capitalize', t => { - t.is(toIdentifier('abc', true), 'Abc'); -}); diff --git a/packages/core-cairo/src/utils/to-identifier.ts b/packages/core-cairo/src/utils/to-identifier.ts deleted file mode 100644 index 2e4a2f9f..00000000 --- a/packages/core-cairo/src/utils/to-identifier.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function toIdentifier(str: string, capitalize = false): string { - return str - .normalize('NFD').replace(/[\u0300-\u036f]/g, '') // remove accents - .replace(/^[^a-zA-Z$_]+/, '') - .replace(/^(.)/, c => capitalize ? c.toUpperCase() : c) - .replace(/[^\w$]+(.?)/g, (_, c) => c.toUpperCase()); -} diff --git a/packages/core-cairo/src/utils/uint256.test.ts b/packages/core-cairo/src/utils/uint256.test.ts deleted file mode 100644 index 6bfcf650..00000000 --- a/packages/core-cairo/src/utils/uint256.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import test from 'ava'; -import BN from 'bn.js'; - -import { NumberTooLarge, toUint256 } from './uint256'; - -test('basic', t => { - t.deepEqual(toUint256('1000'), { lowBits: new BN(1000), highBits: new BN(0) }); - t.deepEqual(toUint256('0'), { lowBits: new BN(0), highBits: new BN(0) }); - t.deepEqual(toUint256(''), { lowBits: new BN(0), highBits: new BN(0) }); -}); - -test('max values', t => { - const twoE128minus1 = toUint256('340282366920938463463374607431768211455'); // 2^128-1 - t.is(twoE128minus1.highBits.toString(), '0'); - t.is(twoE128minus1.lowBits.toString(), '340282366920938463463374607431768211455'); - - const twoE128 = toUint256('340282366920938463463374607431768211456'); // 2^128 - t.is(twoE128.highBits.toString(), '1'); - t.is(twoE128.lowBits.toString(), '0'); - - const twoE128plus1 = toUint256('340282366920938463463374607431768211457'); // 2^128+1 - t.is(twoE128plus1.highBits.toString(), '1'); - t.is(twoE128plus1.lowBits.toString(), '1'); - - const maxValue = toUint256('115792089237316195423570985008687907853269984665640564039457584007913129639935'); // 2^256-1 - t.is(maxValue.highBits.toString(), '340282366920938463463374607431768211455'); // 2^128-1 - t.is(maxValue.lowBits.toString(), '340282366920938463463374607431768211455'); // 2^128-1 - - const error = t.throws(() => toUint256('115792089237316195423570985008687907853269984665640564039457584007913129639936')); // 2^256 - t.assert(error instanceof NumberTooLarge); -}); - diff --git a/packages/core-cairo/src/utils/uint256.ts b/packages/core-cairo/src/utils/uint256.ts deleted file mode 100644 index fabfbbc4..00000000 --- a/packages/core-cairo/src/utils/uint256.ts +++ /dev/null @@ -1,36 +0,0 @@ -import BN from "bn.js"; -import type { ContractBuilder } from "../contract"; -import { defineModules } from "./define-modules"; - -/** - * Returns Uint256 components for low and high bits based on a given number in string format. - * @param num Number in string format - * @returns Object with lowBits and highBits - * @throws {NumberTooLarge} if the provided number is larger than 256 bits - */ -export function toUint256(num: string) { - const bignum = new BN(num, 10); - if (bignum.bitLength() > 256) { // 256 bits - throw new NumberTooLarge(); - } else { - const highBits = bignum.shrn(128); - const lowBits = bignum.maskn(128); - return { - lowBits, highBits - } - } -} - -export class NumberTooLarge extends Error {} - -const modules = defineModules( { - uint256: { - path: 'starkware.cairo.common.uint256', - useNamespace: false - }, -}) - -export function importUint256(c: ContractBuilder) { - c.addModule(modules.uint256, [], [], false); - c.addModuleFunction(modules.uint256, 'Uint256'); -} \ No newline at end of file diff --git a/packages/core-cairo/src/utils/version.ts b/packages/core-cairo/src/utils/version.ts index 7ee0475a..d4e0b24b 100644 --- a/packages/core-cairo/src/utils/version.ts +++ b/packages/core-cairo/src/utils/version.ts @@ -1,2 +1,2 @@ -export const contractsVersion = '0.6.0'; +export const contractsVersion = '0.8.0'; export const contractsVersionTag = `v${contractsVersion}`; diff --git a/packages/core/package.json b/packages/core/package.json index fd262d85..9c3bf5bb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -22,7 +22,7 @@ "devDependencies": { "@openzeppelin/contracts": "^5.0.0", "@openzeppelin/contracts-upgradeable": "^5.0.0", - "@types/node": "^10.17.51", + "@types/node": "^18.0.0", "array.prototype.flat": "^1.2.4", "ava": "^5.0.0", "hardhat": "^2.1.1", diff --git a/packages/ui/package.json b/packages/ui/package.json index 17600a84..d95dc7d9 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -18,6 +18,7 @@ "@types/file-saver": "^2.0.1", "@types/resize-observer-browser": "^0.1.5", "@types/uuid": "^9.0.0", + "@types/node": "^18.0.0", "autoprefixer": "^10.4.2", "path-browserify": "^1.0.1", "postcss": "^8.2.8", diff --git a/packages/ui/src/HelpTooltip.svelte b/packages/ui/src/HelpTooltip.svelte index 12f28ee7..064d8efd 100644 --- a/packages/ui/src/HelpTooltip.svelte +++ b/packages/ui/src/HelpTooltip.svelte @@ -6,7 +6,7 @@ export let placement: 'top' | 'bottom' | 'left' | 'right' = 'right'; - +

Single file

-

Requires installation of Python package (openzeppelin-cairo-contracts).

+

Requires a Scarb project with openzeppelin as a dependency.

@@ -134,13 +133,10 @@
- -
-
- +
- +
diff --git a/packages/ui/src/cairo/CustomControls.svelte b/packages/ui/src/cairo/CustomControls.svelte index 1c21dbff..1770f093 100644 --- a/packages/ui/src/cairo/CustomControls.svelte +++ b/packages/ui/src/cairo/CustomControls.svelte @@ -1,12 +1,13 @@ +
+

Settings

+ + +
+

Features

@@ -25,15 +37,16 @@ Pausable - Privileged accounts will be able to pause the functionality marked with assert_not_paused. + Privileged accounts will be able to pause the functionality marked with self.pausable.assert_not_paused(). Useful for emergency response. + +
+ - - \ No newline at end of file diff --git a/packages/ui/src/cairo/ERC1155Controls.svelte b/packages/ui/src/cairo/ERC1155Controls.svelte deleted file mode 100644 index 802cb0a0..00000000 --- a/packages/ui/src/cairo/ERC1155Controls.svelte +++ /dev/null @@ -1,72 +0,0 @@ - - -
-

Settings

- - -
- -
-

Features

- -
- - - - -
-
- - - - - - diff --git a/packages/ui/src/cairo/ERC20Controls.svelte b/packages/ui/src/cairo/ERC20Controls.svelte index 835260fd..a6e3e0eb 100644 --- a/packages/ui/src/cairo/ERC20Controls.svelte +++ b/packages/ui/src/cairo/ERC20Controls.svelte @@ -5,7 +5,7 @@ import { premintPattern, erc20, infoDefaults } from '@openzeppelin/wizard-cairo'; import AccessControlSection from './AccessControlSection.svelte'; - import UpgradeabilitySection from './UpgradeabilitySection.svelte'; + import UpgradeabilityField from './UpgradeabilityField.svelte'; import InfoSection from './InfoSection.svelte'; import { error } from '../error-tooltip'; @@ -27,22 +27,15 @@
- - + + + + - - diff --git a/packages/ui/src/cairo/ERC721Controls.svelte b/packages/ui/src/cairo/ERC721Controls.svelte index 90aedb63..f66fa34e 100644 --- a/packages/ui/src/cairo/ERC721Controls.svelte +++ b/packages/ui/src/cairo/ERC721Controls.svelte @@ -1,12 +1,13 @@ @@ -23,11 +26,11 @@
@@ -39,14 +42,14 @@ @@ -54,15 +57,14 @@ Pausable - Privileged accounts will be able to pause the functionality marked with assert_not_paused. + Privileged accounts will be able to pause the functionality marked with self.pausable.assert_not_paused(). Useful for emergency response. + - - - + \ No newline at end of file diff --git a/packages/ui/src/cairo/UpgradeabilityField.svelte b/packages/ui/src/cairo/UpgradeabilityField.svelte new file mode 100644 index 00000000..fe97fb42 --- /dev/null +++ b/packages/ui/src/cairo/UpgradeabilityField.svelte @@ -0,0 +1,15 @@ + + + diff --git a/packages/ui/src/cairo/UpgradeabilitySection.svelte b/packages/ui/src/cairo/UpgradeabilitySection.svelte deleted file mode 100644 index 0a073e63..00000000 --- a/packages/ui/src/cairo/UpgradeabilitySection.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - -
-

- - -

- -
- -
-
- diff --git a/packages/ui/src/cairo/highlightjs.ts b/packages/ui/src/cairo/highlightjs.ts index c94c0108..7cff0fef 100644 --- a/packages/ui/src/cairo/highlightjs.ts +++ b/packages/ui/src/cairo/highlightjs.ts @@ -1,7 +1,10 @@ import hljs from 'highlight.js/lib/core'; +import rust from 'highlight.js/lib/languages/rust'; // @ts-ignore -import hljsDefineCairo from 'highlightjs-cairo'; -hljsDefineCairo(hljs); +//import hljsDefineCairo from 'highlightjs-cairo'; +//hljsDefineCairo(hljs); + +hljs.registerLanguage('cairo', rust); export default hljs; diff --git a/packages/ui/src/cairo/inject-hyperlinks.ts b/packages/ui/src/cairo/inject-hyperlinks.ts index b1d0a1c0..abf26095 100644 --- a/packages/ui/src/cairo/inject-hyperlinks.ts +++ b/packages/ui/src/cairo/inject-hyperlinks.ts @@ -1,18 +1,25 @@ import { contractsVersionTag } from "@openzeppelin/wizard-cairo/src"; export function injectHyperlinks(code: string) { - const importRegex = /( )(openzeppelin|starkware)([^\s]*)( )/g + const importRegex = /use<\/span> (openzeppelin)::([^\s]*);/g let result = code; let match = importRegex.exec(code); while (match != null) { - const [line, spaceBefore, libraryPrefix, libraryPath, spaceAfter] = match; - if (line !== undefined && spaceBefore !== undefined && libraryPrefix !== undefined && libraryPath !== undefined && spaceAfter !== undefined) { - const libraryRelativePath = libraryPath.replace(/\./g, '/'); - const githubPrefix = (libraryPrefix === 'openzeppelin') ? - `https://github.com/OpenZeppelin/cairo-contracts/blob/${contractsVersionTag}/src/` : - 'https://github.com/starkware-libs/cairo-lang/blob/master/src/'; - const replacedImportLine = `${spaceBefore}${libraryPrefix}${libraryPath}${spaceAfter}`; - result = result.replace(line, replacedImportLine); + const [line, libraryPrefix, libraryPath] = match; + if (line !== undefined && libraryPrefix !== undefined && libraryPath !== undefined) { + const githubPrefix = `https://github.com/OpenZeppelin/cairo-contracts/blob/${contractsVersionTag}/src/`; + + let libraryPathSegments = libraryPath.split('::'); + + // Remove the component name + if (libraryPathSegments.length > 0 && libraryPathSegments[libraryPathSegments.length - 1] !== 'interface') { + libraryPathSegments.pop(); + } + + if (libraryPathSegments !== undefined && libraryPathSegments.length > 0) { + const replacedImportLine = `use<\/span> ${libraryPrefix}::${libraryPath};`; + result = result.replace(line, replacedImportLine); + } } match = importRegex.exec(code); } diff --git a/yarn.lock b/yarn.lock index df880d28..cf4986ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1038,10 +1038,12 @@ dependencies: undici-types "~5.26.4" -"@types/node@^10.17.51": - version "10.17.60" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" - integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== +"@types/node@^18.0.0": + version "18.19.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.1.tgz#e3ed7d5ab5ea21f33a4503decb2171e0d8f53070" + integrity sha512-mZJ9V11gG5Vp0Ox2oERpeFDl+JvCwK24PGy76vVY/UgBtjwJWc5rYBThFxmbnYOm9UPZNm6wEl/sxHt2SU7x9A== + dependencies: + undici-types "~5.26.4" "@types/parse-json@^4.0.0": version "4.0.2"