Skip to content

Commit 8e22cf8

Browse files
committed
[Ecosystem Portal] Fix: WalletConnect Connection (#5438)
CNCT-2258 <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on enhancing the `wallet-ui` application, introducing multi-tenancy support, updating configurations, and improving code quality with better imports and utility functions. ### Detailed summary - Removed unused files: `erc20.ts`, `Erc20Token.ts` - Added `isMultiTenant` utility for environment checks - Updated `README.md` for ecosystem portal - Enhanced routing with ecosystem ID in redirects - Introduced new configuration files for `knip` and `biome` - Updated ESLint rules and configurations - Refactored image handling to use a new `Image` component - Streamlined various imports across components and hooks > The following files were skipped due to too many changes: `pnpm-lock.yaml` > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 565dbb9 commit 8e22cf8

32 files changed

+400
-327
lines changed

.github/labeler.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ playground:
88
packages:
99
- changed-files:
1010
- any-glob-to-any-file: "packages/**/*"
11+
sdk:
12+
- changed-files:
13+
- any-glob-to-any-file: "packages/thirdweb/**/*"
1114
portal:
1215
- changed-files:
1316
- any-glob-to-any-file: "apps/portal/**/*"
17+
"Ecosystem Portal":
18+
- changed-files:
19+
- any-glob-to-any-file: "apps/wallet-ui/**/*"

apps/wallet-ui/.env.example

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Root domain for the app
2-
NEXT_PUBLIC_ROOT_DOMAIN=ecosystem.localhost:3000
2+
NEXT_PUBLIC_ROOT_DOMAIN=localhost:3000
3+
# If set to production, multi-tenant subdomains will be used
4+
NEXT_PUBLIC_ENVIRONMENT=development
35
# Thirdweb client ID
46
NEXT_PUBLIC_THIRDWEB_CLIENT_ID=
57
# Thirdweb API key

apps/wallet-ui/.eslintrc.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
module.exports = {
2+
extends: [
3+
"eslint:recommended",
4+
"plugin:@typescript-eslint/recommended",
5+
"plugin:@next/next/recommended",
6+
],
7+
rules: {
8+
"react-compiler/react-compiler": "error",
9+
"no-restricted-syntax": [
10+
"error",
11+
{
12+
selector: "CallExpression[callee.name='useEffect']",
13+
message:
14+
'Are you *sure* you need to use "useEffect" here? If you loading any async function prefer using "useQuery".',
15+
},
16+
{
17+
selector: "CallExpression[callee.name='createContext']",
18+
message:
19+
'Are you *sure* you need to use a "Context"? In almost all cases you should prefer passing props directly.',
20+
},
21+
],
22+
"no-restricted-imports": [
23+
"error",
24+
{
25+
paths: [
26+
{
27+
name: "next/navigation",
28+
importNames: ["useRouter"],
29+
message:
30+
'Use `import { useRouter } from "@/lib/useRouter";` instead',
31+
},
32+
],
33+
},
34+
],
35+
},
36+
parser: "@typescript-eslint/parser",
37+
plugins: ["@typescript-eslint", "react-compiler"],
38+
parserOptions: {
39+
ecmaVersion: 2019,
40+
ecmaFeatures: {
41+
impliedStrict: true,
42+
jsx: true,
43+
},
44+
warnOnUnsupportedTypeScriptVersion: true,
45+
},
46+
settings: {
47+
react: {
48+
createClass: "createReactClass",
49+
pragma: "React",
50+
version: "detect",
51+
},
52+
},
53+
overrides: [
54+
// enable rule specifically for TypeScript files
55+
{
56+
files: ["*.ts", "*.tsx"],
57+
rules: {
58+
"@typescript-eslint/explicit-module-boundary-types": ["off"],
59+
},
60+
},
61+
// THIS NEEDS TO GO LAST!
62+
{
63+
files: ["*.ts", "*.js", "*.tsx", "*.jsx"],
64+
extends: ["biome"],
65+
},
66+
],
67+
env: {
68+
browser: true,
69+
node: true,
70+
},
71+
};

apps/wallet-ui/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
# thirdweb Wallet UI
1+
# thirdweb Ecosystem Portal

apps/wallet-ui/biome.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"$schema": "https://biomejs.dev/schemas/1.9.2/schema.json",
3+
"extends": ["../../biome.json"],
4+
"overrides": [
5+
{
6+
"include": ["src/css/swagger-ui.css"],
7+
"linter": {
8+
"rules": {
9+
"suspicious": {
10+
"noImportantInKeyframe": "off"
11+
}
12+
}
13+
}
14+
}
15+
]
16+
}

apps/wallet-ui/knip.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"$schema": "https://unpkg.com/knip@latest/schema.json",
3+
"next": true,
4+
"ignore": ["src/components/ui/**"],
5+
"ignoreBinaries": ["biome"],
6+
"ignoreDependencies": ["thirdweb"],
7+
"project": ["src/**"]
8+
}

apps/wallet-ui/package.json

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
"dev": "next dev --turbopack",
77
"build": "next build",
88
"start": "next start",
9-
"lint": "biome check ./src",
10-
"fix": "biome check ./src --fix"
9+
"lint": "biome check ./src && knip && eslint ./src",
10+
"fix": "biome check ./src --fix && knip --fix --allow-remove-files && eslint ./src --fix"
1111
},
1212
"dependencies": {
1313
"@hookform/resolvers": "^3.9.1",
@@ -16,7 +16,6 @@
1616
"@radix-ui/react-label": "^2.1.0",
1717
"@radix-ui/react-popover": "^1.1.2",
1818
"@radix-ui/react-slot": "^1.1.0",
19-
"@radix-ui/react-toast": "^1.2.2",
2019
"@tanstack/react-query": "5.60.2",
2120
"class-variance-authority": "^0.7.0",
2221
"clsx": "^2.1.1",
@@ -27,6 +26,7 @@
2726
"react": "19.0.0-rc-69d4b800-20241021",
2827
"react-dom": "19.0.0-rc-69d4b800-20241021",
2928
"react-hook-form": "7.52.0",
29+
"server-only": "^0.0.1",
3030
"sonner": "^1.7.0",
3131
"tailwind-merge": "^2.5.4",
3232
"tailwindcss-animate": "^1.0.7",
@@ -35,12 +35,18 @@
3535
"zod": "3.23.8"
3636
},
3737
"devDependencies": {
38+
"@next/eslint-plugin-next": "15.0.3",
3839
"@types/node": "20.14.9",
3940
"@types/react": "npm:types-react@19.0.0-rc.1",
4041
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
42+
"@typescript-eslint/eslint-plugin": "7.14.1",
43+
"@typescript-eslint/parser": "7.14.1",
4144
"eslint": "8.57.0",
42-
"eslint-config-next": "15.0.3",
45+
"eslint-config-biome": "1.9.3",
46+
"eslint-plugin-react-compiler": "19.0.0-beta-a7bf2bd-20241110",
47+
"knip": "5.37.0",
4348
"postcss": "8.4.49",
49+
"postcss-load-config": "^6.0.1",
4450
"tailwindcss": "3.4.15",
4551
"typescript": "5.6.3"
4652
}

apps/wallet-ui/postcss.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ const config = {
55
},
66
};
77

8-
export default config;
8+
export default config;

apps/wallet-ui/src/app/[ecosystem]/(authed)/layout.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import ConnectButton from "@/components/ConnectButton";
2+
import { Image } from "@/components/ui/image";
23
import { authedOnly } from "@/lib/auth";
34
import { getEcosystemInfo } from "@/lib/ecosystems";
45
import { resolveScheme } from "thirdweb/storage";
@@ -12,14 +13,14 @@ export default async function Layout(props: {
1213

1314
const { children } = props;
1415

15-
await authedOnly();
16+
await authedOnly(params.ecosystem);
1617
const ecosystem = await getEcosystemInfo(params.ecosystem);
1718
return (
1819
<div className="flex w-full flex-col items-stretch">
1920
<header className="hidden w-full border-accent border-b bg-card py-4 sm:block">
2021
<div className="container mx-auto flex justify-between">
2122
<div className="flex items-center gap-2">
22-
<img
23+
<Image
2324
className="h-8 w-8"
2425
src={resolveScheme({ uri: ecosystem.imageUrl, client })}
2526
alt={ecosystem.name}

apps/wallet-ui/src/app/[ecosystem]/(authed)/wallet/[address]/page.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ export default async function Page(props: {
66
params: Promise<{ address: string }>;
77
searchParams: Promise<{ chainId?: string; uri?: string }>;
88
}) {
9-
const searchParams = await props.searchParams;
9+
const [searchParams, params] = await Promise.all([
10+
props.searchParams,
11+
props.params,
12+
]);
1013

1114
const { chainId, uri } = searchParams;
12-
13-
const params = await props.params;
14-
1515
const { address } = params;
1616

1717
return (

apps/wallet-ui/src/app/[ecosystem]/login/layout.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
import { redirect } from "next/navigation";
2-
import { getCurrentUser } from "../../../lib/auth";
1+
import { getCurrentUser } from "@/lib/auth";
2+
import { redirect } from "@/lib/redirect";
33

44
export default async function Layout({
55
children,
6-
}: { children: React.ReactNode }) {
6+
params,
7+
}: { children: React.ReactNode; params: Promise<{ ecosystem: string }> }) {
8+
const { ecosystem } = await params;
9+
710
const userAddress = await getCurrentUser();
811
if (userAddress) {
9-
redirect(`/wallet/${userAddress}`);
12+
redirect(`/wallet/${userAddress}`, ecosystem);
1013
}
1114

1215
return (
Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { getCurrentUser } from "@/lib/auth";
2-
import { redirect } from "next/navigation";
2+
import { redirect } from "@/lib/redirect";
33

4-
export default async function Page() {
4+
export default async function Page({
5+
params,
6+
}: { params: Promise<{ ecosystem: string }> }) {
7+
const { ecosystem } = await params;
58
const user = await getCurrentUser();
69
if (user) {
7-
redirect("/wallet/${user}");
10+
redirect(`/wallet/${user}`, ecosystem);
811
}
9-
redirect("/login");
12+
redirect("/login", ecosystem);
1013
}
Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
// This page is to accept a Wallet Connect request
22
import { getCurrentUser } from "@/lib/auth";
3-
import { redirect } from "next/navigation";
3+
import { redirect } from "@/lib/redirect";
44

55
export default async function Page(props: {
6+
params: Promise<{ ecosystem: string }>;
67
searchParams: Promise<{ uri: string }>;
78
}) {
8-
const searchParams = await props.searchParams;
9-
9+
const [params, searchParams] = await Promise.all([
10+
props.params,
11+
props.searchParams,
12+
]);
1013
const { uri } = searchParams;
1114

1215
const currentUser = await getCurrentUser();
1316

1417
if (!currentUser) {
15-
redirect(`/login?uri=${encodeURIComponent(uri)}`);
18+
redirect(`/login?uri=${encodeURIComponent(uri)}`, params.ecosystem);
1619
}
1720

18-
redirect(`/wallet/${currentUser}?uri=${encodeURIComponent(uri)}`);
21+
redirect(
22+
`/wallet/${currentUser}?uri=${encodeURIComponent(uri)}`,
23+
params.ecosystem,
24+
);
1925
}

apps/wallet-ui/src/app/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Providers from "@/components/Providers";
2+
import { cn } from "@/lib/utils";
23
import { Inter } from "next/font/google";
34
import "./globals.css";
4-
import { cn } from "../lib/utils";
55

66
const inter = Inter({ subsets: ["latin"] });
77

apps/wallet-ui/src/app/not-found.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/* eslint-disable @next/next/no-img-element */
2+
13
export default function NotFound() {
24
return (
35
<div className="flex h-screen w-screen items-center justify-center">

apps/wallet-ui/src/components/ChainCombobox.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,18 @@ import {
1414
PopoverContent,
1515
PopoverTrigger,
1616
} from "@/components/ui/popover";
17+
import { useRouter } from "@/hooks/useRouter";
1718
import { cn } from "@/lib/utils";
1819
import { ChevronsUpDown } from "lucide-react";
19-
import { usePathname, useRouter, useSearchParams } from "next/navigation";
20+
import { useParams, usePathname, useSearchParams } from "next/navigation";
2021
import React from "react";
2122
import type { ChainMetadata } from "thirdweb/chains";
2223
import { ChainIcon } from "./ChainIcon";
2324

2425
export function ChainCombobox({ chains }: { chains: ChainMetadata[] }) {
2526
const router = useRouter();
2627
const pathname = usePathname();
28+
const params = useParams();
2729
const searchParams = useSearchParams();
2830
const [open, setOpen] = React.useState(false);
2931
const [value, setValue] = React.useState(searchParams.get("chainId") ?? "0");
@@ -43,7 +45,7 @@ export function ChainCombobox({ chains }: { chains: ChainMetadata[] }) {
4345
const search = current.toString();
4446
const query = search ? `?${search}` : "";
4547

46-
router.push(`${pathname}${query}`);
48+
router.push(`${pathname}${query}`, params.ecosystem as string);
4749
};
4850

4951
return (

apps/wallet-ui/src/components/ChainIcon.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"use client";
2+
import { Image } from "@/components/ui/image";
3+
import { client } from "@/lib/client";
24
import { useQuery } from "@tanstack/react-query";
35
import { resolveScheme } from "thirdweb/storage";
4-
import { client } from "../lib/client";
56

67
const fallbackChainIcon =
78
"data:image/svg+xml;charset=UTF-8,%3csvg width='15' height='14' viewBox='0 0 15 14' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M7 8.04238e-07C5.1435 8.04238e-07 3.36301 0.737501 2.05025 2.05025C0.7375 3.36301 0 5.1435 0 7C0 7.225 -1.52737e-07 7.445 0.0349998 7.665C0.16385 9.0151 0.68213 10.2988 1.52686 11.3598C2.37158 12.4209 3.50637 13.2137 4.79326 13.642C6.0801 14.0702 7.4637 14.1153 8.7758 13.7719C10.0879 13.4285 11.2719 12.7113 12.184 11.7075C13.0961 10.7038 13.6969 9.4567 13.9135 8.1178C14.1301 6.7789 13.9531 5.406 13.4039 4.16587C12.8548 2.92574 11.9573 1.87184 10.8204 1.13228C9.6835 0.392721 8.3563 -0.000649196 7 8.04238e-07ZM7 1C8.581 1.00137 10.0975 1.62668 11.22 2.74V3.24C9.2438 2.55991 7.0956 2.56872 5.125 3.265C4.96758 3.1116 4.76997 3.00586 4.555 2.96H4.43C4.37 2.75 4.315 2.54 4.27 2.325C4.225 2.11 4.2 1.92 4.175 1.715C5.043 1.24658 6.0137 1.00091 7 1ZM5.5 3.935C7.3158 3.32693 9.2838 3.34984 11.085 4C10.8414 5.2703 10.3094 6.4677 9.53 7.5C9.312 7.4077 9.0707 7.3855 8.8395 7.4366C8.6083 7.4877 8.3988 7.6094 8.24 7.785C8.065 7.685 7.89 7.585 7.74 7.47C6.7307 6.7966 5.8877 5.9023 5.275 4.855C5.374 4.73221 5.4461 4.58996 5.4866 4.43749C5.5271 4.28502 5.5351 4.12575 5.51 3.97L5.5 3.935ZM3.5 2.135C3.5 2.24 3.53 2.35 3.55 2.455C3.595 2.675 3.655 2.89 3.715 3.105C3.52353 3.21838 3.36943 3.38531 3.2717 3.58522C3.17397 3.78513 3.13688 4.00927 3.165 4.23C2.37575 4.7454 1.67078 5.3795 1.075 6.11C1.19455 5.3189 1.47112 4.55966 1.88843 3.87701C2.30575 3.19437 2.85539 2.60208 3.505 2.135H3.5ZM3.5 9.99C3.30481 10.0555 3.13037 10.1714 2.9943 10.3259C2.85822 10.4804 2.76533 10.6681 2.725 10.87H2.405C1.59754 9.9069 1.1146 8.7136 1.025 7.46L1.08 7.365C1.70611 6.3942 2.52463 5.562 3.485 4.92C3.62899 5.0704 3.81094 5.179 4.01162 5.2345C4.2123 5.2899 4.42423 5.2901 4.625 5.235C5.2938 6.3652 6.208 7.3306 7.3 8.06C7.505 8.195 7.715 8.32 7.925 8.44C7.9082 8.6312 7.9391 8.8237 8.015 9C7.1 9.7266 6.0445 10.256 4.915 10.555C4.78401 10.3103 4.57028 10.1201 4.31199 10.0184C4.05369 9.9167 3.76766 9.9102 3.505 10L3.5 9.99ZM7 12.99C5.9831 12.9903 4.98307 12.7304 4.095 12.235L4.235 12.205C4.43397 12.1397 4.61176 12.0222 4.74984 11.8648C4.88792 11.7074 4.98122 11.5158 5.02 11.31C6.2985 10.984 7.4921 10.3872 8.52 9.56C8.7642 9.7027 9.0525 9.75 9.3295 9.6927C9.6064 9.6355 9.8524 9.4778 10.02 9.25C10.7254 9.4334 11.4511 9.5275 12.18 9.53H12.445C11.9626 10.5673 11.1938 11.4451 10.2291 12.0599C9.2643 12.6747 8.144 13.0009 7 13V12.99ZM10.255 8.54C10.2545 8.3304 10.1975 8.1249 10.09 7.945C10.9221 6.8581 11.5012 5.5991 11.785 4.26C12.035 4.37667 12.2817 4.50667 12.525 4.65C13.0749 5.9495 13.1493 7.4012 12.735 8.75C11.9049 8.8142 11.0698 8.7484 10.26 8.555L10.255 8.54Z' fill='%23646D7A'/%3e%3c/svg%3e";
@@ -32,7 +33,7 @@ export function ChainIcon(props: {
3233
});
3334

3435
return (
35-
<img
36+
<Image
3637
alt=""
3738
width={100}
3839
height={100}

apps/wallet-ui/src/components/ConnectEmbed.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
"use client";
2+
import { useRouter } from "@/hooks/useRouter";
23
import { generatePayload, getCurrentUser, login, logout } from "@/lib/auth";
34
import { client } from "@/lib/client";
5+
import { useQuery } from "@tanstack/react-query";
46
import { useTheme } from "next-themes";
5-
import { useParams, useRouter, useSearchParams } from "next/navigation";
7+
import { useParams, useSearchParams } from "next/navigation";
68
import type { VerifyLoginPayloadParams } from "thirdweb/auth";
79
import { ConnectEmbed as ThirdwebConnectEmbed } from "thirdweb/react";
810
import { ecosystemWallet } from "thirdweb/wallets";
@@ -13,6 +15,18 @@ export function ConnectEmbed() {
1315
const params = useParams();
1416
const searchParams = useSearchParams();
1517

18+
const { data: userAddress } = useQuery({
19+
queryKey: ["userAddress"],
20+
queryFn: getCurrentUser,
21+
});
22+
23+
if (userAddress) {
24+
router.push(
25+
`/wallet/${userAddress}?${searchParams.toString()}`,
26+
params.ecosystem as string,
27+
);
28+
}
29+
1630
return (
1731
<ThirdwebConnectEmbed
1832
theme={theme === "light" ? "light" : "dark"}
@@ -26,6 +40,7 @@ export function ConnectEmbed() {
2640
if (success) {
2741
router.push(
2842
`/wallet/${loginParams.payload.address}?${searchParams.toString()}`,
43+
params.ecosystem as string,
2944
);
3045
}
3146
},

0 commit comments

Comments
 (0)