From 05573a7c4503b1d018ceb26a96c0ba62c69d429e Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 11 Dec 2024 15:35:54 -0300 Subject: [PATCH 1/9] update readme --- README.md | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 32b84d4..68139f1 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,10 @@ # Defender Deploy Plugin -Plugin to deploy smart contracts using OpenZeppelin Defender. For documentation about usage in Remix please visit the [Defender Docs](https://docs.openzeppelin.com/defender/remix-plugin). +Plugin to deploy smart contracts using OpenZeppelin Defender. Currently supported in: + +- [Remix IDE](https://remix.ethereum.org/) - As a plugin listed in plugins directory, for more information please visit [our docs](https://docs.openzeppelin.com/defender/remix-plugin). +- [Contracts Wizard](https://wizard.openzeppelin.com/) - Integrated in code editor, for more information please visit [our docs](https://docs.openzeppelin.com/defender/remix-plugin). ## Getting Started @@ -16,7 +19,7 @@ pnpm install pnpm dev ``` -The interface is ugly, but don't worry! it's not meant to be used directly, it's used embedded in an iframe instead, and adopts the parent styles. +NOTE: This project is meant to be embedded in other UIs, just running the project won't be enough to see and debug it. You must embed the UI on an external iframe. ## Testing in Remix @@ -31,4 +34,38 @@ Url: http://localhost:5173 # or live version https://defeder-remix-deploy.netlif Type of connection: Iframe Location in Remix: Side Panel ``` -5. You should see the plugin added to the sidebar (new icon with ? symbol). \ No newline at end of file +5. You should see the plugin added to the sidebar (new icon with ? symbol). + +## Testing in Contracts Wizard + +For testing in Contracts Wizard, you must also run the Contracts Wizard UI locally to point to your local plugin. + +1. Run Contracts Wizard locally. + a. Go to (https://github.com/OpenZeppelin/contracts-wizard)[Contracts Wizard Repo]. + b. Clone the latest `master` branch and follow steps to setup the project. + c. Move to `pacakges/ui` and run it with `yarn dev`. +2. In another terminal, run this project using `pnpm dev`, make sure the app is served in `http://localhost:5173`. +3. Open Contracts Wizard local UI, generally in `http://localhost:8080`. +4. Click on "Deploy with Defender" button, you should be able to see embedded the local plugin. + +## Development + +Many parts of codebase are shared across plugins (server side code, state definition, ethereum interactions, etc.), but UI components have a separated implementation to make them more flexible and prevent side-effects. + +We have some bootstrap logic to expose one UI or another depending on the parent iframe domain. + +### Remix +The entrypoint for Remix plugin is `src/routes/remix.svelte`. Its components mainly use [bootstrap](https://getbootstrap.com/) for styling, Remix UI injects bootstrap dependency as a html tag when embedded. + +### Wizard +The entrypoint for Contracts Wizard plugin is `src/routes/wizard.svelte`. Its components mainly use [tailwind CSS](https://tailwindcss.com/) for styling, this is mainly for convenience, since Contracts Wizard was made using this CSS framework. + +## Release + +The repo has a CI/CD connected to our netlify account, when we merge `main` to some of the release branches, a new version of the plugin is released to live. Branches: + +- Remix IDE Plugin - `release-remix` +- Contracts Wizard Plugin - `release-wizard` + +> [!WARNING] +> We use `main` branch as the single source of truth and it's the only branch allowed to be merged to release branches. It should be tested carefully before triggering a new release. \ No newline at end of file From 27780bfa2d097445a3985831467677222a25098b Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 11 Dec 2024 15:40:33 -0300 Subject: [PATCH 2/9] fix list sub items --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 68139f1..e66809d 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,9 @@ Location in Remix: Side Panel For testing in Contracts Wizard, you must also run the Contracts Wizard UI locally to point to your local plugin. 1. Run Contracts Wizard locally. - a. Go to (https://github.com/OpenZeppelin/contracts-wizard)[Contracts Wizard Repo]. - b. Clone the latest `master` branch and follow steps to setup the project. - c. Move to `pacakges/ui` and run it with `yarn dev`. + - a. Go to (https://github.com/OpenZeppelin/contracts-wizard)[Contracts Wizard Repo]. + - b. Clone the latest `master` branch and follow steps to setup the project. + - c. Move to `pacakges/ui` and run it with `yarn dev`. 2. In another terminal, run this project using `pnpm dev`, make sure the app is served in `http://localhost:5173`. 3. Open Contracts Wizard local UI, generally in `http://localhost:8080`. 4. Click on "Deploy with Defender" button, you should be able to see embedded the local plugin. From 854b8a01ccbe3d8c827024a526550528e1e2682d Mon Sep 17 00:00:00 2001 From: Marcos Carlomagno Date: Wed, 11 Dec 2024 21:21:20 -0300 Subject: [PATCH 3/9] Update README.md Co-authored-by: Eric Lau --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e66809d..dad5efa 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Location in Remix: Side Panel For testing in Contracts Wizard, you must also run the Contracts Wizard UI locally to point to your local plugin. 1. Run Contracts Wizard locally. - - a. Go to (https://github.com/OpenZeppelin/contracts-wizard)[Contracts Wizard Repo]. + - a. Go to [https://github.com/OpenZeppelin/contracts-wizard](Contracts Wizard Repo). - b. Clone the latest `master` branch and follow steps to setup the project. - c. Move to `pacakges/ui` and run it with `yarn dev`. 2. In another terminal, run this project using `pnpm dev`, make sure the app is served in `http://localhost:5173`. From 01535fbe80e7723eb397893cfcf97a5d161db065 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 12 Dec 2024 10:49:49 -0300 Subject: [PATCH 4/9] fix prevent adding salt when deterministic is un checked --- src/lib/remix/components/Deploy.svelte | 2 +- src/lib/wizard/components/Deploy.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/remix/components/Deploy.svelte b/src/lib/remix/components/Deploy.svelte index 639c7f4..ea2a4b4 100644 --- a/src/lib/remix/components/Deploy.svelte +++ b/src/lib/remix/components/Deploy.svelte @@ -293,7 +293,7 @@ verifySourceCode: true, artifactPayload, constructorBytecode, - salt, + salt: isDeterministic || enforceDeterministic ? salt : undefined, }; const [newDeploymentId, deployError] = await attempt(async () => createDefenderDeployment(deployRequest)); if (deployError) { diff --git a/src/lib/wizard/components/Deploy.svelte b/src/lib/wizard/components/Deploy.svelte index 1ee82c1..2a8d12d 100644 --- a/src/lib/wizard/components/Deploy.svelte +++ b/src/lib/wizard/components/Deploy.svelte @@ -272,7 +272,7 @@ licenseType: 'MIT', artifactPayload: JSON.stringify(deploymentArtifact), constructorBytecode, - salt, + salt: isDeterministic || enforceDeterministic ? salt : undefined, } const [newDeploymentId, deployError] = await attempt(async () => createDefenderDeployment(deployRequest)); From d43665fd69e78cf36eeb193b131bbc9f094af08a Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 12 Dec 2024 12:01:48 -0300 Subject: [PATCH 5/9] react to all types of assets and contract name changed --- src/lib/models/deploy.ts | 10 +++++++ src/lib/wizard/components/Deploy.svelte | 35 ++++++++++++------------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/lib/models/deploy.ts b/src/lib/models/deploy.ts index c1e1296..ce0e2b2 100644 --- a/src/lib/models/deploy.ts +++ b/src/lib/models/deploy.ts @@ -78,3 +78,13 @@ export interface DeploymentResult { hash: string; sender?: string; } + +export type ABITypeParameter = 'uint' | 'uint[]' | 'int' | 'int[]' | 'address' | 'address[]' | 'bool' | 'bool[]' | 'fixed' | 'fixed[]' | 'ufixed' | 'ufixed[]' | 'bytes' | 'bytes[]' | 'function' | 'function[]' | 'tuple' | 'tuple[]' | string; +export interface ABIParameter { + /** The name of the parameter */ + name: string; + /** The canonical type of the parameter */ + type: ABITypeParameter; + /** Used for tuple types */ + components?: ABIParameter[]; +} \ No newline at end of file diff --git a/src/lib/wizard/components/Deploy.svelte b/src/lib/wizard/components/Deploy.svelte index 1ee82c1..dd212e8 100644 --- a/src/lib/wizard/components/Deploy.svelte +++ b/src/lib/wizard/components/Deploy.svelte @@ -2,7 +2,7 @@ import { API } from "$lib/api"; import { deployContract, switchToNetwork } from "$lib/ethereum"; import type { ApprovalProcess, CreateApprovalProcessRequest } from "$lib/models/approval-process"; - import type { Artifact, DeployContractRequest, DeploymentResult, UpdateDeploymentRequest } from "$lib/models/deploy"; + import type { ABIParameter, Artifact, DeployContractRequest, DeploymentResult, UpdateDeploymentRequest } from "$lib/models/deploy"; import { getNetworkLiteral, isProductionNetwork } from "$lib/models/network"; import { buildCompilerInput, type ContractSources } from "$lib/models/solc"; import type { APIResponse } from "$lib/models/ui"; @@ -43,11 +43,6 @@ } }); - let inputs = $derived.by(() => { - if (!compilationResult) return []; - return getConstructorInputsWizard(globalState.contract?.target, compilationResult.output.contracts); - }); - let displayUpgradeableWarning = $derived.by(() => { return isUpgradeable(globalState.contract?.source?.sources as ContractSources); }); @@ -66,6 +61,9 @@ : undefined ); + + let inputs: ABIParameter[] = $state([]); + $effect(() => { if (globalState.contract?.source?.sources) { compile(); @@ -92,6 +90,10 @@ return; } compilationResult = res.data; + + if (globalState.contract?.target && compilationResult) { + inputs = getConstructorInputsWizard(globalState.contract.target, compilationResult.output.contracts); + } } function displayMessage(message: string, type: "success" | "error") { @@ -266,8 +268,8 @@ const deployRequest: DeployContractRequest = { network: getNetworkLiteral(globalState.form.network), approvalProcessId: approvalProcess.approvalProcessId, - contractName: globalState.contract!.target, - contractPath: globalState.contract!.target, + contractName: globalState.contract.target, + contractPath: globalState.contract.target, verifySourceCode: true, licenseType: 'MIT', artifactPayload: JSON.stringify(deploymentArtifact), @@ -310,19 +312,18 @@
- {#if displayUpgradeableWarning} {/if} {#if inputs.length > 0} -
Constructor Arguments
- {#each inputs as input} - - {/each} -{:else} - -{/if} +
Constructor Arguments
+ {#each inputs as input} + + {/each} + {:else} + + {/if}
{/if} - - {#if currentTab === index} -
+
{#if index === 0} wait(1000).then(() => toggle(1))}/>{/if} {#if index === 1} wait(1000).then(() => toggle(2))}/>{/if} From 7e587e672dab1866a1fd8606493ff5dc56b6a09f Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 12 Dec 2024 17:29:48 -0300 Subject: [PATCH 8/9] debounce calls to compiler api --- src/lib/utils/helpers.ts | 8 ++++++++ src/lib/wizard/components/Deploy.svelte | 17 ++++++++++++----- src/lib/wizard/components/shared/Message.svelte | 7 ++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/lib/utils/helpers.ts b/src/lib/utils/helpers.ts index 558b4b1..87e0ccc 100644 --- a/src/lib/utils/helpers.ts +++ b/src/lib/utils/helpers.ts @@ -26,3 +26,11 @@ export const isUpgradeable = (sources?: ContractSources) => { if (!sources) return false; return Object.keys(sources).some((path) => path.includes('@openzeppelin/contracts-upgradeable')); } + +export const debouncer = (fn: (...args: any[]) => void, delay: number) => { + let timeout: NodeJS.Timeout; + return (...args: any[]) => { + clearTimeout(timeout); + timeout = setTimeout(() => fn(...args), delay); + }; +} diff --git a/src/lib/wizard/components/Deploy.svelte b/src/lib/wizard/components/Deploy.svelte index 8328a13..d4e75b9 100644 --- a/src/lib/wizard/components/Deploy.svelte +++ b/src/lib/wizard/components/Deploy.svelte @@ -9,11 +9,14 @@ import { addAPToDropdown, findDeploymentEnvironment, globalState } from "$lib/state/state.svelte"; import { attempt } from "$lib/utils/attempt"; import { encodeConstructorArgs, getConstructorInputsWizard, getContractBytecode } from "$lib/utils/contracts"; - import { isMultisig, isUpgradeable } from "$lib/utils/helpers"; + import { debouncer, isMultisig, isUpgradeable } from "$lib/utils/helpers"; import Button from "./shared/Button.svelte"; import Input from "./shared/Input.svelte"; import Message from "./shared/Message.svelte"; + // debounce the compile call to avoid sending too many requests while the user is editing. + const compileDebounced = debouncer(compile, 600); + let inputsWithValue = $state>({}); let busy = $state(false); let successMessage = $state(""); @@ -24,6 +27,7 @@ let deploymentResult = $state(undefined); let isDeterministic = $state(false); let salt: string = $state(""); + let isCompiling = $state(false); let contractBytecode = $derived.by(() => { if (!globalState.contract?.target || !compilationResult) return; @@ -61,12 +65,12 @@ : undefined ); - let inputs: ABIParameter[] = $state([]); $effect(() => { if (globalState.contract?.source?.sources) { - compile(); + isCompiling = true; + compileDebounced(); } }); @@ -75,7 +79,7 @@ inputsWithValue[target.name] = target.value; } - async function compile() { + async function compile(): Promise { const sources = globalState.contract?.source?.sources; if (!sources) { return; @@ -94,6 +98,7 @@ if (globalState.contract?.target && compilationResult) { inputs = getConstructorInputsWizard(globalState.contract.target, compilationResult.output.contracts); } + isCompiling = false; } function displayMessage(message: string, type: "success" | "error") { @@ -316,7 +321,9 @@ {/if} - {#if inputs.length > 0} + {#if isCompiling} + + {:else if inputs.length > 0}
Constructor Arguments
{#each inputs as input} diff --git a/src/lib/wizard/components/shared/Message.svelte b/src/lib/wizard/components/shared/Message.svelte index 521e6b5..af5a0e0 100644 --- a/src/lib/wizard/components/shared/Message.svelte +++ b/src/lib/wizard/components/shared/Message.svelte @@ -1,7 +1,7 @@ @@ -359,7 +359,7 @@ {/if} -