diff --git a/apps/dashboard/src/@/components/blocks/multi-select.tsx b/apps/dashboard/src/@/components/blocks/multi-select.tsx index fc022cc1144..4ca98cb78e3 100644 --- a/apps/dashboard/src/@/components/blocks/multi-select.tsx +++ b/apps/dashboard/src/@/components/blocks/multi-select.tsx @@ -119,13 +119,18 @@ export const MultiSelect = forwardRef( if (filteredOptions.length >= itemsToShow) { break; } + const option = options[i]; + if (!option) { + continue; + } + if (overrideSearchFn) { - if (overrideSearchFn(options[i], searchValLowercase)) { - filteredOptions.push(options[i]); + if (overrideSearchFn(option, searchValLowercase)) { + filteredOptions.push(option); } } else { - if (options[i].label.toLowerCase().includes(searchValLowercase)) { - filteredOptions.push(options[i]); + if (option.label.toLowerCase().includes(searchValLowercase)) { + filteredOptions.push(option); } } } diff --git a/apps/dashboard/src/@/components/blocks/select-with-search.tsx b/apps/dashboard/src/@/components/blocks/select-with-search.tsx index 536969e038f..73896e6edb5 100644 --- a/apps/dashboard/src/@/components/blocks/select-with-search.tsx +++ b/apps/dashboard/src/@/components/blocks/select-with-search.tsx @@ -63,14 +63,18 @@ export const SelectWithSearch = React.forwardRef< if (filteredOptions.length >= itemsToShow) { break; } + const option = options[i]; + if (!option) { + continue; + } if (overrideSearchFn) { - if (overrideSearchFn(options[i], searchValLowercase)) { - filteredOptions.push(options[i]); + if (overrideSearchFn(option, searchValLowercase)) { + filteredOptions.push(option); } } else { - if (options[i].label.toLowerCase().includes(searchValLowercase)) { - filteredOptions.push(options[i]); + if (option.label.toLowerCase().includes(searchValLowercase)) { + filteredOptions.push(option); } } } diff --git a/apps/dashboard/src/@/components/ui/chart.tsx b/apps/dashboard/src/@/components/ui/chart.tsx index 99764860e42..1f70c0babc6 100644 --- a/apps/dashboard/src/@/components/ui/chart.tsx +++ b/apps/dashboard/src/@/components/ui/chart.tsx @@ -143,7 +143,7 @@ const ChartTooltipContent = React.forwardRef< } const [item] = payload; - const key = `${labelKey || item.dataKey || item.name || "value"}`; + const key = `${labelKey || item?.dataKey || item?.name || "value"}`; const itemConfig = getPayloadConfigFromPayload(config, item, key); const value = !labelKey && typeof label === "string" diff --git a/apps/dashboard/src/@/components/ui/image-upload.tsx b/apps/dashboard/src/@/components/ui/image-upload.tsx index 4d645101af1..35f8c21174d 100644 --- a/apps/dashboard/src/@/components/ui/image-upload.tsx +++ b/apps/dashboard/src/@/components/ui/image-upload.tsx @@ -16,7 +16,9 @@ const ImageUpload = React.forwardRef( const [activeFile, setActiveFile] = React.useState(null); const onDrop = React.useCallback( (acceptedFiles: File[]) => { - setActiveFile(acceptedFiles[0]); + if (acceptedFiles[0]) { + setActiveFile(acceptedFiles[0]); + } onUpload?.(acceptedFiles); }, [onUpload], diff --git a/apps/dashboard/src/@/components/ui/input-otp.tsx b/apps/dashboard/src/@/components/ui/input-otp.tsx index 832bc62a51d..134e52cfba0 100644 --- a/apps/dashboard/src/@/components/ui/input-otp.tsx +++ b/apps/dashboard/src/@/components/ui/input-otp.tsx @@ -35,7 +35,8 @@ const InputOTPSlot = React.forwardRef< React.ComponentPropsWithoutRef<"div"> & { index: number } >(({ index, className, ...props }, ref) => { const inputOTPContext = React.useContext(OTPInputContext); - const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]; + // biome-ignore lint/style/noNonNullAssertion: pure shadcn component - it works + const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]!; return (
, React.ComponentPropsWithoutRef<"div"> >(({ ...props }, ref) => ( - // biome-ignore lint/a11y/useFocusableInteractive: + // biome-ignore lint/a11y/useFocusableInteractive: pure shadcn component - it works
diff --git a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useActivity.ts b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useActivity.ts index ee290519709..5985f820340 100644 --- a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useActivity.ts +++ b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useActivity.ts @@ -44,13 +44,12 @@ export function useActivity(contract: ThirdwebContract, autoUpdate?: boolean) { } const obj = eventsQuery.data.slice(0, 100).reduce( (acc, curr) => { - if (acc[curr.transactionHash]) { - acc[curr.transactionHash].events.push(curr); - acc[curr.transactionHash].events.sort( - (a, b) => b.logIndex - a.logIndex, - ); - if (acc[curr.transactionHash].blockNumber > curr.blockNumber) { - acc[curr.transactionHash].blockNumber = curr.blockNumber; + const internalTx = acc[curr.transactionHash]; + if (internalTx) { + internalTx.events.push(curr); + internalTx.events.sort((a, b) => b.logIndex - a.logIndex); + if (internalTx.blockNumber > curr.blockNumber) { + internalTx.blockNumber = curr.blockNumber; } } else { acc[curr.transactionHash] = { diff --git a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useDashboardContractMetadata.tsx b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useDashboardContractMetadata.tsx index 2d46da23a43..f9fa400b622 100644 --- a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useDashboardContractMetadata.tsx +++ b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useDashboardContractMetadata.tsx @@ -48,7 +48,7 @@ export async function fetchDashboardContractMetadata( if (compilerMetadata?.name.includes(":")) { const _name = compilerMetadata.name.split(":")[1]; if (_name) { - compilerMetadata.name = compilerMetadata.name.split(":")[1]; + compilerMetadata.name = _name; } } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/embed/embed-setup.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/embed/embed-setup.tsx index 7e54b4cc83e..eb20ae201d8 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/embed/embed-setup.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/embed/embed-setup.tsx @@ -171,7 +171,7 @@ function minimizeChain( return { name: chain.name, chain: chain.chain, - rpc: [firstRpc], + rpc: [firstRpc || ""], nativeCurrency: chain.nativeCurrency, shortName: chain.shortName, chainId: chain.chainId, @@ -509,7 +509,7 @@ export const EmbedSetup: React.FC = ({ )} {colorOptions.map((color) => ( ))} @@ -524,7 +524,7 @@ export const EmbedSetup: React.FC = ({ diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx index c98afd8fcc3..5c14353c650 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/overview/components/PermissionsTable.tsx @@ -41,7 +41,9 @@ export const PermissionsTable: React.FC = ({ roleMembers.forEach((member) => { return !acc.find((m) => m.member === member) ? acc.push({ member, roles: [role] }) - : acc[acc.findIndex((m) => m.member === member)].roles.push(role); + : acc[acc.findIndex((m) => m.member === member)]?.roles.push( + role, + ); }); return acc; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/components/distribute-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/components/distribute-button.tsx index 76e576e472f..2994aa67120 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/components/distribute-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/components/distribute-button.tsx @@ -31,7 +31,7 @@ export const DistributeButton: React.FC = ({ const numTransactions = useMemo(() => { if ( validBalances.length === 1 && - validBalances[0].name === "Native Token" + validBalances[0]?.name === "Native Token" ) { return 1; } diff --git a/apps/dashboard/src/app/(dashboard)/dashboard/connect/ecosystem/EcosystemLandingPage.tsx b/apps/dashboard/src/app/(dashboard)/dashboard/connect/ecosystem/EcosystemLandingPage.tsx index 5b6ff2e8908..873daf8d907 100644 --- a/apps/dashboard/src/app/(dashboard)/dashboard/connect/ecosystem/EcosystemLandingPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/dashboard/connect/ecosystem/EcosystemLandingPage.tsx @@ -25,7 +25,7 @@ export async function EcosystemLandingPage(props: { console.error("failed to fetch ecosystems", err); return []; }); - if (ecosystems.length > 0) { + if (ecosystems[0]) { redirect(`${props.ecosystemLayoutPath}/${ecosystems[0].slug}`); } } diff --git a/apps/dashboard/src/app/(dashboard)/dashboard/connect/ecosystem/create/components/client/create-ecosystem-form.client.tsx b/apps/dashboard/src/app/(dashboard)/dashboard/connect/ecosystem/create/components/client/create-ecosystem-form.client.tsx index 143408298d8..4bd587eb770 100644 --- a/apps/dashboard/src/app/(dashboard)/dashboard/connect/ecosystem/create/components/client/create-ecosystem-form.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/dashboard/connect/ecosystem/create/components/client/create-ecosystem-form.client.tsx @@ -105,8 +105,10 @@ export function CreateEcosystemForm(props: { { - form.setValue("logo", files[0]); - form.clearErrors("logo"); + if (files[0]) { + form.setValue("logo", files[0]); + form.clearErrors("logo"); + } }} /> diff --git a/apps/dashboard/src/app/(dashboard)/explore/[category]/page.tsx b/apps/dashboard/src/app/(dashboard)/explore/[category]/page.tsx index fdd84952190..602335a234e 100644 --- a/apps/dashboard/src/app/(dashboard)/explore/[category]/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/explore/[category]/page.tsx @@ -80,10 +80,14 @@ export default async function ExploreCategoryPage(
{category.contracts.map((publishedContractId, idx) => { - const publisher: string = Array.isArray(publishedContractId) + const publisher: string | undefined = Array.isArray( + publishedContractId, + ) ? publishedContractId[0].split("/")[0] : publishedContractId.split("/")[0]; - const contractId: string = Array.isArray(publishedContractId) + const contractId: string | undefined = Array.isArray( + publishedContractId, + ) ? publishedContractId[0].split("/")[1] : publishedContractId.split("/")[1]; const modules = Array.isArray(publishedContractId) @@ -92,6 +96,10 @@ export default async function ExploreCategoryPage( const overrides = Array.isArray(publishedContractId) ? publishedContractId[2] : undefined; + if (!publisher || !contractId) { + return null; + } + return ( ({ - publisher: m.split("/")[0], - moduleId: m.split("/")[1], + publisher: m.split("/")[0] || "", + moduleId: m.split("/")[1] || "", })) : undefined } diff --git a/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/opengraph-image.tsx b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/opengraph-image.tsx index 80471e8f6a7..96d0f31f88a 100644 --- a/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/opengraph-image.tsx +++ b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/opengraph-image.tsx @@ -32,6 +32,10 @@ export default async function Image(props: { publishedContracts.find((p) => p.version === props.params.version) || publishedContracts[0]; + if (!publishedContract) { + return null; + } + const publishedContractName = publishedContract?.displayName || publishedContract?.name; diff --git a/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/page.tsx b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/page.tsx index 5de41f26c54..6829c17bee5 100644 --- a/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/[version]/page.tsx @@ -4,6 +4,7 @@ import { getThirdwebClient } from "@/constants/thirdweb.server"; import { SimpleGrid } from "@chakra-ui/react"; import { fetchPublishedContractVersions } from "components/contract-components/fetch-contracts-with-versions"; import { PublishedContract } from "components/contract-components/published-contract"; +import { notFound } from "next/navigation"; import { isAddress } from "thirdweb"; import { resolveAddress } from "thirdweb/extensions/ens"; import { PublishedActions } from "../../../components/contract-actions-published.client"; @@ -46,6 +47,10 @@ export default async function PublishedContractPage( publishedContractVersions.find((v) => v.version === props.params.version) || publishedContractVersions[0]; + if (!publishedContract) { + return notFound(); + } + return ( <> v.version === props.version) || publishedContractVersions[0]; + if (!publishedContract) { + return null; + } + const moduleUris = modules - .filter((m) => m !== null) + .filter((m) => m !== null && m !== undefined) .map((m) => m.publishMetadataUri); const [contractMetadata, ...fetchedModules] = await Promise.all([ fetchDeployMetadata({ diff --git a/apps/dashboard/src/app/(dashboard)/support/components/create-ticket.action.ts b/apps/dashboard/src/app/(dashboard)/support/components/create-ticket.action.ts index f342472e207..aa61a76f35a 100644 --- a/apps/dashboard/src/app/(dashboard)/support/components/create-ticket.action.ts +++ b/apps/dashboard/src/app/(dashboard)/support/components/create-ticket.action.ts @@ -116,7 +116,7 @@ export async function createTicketAction( const markdown = prepareEmailBody({ product, - markdownInput: keyVal.markdown, + markdownInput: keyVal.markdown || "", email: account.data.email, name: account.data.name, extraInfoInput: keyVal, diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/_components/sidebar/TeamsMobileNav.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/_components/sidebar/TeamsMobileNav.tsx index 01ff7cea062..3e564e5307a 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/_components/sidebar/TeamsMobileNav.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/settings/_components/sidebar/TeamsMobileNav.tsx @@ -15,7 +15,7 @@ export function TeamSettingsMobileNav(props: { return (
{ setShowFull(true); diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/WalletConnectorsChartCard.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/WalletConnectorsChartCard.tsx index 9c8da7acd88..57da11965b5 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/WalletConnectorsChartCard.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/analytics/_components/WalletConnectorsChartCard.tsx @@ -191,7 +191,7 @@ export function WalletConnectorsChartCard(props: { { - return walletsToPickFrom[ - Math.floor(Math.random() * walletsToPickFrom.length) - ]; + return ( + walletsToPickFrom[Math.floor(Math.random() * walletsToPickFrom.length)] || + "io.metamask" + ); }; export function createWalletStatsStub(days: number): WalletStats[] { diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/contracts/_components/GetStartedWithContractsDeploy.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/contracts/_components/GetStartedWithContractsDeploy.tsx index 29ee0c5474c..d728841e2f0 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/contracts/_components/GetStartedWithContractsDeploy.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/contracts/_components/GetStartedWithContractsDeploy.tsx @@ -37,12 +37,14 @@ type ContentItem = { onClick?: () => void; }; +type TabId = "explore" | "import" | "build" | "deploy"; + const DeployOptions = () => { const [showImportModal, setShowImportModal] = useState(false); const router = useDashboardRouter(); const trackEvent = useTrack(); - const content: Record = useMemo( + const content: Record = useMemo( () => ({ explore: { title: "Ready-to-deploy", @@ -72,8 +74,7 @@ const DeployOptions = () => { [], ); - const contentKeys = Object.keys(content); - const [activeTab, setActiveTab] = useState(contentKeys[0]); + const [activeTab, setActiveTab] = useState("explore"); const activeTabContent = content[activeTab]; return ( @@ -91,7 +92,7 @@ const DeployOptions = () => { name: value.title, isActive: activeTab === key, isEnabled: true, - onClick: () => setActiveTab(key), + onClick: () => setActiveTab(key as TabId), }))} tabClassName="font-medium !text-sm" /> diff --git a/apps/dashboard/src/app/team/components/TeamHeader/TeamAndProjectSelectorPopoverButton.tsx b/apps/dashboard/src/app/team/components/TeamHeader/TeamAndProjectSelectorPopoverButton.tsx index cb8a90bced9..06b560e4569 100644 --- a/apps/dashboard/src/app/team/components/TeamHeader/TeamAndProjectSelectorPopoverButton.tsx +++ b/apps/dashboard/src/app/team/components/TeamHeader/TeamAndProjectSelectorPopoverButton.tsx @@ -29,7 +29,12 @@ export function TeamAndProjectSelectorPopoverButton(props: TeamSwitcherProps) { const { currentTeam, teamsAndProjects } = props; const [hoveredTeam, setHoveredTeam] = useState(); const projectsToShowOfTeam = - hoveredTeam || currentTeam || teamsAndProjects[0].team; + hoveredTeam || currentTeam || teamsAndProjects[0]?.team; + + // if we can't find a single team associated with this user - something is really wrong + if (!projectsToShowOfTeam) { + return null; + } const projectsToShow = teamsAndProjects.find( (x) => x.team.slug === projectsToShowOfTeam.slug, diff --git a/apps/dashboard/src/app/team/components/TeamHeader/TeamHeaderUI.stories.tsx b/apps/dashboard/src/app/team/components/TeamHeader/TeamHeaderUI.stories.tsx index d6e6726a35d..fd021fc9efa 100644 --- a/apps/dashboard/src/app/team/components/TeamHeader/TeamHeaderUI.stories.tsx +++ b/apps/dashboard/src/app/team/components/TeamHeader/TeamHeaderUI.stories.tsx @@ -39,13 +39,21 @@ function Variants(props: { const Comp = props.type === "mobile" ? TeamHeaderMobileUI : TeamHeaderDesktopUI; + const team1 = teamsAndProjectsStub[0]?.team; + const team2 = teamsAndProjectsStub[1]?.team; + const team3 = teamsAndProjectsStub[2]?.team; + const team3Project = teamsAndProjectsStub[2]?.projects[0]; + + if (!team1 || !team2 || !team3 || !team3Project) { + return
failed to get team and project stubs
; + } return (
{}} connectButton={} @@ -60,7 +68,7 @@ function Variants(props: { {}} @@ -72,7 +80,7 @@ function Variants(props: { { const readyStepNum = direction === "next" ? Number(step) + 1 : Number(step) - 1; - const frameImg = connectFrames[readyStepNum]?.imageUrl; + const frameImg = + connectFrames[readyStepNum as keyof typeof connectFrames]?.imageUrl; if (!frameImg) { throw new Error(`Image frame not found for step: ${step}`); diff --git a/apps/dashboard/src/components/analytics/area-chart.tsx b/apps/dashboard/src/components/analytics/area-chart.tsx index 42b04553d79..1ec0675ada9 100644 --- a/apps/dashboard/src/components/analytics/area-chart.tsx +++ b/apps/dashboard/src/components/analytics/area-chart.tsx @@ -58,6 +58,10 @@ const AreaChart = < } const indexType = index.type || "date"; + const firstData = data[0]; + const lastData = data[data.length - 1]; + const firstDataAtIndexId = firstData?.[index.id]; + const lastDataAtIndexId = lastData?.[index.id]; return (
@@ -149,8 +153,8 @@ const AreaChart = < type="number" tick={{ transform: "translate(0, 6)" }} ticks={ - startEndOnly - ? [data[0][index.id], data[data.length - 1][index.id]] + startEndOnly && firstDataAtIndexId && lastDataAtIndexId + ? [firstDataAtIndexId, lastDataAtIndexId] : undefined } /> diff --git a/apps/dashboard/src/components/configure-networks/Form/IconUpload.tsx b/apps/dashboard/src/components/configure-networks/Form/IconUpload.tsx index 74696657ca3..505d5cb09c5 100644 --- a/apps/dashboard/src/components/configure-networks/Form/IconUpload.tsx +++ b/apps/dashboard/src/components/configure-networks/Form/IconUpload.tsx @@ -22,7 +22,11 @@ export const IconUpload: React.FC<{ onUpload: (url: string) => void }> = ({ storageUpload.mutate([file], { onSuccess([uri]) { - onUpload(uri); + if (uri) { + onUpload(uri); + } else { + toast.error("Something went wrong uploading icon"); + } // also refetch the files list queryClient.invalidateQueries({ queryKey: [PINNED_FILES_QUERY_KEY_ROOT], @@ -30,7 +34,7 @@ export const IconUpload: React.FC<{ onUpload: (url: string) => void }> = ({ }, onError(error) { console.error(error); - toast.error("Failed to upload file"); + toast.error("Failed to upload icon"); }, }); }; diff --git a/apps/dashboard/src/components/connect/CodePlayground.tsx b/apps/dashboard/src/components/connect/CodePlayground.tsx index 81a914cab16..238a826dd13 100644 --- a/apps/dashboard/src/components/connect/CodePlayground.tsx +++ b/apps/dashboard/src/components/connect/CodePlayground.tsx @@ -193,12 +193,12 @@ const CodePlayground = ({ diff --git a/apps/dashboard/src/components/contract-functions/contract-function.tsx b/apps/dashboard/src/components/contract-functions/contract-function.tsx index 32f8e4214bc..0c773a3749e 100644 --- a/apps/dashboard/src/components/contract-functions/contract-function.tsx +++ b/apps/dashboard/src/components/contract-functions/contract-function.tsx @@ -324,7 +324,7 @@ export const ContractFunctionsPanel: React.FC = ({ : undefined; const [selectedFunction, setSelectedFunction] = useState< - AbiFunction | AbiEvent + AbiFunction | AbiEvent | undefined >(_item ?? fnsOrEvents[0]); // Set the active tab to Write or Read depends on the `_item` const _defaultTabIndex = @@ -370,14 +370,15 @@ export const ContractFunctionsPanel: React.FC = ({ )} - {filteredFunctions.map((fn) => ( - - ))} + {selectedFunction && + filteredFunctions.map((fn) => ( + + ))} ); }; @@ -447,7 +448,7 @@ export const ContractFunctionsPanel: React.FC = ({ )} - {events.length > 0 && ( + {events.length > 0 && selectedFunction && ( {events.map((fn) => ( = ({ overflow="auto" colSpan={{ base: 12, md: 8 }} > - + {selectedFunction && ( + + )} ); @@ -476,7 +479,9 @@ export const ContractFunctionsPanel: React.FC = ({ interface FunctionsOrEventsListItemProps { fn: AbiFunction | AbiEvent; selectedFunction: AbiFunction | AbiEvent; - setSelectedFunction: Dispatch>; + setSelectedFunction: Dispatch< + SetStateAction + >; } const FunctionsOrEventsListItem: React.FC = ({ diff --git a/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx b/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx index 64e41e2b7fa..3873d8106a3 100644 --- a/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx +++ b/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx @@ -182,13 +182,16 @@ export const InAppWalletSettingsUI: React.FC< // FIXME: This must match components/settings/ApiKeys/Edit/index.tsx // Make it more generic w/o me thinking of values const newServices = [...services]; - newServices[serviceIdx] = { - ...services[serviceIdx], - customAuthentication, - customAuthEndpoint, - applicationImageUrl: branding?.applicationImageUrl, - applicationName: branding?.applicationName || apiKey.name, - }; + + if (services[serviceIdx]) { + newServices[serviceIdx] = { + ...services[serviceIdx], + customAuthentication, + customAuthEndpoint, + applicationImageUrl: branding?.applicationImageUrl, + applicationName: branding?.applicationName || apiKey.name, + }; + } props.updateApiKey( { diff --git a/apps/dashboard/src/components/embedded-wallets/Users/index.tsx b/apps/dashboard/src/components/embedded-wallets/Users/index.tsx index 86f582ebeb4..06eeff99fe0 100644 --- a/apps/dashboard/src/components/embedded-wallets/Users/index.tsx +++ b/apps/dashboard/src/components/embedded-wallets/Users/index.tsx @@ -97,7 +97,7 @@ export const InAppWalletUsersPageContent = (props: { } const csv = Papa.unparse( theWalletsWeWant.map((row) => ({ - email: row.ews_authed_user[0].email, + email: row.ews_authed_user[0]?.email, address: row.embedded_wallet?.[0]?.address || "", created: format(new Date(row.created_at), "MMM dd, yyyy"), last_login: format(new Date(row.last_accessed_at), "MMM dd, yyyy"), diff --git a/apps/dashboard/src/components/engine/overview/transactions-table.tsx b/apps/dashboard/src/components/engine/overview/transactions-table.tsx index bdccc0075f5..fb673428ed3 100644 --- a/apps/dashboard/src/components/engine/overview/transactions-table.tsx +++ b/apps/dashboard/src/components/engine/overview/transactions-table.tsx @@ -279,12 +279,12 @@ export const TransactionsTable: React.FC = ({ disclosure={transactionDisclosure} onClickPrevious={ idx > 0 - ? () => setSelectedTransaction(transactions[idx - 1]) + ? () => setSelectedTransaction(transactions[idx - 1] || null) : undefined } onClickNext={ idx < transactions.length - 1 - ? () => setSelectedTransaction(transactions[idx + 1]) + ? () => setSelectedTransaction(transactions[idx + 1] || null) : undefined } /> diff --git a/apps/dashboard/src/components/explore/contract-card/index.tsx b/apps/dashboard/src/components/explore/contract-card/index.tsx index 3eb651f8d1d..f3484a3cb77 100644 --- a/apps/dashboard/src/components/explore/contract-card/index.tsx +++ b/apps/dashboard/src/components/explore/contract-card/index.tsx @@ -11,6 +11,7 @@ import { moduleToBase64 } from "app/(dashboard)/published-contract/utils/module- import { RocketIcon, ShieldCheckIcon } from "lucide-react"; import Link from "next/link"; import { resolveScheme } from "thirdweb/storage"; +import invariant from "tiny-invariant"; import { fetchPublishedContractVersion } from "../../contract-components/fetch-contracts-with-versions"; import { ContractPublisher, replaceDeployerAddress } from "../publisher"; @@ -255,8 +256,11 @@ function usePublishedContract(publishedContractId: PublishedContractId) { const [publisher, contractId, version] = publishedContractId.split("/"); return useQuery({ queryKey: ["published-contract", { publishedContractId }], - queryFn: () => - fetchPublishedContractVersion(publisher, contractId, version), + queryFn: () => { + invariant(publisher, "publisher is required"); + invariant(contractId, "contractId is required"); + return fetchPublishedContractVersion(publisher, contractId, version); + }, enabled: !!publisher || !!contractId, }); } diff --git a/apps/dashboard/src/components/explore/contract-row/index.tsx b/apps/dashboard/src/components/explore/contract-row/index.tsx index f4534fa7cf6..959e927a0ff 100644 --- a/apps/dashboard/src/components/explore/contract-row/index.tsx +++ b/apps/dashboard/src/components/explore/contract-row/index.tsx @@ -48,10 +48,14 @@ export function ContractRow({ category }: ContractRowProps) {
{category.contracts.slice(0, 6).map((publishedContractId, idx) => { - const publisher: string = Array.isArray(publishedContractId) + const publisher: string | undefined = Array.isArray( + publishedContractId, + ) ? publishedContractId[0].split("/")[0] : publishedContractId.split("/")[0]; - const contractId: string = Array.isArray(publishedContractId) + const contractId: string | undefined = Array.isArray( + publishedContractId, + ) ? publishedContractId[0].split("/")[1] : publishedContractId.split("/")[1]; const modules = Array.isArray(publishedContractId) @@ -60,6 +64,10 @@ export function ContractRow({ category }: ContractRowProps) { const overrides = Array.isArray(publishedContractId) ? publishedContractId[2] : undefined; + + if (!publisher || !contractId) { + return null; + } return ( ({ - publisher: m.split("/")[0], - moduleId: m.split("/")[1], + publisher: m.split("/")[0] || "", + moduleId: m.split("/")[1] || "", })) : undefined } diff --git a/apps/dashboard/src/components/ipfs-upload/button.tsx b/apps/dashboard/src/components/ipfs-upload/button.tsx index 56a1f746d52..0a5c0c94e9c 100644 --- a/apps/dashboard/src/components/ipfs-upload/button.tsx +++ b/apps/dashboard/src/components/ipfs-upload/button.tsx @@ -2,6 +2,7 @@ import type { UseMutationResult } from "@tanstack/react-query"; import { FileInput } from "components/shared/FileInput"; import { useErrorHandler } from "contexts/error-handler"; import { UploadIcon } from "lucide-react"; +import { toast } from "sonner"; import { Button, type ButtonProps } from "tw-components"; import type { ComponentWithChildren } from "types/component-with-children"; @@ -20,7 +21,13 @@ export const IpfsUploadButton: ComponentWithChildren = ({ const { onError } = useErrorHandler(); const handleUpload = (file: File) => { storageUpload.mutate([file], { - onSuccess: ([uri]) => onUpload(uri), + onSuccess: ([uri]) => { + if (uri) { + onUpload(uri); + } else { + toast.error("File Upload but no URI returned"); + } + }, onError: (error) => onError(error, "Failed to upload file"), }); }; diff --git a/apps/dashboard/src/components/ipfs-upload/dropzone.tsx b/apps/dashboard/src/components/ipfs-upload/dropzone.tsx index 632ef17ea51..c7209906942 100644 --- a/apps/dashboard/src/components/ipfs-upload/dropzone.tsx +++ b/apps/dashboard/src/components/ipfs-upload/dropzone.tsx @@ -151,14 +151,15 @@ const FileUpload: React.FC = ({ files, updateFiles }) => { // const progressPercent = (progress.progress / progress.total) * 100; const mainIpfsUri = useMemo(() => { - if (ipfsHashes.length === 0) { + const firstHash = ipfsHashes[0]; + if (firstHash === undefined) { return ""; } if (ipfsHashes.length === 1) { - return replaceIpfsUrl(ipfsHashes[0]); + return replaceIpfsUrl(firstHash); } // get the folder - const uri = ipfsHashes[0].split("ipfs://")[1]; + const uri = firstHash.split("ipfs://")[1] || ""; const folderPath = uri.split("/")[0]; // "Qma.../image.png" -> "Qma..." return `https://ipfs.io/ipfs/${folderPath}`; }, [ipfsHashes]); @@ -183,6 +184,9 @@ const FileUpload: React.FC = ({ files, updateFiles }) => { > {filesToShow.map((file, index) => { const ipfsHash = ipfsHashes[index]; + if (!ipfsHash) { + return null; + } return ( = ({ files, updateFiles }) => { uri: uris.length === 1 ? uris[0] - : uris[0].split("/").slice(0, -1).join("/"), + : (uris[0] || "").split("/").slice(0, -1).join("/"), }); setIpfsHashes(uris); // also refetch the files list diff --git a/apps/dashboard/src/components/landing-pages/dynamic-selector.tsx b/apps/dashboard/src/components/landing-pages/dynamic-selector.tsx index 9ac9421bc10..db4fca9ffc5 100644 --- a/apps/dashboard/src/components/landing-pages/dynamic-selector.tsx +++ b/apps/dashboard/src/components/landing-pages/dynamic-selector.tsx @@ -35,11 +35,16 @@ export const LandingDynamicSelector: React.FC = ({ lineHeight, margin = "", }) => { - const [selectedItemTitle, setSelectedItemTitle] = useState(items[0].title); + const firstItem = items[0]; + const [selectedItemTitle, setSelectedItemTitle] = useState(firstItem?.title); const trackEvent = useTrack(); + if (!firstItem) { + return null; + } + const selectedItem = - items.find((item) => item.title === selectedItemTitle) || items[0]; + items.find((item) => item.title === selectedItemTitle) || firstItem; return ( diff --git a/apps/dashboard/src/components/landing-pages/option-selector.tsx b/apps/dashboard/src/components/landing-pages/option-selector.tsx index ae0ae02c301..9188bbd3a73 100644 --- a/apps/dashboard/src/components/landing-pages/option-selector.tsx +++ b/apps/dashboard/src/components/landing-pages/option-selector.tsx @@ -32,11 +32,17 @@ export const LandingOptionSelector: React.FC = ({ TRACKING_CATEGORY, blackToWhiteTitle, }) => { - const [selectedItemTitle, setSelectedItemTitle] = useState(items[0].title); + const firstItem = items[0]; + + const [selectedItemTitle, setSelectedItemTitle] = useState(firstItem?.title); const trackEvent = useTrack(); + if (!firstItem) { + return null; + } + const selectedItem = - items.find((item) => item.title === selectedItemTitle) || items[0]; + items.find((item) => item.title === selectedItemTitle) || firstItem; return ( diff --git a/apps/dashboard/src/constants/currencies.ts b/apps/dashboard/src/constants/currencies.ts index 5e12f353389..62b5aa5a83f 100644 --- a/apps/dashboard/src/constants/currencies.ts +++ b/apps/dashboard/src/constants/currencies.ts @@ -8,11 +8,8 @@ import { ethereum, fantom, fantomTestnet, - hardhat, - localhost, optimism, polygon, - sepolia, } from "thirdweb/chains"; export interface CurrencyMetadata { @@ -21,160 +18,126 @@ export interface CurrencyMetadata { symbol: string; } -const NATIVE_TOKENS: Record< - number, - { - name: string; - symbol: string; - decimals: number; - wrapped: CurrencyMetadata; - } -> = /* @__PURE__ */ { - [ethereum.id]: { - name: "Ether", - symbol: "ETH", - decimals: 18, - wrapped: { - address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - name: "Wrapped Ether", - symbol: "WETH", - }, - }, - [sepolia.id]: { - name: "Sepolia Ether", - symbol: "SEP", - decimals: 18, - wrapped: { - address: "0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9", - name: "Wrapped Ether", - symbol: "WETH", - }, - }, - [polygon.id]: { - name: "Matic", - symbol: "MATIC", - decimals: 18, - wrapped: { - address: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", - name: "Wrapped Matic", - symbol: "WMATIC", - }, - }, - [avalanche.id]: { - name: "Avalanche", - symbol: "AVAX", - decimals: 18, - wrapped: { - address: "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", - name: "Wrapped AVAX", - symbol: "WAVAX", - }, - }, - [avalancheFuji.id]: { - name: "Avalanche", - symbol: "AVAX", - decimals: 18, - wrapped: { - address: "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", - name: "Wrapped AVAX", - symbol: "WAVAX", - }, - }, - [fantom.id]: { - name: "Fantom", - symbol: "FTM", - decimals: 18, - wrapped: { - address: "0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83", - name: "Wrapped Fantom", - symbol: "WFTM", - }, - }, - [fantomTestnet.id]: { - name: "Fantom", - symbol: "FTM", - decimals: 18, - wrapped: { - address: "0xf1277d1Ed8AD466beddF92ef448A132661956621", - name: "Wrapped Fantom", - symbol: "WFTM", - }, - }, - [arbitrum.id]: { - name: "Ether", - symbol: "ETH", - decimals: 18, - wrapped: { - address: "0x82af49447d8a07e3bd95bd0d56f35241523fbab1", - name: "Wrapped Ether", - symbol: "WETH", - }, - }, - [optimism.id]: { - name: "Ether", - symbol: "ETH", - decimals: 18, - wrapped: { - address: "0x4200000000000000000000000000000000000006", - name: "Wrapped Ether", - symbol: "WETH", - }, - }, - [bsc.id]: { - name: "Binance Chain Native Token", - symbol: "BNB", - decimals: 18, - wrapped: { - address: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", - name: "Wrapped Binance Chain Token", - symbol: "WBNB", - }, - }, - [bscTestnet.id]: { - name: "Binance Chain Native Token", - symbol: "TBNB", - decimals: 18, - wrapped: { - address: "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", - name: "Wrapped Binance Chain Testnet Token", - symbol: "WBNB", - }, - }, - [hardhat.id]: { - name: "Ether", - symbol: "ETH", - decimals: 18, - wrapped: { - address: "0x5FbDB2315678afecb367f032d93F642f64180aa3", - name: "Wrapped Ether", - symbol: "WETH", - }, - }, - [localhost.id]: { - name: "Ether", - symbol: "ETH", - decimals: 18, - wrapped: { - address: "0x5FbDB2315678afecb367f032d93F642f64180aa3", - name: "Wrapped Ether", - symbol: "WETH", - }, - }, - [280]: { - name: "zkSync Era Testnet", - symbol: "ETH", - decimals: 18, - wrapped: { - address: "0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91", - name: "Wrapped Ether", - symbol: "WETH", - }, +type NativeTokenInfo = { + name: string; + symbol: string; + decimals: number; + wrapped: CurrencyMetadata; +}; + +const ethereumNativeToken: NativeTokenInfo = { + name: "Ether", + symbol: "ETH", + decimals: 18, + wrapped: { + address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + name: "Wrapped Ether", + symbol: "WETH", + }, +}; + +const polygonNativeToken: NativeTokenInfo = { + name: "Matic", + symbol: "MATIC", + decimals: 18, + wrapped: { + address: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + name: "Wrapped Matic", + symbol: "WMATIC", + }, +}; + +const avalancheNativeToken: NativeTokenInfo = { + name: "Avalanche", + symbol: "AVAX", + decimals: 18, + wrapped: { + address: "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", + name: "Wrapped AVAX", + symbol: "WAVAX", + }, +}; + +const avalancheFujiNativeToken: NativeTokenInfo = { + name: "Avalanche", + symbol: "AVAX", + decimals: 18, + wrapped: { + address: "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + name: "Wrapped AVAX", + symbol: "WAVAX", + }, +}; + +const fantomNativeToken: NativeTokenInfo = { + name: "Fantom", + symbol: "FTM", + decimals: 18, + wrapped: { + address: "0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83", + name: "Wrapped Fantom", + symbol: "WFTM", + }, +}; + +const fantomTestnetNativeToken: NativeTokenInfo = { + name: "Fantom", + symbol: "FTM", + decimals: 18, + wrapped: { + address: "0xf1277d1Ed8AD466beddF92ef448A132661956621", + name: "Wrapped Fantom", + symbol: "WFTM", + }, +}; + +const arbitrumNativeToken: NativeTokenInfo = { + name: "Ether", + symbol: "ETH", + decimals: 18, + wrapped: { + address: "0x82af49447d8a07e3bd95bd0d56f35241523fbab1", + name: "Wrapped Ether", + symbol: "WETH", + }, +}; + +const optimismNativeToken: NativeTokenInfo = { + name: "Ether", + symbol: "ETH", + decimals: 18, + wrapped: { + address: "0x4200000000000000000000000000000000000006", + name: "Wrapped Ether", + symbol: "WETH", + }, +}; + +const bscNativeToken: NativeTokenInfo = { + name: "Binance Chain Native Token", + symbol: "BNB", + decimals: 18, + wrapped: { + address: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", + name: "Wrapped Binance Chain Token", + symbol: "WBNB", + }, +}; + +const bscTestnetNativeToken: NativeTokenInfo = { + name: "Binance Chain Native Token", + symbol: "TBNB", + decimals: 18, + wrapped: { + address: "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", + name: "Wrapped Binance Chain Testnet Token", + symbol: "WBNB", }, }; const Ethereum: CurrencyMetadata[] = [ { - ...NATIVE_TOKENS[ethereum.id].wrapped, + ...ethereumNativeToken.wrapped, }, { address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", @@ -200,7 +163,7 @@ const Ethereum: CurrencyMetadata[] = [ const Polygon: CurrencyMetadata[] = [ { - ...NATIVE_TOKENS[polygon.id].wrapped, + ...polygonNativeToken.wrapped, }, { address: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", @@ -233,7 +196,7 @@ const Polygon: CurrencyMetadata[] = [ const Fantom: CurrencyMetadata[] = [ { - ...NATIVE_TOKENS[fantom.id].wrapped, + ...fantomNativeToken.wrapped, }, { name: "Wrapped Ether", @@ -254,13 +217,13 @@ const Fantom: CurrencyMetadata[] = [ const FantomTestnet: CurrencyMetadata[] = [ { - ...NATIVE_TOKENS[fantomTestnet.id].wrapped, + ...fantomTestnetNativeToken.wrapped, }, ]; const Avalanche: CurrencyMetadata[] = [ { - ...NATIVE_TOKENS[avalanche.id].wrapped, + ...avalancheNativeToken.wrapped, }, { address: "0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB", @@ -291,7 +254,7 @@ const Avalanche: CurrencyMetadata[] = [ const AvalancheFujiTestnet: CurrencyMetadata[] = [ { - ...NATIVE_TOKENS[avalancheFuji.id].wrapped, + ...avalancheFujiNativeToken.wrapped, }, // Source: https://developers.circle.com/stablecoins/docs/usdc-on-testing-networks#usdc-on-avalanche-testnet { @@ -303,7 +266,7 @@ const AvalancheFujiTestnet: CurrencyMetadata[] = [ const Optimism: CurrencyMetadata[] = [ { - ...NATIVE_TOKENS[optimism.id].wrapped, + ...optimismNativeToken.wrapped, }, // Source: https://www.circle.com/blog/now-available-usdc-on-op-mainnet { @@ -321,7 +284,7 @@ const Optimism: CurrencyMetadata[] = [ const Arbitrum: CurrencyMetadata[] = [ { - ...NATIVE_TOKENS[arbitrum.id].wrapped, + ...arbitrumNativeToken.wrapped, }, // Source: https://www.circle.com/blog/arbitrum-usdc-now-available { @@ -339,7 +302,7 @@ const Arbitrum: CurrencyMetadata[] = [ const BinanceMainnet: CurrencyMetadata[] = [ { - ...NATIVE_TOKENS[bsc.id].wrapped, + ...bscNativeToken.wrapped, }, { address: "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56", @@ -360,7 +323,7 @@ const BinanceMainnet: CurrencyMetadata[] = [ const BinanceTestnet: CurrencyMetadata[] = [ { - ...NATIVE_TOKENS[bscTestnet.id].wrapped, + ...bscTestnetNativeToken.wrapped, }, { address: "0xed24fc36d5ee211ea25a80239fb8c4cfd80f12ee", diff --git a/apps/dashboard/src/contexts/map-chains.tsx b/apps/dashboard/src/contexts/map-chains.tsx index a150ac5e998..bbd52a119e1 100644 --- a/apps/dashboard/src/contexts/map-chains.tsx +++ b/apps/dashboard/src/contexts/map-chains.tsx @@ -1,10 +1,11 @@ -import type { Chain } from "thirdweb"; +import { type Chain, defineChain } from "thirdweb"; import type { StoredChain } from "../stores/chainStores"; export function mapV4ChainToV5Chain(v4Chain: StoredChain) { const chain: Chain = { id: v4Chain.chainId, - rpc: v4Chain.rpc[0], + // eslint-disable-next-line no-restricted-syntax + rpc: v4Chain.rpc[0] || defineChain(v4Chain.chainId).rpc, // TypeScript shenanigans, just avoiding as string assertion here blockExplorers: v4Chain.explorers?.map((x) => x), // TypeScript shenanigans, just avoiding as string assertion here diff --git a/apps/dashboard/src/contract-ui/hooks/permissions.ts b/apps/dashboard/src/contract-ui/hooks/permissions.ts index 17bc7a072aa..5c08bfe4626 100644 --- a/apps/dashboard/src/contract-ui/hooks/permissions.ts +++ b/apps/dashboard/src/contract-ui/hooks/permissions.ts @@ -48,6 +48,9 @@ export function createSetAllRoleMembersTx(options: { const sortedRoles = roles.sort((role) => (role === "admin" ? 1 : -1)); for (let i = 0; i < sortedRoles.length; i++) { const role = sortedRoles[i]; + if (!role) { + continue; + } const [addresses, currentAddresses] = await Promise.all([ Promise.all( options.roleMemberMap[role]?.map((addressOrEns) => diff --git a/apps/dashboard/src/contract-ui/tabs/code/components/code-overview.tsx b/apps/dashboard/src/contract-ui/tabs/code/components/code-overview.tsx index ef054f644a1..c50d68bc612 100644 --- a/apps/dashboard/src/contract-ui/tabs/code/components/code-overview.tsx +++ b/apps/dashboard/src/contract-ui/tabs/code/components/code-overview.tsx @@ -469,10 +469,16 @@ export function formatSnippet( fn?.name && extensionNamespace && extensionNamespace in EXTENSION_NAMESPACE_FUNCTION_MAPPING && + EXTENSION_NAMESPACE_FUNCTION_MAPPING[extensionNamespace] && fn.name in EXTENSION_NAMESPACE_FUNCTION_MAPPING[extensionNamespace] ) { const extensionConfig = EXTENSION_NAMESPACE_FUNCTION_MAPPING[extensionNamespace][fn.name]; + + if (!extensionConfig) { + continue; + } + switch (env) { case "javascript": codeForEnv = buildJavascriptSnippet({ diff --git a/apps/dashboard/src/hooks/chains/allChains.ts b/apps/dashboard/src/hooks/chains/allChains.ts index dd43b77f5a0..2f2def68005 100644 --- a/apps/dashboard/src/hooks/chains/allChains.ts +++ b/apps/dashboard/src/hooks/chains/allChains.ts @@ -53,6 +53,9 @@ function createAllChainsStore() { for (let i = 0; i < originalAllChains.length; i++) { let _chain = originalAllChains[i]; + if (!_chain) { + continue; + } // for dev env, replace the rpc urls to dev domain if (!isProd) { diff --git a/apps/dashboard/src/lib/connect-frames.ts b/apps/dashboard/src/lib/connect-frames.ts index 8780bd50bac..fef15d834a6 100644 --- a/apps/dashboard/src/lib/connect-frames.ts +++ b/apps/dashboard/src/lib/connect-frames.ts @@ -4,7 +4,7 @@ interface Frame { imageUrl: string; } -export const connectFrames: Record = { +export const connectFrames: Record<1 | 2 | 3 | 4 | 5 | 6 | 7, Frame> = { 1: { imageUrl: `${getAbsoluteUrl()}/assets/connect/frames/frame-1.png`, }, diff --git a/apps/dashboard/src/lib/search.ts b/apps/dashboard/src/lib/search.ts index 1278d2319ab..166fb3d6538 100644 --- a/apps/dashboard/src/lib/search.ts +++ b/apps/dashboard/src/lib/search.ts @@ -180,7 +180,7 @@ async function fetchContractName(chainId: number, contractAddress: string) { } const compilationTarget = json.settings.compilationTarget; const targets = Object.keys(compilationTarget); - const name = compilationTarget[targets[0]]; + const name = targets[0] ? compilationTarget[targets[0]] : ""; return name; } diff --git a/apps/dashboard/src/lib/wallet/nfts/simpleHash.ts b/apps/dashboard/src/lib/wallet/nfts/simpleHash.ts index a7a635c95bd..906b62442bc 100644 --- a/apps/dashboard/src/lib/wallet/nfts/simpleHash.ts +++ b/apps/dashboard/src/lib/wallet/nfts/simpleHash.ts @@ -16,7 +16,9 @@ export function generateSimpleHashUrl({ chainId, owner }: GenerateURLParams) { const url = new URL("https://api.simplehash.com/api/v0/nfts/owners"); url.searchParams.append("wallet_addresses", owner); - url.searchParams.append("chains", simpleHashSupportedChainIdsMap[chainId]); + if (simpleHashSupportedChainIdsMap[chainId]) { + url.searchParams.append("chains", simpleHashSupportedChainIdsMap[chainId]); + } return url.toString(); } diff --git a/apps/dashboard/src/lib/wallet/nfts/tokenUri.ts b/apps/dashboard/src/lib/wallet/nfts/tokenUri.ts index b6ae3b1b8b3..6ecb56ec464 100644 --- a/apps/dashboard/src/lib/wallet/nfts/tokenUri.ts +++ b/apps/dashboard/src/lib/wallet/nfts/tokenUri.ts @@ -7,7 +7,7 @@ export function handleArbitraryTokenURI(rawUri: string): string { } const uriSplit = `ipfs://${rawUri.split("/ipfs/")}`; - return uriSplit[uriSplit.length - 1]; + return uriSplit[uriSplit.length - 1] || ""; } export function shouldDownloadURI(rawUri: string): boolean { diff --git a/apps/dashboard/src/middleware.ts b/apps/dashboard/src/middleware.ts index 12fd4435e30..1fd94957975 100644 --- a/apps/dashboard/src/middleware.ts +++ b/apps/dashboard/src/middleware.ts @@ -73,7 +73,7 @@ export async function middleware(request: NextRequest) { // DIFFERENT DYNAMIC ROUTING CASES // /
/... case - if (isPossibleEVMAddress(paths[0])) { + if (paths[0] && isPossibleEVMAddress(paths[0])) { // special case for "deployer.thirdweb.eth" // we want to always redirect this to "thirdweb.eth/..." if (paths[0] === "deployer.thirdweb.eth") { diff --git a/apps/dashboard/src/pages/api/og/profile.tsx b/apps/dashboard/src/pages/api/og/profile.tsx index edac6c826c6..525bb451446 100644 --- a/apps/dashboard/src/pages/api/og/profile.tsx +++ b/apps/dashboard/src/pages/api/og/profile.tsx @@ -87,10 +87,12 @@ function descriptionShortener(description: string) { words.push(word); currentLength += word.length + 1; } - if (words[words.length - 1].length < 4) { + + const lastWord = words[words.length - 1]; + if (lastWord && lastWord.length < 4) { words = words.slice(0, -1); } - if (words[words.length - 1].endsWith(".")) { + if (lastWord?.endsWith(".")) { return words.join(" "); } if (!shortened) { diff --git a/apps/dashboard/src/pages/connect.tsx b/apps/dashboard/src/pages/connect.tsx index 09fc4ba69b3..82f2cf351ad 100644 --- a/apps/dashboard/src/pages/connect.tsx +++ b/apps/dashboard/src/pages/connect.tsx @@ -132,7 +132,10 @@ const ConnectLanding: ThirdwebNextPage = () => { {/* Farcaster frames headers */} - + e.row === i)) { - if (results.data[i].name) { - validResults.data.push(results.data[i]); + const result = results.data[i]; + if (result?.name) { + validResults.data.push(result); } } } diff --git a/apps/dashboard/tsconfig.json b/apps/dashboard/tsconfig.json index e21529eca1f..06452e93d1d 100644 --- a/apps/dashboard/tsconfig.json +++ b/apps/dashboard/tsconfig.json @@ -16,6 +16,7 @@ "jsx": "preserve", "incremental": true, "downlevelIteration": true, + "noUncheckedIndexedAccess": true, "paths": { "tw-components": ["tw-components"], "tw-components/*": ["tw-components/*"]