diff --git a/.changeset/cyan-shrimps-battle.md b/.changeset/cyan-shrimps-battle.md deleted file mode 100644 index deab0a05c96..00000000000 --- a/.changeset/cyan-shrimps-battle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"thirdweb": patch ---- - -Update implementations diff --git a/.changeset/fair-planes-doubt.md b/.changeset/fair-planes-doubt.md deleted file mode 100644 index d3ca2bda5dc..00000000000 --- a/.changeset/fair-planes-doubt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"thirdweb": patch ---- - -SDK: Fix chain switching in smart account transactions diff --git a/.changeset/metal-mails-ring.md b/.changeset/metal-mails-ring.md deleted file mode 100644 index 89c5f6f9e84..00000000000 --- a/.changeset/metal-mails-ring.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -"thirdweb": patch ---- - -- Add onClose callback to Connect Details modal - -```tsx - { - // The last screen name that was being shown when user closed the modal - console.log({ screen }); - } - }} -/> -``` - -- Small fix for ChainIcon: Always resolve IPFS URI - -- Improve test coverage \ No newline at end of file diff --git a/apps/dashboard/framer-rewrites.js b/apps/dashboard/framer-rewrites.js index 9cd44ca676d..6d8f257c5a6 100644 --- a/apps/dashboard/framer-rewrites.js +++ b/apps/dashboard/framer-rewrites.js @@ -26,7 +26,7 @@ module.exports = [ "/solutions/defi", "/solutions/ecosystem", // -- campaigns -- - "/unlimited-wallets", + // "/unlimited-wallets", -- OFF for now // -- TPP -- "/trusted-partner-program", "/trusted-partner-program/:partner_slug", diff --git a/apps/dashboard/next.config.ts b/apps/dashboard/next.config.ts index 58a70941593..dec847c159f 100644 --- a/apps/dashboard/next.config.ts +++ b/apps/dashboard/next.config.ts @@ -19,204 +19,204 @@ const ContentSecurityPolicy = ` `; const securityHeaders = [ - { - key: "X-DNS-Prefetch-Control", - value: "on", - }, - { - key: "X-XSS-Protection", - value: "1; mode=block", - }, - { - key: "X-Frame-Options", - value: "SAMEORIGIN", - }, - { - key: "Referrer-Policy", - value: "origin-when-cross-origin", - }, - { - key: "Content-Security-Policy", - value: ContentSecurityPolicy.replace(/\s{2,}/g, " ").trim(), - }, + { + key: "X-DNS-Prefetch-Control", + value: "on", + }, + { + key: "X-XSS-Protection", + value: "1; mode=block", + }, + { + key: "X-Frame-Options", + value: "SAMEORIGIN", + }, + { + key: "Referrer-Policy", + value: "origin-when-cross-origin", + }, + { + key: "Content-Security-Policy", + value: ContentSecurityPolicy.replace(/\s{2,}/g, " ").trim(), + }, ]; function determineIpfsGateways() { - // add the clientId ipfs gateways - const remotePatterns: RemotePattern[] = []; - if (process.env.API_ROUTES_CLIENT_ID) { - remotePatterns.push({ - protocol: "https", - hostname: `${process.env.API_ROUTES_CLIENT_ID}.ipfscdn.io`, - }); - remotePatterns.push({ - protocol: "https", - hostname: `${process.env.API_ROUTES_CLIENT_ID}.thirdwebstorage-staging.com`, - }); - remotePatterns.push({ - protocol: "https", - hostname: `${process.env.API_ROUTES_CLIENT_ID}.thirdwebstorage-dev.com`, - }); - } else { - // this should only happen in development - remotePatterns.push({ - protocol: "https", - hostname: "ipfs.io", - }); - } - // also add the dashboard clientId ipfs gateway if it is set - if (process.env.NEXT_PUBLIC_DASHBOARD_CLIENT_ID) { - remotePatterns.push({ - protocol: "https", - hostname: `${process.env.NEXT_PUBLIC_DASHBOARD_CLIENT_ID}.ipfscdn.io`, - }); - remotePatterns.push({ - protocol: "https", - hostname: `${process.env.NEXT_PUBLIC_DASHBOARD_CLIENT_ID}.thirdwebstorage-staging.com`, - }); - remotePatterns.push({ - protocol: "https", - hostname: `${process.env.NEXT_PUBLIC_DASHBOARD_CLIENT_ID}.thirdwebstorage-dev.com`, - }); - } - return remotePatterns; + // add the clientId ipfs gateways + const remotePatterns: RemotePattern[] = []; + if (process.env.API_ROUTES_CLIENT_ID) { + remotePatterns.push({ + protocol: "https", + hostname: `${process.env.API_ROUTES_CLIENT_ID}.ipfscdn.io`, + }); + remotePatterns.push({ + protocol: "https", + hostname: `${process.env.API_ROUTES_CLIENT_ID}.thirdwebstorage-staging.com`, + }); + remotePatterns.push({ + protocol: "https", + hostname: `${process.env.API_ROUTES_CLIENT_ID}.thirdwebstorage-dev.com`, + }); + } else { + // this should only happen in development + remotePatterns.push({ + protocol: "https", + hostname: "ipfs.io", + }); + } + // also add the dashboard clientId ipfs gateway if it is set + if (process.env.NEXT_PUBLIC_DASHBOARD_CLIENT_ID) { + remotePatterns.push({ + protocol: "https", + hostname: `${process.env.NEXT_PUBLIC_DASHBOARD_CLIENT_ID}.ipfscdn.io`, + }); + remotePatterns.push({ + protocol: "https", + hostname: `${process.env.NEXT_PUBLIC_DASHBOARD_CLIENT_ID}.thirdwebstorage-staging.com`, + }); + remotePatterns.push({ + protocol: "https", + hostname: `${process.env.NEXT_PUBLIC_DASHBOARD_CLIENT_ID}.thirdwebstorage-dev.com`, + }); + } + return remotePatterns; } const SENTRY_OPTIONS: SentryBuildOptions = { - // For all available options, see: - // https://github.com/getsentry/sentry-webpack-plugin#options + // For all available options, see: + // https://github.com/getsentry/sentry-webpack-plugin#options - org: "thirdweb-dev", - project: "dashboard", - // An auth token is required for uploading source maps. - authToken: process.env.SENTRY_AUTH_TOKEN, - // Suppresses source map uploading logs during build - silent: true, - // For all available options, see: - // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ + org: "thirdweb-dev", + project: "dashboard", + // An auth token is required for uploading source maps. + authToken: process.env.SENTRY_AUTH_TOKEN, + // Suppresses source map uploading logs during build + silent: true, + // For all available options, see: + // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ - // Upload a larger set of source maps for prettier stack traces (increases build time) - widenClientFileUpload: true, + // Upload a larger set of source maps for prettier stack traces (increases build time) + widenClientFileUpload: true, - // Routes browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers (increases server load) - tunnelRoute: "/err", + // Routes browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers (increases server load) + tunnelRoute: "/err", - // Hides source maps from generated client bundles - hideSourceMaps: true, + // Hides source maps from generated client bundles + hideSourceMaps: true, - // Automatically tree-shake Sentry logger statements to reduce bundle size - disableLogger: true, + // Automatically tree-shake Sentry logger statements to reduce bundle size + disableLogger: true, - // Enables automatic instrumentation of Vercel Cron Monitors. - // See the following for more information: - // https://docs.sentry.io/product/crons/ - // https://vercel.com/docs/cron-jobs - automaticVercelMonitors: false, + // Enables automatic instrumentation of Vercel Cron Monitors. + // See the following for more information: + // https://docs.sentry.io/product/crons/ + // https://vercel.com/docs/cron-jobs + automaticVercelMonitors: false, }; const baseNextConfig: NextConfig = { - serverExternalPackages: ["pino-pretty"], - async headers() { - return [ - { - // Apply these headers to all routes in your application. - source: "/(.*)", - headers: [ - ...securityHeaders, - { - key: "accept-ch", - value: "sec-ch-viewport-width", - }, - ], - }, - ]; - }, - async redirects() { - return getRedirects(); - }, - async rewrites() { - return [ - { - source: "/thirdweb.eth", - destination: "/deployer.thirdweb.eth", - }, - { - source: "/thirdweb.eth/:path*", - destination: "/deployer.thirdweb.eth/:path*", - }, - // re-write /home to / (this is so that logged in users will be able to go to /home and NOT be redirected to the logged in app) - { - source: "/home", - destination: "/", - }, - ...FRAMER_PATHS.map((path) => ({ - source: path, - destination: `https://landing.thirdweb.com${path}`, - })), - ]; - }, - images: { - dangerouslyAllowSVG: true, - contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;", - remotePatterns: [ - { - protocol: "https", - hostname: "**.thirdweb.com", - }, - ...determineIpfsGateways(), - ], - }, - compiler: { - emotion: true, - }, - reactStrictMode: true, + serverExternalPackages: ["pino-pretty"], + async headers() { + return [ + { + // Apply these headers to all routes in your application. + source: "/(.*)", + headers: [ + ...securityHeaders, + { + key: "accept-ch", + value: "sec-ch-viewport-width", + }, + ], + }, + ]; + }, + async redirects() { + return getRedirects(); + }, + async rewrites() { + return [ + { + source: "/thirdweb.eth", + destination: "/deployer.thirdweb.eth", + }, + { + source: "/thirdweb.eth/:path*", + destination: "/deployer.thirdweb.eth/:path*", + }, + // re-write /home to / (this is so that logged in users will be able to go to /home and NOT be redirected to the logged in app) + { + source: "/home", + destination: "/", + }, + ...FRAMER_PATHS.map((path) => ({ + source: path, + destination: `https://landing.thirdweb.com${path}`, + })), + ]; + }, + images: { + dangerouslyAllowSVG: true, + contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;", + remotePatterns: [ + { + protocol: "https", + hostname: "**.thirdweb.com", + }, + ...determineIpfsGateways(), + ], + }, + compiler: { + emotion: true, + }, + reactStrictMode: true, }; function getConfig(): NextConfig { - if (process.env.NODE_ENV === "production") { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const withBundleAnalyzer = require("@next/bundle-analyzer")({ - enabled: process.env.ANALYZE === "true", - }); - // eslint-disable-next-line @typescript-eslint/no-var-requires - const { withPlausibleProxy } = require("next-plausible"); - // eslint-disable-next-line @typescript-eslint/no-var-requires - const { withSentryConfig } = require("@sentry/nextjs"); - return withBundleAnalyzer( - withPlausibleProxy({ - customDomain: "https://pl.thirdweb.com", - scriptName: "pl", - })( - withSentryConfig( - { - ...baseNextConfig, - experimental: { - webpackBuildWorker: true, - }, - // @ts-expect-error - this is a valid option - webpack: (config, { dev }) => { - if (config.cache && !dev) { - config.cache = Object.freeze({ - type: "filesystem", - maxMemoryGenerations: 0, - }); - } - config.externals.push("pino-pretty"); - config.module = { - ...config.module, - exprContextCritical: false, - }; - // Important: return the modified config - return config; - }, - }, - SENTRY_OPTIONS, - ), - ), - ); - } - // otherwise return the base - return baseNextConfig; + if (process.env.NODE_ENV === "production") { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const withBundleAnalyzer = require("@next/bundle-analyzer")({ + enabled: process.env.ANALYZE === "true", + }); + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { withPlausibleProxy } = require("next-plausible"); + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { withSentryConfig } = require("@sentry/nextjs"); + return withBundleAnalyzer( + withPlausibleProxy({ + customDomain: "https://pl.thirdweb.com", + scriptName: "pl", + })( + withSentryConfig( + { + ...baseNextConfig, + experimental: { + webpackBuildWorker: true, + webpackMemoryOptimizations: true, + }, + // @ts-expect-error - this is a valid option + webpack: (config) => { + if (config.cache) { + config.cache = Object.freeze({ + type: "memory", + }); + } + config.externals.push("pino-pretty"); + config.module = { + ...config.module, + exprContextCritical: false, + }; + // Important: return the modified config + return config; + }, + }, + SENTRY_OPTIONS, + ), + ), + ); + } + // otherwise return the base + return baseNextConfig; } export default getConfig(); diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index b85cbfcf759..327f18abc0a 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -34,7 +34,7 @@ "@radix-ui/react-alert-dialog": "^1.1.4", "@radix-ui/react-avatar": "^1.1.2", "@radix-ui/react-checkbox": "^1.1.3", - "@radix-ui/react-dialog": "1.1.3", + "@radix-ui/react-dialog": "1.1.4", "@radix-ui/react-dropdown-menu": "^2.1.3", "@radix-ui/react-hover-card": "^1.1.3", "@radix-ui/react-label": "^2.1.1", @@ -48,7 +48,7 @@ "@radix-ui/react-tooltip": "1.1.5", "@sentry/nextjs": "8.45.1", "@shazow/whatsabi": "^0.18.0", - "@tanstack/react-query": "5.62.7", + "@tanstack/react-query": "5.62.16", "@tanstack/react-table": "^8.20.6", "@thirdweb-dev/service-utils": "workspace:*", "@vercel/functions": "^1.5.2", @@ -106,7 +106,7 @@ "@chakra-ui/cli": "^2.4.1", "@chromatic-com/storybook": "3.2.2", "@next/bundle-analyzer": "15.1.0", - "@next/eslint-plugin-next": "15.1.0", + "@next/eslint-plugin-next": "15.1.3", "@playwright/test": "1.49.1", "@storybook/addon-essentials": "8.4.7", "@storybook/addon-interactions": "8.4.7", diff --git a/apps/dashboard/redirects.js b/apps/dashboard/redirects.js index 70a8f256ae6..cbe518a0d6a 100644 --- a/apps/dashboard/redirects.js +++ b/apps/dashboard/redirects.js @@ -331,6 +331,12 @@ async function redirects() { destination: "/templates/:slug", permanent: false, }, + // PREVIOUS CAMPAIGNS + { + source: "/unlimited-wallets", + destination: "/", + permanent: false, + }, ...legacyDashboardToTeamRedirects, ]; } diff --git a/apps/dashboard/src/@/api/team.ts b/apps/dashboard/src/@/api/team.ts index 6569df0fa2c..5baa85542e4 100644 --- a/apps/dashboard/src/@/api/team.ts +++ b/apps/dashboard/src/@/api/team.ts @@ -64,7 +64,7 @@ export async function getTeams() { return null; } -type TeamNebulWaitList = { +type TeamNebulaWaitList = { onWaitlist: boolean; createdAt: null | string; }; @@ -86,7 +86,7 @@ export async function getTeamNebulaWaitList(teamSlug: string) { ); if (res.ok) { - return (await res.json()).result as TeamNebulWaitList; + return (await res.json()).result as TeamNebulaWaitList; } return null; diff --git a/apps/dashboard/src/@/components/ui/button.stories.tsx b/apps/dashboard/src/@/components/ui/button.stories.tsx index 1bd5675e2e7..6b23ebffa6d 100644 --- a/apps/dashboard/src/@/components/ui/button.stories.tsx +++ b/apps/dashboard/src/@/components/ui/button.stories.tsx @@ -20,7 +20,7 @@ export const AllVariants: Story = { function Component() { return ( -
+
@@ -58,7 +58,7 @@ function Variants(props: { {props.size === "icon" ? ( ) : ( - "Desctructive" + "Destructive" )}
diff --git a/apps/dashboard/src/@/components/ui/button.tsx b/apps/dashboard/src/@/components/ui/button.tsx index d533e1b22ba..f2d72c6c9b8 100644 --- a/apps/dashboard/src/@/components/ui/button.tsx +++ b/apps/dashboard/src/@/components/ui/button.tsx @@ -15,7 +15,7 @@ const buttonVariants = cva( destructive: "bg-destructive hover:bg-destructive/90 text-semibold text-destructive-foreground ", outline: - "border border-input bg-transaprent hover:bg-accent hover:text-accent-foreground text-semibold", + "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground text-semibold", secondary: "bg-secondary hover:bg-secondary/80 text-semibold text-secondary-foreground ", ghost: "hover:bg-accent text-semibold hover:text-accent-foreground", diff --git a/apps/dashboard/src/@/components/ui/select.stories.tsx b/apps/dashboard/src/@/components/ui/select.stories.tsx index f8951fa19d9..957c3586df9 100644 --- a/apps/dashboard/src/@/components/ui/select.stories.tsx +++ b/apps/dashboard/src/@/components/ui/select.stories.tsx @@ -31,7 +31,7 @@ function randomName() { function Component() { return ( -
+
diff --git a/apps/dashboard/src/@/constants/thirdweb.client.ts b/apps/dashboard/src/@/constants/thirdweb.client.ts index 3f1639657d6..60e982e2159 100644 --- a/apps/dashboard/src/@/constants/thirdweb.client.ts +++ b/apps/dashboard/src/@/constants/thirdweb.client.ts @@ -32,7 +32,7 @@ export function useThirdwebClient(jwt?: string) { }); return useMemo( - // prfer jwt from props over the one from the token query if it exists + // prefer jwt from props over the one from the token query if it exists () => getThirdwebClient(jwt || query.data), [jwt, query.data], ); diff --git a/apps/dashboard/src/@/lib/DashboardRouter.tsx b/apps/dashboard/src/@/lib/DashboardRouter.tsx index 531de6dd3fd..ac1fc432666 100644 --- a/apps/dashboard/src/@/lib/DashboardRouter.tsx +++ b/apps/dashboard/src/@/lib/DashboardRouter.tsx @@ -100,7 +100,7 @@ function DashboardRouterTopProgressBarInner() { } async function startEffect() { - // if the loading state remains for atleast 500ms start the progress bar + // if the loading state remains for at least 500ms start the progress bar await wait(500); if (isMounted) { updateProgressBar(0, 100); diff --git a/apps/dashboard/src/@/styles/globals.css b/apps/dashboard/src/@/styles/globals.css index 45302ce3ef9..d966cd7bccc 100644 --- a/apps/dashboard/src/@/styles/globals.css +++ b/apps/dashboard/src/@/styles/globals.css @@ -143,7 +143,7 @@ body { min-height: 100%; } -/* Fix colors on autofilled inputs */ +/* Fix colors on auto-filled inputs */ input:-webkit-autofill, input:-webkit-autofill:hover, input:-webkit-autofill:focus, diff --git a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useActivity.ts b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useActivity.ts index 5985f820340..13b04fe4570 100644 --- a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useActivity.ts +++ b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useActivity.ts @@ -22,7 +22,7 @@ export interface InternalTransaction { export function useActivity(contract: ThirdwebContract, autoUpdate?: boolean) { const abiQuery = useResolveContractAbi(contract); - // Get all the PreprareEvents from the contract abis + // Get all the Prepare Events from the contract abis const events: PreparedEvent[] = useMemo(() => { const eventsItems = (abiQuery.data || []).filter((o) => o.type === "event"); const eventSignatures = eventsItems.map((event) => formatAbiItem(event)); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/add-chain-to-wallet.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/add-chain-to-wallet.tsx index 8c78782e31c..d75886fd248 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/add-chain-to-wallet.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/client/add-chain-to-wallet.tsx @@ -47,7 +47,7 @@ export const AddChainToWallet: React.FC = (props) => { className="w-full gap-2" variant="outline" onClick={() => { - // Connct directly to this chain + // Connect directly to this chain if (!account) { return customConnectModal({ chain: props.chain }); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/FaucetSection.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/FaucetSection.tsx index 1e814a7d5be..3ae3cb9c187 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/FaucetSection.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/FaucetSection.tsx @@ -12,7 +12,7 @@ export async function FaucetSection(props: { }) { const { chain, twAccount } = props; - // Check eligibilty. + // Check eligibility. const sanitizedChainName = chain.name.replace("Mainnet", "").trim(); const amountToGive = getFaucetClaimAmount(props.chain.chainId); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx index 0d9b963f380..0f9ea2d37ff 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx @@ -162,7 +162,7 @@ export default async function ChainPageLayout(props: { {/* Gas Sponsored badge - Mobile */} {chainMetadata?.gasSponsored && (
- +
)} @@ -182,7 +182,7 @@ export default async function ChainPageLayout(props: { {/* Gas Sponsored badge - Desktop */} {chainMetadata?.gasSponsored && (
- +
)}
@@ -221,7 +221,7 @@ export default async function ChainPageLayout(props: { ); } -function GasSponseredBadge() { +function GasSponsoredBadge() { return (
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-button.tsx index b05e7f9932c..d30ecc2e13d 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-button.tsx @@ -13,7 +13,7 @@ import { ListerOnly } from "@3rdweb-sdk/react/components/roles/lister-only"; import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { isAlchemySupported } from "lib/wallet/nfts/alchemy"; import { isMoralisSupported } from "lib/wallet/nfts/moralis"; -import { isSimpleHashSupported } from "lib/wallet/nfts/simpleHash"; +import { useSimplehashSupport } from "lib/wallet/nfts/simpleHash"; import { PlusIcon } from "lucide-react"; import { useState } from "react"; import type { ThirdwebContract } from "thirdweb"; @@ -40,9 +40,12 @@ export const CreateListingButton: React.FC = ({ const [open, setOpen] = useState(false); const [listingMode, setListingMode] = useState<(typeof LISTING_MODES)[number]>("Select NFT"); + + const simplehashQuery = useSimplehashSupport(contract.chain.id); + const isSupportedChain = contract.chain.id && - (isSimpleHashSupported(contract.chain.id) || + (simplehashQuery.data || isAlchemySupported(contract.chain.id) || isMoralisSupported(contract.chain.id)); return ( diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx index b864b976426..67c6d02e134 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx @@ -21,7 +21,7 @@ import { useAllChainsData } from "hooks/chains/allChains"; import { useTxNotifications } from "hooks/useTxNotifications"; import { isAlchemySupported } from "lib/wallet/nfts/alchemy"; import { isMoralisSupported } from "lib/wallet/nfts/moralis"; -import { isSimpleHashSupported } from "lib/wallet/nfts/simpleHash"; +import { useSimplehashSupport } from "lib/wallet/nfts/simpleHash"; import type { WalletNFT } from "lib/wallet/nfts/types"; import { CircleAlertIcon, InfoIcon } from "lucide-react"; import Link from "next/link"; @@ -112,10 +112,10 @@ export const CreateListingsForm: React.FC = ({ const { idToChain } = useAllChainsData(); const network = idToChain.get(chainId); const [isFormLoading, setIsFormLoading] = useState(false); - + const simplehashQuery = useSimplehashSupport(contract.chain.id); const isSupportedChain = chainId && - (isSimpleHashSupported(chainId) || + (!!simplehashQuery.data || isAlchemySupported(chainId) || isMoralisSupported(chainId)); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-table.tsx index 1057e113e3b..8273209efef 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-table.tsx @@ -51,7 +51,7 @@ export const AccountsTable: React.FC = ({ contract }) => { const totalAccountsQuery = useReadContract(totalAccounts, { contract }); // the total size should never be more than max int size (that would be hella wallets!) - // so converting the totalAccounts to a nunber should be safe here + // so converting the totalAccounts to a number should be safe here const totalAccountsNum = useMemo( () => Number(totalAccountsQuery.data || 0), [totalAccountsQuery.data], diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx index 801e6d704c5..2ffdc9eca1e 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx @@ -1,9 +1,8 @@ "use client"; import { Button } from "@/components/ui/button"; -import { Card } from "@/components/ui/card"; +import { ToolTipLabel } from "@/components/ui/tooltip"; import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; -import { Tooltip } from "@chakra-ui/react"; import { TransactionButton } from "components/buttons/TransactionButton"; import type { ThirdwebContract } from "thirdweb"; import * as ERC4337Ext from "thirdweb/extensions/erc4337"; @@ -53,23 +52,11 @@ export const CreateAccountButton: React.FC = ({ if (isAccountDeployedQuery.data && accountsForAddressQuery.data?.length) { return ( - -

You can only initialize one account per EOA.

- - } - bg="transparent" - boxShadow="none" - bgColor="backgroundHighlight" - borderRadius="lg" - placement="right" - shouldWrapChildren - > + -
+ ); } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx index b5c9c702652..97c9ee710b3 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx @@ -52,7 +52,7 @@ export default async function Layout(props: { // check if the contract exists const isValidContract = await isContractDeployed(contract).catch(() => false); if (!isValidContract) { - // TODO - replace 404 with a better page to upsale deploy or other thirdweb products + // TODO - replace 404 with a better page to upsell deploy or other thirdweb products notFound(); } const contractPageMetadata = await getContractPageMetadata(contract); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/nft/AdvancedNFTMetadataFormGroup.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/nft/AdvancedNFTMetadataFormGroup.tsx index 613e0937bb8..0550fa2e15d 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/nft/AdvancedNFTMetadataFormGroup.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/nft/AdvancedNFTMetadataFormGroup.tsx @@ -82,7 +82,7 @@ export function AdvancedNFTMetadataFormGroup< - If you already have your NFT Animation URL preuploaded, you can + If you already have your NFT Animation URL pre-uploaded, you can set the URL or URI here. diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx index 35e9ce91517..6e37d7e5249 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/claim-tab.tsx @@ -59,7 +59,7 @@ const ClaimTabERC1155: React.FC = ({ if (approveTx) { const approvalPromise = sendAndConfirmTx.mutateAsync(approveTx); toast.promise(approvalPromise, { - success: "Approved succesfully", + success: "Approved successfully", error: "Failed to approve ERC20", }); await approvalPromise; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx index 78a305d8823..29fa3524010 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/update-metadata-form.tsx @@ -361,7 +361,7 @@ export const UpdateNftMetadata: React.FC = ({ Image URL - If you already have your NFT image preuploaded, you can set the + If you already have your NFT image pre-uploaded, you can set the URL or URI here. @@ -372,7 +372,7 @@ export const UpdateNftMetadata: React.FC = ({ Animation URL - If you already have your NFT Animation URL preuploaded, you can + If you already have your NFT Animation URL pre-uploaded, you can set the URL or URI here. diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx index 507bd9f016d..b6d2f82cac7 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/lazy-mint-form.tsx @@ -294,7 +294,7 @@ export const LazyMintNftForm: React.FC = ({ Image URL - If you already have your NFT image preuploaded, you can set + If you already have your NFT image pre-uploaded, you can set the URL or URI here. @@ -305,7 +305,7 @@ export const LazyMintNftForm: React.FC = ({ Animation URL - If you already have your NFT Animation URL preuploaded, you + If you already have your NFT Animation URL pre-uploaded, you can set the URL or URI here. diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx index d252aeba2d3..6eb3bd6f01b 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/mint-form.tsx @@ -317,7 +317,7 @@ export const NFTMintForm: React.FC = ({ Image URL - If you already have your NFT image preuploaded, you can set + If you already have your NFT image pre-uploaded, you can set the URL or URI here. @@ -328,7 +328,7 @@ export const NFTMintForm: React.FC = ({ Animation URL - If you already have your NFT Animation URL preuploaded, you + If you already have your NFT Animation URL pre-uploaded, you can set the URL or URI here. diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx index 4b9cd98b351..92f07a607cc 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/reveal-button.tsx @@ -70,7 +70,7 @@ export const NFTRevealButton: React.FC = ({ ); } - return batchesQuery.data?.length ? ( + return ( @@ -168,5 +168,5 @@ export const NFTRevealButton: React.FC = ({ - ) : null; + ); }; diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx index b2681a97eb7..0c434351e18 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/shared-metadata-form.tsx @@ -247,7 +247,7 @@ export const SharedMetadataForm: React.FC<{ Image URL - If you already have your NFT image preuploaded, you can set + If you already have your NFT image pre-uploaded, you can set the URL or URI here. @@ -258,7 +258,7 @@ export const SharedMetadataForm: React.FC<{ Animation URL - If you already have your NFT Animation URL preuploaded, you + If you already have your NFT Animation URL pre-uploaded, you can set the URL or URI here. diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/delegate-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/delegate-button.tsx index 8df04974319..f17a45f33df 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/delegate-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/proposals/components/delegate-button.tsx @@ -30,7 +30,7 @@ export const DelegateButton: React.FC = ({ enabled: !!account, }, }); - const delgateMutation = useDelegateMutation(); + const delegateMutation = useDelegateMutation(); if (tokensDelegatedQuery.data || tokensDelegatedQuery.isPending) { return null; @@ -44,7 +44,7 @@ export const DelegateButton: React.FC = ({ transactionCount={1} onClick={() => { toast.promise( - delgateMutation.mutateAsync(contract, { + delegateMutation.mutateAsync(contract, { onSuccess: () => { trackEvent({ category: "vote", @@ -68,7 +68,7 @@ export const DelegateButton: React.FC = ({ }, ); }} - isPending={delgateMutation.isPending} + isPending={delegateMutation.isPending} > Delegate Tokens diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/sources/ContractSourcesPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/sources/ContractSourcesPage.tsx index e2770c6e44e..4db8aad7ca5 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/sources/ContractSourcesPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/sources/ContractSourcesPage.tsx @@ -72,7 +72,7 @@ interface ConnectorModalProps { const VerifyContractModal: React.FC< ConnectorModalProps & { resetSignal: number } > = ({ isOpen, onClose, contract, resetSignal }) => { - const veryifyQuery = useQuery({ + const verifyQuery = useQuery({ queryKey: [ "verify-contract", contract.chain.id, @@ -101,23 +101,23 @@ const VerifyContractModal: React.FC< - {veryifyQuery.isPending && ( + {verifyQuery.isPending && ( Verifying... )} - {veryifyQuery?.error ? ( + {verifyQuery?.error ? ( - {veryifyQuery?.error.toString()} + {verifyQuery?.error.toString()} ) : null} - {veryifyQuery.data?.results - ? veryifyQuery.data?.results.map( + {verifyQuery.data?.results + ? verifyQuery.data?.results.map( (result: VerificationResult, index: number) => ( // biome-ignore lint/suspicious/noArrayIndexKey: FIXME diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.tsx index 3e1e8f66d11..99bb0ed76e9 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/split/ContractSplitPage.tsx @@ -110,7 +110,7 @@ export const ContractSplitPage: React.FC = ({ = ({ contract, balances, - balancesisPending, + balancesIsPending, balancesIsError, twAccount, ...restButtonProps @@ -37,13 +37,13 @@ export const DistributeButton: React.FC = ({ ) { return 1; } - if (!validBalances || balancesisPending) { + if (!validBalances || balancesIsPending) { return 0; } return validBalances?.filter( (b) => b.display_balance !== "0.0" && b.display_balance !== "0", ).length; - }, [validBalances, balancesisPending]); + }, [validBalances, balancesIsPending]); const mutation = useSplitDistributeFunds(contract); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/transfer-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/transfer-button.tsx index dd9f2c2b41a..ed3d3310a10 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/transfer-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/tokens/components/transfer-button.tsx @@ -140,8 +140,8 @@ export const TokenTransferButton: React.FC = ({ }, }); toast.promise(promise, { - loading: "Transfering tokens", - success: "Successfully transfered tokens", + loading: "Transferring tokens", + success: "Successfully transferred tokens", error: "Failed to transfer tokens", }); })} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/chainlist/components/client/filters.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/chainlist/components/client/filters.tsx index 919e7a4567a..685671ade6f 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/chainlist/components/client/filters.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/chainlist/components/client/filters.tsx @@ -162,7 +162,7 @@ export const ChainTypeFilter: React.FC = ({
- +
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/chainlist/components/client/view.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/chainlist/components/client/view.tsx index 7ee28d54227..e1f1677c01b 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/chainlist/components/client/view.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/chainlist/components/client/view.tsx @@ -6,11 +6,11 @@ import { Grid2X2Icon, ListIcon } from "lucide-react"; import { usePathname, useSearchParams } from "next/navigation"; import { useCallback } from "react"; -type hainListViewProps = { +type ChainListViewProps = { activeView: "grid" | "table"; }; -export const ChainListView: React.FC = ({ activeView }) => { +export const ChainListView: React.FC = ({ activeView }) => { const pathname = usePathname(); const searchParams = useSearchParams(); const router = useDashboardRouter(); 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 da711ad5814..783c15d7e65 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 @@ -76,7 +76,7 @@ export default async function PublishedContractPage( > diff --git a/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/page.tsx b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/page.tsx index 5286594dd09..633acaf2568 100644 --- a/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/published-contract/[publisher]/[contract_id]/page.tsx @@ -45,7 +45,7 @@ export default async function PublishedContractPage( > diff --git a/apps/dashboard/src/app/(dashboard)/published-contract/components/contract-actions-deploy.client.tsx b/apps/dashboard/src/app/(dashboard)/published-contract/components/contract-actions-deploy.client.tsx index 3d073b2b031..e940cc00f0a 100644 --- a/apps/dashboard/src/app/(dashboard)/published-contract/components/contract-actions-deploy.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/published-contract/components/contract-actions-deploy.client.tsx @@ -9,7 +9,7 @@ export function DeployActions(props: { publisher: string; contract_id: string; version?: string; - dispayName: string; + displayName: string; }) { const searchparams = useSearchParams(); @@ -30,7 +30,7 @@ export function DeployActions(props: { variant="outline" onClick={() => { shareLink({ - title: `Deploy ${props.dispayName}`, + title: `Deploy ${props.displayName}`, }); }} > diff --git a/apps/dashboard/src/app/(dashboard)/published-contract/components/contract-actions-published.client.tsx b/apps/dashboard/src/app/(dashboard)/published-contract/components/contract-actions-published.client.tsx index 13efeebc2a6..ca849ecad10 100644 --- a/apps/dashboard/src/app/(dashboard)/published-contract/components/contract-actions-published.client.tsx +++ b/apps/dashboard/src/app/(dashboard)/published-contract/components/contract-actions-published.client.tsx @@ -10,7 +10,7 @@ export function PublishedActions(props: { publisher: string; contract_id: string; version?: string; - dispayName: string; + displayName: string; }) { const searchparams = useSearchParams(); const stringifiedSearchParams = searchparams?.toString(); @@ -21,7 +21,7 @@ export function PublishedActions(props: { variant="outline" onClick={() => { shareLink({ - title: `Deploy ${props.dispayName}`, + title: `Deploy ${props.displayName}`, }); }} > diff --git a/apps/dashboard/src/app/(dashboard)/published-contract/components/publish-based-deploy.tsx b/apps/dashboard/src/app/(dashboard)/published-contract/components/publish-based-deploy.tsx index 67095f101f2..f3f119a7a36 100644 --- a/apps/dashboard/src/app/(dashboard)/published-contract/components/publish-based-deploy.tsx +++ b/apps/dashboard/src/app/(dashboard)/published-contract/components/publish-based-deploy.tsx @@ -81,7 +81,7 @@ export async function DeployFormForPublishInfo(props: PublishBasedDeployProps) { > diff --git a/apps/dashboard/src/app/(dashboard)/published-contract/components/uri-based-deploy.tsx b/apps/dashboard/src/app/(dashboard)/published-contract/components/uri-based-deploy.tsx index 67fab66cbc1..e68f5e1bdb1 100644 --- a/apps/dashboard/src/app/(dashboard)/published-contract/components/uri-based-deploy.tsx +++ b/apps/dashboard/src/app/(dashboard)/published-contract/components/uri-based-deploy.tsx @@ -30,7 +30,7 @@ export async function DeployFormForUri(props: DeployFormForUriProps) { redirect(`/login?next=${encodeURIComponent(pathname)}`); } - // TODO: remove the `ChakraProviderSetup` wrapper once the form is updated to no longer use chrakra + // TODO: remove the `ChakraProviderSetup` wrapper once the form is updated to no longer use chakra return ( - Discord Comunity + Discord Community diff --git a/apps/dashboard/src/app/account/overview/AccountTeamsUI.tsx b/apps/dashboard/src/app/account/overview/AccountTeamsUI.tsx index 4826fd6e2cd..1e009aa42ec 100644 --- a/apps/dashboard/src/app/account/overview/AccountTeamsUI.tsx +++ b/apps/dashboard/src/app/account/overview/AccountTeamsUI.tsx @@ -145,7 +145,7 @@ function TeamRow(props: { href={`/team/${props.team.slug}/~/settings`} className="w-full p-1 " > - Manange + Manage diff --git a/apps/dashboard/src/app/account/settings/AccountSettingsPageUI.tsx b/apps/dashboard/src/app/account/settings/AccountSettingsPageUI.tsx index 18840ee4661..ce82075e369 100644 --- a/apps/dashboard/src/app/account/settings/AccountSettingsPageUI.tsx +++ b/apps/dashboard/src/app/account/settings/AccountSettingsPageUI.tsx @@ -213,7 +213,7 @@ function DeleteAccountCard() { function AccountEmailFormControl(props: { email: string; - status: "unverified" | "verfication-sent" | "verified"; + status: "unverified" | "verified"; sendEmail: (email: string) => Promise; updateEmailWithOTP: (otp: string) => Promise; }) { diff --git a/apps/dashboard/src/app/account/wallets/LinkWalletUI.tsx b/apps/dashboard/src/app/account/wallets/LinkWalletUI.tsx index 8ef674888e6..de79533a9f1 100644 --- a/apps/dashboard/src/app/account/wallets/LinkWalletUI.tsx +++ b/apps/dashboard/src/app/account/wallets/LinkWalletUI.tsx @@ -9,7 +9,7 @@ import { SearchInput } from "../components/SearchInput"; export function LinkWalletUI(props: { wallets: string[]; }) { - const [searchValue, setSerchValue] = useState(""); + const [searchValue, setSearchValue] = useState(""); const walletsToShow = !searchValue ? props.wallets : props.wallets.filter((v) => { @@ -40,7 +40,7 @@ export function LinkWalletUI(props: {
diff --git a/apps/dashboard/src/app/api/nft/is-simplehash-supported/route.ts b/apps/dashboard/src/app/api/nft/is-simplehash-supported/route.ts new file mode 100644 index 00000000000..e0a3ccb977e --- /dev/null +++ b/apps/dashboard/src/app/api/nft/is-simplehash-supported/route.ts @@ -0,0 +1,34 @@ +import { isSimpleHashSupported } from "lib/wallet/nfts/simpleHash"; +import { NextResponse } from "next/server"; +import type { NextRequest } from "next/server"; + +export const runtime = "edge"; + +export const GET = async (req: NextRequest) => { + const searchParams = req.nextUrl.searchParams; + const chainIdStr = searchParams.get("chainId"); + + if (!chainIdStr) { + return NextResponse.json( + { + error: "Missing chain ID parameter.", + }, + { status: 400 }, + ); + } + + const chainId = Number.parseInt(chainIdStr); + + if (!Number.isInteger(chainId)) { + return NextResponse.json( + { + error: "Invalid chain ID parameter.", + }, + { status: 400 }, + ); + } + + const found = await isSimpleHashSupported(chainId); + + return NextResponse.json({ result: found }); +}; diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/ChatBar.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/ChatBar.tsx index 5f623c2d7c3..df1a08a181f 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/ChatBar.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/ChatBar.tsx @@ -6,7 +6,7 @@ import { cn } from "@/lib/utils"; import { ArrowUpIcon, CircleStopIcon } from "lucide-react"; import { useState } from "react"; -export function Chatbar(props: { +export function ChatBar(props: { sendMessage: (message: string) => void; isChatStreaming: boolean; abortChatStream: () => void; diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx index fbdf9f65cff..5c2bf88d260 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/ChatPageContent.tsx @@ -18,7 +18,7 @@ import { type ContextFilters, promptNebula } from "../api/chat"; import { createSession, updateSession } from "../api/session"; import type { ExecuteConfig, SessionInfo } from "../api/types"; import { newChatPageUrlStore, newSessionsStore } from "../stores"; -import { Chatbar } from "./ChatBar"; +import { ChatBar } from "./ChatBar"; import { type ChatMessage, Chats } from "./Chats"; import ContextFiltersButton from "./ContextFilters"; import { EmptyStateChatPageContent } from "./EmptyStateChatPageContent"; @@ -406,7 +406,7 @@ export function ChatPageContent(props: { />
- { diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/Chatbar.stories.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/Chatbar.stories.tsx index e54cc308b11..100d697486a 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/Chatbar.stories.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/Chatbar.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react"; import { BadgeContainer, mobileViewport } from "../../../../stories/utils"; -import { Chatbar } from "./ChatBar"; +import { ChatBar } from "./ChatBar"; const meta = { title: "Nebula/Chatbar", @@ -30,7 +30,7 @@ function Story() { return (
- {}} isChatStreaming={false} sendMessage={() => {}} @@ -38,7 +38,7 @@ function Story() { - {}} isChatStreaming={true} sendMessage={() => {}} diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/EmptyStateChatPageContent.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/EmptyStateChatPageContent.tsx index 72626890f78..17a26b30a3c 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/EmptyStateChatPageContent.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/EmptyStateChatPageContent.tsx @@ -3,7 +3,7 @@ import { ArrowUpRightIcon } from "lucide-react"; import { Button } from "../../../../@/components/ui/button"; import { NebulaIcon } from "../icons/NebulaIcon"; -import { Chatbar } from "./ChatBar"; +import { ChatBar } from "./ChatBar"; export function EmptyStateChatPageContent(props: { sendMessage: (message: string) => void; @@ -26,7 +26,7 @@ export function EmptyStateChatPageContent(props: {
- { diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/analytics/page.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/analytics/page.tsx index 1ba33619388..217dace6e70 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/analytics/page.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/analytics/page.tsx @@ -257,7 +257,10 @@ function UsersChartCard({ } data={timeSeriesData} aggregateFn={(_data, key) => - timeSeriesData[timeSeriesData.length - 2]?.[key] + // If there is only one data point, use that one, otherwise use the previous + timeSeriesData.filter((d) => (d[key] as number) > 0).length >= 2 + ? timeSeriesData[timeSeriesData.length - 2]?.[key] + : timeSeriesData[timeSeriesData.length - 1]?.[key] } // Get the trend from the last two COMPLETE periods trendFn={(data, key) => diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/constants.ts b/apps/dashboard/src/app/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/constants.ts index 4423781e36e..04dae439fd4 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/constants.ts +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/ecosystem/[slug]/(active)/configuration/constants.ts @@ -20,7 +20,7 @@ export const partnerFormSchema = z.object({ .refine((domains) => domains.every((d) => isDomainRegex.test(d)), { message: "Invalid domain format", // This error message CANNOT be within the array iteration, or the FormMessage won't be able to find it in the form state }) - .transform((s) => s.join(",")), // This is rejoined to return a string (and later split again) since react-hook-form's typings can't hnadle different input vs output types + .transform((s) => s.join(",")), // This is rejoined to return a string (and later split again) since react-hook-form's typings can't handle different input vs output types bundleIds: z .string() .trim() diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/access-tokens/components/add-access-token-button.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/access-tokens/components/add-access-token-button.tsx index e43917a512f..daa5dd3c03a 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/access-tokens/components/add-access-token-button.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/access-tokens/components/add-access-token-button.tsx @@ -1,3 +1,4 @@ +import { Checkbox, CheckboxWithLabel } from "@/components/ui/checkbox"; import { PlainTextCodeBlock } from "@/components/ui/code/plaintext-code"; import { useEngineCreateAccessToken } from "@3rdweb-sdk/react/hooks/useEngine"; import { @@ -14,7 +15,7 @@ import { useTrack } from "hooks/analytics/useTrack"; import { useTxNotifications } from "hooks/useTxNotifications"; import { CirclePlusIcon } from "lucide-react"; import { useState } from "react"; -import { Button, Checkbox, Text } from "tw-components"; +import { Button, Text } from "tw-components"; interface AddAccessTokenButtonProps { instanceUrl: string; @@ -92,12 +93,13 @@ export const AddAccessTokenButton: React.FC = ({ This access token will not be shown again. - setHasStoredToken(e.target.checked)} - > - I have securely stored this access token. - + + setHasStoredToken(!!val)} + /> + I have securely stored this access token. +
diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/access-tokens/components/engine-access-tokens.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/access-tokens/components/engine-access-tokens.tsx index 6231981e340..0a6ec16b40c 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/access-tokens/components/engine-access-tokens.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/access-tokens/components/engine-access-tokens.tsx @@ -150,7 +150,7 @@ const KeypairAuthenticationPanel = ({ return ( <> - Keypair authentication allows your app to geneate short-lived access + Keypair authentication allows your app to generate short-lived access tokens.
They are securely signed by your backend and verified with a public key.{" "} diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/contract-subscriptions/components/add-contract-subscription-button.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/contract-subscriptions/components/add-contract-subscription-button.tsx index 71dfb12033e..b2096041d55 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/contract-subscriptions/components/add-contract-subscription-button.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/contract-subscriptions/components/add-contract-subscription-button.tsx @@ -1,6 +1,7 @@ "use client"; import { SingleNetworkSelector } from "@/components/blocks/NetworkSelectors"; +import { Checkbox, CheckboxWithLabel } from "@/components/ui/checkbox"; import { useThirdwebClient } from "@/constants/thirdweb.client"; import { type AddContractSubscriptionInput, @@ -36,7 +37,6 @@ import { getContract, isAddress } from "thirdweb"; import { Button, Card, - Checkbox, FormErrorMessage, FormHelperText, FormLabel, @@ -327,8 +327,8 @@ const ModalBodyInputData = ({ { - const { checked } = e.target; + onCheckedChange={(val) => { + const checked = !!val; form.setValue("processEventLogs", checked); if (checked) { processEventLogsDisclosure.onOpen(); @@ -380,21 +380,22 @@ const ModalBodyInputData = ({
- { - const { checked } = e.target; - form.setValue("processTransactionReceipts", checked); - if (checked) { - processTransactionReceiptsDisclosure.onOpen(); - } else { - processTransactionReceiptsDisclosure.onClose(); - } - }} - > + + { + const checked = !!val; + form.setValue("processTransactionReceipts", checked); + if (checked) { + processTransactionReceiptsDisclosure.onOpen(); + } else { + processTransactionReceiptsDisclosure.onClose(); + } + }} + /> Transaction Receipts - + {/* Shows all/specific functions if processing transaction receipts */}
diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/contract-subscriptions/components/contract-subscriptions-table.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/contract-subscriptions/components/contract-subscriptions-table.tsx index 69c18a2d571..c711406d35d 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/contract-subscriptions/components/contract-subscriptions-table.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/contract-subscriptions/components/contract-subscriptions-table.tsx @@ -33,7 +33,7 @@ import { useV5DashboardChain } from "lib/v5-adapter"; import { InfoIcon, Trash2Icon } from "lucide-react"; import { useState } from "react"; import { eth_getBlockByNumber, getRpcClient } from "thirdweb"; -import { shortenAddress as shortenAddresThrows } from "thirdweb/utils"; +import { shortenAddress as shortenAddressThrows } from "thirdweb/utils"; import { Button, Card, FormLabel, LinkButton, Text } from "tw-components"; function shortenAddress(address: string) { @@ -42,7 +42,7 @@ function shortenAddress(address: string) { } try { - return shortenAddresThrows(address); + return shortenAddressThrows(address); } catch { return `${address.substring(0, 6)}...${address.substring(address.length - 4)}`; } diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/nebula/page.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/nebula/page.tsx index 37991880188..fa58d1872d0 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/nebula/page.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/nebula/page.tsx @@ -1,4 +1,5 @@ import { getTeamBySlug } from "@/api/team"; +import { redirect } from "next/navigation"; import { loginRedirect } from "../../../../../login/loginRedirect"; import { NebulaWaitListPage } from "../../../[project_slug]/nebula/components/nebula-waitlist-page"; @@ -14,5 +15,12 @@ export default async function Page(props: { loginRedirect(`/team/${params.team_slug}/~/nebula`); } + // if nebula access is already granted, redirect to nebula web app + const hasNebulaAccess = team.enabledScopes.includes("nebula"); + + if (hasNebulaAccess) { + redirect("https://nebula.thirdweb.com"); + } + return ; } diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/projects/TeamProjectsPage.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/projects/TeamProjectsPage.tsx index b7ee115f177..2bed7fbd2d1 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/projects/TeamProjectsPage.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/projects/TeamProjectsPage.tsx @@ -23,7 +23,7 @@ import { ChevronDownIcon, PlusIcon, SearchIcon } from "lucide-react"; import Link from "next/link"; import { useState } from "react"; -type SortyById = "name" | "createdAt"; +type SortById = "name" | "createdAt"; export function TeamProjectsPage(props: { projects: Project[]; @@ -31,7 +31,7 @@ export function TeamProjectsPage(props: { }) { const { projects } = props; const [searchTerm, setSearchTerm] = useState(""); - const [sortBy, setSortBy] = useState("createdAt"); + const [sortBy, setSortBy] = useState("createdAt"); const [isCreateProjectDialogOpen, setIsCreateProjectDialogOpen] = useState(false); const router = useDashboardRouter(); @@ -206,11 +206,11 @@ function AddNewButton(props: { } function SelectBy(props: { - value: SortyById; - onChange: (value: SortyById) => void; + value: SortById; + onChange: (value: SortById) => void; }) { - const values: SortyById[] = ["name", "createdAt"]; - const valueToLabel: Record = { + const values: SortById[] = ["name", "createdAt"]; + const valueToLabel: Record = { name: "Name", createdAt: "Creation Date", }; @@ -219,7 +219,7 @@ function SelectBy(props: { )} diff --git a/apps/dashboard/src/contract-ui/hooks/useContractFunctionComment.ts b/apps/dashboard/src/contract-ui/hooks/useContractFunctionComment.ts new file mode 100644 index 00000000000..f5bb0777fd1 --- /dev/null +++ b/apps/dashboard/src/contract-ui/hooks/useContractFunctionComment.ts @@ -0,0 +1,139 @@ +import { useThirdwebClient } from "@/constants/thirdweb.client"; +import { useQuery } from "@tanstack/react-query"; +import type { ThirdwebContract } from "thirdweb"; +import { getCompilerMetadata } from "thirdweb/contract"; +import { download } from "thirdweb/storage"; + +/** + * Try to extract the description (or comment) about a contract's method from our contract metadata endpoint + * + * An example of a contract that has both userdoc and devdoc: + * https://contract.thirdweb.com/metadata/1/0x303a465B659cBB0ab36eE643eA362c509EEb5213 + */ +export function useContractFunctionComment( + contract: ThirdwebContract, + functionName: string, +) { + const client = useThirdwebClient(); + return useQuery({ + queryKey: [ + "contract-function-comment", + contract?.chain.id || "", + contract?.address || "", + functionName, + ], + queryFn: async (): Promise => { + const data = await getCompilerMetadata(contract); + let comment = ""; + /** + * If the response data contains userdoc and/or devdoc + * we always prioritize using them. parsing the comment using regex should + * always be the last resort + */ + if (data.metadata.output.devdoc?.methods) { + const keys = Object.keys(data.metadata.output.devdoc.methods); + const matchingKey = keys.find( + (rawKey) => + rawKey.startsWith(functionName) && + rawKey.split("(")[0] === functionName, + ); + const devDocContent = matchingKey + ? data.metadata.output.devdoc.methods[matchingKey]?.details + : undefined; + if (devDocContent) { + comment += `@dev-doc: ${devDocContent}\n`; + } + } + if (data.metadata.output.userdoc?.methods) { + const keys = Object.keys(data.metadata.output.userdoc.methods); + const matchingKey = keys.find( + (rawKey) => + rawKey.startsWith(functionName) && + rawKey.split("(")[0] === functionName, + ); + const userDocContent = matchingKey + ? data.metadata.output.userdoc.methods[matchingKey]?.notice + : undefined; + if (userDocContent) { + comment += `@user-doc: ${userDocContent}\n`; + } + } + if (comment) { + return comment; + } + if (!data.metadata.sources) { + return ""; + } + const sources = await Promise.all( + Object.entries(data.metadata.sources).map(async ([path, info]) => { + if ("content" in info) { + return { + filename: path, + source: info.content || "Could not find source for this file", + }; + } + const urls = info.urls; + const ipfsLink = urls + ? urls.find((url) => url.includes("ipfs")) + : undefined; + if (ipfsLink) { + const ipfsHash = ipfsLink.split("ipfs/")[1]; + const source = await download({ + uri: `ipfs://${ipfsHash}`, + client, + }) + .then((r) => r.text()) + .catch(() => "Failed to fetch source from IPFS"); + return { + filename: path, + source, + }; + } + return { + filename: path, + source: "Could not find source for this file", + }; + }), + ); + const file = sources.find((item) => item.source.includes(functionName)); + if (!file) { + return ""; + } + return extractFunctionComment(file.source, functionName); + }, + }); +} + +function extractFunctionComment( + // The whole code from the solidity file containing (possibly) the function + solidityCode: string, + functionName: string, +): string { + // Regular expression to match function declarations and their preceding comments + // This regex now captures both single-line (//) and multi-line (/** */) comments + const functionRegex = + /(?:\/\/[^\n]*|\/\*\*[\s\S]*?\*\/)\s*function\s+(\w+)\s*\(/g; + + while (true) { + const match = functionRegex.exec(solidityCode); + if (match === null) { + return ""; + } + const [fullMatch, name] = match; + if (!fullMatch || !fullMatch.length) { + return ""; + } + if (name === functionName) { + // Extract the comment part + const comment = (fullMatch.split("function")[0] || "").trim(); + if (!comment) { + return ""; + } + + if (/^[^a-zA-Z0-9]+$/.test(comment)) { + return ""; + } + return comment; + } + } +} 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 5833967e979..81a65844f96 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 @@ -251,7 +251,7 @@ const chain = defineChain({{chainId}}); // First, connect the personal wallet, which can be any wallet (metamask, in-app, etc.) const personalWallet = inAppWallet(); -const peronalAccount = await personalWallet.connect({ +const personalAccount = await personalWallet.connect({ client, chain, strategy: "google", diff --git a/apps/dashboard/src/core-ui/batch-upload/batch-lazy-mint.tsx b/apps/dashboard/src/core-ui/batch-upload/batch-lazy-mint.tsx index a60a360250b..da5b076f741 100644 --- a/apps/dashboard/src/core-ui/batch-upload/batch-lazy-mint.tsx +++ b/apps/dashboard/src/core-ui/batch-upload/batch-lazy-mint.tsx @@ -1,10 +1,10 @@ "use client"; +import { Checkbox, CheckboxWithLabel } from "@/components/ui/checkbox"; import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import { Alert, AlertIcon, - Box, Flex, FormControl, Input, @@ -24,7 +24,6 @@ import type { CreateDelayedRevealBatchParams } from "thirdweb/extensions/erc721" import type { NFTInput } from "thirdweb/utils"; import { Button, - Checkbox, FormErrorMessage, FormHelperText, FormLabel, @@ -196,7 +195,7 @@ export const BatchLazyMint: ComponentWithChildren< />
- +