Compare commits
82
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 |
@@ -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
|
||||
|
||||
@@ -123,6 +123,7 @@ const baseNextConfig: NextConfig = {
|
||||
webpackBuildWorker: true,
|
||||
webpackMemoryOptimizations: true,
|
||||
serverSourceMaps: false,
|
||||
taint: true,
|
||||
},
|
||||
serverExternalPackages: ["pino-pretty"],
|
||||
async 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 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, THIRDWEB_ENGINE_CLOUD_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,22 +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 engineCloudProxy<T>(params: ProxyActionParams) {
|
||||
return proxy<T>(THIRDWEB_ENGINE_CLOUD_URL, 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,
|
||||
);
|
||||
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));
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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,7 +128,7 @@ export const SelectWithSearch = React.forwardRef<
|
||||
<span
|
||||
className={cn(
|
||||
"truncate text-muted-foreground text-sm",
|
||||
selectedOption && "text-foreground",
|
||||
selectedOption && "w-full text-foreground",
|
||||
)}
|
||||
>
|
||||
{renderOption && selectedOption
|
||||
@@ -193,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}
|
||||
|
||||
@@ -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> }
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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,62 +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_VAULT_URL =
|
||||
process.env.NEXT_PUBLIC_THIRDWEB_VAULT_URL || "";
|
||||
|
||||
export const THIRDWEB_ENGINE_CLOUD_URL =
|
||||
process.env.NEXT_PUBLIC_ENGINE_CLOUD_URL || "";
|
||||
|
||||
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,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -39,9 +39,10 @@ export function getFiltersFromSearchParams(params: {
|
||||
};
|
||||
|
||||
const defaultInterval =
|
||||
differenceInCalendarDays(range.to, range.from) > 30
|
||||
? "week"
|
||||
: ("day" as const);
|
||||
params.interval ??
|
||||
(differenceInCalendarDays(range.to, range.from) > 30
|
||||
? ("week" as const)
|
||||
: ("day" as const));
|
||||
|
||||
return {
|
||||
range,
|
||||
@@ -50,6 +51,6 @@ export function getFiltersFromSearchParams(params: {
|
||||
? ("day" as const)
|
||||
: params.interval === "week"
|
||||
? ("week" as const)
|
||||
: defaultInterval,
|
||||
: (defaultInterval as "day" | "week"),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
+3
-2
@@ -3,7 +3,8 @@
|
||||
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 { CircleCheckIcon, XIcon } from "lucide-react";
|
||||
import { hostnameEndsWith } from "utils/url";
|
||||
@@ -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
|
||||
|
||||
-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,7 +13,7 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { getThirdwebClient } from "@/constants/thirdweb.server";
|
||||
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
|
||||
import { ChevronDownIcon, TicketCheckIcon } from "lucide-react";
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
@@ -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 */}
|
||||
|
||||
+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}
|
||||
/>
|
||||
|
||||
+1
-1
@@ -29,7 +29,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());
|
||||
|
||||
+1
-1
@@ -15,5 +15,5 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return <EventsFeed contract={info.contract} />;
|
||||
return <EventsFeed contract={info.clientContract} />;
|
||||
}
|
||||
|
||||
+6
-6
@@ -1,5 +1,4 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { resolveContractAbi } from "thirdweb/contract";
|
||||
import { getRawAccount } from "../../../../../account/settings/getAccount";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
@@ -19,25 +18,26 @@ 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 (
|
||||
<ContractExplorerPageClient
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
chainMetadata={chainMetadata}
|
||||
isLoggedIn={!!account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const abi = await resolveContractAbi(contract).catch(() => undefined);
|
||||
const abi = await resolveContractAbi(serverContract).catch(() => undefined);
|
||||
|
||||
return (
|
||||
<ContractExplorerPage
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
abi={abi}
|
||||
chainMetadata={chainMetadata}
|
||||
isLoggedIn={!!account}
|
||||
|
||||
+17
-15
@@ -4,7 +4,6 @@ import type { MinimalTeamsAndProjects } from "components/contract-components/con
|
||||
import { resolveFunctionSelectors } from "lib/selectors";
|
||||
import type { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getContractMetadata } from "thirdweb/extensions/common";
|
||||
import { isAddress, isContractDeployed } from "thirdweb/utils";
|
||||
import { shortenIfAddress } from "utils/usedapp-external";
|
||||
@@ -40,7 +39,8 @@ export default async function Layout(props: {
|
||||
return <ConfigureCustomChain chainSlug={params.chain_id} />;
|
||||
}
|
||||
|
||||
const { contract, chainMetadata } = info;
|
||||
const { clientContract, serverContract, chainMetadata, isLocalhostChain } =
|
||||
info;
|
||||
|
||||
if (chainMetadata.status === "deprecated") {
|
||||
notFound();
|
||||
@@ -51,11 +51,11 @@ export default async function Layout(props: {
|
||||
const client = await getUserThirdwebClient();
|
||||
const teamsAndProjects = await getTeamsAndProjectsIfLoggedIn();
|
||||
|
||||
if (contract.chain.id === localhost.id) {
|
||||
if (isLocalhostChain) {
|
||||
return (
|
||||
<ContractPageLayoutClient
|
||||
chainMetadata={chainMetadata}
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
teamsAndProjects={teamsAndProjects}
|
||||
client={client}
|
||||
>
|
||||
@@ -65,25 +65,27 @@ export default async function Layout(props: {
|
||||
}
|
||||
|
||||
// check if the contract exists
|
||||
const isValidContract = await isContractDeployed(contract).catch(() => false);
|
||||
const isValidContract = await isContractDeployed(serverContract).catch(
|
||||
() => false,
|
||||
);
|
||||
if (!isValidContract) {
|
||||
// TODO - replace 404 with a better page to upsell deploy or other thirdweb products
|
||||
notFound();
|
||||
}
|
||||
const contractPageMetadata = await getContractPageMetadata(contract);
|
||||
const contractPageMetadata = await getContractPageMetadata(serverContract);
|
||||
|
||||
const sidebarLinks = getContractPageSidebarLinks({
|
||||
chainSlug: chainMetadata.slug,
|
||||
contractAddress: contract.address,
|
||||
contractAddress: serverContract.address,
|
||||
metadata: contractPageMetadata,
|
||||
});
|
||||
|
||||
const { contractMetadata, externalLinks } =
|
||||
await getContractMetadataHeaderData(contract);
|
||||
await getContractMetadataHeaderData(serverContract);
|
||||
|
||||
const contractAddress = info.contract.address;
|
||||
const chainName = info.chainMetadata.name;
|
||||
const chainId = info.contract.chain.id;
|
||||
const contractAddress = serverContract.address;
|
||||
const chainName = chainMetadata.name;
|
||||
const chainId = chainMetadata.chainId;
|
||||
|
||||
const contractPromptPrefix = `A user is viewing the contract address ${contractAddress} on ${chainName} (Chain ID: ${chainId}). Provide a concise summary of this contract's functionalities, such as token minting, staking, or governance mechanisms. Focus on what the contract enables users to do, avoiding transaction execution details unless requested.
|
||||
Users may be interested in how to interact with the contract. Outline common interaction patterns, such as claiming rewards, participating in governance, or transferring assets. Emphasize the contract's capabilities without guiding through transaction processes unless asked.
|
||||
@@ -103,7 +105,7 @@ The following is the user's message:`;
|
||||
return (
|
||||
<ContractPageLayout
|
||||
chainMetadata={chainMetadata}
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
sidebarLinks={sidebarLinks}
|
||||
dashboardContractMetadata={contractMetadata}
|
||||
externalLinks={externalLinks}
|
||||
@@ -119,7 +121,7 @@ The following is the user's message:`;
|
||||
client={client}
|
||||
nebulaParams={{
|
||||
messagePrefix: contractPromptPrefix,
|
||||
chainIds: [info.contract.chain.id],
|
||||
chainIds: [chainId],
|
||||
wallet: accountAddress ?? undefined,
|
||||
}}
|
||||
examplePrompts={examplePrompts.map((prompt) => ({
|
||||
@@ -177,9 +179,9 @@ export async function generateMetadata(props: {
|
||||
}
|
||||
|
||||
const [functionSelectors, contractMetadata] = await Promise.all([
|
||||
resolveFunctionSelectors(info.contract),
|
||||
resolveFunctionSelectors(info.serverContract),
|
||||
getContractMetadata({
|
||||
contract: info.contract,
|
||||
contract: info.serverContract,
|
||||
}),
|
||||
]);
|
||||
|
||||
|
||||
+7
-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,24 +18,26 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const { contract } = info;
|
||||
const { clientContract, serverContract, isLocalhostChain } = info;
|
||||
|
||||
const account = await getRawAccount();
|
||||
|
||||
if (contract.chain.id === localhost.id) {
|
||||
if (isLocalhostChain) {
|
||||
return (
|
||||
<ContractEditModulesPageClient
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
isLoggedIn={!!account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const { isModularCore } = await getContractPageMetadata(contract);
|
||||
const { isModularCore } = await getContractPageMetadata(serverContract);
|
||||
|
||||
if (!isModularCore) {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
}
|
||||
|
||||
return <ContractEditModulesPage contract={contract} isLoggedIn={!!account} />;
|
||||
return (
|
||||
<ContractEditModulesPage contract={clientContract} isLoggedIn={!!account} />
|
||||
);
|
||||
}
|
||||
|
||||
+5
-6
@@ -1,5 +1,4 @@
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getRawAccount } from "../../../../../../account/settings/getAccount";
|
||||
import { getAuthTokenWalletAddress } from "../../../../../../api/lib/getAuthToken";
|
||||
import { getContractPageParamsInfo } from "../../_utils/getContractFromParams";
|
||||
@@ -30,11 +29,11 @@ export default async function Page(props: {
|
||||
getRawAccount(),
|
||||
]);
|
||||
|
||||
const { contract } = info;
|
||||
if (contract.chain.id === localhost.id) {
|
||||
const { clientContract, serverContract, isLocalhostChain } = info;
|
||||
if (isLocalhostChain) {
|
||||
return (
|
||||
<TokenIdPageClient
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
tokenId={params.tokenId}
|
||||
isLoggedIn={!!account}
|
||||
accountAddress={accountAddress || undefined}
|
||||
@@ -42,7 +41,7 @@ export default async function Page(props: {
|
||||
);
|
||||
}
|
||||
|
||||
const { supportedERCs } = await getContractPageMetadata(contract);
|
||||
const { supportedERCs } = await getContractPageMetadata(serverContract);
|
||||
|
||||
if (!supportedERCs.isERC721 && !supportedERCs.isERC1155) {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
@@ -50,7 +49,7 @@ export default async function Page(props: {
|
||||
|
||||
return (
|
||||
<TokenIdPage
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
isErc721={supportedERCs.isERC721}
|
||||
tokenId={params.tokenId}
|
||||
isLoggedIn={!!account}
|
||||
|
||||
+7
-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";
|
||||
@@ -21,13 +20,15 @@ export default async function Page(props: {
|
||||
|
||||
const account = await getRawAccount();
|
||||
|
||||
const { contract } = info;
|
||||
if (contract.chain.id === localhost.id) {
|
||||
return <ContractNFTPageClient contract={contract} isLoggedIn={!!account} />;
|
||||
const { clientContract, serverContract, isLocalhostChain } = info;
|
||||
if (isLocalhostChain) {
|
||||
return (
|
||||
<ContractNFTPageClient contract={clientContract} isLoggedIn={!!account} />
|
||||
);
|
||||
}
|
||||
|
||||
const { supportedERCs, functionSelectors } =
|
||||
await getContractPageMetadata(contract);
|
||||
await getContractPageMetadata(serverContract);
|
||||
|
||||
if (!supportedERCs.isERC721 && !supportedERCs.isERC1155) {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
@@ -35,7 +36,7 @@ export default async function Page(props: {
|
||||
|
||||
return (
|
||||
<ContractNFTPage
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
isErc721={supportedERCs.isERC721}
|
||||
functionSelectors={functionSelectors}
|
||||
isLoggedIn={!!account}
|
||||
|
||||
+6
-5
@@ -1,5 +1,6 @@
|
||||
import { isProd } from "@/constants/env-utils";
|
||||
import { API_ROUTES_CLIENT_ID } from "@/constants/server-envs";
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import { isProd } from "@/constants/env";
|
||||
import { ImageResponse } from "@vercel/og";
|
||||
import { getContractMetadata } from "thirdweb/extensions/common";
|
||||
import { getContractPageParamsInfo } from "./_utils/getContractFromParams";
|
||||
@@ -27,7 +28,7 @@ export default async function Image({
|
||||
}
|
||||
|
||||
const contractMetadata = await getContractMetadata({
|
||||
contract: info.contract,
|
||||
contract: info.serverContract,
|
||||
});
|
||||
|
||||
if (contractMetadata.name === null) {
|
||||
@@ -40,7 +41,7 @@ export default async function Image({
|
||||
|
||||
return contractOGImageTemplate({
|
||||
displayName: contractDisplayName,
|
||||
contractAddress: info.contract.address,
|
||||
contractAddress: info.serverContract.address,
|
||||
chainName: info.chainMetadata.name,
|
||||
logo: contractMetadata.image,
|
||||
});
|
||||
@@ -125,8 +126,8 @@ function textShortener(text: string) {
|
||||
return `${words.join(" ")} ...`;
|
||||
}
|
||||
|
||||
const IPFS_GATEWAY = process.env.API_ROUTES_CLIENT_ID
|
||||
? `https://${process.env.API_ROUTES_CLIENT_ID}.${isProd ? "ipfscdn.io/ipfs/" : "thirdwebstorage-dev.com/ipfs/"}`
|
||||
const IPFS_GATEWAY = API_ROUTES_CLIENT_ID
|
||||
? `https://${API_ROUTES_CLIENT_ID}.${isProd ? "ipfscdn.io/ipfs/" : "thirdwebstorage-dev.com/ipfs/"}`
|
||||
: "https://ipfs.io/ipfs/";
|
||||
|
||||
function replaceAnyIpfsUrlWithGateway(url: string) {
|
||||
|
||||
-2
@@ -147,7 +147,6 @@ export function PublishedByUI(props: {
|
||||
publisher: string;
|
||||
version: string | undefined;
|
||||
isBeta: boolean;
|
||||
client: ThirdwebClient;
|
||||
}) {
|
||||
return (
|
||||
<ContractCard
|
||||
@@ -160,7 +159,6 @@ export function PublishedByUI(props: {
|
||||
moduleId: m.name,
|
||||
version: m.version,
|
||||
}))}
|
||||
client={props.client}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
-1
@@ -25,7 +25,6 @@ export const PublishedBy: React.FC<PublishedByProps> = async ({ contract }) => {
|
||||
name={props.name}
|
||||
publisher={props.publisher}
|
||||
version={props.version}
|
||||
client={contract.client}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+7
-7
@@ -1,6 +1,5 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getContractPageParamsInfo } from "./_utils/getContractFromParams";
|
||||
import { getContractPageMetadata } from "./_utils/getContractPageMetadata";
|
||||
import { ContractOverviewPage } from "./overview/ContractOverviewPage";
|
||||
@@ -20,21 +19,22 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const { contract, chainMetadata } = info;
|
||||
if (contract.chain.id === localhost.id) {
|
||||
const { clientContract, serverContract, chainMetadata, isLocalhostChain } =
|
||||
info;
|
||||
if (isLocalhostChain) {
|
||||
return (
|
||||
<ContractOverviewPageClient
|
||||
chainMetadata={chainMetadata}
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const contractPageMetadata = await getContractPageMetadata(contract);
|
||||
const contractPageMetadata = await getContractPageMetadata(serverContract);
|
||||
|
||||
return (
|
||||
<ContractOverviewPage
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
hasDirectListings={contractPageMetadata.isDirectListingSupported}
|
||||
hasEnglishAuctions={contractPageMetadata.isEnglishAuctionSupported}
|
||||
isErc1155={contractPageMetadata.supportedERCs.isERC1155}
|
||||
@@ -48,7 +48,7 @@ export default async function Page(props: {
|
||||
functionSelectors={contractPageMetadata.functionSelectors}
|
||||
publishedBy={
|
||||
<ErrorBoundary fallback={null}>
|
||||
<PublishedBy contract={contract} />
|
||||
<PublishedBy contract={serverContract} />
|
||||
</ErrorBoundary>
|
||||
}
|
||||
/>
|
||||
|
||||
+5
-6
@@ -1,5 +1,4 @@
|
||||
import { notFound } 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,11 +20,11 @@ export default async function Page(props: {
|
||||
|
||||
const account = await getRawAccount();
|
||||
|
||||
const { contract } = info;
|
||||
if (contract.chain.id === localhost.id) {
|
||||
const { clientContract, serverContract, isLocalhostChain } = info;
|
||||
if (isLocalhostChain) {
|
||||
return (
|
||||
<ContractPermissionsPageClient
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
chainMetadata={info.chainMetadata}
|
||||
isLoggedIn={!!account}
|
||||
/>
|
||||
@@ -33,11 +32,11 @@ export default async function Page(props: {
|
||||
}
|
||||
|
||||
const { isPermissionsEnumerableSupported } =
|
||||
await getContractPageMetadata(contract);
|
||||
await getContractPageMetadata(serverContract);
|
||||
|
||||
return (
|
||||
<ContractPermissionsPage
|
||||
contract={contract}
|
||||
contract={clientContract}
|
||||
chainSlug={info.chainMetadata.slug}
|
||||
detectedPermissionEnumerable={isPermissionsEnumerableSupported}
|
||||
isLoggedIn={!!account}
|
||||
|
||||
+10
-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";
|
||||
@@ -20,18 +19,23 @@ export default async function Page(props: {
|
||||
}
|
||||
const account = await getRawAccount();
|
||||
|
||||
const { contract } = info;
|
||||
if (contract.chain.id === localhost.id) {
|
||||
const { clientContract, serverContract, isLocalhostChain } = info;
|
||||
if (isLocalhostChain) {
|
||||
return (
|
||||
<ContractProposalsPageClient contract={contract} isLoggedIn={!!account} />
|
||||
<ContractProposalsPageClient
|
||||
contract={clientContract}
|
||||
isLoggedIn={!!account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const { isVoteContract } = await getContractPageMetadata(contract);
|
||||
const { isVoteContract } = await getContractPageMetadata(serverContract);
|
||||
|
||||
if (!isVoteContract) {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
}
|
||||
|
||||
return <ContractProposalsPage contract={contract} isLoggedIn={!!account} />;
|
||||
return (
|
||||
<ContractProposalsPage contract={clientContract} isLoggedIn={!!account} />
|
||||
);
|
||||
}
|
||||
|
||||
+9
-7
@@ -1,6 +1,5 @@
|
||||
import { DEFAULT_FEE_RECIPIENT } from "constants/addresses";
|
||||
import { notFound } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import { getPlatformFeeInfo } from "thirdweb/extensions/common";
|
||||
import { getRawAccount } from "../../../../../account/settings/getAccount";
|
||||
import { getContractPageParamsInfo } from "../_utils/getContractFromParams";
|
||||
@@ -21,30 +20,33 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const { contract } = info;
|
||||
const { clientContract, serverContract, isLocalhostChain } = info;
|
||||
|
||||
if (contract.chain.id === localhost.id) {
|
||||
if (isLocalhostChain) {
|
||||
const account = await getRawAccount();
|
||||
return (
|
||||
<ContractSettingsPageClient contract={contract} isLoggedIn={!!account} />
|
||||
<ContractSettingsPageClient
|
||||
contract={clientContract}
|
||||
isLoggedIn={!!account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const [account, metadata] = await Promise.all([
|
||||
getRawAccount(),
|
||||
getContractPageMetadata(info.contract),
|
||||
getContractPageMetadata(serverContract),
|
||||
]);
|
||||
|
||||
let hasDefaultFeeConfig = true;
|
||||
try {
|
||||
const feeInfo = await getPlatformFeeInfo({ contract });
|
||||
const feeInfo = await getPlatformFeeInfo({ contract: serverContract });
|
||||
hasDefaultFeeConfig =
|
||||
feeInfo[0].toLowerCase() === DEFAULT_FEE_RECIPIENT.toLowerCase();
|
||||
} catch {}
|
||||
|
||||
return (
|
||||
<ContractSettingsPage
|
||||
contract={info.contract}
|
||||
contract={clientContract}
|
||||
functionSelectors={metadata.functionSelectors}
|
||||
isLoggedIn={!!account}
|
||||
hasDefaultFeeConfig={hasDefaultFeeConfig}
|
||||
|
||||
+1
-1
@@ -15,5 +15,5 @@ export default async function Page(props: {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return <ContractSourcesPage contract={info.contract} />;
|
||||
return <ContractSourcesPage contract={info.clientContract} />;
|
||||
}
|
||||
|
||||
+10
-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";
|
||||
@@ -18,21 +17,26 @@ export default async function Page(props: {
|
||||
if (!info) {
|
||||
notFound();
|
||||
}
|
||||
const { contract } = info;
|
||||
const { clientContract, serverContract, isLocalhostChain } = info;
|
||||
|
||||
const twAccount = await getRawAccount();
|
||||
|
||||
if (contract.chain.id === localhost.id) {
|
||||
if (isLocalhostChain) {
|
||||
return (
|
||||
<ContractSplitPageClient contract={contract} isLoggedIn={!!twAccount} />
|
||||
<ContractSplitPageClient
|
||||
contract={clientContract}
|
||||
isLoggedIn={!!twAccount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const { isSplitSupported } = await getContractPageMetadata(contract);
|
||||
const { isSplitSupported } = await getContractPageMetadata(serverContract);
|
||||
|
||||
if (!isSplitSupported) {
|
||||
redirect(`/${params.chain_id}/${params.contractAddress}`);
|
||||
}
|
||||
|
||||
return <ContractSplitPage contract={contract} isLoggedIn={!!twAccount} />;
|
||||
return (
|
||||
<ContractSplitPage contract={clientContract} isLoggedIn={!!twAccount} />
|
||||
);
|
||||
}
|
||||
|
||||
+4
-5
@@ -1,5 +1,4 @@
|
||||
import { notFound } from "next/navigation";
|
||||
import { localhost } from "thirdweb/chains";
|
||||
import {
|
||||
isClaimToSupported,
|
||||
isMintToSupported,
|
||||
@@ -25,22 +24,22 @@ export default async function Page(props: {
|
||||
|
||||
const account = await getRawAccount();
|
||||
|
||||
if (info.contract.chain.id === localhost.id) {
|
||||
if (info.isLocalhostChain) {
|
||||
return (
|
||||
<ContractTokensPageClient
|
||||
contract={info.contract}
|
||||
contract={info.clientContract}
|
||||
isLoggedIn={!!account}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const { supportedERCs, functionSelectors } = await getContractPageMetadata(
|
||||
info.contract,
|
||||
info.serverContract,
|
||||
);
|
||||
|
||||
return (
|
||||
<ContractTokensPage
|
||||
contract={info.contract}
|
||||
contract={info.clientContract}
|
||||
isERC20={supportedERCs.isERC20}
|
||||
isMintToSupported={isMintToSupported(functionSelectors)}
|
||||
isClaimToSupported={isClaimToSupported(functionSelectors)}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { serverThirdwebClient } from "@/constants/thirdweb-client.server";
|
||||
import { mapV4ChainToV5Chain } from "contexts/map-chains";
|
||||
import { ZERO_ADDRESS, toTokens } from "thirdweb";
|
||||
import {
|
||||
@@ -9,18 +10,16 @@ import {
|
||||
getRpcClient,
|
||||
} from "thirdweb/rpc";
|
||||
import { hexToNumber, shortenAddress, toEther } from "thirdweb/utils";
|
||||
import { getUserThirdwebClient } from "../../../../../api/lib/getAuthToken";
|
||||
import { getChain } from "../../../utils";
|
||||
|
||||
export default async function Page(props: {
|
||||
params: Promise<{ chain_id: string; txHash: `0x${string}` }>;
|
||||
}) {
|
||||
const params = await props.params;
|
||||
const client = await getUserThirdwebClient();
|
||||
const chain = await getChain(params.chain_id);
|
||||
|
||||
const rpcRequest = getRpcClient({
|
||||
client,
|
||||
client: serverThirdwebClient,
|
||||
// Do not include chain overrides for chain pages
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
chain: mapV4ChainToV5Chain(chain),
|
||||
|
||||
@@ -14,10 +14,7 @@ import {
|
||||
import { ChevronDownIcon } from "lucide-react";
|
||||
import { headers } from "next/headers";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
getAuthToken,
|
||||
getUserThirdwebClient,
|
||||
} from "../../../../api/lib/getAuthToken";
|
||||
import { getAuthToken } from "../../../../api/lib/getAuthToken";
|
||||
import {
|
||||
AllFilters,
|
||||
ChainOptionsFilter,
|
||||
@@ -83,8 +80,6 @@ export default async function ChainListLayout(props: {
|
||||
? "table"
|
||||
: "grid";
|
||||
|
||||
const client = await getUserThirdwebClient();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-14 border-border border-b pl-7">
|
||||
@@ -155,7 +150,6 @@ export default async function ChainListLayout(props: {
|
||||
searchParams={searchParams}
|
||||
activeView={activeView}
|
||||
isLoggedIn={!!authToken}
|
||||
client={client}
|
||||
/>
|
||||
</section>
|
||||
</>
|
||||
|
||||
-4
@@ -7,7 +7,6 @@ import {
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import Fuse from "fuse.js";
|
||||
import type { ThirdwebClient } from "thirdweb";
|
||||
import { StarButton } from "../../../components/client/star-button";
|
||||
import type {
|
||||
ChainMetadataWithServices,
|
||||
@@ -142,7 +141,6 @@ export async function ChainsData(props: {
|
||||
searchParams: SearchParams;
|
||||
activeView: "table" | "grid";
|
||||
isLoggedIn: boolean;
|
||||
client: ThirdwebClient;
|
||||
}) {
|
||||
const { chainsToRender, totalCount, filteredCount } = await getChainsToRender(
|
||||
props.searchParams,
|
||||
@@ -179,7 +177,6 @@ export async function ChainsData(props: {
|
||||
<TableBody>
|
||||
{paginatedChains.map((chain) => (
|
||||
<ChainListRow
|
||||
client={props.client}
|
||||
key={chain.chainId}
|
||||
chainId={chain.chainId}
|
||||
chainName={chain.name}
|
||||
@@ -210,7 +207,6 @@ export async function ChainsData(props: {
|
||||
{paginatedChains.map((chain) => (
|
||||
<li key={chain.chainId} className="h-full">
|
||||
<ChainListCard
|
||||
client={props.client}
|
||||
key={chain.chainId}
|
||||
chainId={chain.chainId}
|
||||
chainName={chain.name}
|
||||
|
||||
+1
-4
@@ -3,7 +3,6 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { CircleAlertIcon, TicketCheckIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import type { JSX } from "react";
|
||||
import type { ThirdwebClient } from "thirdweb";
|
||||
import { ChainIcon } from "../../../components/server/chain-icon";
|
||||
import type { ChainSupportedService } from "../../../types/chain";
|
||||
import { getChainMetadata } from "../../../utils";
|
||||
@@ -17,7 +16,6 @@ type ChainListCardProps = {
|
||||
currencySymbol: string;
|
||||
isDeprecated: boolean;
|
||||
iconUrl?: string;
|
||||
client: ThirdwebClient;
|
||||
};
|
||||
|
||||
export async function ChainListCard({
|
||||
@@ -29,7 +27,6 @@ export async function ChainListCard({
|
||||
enabledServices,
|
||||
favoriteButton,
|
||||
iconUrl,
|
||||
client,
|
||||
}: ChainListCardProps) {
|
||||
const chainMetadata = await getChainMetadata(chainId);
|
||||
|
||||
@@ -38,7 +35,7 @@ export async function ChainListCard({
|
||||
<Card className="h-full w-full transition-colors hover:border-active-border">
|
||||
<CardHeader className="flex flex-row items-center justify-between p-4">
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<ChainIcon iconUrl={iconUrl} className="size-6" client={client} />
|
||||
<ChainIcon iconUrl={iconUrl} className="size-6" />
|
||||
<Link
|
||||
className="group static before:absolute before:top-0 before:right-0 before:bottom-0 before:left-0 before:z-0 before:content-['']"
|
||||
href={`/${chainSlug}`}
|
||||
|
||||
+1
-4
@@ -10,7 +10,6 @@ import {
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import type { JSX } from "react";
|
||||
import type { ThirdwebClient } from "thirdweb";
|
||||
import { ChainIcon } from "../../../components/server/chain-icon";
|
||||
import { products } from "../../../components/server/products";
|
||||
import type { ChainSupportedService } from "../../../types/chain";
|
||||
@@ -25,7 +24,6 @@ type ChainListRowProps = {
|
||||
currencySymbol: string;
|
||||
isDeprecated: boolean;
|
||||
iconUrl?: string;
|
||||
client: ThirdwebClient;
|
||||
};
|
||||
|
||||
export async function ChainListRow({
|
||||
@@ -37,7 +35,6 @@ export async function ChainListRow({
|
||||
enabledServices,
|
||||
favoriteButton,
|
||||
iconUrl,
|
||||
client,
|
||||
}: ChainListRowProps) {
|
||||
const chainMetadata = await getChainMetadata(chainId);
|
||||
return (
|
||||
@@ -47,7 +44,7 @@ export async function ChainListRow({
|
||||
<div className="flex w-[370px] flex-row items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
{favoriteButton && <div className="mr-6"> {favoriteButton} </div>}
|
||||
<ChainIcon iconUrl={iconUrl} className="size-6" client={client} />
|
||||
<ChainIcon iconUrl={iconUrl} className="size-6" />
|
||||
<Link
|
||||
href={`/${chainSlug}`}
|
||||
className="group static before:absolute before:top-0 before:right-0 before:bottom-0 before:left-0 before:z-0 before:content-['']"
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import type { Metadata } from "next";
|
||||
import { headers } from "next/headers";
|
||||
import {
|
||||
getAuthToken,
|
||||
getUserThirdwebClient,
|
||||
} from "../../../api/lib/getAuthToken";
|
||||
import { getAuthToken } from "../../../api/lib/getAuthToken";
|
||||
import {
|
||||
AllFilters,
|
||||
ChainOptionsFilter,
|
||||
@@ -45,8 +42,6 @@ export default async function ChainListPage(props: {
|
||||
? "table"
|
||||
: "grid";
|
||||
|
||||
const client = await getUserThirdwebClient();
|
||||
|
||||
return (
|
||||
<section className="container mx-auto flex h-full flex-col px-4 py-10">
|
||||
<header className="flex flex-col gap-4">
|
||||
@@ -81,7 +76,6 @@ export default async function ChainListPage(props: {
|
||||
searchParams={searchParams}
|
||||
activeView={activeView}
|
||||
isLoggedIn={!!authToken}
|
||||
client={client}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
/* eslint-disable @next/next/no-img-element */
|
||||
import "server-only";
|
||||
import { DASHBOARD_THIRDWEB_SECRET_KEY } from "@/constants/env";
|
||||
import { DASHBOARD_THIRDWEB_SECRET_KEY } from "@/constants/server-envs";
|
||||
import { serverThirdwebClient } from "@/constants/thirdweb-client.server";
|
||||
import { resolveSchemeWithErrorHandler } from "@/lib/resolveSchemeWithErrorHandler";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { ThirdwebClient } from "thirdweb";
|
||||
import { fallbackChainIcon } from "../../../../../../utils/chain-icons";
|
||||
|
||||
export async function ChainIcon(props: {
|
||||
iconUrl?: string;
|
||||
className?: string;
|
||||
client: ThirdwebClient;
|
||||
}) {
|
||||
if (props.iconUrl) {
|
||||
let imageLink = fallbackChainIcon;
|
||||
|
||||
const resolved = resolveSchemeWithErrorHandler({
|
||||
client: props.client,
|
||||
client: serverThirdwebClient,
|
||||
uri: props.iconUrl,
|
||||
});
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user