From d4f6afb1a0e3cdb5a15195a01a6c38d8c7a70a05 Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 1 Oct 2024 15:22:52 +0200 Subject: [PATCH 01/34] Add functionality for declaring constants within traits --- packages/core-cairo/src/contract.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/core-cairo/src/contract.ts b/packages/core-cairo/src/contract.ts index fa635d00b..1929ad969 100644 --- a/packages/core-cairo/src/contract.ts +++ b/packages/core-cairo/src/contract.ts @@ -57,6 +57,7 @@ export interface BaseImplementedTrait { } export interface ImplementedTrait extends BaseImplementedTrait { + superVariables: Variable[]; functions: ContractFunction[]; } @@ -172,6 +173,7 @@ export class ContractBuilder implements Contract { name: baseTrait.name, of: baseTrait.of, tags: [ ...baseTrait.tags ], + superVariables: [], functions: [], priority: baseTrait.priority, }; @@ -180,6 +182,27 @@ export class ContractBuilder implements Contract { } } + addSuperVariableToTrait(baseTrait: BaseImplementedTrait, newVar: Variable): boolean { + const trait = this.addImplementedTrait(baseTrait); + for (const existingVar of trait.superVariables) { + if (existingVar.name === newVar.name) { + if (existingVar.type !== newVar.type) { + throw new Error( + `Tried to add duplicate super var ${newVar.name} with different type: ${newVar.type} instead of ${existingVar.type}.` + ) + } + if (existingVar.value !== newVar.value) { + throw new Error( + `Tried to add duplicate super var ${newVar.name} with different value: ${newVar.value} instead of ${existingVar.value}.` + ) + } + return false; // No need to add, already exists + } + } + trait.superVariables.push(newVar); + return true; + } + addFunction(baseTrait: BaseImplementedTrait, fn: BaseFunction): ContractFunction { const t = this.addImplementedTrait(baseTrait); From 42bca988acdece21fb9832ebdf5f292a5e7138ff Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 1 Oct 2024 15:23:31 +0200 Subject: [PATCH 02/34] Specify return types for printing functions, support constants for traits --- packages/core-cairo/src/print.ts | 38 ++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/packages/core-cairo/src/print.ts b/packages/core-cairo/src/print.ts index 827919a89..bdfaefeea 100644 --- a/packages/core-cairo/src/print.ts +++ b/packages/core-cairo/src/print.ts @@ -37,17 +37,17 @@ function withSemicolons(lines: string[]): string[] { return lines.map(line => line.endsWith(';') ? line : line + ';'); } -function printSuperVariables(contract: Contract) { +function printSuperVariables(contract: Contract): string[] { return withSemicolons(contract.superVariables.map(v => `const ${v.name}: ${v.type} = ${v.value}`)); } -function printImports(contract: Contract) { +function printImports(contract: Contract): string[] { const lines: string[] = []; sortImports(contract).forEach(i => lines.push(`use ${i}`)); return withSemicolons(lines); } -function sortImports(contract: Contract) { +function sortImports(contract: Contract): string[] { const componentImports = contract.components.flatMap(c => `${c.path}::${c.name}`); const allImports = componentImports.concat(contract.standaloneImports); if (contract.superVariables.length > 0) { @@ -56,7 +56,7 @@ function sortImports(contract: Contract) { return allImports.sort(); } -function printComponentDeclarations(contract: Contract) { +function printComponentDeclarations(contract: Contract): string[] { const lines = []; for (const component of contract.components) { lines.push(`component!(path: ${component.name}, storage: ${component.substorage.name}, event: ${component.event.name});`); @@ -64,7 +64,7 @@ function printComponentDeclarations(contract: Contract) { return lines; } -function printImpls(contract: Contract) { +function printImpls(contract: Contract): Lines[] { const externalImpls = contract.components.flatMap(c => c.impls); const internalImpls = contract.components.flatMap(c => c.internalImpl ? [c.internalImpl] : []); @@ -74,7 +74,7 @@ function printImpls(contract: Contract) { ); } -function printImpl(impl: Impl, internal = false) { +function printImpl(impl: Impl, internal = false): string[] { const lines = []; if (!internal) { lines.push('#[abi(embed_v0)]'); @@ -83,7 +83,7 @@ function printImpl(impl: Impl, internal = false) { return lines; } -function printStorage(contract: Contract) { +function printStorage(contract: Contract): (string | string[])[] { const lines = []; // storage is required regardless of whether there are components lines.push('#[storage]'); @@ -98,7 +98,7 @@ function printStorage(contract: Contract) { return lines; } -function printEvents(contract: Contract) { +function printEvents(contract: Contract): (string | string[])[] { const lines = []; if (contract.components.length > 0) { lines.push('#[event]'); @@ -115,7 +115,7 @@ function printEvents(contract: Contract) { return lines; } -function printImplementedTraits(contract: Contract) { +function printImplementedTraits(contract: Contract): Lines[] { const impls = []; // sort first by priority, then number of tags, then name @@ -133,15 +133,22 @@ function printImplementedTraits(contract: Contract) { const implLines = []; implLines.push(...trait.tags.map(t => `#[${t}]`)); implLines.push(`impl ${trait.name} of ${trait.of} {`); + + const superVars = withSemicolons( + trait.superVariables.map(v => `const ${v.name}: ${v.type} = ${v.value}`) + ); + implLines.push(superVars); + const fns = trait.functions.map(fn => printFunction(fn)); implLines.push(spaceBetween(...fns)); + implLines.push('}'); impls.push(implLines); } return spaceBetween(...impls); } -function printFunction(fn: ContractFunction) { +function printFunction(fn: ContractFunction): Lines[] { const head = `fn ${fn.name}`; const args = fn.args.map(a => printArgument(a)); @@ -191,7 +198,7 @@ function printConstructor(contract: Contract): Lines[] { } } -function hasInitializer(parent: Component) { +function hasInitializer(parent: Component): boolean { return parent.initializer !== undefined && parent.substorage?.name !== undefined; } @@ -227,7 +234,14 @@ export function printValue(value: Value): string { // generic for functions and constructors // kindedName = 'fn foo' -function printFunction2(kindedName: string, args: string[], tag: string | undefined, returns: string | undefined, returnLine: string | undefined, code: Lines[]): Lines[] { +function printFunction2( + kindedName: string, + args: string[], + tag: string | undefined, + returns: string | undefined, + returnLine: string | undefined, + code: Lines[] +): Lines[] { const fn = []; if (tag !== undefined) { From 5df51a272f26da6679623a3f5b65953fcd06c249 Mon Sep 17 00:00:00 2001 From: immrsd Date: Wed, 27 Nov 2024 14:08:14 +0100 Subject: [PATCH 03/34] Support uint Cairo types --- packages/core-cairo/src/contract.ts | 2 +- packages/core-cairo/src/print.ts | 2 + .../core-cairo/src/utils/convert-strings.ts | 48 ++++++++++++++++++- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/packages/core-cairo/src/contract.ts b/packages/core-cairo/src/contract.ts index 1929ad969..e79bf19a7 100644 --- a/packages/core-cairo/src/contract.ts +++ b/packages/core-cairo/src/contract.ts @@ -13,7 +13,7 @@ export interface Contract { superVariables: Variable[]; } -export type Value = string | number | { lit: string } | { note: string, value: Value }; +export type Value = string | number | BigInt | { lit: string } | { note: string, value: Value }; export interface Component { name: string; diff --git a/packages/core-cairo/src/print.ts b/packages/core-cairo/src/print.ts index bdfaefeea..373708ab6 100644 --- a/packages/core-cairo/src/print.ts +++ b/packages/core-cairo/src/print.ts @@ -227,6 +227,8 @@ export function printValue(value: Value): string { } else { throw new Error(`Number not representable (${value})`); } + } else if (typeof value === 'bigint') { + return `${value}` } else { return `"${value}"`; } diff --git a/packages/core-cairo/src/utils/convert-strings.ts b/packages/core-cairo/src/utils/convert-strings.ts index fe8369bc2..478b91bb1 100644 --- a/packages/core-cairo/src/utils/convert-strings.ts +++ b/packages/core-cairo/src/utils/convert-strings.ts @@ -45,4 +45,50 @@ export function toFelt252(str: string, field: string): string { } else { return result; } -} \ No newline at end of file +} + +function maxValueOfUint(bits: number): bigint { + if (bits <= 0) { + throw new Error(`Number of bits must be positive (actual '${bits}').`) + } + if (bits % 8 !== 0) { + throw new Error(`The number of bits must be a multiple of 8 (actual '${bits}').`) + } + const bytes = bits / 8; + return BigInt('0x' + 'ff'.repeat(bytes)) +} + +const UINT_MAX_VALUES = { + 'u8': maxValueOfUint(8), + 'u16': maxValueOfUint(16), + 'u32': maxValueOfUint(32), + 'u64': maxValueOfUint(64), + 'u128': maxValueOfUint(128), + 'u256': maxValueOfUint(256) +} as const; + +export type UintType = keyof typeof UINT_MAX_VALUES; + +/** + * Validates a string value to be a valid uint and converts it to bigint + */ +export function toUint(str: string, field: string, type: UintType): bigint { + const isValidNumber = /^\d+$/.test(str); + if (!isValidNumber) { + throw new OptionsError({ + [field]: 'Not a valid number' + }); + } + const numValue = BigInt(str); + if (numValue < BigInt(0)) { + throw new OptionsError({ + [field]: `${type} value cannot be negative` + }); + } + if (numValue > UINT_MAX_VALUES[type]) { + throw new OptionsError({ + [field]: `Value is greater than ${type} max value` + }); + } + return numValue; +} From 5e3cf957afea0a9b4481d2c6cdb1190464f8edb7 Mon Sep 17 00:00:00 2001 From: immrsd Date: Wed, 27 Nov 2024 14:09:37 +0100 Subject: [PATCH 04/34] Add support for ERC2981 extension --- packages/core-cairo/src/set-royalty-info.ts | 120 ++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 packages/core-cairo/src/set-royalty-info.ts diff --git a/packages/core-cairo/src/set-royalty-info.ts b/packages/core-cairo/src/set-royalty-info.ts new file mode 100644 index 000000000..d80280d55 --- /dev/null +++ b/packages/core-cairo/src/set-royalty-info.ts @@ -0,0 +1,120 @@ +import type { BaseImplementedTrait, ContractBuilder } from './contract'; +import { defineComponents } from './utils/define-components'; +import { OptionsError } from "./error"; +import { toUint } from './utils/convert-strings'; +import type { Access } from './set-access-control'; + +const DEFAULT_FEE_DENOMINATOR = BigInt(10_000); + +export const defaults: RoyaltyInfoOptions = { + enabled: false, + defaultRoyaltyFraction: '0', + feeDenominator: DEFAULT_FEE_DENOMINATOR.toString() +}; + +export const enabledDefaults: RoyaltyInfoOptions = { + enabled: true, + defaultRoyaltyFraction: '10', + feeDenominator: DEFAULT_FEE_DENOMINATOR.toString() +}; + +export type RoyaltyInfoOptions = { + enabled: boolean, + defaultRoyaltyFraction: string, + feeDenominator: string, +}; + +export function setRoyaltyInfoIfNeeded(c: ContractBuilder, options: RoyaltyInfoOptions, access: Access): boolean { + if (!options.enabled) { + return false; + } + const { defaultRoyaltyFraction, feeDenominator } = getRoyaltyParameters(options); + const initParams = [ + { lit: 'default_royalty_receiver' }, + defaultRoyaltyFraction + ]; + + c.addComponent(components.ERC2981Component, initParams, true); + c.addStandaloneImport('starknet::ContractAddress'); + c.addConstructorArgument({ name: 'default_royalty_receiver', type: 'ContractAddress'}); + + switch (access) { + case 'ownable': + c.addImplToComponent(components.ERC2981Component, { + name: 'ERC2981AdminOwnableImpl', + value: `ERC2981Component::ERC2981AdminOwnableImpl`, + }); + break; + case 'roles': + c.addImplToComponent(components.ERC2981Component, { + name: 'ERC2981AdminAccessControlImpl', + value: `ERC2981Component::ERC2981AdminAccessControlImpl`, + }); + c.addConstructorArgument({ name: 'royalty_admin', type: 'ContractAddress'}); + c.addConstructorCode('self.accesscontrol._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin)'); + break; + case false: + break; + } + + if (feeDenominator === DEFAULT_FEE_DENOMINATOR) { + c.addStandaloneImport('openzeppelin::token::common::erc2981::DefaultConfig'); + } else { + const trait: BaseImplementedTrait = { + name: 'ERC2981ImmutableConfig', + of: 'ERC2981Component::ImmutableConfig', + tags: [], + }; + c.addImplementedTrait(trait); + c.addSuperVariableToTrait(trait, { + name: 'FEE_DENOMINATOR', + type: 'u128', + value: feeDenominator.toString() + }); + } + return true; +} + +function getRoyaltyParameters(opts: Required): { defaultRoyaltyFraction: bigint, feeDenominator: bigint } { + const feeDenominator = toUint(opts.feeDenominator, 'feeDenominator', 'u128'); + if (feeDenominator === BigInt(0)) { + throw new OptionsError({ + feeDenominator: 'Must be greater than 0' + }); + } + const defaultRoyaltyFraction = toUint(opts.defaultRoyaltyFraction, 'defaultRoyaltyFraction', 'u128'); + if (defaultRoyaltyFraction > feeDenominator) { + throw new OptionsError({ + defaultRoyaltyFraction: 'Cannot be greater than fee denominator' + }); + } + return { defaultRoyaltyFraction, feeDenominator }; +} + +const components = defineComponents({ + ERC2981Component: { + path: 'openzeppelin::token::common::erc2981', + substorage: { + name: 'erc2981', + type: 'ERC2981Component::Storage', + }, + event: { + name: 'ERC2981Event', + type: 'ERC2981Component::Event', + }, + impls: [ + { + name: 'ERC2981Impl', + value: 'ERC2981Component::ERC2981Impl', + }, + { + name: 'ERC2981InfoImpl', + value: 'ERC2981Component::ERC2981InfoImpl', + } + ], + internalImpl: { + name: 'ERC2981InternalImpl', + value: 'ERC2981Component::InternalImpl', + }, + }, +}); From 168c862edb75e21bf4977aee8c18e168788830bd Mon Sep 17 00:00:00 2001 From: immrsd Date: Wed, 27 Nov 2024 14:10:23 +0100 Subject: [PATCH 05/34] Add RoyaltyInfo for ERC1155 --- packages/core-cairo/src/erc1155.ts | 11 ++++++++--- packages/core-cairo/src/generate/erc1155.ts | 2 ++ packages/core-cairo/src/index.ts | 2 ++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/core-cairo/src/erc1155.ts b/packages/core-cairo/src/erc1155.ts index bfb421e6a..aa31a237c 100644 --- a/packages/core-cairo/src/erc1155.ts +++ b/packages/core-cairo/src/erc1155.ts @@ -11,6 +11,7 @@ import { printContract } from './print'; import { addSRC5Component } from './common-components'; import { externalTrait } from './external-trait'; import { toByteArray } from './utils/convert-strings'; +import { RoyaltyInfoOptions, setRoyaltyInfoIfNeeded, defaults as royaltyInfoDefaults } from './set-royalty-info'; export const defaults: Required = { name: 'MyToken', @@ -19,6 +20,7 @@ export const defaults: Required = { pausable: false, mintable: false, updatableUri: true, + royaltyInfo: royaltyInfoDefaults, access: commonDefaults.access, upgradeable: commonDefaults.upgradeable, info: commonDefaults.info @@ -35,6 +37,7 @@ export interface ERC1155Options extends CommonContractOptions { pausable?: boolean; mintable?: boolean; updatableUri?: boolean; + royaltyInfo?: RoyaltyInfoOptions; } function withDefaults(opts: ERC1155Options): Required { @@ -45,11 +48,12 @@ function withDefaults(opts: ERC1155Options): Required { pausable: opts.pausable ?? defaults.pausable, mintable: opts.mintable ?? defaults.mintable, updatableUri: opts.updatableUri ?? defaults.updatableUri, + royaltyInfo: opts.royaltyInfo ?? defaults.royaltyInfo, }; } export function isAccessControlRequired(opts: Partial): boolean { - return opts.mintable === true || opts.pausable === true || opts.updatableUri !== false || opts.upgradeable === true; + return opts.mintable === true || opts.pausable === true || opts.updatableUri !== false || opts.upgradeable === true || opts.royaltyInfo?.enabled === true; } export function buildERC1155(opts: ERC1155Options): Contract { @@ -76,11 +80,12 @@ export function buildERC1155(opts: ERC1155Options): Contract { addSetBaseUri(c, allOpts.access); } - addHooks(c, allOpts); - setAccessControl(c, allOpts.access); setUpgradeable(c, allOpts.upgradeable, allOpts.access); setInfo(c, allOpts.info); + setRoyaltyInfoIfNeeded(c, allOpts.royaltyInfo, allOpts.access); + + addHooks(c, allOpts); return c; } diff --git a/packages/core-cairo/src/generate/erc1155.ts b/packages/core-cairo/src/generate/erc1155.ts index bb5b55b34..607b2118f 100644 --- a/packages/core-cairo/src/generate/erc1155.ts +++ b/packages/core-cairo/src/generate/erc1155.ts @@ -2,6 +2,7 @@ import type { ERC1155Options } from '../erc1155'; import { accessOptions } from '../set-access-control'; import { infoOptions } from '../set-info'; import { upgradeableOptions } from '../set-upgradeable'; +import { enabledDefaults as royaltyInfoEnabled } from '../set-royalty-info'; import { generateAlternatives } from './alternatives'; const booleans = [true, false]; @@ -13,6 +14,7 @@ const blueprint = { pausable: booleans, mintable: booleans, updatableUri: booleans, + royaltyInfo: [royaltyInfoEnabled], access: accessOptions, upgradeable: upgradeableOptions, info: infoOptions, diff --git a/packages/core-cairo/src/index.ts b/packages/core-cairo/src/index.ts index 448e90c47..7551f94aa 100644 --- a/packages/core-cairo/src/index.ts +++ b/packages/core-cairo/src/index.ts @@ -10,10 +10,12 @@ export type { Access } from './set-access-control'; export type { Account } from './account'; export type { Upgradeable } from './set-upgradeable'; export type { Info } from './set-info'; +export type { RoyaltyInfoOptions } from './set-royalty-info'; export { premintPattern } from './erc20'; export { defaults as infoDefaults } from './set-info'; +export { defaults as royaltyInfoDefaults } from './set-royalty-info'; export type { OptionsErrorMessages } from './error'; export { OptionsError } from './error'; From 3d72bd90b89ac3936e02fc9b2a577df8a9a15422 Mon Sep 17 00:00:00 2001 From: immrsd Date: Wed, 27 Nov 2024 14:10:46 +0100 Subject: [PATCH 06/34] Add RoyaltyInfo for ERC721 --- packages/core-cairo/src/erc721.ts | 8 +++++++- packages/core-cairo/src/generate/erc721.ts | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/core-cairo/src/erc721.ts b/packages/core-cairo/src/erc721.ts index 9d0a32c1a..47f08faca 100644 --- a/packages/core-cairo/src/erc721.ts +++ b/packages/core-cairo/src/erc721.ts @@ -12,6 +12,7 @@ import { addSRC5Component, addVotesComponent } from './common-components'; import { externalTrait } from './external-trait'; import { toByteArray, toFelt252 } from './utils/convert-strings'; import { OptionsError } from './error'; +import { RoyaltyInfoOptions, setRoyaltyInfoIfNeeded, defaults as royaltyInfoDefaults } from './set-royalty-info'; export const defaults: Required = { name: 'MyToken', @@ -22,6 +23,7 @@ export const defaults: Required = { mintable: false, enumerable: false, votes: false, + royaltyInfo: royaltyInfoDefaults, appName: '', // Defaults to empty string, but user must provide a non-empty value if votes are enabled appVersion: 'v1', access: commonDefaults.access, @@ -41,6 +43,7 @@ export interface ERC721Options extends CommonContractOptions { pausable?: boolean; mintable?: boolean; enumerable?: boolean; + royaltyInfo?: RoyaltyInfoOptions; votes?: boolean; appName?: string; appVersion?: string; @@ -55,6 +58,7 @@ function withDefaults(opts: ERC721Options): Required { pausable: opts.pausable ?? defaults.pausable, mintable: opts.mintable ?? defaults.mintable, enumerable: opts.enumerable ?? defaults.enumerable, + royaltyInfo: opts.royaltyInfo ?? defaults.royaltyInfo, votes: opts.votes ?? defaults.votes, appName: opts.appName ?? defaults.appName, appVersion: opts.appVersion ?? defaults.appVersion @@ -62,7 +66,7 @@ function withDefaults(opts: ERC721Options): Required { } export function isAccessControlRequired(opts: Partial): boolean { - return opts.mintable === true || opts.pausable === true || opts.upgradeable === true; + return opts.mintable === true || opts.pausable === true || opts.upgradeable === true || opts.royaltyInfo?.enabled === true; } export function buildERC721(opts: ERC721Options): Contract { @@ -92,6 +96,8 @@ export function buildERC721(opts: ERC721Options): Contract { setAccessControl(c, allOpts.access); setUpgradeable(c, allOpts.upgradeable, allOpts.access); setInfo(c, allOpts.info); + setRoyaltyInfoIfNeeded(c, allOpts.royaltyInfo, allOpts.access); + addHooks(c, allOpts); return c; diff --git a/packages/core-cairo/src/generate/erc721.ts b/packages/core-cairo/src/generate/erc721.ts index 99f97a120..5b33e74d9 100644 --- a/packages/core-cairo/src/generate/erc721.ts +++ b/packages/core-cairo/src/generate/erc721.ts @@ -2,6 +2,7 @@ import type { ERC721Options } from '../erc721'; import { accessOptions } from '../set-access-control'; import { infoOptions } from '../set-info'; import { upgradeableOptions } from '../set-upgradeable'; +import { enabledDefaults as royaltyInfoEnabled } from '../set-royalty-info'; import { generateAlternatives } from './alternatives'; const booleans = [true, false]; @@ -17,6 +18,7 @@ const blueprint = { appVersion: ['v1'], pausable: booleans, mintable: booleans, + royaltyInfo: [royaltyInfoEnabled], access: accessOptions, upgradeable: upgradeableOptions, info: infoOptions, From 6155fea87f37b39b90a28ccb48def06b4baf5c42 Mon Sep 17 00:00:00 2001 From: immrsd Date: Wed, 27 Nov 2024 14:11:25 +0100 Subject: [PATCH 07/34] Introduce RoyaltyInfoSection with UI controls --- .../ui/src/cairo/RoyaltyInfoSection.svelte | 44 +++++++++++++++++++ packages/ui/src/cairo/inject-hyperlinks.ts | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 packages/ui/src/cairo/RoyaltyInfoSection.svelte diff --git a/packages/ui/src/cairo/RoyaltyInfoSection.svelte b/packages/ui/src/cairo/RoyaltyInfoSection.svelte new file mode 100644 index 000000000..3654fddbf --- /dev/null +++ b/packages/ui/src/cairo/RoyaltyInfoSection.svelte @@ -0,0 +1,44 @@ + + +
+

+ + +

+ + + + +
diff --git a/packages/ui/src/cairo/inject-hyperlinks.ts b/packages/ui/src/cairo/inject-hyperlinks.ts index c707141b3..0998a203e 100644 --- a/packages/ui/src/cairo/inject-hyperlinks.ts +++ b/packages/ui/src/cairo/inject-hyperlinks.ts @@ -33,7 +33,7 @@ function removeComponentName(libraryPathSegments: Array) { const lastItem = libraryPathSegments[libraryPathSegments.length - 1]; if (lastItem !== undefined && componentMappings[lastItem] !== undefined) { // Replace component name with the name of its .cairo file - libraryPathSegments.splice(-1, 1, componentMappings[lastItem]); + libraryPathSegments.splice(-1, 1, componentMappings[lastItem] as string); } else { libraryPathSegments.pop(); } From 6b37bd9ff152bc715c8d3937f584b44c944fe40f Mon Sep 17 00:00:00 2001 From: immrsd Date: Wed, 27 Nov 2024 14:12:17 +0100 Subject: [PATCH 08/34] Enable errors proxying to ERC1155 controls --- packages/ui/src/cairo/App.svelte | 2 +- packages/ui/src/main.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/cairo/App.svelte b/packages/ui/src/cairo/App.svelte index ba1956cce..f80714492 100644 --- a/packages/ui/src/cairo/App.svelte +++ b/packages/ui/src/cairo/App.svelte @@ -160,7 +160,7 @@
- +
diff --git a/packages/ui/src/main.ts b/packages/ui/src/main.ts index e2a9cba37..3cb12a50a 100644 --- a/packages/ui/src/main.ts +++ b/packages/ui/src/main.ts @@ -8,7 +8,7 @@ import UnsupportedVersion from './UnsupportedVersion.svelte'; import semver from 'semver'; import { compatibleContractsSemver as compatibleSolidityContractsSemver } from '@openzeppelin/wizard'; import { compatibleContractsSemver as compatibleCairoContractsSemver } from '@openzeppelin/wizard-cairo'; -import { InitialOptions } from './initial-options'; +import type { InitialOptions } from './initial-options'; function postResize() { const { height } = document.documentElement.getBoundingClientRect(); From 0f52a2cd770e7c372baab9d27200c4c9054fa8cc Mon Sep 17 00:00:00 2001 From: immrsd Date: Wed, 27 Nov 2024 14:12:43 +0100 Subject: [PATCH 09/34] Add RoyaltyInfo section to ERC1155 controls --- packages/ui/src/cairo/ERC1155Controls.svelte | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/cairo/ERC1155Controls.svelte b/packages/ui/src/cairo/ERC1155Controls.svelte index 13c528ea1..8ce915f51 100644 --- a/packages/ui/src/cairo/ERC1155Controls.svelte +++ b/packages/ui/src/cairo/ERC1155Controls.svelte @@ -1,11 +1,12 @@ @@ -70,6 +73,8 @@
+ + From 6fee79c46e1861fa190d52c918e27f015ea44d82 Mon Sep 17 00:00:00 2001 From: immrsd Date: Wed, 27 Nov 2024 14:12:59 +0100 Subject: [PATCH 10/34] Add RoyaltyInfo section to ERC721 controls --- packages/ui/src/cairo/ERC721Controls.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/ui/src/cairo/ERC721Controls.svelte b/packages/ui/src/cairo/ERC721Controls.svelte index 1d78ffe78..62f786016 100644 --- a/packages/ui/src/cairo/ERC721Controls.svelte +++ b/packages/ui/src/cairo/ERC721Controls.svelte @@ -6,6 +6,7 @@ import AccessControlSection from './AccessControlSection.svelte'; import UpgradeabilityField from './UpgradeabilityField.svelte'; + import RoyaltyInfoSection from './RoyaltyInfoSection.svelte'; import InfoSection from './InfoSection.svelte'; import { error } from '../error-tooltip'; @@ -110,6 +111,8 @@ + + \ No newline at end of file From b02d19d21ef6777e60e4b8addc5ae8229f452808 Mon Sep 17 00:00:00 2001 From: immrsd Date: Wed, 27 Nov 2024 14:17:14 +0100 Subject: [PATCH 11/34] Update changelog --- packages/core-cairo/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/core-cairo/CHANGELOG.md b/packages/core-cairo/CHANGELOG.md index 9a952b838..a2417913b 100644 --- a/packages/core-cairo/CHANGELOG.md +++ b/packages/core-cairo/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.19.0 (2024-11-27) + +- Add ERC2981 (RoyaltyInfo) for ERC721 and ERC1155 ([#413](https://github.com/OpenZeppelin/contracts-wizard/pull/413)) + ## 0.18.0 (2024-11-15) - **Breaking changes**: From 9219dfd26f3336669d5c68383ce75a7762476f08 Mon Sep 17 00:00:00 2001 From: immrsd Date: Wed, 27 Nov 2024 14:33:37 +0100 Subject: [PATCH 12/34] Fix unnecessary brackets for Cairo constants import --- packages/core-cairo/src/print.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/core-cairo/src/print.ts b/packages/core-cairo/src/print.ts index 373708ab6..153521e00 100644 --- a/packages/core-cairo/src/print.ts +++ b/packages/core-cairo/src/print.ts @@ -50,8 +50,11 @@ function printImports(contract: Contract): string[] { function sortImports(contract: Contract): string[] { const componentImports = contract.components.flatMap(c => `${c.path}::${c.name}`); const allImports = componentImports.concat(contract.standaloneImports); - if (contract.superVariables.length > 0) { - allImports.push(`super::{${contract.superVariables.map(v => v.name).join(', ')}}`); + const superVars = contract.superVariables; + if (superVars.length === 1) { + allImports.push(`super::${superVars[0]!.name}`); + } else if (superVars.length > 1) { + allImports.push(`super::{${superVars.map(v => v.name).join(', ')}}`); } return allImports.sort(); } From 83d80179a569f4429a37d1f85f69a4d56d28a7cb Mon Sep 17 00:00:00 2001 From: immrsd Date: Wed, 27 Nov 2024 15:42:49 +0100 Subject: [PATCH 13/34] Make Access control required for RoyaltyInfo --- packages/core-cairo/src/set-royalty-info.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/core-cairo/src/set-royalty-info.ts b/packages/core-cairo/src/set-royalty-info.ts index d80280d55..03b544d98 100644 --- a/packages/core-cairo/src/set-royalty-info.ts +++ b/packages/core-cairo/src/set-royalty-info.ts @@ -2,7 +2,7 @@ import type { BaseImplementedTrait, ContractBuilder } from './contract'; import { defineComponents } from './utils/define-components'; import { OptionsError } from "./error"; import { toUint } from './utils/convert-strings'; -import type { Access } from './set-access-control'; +import { Access, setAccessControl } from './set-access-control'; const DEFAULT_FEE_DENOMINATOR = BigInt(10_000); @@ -28,6 +28,11 @@ export function setRoyaltyInfoIfNeeded(c: ContractBuilder, options: RoyaltyInfoO if (!options.enabled) { return false; } + if (access === false) { + access = 'ownable'; + } + setAccessControl(c, access); + const { defaultRoyaltyFraction, feeDenominator } = getRoyaltyParameters(options); const initParams = [ { lit: 'default_royalty_receiver' }, @@ -53,8 +58,6 @@ export function setRoyaltyInfoIfNeeded(c: ContractBuilder, options: RoyaltyInfoO c.addConstructorArgument({ name: 'royalty_admin', type: 'ContractAddress'}); c.addConstructorCode('self.accesscontrol._grant_role(ERC2981Component::ROYALTY_ADMIN_ROLE, royalty_admin)'); break; - case false: - break; } if (feeDenominator === DEFAULT_FEE_DENOMINATOR) { From 8b4037c8d94b84ccf90505bd64ecedd864371e2f Mon Sep 17 00:00:00 2001 From: immrsd Date: Wed, 27 Nov 2024 15:43:11 +0100 Subject: [PATCH 14/34] Update snapshot for Cairo custom contract tests --- packages/core-cairo/src/custom.test.ts.md | 2 +- packages/core-cairo/src/custom.test.ts.snap | Bin 1324 -> 1320 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-cairo/src/custom.test.ts.md b/packages/core-cairo/src/custom.test.ts.md index 998b9155d..ef3f7ffbb 100644 --- a/packages/core-cairo/src/custom.test.ts.md +++ b/packages/core-cairo/src/custom.test.ts.md @@ -301,7 +301,7 @@ Generated by [AVA](https://avajs.dev). use openzeppelin::upgrades::interface::IUpgradeable;␊ use starknet::ClassHash;␊ use starknet::ContractAddress;␊ - use super::{UPGRADER_ROLE};␊ + use super::UPGRADER_ROLE;␊ ␊ component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);␊ component!(path: SRC5Component, storage: src5, event: SRC5Event);␊ diff --git a/packages/core-cairo/src/custom.test.ts.snap b/packages/core-cairo/src/custom.test.ts.snap index 4274266201478aa0fff69fd7df7839fb31bf3443..08994387bb602233ccfe7605e3d8daec92b51c58 100644 GIT binary patch literal 1320 zcmV+@1=spPRzV$1$@$RCRc00000000B+TFq|bHV{r*G(~)CpMcq-fCD%`T_A@t-2#S_b`2y=kRNo@ zA{A(96cdq2fu!=YMGn37HMZAYdhVq!&^PGI6p|>J)K8ogcGfnWOH65I$Zt5qZ#2?7 zzZY=#O8)jcD5?Uub|b0y05Q(iqwrD?4@fTnDj5l7pDX$6JI^DEy!rd1rB9aPzYjiM z`t-v;mo_)i$hRp=zWre*FM_YIAgYrG!#Bnp%)0X<*-!9x0ad0LQ05)co8z14;>Caw!o zV$5$?ylEI8ra$84a0X26)F|qmmKdK6_xuT3lqnECaRKA5VPU8^1l0+Ml&^{GSKibJ z+ny&t%AH0c<7Nw4ZH7ekF{)Z*3+a8w$cWn(f@@&PQrU~ItnE~rE_%!`tG$|=)S{U= z9et!nn=t5s=Ui{Cr7=hcVUS55&|Av7@r75Vx$^zWg}%-la-xWWZ}cRIJp)iT_C6zl zHe2N#j7_1E+2Se-7Rw8L(AuN(P|HOLn7CJv5YPgDG7}fzxZnkR8n4wpN$WYJmxSXY% zz(s1vXUMqL6yPHXfsc{3pRL$)@zGYu%{2pMthS*Yyg?aNlmzrGh-RXyQ|*3KhL{l@ zkGYS`!=w;q#`HeRVx=AggB!<WAs9AG9+rfq@_j93=?N<280s2*RNX2BwSlZjD2N`}z1ZR<`06 z^c_2!e3qnlt)Ir=jO_T>NGGz8KW2Rqd$tv3Q$1AOT|Lx9S@agBuSQ$+78bpQMQ>rz zTQC@9=4ykSeQwfI8zrYb50FO_=JMvYoqt^btew4J-Wg!tS?pzun|bHIJur`t^^2$S z1@jh&NEL`k@Bk`aF0ZUwH1yeIJN!vtptGazj@!F?$IkKL{vJj?1nTAXOY|y+4RO${ zHeXmWFXD4>EEJah*FM3xoeVqaa3nF+7}?$XwtcpL>a=$cy5A@3tusJl4H<%~D39Mf z2zMLfljF|z>@sOC5``czwzjQ~XREz#P^0DGoE$B+2btG$sbM#Ecy2BhdjI@wfihXV z+DV0weGH>p3I!Ifn`Cy`-OkNgfKOohBunj(n&C>JoJ93b8c{Xm2~_gVdn`BdN$My& z7tkMEIm8=KR;oi<#r-RnDyc}RV6h_5DqFJCphncW6oe@!ChBIHQ(E?d4eB3Z$~ux; zIh+|RpKOd~5mg2{{(|cEi>&XSRy~jHY@9FuYn534%;S^(6F;Ju=?@o1B+TOLf+=aS eKlgV4^Yi9@`X25VZ<(j`E%SfCKKdEIF8}~QM}XD< literal 1324 zcmV+{1=IRLRzVuYSUz4Y8mU!ZT$mnkGsGO3?9DeSCmIG32x%#h!3hTmwU z_kK6v?v?!QcTiLXaP3A?@jhaltwrIbARdrz08}y(${ttp*Y}=B6nXRaCrh6##eW}t zy7c+Se=coopp&EBH*5RU1twvw;{j9Drywvo=$tk-Hc*@QL!xLm0CYoD4;_ZUeu6NB zfHKtPObOyDiF__ln^3{gFPrOMeZ9WfXuSAYDk82JD2!b#c+u!{4;|cPis)SfA%tR4 zF32TZBtxl090& zN{smpi#HA9!}LeI9L|8L?HWbB(-Pye;hsN2i!uemCoW*zF)R!fhoIU4k@7W>y~>*! z;g07CkaD|`$hg@;o;5?FdKgtLvW4`%V`Ri_3&AxoWvT4NS5~*HO&2|8nAKj*O={81 zoQ^)yrA_E}!E>%RSJN1z{V>R+59lpro%q75(p>p|0GglAEnsGiK;jAu`l+JT7PH zCUB7&@)p2d`5`6(s?E3!<5*>Qozy$`CW6 z<1zP0k54TP0iuyJYBdddY+* zL{}3e))9H00n_ACa>{W3Yq{#_Xf|Jp&4?9?enNtsOc5|9+RoFs%1IL-?C};6lcj$v zrz;&aKH{%Z{~zVYqJEgp`awJ65*P@gz)^zGJYHiLg&-WNpl`Zp@76dZxSx+tV`VFD zLEo{n$!AG=*ZOG;&d83B^>iW&`9szhu_s$$Hr0LA-PL_fltpi0`f9XAZ(-3}So9Va zy#<3&X0A5K+2fO+xgc8z}ndh=A8lNoyA_pxS4nU+XM6XSig8G zUodZhh*W`y1P`F%2M@5)fm~``)=oK|J2#pJ?Q+9thdepjWuKluA)4C z^B~+Uj8Bf+TeHigxkwa(!1$fDZ-Y9ft@gV?j+TpadbHH;JsS;@w_pe;6q#~t~#fm_yaLG=C8d2v`5T=}%s+(n2Y1s=l$bW<> z>r8ItaAvT8vN4)vR2k^_ORC#1v(9^3_dK+>alZVoRb>4$kI(v#{ET9zKV2A+FpKXC irliUK)L#P3&%68Sd%Rz~XP(mc%>Mz02JpqfF8}~+G?C>1 From f424d788073874d8844346d9f120374b3f08ebf6 Mon Sep 17 00:00:00 2001 From: immrsd Date: Wed, 27 Nov 2024 15:54:37 +0100 Subject: [PATCH 15/34] Apply minor fixes --- packages/core-cairo/src/contract.ts | 2 +- packages/core-cairo/src/erc721.ts | 2 +- packages/core-cairo/src/generate/erc1155.ts | 2 +- packages/ui/src/cairo/ERC1155Controls.svelte | 2 +- packages/ui/src/cairo/RoyaltyInfoSection.svelte | 6 ++---- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/core-cairo/src/contract.ts b/packages/core-cairo/src/contract.ts index e79bf19a7..da077abd0 100644 --- a/packages/core-cairo/src/contract.ts +++ b/packages/core-cairo/src/contract.ts @@ -13,7 +13,7 @@ export interface Contract { superVariables: Variable[]; } -export type Value = string | number | BigInt | { lit: string } | { note: string, value: Value }; +export type Value = string | number | bigint | { lit: string } | { note: string, value: Value }; export interface Component { name: string; diff --git a/packages/core-cairo/src/erc721.ts b/packages/core-cairo/src/erc721.ts index 47f08faca..ab3d636e5 100644 --- a/packages/core-cairo/src/erc721.ts +++ b/packages/core-cairo/src/erc721.ts @@ -43,8 +43,8 @@ export interface ERC721Options extends CommonContractOptions { pausable?: boolean; mintable?: boolean; enumerable?: boolean; - royaltyInfo?: RoyaltyInfoOptions; votes?: boolean; + royaltyInfo?: RoyaltyInfoOptions; appName?: string; appVersion?: string; } diff --git a/packages/core-cairo/src/generate/erc1155.ts b/packages/core-cairo/src/generate/erc1155.ts index 607b2118f..b7447fbbc 100644 --- a/packages/core-cairo/src/generate/erc1155.ts +++ b/packages/core-cairo/src/generate/erc1155.ts @@ -14,9 +14,9 @@ const blueprint = { pausable: booleans, mintable: booleans, updatableUri: booleans, + upgradeable: upgradeableOptions, royaltyInfo: [royaltyInfoEnabled], access: accessOptions, - upgradeable: upgradeableOptions, info: infoOptions, }; diff --git a/packages/ui/src/cairo/ERC1155Controls.svelte b/packages/ui/src/cairo/ERC1155Controls.svelte index 8ce915f51..a292405ee 100644 --- a/packages/ui/src/cairo/ERC1155Controls.svelte +++ b/packages/ui/src/cairo/ERC1155Controls.svelte @@ -4,7 +4,7 @@ import type { KindedOptions, OptionsErrorMessages } from '@openzeppelin/wizard-cairo'; import { erc1155, infoDefaults } from '@openzeppelin/wizard-cairo'; - import AccessControlSection from './AccessControlSection.svelte'; + import AccessControlSection from './AccessControlSection.svelte'; import UpgradeabilityField from './UpgradeabilityField.svelte'; import RoyaltyInfoSection from './RoyaltyInfoSection.svelte'; import InfoSection from './InfoSection.svelte'; diff --git a/packages/ui/src/cairo/RoyaltyInfoSection.svelte b/packages/ui/src/cairo/RoyaltyInfoSection.svelte index 3654fddbf..6316888c9 100644 --- a/packages/ui/src/cairo/RoyaltyInfoSection.svelte +++ b/packages/ui/src/cairo/RoyaltyInfoSection.svelte @@ -1,11 +1,9 @@