From b6f21a9907cd04b9044c5d904768f8f445e19b1a Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Tue, 17 Jun 2025 11:39:01 -0400 Subject: [PATCH 01/54] prototype --- packages/core/solidity/src/contract.ts | 2 +- packages/core/solidity/src/custom.ts | 41 ++++++++++++++++++- packages/core/solidity/src/generate/custom.ts | 1 + .../ui/src/solidity/CustomControls.svelte | 5 +++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/packages/core/solidity/src/contract.ts b/packages/core/solidity/src/contract.ts index 6ed276124..bf0f1b0a9 100644 --- a/packages/core/solidity/src/contract.ts +++ b/packages/core/solidity/src/contract.ts @@ -52,7 +52,7 @@ export interface ContractFunction extends BaseFunction { comments: string[]; } -export type FunctionKind = 'internal' | 'public'; +export type FunctionKind = 'internal' | 'public' | 'external'; export type FunctionMutability = (typeof mutabilityRank)[number]; // Order is important diff --git a/packages/core/solidity/src/custom.ts b/packages/core/solidity/src/custom.ts index 5baaf31b5..4ff7fec12 100644 --- a/packages/core/solidity/src/custom.ts +++ b/packages/core/solidity/src/custom.ts @@ -1,20 +1,23 @@ -import type { Contract } from './contract'; +import type { BaseFunction, Contract } from './contract'; import { ContractBuilder } from './contract'; import type { CommonOptions } from './common-options'; import { withCommonDefaults, defaults as commonDefaults } from './common-options'; import { setUpgradeable } from './set-upgradeable'; import { setInfo } from './set-info'; -import { setAccessControl } from './set-access-control'; +import { Access, requireAccessControl, setAccessControl } from './set-access-control'; import { addPausable } from './add-pausable'; import { printContract } from './print'; +import { defineFunctions } from './utils/define-functions'; export interface CustomOptions extends CommonOptions { name: string; + superchainInteropFunction?: string; pausable?: boolean; } export const defaults: Required = { name: 'MyContract', + superchainInteropFunction: '', pausable: false, access: commonDefaults.access, upgradeable: commonDefaults.upgradeable, @@ -25,6 +28,7 @@ function withDefaults(opts: CustomOptions): Required { return { ...opts, ...withCommonDefaults(opts), + superchainInteropFunction: opts.superchainInteropFunction ?? defaults.superchainInteropFunction, pausable: opts.pausable ?? defaults.pausable, }; } @@ -44,6 +48,10 @@ export function buildCustom(opts: CustomOptions): Contract { const { access, upgradeable, info } = allOpts; + if (allOpts.superchainInteropFunction) { + addSuperchainInterop(c, allOpts.superchainInteropFunction, allOpts.access, allOpts.pausable); + } + if (allOpts.pausable) { addPausable(c, access, []); } @@ -54,3 +62,32 @@ export function buildCustom(opts: CustomOptions): Contract { return c; } + +function addSuperchainInterop(c: ContractBuilder, superchainInteropFunction: string, access: Access, pausable: boolean) { + // Add source function + const sourceFn: BaseFunction = { + name: `call${superchainInteropFunction.replace(/^(.)/i, c => c.toUpperCase())}`, + kind: 'public' as const, + args: [], + }; + + requireAccessControl(c, sourceFn, access, 'CROSS_CHAIN_CALLER', 'crossChainCaller'); + c.addFunctionCode( + `messenger.sendMessage(_toChainId, address(this), abi.encodeCall(this.${superchainInteropFunction}, (/* TODO: Add arguments */)));`, + sourceFn, + ); + + // Add destination function + const destFn: BaseFunction = { + name: superchainInteropFunction, + kind: 'external' as const, + args: [], + }; + + if (pausable) { + c.addModifier('whenNotPaused', destFn); + } + + c.addFunctionCode('// TODO: Implement logic for the function that will be called from another chain', destFn); +} + diff --git a/packages/core/solidity/src/generate/custom.ts b/packages/core/solidity/src/generate/custom.ts index 40207666a..cfc8fc239 100644 --- a/packages/core/solidity/src/generate/custom.ts +++ b/packages/core/solidity/src/generate/custom.ts @@ -8,6 +8,7 @@ const booleans = [true, false]; const blueprint = { name: ['MyContract'], + superchainInteropFunction: ['', 'myFunction'], pausable: booleans, access: accessOptions, upgradeable: upgradeableOptions, diff --git a/packages/ui/src/solidity/CustomControls.svelte b/packages/ui/src/solidity/CustomControls.svelte index bb54fd2f5..109cc956d 100644 --- a/packages/ui/src/solidity/CustomControls.svelte +++ b/packages/ui/src/solidity/CustomControls.svelte @@ -29,6 +29,11 @@

Features

+ +
-

Features

+

Cross-Chain Messaging

 
+
+ +
+

Features

-

Cross-Chain Messaging

 
+ +
+ + + +

Cross-Chain Messaging

 
+
From 7a02121918a50796dc9cc3ba55830ef4504b7426 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Fri, 4 Jul 2025 15:09:13 -0400 Subject: [PATCH 05/54] Use svelte component for crosschain messaging --- .../ui/src/common/ExpandableCheckbox.svelte | 3 ++ .../src/common/ParentExpandableSection.svelte | 6 +++- .../CrossChainMessagingSection.svelte | 33 +++++++++++++++++++ .../ui/src/solidity/CustomControls.svelte | 21 ++---------- 4 files changed, 44 insertions(+), 19 deletions(-) create mode 100644 packages/ui/src/solidity/CrossChainMessagingSection.svelte diff --git a/packages/ui/src/common/ExpandableCheckbox.svelte b/packages/ui/src/common/ExpandableCheckbox.svelte index 6a164677d..ec7759413 100644 --- a/packages/ui/src/common/ExpandableCheckbox.svelte +++ b/packages/ui/src/common/ExpandableCheckbox.svelte @@ -1,8 +1,10 @@ + + + + + \ No newline at end of file diff --git a/packages/ui/src/solidity/CustomControls.svelte b/packages/ui/src/solidity/CustomControls.svelte index c18b57e32..8898313ed 100644 --- a/packages/ui/src/solidity/CustomControls.svelte +++ b/packages/ui/src/solidity/CustomControls.svelte @@ -7,8 +7,7 @@ import AccessControlSection from './AccessControlSection.svelte'; import UpgradeabilitySection from './UpgradeabilitySection.svelte'; import InfoSection from './InfoSection.svelte'; - - import OPIcon from '../common/icons/OPIcon.svelte'; + import CrossChainMessagingSection from './CrossChainMessagingSection.svelte'; export let opts: Required = { kind: 'Custom', @@ -16,8 +15,7 @@ info: { ...infoDefaults }, // create new object since Info is nested }; - let crossChainMessagingEnabled = false; - $: opts.crossChainMessaging = crossChainMessagingEnabled ? 'superchain' : false; + $: requireAccessControl = custom.isAccessControlRequired(opts); @@ -31,20 +29,7 @@ -
- -
- - - -

Cross-Chain Messaging

 
-
- - -
+

Features

From ce336b972a2adafc6c570a5f617a919e42cb0270 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Fri, 4 Jul 2025 15:16:09 -0400 Subject: [PATCH 06/54] Simplify options passing --- .../src/solidity/CrossChainMessagingSection.svelte | 13 +++++-------- packages/ui/src/solidity/CustomControls.svelte | 7 ++++--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/ui/src/solidity/CrossChainMessagingSection.svelte b/packages/ui/src/solidity/CrossChainMessagingSection.svelte index f3fb54a20..b240df33d 100644 --- a/packages/ui/src/solidity/CrossChainMessagingSection.svelte +++ b/packages/ui/src/solidity/CrossChainMessagingSection.svelte @@ -1,19 +1,16 @@ @@ -25,8 +22,8 @@ diff --git a/packages/ui/src/solidity/CustomControls.svelte b/packages/ui/src/solidity/CustomControls.svelte index 8898313ed..978070e7c 100644 --- a/packages/ui/src/solidity/CustomControls.svelte +++ b/packages/ui/src/solidity/CustomControls.svelte @@ -15,7 +15,8 @@ info: { ...infoDefaults }, // create new object since Info is nested }; - + let crossChainMessagingEnabled = false; + $: opts.crossChainMessaging = crossChainMessagingEnabled ? 'superchain' : false; $: requireAccessControl = custom.isAccessControlRequired(opts); @@ -29,8 +30,6 @@
- -

Features

@@ -46,6 +45,8 @@
+ + From 4302bc9ebeb3551f378214afd5b2ff2551d85ce9 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Fri, 4 Jul 2025 15:26:00 -0400 Subject: [PATCH 07/54] Sanitize --- packages/core/solidity/src/custom.ts | 9 ++++++--- .../ui/src/solidity/CrossChainMessagingSection.svelte | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/core/solidity/src/custom.ts b/packages/core/solidity/src/custom.ts index a63762ae5..b8b5b69f9 100644 --- a/packages/core/solidity/src/custom.ts +++ b/packages/core/solidity/src/custom.ts @@ -8,6 +8,7 @@ import { Access, requireAccessControl, setAccessControl } from './set-access-con import { addPausable } from './add-pausable'; import { printContract } from './print'; import { defineFunctions } from './utils/define-functions'; +import { toIdentifier } from './utils/to-identifier'; export const CrossChainMessagingOptions = [false, 'superchain'] as const; export type CrossChainMessaging = (typeof CrossChainMessagingOptions)[number]; @@ -70,22 +71,24 @@ export function buildCustom(opts: CustomOptions): Contract { } function addSuperchainInterop(c: ContractBuilder, functionName: string, access: Access, pausable: boolean) { + const sanitizedFunctionName = toIdentifier(functionName, false); + // Add source function const sourceFn: BaseFunction = { - name: `call${functionName.replace(/^(.)/i, c => c.toUpperCase())}`, + name: `call${sanitizedFunctionName.replace(/^(.)/, c => c.toUpperCase())}`, kind: 'public' as const, args: [], }; requireAccessControl(c, sourceFn, access, 'CROSS_CHAIN_CALLER', 'crossChainCaller'); c.addFunctionCode( - `messenger.sendMessage(_toChainId, address(this), abi.encodeCall(this.${functionName}, (/* TODO: Add arguments */)));`, + `messenger.sendMessage(_toChainId, address(this), abi.encodeCall(this.${sanitizedFunctionName}, (/* TODO: Add arguments */)));`, sourceFn, ); // Add destination function const destFn: BaseFunction = { - name: functionName, + name: sanitizedFunctionName, kind: 'external' as const, args: [], }; diff --git a/packages/ui/src/solidity/CrossChainMessagingSection.svelte b/packages/ui/src/solidity/CrossChainMessagingSection.svelte index b240df33d..fdfc98b2a 100644 --- a/packages/ui/src/solidity/CrossChainMessagingSection.svelte +++ b/packages/ui/src/solidity/CrossChainMessagingSection.svelte @@ -24,6 +24,7 @@ From bb68267f7f25fbed594a724ca70533a5669d6643 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Fri, 4 Jul 2025 15:31:50 -0400 Subject: [PATCH 08/54] Adjust min width --- packages/ui/src/solidity/App.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/solidity/App.svelte b/packages/ui/src/solidity/App.svelte index db47ce539..19ddb5249 100644 --- a/packages/ui/src/solidity/App.svelte +++ b/packages/ui/src/solidity/App.svelte @@ -347,7 +347,7 @@
From b4a85d9567200d1c785d93cbbd21e42f2c1cd3e2 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Fri, 4 Jul 2025 15:50:22 -0400 Subject: [PATCH 09/54] Track errors --- packages/core/solidity/src/custom.ts | 9 +++++++-- packages/ui/src/solidity/App.svelte | 2 +- .../ui/src/solidity/CrossChainMessagingSection.svelte | 6 +++++- packages/ui/src/solidity/CustomControls.svelte | 6 ++++-- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/core/solidity/src/custom.ts b/packages/core/solidity/src/custom.ts index b8b5b69f9..d995e9949 100644 --- a/packages/core/solidity/src/custom.ts +++ b/packages/core/solidity/src/custom.ts @@ -7,8 +7,8 @@ import { setInfo } from './set-info'; import { Access, requireAccessControl, setAccessControl } from './set-access-control'; import { addPausable } from './add-pausable'; import { printContract } from './print'; -import { defineFunctions } from './utils/define-functions'; import { toIdentifier } from './utils/to-identifier'; +import { OptionsError } from './error'; export const CrossChainMessagingOptions = [false, 'superchain'] as const; export type CrossChainMessaging = (typeof CrossChainMessagingOptions)[number]; @@ -35,7 +35,7 @@ function withDefaults(opts: CustomOptions): Required { ...opts, ...withCommonDefaults(opts), crossChainMessaging: opts.crossChainMessaging ?? defaults.crossChainMessaging, - crossChainFunctionName: opts.crossChainFunctionName?.length ? opts.crossChainFunctionName : defaults.crossChainFunctionName, + crossChainFunctionName: opts.crossChainFunctionName ?? defaults.crossChainFunctionName, pausable: opts.pausable ?? defaults.pausable, }; } @@ -72,6 +72,11 @@ export function buildCustom(opts: CustomOptions): Contract { function addSuperchainInterop(c: ContractBuilder, functionName: string, access: Access, pausable: boolean) { const sanitizedFunctionName = toIdentifier(functionName, false); + if (sanitizedFunctionName.length === 0) { + throw new OptionsError({ + crossChainFunctionName: 'Not a valid function name', + }); + } // Add source function const sourceFn: BaseFunction = { diff --git a/packages/ui/src/solidity/App.svelte b/packages/ui/src/solidity/App.svelte index 19ddb5249..674f716c7 100644 --- a/packages/ui/src/solidity/App.svelte +++ b/packages/ui/src/solidity/App.svelte @@ -371,7 +371,7 @@
- +
diff --git a/packages/ui/src/solidity/CrossChainMessagingSection.svelte b/packages/ui/src/solidity/CrossChainMessagingSection.svelte index fdfc98b2a..781d29c81 100644 --- a/packages/ui/src/solidity/CrossChainMessagingSection.svelte +++ b/packages/ui/src/solidity/CrossChainMessagingSection.svelte @@ -2,9 +2,12 @@ import HelpTooltip from '../common/HelpTooltip.svelte'; import ExpandableCheckbox from '../common/ExpandableCheckbox.svelte'; import OPIcon from '../common/icons/OPIcon.svelte'; + import { error } from '../common/error-tooltip'; + import type { OptionsErrorMessages } from '@openzeppelin/wizard'; export let enabled: boolean; export let functionName: string; + export let errors: undefined | OptionsErrorMessages; diff --git a/packages/ui/src/solidity/CustomControls.svelte b/packages/ui/src/solidity/CustomControls.svelte index 978070e7c..a317fbef0 100644 --- a/packages/ui/src/solidity/CustomControls.svelte +++ b/packages/ui/src/solidity/CustomControls.svelte @@ -1,7 +1,7 @@ - - - - - \ No newline at end of file + export let enabled: boolean; + export let functionName: string; + export let errors: undefined | OptionsErrorMessages; + + + + + diff --git a/packages/ui/src/solidity/CustomControls.svelte b/packages/ui/src/solidity/CustomControls.svelte index a317fbef0..7b1ae85a0 100644 --- a/packages/ui/src/solidity/CustomControls.svelte +++ b/packages/ui/src/solidity/CustomControls.svelte @@ -47,7 +47,11 @@
- + From 019c9ba03adf7e12aa8b609378656a9c5087e185 Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Mon, 28 Jul 2025 16:51:55 -0400 Subject: [PATCH 24/54] Add tooltip when enabled --- .../CrossChainMessagingSection.svelte | 21 ++++++++++++++++++- packages/ui/src/solidity/ERC20Controls.svelte | 4 ++-- .../solidity/RealWorldAssetControls.svelte | 4 ++-- .../ui/src/solidity/StablecoinControls.svelte | 4 ++-- .../ui/src/solidity/superchain-tooltip.ts | 17 ++++++++++++--- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/packages/ui/src/solidity/CrossChainMessagingSection.svelte b/packages/ui/src/solidity/CrossChainMessagingSection.svelte index 6f545a7a0..91a9af8bd 100644 --- a/packages/ui/src/solidity/CrossChainMessagingSection.svelte +++ b/packages/ui/src/solidity/CrossChainMessagingSection.svelte @@ -8,6 +8,25 @@ export let enabled: boolean; export let functionName: string; export let errors: undefined | OptionsErrorMessages; + + // Show notice when enabled + import { superchainGenericTooltipProps } from './superchain-tooltip'; + import tippy, { type Instance as TippyInstance } from 'tippy.js'; + import { onMount } from 'svelte'; + + let crosschainLabel: HTMLElement; + let crosschainTooltip: TippyInstance; + onMount(() => { + crosschainTooltip = tippy(crosschainLabel, superchainGenericTooltipProps); + }); + + let wasCrosschain = false; + $: { + if (!wasCrosschain && enabled) { + crosschainTooltip.show(); + } + wasCrosschain = enabled; + } -