Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion apps/dashboard/src/@/hooks/chains/v5-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useMemo } from "react";
import { useCallback, useMemo } from "react";
import type { Chain, ChainMetadata } from "thirdweb/chains";
import { useActiveWalletChain } from "thirdweb/react";
import { useAllChainsData } from "@/hooks/chains/allChains";
Expand All @@ -27,6 +27,17 @@ export function useV5DashboardChain(
}, [chainId, idToChain]);
}

export function useGetV5DashboardChain() {
const { idToChain } = useAllChainsData();
return useCallback(
(chainId: number) => {
// eslint-disable-next-line no-restricted-syntax
return defineDashboardChain(chainId, idToChain.get(chainId));
},
[idToChain],
);
}

/**
* same behavior as v4 `useChain()` but for v5
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use client";
import { useRef } from "react";
import {
defineChain,
encode,
getContract,
sendAndConfirmTransaction,
Expand All @@ -25,6 +24,7 @@ import { create7702MinimalAccount } from "thirdweb/wallets/smart";
import { revalidatePathAction } from "@/actions/revalidate";
import { reportContractDeployed } from "@/analytics/report";
import type { Team } from "@/api/team/get-team";
import { useGetV5DashboardChain } from "@/hooks/chains/v5-adapter";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Double-check hook export and RPC source for custom chains (Freeq 80087)

To ensure MNY-115 is fully addressed, verify that useGetV5DashboardChain returns a chain whose rpc array prioritizes the user-provided RPC (e.g., https://rpc.testnet.superfreeq.com/) rather than a thirdweb aggregator URL for hidden/custom networks. This was the root cause of batched getCode failures.

Run this quick repo inspection to confirm the hook’s export and the RPC behavior:


🏁 Script executed:

#!/bin/bash
# Verify the hook is a named export
fd v5-adapter.ts
rg -nP 'export\s+(const|function)\s+useGetV5DashboardChain' -n

# Inspect how RPCs are selected/constructed inside the hook
rg -n 'rpc' $(fd v5-adapter.ts)

Length of output: 305


🏁 Script executed:

#!/bin/bash
file="apps/dashboard/src/@/hooks/chains/v5-adapter.ts"

echo "===== Lines 1-200 of $file ====="
sed -n '1,200p' "$file"

echo ""
echo "===== Case-insensitive search for 'rpc' ====="
rg -n -i 'rpc' "$file" || echo "No matches for 'rpc'"

echo ""
echo "===== Search for 'url' ====="
rg -n 'url' "$file" || echo "No matches for 'url'"

Length of output: 2386


🏁 Script executed:

#!/bin/bash
# Locate defineDashboardChain implementation
file=$(fd defineDashboardChain.ts)

echo "=== File: $file ==="
sed -n '1,200p' "$file"

echo ""
echo "=== Search for 'rpc' occurrences ==="
rg -n 'rpc' "$file" || echo "No 'rpc' references found"

echo ""
echo "=== Search for array of RPCs or custom RPC logic ==="
rg -nP 'rpc[s]?[\s]*=' "$file" || echo "No RPC assignment pattern found"

echo ""
echo "=== Search for user-provided RPC override handling ==="
rg -nP 'custom|override|user' "$file" || echo "No custom RPC override logic found"

Length of output: 1665


RPC override not implemented in defineDashboardChain

We confirmed that:

  • useGetV5DashboardChain is correctly exported (apps/dashboard/src/@/hooks/chains/v5-adapter.ts:30).
  • defineDashboardChain (apps/dashboard/src/@/lib/defineDashboardChain.ts:15–20) always falls back to a single default RPC URL (https://${chainId}.rpc.${PROD_OR_DEV_URL}) and does not include or prioritize any user-provided/custom RPCs.

To fully address MNY-115 and avoid batched getCode failures on hidden/custom networks, you need to update defineDashboardChain to:

  • Accept an optional user-provided RPC override (e.g. https://rpc.testnet.superfreeq.com/).
  • Construct the resulting chain’s rpc field as an array that places the override first, followed by the default/aggregator URLs.

Locations to update:

  • apps/dashboard/src/@/lib/defineDashboardChain.ts (around the return defineChain({ … rpc: … }) block)
  • Ensure useGetV5DashboardChain consumers pass through any available override into defineDashboardChain.

Please implement these changes so that custom RPCs are prioritized over the default aggregator URLs.

🤖 Prompt for AI Agents
In apps/dashboard/src/@/lib/defineDashboardChain.ts (around the defineChain
return block) and update usages in
apps/dashboard/src/@/hooks/chains/v5-adapter.ts (and any consumers) —
defineDashboardChain currently always builds a single default RPC URL; change
its signature to accept an optional rpcOverride string (or array) and set the
chain.rpc field to an array that places the override(s) first followed by the
existing default/aggregator URLs (de-duplicate if needed), then update
useGetV5DashboardChain to accept/forward any available override into
defineDashboardChain so consumers pass the custom RPC through and it is
prioritized over defaults.

import { useAddContractToProject } from "@/hooks/project-contracts";
import type { CreateNFTCollectionAllValues } from "./_common/form";
import { CreateNFTPageUI } from "./create-nft-page-ui";
Expand All @@ -41,6 +41,7 @@ export function CreateNFTPage(props: {
const activeAccount = useActiveAccount();
const addContractToProject = useAddContractToProject();
const contractAddressRef = useRef<string | undefined>(undefined);
const getChain = useGetV5DashboardChain();

function getAccount(params: { gasless: boolean }) {
if (!activeAccount) {
Expand All @@ -59,9 +60,7 @@ export function CreateNFTPage(props: {
}

function getDeployedContract(params: { chain: string }) {
// eslint-disable-next-line no-restricted-syntax
const chain = defineChain(Number(params.chain));

const chain = getChain(Number(params.chain));
const contractAddress = contractAddressRef.current;

if (!contractAddress) {
Expand Down Expand Up @@ -89,8 +88,7 @@ export function CreateNFTPage(props: {
gasless: params.gasless,
});

// eslint-disable-next-line no-restricted-syntax
const chain = defineChain(Number(collectionInfo.chain));
const chain = getChain(Number(collectionInfo.chain));

let contractAddress: string;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from "lucide-react";
import Link from "next/link";
import { useMemo, useRef, useState } from "react";
import { defineChain, type ThirdwebClient } from "thirdweb";
import type { ThirdwebClient } from "thirdweb";
import {
TokenProvider,
TokenSymbol,
Expand Down Expand Up @@ -35,6 +35,7 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { Skeleton } from "@/components/ui/skeleton";
import { useV5DashboardChain } from "@/hooks/chains/v5-adapter";
import { parseError } from "@/utils/errorParser";
import { ChainOverview } from "../../_common/chain-overview";
import { StepCard } from "../../_common/step-card";
Expand Down Expand Up @@ -308,6 +309,8 @@ export function LaunchNFT(props: {
(nft) => nft.price_amount === props.values.nfts[0]?.price_amount,
);

const chain = useV5DashboardChain(Number(formValues.collectionInfo.chain));

const uniqueAttributes = useMemo(() => {
const attributeNames = new Set<string>();
for (const nft of props.values.nfts) {
Expand Down Expand Up @@ -500,8 +503,7 @@ export function LaunchNFT(props: {
{formValues.nfts[0].price_amount}{" "}
<TokenProvider
address={formValues.nfts[0].price_currency}
// eslint-disable-next-line no-restricted-syntax
chain={defineChain(Number(formValues.collectionInfo.chain))}
chain={chain}
client={props.client}
>
<TokenSymbol
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use client";
import { useRef } from "react";
import {
defineChain,
getAddress,
getContract,
NATIVE_TOKEN_ADDRESS,
Expand Down Expand Up @@ -33,6 +32,7 @@ import {
DEFAULT_FEE_BPS_NEW,
DEFAULT_FEE_RECIPIENT,
} from "@/constants/addresses";
import { useGetV5DashboardChain } from "@/hooks/chains/v5-adapter";
import { useAddContractToProject } from "@/hooks/project-contracts";
import { pollWithTimeout } from "@/utils/pollWithTimeout";
import { createTokenOnUniversalBridge } from "../_apis/create-token-on-bridge";
Expand All @@ -52,6 +52,7 @@ export function CreateTokenAssetPage(props: {
const activeAccount = useActiveAccount();
const addContractToProject = useAddContractToProject();
const contractAddressRef = useRef<string | undefined>(undefined);
const getChain = useGetV5DashboardChain();

function getAccount(gasless: boolean) {
if (!activeAccount) {
Expand All @@ -75,8 +76,7 @@ export function CreateTokenAssetPage(props: {
throw new Error("Contract address not set");
}

// eslint-disable-next-line no-restricted-syntax
const chain = defineChain(Number(params.chain));
const chain = getChain(Number(params.chain));

return getContract({
address: contractAddress,
Expand Down Expand Up @@ -111,10 +111,11 @@ export function CreateTokenAssetPage(props: {
Number(params.values.supply) * (salePercent / 100),
);

const chain = getChain(Number(params.values.chain));

const contractAddress = await createToken({
account,
// eslint-disable-next-line no-restricted-syntax
chain: defineChain(Number(params.values.chain)),
chain: chain,
client: props.client,
launchConfig:
params.values.saleEnabled && saleAmount !== 0
Expand Down Expand Up @@ -245,10 +246,11 @@ export function CreateTokenAssetPage(props: {
{} as Record<string, string>,
);

const chain = getChain(Number(params.values.chain));

const contractAddress = await deployERC20Contract({
account,
// eslint-disable-next-line no-restricted-syntax
chain: defineChain(Number(values.chain)),
chain: chain,
client: props.client,
params: {
description: values.description,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { useForm } from "react-hook-form";
import {
defineChain,
getAddress,
NATIVE_TOKEN_ADDRESS,
type ThirdwebClient,
Expand All @@ -18,6 +17,7 @@ import {
import { reportAssetCreationStepConfigured } from "@/analytics/report";
import type { Team } from "@/api/team/get-team";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { useV5DashboardChain } from "@/hooks/chains/v5-adapter";
import { StepCard } from "../_common/step-card";
import {
type CreateAssetFormValues,
Expand Down Expand Up @@ -94,13 +94,15 @@ export function CreateTokenAssetPageUI(props: {
reValidateMode: "onChange",
});

const chain = useV5DashboardChain(Number(tokenInfoForm.watch("chain")));

const isERC20AssetSupportedQuery = useQuery({
queryKey: ["is-erc20-asset-supported", tokenInfoForm.watch("chain")],
queryKey: ["is-erc20-asset-supported", chain],
queryFn: async () => {
try {
const res = await getDeployedContractFactory({
// eslint-disable-next-line no-restricted-syntax
chain: defineChain(Number(tokenInfoForm.watch("chain"))),
chain: chain,
client: props.client,
});
return !!res;
Expand All @@ -114,15 +116,14 @@ export function CreateTokenAssetPageUI(props: {
queryFn: async () => {
try {
return await isPoolRouterEnabled({
// eslint-disable-next-line no-restricted-syntax
chain: defineChain(Number(tokenInfoForm.watch("chain"))),
chain: chain,
client: props.client,
});
} catch {
return false;
}
},
queryKey: ["is-asset-router-enabled", tokenInfoForm.watch("chain")],
queryKey: ["is-asset-router-enabled", chain],
});

const defaultSaleMode = isERC20AssetSupportedQuery.data
Expand Down
Loading