diff --git a/apps/dashboard/src/@/components/blocks/FormFieldSetup.tsx b/apps/dashboard/src/@/components/blocks/FormFieldSetup.tsx index 3483026c579..ce8b44387c1 100644 --- a/apps/dashboard/src/@/components/blocks/FormFieldSetup.tsx +++ b/apps/dashboard/src/@/components/blocks/FormFieldSetup.tsx @@ -3,16 +3,17 @@ import { ToolTipLabel } from "@/components/ui/tooltip"; import { AsteriskIcon, InfoIcon } from "lucide-react"; export function FormFieldSetup(props: { - htmlFor: string; + htmlFor?: string; label: string; errorMessage: React.ReactNode | undefined; children: React.ReactNode; tooltip?: React.ReactNode; isRequired: boolean; helperText?: React.ReactNode; + className?: string; }) { return ( -
+
diff --git a/apps/dashboard/src/app/(dashboard)/contracts/publish/[publish_uri]/page.tsx b/apps/dashboard/src/app/(dashboard)/contracts/publish/[publish_uri]/page.tsx index 0ac4bc08aa9..6921417ff96 100644 --- a/apps/dashboard/src/app/(dashboard)/contracts/publish/[publish_uri]/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/contracts/publish/[publish_uri]/page.tsx @@ -73,7 +73,7 @@ export default async function PublishContractPage( } return ( -
+
; + deployParams: Record; moduleData: Record>; contractMetadata?: { name: string; @@ -80,6 +80,18 @@ type CustomContractDeploymentFormData = { recipients?: Recipient[]; }; +export interface DynamicValue { + dynamicValue: { + type: string; + refContracts?: { + publisherAddress: string; + version: string; + contractId: string; + salt?: string; + }[]; + }; +} + export type CustomContractDeploymentForm = UseFormReturn; @@ -159,6 +171,8 @@ export const CustomContractForm: React.FC = ({ "initialize", ); + const implementationConstructorParams = metadata?.implConstructorParams; + const isFactoryDeployment = metadata?.isDeployableViaFactory || metadata?.isDeployableViaProxy || @@ -206,9 +220,16 @@ export const CustomContractForm: React.FC = ({ acc[param.name] = activeAccount.address; } + // specify refs if present + const dynamicValue = + metadata?.constructorParams?.[param.name]?.dynamicValue; + if (dynamicValue && acc[param.name] === "") { + acc[param.name] = { dynamicValue }; + } + return acc; }, - {} as Record, + {} as Record, ), }), [deployParams, metadata?.constructorParams, activeAccount, walletChain?.id], @@ -353,7 +374,11 @@ export const CustomContractForm: React.FC = ({ const contructorParams = metadata?.constructorParams || {}; const extraMetadataParam = contructorParams[paramKey]; - if (shouldHide(paramKey) || !extraMetadataParam?.hidden) { + if ( + shouldHide(paramKey) || + extraMetadataParam?.hidden !== true || + extraMetadataParam?.dynamicValue + ) { return null; } @@ -412,11 +437,12 @@ export const CustomContractForm: React.FC = ({ params: { name: params.contractMetadata?.name || "", contractURI: _contractURI, - defaultAdmin: params.deployParams._defaultAdmin, + defaultAdmin: params.deployParams._defaultAdmin as string, platformFeeBps: Number(params.deployParams._platformFeeBps), - platformFeeRecipient: params.deployParams._platformFeeRecipient, + platformFeeRecipient: params.deployParams + ._platformFeeRecipient as string, trustedForwarders: params.deployParams._trustedForwarders - ? JSON.parse(params.deployParams._trustedForwarders) + ? JSON.parse(params.deployParams._trustedForwarders as string) : undefined, }, }); @@ -442,6 +468,7 @@ export const CustomContractForm: React.FC = ({ client: thirdwebClient, deployMetadata: metadata, initializeParams, + implementationConstructorParams, salt, modules: modules?.map((m) => ({ deployMetadata: m, @@ -652,7 +679,7 @@ export const CustomContractForm: React.FC = ({ ).error?.message, }} royaltyBps={{ - value: form.watch("deployParams._royaltyBps"), + value: form.watch("deployParams._royaltyBps") as string, isInvalid: !!form.getFieldState( "deployParams._royaltyBps", form.formState, @@ -749,7 +776,11 @@ export const CustomContractForm: React.FC = ({ const contructorParams = metadata?.constructorParams || {}; const extraMetadataParam = contructorParams[paramKey]; - if (shouldHide(paramKey) || extraMetadataParam?.hidden) { + if ( + shouldHide(paramKey) || + extraMetadataParam?.hidden === true || + extraMetadataParam?.dynamicValue + ) { return null; } diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/trusted-forwarders-fieldset.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/trusted-forwarders-fieldset.tsx index 3846b180e64..f848d722d4a 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/trusted-forwarders-fieldset.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/trusted-forwarders-fieldset.tsx @@ -2,7 +2,10 @@ import { Flex, FormControl, InputGroup } from "@chakra-ui/react"; import { SolidityInput } from "contract-ui/components/solidity-inputs"; import { FormErrorMessage, FormHelperText, FormLabel } from "tw-components"; import { Fieldset } from "./common"; -import type { CustomContractDeploymentForm } from "./custom-contract"; +import type { + CustomContractDeploymentForm, + DynamicValue, +} from "./custom-contract"; interface TrustedForwardersFieldsetProps { form: CustomContractDeploymentForm; @@ -11,56 +14,65 @@ interface TrustedForwardersFieldsetProps { export const TrustedForwardersFieldset: React.FC< TrustedForwardersFieldsetProps > = ({ form }) => { + const isDynamicValue = (val: string | DynamicValue): val is DynamicValue => { + return typeof val === "object" && val !== null && "dynamicValue" in val; + }; + + const value = form.watch("deployParams._trustedForwarders"); return ( -
- -
- {/* left */} -
- Trusted Forwarders + <> + {!isDynamicValue(value) && ( +
+ +
+ {/* left */} +
+ Trusted Forwarders - - - Trusted forwarder addresses to enable ERC-2771 transactions - (i.e. gasless). - + + + Trusted forwarder addresses to enable ERC-2771 transactions + (i.e. gasless). + - - You can provide your own forwarder. - - -
-
+ + You can provide your own forwarder. + + +
+
-
- - - - - +
+ + + + + - - { - form.getFieldState( - "deployParams._trustedForwarders", - form.formState, - ).error?.message - } - -
- -
+ + { + form.getFieldState( + "deployParams._trustedForwarders", + form.formState, + ).error?.message + } + +
+ + + )} + ); }; diff --git a/apps/dashboard/src/components/contract-components/contract-publish-form/contract-params-fieldset.tsx b/apps/dashboard/src/components/contract-components/contract-publish-form/contract-params-fieldset.tsx index b9cdbef3c58..901be57a02b 100644 --- a/apps/dashboard/src/components/contract-components/contract-publish-form/contract-params-fieldset.tsx +++ b/apps/dashboard/src/components/contract-components/contract-publish-form/contract-params-fieldset.tsx @@ -1,29 +1,19 @@ -import { - Divider, - Flex, - FormControl, - Input, - InputGroup, - InputRightElement, - Textarea, - Tooltip, - useBreakpointValue, -} from "@chakra-ui/react"; +import { FormFieldSetup } from "@/components/blocks/FormFieldSetup"; +import { Checkbox, CheckboxWithLabel } from "@/components/ui/checkbox"; +import { InlineCode } from "@/components/ui/inline-code"; +import { Input } from "@/components/ui/input"; +import { Separator } from "@/components/ui/separator"; +import { Switch } from "@/components/ui/switch"; +import { Textarea } from "@/components/ui/textarea"; +import { FormControl, useBreakpointValue } from "@chakra-ui/react"; import type { AbiParameter } from "abitype"; import { SolidityInput } from "contract-ui/components/solidity-inputs"; import { camelToTitle } from "contract-ui/components/solidity-inputs/helpers"; import { getTemplateValuesForType } from "lib/deployment/template-values"; +import { useState } from "react"; import { useFormContext } from "react-hook-form"; -import { - Button, - Card, - Checkbox, - FormErrorMessage, - FormHelperText, - FormLabel, - Heading, - Text, -} from "tw-components"; +import { DecodedInputArrayFieldset } from "./decoded-bytes-input/decoded-input-array-fieldset"; +import { RefInputFieldset } from "./ref-contract-input/ref-input-fieldset"; interface ContractParamsFieldsetProps { deployParams: readonly AbiParameter[]; @@ -32,243 +22,288 @@ export const ContractParamsFieldset: React.FC = ({ deployParams, }) => { const form = useFormContext(); - const isMobile = useBreakpointValue({ base: true, md: false }); + const [isCustomInputEnabledArray, setIsCustomInputEnabledArray] = useState( + Array(deployParams.length).fill(false), + ); + + const handleCustomInputEnabledArrayChange = ( + index: number, + newValue: boolean, + ) => { + const newIsCustomInputEnabledArray = [...isCustomInputEnabledArray]; + newIsCustomInputEnabledArray[index] = newValue; + setIsCustomInputEnabledArray(newIsCustomInputEnabledArray); + + if (newValue) { + form.setValue( + `constructorParams.${deployParams[index]?.name || "*"}.defaultValue`, + "", + { + shouldDirty: true, + }, + ); + + form.setValue( + `constructorParams.${deployParams[index]?.name || "*"}.dynamicValue.type`, + deployParams[index]?.type, + ); + } else { + form.setValue( + `constructorParams.${deployParams[index]?.name || "*"}.dynamicValue.type`, + "", + ); + form.setValue( + `constructorParams.${deployParams[index]?.name || "*"}.dynamicValue`, + "", + { + shouldDirty: true, + }, + ); + } + }; + return ( - - - Contract Parameters - - These are the parameters users will need to fill in when deploying - this contract. - - - +
+

+ Contract Parameters +

+

+ These are the parameters users will need to fill in when deploying this + contract. +

+ +
+ +
{deployParams.map((param, idx) => { const paramTemplateValues = getTemplateValuesForType(param.type); return ( - - - {param.name ? ( - {param.name} - ) : ( - - Unnamed param (will not be used) - - )} - {param.type} - - - - - - Display Name - - + {/* Title + Type */} +
+

+ {param.name ? ( + param.name + ) : ( + + Unnamed param (will not be used) + + )} +

+ +
+ +
+ + {/* Display Name */} + + + form.setValue( + `constructorParams.${ + param.name ? param.name : "*" + }.displayName`, + e.target.value, + ) + } + placeholder={camelToTitle(param.name ? param.name : "*")} + /> + + +
+ + {/* Description */} + + {form.watch( + `constructorParams.${ + param.name ? param.name : "*" + }.description`, + )?.length ?? 0} + /400 characters + + } + > +