rainbow-me / rainbowkit

The best way to connect a wallet 🌈 🧰
https://rainbowkit.com
MIT License
2.44k stars 664 forks source link

[bug] getDefaultConfig is not a function #1777

Closed nfadeluca closed 9 months ago

nfadeluca commented 9 months ago

Is there an existing issue for this?

RainbowKit Version

2.0.0

wagmi Version

2.5.7

Current Behavior

In a next v14.0.4 project setup with wagmi 2.5.7 and rainbowkit 2.0.0.

Upon page compilation finishing, the error displays:

src\blockchain\config\index.ts (40:39) @ eval
 ⨯ TypeError: (0 , _rainbow_me_rainbowkit__WEBPACK_IMPORTED_MODULE_0__.getDefaultConfig) is not a function

Expected Behavior

getDefaultConfig should work as per the docs.

Steps To Reproduce

I have a config file in /config/index.ts:

import { cookieStorage, createStorage } from 'wagmi'
import {
  getDefaultConfig,
  Chain,
} from '@rainbow-me/rainbowkit';
import {
  mainnet,
  polygon,
  optimism,
  arbitrum,
  base,
  zora,
  goerli,
} from 'wagmi/chains';

export const projectId = process.env.PROJECT_ID;

if (!projectId) throw new Error('Project ID is not defined')

// Local avax custom chain
const avalanche = {
  ...
} as const satisfies Chain

// Create wagmiConfig
export const config = getDefaultConfig({
  appName: "App",
  chains: [avalanche, mainnet, goerli, polygon, optimism, arbitrum, base, zora]
  ssr: true,
  storage: createStorage({
    storage: cookieStorage
  })
})

And I use it in a context provider in /context/index.ts:

import React, { ReactNode } from 'react'
import { config, projectId } from '@/blockchain/config'
import {
  RainbowKitProvider,
} from '@rainbow-me/rainbowkit';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { State, WagmiProvider } from 'wagmi'

// Setup queryClient
const queryClient = new QueryClient()

if (!projectId) throw new Error('Project ID is not defined')

export function ContextProvider({
  children,
  initialState
}: {
  children: ReactNode
  initialState?: State
}) {
  return (
    <WagmiProvider config={config} initialState={initialState}>
      <QueryClientProvider client={queryClient}>
        <RainbowKitProvider>
          {children}
        </RainbowKitProvider>
      </QueryClientProvider>
    </WagmiProvider>
  )
}

And this context provider is finally used in the root layout:

import type { Metadata } from 'next'
import { headers } from 'next/headers'
import { cookieToInitialState } from 'wagmi'
import { config } from '@/blockchain/config'
import { ContextProvider } from '@/context'

import '@/app/globals.css';

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app'
}

type LayoutProps = {
  children: React.ReactNode;
};

const RootLayout: React.FC<LayoutProps> = ({children}) => {
  const initialState = cookieToInitialState(config, headers().get('cookie'))
  return (
    <html lang="en">
      <body>
        <ContextProvider initialState={initialState}>
            {children}
        </ContextProvider>
      </body>
    </html>
  )
}

export default RootLayout;

Ran using npm run dev

Link to Minimal Reproducible Example (CodeSandbox, StackBlitz, etc.)

No response

Anything else?

My directory looks like so:

src
-app
--layout.tsx
-blockchain
--index.ts
-context
--index.ts
...
magiziz commented 9 months ago

@nfadeluca Thanks for raising this issue!

I've taken a look at your code and a few things that needs to be done to solve this issue:

Here is the final code for you to try:

layout.tsx

import "@rainbow-me/rainbowkit/styles.css";
import type { Metadata } from 'next'
import { headers } from 'next/headers'
import { cookieToInitialState } from 'wagmi'
import { config } from '@/blockchain/config'
import { ContextProvider } from '@/context'

import '@/app/globals.css';

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app'
}

type LayoutProps = {
  children: React.ReactNode;
};

const RootLayout: React.FC<LayoutProps> = ({children}) => {
  const initialState = cookieToInitialState(config, headers().get('cookie'))
  return (
    <html lang="en">
      <body>
        <ContextProvider initialState={initialState}>
            {children}
        </ContextProvider>
      </body>
    </html>
  )
}

export default RootLayout;

context/index.tsx

"use client";

import React, { ReactNode } from 'react'
import { config } from '@/blockchain/config'
import {
  RainbowKitProvider,
} from '@rainbow-me/rainbowkit';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { State, WagmiProvider } from 'wagmi'

// Setup queryClient
const queryClient = new QueryClient()

export function ContextProvider({
  children,
  initialState
}: {
  children: ReactNode
  initialState?: State
}) {
  return (
    <WagmiProvider config={config} initialState={initialState}>
      <QueryClientProvider client={queryClient}>
        <RainbowKitProvider>
          {children}
        </RainbowKitProvider>
      </QueryClientProvider>
    </WagmiProvider>
  )
}

config/index.ts

import { cookieStorage, createStorage } from 'wagmi'
import {
  getDefaultConfig,
  Chain,
} from '@rainbow-me/rainbowkit';
import {
  mainnet,
  polygon,
  optimism,
  arbitrum,
  base,
  zora,
  goerli,
} from 'wagmi/chains';

export const projectId = process.env.PROJECT_ID;

// Local avax custom chain
const avalanche = {
  ...
} as const satisfies Chain

// Create wagmiConfig
export const config = getDefaultConfig({
  appName: "App",
  chains: [avalanche, mainnet, goerli, polygon, optimism, arbitrum, base, zora],
  ssr: true,
  projectId,
  storage: createStorage({
    storage: cookieStorage
  })
})

Note: You might also get some polyfill errors so make sure to have this fallback in your next.config.mjs file.

Also if it's helpful please refer to our app router example here. Let me know if everything works 🙏

nfadeluca commented 9 months ago

@KosmosKey Following the example fixed it, I just ended up putting everything in a context file and removing the cookies/state, thank you.

agonist commented 9 months ago

@KosmosKey in the code you provided, importing the wagmi config inside the layout cause an error since getDefaultConfig seems to be client side. should we use createConfig instead when using cookieToInitialState ?

magiziz commented 9 months ago

@KosmosKey in the code you provided, importing the wagmi config inside the layout cause an error since getDefaultConfig seems to be client side. should we use createConfig instead when using cookieToInitialState ?

createConfig will also cause the error if rendered as server component. Your choice on whether you want to use getDefaultConfig or createConfig, but getDefaultConfig is much easier since it gives you all the wagmi config + RainbowKit wallets.

radulff commented 9 months ago

Hi there, i'm getting this exact same error:

"Error: (0 , _rainbow_me_rainbowkit__WEBPACK_IMPORTED_MODULE_2__.getDefaultConfig) is not a function".

I have this next.config.ts file:

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  trailingSlash: false,
  swcMinify: true,
  typescript: {
    ignoreBuildErrors: false
  },
  eslint: {
    ignoreDuringBuilds: false
  },
  experimental: {
    urlImports: [],
    scrollRestoration: false
  },
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'raw.githubusercontent.com',
        port: '',
        pathname: '**'
      },
      {
        protocol: 'https',
        hostname: 'avatars.githubusercontent.com',
        port: '',
        pathname: '**'
      }
    ],
    dangerouslyAllowSVG: true
  },
  webpack: config => {
    config.resolve.fallback = { fs: false, net: false, tls: false }
    config.externals.push('pino-pretty', 'lokijs', 'encoding')
    return config
  }
}

module.exports = nextConfig

Currently running these package versions:

   "@rainbow-me/rainbowkit": "^2.0.0",
    "@tanstack/react-query": "^5.20.1",
    "@wagmi/connectors": "^4.1.13",
    "@wagmi/core": "2.6.4",
    "viem": "~2.7.6",
    "wagmi": "^2.5.6"
    "ethers": "6.10.0",
    "next": "14.1.0",

(I plan on removing ethers, but for now still around)

This is my wagmi config file:

import { getDefaultConfig } from '@rainbow-me/rainbowkit'
import { http } from '@wagmi/core'
import { Chain, avalanche, avalancheFuji } from '@wagmi/core/chains'

export const chains: [Chain, ...Chain[]] = [avalanche, avalancheFuji]

export const config = getDefaultConfig({
  appName: 'App',
  appDescription: 'App Description',
  appIcon: '/favicon.ico',
  appUrl: process.env.NEXT_PUBLIC_APP_URL!,
  projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID!,
  chains,
  transports: {
    [avalanche.id]: http()
  },
  ssr: true
})

My providers.tsx:

'use client'

import { RainbowKitProvider } from '@rainbow-me/rainbowkit'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import * as React from 'react'
import { WagmiProvider } from 'wagmi'
import { config } from '../clients/wagmi/config'

const queryClient = new QueryClient()

const wagmiConfig = { ...config }

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <WagmiProvider config={wagmiConfig}>
      <QueryClientProvider client={queryClient}>
        <RainbowKitProvider>{children}</RainbowKitProvider>
      </QueryClientProvider>
    </WagmiProvider>
  )
}

What am I doing wrong here?

Thanks

magiziz commented 8 months ago

@radulff Can you import 'use-client' at the top of your providers.tsx file ?

radulff commented 8 months ago

@KosmosKey I'm already doing so, sorry I didn't put it in the question. (Just updated my previous code, to make sure it appears)

magiziz commented 8 months ago

@radulff The code you provided works totally fine for me i get no errors. Could you please try following this with-next-app example we have and let me know if there is anything else missing in your project ?

radulff commented 8 months ago

Hi there @KosmosKey, indeed it is working well in the “with-next-app” template. Sorry for the hassle. Do you have any ideas of what could the issue be on my end? How could I debug it? Thanks in advance.

magiziz commented 8 months ago

@radulff I would suggest looking at the build configuration in your dApp. I am pretty sure you just have to do 'use-client' at the top of your providers.tsx component, but if that doesn't seem to work please feel free to look our next app code here and try to see what's wrong.

smauret commented 8 months ago

The error persist if you import config in the root layout (needed for cookieToInitialState), is there a working example of rainbowkit + cookieToInitialState somewhere ?

magiziz commented 8 months ago

@smauret Usually when doing cookieToInitialState it requires some server side action to get the cookies from headers which means it won't be possible if you use 'use-client' which is required in this case.

The one thing you can do is follow the wagmi's cookieToInitialState for config and use that instead.

Just pushed a super simple next app router example with cookieToInitialState here: https://github.com/KosmosKey/rainbowkit-v2-app-router

smauret commented 8 months ago

@KosmosKey thanks for your answer ! That works ! So it's in the root layout that you can only pass the cookies and it's in the provider that you use cookieToInitialState

magiziz commented 8 months ago

@smauret Yeah. Maybe there is a better approach, but at least it solves the problem.

zwergdev commented 8 months ago

@smauret Usually when doing cookieToInitialState it requires some server side action to get the cookies from headers which means it won't be possible if you use 'use-client' which is required in this case.

The one thing you can do is follow the wagmi's cookieToInitialState for config and use that instead.

Just pushed a super simple next app router example with cookieToInitialState here: https://github.com/KosmosKey/rainbowkit-v2-app-router

Thanks, this example helped me

songkeys commented 8 months ago

What should I do if I want to use the wagmiConfig (derived from the rainbow helper function) on server side?

It looks like the rainbowkit can only import on client side, otherwise error like this will be thrown:

TypeError: (0 , _rainbow_me_rainbowkit__WEBPACK_IMPORTED_MODULE_2__.getDefaultWallets) is not a function
magiziz commented 8 months ago

@Songkeys The same thing will happen if you use createConfig from wagmi on server component, you just need to put 'use-client' at the top of your component wrapper. You can't use rainbow / wagmi config functions in server side.

songkeys commented 8 months ago

You can't use rainbow / wagmi config functions in server side.

I should be more clear. In my case, I want to use the viem action generated by wagmi cli. Every action needs a wagmiConfig to run in vanilla side.

The same thing will happen if you use createConfig from wagmi on server component

I'm not sure if this is true because I actually made it work - I created another config.server.ts file to write the wagmiConfig again but without rainbowkit related client-end config imported, and use this server config to run with viem actions in server side.

magiziz commented 8 months ago

@Songkeys I see what you mean. I think wagmi has it's own CLI compatibility when it comes to server side rendering which is not supported by RainbowKit yet. Thanks for bringing that up i'll try to investigate more on this and hopefully see what we can do on our end 🙏

nakedfool commented 7 months ago

Hi @magiziz

I have found an issue related to this implementation https://github.com/magiziz/rainbowkit-v2-app-router

Example is that we have a test route and inside that folder we have page.tsx and test.tsx

If you try to use useAccount inside test.tsx like this

"use client";
import { useAccount } from "wagmi";

const Test = () => {
  const account = useAccount();

  return <div>Account: {account?.address}</div>;
};

export default Test;

It would fail with the following error

WagmiProviderNotFoundError: `useConfig` must be used within `WagmiProvider`.

Docs: https://wagmi.sh/react/api/WagmiProvider.html
Version: wagmi@2.5.16
knight174 commented 6 months ago

it works, thanks! @magiziz

arewageek commented 4 months ago

I had same issue in my Next.js application and after long hours of trying to fix it I noticed I left an extra whitespace in the use client flag I used this: "use client " Instead of : "use client"

You should probably check that too if you're having a similar issue