From 5da500edcae2881c7f838b36ab6cc33c6110a21b Mon Sep 17 00:00:00 2001 From: MananTank Date: Sat, 19 Oct 2024 02:46:44 +0000 Subject: [PATCH] Add noUncheckedIndexedAccess: true in dashboard tsconfig and fix all issues (#5084) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem solved Short description of the bug fixed or feature added --- ## PR-Codex overview This PR introduces various enhancements and bug fixes across multiple files, focusing on improving type safety, handling undefined values, and refining conditions to prevent errors. It also updates the handling of cookies, contract metadata, and native token definitions. ### Detailed summary - Added `"noUncheckedIndexedAccess": true` in `tsconfig.json`. - Updated cookie retrieval to check for `_val` in `SyncStoreToCookies.tsx`. - Modified ecosystem redirection condition in `EcosystemLandingPage.tsx`. - Added checks for undefined chains in `allChains.ts`. - Improved handling of optional values in various components. - Enhanced error handling for uploaded files in `IconUpload.tsx`. - Refactored native token definitions for better clarity and maintainability. - Checked for existence of items before accessing properties in several components. - Ensured consistent handling of optional chaining to avoid runtime errors. - Updated metadata handling in various contract-related components. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` --- .../src/@/components/blocks/multi-select.tsx | 13 +- .../components/blocks/select-with-search.tsx | 12 +- apps/dashboard/src/@/components/ui/chart.tsx | 2 +- .../src/@/components/ui/image-upload.tsx | 4 +- .../src/@/components/ui/input-otp.tsx | 5 +- .../@3rdweb-sdk/react/hooks/useActivity.ts | 13 +- .../hooks/useDashboardContractMetadata.tsx | 2 +- .../[contractAddress]/embed/embed-setup.tsx | 6 +- .../overview/components/PermissionsTable.tsx | 4 +- .../split/components/distribute-button.tsx | 2 +- .../ecosystem/EcosystemLandingPage.tsx | 2 +- .../client/create-ecosystem-form.client.tsx | 6 +- .../(dashboard)/explore/[category]/page.tsx | 16 +- .../[version]/opengraph-image.tsx | 4 + .../[contract_id]/[version]/page.tsx | 5 + .../[contract_id]/opengraph-image.tsx | 4 + .../[publisher]/[contract_id]/page.tsx | 5 + .../publishedContractOGImageTemplate.tsx | 5 +- .../components/publish-based-deploy.tsx | 6 +- .../components/create-ticket.action.ts | 2 +- .../_components/sidebar/TeamsMobileNav.tsx | 2 +- .../_components/WalletConnectorsChartCard.tsx | 2 +- .../WalletDistributionChartCard.tsx | 2 +- .../analytics/_components/storyUtils.ts | 7 +- .../GetStartedWithContractsDeploy.tsx | 9 +- .../TeamAndProjectSelectorPopoverButton.tsx | 7 +- .../TeamHeader/TeamHeaderUI.stories.tsx | 18 +- apps/dashboard/src/classes/ConnectFrame.ts | 5 +- .../src/components/analytics/area-chart.tsx | 8 +- .../configure-networks/Form/IconUpload.tsx | 8 +- .../src/components/connect/CodePlayground.tsx | 12 +- .../contract-deploy-form/custom-contract.tsx | 6 +- .../contract-params-fieldset.tsx | 6 +- .../contract-functions/contract-function.tsx | 29 +- .../embedded-wallets/Configure/index.tsx | 17 +- .../embedded-wallets/Users/index.tsx | 2 +- .../engine/overview/transactions-table.tsx | 4 +- .../explore/contract-card/index.tsx | 8 +- .../components/explore/contract-row/index.tsx | 16 +- .../src/components/ipfs-upload/button.tsx | 9 +- .../src/components/ipfs-upload/dropzone.tsx | 12 +- .../landing-pages/dynamic-selector.tsx | 9 +- .../landing-pages/option-selector.tsx | 10 +- apps/dashboard/src/constants/currencies.ts | 285 ++++++++---------- apps/dashboard/src/contexts/map-chains.tsx | 5 +- .../src/contract-ui/hooks/permissions.ts | 3 + .../tabs/code/components/code-overview.tsx | 6 + apps/dashboard/src/hooks/chains/allChains.ts | 3 + apps/dashboard/src/lib/connect-frames.ts | 2 +- apps/dashboard/src/lib/search.ts | 2 +- .../src/lib/wallet/nfts/simpleHash.ts | 4 +- .../dashboard/src/lib/wallet/nfts/tokenUri.ts | 2 +- apps/dashboard/src/middleware.ts | 2 +- apps/dashboard/src/pages/api/og/profile.tsx | 6 +- apps/dashboard/src/pages/connect.tsx | 5 +- .../src/stores/SyncStoreToCookies.tsx | 2 +- apps/dashboard/src/utils/batch.ts | 5 +- apps/dashboard/tsconfig.json | 1 + 58 files changed, 378 insertions(+), 281 deletions(-) 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/*"]