From 1b90b72faeb4a3ca9af9bca2b441f49f279e16c4 Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 24 Dec 2024 15:50:31 +0100 Subject: [PATCH 01/16] Add vesting --- packages/core-cairo/src/vesting.ts | 223 +++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 packages/core-cairo/src/vesting.ts diff --git a/packages/core-cairo/src/vesting.ts b/packages/core-cairo/src/vesting.ts new file mode 100644 index 000000000..a38afb170 --- /dev/null +++ b/packages/core-cairo/src/vesting.ts @@ -0,0 +1,223 @@ +import { BaseImplementedTrait, Contract, ContractBuilder } from './contract'; +import { setAccessControl } from './set-access-control'; +import { setUpgradeable } from './set-upgradeable'; +import { Info, setInfo } from './set-info'; +import { defineComponents } from './utils/define-components'; +import { contractDefaults as commonDefaults } from './common-options'; +import { printContract } from './print'; +import { OptionsError } from './error'; +import { durationToTimestamp } from './utils/duration'; +import { toUint, validateUint } from './utils/convert-strings'; + +export type VestingSchedule = 'linear' | 'custom'; + +export const defaults: Required = { + name: 'VestingWallet', + startDate: '', + duration: '0 day', + cliffDuration: '0 day', + schedule: 'custom', + info: commonDefaults.info +} as const; + +export function printVesting(opts: VestingOptions = defaults): string { + return printContract(buildVesting(opts)); +} + +export interface VestingOptions { + name: string; + startDate: string; + duration: string; + cliffDuration: string; + schedule: VestingSchedule; + info?: Info; +} + +function withDefaults(opts: VestingOptions): Required { + return { + name: opts.name ?? defaults.name, + startDate: opts.startDate ?? defaults.startDate, + duration: opts.duration ?? defaults.duration, + cliffDuration: opts.cliffDuration ?? defaults.cliffDuration, + schedule: opts.schedule ?? defaults.schedule, + info: opts.info ?? defaults.info + }; +} + +export function isAccessControlRequired(_: Partial): boolean { + return true; +} + +export function buildVesting(opts: VestingOptions): Contract { + const c = new ContractBuilder(opts.name); + const allOpts = withDefaults(opts); + + addBase(c, opts); + addSchedule(c, opts); + setInfo(c, allOpts.info); + + // Vesting component depends on Ownable component + const access = 'ownable'; + setAccessControl(c, access); + + // Must be non-upgradable to guarantee vesting according to the schedule + setUpgradeable(c, false, access); + + return c; +} + +function addBase(c: ContractBuilder, opts: VestingOptions) { + c.addUseClause('starknet', 'ContractAddress'); + const startDate = getVestingStart(opts); + const totalDuration = getVestingDuration(opts); + const cliffDuration = getCliffDuration(opts); + validateDurations(totalDuration, cliffDuration); + if (startDate !== undefined) { + c.addConstant({ + name: 'START', + type: 'u64', + value: startDate.timestampInSec.toString(), + comment: startDate.formattedDate, + inlineComment: true + }); + } else { + c.addConstant({ + name: 'START', + type: 'u64', + value: '0' + }); + } + c.addConstant({ + name: 'DURATION', + type: 'u64', + value: totalDuration.toString(), + comment: opts.duration, + inlineComment: true + }); + c.addConstant({ + name: 'CLIFF_DURATION', + type: 'u64', + value: cliffDuration.toString(), + comment: opts.cliffDuration, + inlineComment: true + }); + const initParams = [{ lit: 'START' }, { lit: 'DURATION' }, { lit: 'CLIFF_DURATION' }]; + c.addComponent(components.VestingComponent, initParams, true); +} + +function addSchedule(c: ContractBuilder, opts: VestingOptions) { + switch (opts.schedule) { + case 'linear': + c.addUseClause('openzeppelin::finance::vesting', 'LinearVestingSchedule'); + return; + case 'custom': + const scheduleTrait: BaseImplementedTrait = { + name: `VestingSchedule`, + of: 'VestingComponent::VestingScheduleTrait', + tags: [], + priority: 0, + }; + c.addImplementedTrait(scheduleTrait); + c.addFunction(scheduleTrait, { + name: 'calculate_vested_amount', + returns: 'u256', + args: [ + { name: 'self', type: `@VestingComponent::ComponentState` }, + { name: 'token', type: 'ContractAddress' }, + { name: 'total_allocation', type: 'u256' }, + { name: 'timestamp', type: 'u64' }, + { name: 'start', type: 'u64' }, + { name: 'duration', type: 'u64' }, + { name: 'cliff', type: 'u64' } + ], + code: [ + '// TODO: Must be implemented according to the desired vesting schedule', + '0', + ], + }); + return; + } +} + +function getVestingStart(opts: VestingOptions): { timestampInSec: bigint, formattedDate: string } | undefined { + if (opts.startDate === '' || opts.startDate === 'NaN') { + return undefined; + } + const startDate = new Date(`${opts.startDate}Z`); + const timestampInMillis = startDate.getTime(); + const timestampInSec = toUint( + Math.floor(timestampInMillis / 1000), + 'startDate', + 'u64' + ); + const formattedDate = startDate.toLocaleString('en-GB', { + day: '2-digit', + month: 'short', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + hour12: false, + timeZone: 'UTC' + }); + return { timestampInSec, formattedDate: `${formattedDate} (UTC)` }; +} + +function getVestingDuration(opts: VestingOptions): number { + try { + return durationToTimestamp(opts.duration); + } catch (e) { + if (e instanceof Error) { + throw new OptionsError({ + duration: e.message + }); + } else { + throw e; + } + } +} + +function getCliffDuration(opts: VestingOptions): number { + try { + return durationToTimestamp(opts.cliffDuration); + } catch (e) { + if (e instanceof Error) { + throw new OptionsError({ + cliffDuration: e.message + }); + } else { + throw e; + } + } +} + +function validateDurations(duration: number, cliffDuration: number): void { + validateUint(duration, 'duration', 'u64'); + validateUint(cliffDuration, 'cliffDuration', 'u64'); + if (cliffDuration > duration) { + throw new OptionsError({ + cliffDuration: `Cliff duration must be less than or equal to the total duration` + }); + } +} + +const components = defineComponents({ + VestingComponent: { + path: 'openzeppelin::finance::vesting', + substorage: { + name: 'vesting', + type: 'VestingComponent::Storage', + }, + event: { + name: 'VestingEvent', + type: 'VestingComponent::Event', + }, + impls: [{ + name: 'VestingImpl', + value: 'VestingComponent::VestingImpl' + }, { + name: 'VestingInternalImpl', + embed: false, + value: 'VestingComponent::InternalImpl', + }], + } +}); From 3339094c2a3d21763003d21ecd153bfa05d8c339 Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 24 Dec 2024 15:50:58 +0100 Subject: [PATCH 02/16] Add function for uint validation --- packages/core-cairo/src/utils/convert-strings.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/core-cairo/src/utils/convert-strings.ts b/packages/core-cairo/src/utils/convert-strings.ts index 75cc1758a..d1681ec09 100644 --- a/packages/core-cairo/src/utils/convert-strings.ts +++ b/packages/core-cairo/src/utils/convert-strings.ts @@ -70,16 +70,17 @@ const UINT_MAX_VALUES = { export type UintType = keyof typeof UINT_MAX_VALUES; /** - * Validates a string value to be a valid uint and converts it to bigint + * Checks that a string/number value is a valid `uint` value and converts it to bigint */ -export function toUint(str: string, field: string, type: UintType): bigint { - const isValidNumber = /^\d+$/.test(str); +export function toUint(value: number | string, field: string, type: UintType): bigint { + const valueAsStr = value.toString(); + const isValidNumber = /^\d+$/.test(valueAsStr); if (!isValidNumber) { throw new OptionsError({ [field]: 'Not a valid number' }); } - const numValue = BigInt(str); + const numValue = BigInt(valueAsStr); if (numValue > UINT_MAX_VALUES[type]) { throw new OptionsError({ [field]: `Value is greater than ${type} max value` @@ -87,3 +88,10 @@ export function toUint(str: string, field: string, type: UintType): bigint { } return numValue; } + +/** + * Checks that a string/number value is a valid `uint` value + */ +export function validateUint(value: number | string, field: string, type: UintType): void { + const _ = toUint(value, field, type); +} From 3712400746d99385aa3dbdd5a290971142ad3e4e Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 24 Dec 2024 15:51:19 +0100 Subject: [PATCH 03/16] Minor Governor fixes --- packages/core-cairo/src/governor.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core-cairo/src/governor.test.ts b/packages/core-cairo/src/governor.test.ts index 5b0e0422d..e3b3f0c22 100644 --- a/packages/core-cairo/src/governor.test.ts +++ b/packages/core-cairo/src/governor.test.ts @@ -125,9 +125,9 @@ testAPIEquivalence('API erc721 votes + timelock', { }); testAPIEquivalence('API custom name', { + name: 'CustomGovernor', delay: '1 day', period: '1 week', - name: 'CustomGovernor', }); testAPIEquivalence('API custom settings', { @@ -146,7 +146,8 @@ testAPIEquivalence('API quorum mode absolute', { quorumAbsolute: '200', }); -testAPIEquivalence('API quorum mode percent', { name: NAME, +testAPIEquivalence('API quorum mode percent', { + name: NAME, delay: '1 day', period: '1 week', quorumMode: 'percent', From 14efb4766725fa8267a04526a278dd46457d1d47 Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 24 Dec 2024 15:52:02 +0100 Subject: [PATCH 04/16] Support Vesting --- packages/core-cairo/src/api.ts | 7 +++++++ packages/core-cairo/src/build-generic.ts | 6 ++++++ packages/core-cairo/src/index.ts | 2 +- packages/core-cairo/src/kind.ts | 1 + packages/core-cairo/src/set-upgradeable.ts | 2 +- 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/core-cairo/src/api.ts b/packages/core-cairo/src/api.ts index d5634fb5c..a061e6993 100644 --- a/packages/core-cairo/src/api.ts +++ b/packages/core-cairo/src/api.ts @@ -5,6 +5,7 @@ import { printERC1155, defaults as erc1155defaults, isAccessControlRequired as e import { printAccount, defaults as accountDefaults, AccountOptions } from './account'; import { printGovernor, defaults as governorDefaults, isAccessControlRequired as governorIsAccessControlRequired, GovernorOptions } from './governor'; import { printCustom, defaults as customDefaults, isAccessControlRequired as customIsAccessControlRequired, CustomOptions } from './custom'; +import { printVesting, defaults as vestingDefaults, isAccessControlRequired as vestingIsAccessControlRequired, VestingOptions } from './vesting'; export interface WizardAccountAPI{ /** @@ -41,6 +42,7 @@ export type ERC721 = WizardContractAPI; export type ERC1155 = WizardContractAPI; export type Account = WizardAccountAPI; export type Governor = WizardContractAPI; +export type Vesting = WizardContractAPI; export type Custom = WizardContractAPI; export const erc20: ERC20 = { @@ -67,6 +69,11 @@ export const governor: Governor = { defaults: governorDefaults, isAccessControlRequired: governorIsAccessControlRequired } +export const vesting: Vesting = { + print: printVesting, + defaults: vestingDefaults, + isAccessControlRequired: vestingIsAccessControlRequired +} export const custom: Custom = { print: printCustom, defaults: customDefaults, diff --git a/packages/core-cairo/src/build-generic.ts b/packages/core-cairo/src/build-generic.ts index 27fec9d27..fc04c30dc 100644 --- a/packages/core-cairo/src/build-generic.ts +++ b/packages/core-cairo/src/build-generic.ts @@ -4,12 +4,15 @@ import { ERC1155Options, buildERC1155 } from './erc1155'; import { CustomOptions, buildCustom } from './custom'; import { AccountOptions, buildAccount } from './account'; import { GovernorOptions, buildGovernor } from './governor'; +import { VestingOptions, buildVesting } from './vesting'; + export interface KindedOptions { ERC20: { kind: 'ERC20' } & ERC20Options; ERC721: { kind: 'ERC721' } & ERC721Options; ERC1155: { kind: 'ERC1155' } & ERC1155Options; Account: { kind: 'Account' } & AccountOptions; Governor: { kind: 'Governor' } & GovernorOptions; + Vesting: { kind: 'Vesting' } & VestingOptions; Custom: { kind: 'Custom' } & CustomOptions; } @@ -32,6 +35,9 @@ export function buildGeneric(opts: GenericOptions) { case 'Governor': return buildGovernor(opts); + case 'Vesting': + return buildVesting(opts); + case 'Custom': return buildCustom(opts); diff --git a/packages/core-cairo/src/index.ts b/packages/core-cairo/src/index.ts index c799d08f5..5f102fabb 100644 --- a/packages/core-cairo/src/index.ts +++ b/packages/core-cairo/src/index.ts @@ -25,4 +25,4 @@ export { sanitizeKind } from './kind'; export { contractsVersion, contractsVersionTag, compatibleContractsSemver } from './utils/version'; -export { erc20, erc721, erc1155, account, governor, custom } from './api'; +export { erc20, erc721, erc1155, account, governor, vesting, custom } from './api'; diff --git a/packages/core-cairo/src/kind.ts b/packages/core-cairo/src/kind.ts index 0f6ee0683..d61180216 100644 --- a/packages/core-cairo/src/kind.ts +++ b/packages/core-cairo/src/kind.ts @@ -19,6 +19,7 @@ function isKind(value: Kind | T): value is Kind { case 'ERC1155': case 'Account': case 'Governor': + case 'Vesting': case 'Custom': return true; diff --git a/packages/core-cairo/src/set-upgradeable.ts b/packages/core-cairo/src/set-upgradeable.ts index 30793da20..65a944128 100644 --- a/packages/core-cairo/src/set-upgradeable.ts +++ b/packages/core-cairo/src/set-upgradeable.ts @@ -11,7 +11,7 @@ export type Upgradeable = typeof upgradeableOptions[number]; function setUpgradeableBase(c: ContractBuilder, upgradeable: Upgradeable): BaseImplementedTrait | undefined { if (upgradeable === false) { - return; + return undefined; } c.upgradeable = true; From b66b2c6f1d3c62b4b4865759d2165a209cbd05d8 Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 24 Dec 2024 15:55:09 +0100 Subject: [PATCH 05/16] Add vesting generators --- packages/core-cairo/src/generate/sources.ts | 7 +++++++ packages/core-cairo/src/generate/vesting.ts | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 packages/core-cairo/src/generate/vesting.ts diff --git a/packages/core-cairo/src/generate/sources.ts b/packages/core-cairo/src/generate/sources.ts index 3ff9365ce..b56f0b1a3 100644 --- a/packages/core-cairo/src/generate/sources.ts +++ b/packages/core-cairo/src/generate/sources.ts @@ -8,6 +8,7 @@ import { generateERC1155Options } from './erc1155'; import { generateAccountOptions } from './account'; import { generateCustomOptions } from './custom'; import { generateGovernorOptions } from './governor'; +import { generateVestingOptions } from './vesting'; import { buildGeneric, GenericOptions, KindedOptions } from '../build-generic'; import { printContract } from '../print'; import { OptionsError } from '../error'; @@ -54,6 +55,12 @@ export function* generateOptions(kind?: Kind): Generator { yield { kind: 'Governor', ...kindOpts }; } } + + if (!kind || kind === 'Vesting') { + for (const kindOpts of generateVestingOptions()) { + yield { kind: 'Vesting', ...kindOpts }; + } + } } interface GeneratedContract { diff --git a/packages/core-cairo/src/generate/vesting.ts b/packages/core-cairo/src/generate/vesting.ts new file mode 100644 index 000000000..77484943b --- /dev/null +++ b/packages/core-cairo/src/generate/vesting.ts @@ -0,0 +1,16 @@ +import { infoOptions } from '../set-info'; +import type { VestingOptions } from '../vesting'; +import { generateAlternatives } from './alternatives'; + +const blueprint = { + name: ['MyVesting'], + startDate: [new Date().toDateString()], + duration: ['90 days', '1 year'], + cliffDuration: ['0', '30 day'], + schedule: ['linear', 'custom'] as const, + info: infoOptions +}; + +export function* generateVestingOptions(): Generator> { + yield* generateAlternatives(blueprint); +} From 2db8cfb2cad01bf58fcee75c036bc64640793dd2 Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 24 Dec 2024 15:55:24 +0100 Subject: [PATCH 06/16] Add vesting page --- packages/core-cairo/src/utils/duration.ts | 2 +- packages/ui/src/cairo/App.svelte | 8 +++ packages/ui/src/cairo/VestingControls.svelte | 68 ++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 packages/ui/src/cairo/VestingControls.svelte diff --git a/packages/core-cairo/src/utils/duration.ts b/packages/core-cairo/src/utils/duration.ts index de24b28c2..ffc2fe5b5 100644 --- a/packages/core-cairo/src/utils/duration.ts +++ b/packages/core-cairo/src/utils/duration.ts @@ -14,7 +14,7 @@ const secondsForUnit = { second, minute, hour, day, week, month, year }; export function durationToTimestamp(duration: string): number { const match = duration.trim().match(durationPattern); - if (!match) { + if (!match || match.length < 2) { throw new Error('Bad duration format'); } diff --git a/packages/ui/src/cairo/App.svelte b/packages/ui/src/cairo/App.svelte index baa35ebe4..ade81e466 100644 --- a/packages/ui/src/cairo/App.svelte +++ b/packages/ui/src/cairo/App.svelte @@ -23,6 +23,7 @@ import { saveAs } from 'file-saver'; import { injectHyperlinks } from './inject-hyperlinks'; import { InitialOptions } from '../initial-options'; + import VestingControls from './VestingControls.svelte'; const dispatch = createEventDispatcher(); @@ -56,6 +57,7 @@ break; case 'Account': case 'Governor': + case 'Vesting': case 'ERC1155': case 'Custom': } @@ -121,6 +123,9 @@ + @@ -170,6 +175,9 @@
+
+ +
diff --git a/packages/ui/src/cairo/VestingControls.svelte b/packages/ui/src/cairo/VestingControls.svelte new file mode 100644 index 000000000..a860f184b --- /dev/null +++ b/packages/ui/src/cairo/VestingControls.svelte @@ -0,0 +1,68 @@ + + +
+

Settings

+ + + + + + + +
+ +
+

Vesting Schedule

+
+ + +
+
+ + From 3247bcd145a6f5104ed5677fcb7c0412cdb86c02 Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 24 Dec 2024 15:55:41 +0100 Subject: [PATCH 07/16] Add Vesting test cases --- packages/core-cairo/src/vesting.test.ts | 123 +++++++ packages/core-cairo/src/vesting.test.ts.md | 365 +++++++++++++++++++ packages/core-cairo/src/vesting.test.ts.snap | Bin 0 -> 1105 bytes 3 files changed, 488 insertions(+) create mode 100644 packages/core-cairo/src/vesting.test.ts create mode 100644 packages/core-cairo/src/vesting.test.ts.md create mode 100644 packages/core-cairo/src/vesting.test.ts.snap diff --git a/packages/core-cairo/src/vesting.test.ts b/packages/core-cairo/src/vesting.test.ts new file mode 100644 index 000000000..b8f8c0c33 --- /dev/null +++ b/packages/core-cairo/src/vesting.test.ts @@ -0,0 +1,123 @@ +import test from 'ava'; +import { OptionsError, vesting } from '.'; +import { buildVesting, VestingOptions } from './vesting'; +import { printContract } from './print'; + +const defaults: VestingOptions = { + name: 'MyVesting', + startDate: '', + duration: '0 day', + cliffDuration: '0 day', + schedule: 'linear' +}; + +const CUSTOM_NAME = 'CustomVesting'; +const CUSTOM_DATE = '2025-07-25T19:30'; +const CUSTOM_DURATION = '36 months'; +const CUSTOM_CLIFF = '90 days'; + +// +// Test helpers +// + +function testVesting(title: string, opts: Partial) { + test(title, t => { + const c = buildVesting({ + ...defaults, + ...opts + }); + t.snapshot(printContract(c)); + }); +} + +function testAPIEquivalence(title: string, opts?: VestingOptions) { + test(title, t => { + t.is(vesting.print(opts), printContract(buildVesting({ + ...defaults, + ...opts + }))); + }); +} + +// +// Snapshot tests +// + +testVesting('custom name', { + name: CUSTOM_NAME, +}); + +testVesting('custom start date', { + startDate: CUSTOM_DATE +}); + +testVesting('custom duration', { + duration: CUSTOM_DURATION +}); + +testVesting('custom cliff', { + duration: CUSTOM_DURATION, + cliffDuration: CUSTOM_CLIFF +}); + +testVesting('custom schedule', { + schedule: 'custom' +}); + +testVesting('all custom settings', { + startDate: CUSTOM_DATE, + duration: CUSTOM_DURATION, + cliffDuration: CUSTOM_CLIFF, + schedule: 'custom' +}); + +// +// API tests +// + +testAPIEquivalence('API custom name', { + ...defaults, + name: CUSTOM_NAME +}); + +testAPIEquivalence('API custom start date', { + ...defaults, + startDate: CUSTOM_DATE +}); + +testAPIEquivalence('API custom duration', { + ...defaults, + duration: CUSTOM_DURATION +}); + +testAPIEquivalence('API custom cliff', { + ...defaults, + duration: CUSTOM_DURATION, + cliffDuration: CUSTOM_CLIFF +}); + +testAPIEquivalence('API custom schedule', { + ...defaults, + schedule: 'custom' +}); + +testAPIEquivalence('API all custom settings', { + ...defaults, + startDate: CUSTOM_DATE, + duration: CUSTOM_DURATION, + cliffDuration: CUSTOM_CLIFF, + schedule: 'custom' +}); + +test('Vesting API isAccessControlRequired', async t => { + t.is(vesting.isAccessControlRequired({}), true); +}); + +test('cliff too high', async t => { + const error = t.throws(() => buildVesting({ + ...defaults, + duration: '20 days', + cliffDuration: '21 days' + })); + t.is((error as OptionsError).messages.cliffDuration, 'Cliff duration must be less than or equal to the total duration'); +}); diff --git a/packages/core-cairo/src/vesting.test.ts.md b/packages/core-cairo/src/vesting.test.ts.md new file mode 100644 index 000000000..205fcb2b1 --- /dev/null +++ b/packages/core-cairo/src/vesting.test.ts.md @@ -0,0 +1,365 @@ +# Snapshot report for `src/vesting.test.ts` + +The actual snapshot is saved in `vesting.test.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## custom name + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.20.0␊ + ␊ + #[starknet::contract]␊ + mod CustomVesting {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::finance::vesting::{LinearVestingSchedule, VestingComponent};␊ + use starknet::ContractAddress;␊ + ␊ + const START: u64 = 0;␊ + const DURATION: u64 = 0; // 0 day␊ + const CLIFF_DURATION: u64 = 0; // 0 day␊ + ␊ + component!(path: VestingComponent, storage: vesting, event: VestingEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl VestingImpl = VestingComponent::VestingImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + ␊ + // Internal␊ + impl VestingInternalImpl = VestingComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + vesting: VestingComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + VestingEvent: VestingComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.vesting.initializer(START, DURATION, CLIFF_DURATION);␊ + self.ownable.initializer(owner);␊ + }␊ + }␊ + ` + +## custom start date + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.20.0␊ + ␊ + #[starknet::contract]␊ + mod MyVesting {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::finance::vesting::{LinearVestingSchedule, VestingComponent};␊ + use starknet::ContractAddress;␊ + ␊ + const START: u64 = 1753471800; // 25 Jul 2025, 19:30 (UTC)␊ + const DURATION: u64 = 0; // 0 day␊ + const CLIFF_DURATION: u64 = 0; // 0 day␊ + ␊ + component!(path: VestingComponent, storage: vesting, event: VestingEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl VestingImpl = VestingComponent::VestingImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + ␊ + // Internal␊ + impl VestingInternalImpl = VestingComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + vesting: VestingComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + VestingEvent: VestingComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.vesting.initializer(START, DURATION, CLIFF_DURATION);␊ + self.ownable.initializer(owner);␊ + }␊ + }␊ + ` + +## custom duration + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.20.0␊ + ␊ + #[starknet::contract]␊ + mod MyVesting {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::finance::vesting::{LinearVestingSchedule, VestingComponent};␊ + use starknet::ContractAddress;␊ + ␊ + const START: u64 = 0;␊ + const DURATION: u64 = 93312000; // 36 months␊ + const CLIFF_DURATION: u64 = 0; // 0 day␊ + ␊ + component!(path: VestingComponent, storage: vesting, event: VestingEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl VestingImpl = VestingComponent::VestingImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + ␊ + // Internal␊ + impl VestingInternalImpl = VestingComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + vesting: VestingComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + VestingEvent: VestingComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.vesting.initializer(START, DURATION, CLIFF_DURATION);␊ + self.ownable.initializer(owner);␊ + }␊ + }␊ + ` + +## custom cliff + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.20.0␊ + ␊ + #[starknet::contract]␊ + mod MyVesting {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::finance::vesting::{LinearVestingSchedule, VestingComponent};␊ + use starknet::ContractAddress;␊ + ␊ + const START: u64 = 0;␊ + const DURATION: u64 = 93312000; // 36 months␊ + const CLIFF_DURATION: u64 = 7776000; // 90 days␊ + ␊ + component!(path: VestingComponent, storage: vesting, event: VestingEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl VestingImpl = VestingComponent::VestingImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + ␊ + // Internal␊ + impl VestingInternalImpl = VestingComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + vesting: VestingComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + VestingEvent: VestingComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.vesting.initializer(START, DURATION, CLIFF_DURATION);␊ + self.ownable.initializer(owner);␊ + }␊ + }␊ + ` + +## custom schedule + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.20.0␊ + ␊ + #[starknet::contract]␊ + mod MyVesting {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::finance::vesting::VestingComponent;␊ + use starknet::ContractAddress;␊ + ␊ + const START: u64 = 0;␊ + const DURATION: u64 = 0; // 0 day␊ + const CLIFF_DURATION: u64 = 0; // 0 day␊ + ␊ + component!(path: VestingComponent, storage: vesting, event: VestingEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl VestingImpl = VestingComponent::VestingImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + ␊ + // Internal␊ + impl VestingInternalImpl = VestingComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + vesting: VestingComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + VestingEvent: VestingComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.vesting.initializer(START, DURATION, CLIFF_DURATION);␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + impl VestingSchedule of VestingComponent::VestingScheduleTrait {␊ + fn calculate_vested_amount(␊ + self: @VestingComponent::ComponentState,␊ + token: ContractAddress,␊ + total_allocation: u256,␊ + timestamp: u64,␊ + start: u64,␊ + duration: u64,␊ + cliff: u64,␊ + ) -> u256 {␊ + // TODO: Must be implemented according to the desired vesting schedule;␊ + 0␊ + }␊ + }␊ + }␊ + ` + +## all custom settings + +> Snapshot 1 + + `// SPDX-License-Identifier: MIT␊ + // Compatible with OpenZeppelin Contracts for Cairo ^0.20.0␊ + ␊ + #[starknet::contract]␊ + mod MyVesting {␊ + use openzeppelin::access::ownable::OwnableComponent;␊ + use openzeppelin::finance::vesting::VestingComponent;␊ + use starknet::ContractAddress;␊ + ␊ + const START: u64 = 1753471800; // 25 Jul 2025, 19:30 (UTC)␊ + const DURATION: u64 = 93312000; // 36 months␊ + const CLIFF_DURATION: u64 = 7776000; // 90 days␊ + ␊ + component!(path: VestingComponent, storage: vesting, event: VestingEvent);␊ + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);␊ + ␊ + // External␊ + #[abi(embed_v0)]␊ + impl VestingImpl = VestingComponent::VestingImpl;␊ + #[abi(embed_v0)]␊ + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;␊ + ␊ + // Internal␊ + impl VestingInternalImpl = VestingComponent::InternalImpl;␊ + impl OwnableInternalImpl = OwnableComponent::InternalImpl;␊ + ␊ + #[storage]␊ + struct Storage {␊ + #[substorage(v0)]␊ + vesting: VestingComponent::Storage,␊ + #[substorage(v0)]␊ + ownable: OwnableComponent::Storage,␊ + }␊ + ␊ + #[event]␊ + #[derive(Drop, starknet::Event)]␊ + enum Event {␊ + #[flat]␊ + VestingEvent: VestingComponent::Event,␊ + #[flat]␊ + OwnableEvent: OwnableComponent::Event,␊ + }␊ + ␊ + #[constructor]␊ + fn constructor(ref self: ContractState, owner: ContractAddress) {␊ + self.vesting.initializer(START, DURATION, CLIFF_DURATION);␊ + self.ownable.initializer(owner);␊ + }␊ + ␊ + impl VestingSchedule of VestingComponent::VestingScheduleTrait {␊ + fn calculate_vested_amount(␊ + self: @VestingComponent::ComponentState,␊ + token: ContractAddress,␊ + total_allocation: u256,␊ + timestamp: u64,␊ + start: u64,␊ + duration: u64,␊ + cliff: u64,␊ + ) -> u256 {␊ + // TODO: Must be implemented according to the desired vesting schedule;␊ + 0␊ + }␊ + }␊ + }␊ + ` diff --git a/packages/core-cairo/src/vesting.test.ts.snap b/packages/core-cairo/src/vesting.test.ts.snap new file mode 100644 index 0000000000000000000000000000000000000000..7658f9cbba145487fa1644bc14940430ce7a4cd3 GIT binary patch literal 1105 zcmV-X1g`r*RzVb6`5%i200000000B+nayt7Mi9r{7L5b-)Sil*Cdi=@KwGpFTlK05YRCzo!mf%) zDcT0Eup)=i!pmKjyL7&wLoa=g^vU`LeT9P5k|rrS?a6hU)rs64{&!~f<2MxF1`95| zbM@QrsF~(??j=gg5Cjd8DM2H(lv@4z*7vEVKi<0Y(bo1>`upJHtxrGvBA^<@_SU8iRbX91ptsJ1R0@tZL|rYo`*^i zB3}r);7G`1eUW-31qwa9;=zCkDm*0QVm$&OZ%&v%8k-TP-V*(UqXVX)P+VO~UizK~ zdF1;sDm83nHl7qp!*q7^WJVx4xDOA&9hQ2IpFTO7jVIq;c0#ucF8K6S$!c^me*Ad; zE?Z-49K73(s9ur{#~n~w#`HN7FhM(j7btXL`Y65Y44dWZnJvkg>=$yzG?&Ztx?Onm zQe!MA&+hL%qYKu?aDo2(!tI=8jVz3K?#wv7ey|Y*A*KGW^1M$q)%bAuH_FDZQ}&Vx zBVUiISV4u+#u#}xV^IpH;<@R#GJ>)}V+dt$YigSgzf;X*#7xyX7fQ#8r(wEoGkeR} zB@1KPE>f5N@?FvpjFEBvfl9th8-l2)ta7)r^>k)#e2m!zwvS^OIi;<)US(?|6iEnK zLnW$!Q+?@0d9gPIlyx{)K3-njO_7uyFXG5Ha+-7*uZ02urM7mA0Vw1Ffy(?jX~xoX zwsyR9O3|f{?V9P?WrAr&IeU$9J3Drq;*4=>=ZM+ng@l=#ibOWLF$wI%{R0T4&`V`mVp(EYVp(z% zS;7rVJkElk0VJIYNbXpW+-Q&t27`nAsl$vX$^yj##RA0w<+@)^lsPsxnX>(7g(-h* zzYk2QuO$1i#B#!N!gAt$Duxvg^?zNaxdXj<2(%q{W%R~ zB6Pd3$$cg8_2zKJWhPx4$f;;(d5&TusdXDo`J8etz3h)V0!eTGpk~8DRGNlSb|zQ* zvJXiO?fD;n4PDuLT&=wWI}fvwOR0kO@iUtoPY9eQO2Y!PdBG5cMjxo>$=FZns3qtn zf{%*D=;yn*0Q|6`=@xgZdS##}=a5$is8bQOSsmQ|qEa1ve%n?D*Zv*MzJ1!N!d4Zw Xs<2fB*s20owJ literal 0 HcmV?d00001 From 5248e1754576ff3e30f294a63af0874234faced6 Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 24 Dec 2024 18:07:06 +0100 Subject: [PATCH 08/16] Fix issues --- packages/core-cairo/src/generate/sources.ts | 5 +++-- packages/core-cairo/src/generate/vesting.ts | 2 +- packages/core-cairo/src/test.ts | 25 +++++++++++++++------ packages/core-cairo/src/vesting.test.ts | 2 +- packages/core-cairo/src/vesting.ts | 2 +- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/packages/core-cairo/src/generate/sources.ts b/packages/core-cairo/src/generate/sources.ts index b56f0b1a3..e6a5ed818 100644 --- a/packages/core-cairo/src/generate/sources.ts +++ b/packages/core-cairo/src/generate/sources.ts @@ -100,8 +100,9 @@ function generateContractSubset(subset: Subset, kind?: Kind): GeneratedContract[ } else { 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), + ...findCover(contracts.filter(c => c.options.upgradeable === undefined), getParents), + ...findCover(contracts.filter(c => c.options.upgradeable === true), getParents), + ...findCover(contracts.filter(c => c.options.upgradeable === false), getParents), ]; } } diff --git a/packages/core-cairo/src/generate/vesting.ts b/packages/core-cairo/src/generate/vesting.ts index 77484943b..186a45cea 100644 --- a/packages/core-cairo/src/generate/vesting.ts +++ b/packages/core-cairo/src/generate/vesting.ts @@ -4,7 +4,7 @@ import { generateAlternatives } from './alternatives'; const blueprint = { name: ['MyVesting'], - startDate: [new Date().toDateString()], + startDate: ['2024-12-31T23:59'], duration: ['90 days', '1 year'], cliffDuration: ['0', '30 day'], schedule: ['linear', 'custom'] as const, diff --git a/packages/core-cairo/src/test.ts b/packages/core-cairo/src/test.ts index a6d7fce7c..4c3611e1f 100644 --- a/packages/core-cairo/src/test.ts +++ b/packages/core-cairo/src/test.ts @@ -63,12 +63,23 @@ test('is access control required', async t => { for (const contract of generateSources('all')) { const regexOwnable = /(use openzeppelin::access::ownable::OwnableComponent)/gm; - if (contract.options.kind !== 'Account' && contract.options.kind !== 'Governor' && !contract.options.access) { - if (isAccessControlRequired(contract.options)) { - t.regex(contract.source, regexOwnable, JSON.stringify(contract.options)); - } else { - t.notRegex(contract.source, regexOwnable, JSON.stringify(contract.options)); - } + switch (contract.options.kind) { + case 'Account': + case 'Governor': + case 'Vesting': + // These contracts have no acess control + return + case 'ERC20': + case 'ERC721': + case 'ERC1155': + case 'Custom': + if (!contract.options.access) { + if (isAccessControlRequired(contract.options)) { + t.regex(contract.source, regexOwnable, JSON.stringify(contract.options)); + } else { + t.notRegex(contract.source, regexOwnable, JSON.stringify(contract.options)); + } + } } } -}); \ No newline at end of file +}); diff --git a/packages/core-cairo/src/vesting.test.ts b/packages/core-cairo/src/vesting.test.ts index b8f8c0c33..6a931f275 100644 --- a/packages/core-cairo/src/vesting.test.ts +++ b/packages/core-cairo/src/vesting.test.ts @@ -12,7 +12,7 @@ const defaults: VestingOptions = { }; const CUSTOM_NAME = 'CustomVesting'; -const CUSTOM_DATE = '2025-07-25T19:30'; +const CUSTOM_DATE = '2024-12-31T23:59'; const CUSTOM_DURATION = '36 months'; const CUSTOM_CLIFF = '90 days'; diff --git a/packages/core-cairo/src/vesting.ts b/packages/core-cairo/src/vesting.ts index a38afb170..543ff0534 100644 --- a/packages/core-cairo/src/vesting.ts +++ b/packages/core-cairo/src/vesting.ts @@ -1,9 +1,9 @@ import { BaseImplementedTrait, Contract, ContractBuilder } from './contract'; +import { contractDefaults as commonDefaults } from './common-options'; import { setAccessControl } from './set-access-control'; import { setUpgradeable } from './set-upgradeable'; import { Info, setInfo } from './set-info'; import { defineComponents } from './utils/define-components'; -import { contractDefaults as commonDefaults } from './common-options'; import { printContract } from './print'; import { OptionsError } from './error'; import { durationToTimestamp } from './utils/duration'; From 6d7bbc4d6a23dd5851c9f3cc435bc59b9aab23ac Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 24 Dec 2024 18:08:57 +0100 Subject: [PATCH 09/16] Fix test snapshots --- packages/core-cairo/src/vesting.test.ts.md | 4 ++-- packages/core-cairo/src/vesting.test.ts.snap | Bin 1105 -> 1105 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-cairo/src/vesting.test.ts.md b/packages/core-cairo/src/vesting.test.ts.md index 205fcb2b1..6c35d8c65 100644 --- a/packages/core-cairo/src/vesting.test.ts.md +++ b/packages/core-cairo/src/vesting.test.ts.md @@ -72,7 +72,7 @@ Generated by [AVA](https://avajs.dev). use openzeppelin::finance::vesting::{LinearVestingSchedule, VestingComponent};␊ use starknet::ContractAddress;␊ ␊ - const START: u64 = 1753471800; // 25 Jul 2025, 19:30 (UTC)␊ + const START: u64 = 1735689540; // 31 Dec 2024, 23:59 (UTC)␊ const DURATION: u64 = 0; // 0 day␊ const CLIFF_DURATION: u64 = 0; // 0 day␊ ␊ @@ -307,7 +307,7 @@ Generated by [AVA](https://avajs.dev). use openzeppelin::finance::vesting::VestingComponent;␊ use starknet::ContractAddress;␊ ␊ - const START: u64 = 1753471800; // 25 Jul 2025, 19:30 (UTC)␊ + const START: u64 = 1735689540; // 31 Dec 2024, 23:59 (UTC)␊ const DURATION: u64 = 93312000; // 36 months␊ const CLIFF_DURATION: u64 = 7776000; // 90 days␊ ␊ diff --git a/packages/core-cairo/src/vesting.test.ts.snap b/packages/core-cairo/src/vesting.test.ts.snap index 7658f9cbba145487fa1644bc14940430ce7a4cd3..7051100b2580ab1369a3d8e9432ec7fd87aa4d36 100644 GIT binary patch delta 1103 zcmV-V1hD(j2+;_CK~_N^Q*L2!b7*gLAa*kf0|3+IH(U8!RAy2xI$k&F*s3gI&8iW$ z_f%Z2oyTSpP~{(s2mk;800003?U~JP+eQ$_-3E;V_0*nSj55M?!--n0L85DX(&z>I7#*^qKfEOrqZu&U6YYpnl_1bNC{90oqC{OS2 zKc@@U#9)Em{G!u3OB-1b^30iWa{X{43PK9~-(-28YO3+k;BS9|#*%Zka=f$( z(It=Vn(5hPf@wxMdy7#sJ$9V@jBzUGh}q`3gqfR?L^`@LTs2k=1CW;nAjuqP@TjiC z$e@G~=ikD}>6`x@LiP@O2mJ>}2m5OT^!8wcE_6HHeFwTda&QFAXR~3;a>jDTa>jDz zzsr9aX*lD>5!FnJ8p_-&QRYiand^B`IqLQHx}A;zP#*#*^io-tSe96pSeD#GmT(7i79_bY3t(q0s~Cx-#+-I%)}eiQq?} zVi9`TF3tfzC~0=`yH&L^P?U4Xssq%Ch}x_U?tERU4!*i$tAlI*4rbpzZB=2b3R_j! Vsse0P0XMX&_zO0umkqxu008;@8Fc^v delta 1103 zcmV-V1hD(j2+;_CK~_N^Q*L2!b7*gLAa*kf0|5MOs?%?j?9(u0n{{|5l@j=-5ezjx z7tgDDE=8U;>-isx2mk;800003?U~JP+eQ$_-4=}l_0*nSj51PzfXK_j)4TK)Rg_o=2o-n#SA*7jEV`{3iPPe1(RR<{e&?~Z@iIbj|Og*#&( zg=PW6n84|H*6Ma)B*Tbmw%`aam|nspLh%zu5ppJAB!!NtrxgS;h7o14grD8rp1bR| zTKAqQP2+Qcnh<}_7@oC4>BA^<@_SU8iRbX91ptsJ1R0@tZL|rYo`*^iB3}r);7G`1 zeUW-31qwa9;=zCkDm*0QVm$&OZ%&v%8k-TP-V*(UqXVX)P+VO~UizK~dF1;sDm83n zHl7qp!*q7^WJVx4xDOA&9hQ2IpFTO7jVIq;c0#ucF8F`+Rmo~}GJgDc{w`Z%Y#hAX zj;LOe4aXf&TE_G_5->qKfEOrqVfrY&>kOOa>X|Lcnd}#G#x$49^txSm^ipFiD9`Ti zJ);ZO#&CiD{KD;=WsNM1c<#(Ny?(F}1tF#WukyT4HP!fV_&3VNuT%Dt2_s*Rs#rmV z(Z(2gIAecN3a8?^>9{h2vO!}AWp8V0n-0HI&1A$()jAhS$BCz5x^6Rj%h)9gW7;lK zm;Uly(h!W1asGiyzDpZ|sHm)Rx3l$hW^R0p*#)+bV;MQ6t+!rfYa$wI%{R0T4&`V`mVp(EYVp(z%S;7rV zJkElk0VJIYNbXpW+-Q&t27`nAsl$vX$^yj##RA0w<+@)^lsPsxnX>(7g(-h*zYk2Q zuO$1i#B#!N!gAt$Wg>LD zu*rQT@b%_!#bqX48_20>XnBreBdK*8P5GR1F1_rJIs!>=|Da~WLR6ZDQFbO*`?3#7 z4ej|Ke+^yPdt9x(13M41kxQw9_3<;C98U8K^>C4xtf zipA*XySM=Su%hV}cdL43peW~%R|lw55w%$z-2S3c9ejS Date: Tue, 24 Dec 2024 18:11:56 +0100 Subject: [PATCH 10/16] Update changelog --- packages/core-cairo/CHANGELOG.md | 3 ++- packages/core-cairo/package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core-cairo/CHANGELOG.md b/packages/core-cairo/CHANGELOG.md index 4b6d5c18b..3f6ad279e 100644 --- a/packages/core-cairo/CHANGELOG.md +++ b/packages/core-cairo/CHANGELOG.md @@ -1,7 +1,8 @@ # Changelog -## Unreleased +## 0.21.0 (2024-12-24) +- Add Vesting tab. ([#425](https://github.com/OpenZeppelin/contracts-wizard/pull/425)) - Update Contracts Wizard license to AGPLv3. ([#424](https://github.com/OpenZeppelin/contracts-wizard/pull/424)) ## 0.20.1 (2024-12-17) diff --git a/packages/core-cairo/package.json b/packages/core-cairo/package.json index 0793295a9..1c85b0bf2 100644 --- a/packages/core-cairo/package.json +++ b/packages/core-cairo/package.json @@ -1,6 +1,6 @@ { "name": "@openzeppelin/wizard-cairo", - "version": "0.20.1", + "version": "0.21.0", "description": "A boilerplate generator to get started with OpenZeppelin Contracts for Cairo", "license": "AGPL-3.0-only", "repository": "https://github.com/OpenZeppelin/contracts-wizard", From 2112bbf6ee07ce6a065f180bc6db605c7a751662 Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 24 Dec 2024 18:37:31 +0100 Subject: [PATCH 11/16] Fix compilation issue --- packages/core-cairo/src/generate/sources.ts | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/core-cairo/src/generate/sources.ts b/packages/core-cairo/src/generate/sources.ts index e6a5ed818..6c91cd346 100644 --- a/packages/core-cairo/src/generate/sources.ts +++ b/packages/core-cairo/src/generate/sources.ts @@ -99,10 +99,24 @@ function generateContractSubset(subset: Subset, kind?: Kind): GeneratedContract[ return contracts; } else { const getParents = (c: GeneratedContract) => c.contract.components.map(p => p.path); + function filterByUpgradeableSetTo(isUpgradeable: boolean) { + return (c: GeneratedContract) => { + switch (c.options.kind) { + case 'Vesting': + return isUpgradeable === false; + case 'Account': + case 'ERC20': + case 'ERC721': + case 'ERC1155': + case 'Governor': + case 'Custom': + return c.options.upgradeable === isUpgradeable; + } + } + } return [ - ...findCover(contracts.filter(c => c.options.upgradeable === undefined), getParents), - ...findCover(contracts.filter(c => c.options.upgradeable === true), getParents), - ...findCover(contracts.filter(c => c.options.upgradeable === false), getParents), + ...findCover(contracts.filter(filterByUpgradeableSetTo(true)), getParents), + ...findCover(contracts.filter(filterByUpgradeableSetTo(false)), getParents), ]; } } From 17645fda93f0e0632ff34524a358257164cac656 Mon Sep 17 00:00:00 2001 From: immrsd Date: Mon, 30 Dec 2024 12:16:58 +0100 Subject: [PATCH 12/16] Fix tips --- packages/ui/src/cairo/VestingControls.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/cairo/VestingControls.svelte b/packages/ui/src/cairo/VestingControls.svelte index a860f184b..06fe80f81 100644 --- a/packages/ui/src/cairo/VestingControls.svelte +++ b/packages/ui/src/cairo/VestingControls.svelte @@ -51,15 +51,15 @@ From 5e093d8131d7a8a2d709014c9342e1aa7fca5db5 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Tue, 7 Jan 2025 15:01:41 -0500 Subject: [PATCH 13/16] Apply style to datetime-local --- packages/ui/src/styles/global.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/styles/global.css b/packages/ui/src/styles/global.css index 4f1336cde..3ccefb126 100644 --- a/packages/ui/src/styles/global.css +++ b/packages/ui/src/styles/global.css @@ -21,7 +21,7 @@ svg.icon { fill: currentColor; } -input:not([type]), input[type="text"], input[type="number"] { +input:not([type]), input[type="text"], input[type="number"], input[type="datetime-local"] { border: 1px solid var(--gray-3); padding: var(--size-2) var(--size-3); border-radius: 6px; From f823667c6e85404ea3a7dc711b3bc8326453598f Mon Sep 17 00:00:00 2001 From: immrsd Date: Thu, 9 Jan 2025 19:34:52 +0100 Subject: [PATCH 14/16] Add guards for unhandled new contract kinds --- packages/core-cairo/src/generate/sources.ts | 3 +++ packages/core-cairo/src/test.ts | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/core-cairo/src/generate/sources.ts b/packages/core-cairo/src/generate/sources.ts index 6c91cd346..5be28487c 100644 --- a/packages/core-cairo/src/generate/sources.ts +++ b/packages/core-cairo/src/generate/sources.ts @@ -111,6 +111,9 @@ function generateContractSubset(subset: Subset, kind?: Kind): GeneratedContract[ case 'Governor': case 'Custom': return c.options.upgradeable === isUpgradeable; + default: + const _: never = c.options; + throw new Error('Unknown kind'); } } } diff --git a/packages/core-cairo/src/test.ts b/packages/core-cairo/src/test.ts index 4c3611e1f..967e17ece 100644 --- a/packages/core-cairo/src/test.ts +++ b/packages/core-cairo/src/test.ts @@ -7,7 +7,6 @@ import { generateSources, writeGeneratedSources } from './generate/sources'; import type { GenericOptions, KindedOptions } from './build-generic'; import { custom, erc20, erc721, erc1155 } from './api'; - interface Context { generatedSourcesPath: string } @@ -67,8 +66,8 @@ test('is access control required', async t => { case 'Account': case 'Governor': case 'Vesting': - // These contracts have no acess control - return + // These contracts have no access control option + break; case 'ERC20': case 'ERC721': case 'ERC1155': @@ -80,6 +79,10 @@ test('is access control required', async t => { t.notRegex(contract.source, regexOwnable, JSON.stringify(contract.options)); } } + break; + default: + const _: never = contract.options; + throw new Error('Unknown kind'); } } }); From 548138981e538540aa3626e8d9762377bc0c9093 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Thu, 9 Jan 2025 13:41:18 -0500 Subject: [PATCH 15/16] Update changelog date --- packages/core-cairo/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-cairo/CHANGELOG.md b/packages/core-cairo/CHANGELOG.md index 3f6ad279e..a62296b4f 100644 --- a/packages/core-cairo/CHANGELOG.md +++ b/packages/core-cairo/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.21.0 (2024-12-24) +## 0.21.0 (2025-01-09) - Add Vesting tab. ([#425](https://github.com/OpenZeppelin/contracts-wizard/pull/425)) - Update Contracts Wizard license to AGPLv3. ([#424](https://github.com/OpenZeppelin/contracts-wizard/pull/424)) From 600ab6a61b9a669dbf07520829193d6d2053ff40 Mon Sep 17 00:00:00 2001 From: immrsd Date: Thu, 9 Jan 2025 19:49:12 +0100 Subject: [PATCH 16/16] Fix vesting generate options --- packages/core-cairo/src/generate/vesting.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-cairo/src/generate/vesting.ts b/packages/core-cairo/src/generate/vesting.ts index 186a45cea..45de06143 100644 --- a/packages/core-cairo/src/generate/vesting.ts +++ b/packages/core-cairo/src/generate/vesting.ts @@ -6,7 +6,7 @@ const blueprint = { name: ['MyVesting'], startDate: ['2024-12-31T23:59'], duration: ['90 days', '1 year'], - cliffDuration: ['0', '30 day'], + cliffDuration: ['0 seconds', '30 day'], schedule: ['linear', 'custom'] as const, info: infoOptions };