Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ function ClaimableModule(props: ModuleInstanceProps) {
const [tokenId, setTokenId] = useState<string>("");

const isErc721 = props.contractInfo.name === "ClaimableERC721";
const isErc20 = props.contractInfo.name === "ClaimableERC20";
const isValidTokenId = positiveIntegerRegex.test(tokenId);

const primarySaleRecipientQuery = useReadContract(
Expand Down Expand Up @@ -241,6 +242,7 @@ function ClaimableModule(props: ModuleInstanceProps) {
}}
isOwnerAccount={!!ownerAccount}
isErc721={isErc721}
isErc20={isErc20}
contractChainId={props.contract.chain.id}
setTokenId={setTokenId}
isValidTokenId={isValidTokenId}
Expand All @@ -256,6 +258,7 @@ export function ClaimableModuleUI(
props: Omit<ModuleCardUIProps, "children" | "updateButton"> & {
isOwnerAccount: boolean;
isErc721: boolean;
isErc20: boolean;
contractChainId: number;
setTokenId: Dispatch<SetStateAction<string>>;
isValidTokenId: boolean;
Expand Down Expand Up @@ -295,7 +298,7 @@ export function ClaimableModuleUI(
<Accordion type="single" collapsible className="-mx-1">
<AccordionItem value="metadata" className="border-none">
<AccordionTrigger className="border-border border-t px-1">
Mint NFT
Mint {props.isErc20 ? "Token" : "NFT"}
</AccordionTrigger>
<AccordionContent className="px-1">
<MintNFTSection
Expand Down Expand Up @@ -835,7 +838,7 @@ function MintNFTSection(props: {
name="quantity"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel>quantity</FormLabel>
<FormLabel>Quantity</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ function RoyaltyModule(props: ModuleInstanceProps) {
const setRoyaltyForTokenTx = setRoyaltyInfoForToken({
contract: contract,
recipient: values.recipient,
bps: Number(values.bps),
// BPS is 10_000 so we need to multiply by 100
bps: Number(values.percentage) * 100,
tokenId: BigInt(values.tokenId),
});

Expand Down Expand Up @@ -108,14 +109,14 @@ function RoyaltyModule(props: ModuleInstanceProps) {
if (!ownerAccount) {
throw new Error("Not an owner account");
}
const [defaultRoyaltyRecipient, defaultRoyaltyBps] =
const [defaultRoyaltyRecipient, defaultRoyaltyPercentage] =
defaultRoyaltyInfoQuery.data || [];

if (
values.recipient &&
values.bps &&
values.percentage &&
(values.recipient !== defaultRoyaltyRecipient ||
Number(values.bps) !== defaultRoyaltyBps)
Number(values.percentage) * 100 !== defaultRoyaltyPercentage)
) {
const setDefaultRoyaltyInfo = isErc721
? RoyaltyERC721.setDefaultRoyaltyInfo
Expand All @@ -124,7 +125,7 @@ function RoyaltyModule(props: ModuleInstanceProps) {
const setSaleConfigTx = setDefaultRoyaltyInfo({
contract: contract,
royaltyRecipient: values.recipient,
royaltyBps: Number(values.bps),
royaltyBps: Number(values.percentage) * 100,
});

await sendAndConfirmTransaction({
Expand Down Expand Up @@ -250,10 +251,12 @@ const royaltyInfoFormSchema = z.object({
}),

recipient: addressSchema,
bps: z
percentage: z
.string()
.min(1, { message: "Invalid BPS" })
.refine((v) => Number(v) >= 0, { message: "Invalid BPS" }),
.min(1, { message: "Invalid percentage" })
.refine((v) => Number(v) === 0 || (Number(v) >= 0.01 && Number(v) <= 100), {
message: "Invalid percentage",
}),
Comment on lines +254 to +259
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There appears to be conflicting validation logic for zero values. The min(1) string validation will reject "0" inputs, while the refine() check explicitly allows zero values with Number(v) === 0. To consistently support zero values, the min(1) validation should be removed, leaving only the refine() check to handle the full range of valid inputs (0 and 0.01-100).

Spotted by Graphite Reviewer

Is this helpful? React 👍 or 👎 to let us know.

});

export type RoyaltyInfoFormValues = z.infer<typeof royaltyInfoFormSchema>;
Expand All @@ -267,7 +270,7 @@ function RoyaltyInfoPerTokenSection(props: {
values: {
tokenId: "",
recipient: "",
bps: "",
percentage: "",
},
reValidateMode: "onChange",
});
Expand Down Expand Up @@ -321,12 +324,17 @@ function RoyaltyInfoPerTokenSection(props: {

<FormField
control={form.control}
name="bps"
name="percentage"
render={({ field }) => (
<FormItem>
<FormLabel>BPS</FormLabel>
<FormLabel>Percentage</FormLabel>
<FormControl>
<Input {...field} />
<div className="flex items-center">
<Input {...field} className="rounded-r-none border-r-0" />
<div className="h-10 rounded-lg rounded-l-none border border-input px-3 py-2">
%
</div>
</div>
</FormControl>
<FormMessage />
</FormItem>
Expand Down Expand Up @@ -355,9 +363,12 @@ function RoyaltyInfoPerTokenSection(props: {

const defaultRoyaltyFormSchema = z.object({
recipient: addressSchema,
bps: z.string().refine((v) => v.length === 0 || Number(v) >= 0, {
message: "Invalid BPS",
}),
percentage: z
.string()
.min(1, { message: "Invalid percentage" })
.refine((v) => Number(v) === 0 || (Number(v) >= 0.01 && Number(v) <= 100), {
message: "Invalid percentage",
}),
});

export type DefaultRoyaltyFormValues = z.infer<typeof defaultRoyaltyFormSchema>;
Expand All @@ -367,14 +378,16 @@ function DefaultRoyaltyInfoSection(props: {
update: (values: DefaultRoyaltyFormValues) => Promise<void>;
contractChainId: number;
}) {
const [defaultRoyaltyRecipient, defaultRoyaltyBps] =
const [defaultRoyaltyRecipient, defaultRoyaltyPercentage] =
props.defaultRoyaltyInfo || [];

const form = useForm<DefaultRoyaltyFormValues>({
resolver: zodResolver(defaultRoyaltyFormSchema),
values: {
recipient: defaultRoyaltyRecipient || "",
bps: defaultRoyaltyBps ? String(defaultRoyaltyBps) : "",
percentage: defaultRoyaltyPercentage
? String(defaultRoyaltyPercentage / 100)
: "",
},
reValidateMode: "onChange",
});
Expand Down Expand Up @@ -414,12 +427,17 @@ function DefaultRoyaltyInfoSection(props: {

<FormField
control={form.control}
name="bps"
name="percentage"
render={({ field }) => (
<FormItem>
<FormLabel>Default Royalty BPS</FormLabel>
<FormLabel>Default Royalty Percentage</FormLabel>
<FormControl>
<Input {...field} />
<div className="flex items-center">
<Input {...field} className="rounded-r-none border-r-0" />
<div className="h-10 rounded-lg rounded-l-none border border-input px-3 py-2">
%
</div>
</div>
</FormControl>
<FormMessage />
</FormItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,18 +237,23 @@ export function TransferableModuleUI(

{isRestricted && (
<div className="w-full">
{/* Warning - TODO add later */}
{/* {formFields.fields.length === 0 && (
{formFields.fields.length === 0 && (
<Alert variant="warning">
<CircleAlertIcon className="size-5 max-sm:hidden" />
<AlertTitle className="max-sm:!pl-0">
Nobody has permission to transfer tokens on this
contract
</AlertTitle>
</Alert>
)} */}
)}

<div className="flex flex-col gap-3">
{formFields.fields.length > 0 && (
<p className="text-muted-foreground text-sm">
Accounts that may override the transfer restrictions
</p>
)}

{/* Addresses */}
{formFields.fields.map((fieldItem, index) => (
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const claimCondition = {
function Component() {
const [isOwner, setIsOwner] = useState(true);
const [isErc721, setIsErc721] = useState(false);
const [isErc20, setIsErc20] = useState(false);
const [isClaimConditionLoading, setIsClaimConditionLoading] = useState(false);
const [isPrimarySaleRecipientLoading, setIsPrimarySaleRecipientLoading] =
useState(false);
Expand Down Expand Up @@ -125,6 +126,13 @@ function Component() {
label="isErc721"
/>

<CheckboxWithLabel
value={isErc20}
onChange={setIsErc20}
id="isErc20"
label="isErc20"
/>

<CheckboxWithLabel
value={isClaimConditionLoading}
onChange={setIsClaimConditionLoading}
Expand Down Expand Up @@ -179,6 +187,7 @@ function Component() {
}}
isOwnerAccount={isOwner}
isErc721={isErc721}
isErc20={isErc20}
contractChainId={1}
setTokenId={setTokenId}
isValidTokenId={true}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { FormErrorMessage, FormLabel } from "tw-components";
import type { CustomContractDeploymentForm } from "./custom-contract";
import { PrimarySaleFieldset } from "./primary-sale-fieldset";
import { RoyaltyFieldset } from "./royalty-fieldset";
import { SequentialTokenIdFieldset } from "./sequential-token-id-fieldset";

export function getModuleInstallParams(mod: FetchDeployMetadataResult) {
return (
Expand Down Expand Up @@ -67,6 +68,16 @@ function RenderModule(props: {
<RenderPrimarySaleFieldset module={module} form={form} isTWPublisher />
);
}

if (showSequentialTokenIdFieldset(paramNames)) {
return (
<RenderSequentialTokenIdFieldset
module={module}
form={form}
isTWPublisher
/>
);
}
}

return (
Expand Down Expand Up @@ -133,6 +144,26 @@ function RenderPrimarySaleFieldset(prosp: {
);
}

function RenderSequentialTokenIdFieldset(prosp: {
module: FetchDeployMetadataResult;
form: CustomContractDeploymentForm;
isTWPublisher: boolean;
}) {
const { module, form } = prosp;

const startTokenIdPath = `moduleData.${module.name}.startTokenId` as const;

return (
<SequentialTokenIdFieldset
isInvalid={!!form.getFieldState(startTokenIdPath, form.formState).error}
register={form.register(startTokenIdPath)}
errorMessage={
form.getFieldState(startTokenIdPath, form.formState).error?.message
}
/>
);
}

function RenderRoyaltyFieldset(props: {
module: FetchDeployMetadataResult;
form: CustomContractDeploymentForm;
Expand Down Expand Up @@ -194,3 +225,7 @@ export function showRoyaltyFieldset(paramNames: string[]) {
export function showPrimarySaleFiedset(paramNames: string[]) {
return paramNames.length === 1 && paramNames.includes("primarySaleRecipient");
}

function showSequentialTokenIdFieldset(paramNames: string[]) {
return paramNames.length === 1 && paramNames.includes("startTokenId");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { FormFieldSetup } from "@/components/blocks/FormFieldSetup";
import { FormControl } from "@/components/ui/form";
import { SolidityInput } from "contract-ui/components/solidity-inputs";
import type { UseFormRegisterReturn } from "react-hook-form";

interface SequentialTokenIdFieldsetProps {
isInvalid: boolean;
register: UseFormRegisterReturn;
errorMessage: string | undefined;
}

export const SequentialTokenIdFieldset: React.FC<
SequentialTokenIdFieldsetProps
> = (props) => {
return (
<FormFieldSetup
htmlFor="startTokenId"
label="Start Token ID"
isRequired={true}
errorMessage={props.errorMessage}
helperText="The starting token ID for the NFT collection."
>
<FormControl>
<SolidityInput
solidityType="uint256"
variant="filled"
{...props.register}
/>
</FormControl>
</FormFieldSetup>
);
};
Loading