Compare commits
101
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0c2ac63a9 | ||
|
|
170f14fb54 | ||
|
|
da2a2a0e86 | ||
|
|
e62fd3fe40 | ||
|
|
99a7cce624 | ||
|
|
61152dd098 | ||
|
|
05519fcdde | ||
|
|
7257747520 | ||
|
|
e71e7d70a6 | ||
|
|
8d47864967 | ||
|
|
4a8adc143f | ||
|
|
db538473cf | ||
|
|
2057546934 | ||
|
|
ba52e68eab | ||
|
|
0eff64824b | ||
|
|
ae7407dca4 | ||
|
|
a443b309dc | ||
|
|
ae2ff743c0 | ||
|
|
dfa8603593 | ||
|
|
9f1ca54c26 | ||
|
|
7df81b7323 | ||
|
|
ab74fcdaf8 | ||
|
|
1191d5dad9 | ||
|
|
20b5ba943a | ||
|
|
f0f8da8fab | ||
|
|
da3fc99c0b | ||
|
|
fb4c190eb3 | ||
|
|
1c31a1e4df | ||
|
|
04c10f82fd | ||
|
|
ab173886ac | ||
|
|
df06bec699 | ||
|
|
6000dacfbb | ||
|
|
fc6535069b | ||
|
|
b15c3c0747 | ||
|
|
1985de89aa | ||
|
|
2b87b7bb75 | ||
|
|
d99a985ab4 | ||
|
|
e74574ccf5 | ||
|
|
98d05d6fb6 | ||
|
|
f4ac3c2a3a | ||
|
|
a3a4bf4361 | ||
|
|
e8ec54e6e2 | ||
|
|
58e343c10c | ||
|
|
bad9db9760 | ||
|
|
6a1e54ec69 | ||
|
|
7ddedfbed1 | ||
|
|
98eda9dd3d | ||
|
|
5a13ad4521 | ||
|
|
557a29a960 | ||
|
|
ff565cb0f8 | ||
|
|
816aa6137b | ||
|
|
7de273c59d | ||
|
|
95cc441edd | ||
|
|
6f1d4b1fcf | ||
|
|
7ede361870 | ||
|
|
8b50779229 | ||
|
|
282d9f2c39 | ||
|
|
e739de158d | ||
|
|
451594c80c | ||
|
|
9634e49113 | ||
|
|
a4e12c5516 | ||
|
|
2297915633 | ||
|
|
585ba2f575 | ||
|
|
a68ec74d25 | ||
|
|
7ae3a81a11 | ||
|
|
ccb9cd5d0a | ||
|
|
3dc11c087a | ||
|
|
49479d40b0 | ||
|
|
3d7c2d4f67 | ||
|
|
5b40f76b4d | ||
|
|
ef1550cff5 | ||
|
|
b1719fd4ff | ||
|
|
598d4006d9 | ||
|
|
bc75ae6826 | ||
|
|
8fb8a7530c | ||
|
|
106ca3cd01 | ||
|
|
76a718c8f5 | ||
|
|
1e924e037f | ||
|
|
99d2497b51 | ||
|
|
a5a1d17f3b | ||
|
|
62c179d775 | ||
|
|
e96ebba2cb | ||
|
|
4e6daac6dd | ||
|
|
5f6403bb8d | ||
|
|
4c5fc839d6 | ||
|
|
f6eff13492 | ||
|
|
ebbda37d5a | ||
|
|
c81fed3617 | ||
|
|
ec50f8cc3c | ||
|
|
3315a53653 | ||
|
|
4e42158c7b | ||
|
|
5a069fe657 | ||
|
|
490497c68b | ||
|
|
bdb4c31a6a | ||
|
|
171594d66a | ||
|
|
760b250bc3 | ||
|
|
669a95f983 | ||
|
|
928113bb0b | ||
|
|
c8b8b2faad | ||
|
|
9d40b13299 | ||
|
|
bda0f3edb8 |
@@ -14,7 +14,7 @@ We use [Turborepo](https://turbo.build/repo/docs) to manage the repository, and
|
||||
|
||||
We use [pnpm](https://pnpm.io) for package management across the repo. `pnpm` is similar to `npm` or `yarn` but with more efficient disk space usage.
|
||||
|
||||
**With the v5 SDK, we've consolidated everything into a single project at [/packages/thirdweb](./packages/thirdweb). You can still find the legacy packages at [/legacy_packages](./legacy_packages).**
|
||||
**With the v5 SDK, we've consolidated everything into a single project at [/packages/thirdweb](../packages/thirdweb). You can still find the legacy packages at [/legacy_packages](../legacy_packages).**
|
||||
|
||||
This single package provides a performant & lightweight SDK to interact with any EVM chain across Node, React, and React Native. Learn more about how to use the thirdweb SDK in our [documentation](https://portal.thirdweb.com/typescript/v5).
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
|
||||
uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
|
||||
uses: github/codeql-action/autobuild@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
@@ -72,4 +72,4 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
|
||||
uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
- name: Create release Pull Request or publish to NPM
|
||||
id: changesets
|
||||
uses: changesets/action@06245a4e0a36c064a573d4150030f5ec548e4fcc #v1.4.0
|
||||
uses: changesets/action@746c25e23caa47dceb6a48ee85b4cbc5a9f5f293 #v1.4.0
|
||||
with:
|
||||
publish: pnpm release
|
||||
version: pnpm version-packages
|
||||
|
||||
+2
-1
@@ -30,4 +30,5 @@ packages/*/typedoc/*
|
||||
storybook-static
|
||||
.aider*
|
||||
|
||||
tsconfig.tsbuildinfo
|
||||
tsconfig.tsbuildinfo
|
||||
.cursor
|
||||
Vendored
+6
@@ -9,6 +9,12 @@
|
||||
"typescript.preferences.autoImportFileExcludePatterns": [
|
||||
"./packages/thirdweb/src/exports"
|
||||
],
|
||||
"typescript.preferences.autoImportSpecifierExcludeRegexes": [
|
||||
"@radix-ui",
|
||||
"next/router",
|
||||
"next/dist",
|
||||
"^lucide-react/dist/lucide-react.suffixed$"
|
||||
],
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
|
||||
@@ -104,4 +104,8 @@ ANALYTICS_SERVICE_URL=""
|
||||
NEXT_PUBLIC_NEBULA_URL=""
|
||||
|
||||
# required for billing parts of the dashboard (team -> settings -> billing / invoices)
|
||||
STRIPE_SECRET_KEY=""
|
||||
STRIPE_SECRET_KEY=""
|
||||
|
||||
# required for server wallet management
|
||||
NEXT_PUBLIC_THIRDWEB_VAULT_URL=""
|
||||
NEXT_PUBLIC_ENGINE_CLOUD_URL=""
|
||||
@@ -12,6 +12,7 @@
|
||||
"ignoreDependencies": [
|
||||
"@storybook/blocks",
|
||||
"@thirdweb-dev/service-utils",
|
||||
"@thirdweb-dev/vault-sdk",
|
||||
"@types/color",
|
||||
"fast-xml-parser"
|
||||
]
|
||||
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
declare module "lucide-react" {
|
||||
export * from "lucide-react/dist/lucide-react.suffixed";
|
||||
}
|
||||
@@ -123,6 +123,7 @@ const baseNextConfig: NextConfig = {
|
||||
webpackBuildWorker: true,
|
||||
webpackMemoryOptimizations: true,
|
||||
serverSourceMaps: false,
|
||||
taint: true,
|
||||
},
|
||||
serverExternalPackages: ["pino-pretty"],
|
||||
async headers() {
|
||||
|
||||
@@ -45,11 +45,13 @@
|
||||
"@radix-ui/react-slot": "^1.2.0",
|
||||
"@radix-ui/react-switch": "^1.2.2",
|
||||
"@radix-ui/react-tooltip": "1.2.3",
|
||||
"@scalar/api-reference-react": "^0.6.19",
|
||||
"@sentry/nextjs": "9.13.0",
|
||||
"@shazow/whatsabi": "0.21.0",
|
||||
"@tanstack/react-query": "5.74.4",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"@thirdweb-dev/service-utils": "workspace:*",
|
||||
"@thirdweb-dev/vault-sdk": "workspace:*",
|
||||
"@vercel/functions": "2.0.0",
|
||||
"@vercel/og": "^0.6.8",
|
||||
"abitype": "1.0.8",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
import { API_SERVER_URL } from "../constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "../constants/public-envs";
|
||||
|
||||
export async function acceptInvite(options: {
|
||||
teamId: string;
|
||||
@@ -17,7 +17,7 @@ export async function acceptInvite(options: {
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`${API_SERVER_URL}/v1/teams/${options.teamId}/invites/${options.inviteId}/accept`,
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${options.teamId}/invites/${options.inviteId}/accept`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
import { API_SERVER_URL } from "../constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "../constants/public-envs";
|
||||
|
||||
export async function reSubscribePlan(options: {
|
||||
teamId: string;
|
||||
@@ -14,7 +14,7 @@ export async function reSubscribePlan(options: {
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`${API_SERVER_URL}/v1/teams/${options.teamId}/checkout/resubscribe-plan`,
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${options.teamId}/checkout/resubscribe-plan`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
import { API_SERVER_URL } from "../constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "../constants/public-envs";
|
||||
|
||||
export async function confirmEmailWithOTP(otp: string) {
|
||||
const token = await getAuthToken();
|
||||
@@ -12,16 +12,19 @@ export async function confirmEmailWithOTP(otp: string) {
|
||||
};
|
||||
}
|
||||
|
||||
const res = await fetch(`${API_SERVER_URL}/v1/account/confirmEmail`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
const res = await fetch(
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/account/confirmEmail`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
confirmationToken: otp,
|
||||
}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
confirmationToken: otp,
|
||||
}),
|
||||
});
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
const json = await res.json();
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
import { defineDashboardChain } from "lib/defineDashboardChain";
|
||||
import { ZERO_ADDRESS, isAddress, toTokens } from "thirdweb";
|
||||
import { getWalletBalance } from "thirdweb/wallets";
|
||||
import { getUserThirdwebClient } from "../../app/(app)/api/lib/getAuthToken";
|
||||
import { MORALIS_API_KEY } from "../constants/server-envs";
|
||||
import { serverThirdwebClient } from "../constants/thirdweb-client.server";
|
||||
|
||||
type BalanceQueryResponse = Array<{
|
||||
balance: string;
|
||||
@@ -23,7 +24,6 @@ export async function getTokenBalancesFromMoralis(params: {
|
||||
error: string;
|
||||
}
|
||||
> {
|
||||
const client = await getUserThirdwebClient();
|
||||
const { contractAddress, chainId } = params;
|
||||
|
||||
if (!isAddress(contractAddress)) {
|
||||
@@ -39,7 +39,7 @@ export async function getTokenBalancesFromMoralis(params: {
|
||||
const balance = await getWalletBalance({
|
||||
address: contractAddress,
|
||||
chain,
|
||||
client,
|
||||
client: serverThirdwebClient,
|
||||
});
|
||||
return [
|
||||
{
|
||||
@@ -61,7 +61,7 @@ export async function getTokenBalancesFromMoralis(params: {
|
||||
const resp = await fetch(tokenBalanceEndpoint, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"x-api-key": process.env.MORALIS_API_KEY || "",
|
||||
"x-api-key": MORALIS_API_KEY,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ import type { WalletNFT } from "lib/wallet/nfts/types";
|
||||
import { getVercelEnv } from "../../lib/vercel-utils";
|
||||
import { isAlchemySupported } from "../../lib/wallet/nfts/isAlchemySupported";
|
||||
import { isMoralisSupported } from "../../lib/wallet/nfts/isMoralisSupported";
|
||||
import { DASHBOARD_THIRDWEB_CLIENT_ID } from "../constants/env";
|
||||
import { NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID } from "../constants/public-envs";
|
||||
import { MORALIS_API_KEY } from "../constants/server-envs";
|
||||
|
||||
type WalletNFTApiReturn =
|
||||
| { result: WalletNFT[]; error?: undefined }
|
||||
@@ -59,13 +60,13 @@ export async function getWalletNFTs(params: {
|
||||
}
|
||||
}
|
||||
|
||||
if (isMoralisSupported(chainId) && process.env.MORALIS_API_KEY) {
|
||||
if (isMoralisSupported(chainId) && MORALIS_API_KEY) {
|
||||
const url = generateMoralisUrl({ chainId, owner });
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"X-API-Key": process.env.MORALIS_API_KEY,
|
||||
"X-API-Key": MORALIS_API_KEY,
|
||||
},
|
||||
next: {
|
||||
revalidate: 10, // cache for 10 seconds
|
||||
@@ -148,7 +149,7 @@ async function getWalletNFTsFromInsight(params: {
|
||||
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
"x-client-id": DASHBOARD_THIRDWEB_CLIENT_ID,
|
||||
"x-client-id": NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
"use server";
|
||||
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
import { API_SERVER_URL } from "../constants/env";
|
||||
import {
|
||||
NEXT_PUBLIC_ENGINE_CLOUD_URL,
|
||||
NEXT_PUBLIC_THIRDWEB_API_HOST,
|
||||
} from "../constants/public-envs";
|
||||
import { ANALYTICS_SERVICE_URL } from "../constants/server-envs";
|
||||
|
||||
type ProxyActionParams = {
|
||||
pathname: string;
|
||||
@@ -76,18 +80,13 @@ async function proxy<T>(
|
||||
}
|
||||
|
||||
export async function apiServerProxy<T>(params: ProxyActionParams) {
|
||||
return proxy<T>(API_SERVER_URL, params);
|
||||
return proxy<T>(NEXT_PUBLIC_THIRDWEB_API_HOST, params);
|
||||
}
|
||||
|
||||
export async function payServerProxy<T>(params: ProxyActionParams) {
|
||||
return proxy<T>(
|
||||
process.env.NEXT_PUBLIC_PAY_URL
|
||||
? `https://${process.env.NEXT_PUBLIC_PAY_URL}`
|
||||
: "https://pay.thirdweb-dev.com",
|
||||
params,
|
||||
);
|
||||
export async function engineCloudProxy<T>(params: ProxyActionParams) {
|
||||
return proxy<T>(NEXT_PUBLIC_ENGINE_CLOUD_URL, params);
|
||||
}
|
||||
|
||||
export async function analyticsServerProxy<T>(params: ProxyActionParams) {
|
||||
return proxy<T>(process.env.ANALYTICS_SERVICE_URL || "", params);
|
||||
return proxy<T>(ANALYTICS_SERVICE_URL, params);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use server";
|
||||
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
import { API_SERVER_URL } from "../constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "../constants/public-envs";
|
||||
|
||||
export async function sendTeamInvites(options: {
|
||||
teamId: string;
|
||||
@@ -40,17 +40,20 @@ async function sendInvite(
|
||||
invite: { email: string; role: "OWNER" | "MEMBER" },
|
||||
token: string,
|
||||
) {
|
||||
const res = await fetch(`${API_SERVER_URL}/v1/teams/${teamId}/invites`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
const res = await fetch(
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${teamId}/invites`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
inviteEmail: invite.email,
|
||||
inviteRole: invite.role,
|
||||
}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
inviteEmail: invite.email,
|
||||
inviteRole: invite.role,
|
||||
}),
|
||||
});
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
const errorMessage = await res.text();
|
||||
|
||||
@@ -2,13 +2,12 @@ import "server-only";
|
||||
|
||||
import Stripe from "stripe";
|
||||
import type { Team } from "../api/team";
|
||||
import { STRIPE_SECRET_KEY } from "../constants/server-envs";
|
||||
|
||||
let existingStripe: Stripe | undefined;
|
||||
|
||||
function getStripe() {
|
||||
if (!existingStripe) {
|
||||
const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY;
|
||||
|
||||
if (!STRIPE_SECRET_KEY) {
|
||||
throw new Error("STRIPE_SECRET_KEY is not set");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use server";
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
import { API_SERVER_URL } from "../constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "../constants/public-envs";
|
||||
|
||||
export async function updateAccount(values: {
|
||||
name?: string;
|
||||
@@ -13,7 +13,7 @@ export async function updateAccount(values: {
|
||||
throw new Error("No Auth token");
|
||||
}
|
||||
|
||||
const res = await fetch(`${API_SERVER_URL}/v1/account`, {
|
||||
const res = await fetch(`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/account`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
@@ -6,11 +6,14 @@ import type {
|
||||
InAppWalletStats,
|
||||
RpcMethodStats,
|
||||
TransactionStats,
|
||||
UniversalBridgeStats,
|
||||
UniversalBridgeWalletStats,
|
||||
UserOpStats,
|
||||
WalletStats,
|
||||
WalletUserStats,
|
||||
} from "types/analytics";
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
import { ANALYTICS_SERVICE_URL } from "../constants/server-envs";
|
||||
import { getChains } from "./chain";
|
||||
|
||||
async function fetchAnalytics(
|
||||
@@ -29,34 +32,38 @@ async function fetchAnalytics(
|
||||
}
|
||||
|
||||
// create a new URL object for the analytics server
|
||||
const ANALYTICS_SERVICE_URL = new URL(
|
||||
process.env.ANALYTICS_SERVICE_URL || "https://analytics.thirdweb.com",
|
||||
const analyticsServiceUrl = new URL(
|
||||
ANALYTICS_SERVICE_URL || "https://analytics.thirdweb.com",
|
||||
);
|
||||
ANALYTICS_SERVICE_URL.pathname = pathname;
|
||||
|
||||
analyticsServiceUrl.pathname = pathname;
|
||||
for (const param of searchParams?.split("&") || []) {
|
||||
const [key, value] = param.split("=");
|
||||
if (!key || !value) {
|
||||
throw new Error("Invalid input, no key or value provided");
|
||||
}
|
||||
ANALYTICS_SERVICE_URL.searchParams.append(
|
||||
analyticsServiceUrl.searchParams.append(
|
||||
decodeURIComponent(key),
|
||||
decodeURIComponent(value),
|
||||
);
|
||||
}
|
||||
// client id DEBUG OVERRIDE
|
||||
// ANALYTICS_SERVICE_URL.searchParams.delete("clientId");
|
||||
// ANALYTICS_SERVICE_URL.searchParams.delete("accountId");
|
||||
// ANALYTICS_SERVICE_URL.searchParams.append(
|
||||
// "clientId",
|
||||
// "...",
|
||||
// analyticsServiceUrl.searchParams.delete("projectId");
|
||||
// analyticsServiceUrl.searchParams.delete("teamId");
|
||||
// analyticsServiceUrl.searchParams.append(
|
||||
// "teamId",
|
||||
// "team_cm0lde33r02344w129k5hm2xz",
|
||||
// );
|
||||
// analyticsServiceUrl.searchParams.append(
|
||||
// "projectId",
|
||||
// "prj_cm4rqwx9b002qrnsnr37wqpo6",
|
||||
// );
|
||||
|
||||
return fetch(ANALYTICS_SERVICE_URL, {
|
||||
return fetch(analyticsServiceUrl, {
|
||||
...init,
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
...init?.headers,
|
||||
authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -367,3 +374,60 @@ export async function getEcosystemWalletUsage(args: {
|
||||
|
||||
return json.data as EcosystemWalletStats[];
|
||||
}
|
||||
|
||||
export async function getUniversalBridgeUsage(args: {
|
||||
teamId: string;
|
||||
projectId?: string;
|
||||
from?: Date;
|
||||
to?: Date;
|
||||
period?: "day" | "week" | "month" | "year" | "all";
|
||||
}) {
|
||||
const searchParams = buildSearchParams(args);
|
||||
const res = await fetchAnalytics(`v2/universal?${searchParams.toString()}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (res?.status !== 200) {
|
||||
const reason = await res?.text();
|
||||
console.error(
|
||||
`Failed to fetch universal bridge stats: ${res?.status} - ${res.statusText} - ${reason}`,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
return json.data as UniversalBridgeStats[];
|
||||
}
|
||||
|
||||
export async function getUniversalBridgeWalletUsage(args: {
|
||||
teamId: string;
|
||||
projectId: string;
|
||||
from?: Date;
|
||||
to?: Date;
|
||||
period?: "day" | "week" | "month" | "year" | "all";
|
||||
}) {
|
||||
const searchParams = buildSearchParams(args);
|
||||
const res = await fetchAnalytics(
|
||||
`v2/universal/wallets?${searchParams.toString()}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (res?.status !== 200) {
|
||||
const reason = await res?.text();
|
||||
console.error(
|
||||
`Failed to fetch universal bridge wallet stats: ${res?.status} - ${res.statusText} - ${reason}`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
return json.data as UniversalBridgeWalletStats[];
|
||||
}
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
import "server-only";
|
||||
import type { ChainMetadata } from "thirdweb/chains";
|
||||
import type { ChainService } from "../../app/(app)/(dashboard)/(chain)/types/chain";
|
||||
import { API_SERVER_URL, THIRDWEB_API_SECRET } from "../constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "../constants/public-envs";
|
||||
import { API_SERVER_SECRET } from "../constants/server-envs";
|
||||
|
||||
export async function getGasSponsoredChains() {
|
||||
if (!THIRDWEB_API_SECRET) {
|
||||
if (!API_SERVER_SECRET) {
|
||||
throw new Error("API_SERVER_SECRET is not set");
|
||||
}
|
||||
const res = await fetch(`${API_SERVER_URL}/v1/chains/gas-sponsored`, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-service-api-key": THIRDWEB_API_SECRET,
|
||||
const res = await fetch(
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/chains/gas-sponsored`,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-service-api-key": API_SERVER_SECRET,
|
||||
},
|
||||
next: {
|
||||
revalidate: 15 * 60, //15 minutes
|
||||
},
|
||||
},
|
||||
next: {
|
||||
revalidate: 15 * 60, //15 minutes
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
console.error(
|
||||
@@ -37,7 +41,7 @@ export async function getGasSponsoredChains() {
|
||||
|
||||
export function getChains() {
|
||||
return fetch(
|
||||
`${API_SERVER_URL}/v1/chains`,
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/chains`,
|
||||
// revalidate every 60 minutes
|
||||
{ next: { revalidate: 60 * 60 } },
|
||||
).then((res) => res.json()) as Promise<{ data: ChainMetadata[] }>;
|
||||
@@ -45,7 +49,7 @@ export function getChains() {
|
||||
|
||||
export function getChainServices() {
|
||||
return fetch(
|
||||
`${API_SERVER_URL}/v1/chains/services`,
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/chains/services`,
|
||||
// revalidate every 60 minutes
|
||||
{ next: { revalidate: 60 * 60 } },
|
||||
).then((res) => res.json()) as Promise<{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
import { API_SERVER_URL } from "../constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "../constants/public-envs";
|
||||
|
||||
export type LinkedWallet = {
|
||||
createdAt: string;
|
||||
@@ -14,11 +14,14 @@ export async function getLinkedWallets() {
|
||||
return null;
|
||||
}
|
||||
|
||||
const res = await fetch(`${API_SERVER_URL}/v1/account/wallets`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
const res = await fetch(
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/account/wallets`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
if (res.ok) {
|
||||
const json = (await res.json()) as {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import "server-only";
|
||||
import { API_SERVER_URL } from "@/constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "@/constants/public-envs";
|
||||
import type { ProjectResponse } from "@thirdweb-dev/service-utils";
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
|
||||
@@ -13,7 +13,7 @@ export async function getProjects(teamSlug: string) {
|
||||
}
|
||||
|
||||
const teamsRes = await fetch(
|
||||
`${API_SERVER_URL}/v1/teams/${teamSlug}/projects`,
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${teamSlug}/projects`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
@@ -34,7 +34,7 @@ export async function getProject(teamSlug: string, projectSlug: string) {
|
||||
}
|
||||
|
||||
const teamsRes = await fetch(
|
||||
`${API_SERVER_URL}/v1/teams/${teamSlug}/projects/${projectSlug}`,
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${teamSlug}/projects/${projectSlug}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { API_SERVER_URL, THIRDWEB_API_SECRET } from "../constants/env";
|
||||
import "server-only";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "../constants/public-envs";
|
||||
import { API_SERVER_SECRET } from "../constants/server-envs";
|
||||
|
||||
export type SMSCountryTiers = {
|
||||
tier1: string[];
|
||||
@@ -9,18 +11,21 @@ export type SMSCountryTiers = {
|
||||
};
|
||||
|
||||
export async function getSMSCountryTiers() {
|
||||
if (!THIRDWEB_API_SECRET) {
|
||||
if (!API_SERVER_SECRET) {
|
||||
throw new Error("API_SERVER_SECRET is not set");
|
||||
}
|
||||
const res = await fetch(`${API_SERVER_URL}/v1/sms/list-country-tiers`, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-service-api-key": THIRDWEB_API_SECRET,
|
||||
const res = await fetch(
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/sms/list-country-tiers`,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-service-api-key": API_SERVER_SECRET,
|
||||
},
|
||||
next: {
|
||||
revalidate: 15 * 60, //15 minutes
|
||||
},
|
||||
},
|
||||
next: {
|
||||
revalidate: 15 * 60, //15 minutes
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
console.error(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
import { API_SERVER_URL } from "../constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "../constants/public-envs";
|
||||
|
||||
export type TeamInvite = {
|
||||
id: string;
|
||||
@@ -25,7 +25,7 @@ export async function getTeamInvites(
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`${API_SERVER_URL}/v1/teams/${teamId}/invites?skip=${options.start}&take=${options.count}`,
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${teamId}/invites?skip=${options.start}&take=${options.count}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import "server-only";
|
||||
import { API_SERVER_URL } from "@/constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "@/constants/public-envs";
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
|
||||
const TeamAccountRole = {
|
||||
@@ -33,7 +33,7 @@ export async function getMembers(teamSlug: string) {
|
||||
}
|
||||
|
||||
const teamsRes = await fetch(
|
||||
`${API_SERVER_URL}/v1/teams/${teamSlug}/members`,
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${teamSlug}/members`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
@@ -56,7 +56,7 @@ export async function getMemberById(teamSlug: string, memberId: string) {
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`${API_SERVER_URL}/v1/teams/${teamSlug}/members/${memberId}`,
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${teamSlug}/members/${memberId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
import { API_SERVER_URL } from "../constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "../constants/public-envs";
|
||||
import type { ProductSKU } from "../lib/billing";
|
||||
|
||||
type InvoiceLine = {
|
||||
@@ -47,7 +47,7 @@ export async function getTeamSubscriptions(slug: string) {
|
||||
}
|
||||
|
||||
const teamRes = await fetch(
|
||||
`${API_SERVER_URL}/v1/teams/${slug}/subscriptions`,
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${slug}/subscriptions`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { API_SERVER_URL, THIRDWEB_API_SECRET } from "@/constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "@/constants/public-envs";
|
||||
import { API_SERVER_SECRET } from "@/constants/server-envs";
|
||||
import type { TeamResponse } from "@thirdweb-dev/service-utils";
|
||||
import { cookies } from "next/headers";
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
@@ -14,11 +15,14 @@ export async function getTeamBySlug(slug: string) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const teamRes = await fetch(`${API_SERVER_URL}/v1/teams/${slug}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
const teamRes = await fetch(
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${slug}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
if (teamRes.ok) {
|
||||
return (await teamRes.json())?.result as Team;
|
||||
}
|
||||
@@ -26,11 +30,14 @@ export async function getTeamBySlug(slug: string) {
|
||||
}
|
||||
|
||||
export async function service_getTeamBySlug(slug: string) {
|
||||
const teamRes = await fetch(`${API_SERVER_URL}/v1/teams/${slug}`, {
|
||||
headers: {
|
||||
"x-service-api-key": THIRDWEB_API_SECRET,
|
||||
const teamRes = await fetch(
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${slug}`,
|
||||
{
|
||||
headers: {
|
||||
"x-service-api-key": API_SERVER_SECRET,
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
if (teamRes.ok) {
|
||||
return (await teamRes.json())?.result as Team;
|
||||
@@ -49,7 +56,7 @@ export async function getTeams() {
|
||||
return null;
|
||||
}
|
||||
|
||||
const teamsRes = await fetch(`${API_SERVER_URL}/v1/teams`, {
|
||||
const teamsRes = await fetch(`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
@@ -66,7 +73,7 @@ export async function getDefaultTeam() {
|
||||
return null;
|
||||
}
|
||||
|
||||
const res = await fetch(`${API_SERVER_URL}/v1/teams/~`, {
|
||||
const res = await fetch(`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/~`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const UB_BASE_URL = process.env.NEXT_PUBLIC_THIRDWEB_BRIDGE_HOST;
|
||||
@@ -1,7 +1,8 @@
|
||||
"use server";
|
||||
import { getAuthToken } from "app/(app)/api/lib/getAuthToken";
|
||||
import { NEXT_PUBLIC_THIRDWEB_BRIDGE_HOST } from "../../constants/public-envs";
|
||||
|
||||
const UB_BASE_URL = process.env.NEXT_PUBLIC_THIRDWEB_BRIDGE_HOST;
|
||||
const UB_BASE_URL = NEXT_PUBLIC_THIRDWEB_BRIDGE_HOST;
|
||||
|
||||
type Webhook = {
|
||||
url: string;
|
||||
@@ -15,13 +16,15 @@ type Webhook = {
|
||||
|
||||
export async function getWebhooks(props: {
|
||||
clientId: string;
|
||||
teamId: string;
|
||||
}) {
|
||||
const authToken = await getAuthToken();
|
||||
const res = await fetch(`${UB_BASE_URL}/v1/developer/webhooks`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-client-id-override": props.clientId,
|
||||
"x-client-id": props.clientId,
|
||||
"x-team-id": props.teamId,
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
});
|
||||
@@ -37,12 +40,20 @@ export async function getWebhooks(props: {
|
||||
|
||||
export async function createWebhook(props: {
|
||||
clientId: string;
|
||||
teamId: string;
|
||||
version?: number;
|
||||
url: string;
|
||||
label: string;
|
||||
secret?: string;
|
||||
}) {
|
||||
const authToken = await getAuthToken();
|
||||
console.log(
|
||||
"UB_BASE_URL",
|
||||
UB_BASE_URL,
|
||||
props.clientId,
|
||||
props.teamId,
|
||||
authToken,
|
||||
);
|
||||
const res = await fetch(`${UB_BASE_URL}/v1/developer/webhooks`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
@@ -53,7 +64,8 @@ export async function createWebhook(props: {
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-client-id-override": props.clientId,
|
||||
"x-client-id": props.clientId,
|
||||
"x-team-id": props.teamId,
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
});
|
||||
@@ -68,6 +80,7 @@ export async function createWebhook(props: {
|
||||
|
||||
export async function deleteWebhook(props: {
|
||||
clientId: string;
|
||||
teamId: string;
|
||||
webhookId: string;
|
||||
}) {
|
||||
const authToken = await getAuthToken();
|
||||
@@ -77,7 +90,8 @@ export async function deleteWebhook(props: {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-client-id-override": props.clientId,
|
||||
"x-client-id": props.clientId,
|
||||
"x-team-id": props.teamId,
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
},
|
||||
@@ -108,7 +122,7 @@ export async function getFees(props: {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-team-id": props.teamId,
|
||||
"x-client-id-override": props.clientId,
|
||||
"x-client-id": props.clientId,
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
});
|
||||
@@ -133,8 +147,8 @@ export async function updateFee(props: {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-client-id-override": props.clientId,
|
||||
"x-team-id": props.teamId,
|
||||
"x-client-id": props.clientId,
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
@@ -150,3 +164,87 @@ export async function updateFee(props: {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
export type PaymentsResponse = {
|
||||
data: Payment[];
|
||||
meta: {
|
||||
totalCount: number;
|
||||
};
|
||||
};
|
||||
export type Payment = {
|
||||
id: string;
|
||||
blockNumber?: bigint;
|
||||
transactionId: string;
|
||||
clientId: string;
|
||||
sender: string;
|
||||
receiver: string;
|
||||
developerFeeRecipient: string;
|
||||
developerFeeBps: number;
|
||||
transactions: Array<{
|
||||
chainId: number;
|
||||
transactionHash: string;
|
||||
}>;
|
||||
status: "PENDING" | "COMPLETED" | "FAILED" | "NOT_FOUND";
|
||||
type: "buy" | "sell" | "transfer";
|
||||
originAmount: bigint;
|
||||
destinationAmount: bigint;
|
||||
purchaseData: unknown;
|
||||
originToken: {
|
||||
address: string;
|
||||
symbol: string;
|
||||
decimals: number;
|
||||
chainId: number;
|
||||
};
|
||||
destinationToken: {
|
||||
address: string;
|
||||
symbol: string;
|
||||
decimals: number;
|
||||
chainId: number;
|
||||
};
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
export async function getPayments(props: {
|
||||
clientId: string;
|
||||
teamId: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}) {
|
||||
const authToken = await getAuthToken();
|
||||
|
||||
// Build URL with query parameters if provided
|
||||
let url = `${UB_BASE_URL}/v1/developer/payments`;
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
if (props.limit) {
|
||||
queryParams.append("limit", props.limit.toString());
|
||||
}
|
||||
|
||||
if (props.offset) {
|
||||
queryParams.append("offset", props.offset.toString());
|
||||
}
|
||||
|
||||
// Append query params to URL if any exist
|
||||
const queryString = queryParams.toString();
|
||||
if (queryString) {
|
||||
url = `${url}?${queryString}`;
|
||||
}
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-client-id": props.clientId,
|
||||
"x-team-id": props.teamId,
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new Error(text);
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
return json as PaymentsResponse;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"use server";
|
||||
import { DASHBOARD_THIRDWEB_SECRET_KEY } from "@/constants/server-envs";
|
||||
import { UB_BASE_URL } from "./constants";
|
||||
|
||||
export type TokenMetadata = {
|
||||
name: string;
|
||||
symbol: string;
|
||||
address: string;
|
||||
decimals: number;
|
||||
chainId: number;
|
||||
iconUri?: string;
|
||||
};
|
||||
|
||||
export async function getUniversalBridgeTokens(props: {
|
||||
chainId?: number;
|
||||
}) {
|
||||
const url = new URL(`${UB_BASE_URL}/v1/tokens`);
|
||||
|
||||
if (props.chainId) {
|
||||
url.searchParams.append("chainId", String(props.chainId));
|
||||
}
|
||||
url.searchParams.append("limit", "1000");
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-secret-key": DASHBOARD_THIRDWEB_SECRET_KEY,
|
||||
} as Record<string, string>,
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new Error(text);
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
return json.data as Array<TokenMetadata>;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import "server-only";
|
||||
import { unstable_cache } from "next/cache";
|
||||
import { ANALYTICS_SERVICE_URL } from "../../constants/server-envs";
|
||||
|
||||
export type RPCUsageDataItem = {
|
||||
date: string;
|
||||
@@ -16,7 +17,7 @@ export const fetchRPCUsage = unstable_cache(
|
||||
to: string;
|
||||
period: "day" | "week" | "month" | "year" | "all";
|
||||
}) => {
|
||||
const analyticsEndpoint = process.env.ANALYTICS_SERVICE_URL as string;
|
||||
const analyticsEndpoint = ANALYTICS_SERVICE_URL;
|
||||
const url = new URL(`${analyticsEndpoint}/v2/rpc/usage-types`);
|
||||
url.searchParams.set("teamId", params.teamId);
|
||||
if (params.projectId) {
|
||||
@@ -79,7 +80,7 @@ export const getLast24HoursRPCUsage = unstable_cache(
|
||||
projectId?: string;
|
||||
authToken: string;
|
||||
}) => {
|
||||
const analyticsEndpoint = process.env.ANALYTICS_SERVICE_URL as string;
|
||||
const analyticsEndpoint = ANALYTICS_SERVICE_URL;
|
||||
const url = new URL(`${analyticsEndpoint}/v2/rpc/24-hours`);
|
||||
url.searchParams.set("teamId", params.teamId);
|
||||
if (params.projectId) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import "server-only";
|
||||
|
||||
import { getAuthToken } from "../../app/(app)/api/lib/getAuthToken";
|
||||
import { API_SERVER_URL } from "../constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_API_HOST } from "../constants/public-envs";
|
||||
|
||||
export type VerifiedDomainResponse =
|
||||
| {
|
||||
@@ -27,7 +27,7 @@ export async function checkDomainVerification(
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`${API_SERVER_URL}/v1/teams/${teamIdOrSlug}/verified-domain`,
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${teamIdOrSlug}/verified-domain`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
@@ -54,7 +54,7 @@ export async function createDomainVerification(
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`${API_SERVER_URL}/v1/teams/${teamIdOrSlug}/verified-domain`,
|
||||
`${NEXT_PUBLIC_THIRDWEB_API_HOST}/v1/teams/${teamIdOrSlug}/verified-domain`,
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify({ domain }),
|
||||
|
||||
@@ -71,7 +71,7 @@ export function MultiNetworkSelector(props: {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Number.isInteger(Number.parseInt(searchValue))) {
|
||||
if (Number.isInteger(Number(searchValue))) {
|
||||
return String(chain.chainId).startsWith(searchValue);
|
||||
}
|
||||
return chain.name.toLowerCase().includes(searchValue.toLowerCase());
|
||||
@@ -144,18 +144,26 @@ export function SingleNetworkSelector(props: {
|
||||
side?: "left" | "right" | "top" | "bottom";
|
||||
disableChainId?: boolean;
|
||||
align?: "center" | "start" | "end";
|
||||
disableTestnets?: boolean;
|
||||
placeholder?: string;
|
||||
client: ThirdwebClient;
|
||||
}) {
|
||||
const { allChains, idToChain } = useAllChainsData();
|
||||
|
||||
const chainsToShow = useMemo(() => {
|
||||
if (!props.chainIds) {
|
||||
return allChains;
|
||||
let chains = allChains;
|
||||
|
||||
if (props.disableTestnets) {
|
||||
chains = chains.filter((chain) => !chain.testnet);
|
||||
}
|
||||
const chainIdSet = new Set(props.chainIds);
|
||||
return allChains.filter((chain) => chainIdSet.has(chain.chainId));
|
||||
}, [allChains, props.chainIds]);
|
||||
|
||||
if (props.chainIds) {
|
||||
const chainIdSet = new Set(props.chainIds);
|
||||
chains = chains.filter((chain) => chainIdSet.has(chain.chainId));
|
||||
}
|
||||
|
||||
return chains;
|
||||
}, [allChains, props.chainIds, props.disableTestnets]);
|
||||
|
||||
const options = useMemo(() => {
|
||||
return chainsToShow.map((chain) => {
|
||||
@@ -173,7 +181,7 @@ export function SingleNetworkSelector(props: {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Number.isInteger(Number.parseInt(searchValue))) {
|
||||
if (Number.isInteger(Number(searchValue))) {
|
||||
return String(chain.chainId).startsWith(searchValue);
|
||||
}
|
||||
return chain.name.toLowerCase().includes(searchValue.toLowerCase());
|
||||
@@ -218,6 +226,7 @@ export function SingleNetworkSelector(props: {
|
||||
<SelectWithSearch
|
||||
searchPlaceholder="Search by Name or Chain ID"
|
||||
value={String(props.chainId)}
|
||||
showCheck={false}
|
||||
options={options}
|
||||
onValueChange={(chainId) => {
|
||||
props.onChange(Number(chainId));
|
||||
|
||||
@@ -95,7 +95,7 @@ export function FullWidthSidebarLayout(props: {
|
||||
links={[...contentSidebarLinks, ...(footerSidebarLinks || [])]}
|
||||
/>
|
||||
|
||||
<main className="z-0 flex min-w-0 grow flex-col max-sm:w-full">
|
||||
<main className="flex min-w-0 grow flex-col max-sm:w-full">
|
||||
{children}
|
||||
</main>
|
||||
<AppFooter containerClassName="max-w-7xl" />
|
||||
@@ -137,7 +137,7 @@ function RenderSidebarGroup(props: {
|
||||
}
|
||||
|
||||
if ("separator" in link) {
|
||||
return <SidebarSeparator className="my-1" />;
|
||||
return <SidebarSeparator key={Math.random()} className="my-1" />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
BadgeContainer,
|
||||
storybookThirdwebClient,
|
||||
} from "../../../stories/utils";
|
||||
import { TokenSelector } from "./TokenSelector";
|
||||
|
||||
const meta = {
|
||||
title: "blocks/Cards/TokenSelector",
|
||||
component: Story,
|
||||
parameters: {
|
||||
nextjs: {
|
||||
appDirectory: true,
|
||||
},
|
||||
},
|
||||
} satisfies Meta<typeof Story>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Variants: Story = {
|
||||
args: {},
|
||||
};
|
||||
|
||||
function Story() {
|
||||
return (
|
||||
<div className="container flex max-w-6xl flex-col gap-8 py-10">
|
||||
<Variant label="No Chains selected by default" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Variant(props: {
|
||||
label: string;
|
||||
selectedChainId?: number;
|
||||
}) {
|
||||
const [tokenAddress, setTokenAddress] = useState<string>("");
|
||||
return (
|
||||
<BadgeContainer label={props.label}>
|
||||
<TokenSelector
|
||||
tokenAddress={tokenAddress}
|
||||
chainId={props.selectedChainId}
|
||||
client={storybookThirdwebClient}
|
||||
onChange={setTokenAddress}
|
||||
/>
|
||||
</BadgeContainer>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import { useCallback, useMemo } from "react";
|
||||
import type { ThirdwebClient } from "thirdweb";
|
||||
import { shortenAddress } from "thirdweb/utils";
|
||||
import { useTokensData } from "../../../hooks/tokens/tokens";
|
||||
import { replaceIpfsUrl } from "../../../lib/sdk";
|
||||
import { fallbackChainIcon } from "../../../utils/chain-icons";
|
||||
import { cn } from "../../lib/utils";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { Img } from "./Img";
|
||||
import { SelectWithSearch } from "./select-with-search";
|
||||
|
||||
type Option = { label: string; value: string };
|
||||
|
||||
export function TokenSelector(props: {
|
||||
tokenAddress: string | undefined;
|
||||
onChange: (tokenAddress: string) => void;
|
||||
className?: string;
|
||||
popoverContentClassName?: string;
|
||||
chainId?: number;
|
||||
side?: "left" | "right" | "top" | "bottom";
|
||||
disableChainId?: boolean;
|
||||
align?: "center" | "start" | "end";
|
||||
placeholder?: string;
|
||||
client: ThirdwebClient;
|
||||
disabled?: boolean;
|
||||
enabled?: boolean;
|
||||
}) {
|
||||
const { tokens, isFetching } = useTokensData({
|
||||
chainId: props.chainId,
|
||||
enabled: props.enabled,
|
||||
});
|
||||
|
||||
const options = useMemo(() => {
|
||||
return tokens.allTokens.map((token) => {
|
||||
return {
|
||||
label: token.symbol,
|
||||
value: `${token.chainId}:${token.address}`,
|
||||
};
|
||||
});
|
||||
}, [tokens.allTokens]);
|
||||
|
||||
const searchFn = useCallback(
|
||||
(option: Option, searchValue: string) => {
|
||||
const token = tokens.addressChainToToken.get(option.value);
|
||||
if (!token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Number.isInteger(Number(searchValue))) {
|
||||
return String(token.chainId).startsWith(searchValue);
|
||||
}
|
||||
return (
|
||||
token.name.toLowerCase().includes(searchValue.toLowerCase()) ||
|
||||
token.symbol.toLowerCase().includes(searchValue.toLowerCase()) ||
|
||||
token.address.toLowerCase().includes(searchValue.toLowerCase())
|
||||
);
|
||||
},
|
||||
[tokens],
|
||||
);
|
||||
|
||||
const renderOption = useCallback(
|
||||
(option: Option) => {
|
||||
const token = tokens.addressChainToToken.get(option.value);
|
||||
if (!token) {
|
||||
return option.label;
|
||||
}
|
||||
const resolvedSrc = token.iconUri
|
||||
? replaceIpfsUrl(token.iconUri, props.client)
|
||||
: fallbackChainIcon;
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<span className="flex grow gap-2 truncate text-left">
|
||||
<Img
|
||||
// render different image element if src changes to avoid showing old image while loading new one
|
||||
key={resolvedSrc}
|
||||
className={cn("size-5 rounded-full object-contain")}
|
||||
src={resolvedSrc}
|
||||
loading={"lazy"}
|
||||
alt=""
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
fallback={<img src={fallbackChainIcon} alt="" />}
|
||||
skeleton={
|
||||
<div className="animate-pulse rounded-full bg-border" />
|
||||
}
|
||||
/>
|
||||
{token.symbol}
|
||||
</span>
|
||||
|
||||
{!props.disableChainId && (
|
||||
<Badge variant="outline" className="gap-2 max-sm:hidden">
|
||||
<span className="text-muted-foreground">Address</span>
|
||||
{shortenAddress(token.address, 4)}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
[tokens, props.disableChainId, props.client],
|
||||
);
|
||||
|
||||
return (
|
||||
<SelectWithSearch
|
||||
searchPlaceholder="Search by name or symbol"
|
||||
value={props.tokenAddress}
|
||||
options={options}
|
||||
onValueChange={(tokenAddress) => {
|
||||
props.onChange(tokenAddress);
|
||||
}}
|
||||
closeOnSelect={true}
|
||||
showCheck={false}
|
||||
placeholder={
|
||||
isFetching ? "Loading Tokens..." : props.placeholder || "Select Token"
|
||||
}
|
||||
overrideSearchFn={searchFn}
|
||||
renderOption={renderOption}
|
||||
className={props.className}
|
||||
popoverContentClassName={props.popoverContentClassName}
|
||||
disabled={isFetching || props.disabled}
|
||||
side={props.side}
|
||||
align={props.align}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
"use client";
|
||||
|
||||
import { XIcon } from "lucide-react";
|
||||
import { useLocalStorage } from "../../../hooks/useLocalStorage";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
export function DismissibleAlert(props: {
|
||||
title: React.ReactNode;
|
||||
description: React.ReactNode;
|
||||
localStorageId: string;
|
||||
}) {
|
||||
const [isVisible, setIsVisible] = useLocalStorage(
|
||||
props.localStorageId,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<div className="relative rounded-lg border border-border bg-card p-4">
|
||||
<Button
|
||||
onClick={() => setIsVisible(false)}
|
||||
className="absolute top-4 right-4 h-auto w-auto p-1 text-muted-foreground"
|
||||
aria-label="Close alert"
|
||||
variant="ghost"
|
||||
>
|
||||
<XIcon className="size-5" />
|
||||
</Button>
|
||||
<div>
|
||||
<h2 className="mb-0.5 font-semibold">{props.title} </h2>
|
||||
<div className="text-muted-foreground text-sm">{props.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from "@/components/ui/popover";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { CheckIcon, ChevronDown, SearchIcon, XIcon } from "lucide-react";
|
||||
import { CheckIcon, ChevronDownIcon, SearchIcon, XIcon } from "lucide-react";
|
||||
import {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
@@ -201,7 +201,7 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
|
||||
orientation="vertical"
|
||||
className="flex h-full min-h-6"
|
||||
/>
|
||||
<ChevronDown className="h-4 cursor-pointer text-muted-foreground" />
|
||||
<ChevronDownIcon className="h-4 cursor-pointer text-muted-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
@@ -209,7 +209,7 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
|
||||
<span className="text-muted-foreground text-sm">
|
||||
{placeholder}
|
||||
</span>
|
||||
<ChevronDown className="h-4 cursor-pointer text-muted-foreground" />
|
||||
<ChevronDownIcon className="h-4 cursor-pointer text-muted-foreground" />
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { CheckIcon, ChevronDown, SearchIcon } from "lucide-react";
|
||||
import { CheckIcon, ChevronDownIcon, SearchIcon } from "lucide-react";
|
||||
import React, { useRef, useMemo, useEffect } from "react";
|
||||
import { useShowMore } from "../../lib/useShowMore";
|
||||
import { ScrollShadow } from "../ui/ScrollShadow/ScrollShadow";
|
||||
@@ -34,6 +34,7 @@ interface SelectWithSearchProps
|
||||
side?: "left" | "right" | "top" | "bottom";
|
||||
align?: "center" | "start" | "end";
|
||||
closeOnSelect?: boolean;
|
||||
showCheck?: boolean;
|
||||
}
|
||||
|
||||
export const SelectWithSearch = React.forwardRef<
|
||||
@@ -52,6 +53,7 @@ export const SelectWithSearch = React.forwardRef<
|
||||
popoverContentClassName,
|
||||
searchPlaceholder,
|
||||
closeOnSelect,
|
||||
showCheck = true,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
@@ -126,12 +128,14 @@ export const SelectWithSearch = React.forwardRef<
|
||||
<span
|
||||
className={cn(
|
||||
"truncate text-muted-foreground text-sm",
|
||||
selectedOption && "text-foreground",
|
||||
selectedOption && "w-full text-foreground",
|
||||
)}
|
||||
>
|
||||
{selectedOption?.label || placeholder}
|
||||
{renderOption && selectedOption
|
||||
? renderOption(selectedOption)
|
||||
: selectedOption?.label || placeholder}
|
||||
</span>
|
||||
<ChevronDown className="size-4 cursor-pointer text-muted-foreground" />
|
||||
<ChevronDownIcon className="size-4 cursor-pointer text-muted-foreground" />
|
||||
</div>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
@@ -191,9 +195,11 @@ export const SelectWithSearch = React.forwardRef<
|
||||
i === optionsToShow.length - 1 ? lastItemRef : undefined
|
||||
}
|
||||
>
|
||||
<div className="flex size-4 items-center justify-center">
|
||||
{isSelected && <CheckIcon className="size-4" />}
|
||||
</div>
|
||||
{showCheck && (
|
||||
<div className="flex size-4 items-center justify-center">
|
||||
{isSelected && <CheckIcon className="size-4" />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="min-w-0 grow">
|
||||
{renderOption ? renderOption(option) : option.label}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { useThirdwebClient } from "@/constants/thirdweb.client";
|
||||
import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler";
|
||||
import { useClipboard } from "hooks/useClipboard";
|
||||
import { Check, Copy, XIcon } from "lucide-react";
|
||||
import { CheckIcon, CopyIcon, XIcon } from "lucide-react";
|
||||
import { useMemo } from "react";
|
||||
import { type ThirdwebClient, isAddress } from "thirdweb";
|
||||
import { ZERO_ADDRESS } from "thirdweb";
|
||||
@@ -104,9 +104,9 @@ export function WalletAddress(props: {
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
{hasCopied ? (
|
||||
<Check className="h-4 w-4" />
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
) : (
|
||||
<Copy className="h-4 w-4" />
|
||||
<CopyIcon className="h-4 w-4" />
|
||||
)}
|
||||
{hasCopied ? "Copied!" : "Copy"}
|
||||
</Button>
|
||||
|
||||
@@ -9,11 +9,7 @@ import {
|
||||
} from "@/components/ui/popover";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { format, isBefore } from "date-fns";
|
||||
import {
|
||||
Calendar as CalendarIcon,
|
||||
CalendarX2Icon,
|
||||
ChevronDownIcon,
|
||||
} from "lucide-react";
|
||||
import { CalendarIcon, CalendarX2Icon, ChevronDownIcon } from "lucide-react";
|
||||
import React from "react";
|
||||
import { DynamicHeight } from "./DynamicHeight";
|
||||
import { TabButtons } from "./tabs";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { ChevronDownIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -34,7 +34,7 @@ const AccordionTrigger = React.forwardRef<
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
||||
<ChevronDownIcon className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { ChevronRight, MoreHorizontal } from "lucide-react";
|
||||
import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -84,7 +84,7 @@ const BreadcrumbSeparator = ({
|
||||
className={cn("[&>svg]:size-3.5", className)}
|
||||
{...props}
|
||||
>
|
||||
{children ?? <ChevronRight />}
|
||||
{children ?? <ChevronRightIcon />}
|
||||
</li>
|
||||
);
|
||||
BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
|
||||
@@ -102,7 +102,7 @@ const BreadcrumbEllipsis = ({
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
<MoreHorizontalIcon className="h-4 w-4" />
|
||||
<span className="sr-only">More</span>
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -22,7 +22,7 @@ const buttonVariants = cva(
|
||||
link: "text-primary underline-offset-4 hover:underline text-semibold",
|
||||
pink: "border border-nebula-pink-foreground !text-nebula-pink-foreground bg-[hsl(var(--nebula-pink-foreground)/5%)] hover:bg-nebula-pink-foreground/10 dark:!text-foreground dark:bg-nebula-pink-foreground/10 dark:hover:bg-nebula-pink-foreground/20",
|
||||
upsell:
|
||||
"bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:from-purple-600 hover:to-pink-600 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200",
|
||||
"bg-green-600 text-white hover:bg-green-700 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
||||
import type * as React from "react";
|
||||
import { DayPicker } from "react-day-picker";
|
||||
|
||||
@@ -54,8 +54,8 @@ function Calendar({
|
||||
...classNames,
|
||||
}}
|
||||
components={{
|
||||
IconLeft: () => <ChevronLeft className="h-4 w-4" />,
|
||||
IconRight: () => <ChevronRight className="h-4 w-4" />,
|
||||
IconLeft: () => <ChevronLeftIcon className="size-4" />,
|
||||
IconRight: () => <ChevronRightIcon className="size-4" />,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -12,6 +12,7 @@ export type ChartConfig = {
|
||||
[k in string]: {
|
||||
label?: React.ReactNode;
|
||||
icon?: React.ComponentType;
|
||||
isCurrency?: boolean;
|
||||
} & (
|
||||
| { color?: string; theme?: never }
|
||||
| { color?: never; theme: Record<keyof typeof THEMES, string> }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
||||
import { Check } from "lucide-react";
|
||||
import { CheckIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -21,7 +21,7 @@ const Checkbox = React.forwardRef<
|
||||
<CheckboxPrimitive.Indicator
|
||||
className={cn("flex items-center justify-center text-current")}
|
||||
>
|
||||
<Check className="h-4 w-4" />
|
||||
<CheckIcon className="size-4" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||
import { X } from "lucide-react";
|
||||
import { XIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -65,7 +65,7 @@ const DialogContent = React.forwardRef<
|
||||
dialogCloseClassName,
|
||||
)}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
<XIcon className="size-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||
import { Check, ChevronRight, Circle } from "lucide-react";
|
||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -34,7 +34,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRight className="ml-auto h-4 w-4" />
|
||||
<ChevronRightIcon className="ml-auto size-4" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
));
|
||||
DropdownMenuSubTrigger.displayName =
|
||||
@@ -107,7 +107,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Check className="h-4 w-4" />
|
||||
<CheckIcon className="size-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
@@ -130,7 +130,7 @@ const DropdownMenuRadioItem = React.forwardRef<
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Circle className="h-2 w-2 fill-current" />
|
||||
<CircleIcon className="size-2 fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type React from "react";
|
||||
import { useRef } from "react";
|
||||
|
||||
interface ImageUploadProps {
|
||||
value: File | undefined;
|
||||
onChange?: (files: File[]) => void;
|
||||
children?: React.ReactNode;
|
||||
variant?: React.ComponentProps<typeof Button>["variant"];
|
||||
className?: string;
|
||||
multiple?: boolean;
|
||||
accept: string;
|
||||
}
|
||||
|
||||
export function ImageUploadButton(props: ImageUploadProps) {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = Array.from(e.target.files || []);
|
||||
props.onChange?.(files);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
variant={props.variant}
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
className={props.className}
|
||||
>
|
||||
{props.children}
|
||||
</Button>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
multiple={props.multiple}
|
||||
accept={props.accept}
|
||||
onChange={handleFileChange}
|
||||
className="hidden"
|
||||
aria-label="Upload image"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { OTPInput, OTPInputContext } from "input-otp";
|
||||
import { Dot } from "lucide-react";
|
||||
import { DotIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -65,7 +65,7 @@ const InputOTPSeparator = React.forwardRef<
|
||||
>(({ ...props }, ref) => (
|
||||
// biome-ignore lint/a11y/useFocusableInteractive: pure shadcn component - it works
|
||||
<div ref={ref} role="separator" {...props}>
|
||||
<Dot />
|
||||
<DotIcon />
|
||||
</div>
|
||||
));
|
||||
InputOTPSeparator.displayName = "InputOTPSeparator";
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
|
||||
import {
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
MoreHorizontalIcon,
|
||||
} from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { Button, type ButtonProps } from "@/components/ui/button";
|
||||
@@ -66,7 +70,7 @@ const PaginationPrevious = ({
|
||||
className={cn("h-10 gap-1 pl-2.5", className)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
<ChevronLeftIcon className="size-4" />
|
||||
<span className="max-sm:hidden">Previous</span>
|
||||
</PaginationLink>
|
||||
);
|
||||
@@ -83,7 +87,7 @@ const PaginationNext = ({
|
||||
{...props}
|
||||
>
|
||||
<span className="max-sm:hidden">Next</span>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
<ChevronRightIcon className="size-4" />
|
||||
</PaginationLink>
|
||||
);
|
||||
PaginationNext.displayName = "PaginationNext";
|
||||
@@ -100,7 +104,7 @@ const PaginationEllipsis = ({
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
<MoreHorizontalIcon className="size-4" />
|
||||
<span className="sr-only">More pages</span>
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
||||
import { Circle } from "lucide-react";
|
||||
import { CircleIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -35,7 +35,7 @@ const RadioGroupItem = React.forwardRef<
|
||||
{...props}
|
||||
>
|
||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
||||
<Circle className="h-2.5 w-2.5 fill-current text-current" />
|
||||
<CircleIcon className="size-2.5 fill-current text-current" />
|
||||
</RadioGroupPrimitive.Indicator>
|
||||
</RadioGroupPrimitive.Item>
|
||||
);
|
||||
@@ -59,11 +59,11 @@ const RadioGroupItemButton = React.forwardRef<
|
||||
<div className="flex aspect-square h-4 w-4 items-center justify-center rounded-full border text-inverted ring-offset-background transition-all focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 group-hover:border-foreground/25 group-data-[state=checked]:border-inverted">
|
||||
{/* Show on checked */}
|
||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center rounded-full transition-all">
|
||||
<Circle className="h-2 w-2 fill-current text-current" />
|
||||
<CircleIcon className="size-2 fill-current text-current" />
|
||||
</RadioGroupPrimitive.Indicator>
|
||||
{/* Show on hover */}
|
||||
<div className="hidden items-center justify-center rounded-full text-foreground/25 transition-all group-hover:flex group-data-[state=checked]:hidden">
|
||||
<Circle className="h-2 w-2 fill-current text-current" />
|
||||
<CircleIcon className="size-2 fill-current text-current" />
|
||||
</div>
|
||||
</div>
|
||||
<Label className="cursor-pointer">{props.children}</Label>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||
import { Check, ChevronDown } from "lucide-react";
|
||||
import { CheckIcon, ChevronDownIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -28,7 +28,7 @@ const SelectTrigger = React.forwardRef<
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<ChevronDown className={cn("h-4 w-4 opacity-50", chevronClassName)} />
|
||||
<ChevronDownIcon className={cn("h-4 w-4 opacity-50", chevronClassName)} />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
));
|
||||
@@ -127,7 +127,7 @@ const SelectItem = React.forwardRef<
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<Check className="h-4 w-4" />
|
||||
<CheckIcon className="size-4" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
||||
import { type VariantProps, cva } from "class-variance-authority";
|
||||
import { X } from "lucide-react";
|
||||
import { XIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -66,7 +66,7 @@ const SheetContent = React.forwardRef<
|
||||
>
|
||||
{children}
|
||||
<SheetPrimitive.Close className="absolute top-4 right-4 rounded-sm border-border opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
||||
<X className="h-4 w-4" />
|
||||
<XIcon className="size-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
</SheetPrimitive.Content>
|
||||
|
||||
@@ -21,7 +21,7 @@ import { useIsMobile } from "@/hooks/use-mobile";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { type VariantProps, cva } from "class-variance-authority";
|
||||
import { PanelLeft } from "lucide-react";
|
||||
import { PanelLeftIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
const SIDEBAR_COOKIE_NAME = "sidebar_state";
|
||||
@@ -288,7 +288,7 @@ const SidebarTrigger = React.forwardRef<
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<PanelLeft />
|
||||
<PanelLeftIcon />
|
||||
<span className="sr-only">Toggle Sidebar</span>
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Button } from "./button";
|
||||
import { ToolTipLabel } from "./tooltip";
|
||||
|
||||
export type TabLink = {
|
||||
name: string;
|
||||
name: React.ReactNode;
|
||||
href: string;
|
||||
isActive: boolean;
|
||||
isDisabled?: boolean;
|
||||
@@ -43,7 +43,7 @@ export function TabLinks(props: {
|
||||
return (
|
||||
<Button
|
||||
asChild
|
||||
key={tab.name}
|
||||
key={tab.href}
|
||||
disabled={tab.isDisabled}
|
||||
variant="ghost"
|
||||
>
|
||||
@@ -206,7 +206,7 @@ function useUnderline<El extends HTMLElement>() {
|
||||
|
||||
export function TabPathLinks(props: {
|
||||
links: {
|
||||
name: string;
|
||||
name: React.ReactNode;
|
||||
path: string;
|
||||
exactMatch?: boolean;
|
||||
isDisabled?: boolean;
|
||||
|
||||
@@ -54,11 +54,11 @@ export function ToolTipLabel(props: {
|
||||
align={props.align}
|
||||
sideOffset={10}
|
||||
className={cn(
|
||||
"max-w-[400px] whitespace-normal leading-relaxed",
|
||||
"max-w-[400px] whitespace-normal p-0 leading-relaxed",
|
||||
props.contentClassName,
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-1.5 p-2 text-sm">
|
||||
<div className="flex items-center gap-1.5 p-4 text-sm">
|
||||
{props.leftIcon}
|
||||
{props.label}
|
||||
{props.rightIcon}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
export const isProd =
|
||||
(process.env.VERCEL_ENV || process.env.NEXT_PUBLIC_VERCEL_ENV) ===
|
||||
"production";
|
||||
|
||||
export const PROD_OR_DEV_URL = isProd ? "thirdweb.com" : "thirdweb-dev.com";
|
||||
|
||||
export const BASE_URL = isProd
|
||||
? "https://thirdweb.com"
|
||||
: (process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL
|
||||
? `https://${process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL}`
|
||||
: "http://localhost:3000") || "https://thirdweb-dev.com";
|
||||
@@ -1,56 +0,0 @@
|
||||
export const DASHBOARD_THIRDWEB_CLIENT_ID =
|
||||
process.env.NEXT_PUBLIC_DASHBOARD_CLIENT_ID || "";
|
||||
|
||||
export const DASHBOARD_THIRDWEB_SECRET_KEY =
|
||||
process.env.DASHBOARD_SECRET_KEY || "";
|
||||
|
||||
export const NEXT_PUBLIC_NEBULA_APP_CLIENT_ID =
|
||||
process.env.NEXT_PUBLIC_NEBULA_APP_CLIENT_ID || "";
|
||||
|
||||
export const NEBULA_APP_SECRET_KEY = process.env.NEBULA_APP_SECRET_KEY || "";
|
||||
|
||||
export const THIRDWEB_API_SECRET = process.env.API_SERVER_SECRET || "";
|
||||
|
||||
export const IPFS_GATEWAY_URL =
|
||||
(process.env.NEXT_PUBLIC_IPFS_GATEWAY_URL as string) ||
|
||||
"https://{clientId}.ipfscdn.io/ipfs/{cid}/{path}";
|
||||
|
||||
export const isProd =
|
||||
(process.env.VERCEL_ENV || process.env.NEXT_PUBLIC_VERCEL_ENV) ===
|
||||
"production";
|
||||
|
||||
export const PROD_OR_DEV_URL = isProd ? "thirdweb.com" : "thirdweb-dev.com";
|
||||
|
||||
export const DASHBOARD_STORAGE_URL =
|
||||
process.env.NEXT_PUBLIC_DASHBOARD_UPLOAD_SERVER ||
|
||||
"https://storage.thirdweb.com";
|
||||
|
||||
export const API_SERVER_URL =
|
||||
process.env.NEXT_PUBLIC_THIRDWEB_API_HOST || "https://api.thirdweb.com";
|
||||
|
||||
export const BRIDGE_URL =
|
||||
process.env.NEXT_PUBLIC_THIRDWEB_BRIDGE_HOST || "https://bridge.thirdweb.com";
|
||||
|
||||
/**
|
||||
* Faucet stuff
|
||||
*/
|
||||
// Cloudflare Turnstile Site key
|
||||
export const TURNSTILE_SITE_KEY =
|
||||
process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY || "";
|
||||
export const THIRDWEB_ENGINE_FAUCET_WALLET =
|
||||
process.env.NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET || "";
|
||||
export const THIRDWEB_ENGINE_URL = process.env.THIRDWEB_ENGINE_URL;
|
||||
export const THIRDWEB_ACCESS_TOKEN = process.env.THIRDWEB_ACCESS_TOKEN;
|
||||
// Comma-separated list of chain IDs to disable faucet for.
|
||||
export const DISABLE_FAUCET_CHAIN_IDS = process.env.DISABLE_FAUCET_CHAIN_IDS;
|
||||
|
||||
export const BASE_URL = isProd
|
||||
? "https://thirdweb.com"
|
||||
: (process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL
|
||||
? `https://${process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL}`
|
||||
: "http://localhost:3000") || "https://thirdweb-dev.com";
|
||||
|
||||
export const NEXT_PUBLIC_NEBULA_URL = process.env.NEXT_PUBLIC_NEBULA_URL;
|
||||
|
||||
export const INSIGHT_SERVICE_API_KEY =
|
||||
process.env.INSIGHT_SERVICE_API_KEY || "";
|
||||
@@ -0,0 +1,36 @@
|
||||
export const NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID =
|
||||
process.env.NEXT_PUBLIC_DASHBOARD_CLIENT_ID || "";
|
||||
|
||||
export const NEXT_PUBLIC_NEBULA_APP_CLIENT_ID =
|
||||
process.env.NEXT_PUBLIC_NEBULA_APP_CLIENT_ID || "";
|
||||
|
||||
export const NEXT_PUBLIC_THIRDWEB_VAULT_URL =
|
||||
process.env.NEXT_PUBLIC_THIRDWEB_VAULT_URL || "";
|
||||
|
||||
export const NEXT_PUBLIC_ENGINE_CLOUD_URL =
|
||||
process.env.NEXT_PUBLIC_ENGINE_CLOUD_URL || "";
|
||||
|
||||
export const NEXT_PUBLIC_IPFS_GATEWAY_URL =
|
||||
(process.env.NEXT_PUBLIC_IPFS_GATEWAY_URL as string) ||
|
||||
"https://{clientId}.ipfscdn.io/ipfs/{cid}/{path}";
|
||||
|
||||
export const NEXT_PUBLIC_DASHBOARD_UPLOAD_SERVER =
|
||||
process.env.NEXT_PUBLIC_DASHBOARD_UPLOAD_SERVER ||
|
||||
"https://storage.thirdweb.com";
|
||||
|
||||
export const NEXT_PUBLIC_THIRDWEB_API_HOST =
|
||||
process.env.NEXT_PUBLIC_THIRDWEB_API_HOST || "https://api.thirdweb.com";
|
||||
|
||||
export const NEXT_PUBLIC_THIRDWEB_BRIDGE_HOST =
|
||||
process.env.NEXT_PUBLIC_THIRDWEB_BRIDGE_HOST || "https://bridge.thirdweb.com";
|
||||
|
||||
export const NEXT_PUBLIC_TURNSTILE_SITE_KEY =
|
||||
process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY || "";
|
||||
|
||||
export const NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET =
|
||||
process.env.NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET || "";
|
||||
|
||||
export const NEXT_PUBLIC_NEBULA_URL = process.env.NEXT_PUBLIC_NEBULA_URL || "";
|
||||
|
||||
export const NEXT_PUBLIC_DEMO_ENGINE_URL =
|
||||
process.env.NEXT_PUBLIC_DEMO_ENGINE_URL || "";
|
||||
@@ -0,0 +1,117 @@
|
||||
import "server-only";
|
||||
import { experimental_taintUniqueValue } from "react";
|
||||
import { isProd } from "./env-utils";
|
||||
|
||||
// Make sure to taint the server only envs here with experimental_taintUniqueValue ONLY if they contain a UNIQUE sensitive value
|
||||
// if an env has a generic value that may appear naturally in client components - do not taint it
|
||||
|
||||
export const DASHBOARD_THIRDWEB_SECRET_KEY =
|
||||
process.env.DASHBOARD_SECRET_KEY || "";
|
||||
|
||||
experimental_taintUniqueValue(
|
||||
"Do not pass DASHBOARD_THIRDWEB_SECRET_KEY to the client",
|
||||
process,
|
||||
DASHBOARD_THIRDWEB_SECRET_KEY,
|
||||
);
|
||||
|
||||
export const NEBULA_APP_SECRET_KEY = process.env.NEBULA_APP_SECRET_KEY || "";
|
||||
|
||||
if (NEBULA_APP_SECRET_KEY) {
|
||||
experimental_taintUniqueValue(
|
||||
"Do not pass NEBULA_APP_SECRET_KEY to the client",
|
||||
process,
|
||||
NEBULA_APP_SECRET_KEY,
|
||||
);
|
||||
}
|
||||
|
||||
export const API_SERVER_SECRET = process.env.API_SERVER_SECRET || "";
|
||||
|
||||
if (API_SERVER_SECRET) {
|
||||
experimental_taintUniqueValue(
|
||||
"Do not pass API_SERVER_SECRET to the client",
|
||||
process,
|
||||
API_SERVER_SECRET,
|
||||
);
|
||||
}
|
||||
|
||||
export const THIRDWEB_ENGINE_URL = process.env.THIRDWEB_ENGINE_URL || "";
|
||||
|
||||
if (THIRDWEB_ENGINE_URL) {
|
||||
experimental_taintUniqueValue(
|
||||
"Do not pass THIRDWEB_ENGINE_URL to the client",
|
||||
process,
|
||||
THIRDWEB_ENGINE_URL,
|
||||
);
|
||||
}
|
||||
|
||||
export const THIRDWEB_ACCESS_TOKEN = process.env.THIRDWEB_ACCESS_TOKEN || "";
|
||||
|
||||
if (THIRDWEB_ACCESS_TOKEN) {
|
||||
experimental_taintUniqueValue(
|
||||
"Do not pass THIRDWEB_ACCESS_TOKEN to the client",
|
||||
process,
|
||||
THIRDWEB_ACCESS_TOKEN,
|
||||
);
|
||||
}
|
||||
|
||||
// Comma-separated list of chain IDs to disable faucet for.
|
||||
// Note: Can not taint this - because the value is too generic
|
||||
export const DISABLE_FAUCET_CHAIN_IDS =
|
||||
process.env.DISABLE_FAUCET_CHAIN_IDS || "";
|
||||
|
||||
export const INSIGHT_SERVICE_API_KEY =
|
||||
process.env.INSIGHT_SERVICE_API_KEY || "";
|
||||
|
||||
// The value of INSIGHT_SERVICE_API_KEY on dev is generic - so we don't taint it
|
||||
// TODO - have better INSIGHT_SERVICE_API_KEY on dev
|
||||
if (isProd && INSIGHT_SERVICE_API_KEY) {
|
||||
experimental_taintUniqueValue(
|
||||
"Do not pass INSIGHT_SERVICE_API_KEY to the client",
|
||||
process,
|
||||
INSIGHT_SERVICE_API_KEY,
|
||||
);
|
||||
}
|
||||
|
||||
export const MORALIS_API_KEY = process.env.MORALIS_API_KEY || "";
|
||||
|
||||
if (MORALIS_API_KEY) {
|
||||
experimental_taintUniqueValue(
|
||||
"Do not pass MORALIS_API_KEY to the client",
|
||||
process,
|
||||
MORALIS_API_KEY,
|
||||
);
|
||||
}
|
||||
|
||||
export const ANALYTICS_SERVICE_URL = process.env.ANALYTICS_SERVICE_URL || "";
|
||||
|
||||
export const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY || "";
|
||||
|
||||
if (STRIPE_SECRET_KEY) {
|
||||
experimental_taintUniqueValue(
|
||||
"Do not pass STRIPE_SECRET_KEY to the client",
|
||||
process,
|
||||
STRIPE_SECRET_KEY,
|
||||
);
|
||||
}
|
||||
|
||||
export const API_ROUTES_CLIENT_ID = process.env.API_ROUTES_CLIENT_ID || "";
|
||||
|
||||
export const TURNSTILE_SECRET_KEY = process.env.TURNSTILE_SECRET_KEY || "";
|
||||
|
||||
if (TURNSTILE_SECRET_KEY) {
|
||||
experimental_taintUniqueValue(
|
||||
"Do not pass TURNSTILE_SECRET_KEY to the client",
|
||||
process,
|
||||
TURNSTILE_SECRET_KEY,
|
||||
);
|
||||
}
|
||||
|
||||
export const REDIS_URL = process.env.REDIS_URL || "";
|
||||
|
||||
if (REDIS_URL) {
|
||||
experimental_taintUniqueValue(
|
||||
"Do not pass REDIS_URL to the client",
|
||||
process,
|
||||
REDIS_URL,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { getConfiguredThirdwebClient } from "./thirdweb.server";
|
||||
|
||||
export function getClientThirdwebClient(params?: {
|
||||
jwt: string | undefined | null;
|
||||
teamId: string | undefined | null;
|
||||
}) {
|
||||
return getConfiguredThirdwebClient({
|
||||
secretKey: params?.jwt ?? undefined,
|
||||
teamId: params?.teamId ?? undefined,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import "server-only";
|
||||
|
||||
import { DASHBOARD_THIRDWEB_SECRET_KEY } from "./server-envs";
|
||||
import { getConfiguredThirdwebClient } from "./thirdweb.server";
|
||||
|
||||
export const serverThirdwebClient = getConfiguredThirdwebClient({
|
||||
teamId: undefined,
|
||||
secretKey: DASHBOARD_THIRDWEB_SECRET_KEY,
|
||||
});
|
||||
@@ -4,7 +4,7 @@ import { useActiveAccount } from "thirdweb/react";
|
||||
import type { GetAuthTokenResponse } from "../../app/(app)/api/auth/get-auth-token/route";
|
||||
import { LAST_USED_TEAM_ID } from "../../constants/cookies";
|
||||
import { getCookie } from "../../lib/cookie";
|
||||
import { getThirdwebClient } from "./thirdweb.server";
|
||||
import { getClientThirdwebClient } from "./thirdweb-client.client";
|
||||
|
||||
// returns a thirdweb client with optional JWT passed i
|
||||
|
||||
@@ -38,7 +38,7 @@ export function useThirdwebClient(jwt?: string) {
|
||||
return useMemo(
|
||||
// prefer jwt from props over the one from the token query if it exists
|
||||
() =>
|
||||
getThirdwebClient({
|
||||
getClientThirdwebClient({
|
||||
jwt: jwt || query.data,
|
||||
teamId: lastUsedTeamId,
|
||||
}),
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import {
|
||||
DASHBOARD_THIRDWEB_CLIENT_ID,
|
||||
DASHBOARD_THIRDWEB_SECRET_KEY,
|
||||
IPFS_GATEWAY_URL,
|
||||
} from "@/constants/env";
|
||||
NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID,
|
||||
NEXT_PUBLIC_IPFS_GATEWAY_URL,
|
||||
} from "@/constants/public-envs";
|
||||
import {
|
||||
THIRDWEB_BUNDLER_DOMAIN,
|
||||
THIRDWEB_INAPP_WALLET_DOMAIN,
|
||||
@@ -12,7 +11,7 @@ import {
|
||||
THIRDWEB_SOCIAL_API_DOMAIN,
|
||||
THIRDWEB_STORAGE_DOMAIN,
|
||||
} from "constants/urls";
|
||||
import { createThirdwebClient } from "thirdweb";
|
||||
import { type ThirdwebClient, createThirdwebClient } from "thirdweb";
|
||||
import { populateEip712Transaction } from "thirdweb/transaction";
|
||||
import {
|
||||
getTransactionDecorator,
|
||||
@@ -22,15 +21,10 @@ import {
|
||||
import { getZkPaymasterData } from "thirdweb/wallets/smart";
|
||||
import { getVercelEnv } from "../../lib/vercel-utils";
|
||||
|
||||
// returns a thirdweb client with optional JWT passed in
|
||||
export function getThirdwebClient(
|
||||
options:
|
||||
| {
|
||||
jwt: string | null | undefined;
|
||||
teamId: string | undefined;
|
||||
}
|
||||
| undefined,
|
||||
) {
|
||||
export function getConfiguredThirdwebClient(options: {
|
||||
secretKey: string | undefined;
|
||||
teamId: string | undefined;
|
||||
}): ThirdwebClient {
|
||||
if (getVercelEnv() !== "production") {
|
||||
// if not on production: run this when creating a client to set the domains
|
||||
setThirdwebDomains({
|
||||
@@ -78,12 +72,12 @@ export function getThirdwebClient(
|
||||
}
|
||||
|
||||
return createThirdwebClient({
|
||||
teamId: options?.teamId,
|
||||
secretKey: options?.jwt ? options.jwt : DASHBOARD_THIRDWEB_SECRET_KEY,
|
||||
clientId: DASHBOARD_THIRDWEB_CLIENT_ID,
|
||||
teamId: options.teamId,
|
||||
secretKey: options.secretKey,
|
||||
clientId: NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID,
|
||||
config: {
|
||||
storage: {
|
||||
gatewayUrl: IPFS_GATEWAY_URL,
|
||||
gatewayUrl: NEXT_PUBLIC_IPFS_GATEWAY_URL,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { differenceInCalendarDays } from "date-fns";
|
||||
import {
|
||||
type DurationId,
|
||||
type Range,
|
||||
getLastNDaysRange,
|
||||
} from "../../components/analytics/date-range-selector";
|
||||
|
||||
export function normalizeTime(date: Date) {
|
||||
const newDate = new Date(date);
|
||||
newDate.setHours(1, 0, 0, 0);
|
||||
return newDate;
|
||||
}
|
||||
|
||||
export function normalizeTimeISOString(date: Date) {
|
||||
return normalizeTime(date).toISOString();
|
||||
}
|
||||
|
||||
export function getFiltersFromSearchParams(params: {
|
||||
from: string | undefined | string[];
|
||||
to: string | undefined | string[];
|
||||
interval: string | undefined | string[];
|
||||
defaultRange: DurationId;
|
||||
}) {
|
||||
const fromStr = params.from;
|
||||
const toStr = params.to;
|
||||
const defaultRange = getLastNDaysRange(params.defaultRange);
|
||||
|
||||
const range: Range =
|
||||
fromStr && toStr && typeof fromStr === "string" && typeof toStr === "string"
|
||||
? {
|
||||
from: normalizeTime(new Date(fromStr)),
|
||||
to: normalizeTime(new Date(toStr)),
|
||||
type: "custom",
|
||||
}
|
||||
: {
|
||||
from: normalizeTime(defaultRange.from),
|
||||
to: normalizeTime(defaultRange.to),
|
||||
type: defaultRange.type,
|
||||
};
|
||||
|
||||
const defaultInterval =
|
||||
params.interval ??
|
||||
(differenceInCalendarDays(range.to, range.from) > 30
|
||||
? ("week" as const)
|
||||
: ("day" as const));
|
||||
|
||||
return {
|
||||
range,
|
||||
interval:
|
||||
params.interval === "day"
|
||||
? ("day" as const)
|
||||
: params.interval === "week"
|
||||
? ("week" as const)
|
||||
: (defaultInterval as "day" | "week"),
|
||||
};
|
||||
}
|
||||
@@ -7,12 +7,12 @@ import {
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
import type { ResultItem } from "app/(app)/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/metrics/components/StatusCodes";
|
||||
import type { EngineBackendWalletType } from "lib/engine";
|
||||
import { useState } from "react";
|
||||
import { useActiveAccount } from "thirdweb/react";
|
||||
import invariant from "tiny-invariant";
|
||||
import type { EngineStatus } from "../../../app/(app)/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/overview/components/transactions-table";
|
||||
import type { ResultItem } from "../../../app/(app)/team/[team_slug]/[project_slug]/engine/dedicated/(instance)/[engineId]/metrics/components/StatusCodes";
|
||||
import type { EngineStatus } from "../../../app/(app)/team/[team_slug]/[project_slug]/engine/dedicated/(instance)/[engineId]/overview/components/transactions-table";
|
||||
import { engineKeys } from "../cache-keys";
|
||||
|
||||
// Engine instances
|
||||
@@ -1641,7 +1641,11 @@ interface EngineResourceMetrics {
|
||||
};
|
||||
}
|
||||
|
||||
export function useEngineSystemMetrics(engineId: string, teamIdOrSlug: string) {
|
||||
export function useEngineSystemMetrics(
|
||||
engineId: string,
|
||||
teamIdOrSlug: string,
|
||||
projectSlug: string,
|
||||
) {
|
||||
const [enabled, setEnabled] = useState(true);
|
||||
|
||||
return useQuery({
|
||||
@@ -1649,7 +1653,7 @@ export function useEngineSystemMetrics(engineId: string, teamIdOrSlug: string) {
|
||||
queryFn: async () => {
|
||||
const res = await apiServerProxy({
|
||||
method: "GET",
|
||||
pathname: `/v1/teams/${teamIdOrSlug}/engine/${engineId}/metrics`,
|
||||
pathname: `/v1/teams/${teamIdOrSlug}/${projectSlug}/engine/dedicated/${engineId}/metrics`,
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getThirdwebClient } from "@/constants/thirdweb.server";
|
||||
import type { Metadata } from "next";
|
||||
import { getClientThirdwebClient } from "../../../../../@/constants/thirdweb-client.client";
|
||||
import { UniversalBridgeEmbed } from "./components/client/UniversalBridgeEmbed";
|
||||
|
||||
const title = "Universal Bridge: Swap, Bridge, and On-Ramp";
|
||||
@@ -19,7 +19,7 @@ export default async function RoutesPage({
|
||||
searchParams,
|
||||
}: { searchParams: Record<string, string | string[]> }) {
|
||||
const { chainId } = searchParams;
|
||||
const client = getThirdwebClient(undefined);
|
||||
const client = getClientThirdwebClient(undefined);
|
||||
return (
|
||||
<div className="relative mx-auto flex h-screen w-full flex-col items-center justify-center overflow-hidden border py-10">
|
||||
<main className="container z-10 flex justify-center">
|
||||
|
||||
+3
-5
@@ -1,8 +1,8 @@
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler";
|
||||
import type { ThirdwebClient } from "thirdweb";
|
||||
import { defineChain } from "thirdweb";
|
||||
import { getChainMetadata } from "thirdweb/chains";
|
||||
import { serverThirdwebClient } from "../../../../../../../@/constants/thirdweb-client.server";
|
||||
|
||||
type RouteListCardProps = {
|
||||
originChainId: number;
|
||||
@@ -15,7 +15,6 @@ type RouteListCardProps = {
|
||||
destinationTokenIconUri?: string | null;
|
||||
destinationTokenSymbol: string;
|
||||
destinationTokenName: string;
|
||||
client: ThirdwebClient;
|
||||
};
|
||||
|
||||
export async function RouteListCard({
|
||||
@@ -27,7 +26,6 @@ export async function RouteListCard({
|
||||
destinationTokenAddress,
|
||||
destinationTokenIconUri,
|
||||
destinationTokenName,
|
||||
client,
|
||||
}: RouteListCardProps) {
|
||||
const [
|
||||
originChain,
|
||||
@@ -42,13 +40,13 @@ export async function RouteListCard({
|
||||
originTokenIconUri
|
||||
? resolveSchemeWithErrorHandler({
|
||||
uri: originTokenIconUri,
|
||||
client,
|
||||
client: serverThirdwebClient,
|
||||
})
|
||||
: undefined,
|
||||
destinationTokenIconUri
|
||||
? resolveSchemeWithErrorHandler({
|
||||
uri: destinationTokenIconUri,
|
||||
client,
|
||||
client: serverThirdwebClient,
|
||||
})
|
||||
: undefined,
|
||||
]);
|
||||
|
||||
+3
-5
@@ -1,7 +1,7 @@
|
||||
import { CopyTextButton } from "@/components/ui/CopyTextButton";
|
||||
import { TableCell, TableRow } from "@/components/ui/table";
|
||||
import { serverThirdwebClient } from "@/constants/thirdweb-client.server";
|
||||
import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler";
|
||||
import type { ThirdwebClient } from "thirdweb";
|
||||
import { defineChain, getChainMetadata } from "thirdweb/chains";
|
||||
|
||||
type RouteListRowProps = {
|
||||
@@ -15,7 +15,6 @@ type RouteListRowProps = {
|
||||
destinationTokenIconUri?: string | null;
|
||||
destinationTokenSymbol?: string;
|
||||
destinationTokenName?: string;
|
||||
client: ThirdwebClient;
|
||||
};
|
||||
|
||||
export async function RouteListRow({
|
||||
@@ -27,7 +26,6 @@ export async function RouteListRow({
|
||||
destinationTokenAddress,
|
||||
destinationTokenIconUri,
|
||||
destinationTokenSymbol,
|
||||
client,
|
||||
}: RouteListRowProps) {
|
||||
const [
|
||||
originChain,
|
||||
@@ -42,13 +40,13 @@ export async function RouteListRow({
|
||||
originTokenIconUri
|
||||
? resolveSchemeWithErrorHandler({
|
||||
uri: originTokenIconUri,
|
||||
client,
|
||||
client: serverThirdwebClient,
|
||||
})
|
||||
: undefined,
|
||||
destinationTokenIconUri
|
||||
? resolveSchemeWithErrorHandler({
|
||||
uri: destinationTokenIconUri,
|
||||
client,
|
||||
client: serverThirdwebClient,
|
||||
})
|
||||
: undefined,
|
||||
]);
|
||||
|
||||
-4
@@ -6,7 +6,6 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { getThirdwebClient } from "@/constants/thirdweb.server";
|
||||
import type { Address } from "thirdweb";
|
||||
import { checksumAddress } from "thirdweb/utils";
|
||||
import { getRoutes } from "../../../utils";
|
||||
@@ -137,7 +136,6 @@ export async function RoutesData(props: {
|
||||
const startIndex = (activePage - 1) * pageSize;
|
||||
const endIndex = startIndex + pageSize;
|
||||
const paginatedRoutes = routesToRender.slice(startIndex, endIndex);
|
||||
const client = getThirdwebClient(undefined);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -172,7 +170,6 @@ export async function RoutesData(props: {
|
||||
destinationTokenIconUri={route.destinationToken.iconUri}
|
||||
destinationTokenSymbol={route.destinationToken.symbol}
|
||||
destinationTokenName={route.destinationToken.name}
|
||||
client={client}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
@@ -196,7 +193,6 @@ export async function RoutesData(props: {
|
||||
destinationTokenIconUri={route.destinationToken.iconUri}
|
||||
destinationTokenSymbol={route.destinationToken.symbol}
|
||||
destinationTokenName={route.destinationToken.name}
|
||||
client={client}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import "server-only";
|
||||
|
||||
import { BRIDGE_URL, DASHBOARD_THIRDWEB_SECRET_KEY } from "@/constants/env";
|
||||
import { NEXT_PUBLIC_THIRDWEB_BRIDGE_HOST } from "@/constants/public-envs";
|
||||
import { DASHBOARD_THIRDWEB_SECRET_KEY } from "@/constants/server-envs";
|
||||
import { getAuthToken } from "app/(app)/api/lib/getAuthToken";
|
||||
import type { Route } from "./types/route";
|
||||
|
||||
@@ -9,7 +10,7 @@ export async function getRoutes({
|
||||
}: {
|
||||
limit?: number;
|
||||
} = {}) {
|
||||
const url = new URL(`${BRIDGE_URL}/v1/routes`);
|
||||
const url = new URL(`${NEXT_PUBLIC_THIRDWEB_BRIDGE_HOST}/v1/routes`);
|
||||
if (limit) {
|
||||
url.searchParams.set("limit", limit.toString());
|
||||
}
|
||||
|
||||
+8
-8
@@ -20,9 +20,9 @@ import {
|
||||
} from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
THIRDWEB_ENGINE_FAUCET_WALLET,
|
||||
TURNSTILE_SITE_KEY,
|
||||
} from "@/constants/env";
|
||||
NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET,
|
||||
NEXT_PUBLIC_TURNSTILE_SITE_KEY,
|
||||
} from "@/constants/public-envs";
|
||||
import { useThirdwebClient } from "@/constants/thirdweb.client";
|
||||
import { CustomConnectWallet } from "@3rdweb-sdk/react/components/connect-wallet";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
@@ -90,7 +90,7 @@ export function FaucetButton({
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
const definedChain = mapV4ChainToV5Chain(chain);
|
||||
const faucetWalletBalanceQuery = useWalletBalance({
|
||||
address: THIRDWEB_ENGINE_FAUCET_WALLET,
|
||||
address: NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET,
|
||||
chain: definedChain,
|
||||
client,
|
||||
});
|
||||
@@ -260,7 +260,7 @@ export function FaucetButton({
|
||||
<FormControl>
|
||||
<Turnstile
|
||||
className="mt-4"
|
||||
siteKey={TURNSTILE_SITE_KEY}
|
||||
siteKey={NEXT_PUBLIC_TURNSTILE_SITE_KEY}
|
||||
onSuccess={(token) => field.onChange(token)}
|
||||
/>
|
||||
</FormControl>
|
||||
@@ -342,7 +342,7 @@ function SendFundsToFaucetModalContent(props: {
|
||||
const sendNativeTokenTx = prepareTransaction({
|
||||
chain: props.chain,
|
||||
client: props.client,
|
||||
to: THIRDWEB_ENGINE_FAUCET_WALLET,
|
||||
to: NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET,
|
||||
value: toWei(values.amount.toString()),
|
||||
});
|
||||
|
||||
@@ -370,8 +370,8 @@ function SendFundsToFaucetModalContent(props: {
|
||||
copyIconPosition="right"
|
||||
variant="outline"
|
||||
className="w-full justify-between bg-card py-2 font-mono"
|
||||
textToCopy={THIRDWEB_ENGINE_FAUCET_WALLET}
|
||||
textToShow={THIRDWEB_ENGINE_FAUCET_WALLET}
|
||||
textToCopy={NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET}
|
||||
textToShow={NEXT_PUBLIC_THIRDWEB_ENGINE_FAUCET_WALLET}
|
||||
tooltip={undefined}
|
||||
/>
|
||||
</div>
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
import { useTrack } from "hooks/analytics/useTrack";
|
||||
import { FileText } from "lucide-react";
|
||||
import { FileTextIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import type { ChainMetadata } from "thirdweb/chains";
|
||||
import { SectionTitle } from "../server/SectionTitle";
|
||||
@@ -14,7 +14,7 @@ export default function NextSteps(props: { chain: ChainMetadata }) {
|
||||
<SectionTitle title="Next Steps" />
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3">
|
||||
<div className="relative flex gap-3 rounded-lg border bg-card p-4 pr-8 transition-colors hover:border-active-border">
|
||||
<FileText className="mt-0.5 size-5 shrink-0" />
|
||||
<FileTextIcon className="mt-0.5 size-5 shrink-0" />
|
||||
<div>
|
||||
<h3 className="mb-1.5 font-medium">
|
||||
<Link
|
||||
|
||||
+5
-4
@@ -3,9 +3,10 @@
|
||||
import { CopyTextButton } from "@/components/ui/CopyTextButton";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { ToolTipLabel } from "@/components/ui/tooltip";
|
||||
import { DASHBOARD_THIRDWEB_CLIENT_ID, isProd } from "@/constants/env";
|
||||
import { isProd } from "@/constants/env-utils";
|
||||
import { NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID } from "@/constants/public-envs";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { CircleCheck, XIcon } from "lucide-react";
|
||||
import { CircleCheckIcon, XIcon } from "lucide-react";
|
||||
import { hostnameEndsWith } from "utils/url";
|
||||
import { PrimaryInfoItem } from "../server/primary-info-item";
|
||||
|
||||
@@ -13,7 +14,7 @@ function useChainStatswithRPC(_rpcUrl: string) {
|
||||
let rpcUrl = _rpcUrl.replace(
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
"${THIRDWEB_API_KEY}",
|
||||
DASHBOARD_THIRDWEB_CLIENT_ID,
|
||||
NET_PUBLIC_DASHBOARD_THIRDWEB_CLIENT_ID,
|
||||
);
|
||||
|
||||
// based on the environment hit dev or production
|
||||
@@ -67,7 +68,7 @@ export function ChainLiveStats(props: { rpc: string }) {
|
||||
titleIcon={
|
||||
stats.isSuccess ? (
|
||||
<ToolTipLabel label="Working">
|
||||
<CircleCheck className="size-4 text-success-text" />
|
||||
<CircleCheckIcon className="size-4 text-success-text" />
|
||||
</ToolTipLabel>
|
||||
) : stats.isError ? (
|
||||
<ToolTipLabel label="Not Working">
|
||||
|
||||
-3
@@ -1,6 +1,5 @@
|
||||
import { ExternalLinkIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import type { ThirdwebClient } from "thirdweb";
|
||||
import type { ChainMetadata } from "thirdweb/chains";
|
||||
import { ChainIcon } from "../../../../components/server/chain-icon";
|
||||
import { PayModalButton } from "../client/PayModal";
|
||||
@@ -9,7 +8,6 @@ import { SectionTitle } from "./SectionTitle";
|
||||
|
||||
export function BuyFundsSection(props: {
|
||||
chain: ChainMetadata;
|
||||
client: ThirdwebClient;
|
||||
}) {
|
||||
const sanitizedChainName = props.chain.name.replace("Mainnet", "").trim();
|
||||
|
||||
@@ -22,7 +20,6 @@ export function BuyFundsSection(props: {
|
||||
<ChainIcon
|
||||
className="-mr-2 size-12 rounded-full border p-1"
|
||||
iconUrl={props.chain.icon?.url}
|
||||
client={props.client}
|
||||
/>
|
||||
<CreditCardIcon
|
||||
bg="hsl(var(--background))"
|
||||
|
||||
+1
-4
@@ -1,4 +1,3 @@
|
||||
import type { ThirdwebClient } from "thirdweb";
|
||||
import type { ChainMetadata } from "thirdweb/chains";
|
||||
import { getFaucetClaimAmount } from "../../../../../../api/testnet-faucet/claim/claim-amount";
|
||||
import { ChainIcon } from "../../../../components/server/chain-icon";
|
||||
@@ -9,9 +8,8 @@ import { SectionTitle } from "./SectionTitle";
|
||||
export async function FaucetSection(props: {
|
||||
chain: ChainMetadata;
|
||||
isLoggedIn: boolean;
|
||||
client: ThirdwebClient;
|
||||
}) {
|
||||
const { chain, isLoggedIn, client } = props;
|
||||
const { chain, isLoggedIn } = props;
|
||||
|
||||
// Check eligibility.
|
||||
const sanitizedChainName = chain.name.replace("Mainnet", "").trim();
|
||||
@@ -26,7 +24,6 @@ export async function FaucetSection(props: {
|
||||
<ChainIcon
|
||||
className="-mr-2 size-12 rounded-full border p-1"
|
||||
iconUrl={props.chain.icon?.url}
|
||||
client={client}
|
||||
/>
|
||||
<GiftIcon bg="hsl(var(--background))" className="-ml-2 size-12" />
|
||||
</div>
|
||||
|
||||
-3
@@ -1,7 +1,6 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import Link from "next/link";
|
||||
import type { ThirdwebClient } from "thirdweb";
|
||||
import type { ChainMetadata } from "thirdweb/chains";
|
||||
import { mapV4ChainToV5Chain } from "../../../../../../../../contexts/map-chains";
|
||||
import { ChainIcon } from "../../../../components/server/chain-icon";
|
||||
@@ -11,7 +10,6 @@ type ChainHeaderProps = {
|
||||
headerImageUrl?: string;
|
||||
logoUrl?: string;
|
||||
chain: ChainMetadata;
|
||||
client: ThirdwebClient;
|
||||
};
|
||||
|
||||
// TODO: improve the behavior when clicking "Get started with thirdweb", currently just redirects to the dashboard
|
||||
@@ -41,7 +39,6 @@ export function ChainHeader(props: ChainHeaderProps) {
|
||||
{/* chain logo */}
|
||||
|
||||
<ChainIcon
|
||||
client={props.client}
|
||||
iconUrl={props.logoUrl}
|
||||
className={cn(
|
||||
"-translate-y-[50%] absolute top-0 left-0 size-20 overflow-hidden rounded-full border border-border bg-muted p-2 lg:size-36 lg:p-4",
|
||||
|
||||
@@ -13,11 +13,11 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
|
||||
import { ChevronDownIcon, TicketCheckIcon } from "lucide-react";
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getThirdwebClient } from "../../../../../../@/constants/thirdweb.server";
|
||||
import { mapV4ChainToV5Chain } from "../../../../../../contexts/map-chains";
|
||||
import { NebulaChatButton } from "../../../../../nebula-app/(app)/components/FloatingChat/FloatingChat";
|
||||
import {
|
||||
@@ -71,10 +71,7 @@ export default async function ChainPageLayout(props: {
|
||||
}
|
||||
|
||||
const chainMetadata = await getChainMetadata(chain.chainId);
|
||||
const client = getThirdwebClient({
|
||||
jwt: authToken,
|
||||
teamId: undefined,
|
||||
});
|
||||
const client = getClientThirdwebClient(undefined);
|
||||
|
||||
const chainPromptPrefix = `\
|
||||
You are assisting users exploring the chain ${chain.name} (Chain ID: ${chain.chainId}). Provide concise insights into the types of applications and activities prevalent on this chain, such as DeFi protocols, NFT marketplaces, or gaming platforms. Highlight notable projects or trends without delving into technical details like consensus mechanisms or gas fees.
|
||||
@@ -163,7 +160,6 @@ The following is the user's message:
|
||||
headerImageUrl={chainMetadata?.headerImgUrl}
|
||||
logoUrl={chain.icon?.url}
|
||||
chain={chain}
|
||||
client={client}
|
||||
/>
|
||||
|
||||
<div className="h-4 md:h-8" />
|
||||
|
||||
+2
-3
@@ -1,8 +1,7 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { getThirdwebClient } from "@/constants/thirdweb.server";
|
||||
import { ImageResponse } from "next/og";
|
||||
import { download } from "thirdweb/storage";
|
||||
import { fetchChain } from "utils/fetchChain";
|
||||
import { serverThirdwebClient } from "../../../../../../@/constants/thirdweb-client.server";
|
||||
|
||||
// Route segment config
|
||||
export const runtime = "edge";
|
||||
@@ -79,7 +78,7 @@ export default async function Image({
|
||||
chain.icon?.url && hasWorkingChainIcon
|
||||
? download({
|
||||
uri: chain.icon.url,
|
||||
client: getThirdwebClient(undefined),
|
||||
client: serverThirdwebClient,
|
||||
}).then((res) => res.arrayBuffer())
|
||||
: undefined,
|
||||
// download the background image (based on chain)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { CircleAlertIcon } from "lucide-react";
|
||||
import { getRawAccount } from "../../../../account/settings/getAccount";
|
||||
import { getUserThirdwebClient } from "../../../../api/lib/getAuthToken";
|
||||
import { getChain, getChainMetadata } from "../../utils";
|
||||
import NextSteps from "./components/client/NextSteps";
|
||||
import { BuyFundsSection } from "./components/server/BuyFundsSection";
|
||||
@@ -19,7 +18,6 @@ export default async function Page(props: {
|
||||
const chainMetadata = await getChainMetadata(chain.chainId);
|
||||
const isDeprecated = chain.status === "deprecated";
|
||||
|
||||
const client = await getUserThirdwebClient();
|
||||
const account = await getRawAccount();
|
||||
|
||||
return (
|
||||
@@ -44,9 +42,9 @@ export default async function Page(props: {
|
||||
|
||||
{/* Faucet / Buy Funds */}
|
||||
{chain.testnet ? (
|
||||
<FaucetSection chain={chain} isLoggedIn={!!account} client={client} />
|
||||
<FaucetSection chain={chain} isLoggedIn={!!account} />
|
||||
) : chain.services.find((c) => c.service === "pay" && c.enabled) ? (
|
||||
<BuyFundsSection chain={chain} client={client} />
|
||||
<BuyFundsSection chain={chain} />
|
||||
) : null}
|
||||
|
||||
{/* Chain Overview */}
|
||||
|
||||
+1
-1
@@ -345,7 +345,7 @@ export const CreateListingsForm: React.FC<CreateListingsFormProps> = ({
|
||||
const promise = sendAndConfirmTx.mutateAsync(approveTx);
|
||||
toast.promise(promise, {
|
||||
loading: "Approving NFT for listing",
|
||||
success: "NFT approved succesfully",
|
||||
success: "NFT approved successfully",
|
||||
error: "Failed to approve NFT",
|
||||
});
|
||||
await promise;
|
||||
|
||||
+4
-5
@@ -1,5 +1,4 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getRawAccount } from "../../../../../../account/settings/getAccount";
|
||||
import { getContractPageParamsInfo } from "../../_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "../../_utils/getContractPageMetadata";
|
||||
@@ -20,17 +19,17 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
if (info.chainMetadata.chainId === localhost.id) {
|
||||
if (info.isLocalhostChain) {
|
||||
return (
|
||||
<ContractDirectListingsPageClient
|
||||
contract={info.contract}
|
||||
contract={info.clientContract}
|
||||
isLoggedIn={!!account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const { isDirectListingSupported, isInsightSupported } =
|
||||
await getContractPageMetadata(info.contract);
|
||||
await getContractPageMetadata(info.serverContract);
|
||||
|
||||
if (!isDirectListingSupported) {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
@@ -38,7 +37,7 @@ export default async function Page(props: {
|
||||
|
||||
return (
|
||||
<ContractDirectListingsPage
|
||||
contract={info.contract}
|
||||
contract={info.clientContract}
|
||||
isLoggedIn={!!account}
|
||||
isInsightSupported={isInsightSupported}
|
||||
/>
|
||||
|
||||
+4
-5
@@ -1,5 +1,4 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getRawAccount } from "../../../../../../account/settings/getAccount";
|
||||
import { getContractPageParamsInfo } from "../../_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "../../_utils/getContractPageMetadata";
|
||||
@@ -21,17 +20,17 @@ export default async function Page(props: {
|
||||
|
||||
const twAccount = await getRawAccount();
|
||||
|
||||
if (info.chainMetadata.chainId === localhost.id) {
|
||||
if (info.isLocalhostChain) {
|
||||
return (
|
||||
<ContractEnglishAuctionsPageClient
|
||||
contract={info.contract}
|
||||
contract={info.clientContract}
|
||||
isLoggedIn={!!twAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const { isEnglishAuctionSupported, isInsightSupported } =
|
||||
await getContractPageMetadata(info.contract);
|
||||
await getContractPageMetadata(info.serverContract);
|
||||
|
||||
if (!isEnglishAuctionSupported) {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
@@ -39,7 +38,7 @@ export default async function Page(props: {
|
||||
|
||||
return (
|
||||
<ContractEnglishAuctionsPage
|
||||
contract={info.contract}
|
||||
contract={info.clientContract}
|
||||
isLoggedIn={!!twAccount}
|
||||
isInsightSupported={isInsightSupported}
|
||||
/>
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import { getContractPageMetadataSetup } from "../_utils/getContractPageMetadata";
|
||||
import { getContractPageMetadataSetup } from "../_utils/getContractPageMetadataSetup";
|
||||
|
||||
export function useContractPageMetadata(contract: ThirdwebContract) {
|
||||
return useQuery({
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ export function ConfigureCustomChain(props: {
|
||||
chainSlug: string;
|
||||
}) {
|
||||
const { chainSlug } = props;
|
||||
const isSlugNumber = Number.isInteger(Number.parseInt(chainSlug));
|
||||
const isSlugNumber = Number.isInteger(Number(chainSlug));
|
||||
const [isConfigured, setIsConfigured] = useState(false);
|
||||
|
||||
if (isConfigured) {
|
||||
|
||||
+15
-13
@@ -1,10 +1,9 @@
|
||||
import { getThirdwebClient } from "@/constants/thirdweb.server";
|
||||
import { serverThirdwebClient } from "@/constants/thirdweb-client.server";
|
||||
import { mapV4ChainToV5Chain } from "contexts/map-chains";
|
||||
import { cookies } from "next/headers";
|
||||
import { getAddress, getContract, isAddress } from "thirdweb";
|
||||
import { LAST_USED_TEAM_ID } from "../../../../../../../constants/cookies";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { fetchChainWithLocalOverrides } from "../../../../../../../utils/fetchChainWithLocalOverrides";
|
||||
import { getAuthToken } from "../../../../../api/lib/getAuthToken";
|
||||
import { getUserThirdwebClient } from "../../../../../api/lib/getAuthToken";
|
||||
|
||||
export async function getContractPageParamsInfo(params: {
|
||||
contractAddress: string;
|
||||
@@ -19,24 +18,27 @@ export async function getContractPageParamsInfo(params: {
|
||||
}
|
||||
|
||||
// attempt to get the auth token
|
||||
const token = await getAuthToken();
|
||||
const cookiesObj = await cookies();
|
||||
const teamId = cookiesObj.get(LAST_USED_TEAM_ID)?.value;
|
||||
|
||||
const contract = getContract({
|
||||
const serverContract = getContract({
|
||||
address: contractAddress,
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
chain: mapV4ChainToV5Chain(chainMetadata),
|
||||
client: serverThirdwebClient,
|
||||
});
|
||||
|
||||
const clientContract = getContract({
|
||||
address: contractAddress,
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
chain: mapV4ChainToV5Chain(chainMetadata),
|
||||
// if we have the auth token pass it into the client
|
||||
client: getThirdwebClient({
|
||||
jwt: token || undefined,
|
||||
teamId,
|
||||
}),
|
||||
client: await getUserThirdwebClient(),
|
||||
});
|
||||
|
||||
return {
|
||||
chainMetadata,
|
||||
contract,
|
||||
serverContract,
|
||||
clientContract,
|
||||
isLocalhostChain: chainMetadata.chainId === localhost.id,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+3
-73
@@ -1,26 +1,7 @@
|
||||
import { resolveFunctionSelectors } from "lib/selectors";
|
||||
import "server-only";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import { contractType as getContractType } from "thirdweb/extensions/thirdweb";
|
||||
import {
|
||||
isERC20ClaimConditionsSupported,
|
||||
isERC721ClaimConditionsSupported,
|
||||
} from "./detectedFeatures/claimConditions";
|
||||
import {
|
||||
isAccountContract,
|
||||
isAccountFactoryContract,
|
||||
isAccountPermissionsSupported,
|
||||
} from "./detectedFeatures/erc4337";
|
||||
import {
|
||||
isDirectListingSupported,
|
||||
isEnglishAuctionSupported,
|
||||
} from "./detectedFeatures/marketplace";
|
||||
import { isModularCoreContract } from "./detectedFeatures/modular";
|
||||
import {
|
||||
isPermissionsEnumerableSupported,
|
||||
isPermissionsSupported,
|
||||
} from "./detectedFeatures/permissions";
|
||||
import { supportedERCs } from "./detectedFeatures/supportedERCs";
|
||||
import { type EmbedTypeToShow, getEmbedTypeToShow } from "./getEmbedTypeToShow";
|
||||
import { getContractPageMetadataSetup } from "./getContractPageMetadataSetup";
|
||||
import type { EmbedTypeToShow } from "./getEmbedTypeToShow";
|
||||
import { isInsightSupportedForChain } from "./isAnalyticsSupportedForChain";
|
||||
|
||||
export type ContractPageMetadata = {
|
||||
@@ -49,54 +30,3 @@ export type ContractPageMetadata = {
|
||||
export async function getContractPageMetadata(contract: ThirdwebContract) {
|
||||
return getContractPageMetadataSetup(contract, isInsightSupportedForChain);
|
||||
}
|
||||
|
||||
export async function getContractPageMetadataSetup(
|
||||
contract: ThirdwebContract,
|
||||
isAnalyticsSupportedFn: (chainId: number) => Promise<boolean>,
|
||||
): Promise<ContractPageMetadata> {
|
||||
const [
|
||||
functionSelectorsResult,
|
||||
isInsightSupportedResult,
|
||||
contractTypeResult,
|
||||
] = await Promise.allSettled([
|
||||
resolveFunctionSelectors(contract),
|
||||
isAnalyticsSupportedFn(contract.chain.id),
|
||||
getContractType({ contract }),
|
||||
]);
|
||||
|
||||
const functionSelectors =
|
||||
functionSelectorsResult.status === "fulfilled"
|
||||
? functionSelectorsResult.value
|
||||
: [];
|
||||
|
||||
const isInsightSupported =
|
||||
isInsightSupportedResult.status === "fulfilled"
|
||||
? isInsightSupportedResult.value
|
||||
: true;
|
||||
|
||||
const contractType =
|
||||
contractTypeResult.status === "fulfilled" ? contractTypeResult.value : null;
|
||||
|
||||
return {
|
||||
supportedERCs: supportedERCs(functionSelectors),
|
||||
isDirectListingSupported: isDirectListingSupported(functionSelectors),
|
||||
isEnglishAuctionSupported: isEnglishAuctionSupported(functionSelectors),
|
||||
isPermissionsSupported: isPermissionsSupported(functionSelectors),
|
||||
isPermissionsEnumerableSupported:
|
||||
isPermissionsEnumerableSupported(functionSelectors),
|
||||
isModularCore: isModularCoreContract(functionSelectors),
|
||||
embedType: getEmbedTypeToShow(functionSelectors),
|
||||
isInsightSupported: isInsightSupported,
|
||||
isSplitSupported: contractType === "Split",
|
||||
isVoteContract: contractType === "VoteERC20",
|
||||
isERC721ClaimConditionsSupported:
|
||||
isERC721ClaimConditionsSupported(functionSelectors),
|
||||
isERC20ClaimConditionsSupported:
|
||||
isERC20ClaimConditionsSupported(functionSelectors),
|
||||
isAccountFactory: isAccountFactoryContract(functionSelectors),
|
||||
isAccount: isAccountContract(functionSelectors),
|
||||
isAccountPermissionsSupported:
|
||||
isAccountPermissionsSupported(functionSelectors),
|
||||
functionSelectors,
|
||||
};
|
||||
}
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
import { resolveFunctionSelectors } from "lib/selectors";
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import { contractType as getContractType } from "thirdweb/extensions/thirdweb";
|
||||
import {
|
||||
isERC20ClaimConditionsSupported,
|
||||
isERC721ClaimConditionsSupported,
|
||||
} from "./detectedFeatures/claimConditions";
|
||||
import {
|
||||
isAccountContract,
|
||||
isAccountFactoryContract,
|
||||
isAccountPermissionsSupported,
|
||||
} from "./detectedFeatures/erc4337";
|
||||
import {
|
||||
isDirectListingSupported,
|
||||
isEnglishAuctionSupported,
|
||||
} from "./detectedFeatures/marketplace";
|
||||
import { isModularCoreContract } from "./detectedFeatures/modular";
|
||||
import {
|
||||
isPermissionsEnumerableSupported,
|
||||
isPermissionsSupported,
|
||||
} from "./detectedFeatures/permissions";
|
||||
import { supportedERCs } from "./detectedFeatures/supportedERCs";
|
||||
import { type EmbedTypeToShow, getEmbedTypeToShow } from "./getEmbedTypeToShow";
|
||||
|
||||
type ContractPageMetadata = {
|
||||
supportedERCs: {
|
||||
isERC721: boolean;
|
||||
isERC1155: boolean;
|
||||
isERC20: boolean;
|
||||
};
|
||||
isDirectListingSupported: boolean;
|
||||
isEnglishAuctionSupported: boolean;
|
||||
isPermissionsSupported: boolean;
|
||||
isVoteContract: boolean;
|
||||
isPermissionsEnumerableSupported: boolean;
|
||||
isModularCore: boolean;
|
||||
embedType: EmbedTypeToShow;
|
||||
isInsightSupported: boolean;
|
||||
isSplitSupported: boolean;
|
||||
isERC721ClaimConditionsSupported: boolean;
|
||||
isERC20ClaimConditionsSupported: boolean;
|
||||
isAccountFactory: boolean;
|
||||
isAccount: boolean;
|
||||
isAccountPermissionsSupported: boolean;
|
||||
functionSelectors: string[];
|
||||
};
|
||||
|
||||
export async function getContractPageMetadataSetup(
|
||||
contract: ThirdwebContract,
|
||||
isAnalyticsSupportedFn: (chainId: number) => Promise<boolean>,
|
||||
): Promise<ContractPageMetadata> {
|
||||
const [
|
||||
functionSelectorsResult,
|
||||
isInsightSupportedResult,
|
||||
contractTypeResult,
|
||||
] = await Promise.allSettled([
|
||||
resolveFunctionSelectors(contract),
|
||||
isAnalyticsSupportedFn(contract.chain.id),
|
||||
getContractType({ contract }),
|
||||
]);
|
||||
|
||||
const functionSelectors =
|
||||
functionSelectorsResult.status === "fulfilled"
|
||||
? functionSelectorsResult.value
|
||||
: [];
|
||||
|
||||
const isInsightSupported =
|
||||
isInsightSupportedResult.status === "fulfilled"
|
||||
? isInsightSupportedResult.value
|
||||
: true;
|
||||
|
||||
const contractType =
|
||||
contractTypeResult.status === "fulfilled" ? contractTypeResult.value : null;
|
||||
|
||||
return {
|
||||
supportedERCs: supportedERCs(functionSelectors),
|
||||
isDirectListingSupported: isDirectListingSupported(functionSelectors),
|
||||
isEnglishAuctionSupported: isEnglishAuctionSupported(functionSelectors),
|
||||
isPermissionsSupported: isPermissionsSupported(functionSelectors),
|
||||
isPermissionsEnumerableSupported:
|
||||
isPermissionsEnumerableSupported(functionSelectors),
|
||||
isModularCore: isModularCoreContract(functionSelectors),
|
||||
embedType: getEmbedTypeToShow(functionSelectors),
|
||||
isInsightSupported: isInsightSupported,
|
||||
isSplitSupported: contractType === "Split",
|
||||
isVoteContract: contractType === "VoteERC20",
|
||||
isERC721ClaimConditionsSupported:
|
||||
isERC721ClaimConditionsSupported(functionSelectors),
|
||||
isERC20ClaimConditionsSupported:
|
||||
isERC20ClaimConditionsSupported(functionSelectors),
|
||||
isAccountFactory: isAccountFactoryContract(functionSelectors),
|
||||
isAccount: isAccountContract(functionSelectors),
|
||||
isAccountPermissionsSupported:
|
||||
isAccountPermissionsSupported(functionSelectors),
|
||||
functionSelectors,
|
||||
};
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { INSIGHT_SERVICE_API_KEY } from "@/constants/env";
|
||||
import { INSIGHT_SERVICE_API_KEY } from "@/constants/server-envs";
|
||||
import { getVercelEnv } from "lib/vercel-utils";
|
||||
|
||||
const thirdwebDomain =
|
||||
|
||||
+5
-6
@@ -1,5 +1,4 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
|
||||
import { AccountSignersClient } from "./AccountSigners.client";
|
||||
@@ -18,18 +17,18 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const { contract, chainMetadata } = info;
|
||||
const { clientContract, serverContract, isLocalhostChain } = info;
|
||||
|
||||
if (chainMetadata.chainId === localhost.id) {
|
||||
return <AccountSignersClient contract={contract} />;
|
||||
if (isLocalhostChain) {
|
||||
return <AccountSignersClient contract={clientContract} />;
|
||||
}
|
||||
|
||||
const { isAccountPermissionsSupported } =
|
||||
await getContractPageMetadata(contract);
|
||||
await getContractPageMetadata(serverContract);
|
||||
|
||||
if (!isAccountPermissionsSupported) {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
}
|
||||
|
||||
return <AccountSigners contract={contract} />;
|
||||
return <AccountSigners contract={clientContract} />;
|
||||
}
|
||||
|
||||
+6
-6
@@ -1,5 +1,4 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getRawAccount } from "../../../../../account/settings/getAccount";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
|
||||
@@ -19,14 +18,15 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const { contract, chainMetadata } = info;
|
||||
const { clientContract, serverContract, chainMetadata, isLocalhostChain } =
|
||||
info;
|
||||
|
||||
const account = await getRawAccount();
|
||||
|
||||
if (contract.chain.id === localhost.id) {
|
||||
if (isLocalhostChain) {
|
||||
return (
|
||||
<AccountPageClient
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
chainMetadata={chainMetadata}
|
||||
isLoggedIn={!!account}
|
||||
/>
|
||||
@@ -34,7 +34,7 @@ export default async function Page(props: {
|
||||
}
|
||||
|
||||
const { isAccount, isInsightSupported } =
|
||||
await getContractPageMetadata(contract);
|
||||
await getContractPageMetadata(serverContract);
|
||||
|
||||
if (!isAccount) {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
@@ -42,7 +42,7 @@ export default async function Page(props: {
|
||||
|
||||
return (
|
||||
<AccountPage
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
chainMetadata={chainMetadata}
|
||||
isLoggedIn={!!account}
|
||||
isInsightSupported={isInsightSupported}
|
||||
|
||||
+12
-8
@@ -1,5 +1,4 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getRawAccount } from "../../../../../account/settings/getAccount";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
|
||||
@@ -19,19 +18,24 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const { contract, chainMetadata } = info;
|
||||
const { clientContract, serverContract, isLocalhostChain } = info;
|
||||
|
||||
const account = await getRawAccount();
|
||||
const [account, { isAccountFactory }] = await Promise.all([
|
||||
getRawAccount(),
|
||||
isLocalhostChain
|
||||
? { isAccountFactory: undefined }
|
||||
: getContractPageMetadata(serverContract),
|
||||
]);
|
||||
|
||||
if (chainMetadata.chainId === localhost.id) {
|
||||
return <AccountsPageClient contract={contract} isLoggedIn={!!account} />;
|
||||
if (isLocalhostChain) {
|
||||
return (
|
||||
<AccountsPageClient contract={clientContract} isLoggedIn={!!account} />
|
||||
);
|
||||
}
|
||||
|
||||
const { isAccountFactory } = await getContractPageMetadata(contract);
|
||||
|
||||
if (!isAccountFactory) {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
}
|
||||
|
||||
return <AccountsPage contract={contract} isLoggedIn={!!account} />;
|
||||
return <AccountsPage contract={clientContract} isLoggedIn={!!account} />;
|
||||
}
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import type { ThirdwebContract } from "thirdweb";
|
||||
import { ErrorPage, LoadingPage } from "../_components/page-skeletons";
|
||||
import { RedirectToContractOverview } from "../_components/redirect-contract-overview.client";
|
||||
import { useContractPageMetadata } from "../_hooks/useContractPageMetadata";
|
||||
import { ContractAnalyticsPage } from "./ContractAnalyticsPage";
|
||||
|
||||
export function ContractAnalyticsPageClient(props: {
|
||||
contract: ThirdwebContract;
|
||||
writeFnSelectorToNameRecord: Record<string, string>;
|
||||
eventSelectorToNameRecord: Record<string, string>;
|
||||
}) {
|
||||
const metadataQuery = useContractPageMetadata(props.contract);
|
||||
|
||||
if (metadataQuery.isPending) {
|
||||
return <LoadingPage />;
|
||||
}
|
||||
|
||||
if (metadataQuery.isError) {
|
||||
return <ErrorPage />;
|
||||
}
|
||||
|
||||
if (!metadataQuery.data.isInsightSupported) {
|
||||
return <RedirectToContractOverview contract={props.contract} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ContractAnalyticsPage
|
||||
contract={props.contract}
|
||||
writeFnSelectorToNameRecord={props.writeFnSelectorToNameRecord}
|
||||
eventSelectorToNameRecord={props.eventSelectorToNameRecord}
|
||||
/>
|
||||
);
|
||||
}
|
||||
+8
-18
@@ -1,11 +1,9 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { type ThirdwebContract, resolveContractAbi } from "thirdweb/contract";
|
||||
import { type Abi, toEventSelector, toFunctionSelector } from "thirdweb/utils";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "../_utils/getContractPageMetadata";
|
||||
import { ContractAnalyticsPage } from "./ContractAnalyticsPage";
|
||||
import { ContractAnalyticsPageClient } from "./ContractAnalyticsPage.client";
|
||||
|
||||
export default async function Page(props: {
|
||||
params: Promise<{
|
||||
@@ -20,21 +18,13 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const { eventSelectorToName, writeFnSelectorToName } = await getSelectors(
|
||||
info.contract,
|
||||
);
|
||||
|
||||
if (info.chainMetadata.chainId === localhost.id) {
|
||||
return (
|
||||
<ContractAnalyticsPageClient
|
||||
contract={info.contract}
|
||||
writeFnSelectorToNameRecord={writeFnSelectorToName}
|
||||
eventSelectorToNameRecord={eventSelectorToName}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const { isInsightSupported } = await getContractPageMetadata(info.contract);
|
||||
const [
|
||||
{ eventSelectorToName, writeFnSelectorToName },
|
||||
{ isInsightSupported },
|
||||
] = await Promise.all([
|
||||
getSelectors(info.serverContract),
|
||||
getContractPageMetadata(info.serverContract),
|
||||
]);
|
||||
|
||||
if (!isInsightSupported) {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
@@ -42,7 +32,7 @@ export default async function Page(props: {
|
||||
|
||||
return (
|
||||
<ContractAnalyticsPage
|
||||
contract={info.contract}
|
||||
contract={info.clientContract}
|
||||
writeFnSelectorToNameRecord={writeFnSelectorToName}
|
||||
eventSelectorToNameRecord={eventSelectorToName}
|
||||
/>
|
||||
|
||||
+23
-12
@@ -1,5 +1,4 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getRawAccount } from "../../../../../account/settings/getAccount";
|
||||
import { ClaimConditions } from "../_components/claim-conditions/claim-conditions";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
@@ -18,27 +17,39 @@ export default async function Page(props: {
|
||||
if (!info) {
|
||||
notFound();
|
||||
}
|
||||
const { contract, chainMetadata } = info;
|
||||
const { clientContract, serverContract, isLocalhostChain } = info;
|
||||
|
||||
const account = await getRawAccount();
|
||||
const [
|
||||
account,
|
||||
{
|
||||
isERC20ClaimConditionsSupported,
|
||||
isERC721ClaimConditionsSupported,
|
||||
supportedERCs,
|
||||
},
|
||||
] = await Promise.all([
|
||||
getRawAccount(),
|
||||
isLocalhostChain
|
||||
? {
|
||||
isERC20ClaimConditionsSupported: undefined,
|
||||
isERC721ClaimConditionsSupported: undefined,
|
||||
supportedERCs: undefined,
|
||||
}
|
||||
: getContractPageMetadata(serverContract),
|
||||
]);
|
||||
|
||||
if (chainMetadata.chainId === localhost.id) {
|
||||
return <ClaimConditionsClient contract={contract} isLoggedIn={!!account} />;
|
||||
if (isLocalhostChain) {
|
||||
return (
|
||||
<ClaimConditionsClient contract={clientContract} isLoggedIn={!!account} />
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
isERC20ClaimConditionsSupported,
|
||||
isERC721ClaimConditionsSupported,
|
||||
supportedERCs,
|
||||
} = await getContractPageMetadata(contract);
|
||||
|
||||
if (!isERC20ClaimConditionsSupported && !isERC721ClaimConditionsSupported) {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
}
|
||||
|
||||
return (
|
||||
<ClaimConditions
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
isERC20={supportedERCs.isERC20}
|
||||
isLoggedIn={!!account}
|
||||
isMultiphase={true}
|
||||
|
||||
+6
-6
@@ -1,5 +1,4 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { resolveContractAbi } from "thirdweb/contract";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
import { ContractCodePage } from "./contract-code-page";
|
||||
@@ -18,23 +17,24 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const { contract, chainMetadata } = info;
|
||||
const { clientContract, serverContract, chainMetadata, isLocalhostChain } =
|
||||
info;
|
||||
|
||||
if (contract.chain.id === localhost.id) {
|
||||
if (isLocalhostChain) {
|
||||
return (
|
||||
<ContractCodePageClient
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
chainMetadata={chainMetadata}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const abi = await resolveContractAbi(contract).catch(() => undefined);
|
||||
const abi = await resolveContractAbi(serverContract).catch(() => undefined);
|
||||
|
||||
return (
|
||||
<ContractCodePage
|
||||
abi={abi}
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
chainMetadata={chainMetadata}
|
||||
/>
|
||||
);
|
||||
|
||||
+24
-21
@@ -53,27 +53,28 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const { contract } = info;
|
||||
const { clientContract, serverContract } = info;
|
||||
|
||||
const isModularCore = (await getContractPageMetadata(contract)).isModularCore;
|
||||
const isModularCore = (await getContractPageMetadata(serverContract))
|
||||
.isModularCore;
|
||||
|
||||
let twCloneFactoryContract: Readonly<
|
||||
ContractOptions<[], `0x${string}`>
|
||||
> | null = null;
|
||||
try {
|
||||
twCloneFactoryContract = await getDeployedCloneFactoryContract({
|
||||
chain: contract.chain,
|
||||
client: contract.client,
|
||||
chain: serverContract.chain,
|
||||
client: serverContract.client,
|
||||
});
|
||||
} catch {}
|
||||
|
||||
const originalCode = await eth_getCode(
|
||||
getRpcClient({
|
||||
client: contract.client,
|
||||
chain: contract.chain,
|
||||
client: serverContract.client,
|
||||
chain: serverContract.chain,
|
||||
}),
|
||||
{
|
||||
address: contract.address,
|
||||
address: serverContract.address,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -82,14 +83,14 @@ export default async function Page(props: {
|
||||
let isDirectDeploy = false;
|
||||
try {
|
||||
const res = await fetch(
|
||||
`https://contract.thirdweb-dev.com/creation/${contract.chain.id}/${contract.address}`,
|
||||
`https://contract.thirdweb-dev.com/creation/${serverContract.chain.id}/${serverContract.address}`,
|
||||
);
|
||||
const creationData = await res.json();
|
||||
|
||||
if (creationData.status === "1" && creationData.result[0]?.txHash) {
|
||||
const rpcClient = getRpcClient({
|
||||
client: contract.client,
|
||||
chain: contract.chain,
|
||||
client: serverContract.client,
|
||||
chain: serverContract.chain,
|
||||
});
|
||||
creationTxReceipt = await eth_getTransactionReceipt(rpcClient, {
|
||||
hash: creationData.result[0]?.txHash,
|
||||
@@ -141,7 +142,7 @@ export default async function Page(props: {
|
||||
const chainMetadata = await getChainMetadata(chain);
|
||||
|
||||
const rpcRequest = getRpcClient({
|
||||
client: contract.client,
|
||||
client: serverContract.client,
|
||||
chain,
|
||||
});
|
||||
const code = await eth_getCode(rpcRequest, {
|
||||
@@ -167,14 +168,14 @@ export default async function Page(props: {
|
||||
}
|
||||
}),
|
||||
)
|
||||
).filter((c) => c.chainId !== contract.chain.id);
|
||||
).filter((c) => c.chainId !== serverContract.chain.id);
|
||||
|
||||
let coreMetadata: FetchDeployMetadataResult | undefined;
|
||||
try {
|
||||
coreMetadata = (
|
||||
await fetchPublishedContractsFromDeploy({
|
||||
contract,
|
||||
client: contract.client,
|
||||
contract: serverContract,
|
||||
client: serverContract.client,
|
||||
})
|
||||
).at(-1) as FetchDeployMetadataResult;
|
||||
} catch {}
|
||||
@@ -201,7 +202,7 @@ export default async function Page(props: {
|
||||
// fetch events from contract
|
||||
if (!modules && creationBlockNumber) {
|
||||
const events = await getContractEvents({
|
||||
contract: contract,
|
||||
contract: serverContract,
|
||||
events: [moduleEvent],
|
||||
});
|
||||
|
||||
@@ -216,11 +217,13 @@ export default async function Page(props: {
|
||||
if (!modules && initializeData) {
|
||||
// biome-ignore lint/suspicious/noExplicitAny: FIXME
|
||||
const decodedData: any = await decodeFunctionData({
|
||||
contract,
|
||||
contract: serverContract,
|
||||
data: initializeData,
|
||||
});
|
||||
|
||||
const abi = await resolveContractAbi(contract).catch(() => undefined);
|
||||
const abi = await resolveContractAbi(serverContract).catch(
|
||||
() => undefined,
|
||||
);
|
||||
|
||||
if (abi) {
|
||||
const initializeFunction = abi.find(
|
||||
@@ -241,11 +244,11 @@ export default async function Page(props: {
|
||||
(
|
||||
await fetchPublishedContractsFromDeploy({
|
||||
contract: getContract({
|
||||
chain: contract.chain,
|
||||
client: contract.client,
|
||||
chain: serverContract.chain,
|
||||
client: serverContract.client,
|
||||
address: m,
|
||||
}),
|
||||
client: contract.client,
|
||||
client: serverContract.client,
|
||||
})
|
||||
).at(-1),
|
||||
),
|
||||
@@ -275,7 +278,7 @@ export default async function Page(props: {
|
||||
initializeData={initializeData}
|
||||
inputSalt={inputSalt}
|
||||
data={chainsDeployedOn}
|
||||
coreContract={contract}
|
||||
coreContract={clientContract}
|
||||
initCode={initCode}
|
||||
isDirectDeploy={isDirectDeploy}
|
||||
/>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user