Skip to content

Commit efbe0b8

Browse files
authored
fix(adapter-polkadot): add RainbowKit provider support (#326)
- Move createRainbowKitWagmiConfig and getWagmiConfigForRainbowKit to adapter-evm-core for shared use between EVM and Polkadot adapters - Update PolkadotWalletImplementation to inject RainbowKit config function - Update PolkadotWalletUiRoot to subscribe to UI kit manager state and render RainbowKitProvider when configured This fixes the "Transaction hooks must be used within RainbowKitProvider" error when loading saved records with RainbowKit selected in Polkadot adapter.
1 parent 1789279 commit efbe0b8

9 files changed

Lines changed: 158 additions & 81 deletions

File tree

packages/adapter-evm-core/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ export {
134134
// RainbowKit component factories
135135
createRainbowKitConnectButton,
136136
createRainbowKitComponents,
137+
// RainbowKit config service
138+
createRainbowKitWagmiConfig,
139+
getWagmiConfigForRainbowKit,
137140
// UI Kit Manager factory
138141
createUiKitManager,
139142
type UiKitManagerState,

packages/adapter-evm-core/src/wallet/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ export {
6060
// RainbowKit component factories
6161
createRainbowKitConnectButton,
6262
createRainbowKitComponents,
63+
// RainbowKit config service
64+
createRainbowKitWagmiConfig,
65+
getWagmiConfigForRainbowKit,
6366
} from './rainbowkit';
6467

6568
// UI Kit Manager factory

packages/adapter-evm/src/wallet/rainbowkit/config-service.ts renamed to packages/adapter-evm-core/src/wallet/rainbowkit/config-service.ts

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
1+
/**
2+
* RainbowKit Config Service
3+
*
4+
* Creates Wagmi configuration for RainbowKit. This is shared between
5+
* EVM and Polkadot adapters to avoid code duplication.
6+
*/
17
import { Config, http } from '@wagmi/core';
28
import { type Chain } from 'viem';
39

410
import type { UiKitConfiguration } from '@openzeppelin/ui-types';
511
import { logger } from '@openzeppelin/ui-utils';
612

7-
import { type WagmiConfigChains } from '../types';
13+
import type { WagmiConfigChains } from '../types';
14+
15+
const LOG_PREFIX = 'rainbowkit/config-service';
816

917
/**
1018
* Creates a Wagmi configuration for RainbowKit using getDefaultConfig
@@ -27,10 +35,7 @@ export async function createRainbowKitWagmiConfig(
2735
try {
2836
const { getDefaultConfig } = await import('@rainbow-me/rainbowkit');
2937
if (!getDefaultConfig) {
30-
logger.error(
31-
'rainbowkit/config-service',
32-
'Failed to import getDefaultConfig from RainbowKit'
33-
);
38+
logger.error(LOG_PREFIX, 'Failed to import getDefaultConfig from RainbowKit');
3439
return null;
3540
}
3641

@@ -39,25 +44,19 @@ export async function createRainbowKitWagmiConfig(
3944

4045
if (!wagmiParams) {
4146
logger.warn(
42-
'rainbowkit/config-service',
47+
LOG_PREFIX,
4348
'Resolved kitConfig does not contain a `wagmiParams` object. Cannot create RainbowKit Wagmi config.'
4449
);
4550
return null;
4651
}
4752

4853
// Ensure essential appName and projectId are present in user's wagmiParams
4954
if (typeof wagmiParams.appName !== 'string' || !wagmiParams.appName) {
50-
logger.warn(
51-
'rainbowkit/config-service',
52-
'kitConfig.wagmiParams is missing or has invalid `appName`.'
53-
);
55+
logger.warn(LOG_PREFIX, 'kitConfig.wagmiParams is missing or has invalid `appName`.');
5456
return null;
5557
}
5658
if (typeof wagmiParams.projectId !== 'string' || !wagmiParams.projectId) {
57-
logger.warn(
58-
'rainbowkit/config-service',
59-
'kitConfig.wagmiParams is missing or has invalid `projectId`.'
60-
);
59+
logger.warn(LOG_PREFIX, 'kitConfig.wagmiParams is missing or has invalid `projectId`.');
6160
return null;
6261
}
6362

@@ -88,7 +87,7 @@ export async function createRainbowKitWagmiConfig(
8887

8988
if (httpRpcOverride) {
9089
logger.info(
91-
'rainbowkit/config-service',
90+
LOG_PREFIX,
9291
`Using overridden RPC for chain ${chainDefinition.name}: ${httpRpcOverride}`
9392
);
9493
rpcUrlToUse = httpRpcOverride;
@@ -117,14 +116,10 @@ export async function createRainbowKitWagmiConfig(
117116
// eslint-disable-next-line @typescript-eslint/no-explicit-any
118117
const config = getDefaultConfig(finalConfigOptions as any);
119118

120-
logger.info(
121-
'rainbowkit/config-service',
122-
'Successfully created RainbowKit Wagmi config object.',
123-
config
124-
);
119+
logger.info(LOG_PREFIX, 'Successfully created RainbowKit Wagmi config object.', config);
125120
return config;
126121
} catch (error) {
127-
logger.error('rainbowkit/config-service', 'Error creating RainbowKit Wagmi config:', error);
122+
logger.error(LOG_PREFIX, 'Error creating RainbowKit Wagmi config:', error);
128123
return null;
129124
}
130125
}
@@ -133,7 +128,7 @@ export async function createRainbowKitWagmiConfig(
133128
* Gets the Wagmi configuration for RainbowKit based on the global UI kit settings.
134129
* This function is intended to be called by WagmiWalletImplementation.
135130
*
136-
* @param uiKitConfiguration The UI kit configuration object from EvmUiKitManager (contains the resolved kitConfig).
131+
* @param uiKitConfiguration The UI kit configuration object from UiKitManager (contains the resolved kitConfig).
137132
* @param chains Array of viem Chain objects to use with RainbowKit
138133
* @param chainIdToNetworkIdMap Mapping of chain IDs to network IDs for RPC override lookups
139134
* @param getRpcEndpointOverride Function to get RPC endpoint overrides
@@ -151,7 +146,7 @@ export async function getWagmiConfigForRainbowKit(
151146
!uiKitConfiguration.kitConfig // kitConfig is the fully resolved config here
152147
) {
153148
logger.debug(
154-
'rainbowkit/config-service',
149+
LOG_PREFIX,
155150
'Not configured for RainbowKit or kitConfig (resolved native + programmatic) is missing.'
156151
);
157152
return null;

packages/adapter-evm-core/src/wallet/rainbowkit/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,6 @@ export { validateRainbowKitConfig, getRawUserNativeConfig } from './utils';
2626
// RainbowKit component factory
2727
export { createRainbowKitConnectButton } from './components';
2828
export { createRainbowKitComponents } from './componentFactory';
29+
30+
// RainbowKit config service (creates RainbowKit wagmi config)
31+
export { createRainbowKitWagmiConfig, getWagmiConfigForRainbowKit } from './config-service';

packages/adapter-evm/src/wallet/implementation/wagmi-implementation.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@
66
*/
77
import {
88
WagmiWalletImplementation as CoreWagmiWalletImplementation,
9+
getWagmiConfigForRainbowKit,
10+
type WagmiConfigChains,
911
type WagmiWalletConfig,
1012
} from '@openzeppelin/ui-builder-adapter-evm-core';
1113
import type { UiKitConfiguration } from '@openzeppelin/ui-types';
1214

1315
import { evmNetworks } from '../../networks';
14-
// Import directly from config-service to avoid circular dependency through the barrel file
15-
// (components.tsx → evmUiKitManager → wagmi-implementation → rainbowkit/index.ts → components.tsx)
16-
import { getWagmiConfigForRainbowKit } from '../rainbowkit/config-service';
17-
import { type WagmiConfigChains } from '../types';
1816

1917
/**
2018
* Generates the supported chains from EVM network configurations.

packages/adapter-evm/src/wallet/rainbowkit/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,8 @@ export { RainbowKitConnectButton } from './components';
2020
// EVM-specific factory functions
2121
export { createRainbowKitComponents } from './componentFactory';
2222

23-
// EVM-specific config service (creates RainbowKit wagmi config)
24-
export { createRainbowKitWagmiConfig, getWagmiConfigForRainbowKit } from './config-service';
23+
// Re-export config service from core for convenience
24+
export {
25+
createRainbowKitWagmiConfig,
26+
getWagmiConfigForRainbowKit,
27+
} from '@openzeppelin/ui-builder-adapter-evm-core';

packages/adapter-polkadot/src/wallet/PolkadotWalletUiRoot.tsx

Lines changed: 103 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,35 @@
66
*/
77

88
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
9-
import type { Chain } from 'viem';
10-
import { createConfig, http, WagmiProvider } from 'wagmi';
9+
import { createConfig, http } from '@wagmi/core';
10+
import { mainnet } from 'viem/chains';
11+
import { WagmiProvider } from 'wagmi';
1112
import React, { useEffect, useMemo, useState } from 'react';
1213

1314
import { WagmiProviderInitializedContext } from '@openzeppelin/ui-builder-adapter-evm-core';
1415
import type { EcosystemReactUiProviderProps } from '@openzeppelin/ui-types';
16+
import { logger } from '@openzeppelin/ui-utils';
1517

16-
import { polkadotChains } from './chains';
17-
import { initializePolkadotWallet } from './implementation';
18+
import { polkadotUiKitManager, type PolkadotUiKitManagerState } from './polkadotUiKitManager';
19+
import type { RainbowKitKitConfig, RainbowKitProviderProps } from './rainbowkit';
1820

1921
/**
2022
* Props for the PolkadotWalletUiRoot component.
2123
*/
22-
export interface PolkadotWalletUiRootProps extends EcosystemReactUiProviderProps {
23-
/** Optional override for chains to configure (defaults to all Polkadot ecosystem chains) */
24-
chains?: readonly [Chain, ...Chain[]];
25-
}
24+
export type PolkadotWalletUiRootProps = EcosystemReactUiProviderProps;
2625

27-
// Create a stable QueryClient for react-query
28-
const queryClient = new QueryClient();
26+
// Create a single QueryClient instance to be reused
27+
const stableQueryClient = new QueryClient();
28+
29+
// Create a minimal, default WagmiConfig to use when no other config is ready.
30+
// This ensures WagmiProvider can always be mounted with a valid config object.
31+
const minimalDefaultWagmiConfig = createConfig({
32+
chains: [mainnet], // At least one chain is required in wagmi v2.20+
33+
connectors: [],
34+
transports: {
35+
[mainnet.id]: http(),
36+
},
37+
});
2938

3039
/**
3140
* Polkadot ecosystem wallet UI root component.
@@ -34,6 +43,9 @@ const queryClient = new QueryClient();
3443
* EVM-compatible networks. It wraps children with WagmiProvider and
3544
* QueryClientProvider configured for Polkadot Hub and parachain networks.
3645
*
46+
* When RainbowKit is configured, it wraps children with RainbowKitProvider
47+
* to enable the RainbowKit UI components.
48+
*
3749
* @example
3850
* ```tsx
3951
* import { PolkadotWalletUiRoot } from '@openzeppelin/ui-builder-adapter-polkadot';
@@ -47,57 +59,98 @@ const queryClient = new QueryClient();
4759
* }
4860
* ```
4961
*
50-
* @example
51-
* ```tsx
52-
* // Using with specific chains only
53-
* import { PolkadotWalletUiRoot, polkadotHub, moonbeam } from '@openzeppelin/ui-builder-adapter-polkadot';
54-
*
55-
* function App() {
56-
* return (
57-
* <PolkadotWalletUiRoot chains={[polkadotHub, moonbeam]}>
58-
* <YourAppContent />
59-
* </PolkadotWalletUiRoot>
60-
* );
61-
* }
62-
* ```
63-
*
6462
* @remarks
6563
* The component pre-configures all Polkadot ecosystem EVM networks by default:
6664
* - Hub networks: Polkadot Hub, Kusama Hub, Polkadot Hub TestNet
6765
* - Parachains: Moonbeam, Moonriver, Moonbase Alpha
68-
*
69-
* For production apps, you should integrate with RainbowKit or another
70-
* wallet UI library. This component provides the base wagmi/react-query
71-
* providers that those libraries require.
7266
*/
73-
export const PolkadotWalletUiRoot: React.FC<PolkadotWalletUiRootProps> = ({
74-
children,
75-
chains = polkadotChains,
76-
}) => {
77-
const [isProviderInitialized, setIsProviderInitialized] = useState(false);
67+
export const PolkadotWalletUiRoot: React.FC<PolkadotWalletUiRootProps> = ({ children }) => {
68+
const [managerState, setManagerState] = useState<PolkadotUiKitManagerState>(
69+
polkadotUiKitManager.getState()
70+
);
71+
72+
useEffect(() => {
73+
const handleStateChange = () => {
74+
setManagerState(polkadotUiKitManager.getState());
75+
};
76+
const unsubscribe = polkadotUiKitManager.subscribe(handleStateChange);
77+
handleStateChange();
78+
return unsubscribe;
79+
}, []);
7880

79-
// Create wagmi config with Polkadot ecosystem chains
80-
const wagmiConfig = useMemo(() => {
81-
const transports = Object.fromEntries(chains.map((chain) => [chain.id, http()]));
81+
const queryClient = useMemo(() => stableQueryClient, []);
8282

83-
return createConfig({
84-
chains,
85-
transports,
86-
});
87-
}, [chains]);
83+
const {
84+
wagmiConfig,
85+
kitProviderComponent,
86+
isKitAssetsLoaded,
87+
currentFullUiKitConfig,
88+
isInitializing,
89+
error,
90+
} = managerState;
8891

89-
// Initialize the wallet implementation singleton with the config
90-
useEffect(() => {
91-
initializePolkadotWallet(wagmiConfig);
92-
// Mark provider as initialized after the wallet implementation is set up
93-
setIsProviderInitialized(true);
94-
}, [wagmiConfig]);
92+
const configForWagmiProvider = wagmiConfig || minimalDefaultWagmiConfig;
93+
const isWagmiContextEffectivelyReady = !!wagmiConfig && !error;
94+
95+
let finalChildren = children;
96+
97+
// When RainbowKit is configured, wrap children with RainbowKitProvider
98+
if (
99+
isWagmiContextEffectivelyReady &&
100+
currentFullUiKitConfig?.kitName === 'rainbowkit' &&
101+
kitProviderComponent &&
102+
isKitAssetsLoaded
103+
) {
104+
const DynKitProvider = kitProviderComponent;
105+
const kitConfig: RainbowKitKitConfig = currentFullUiKitConfig.kitConfig || {};
106+
const providerProps: RainbowKitProviderProps = kitConfig.providerProps || {};
107+
108+
logger.info(
109+
'PolkadotWalletUiRoot',
110+
'Wrapping children with dynamically loaded KitProvider (RainbowKit).'
111+
);
112+
finalChildren = <DynKitProvider {...providerProps}>{children}</DynKitProvider>;
113+
} else if (currentFullUiKitConfig?.kitName === 'rainbowkit' && !isWagmiContextEffectivelyReady) {
114+
logger.info(
115+
'PolkadotWalletUiRoot',
116+
'RainbowKit configured, but context or assets not ready. Button may show its loading/error state.'
117+
);
118+
}
95119

96120
return (
97-
<WagmiProvider config={wagmiConfig}>
121+
<WagmiProvider config={configForWagmiProvider}>
98122
<QueryClientProvider client={queryClient}>
99-
<WagmiProviderInitializedContext.Provider value={isProviderInitialized}>
100-
{children}
123+
<WagmiProviderInitializedContext.Provider value={isWagmiContextEffectivelyReady}>
124+
{finalChildren}
125+
{isInitializing && (
126+
<div
127+
style={{
128+
position: 'fixed',
129+
top: '10px',
130+
right: '10px',
131+
background: 'rgba(0,0,0,0.1)',
132+
padding: '5px',
133+
borderRadius: '3px',
134+
fontSize: '0.8em',
135+
}}
136+
>
137+
Updating network...
138+
</div>
139+
)}
140+
{error && !wagmiConfig && (
141+
<div
142+
style={{
143+
position: 'fixed',
144+
bottom: '10px',
145+
left: '10px',
146+
background: 'red',
147+
color: 'white',
148+
padding: '10px',
149+
}}
150+
>
151+
Error initializing wallet provider: {error.message}
152+
</div>
153+
)}
101154
</WagmiProviderInitializedContext.Provider>
102155
</QueryClientProvider>
103156
</WagmiProvider>

packages/adapter-polkadot/src/wallet/implementation.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
import type { Config } from '@wagmi/core';
1616

1717
import {
18-
WagmiWalletImplementation as CoreWagmiWalletImplementation,
18+
getWagmiConfigForRainbowKit,
19+
WagmiWalletImplementation,
1920
type WagmiConfigChains,
2021
type WagmiWalletConfig,
2122
type WalletNetworkConfig,
@@ -59,7 +60,7 @@ function toWalletNetworkConfigs(): WalletNetworkConfig[] {
5960
* The config should be created by the PolkadotWalletUiRoot component
6061
* or passed directly by the consuming application.
6162
*/
62-
export class PolkadotWalletImplementation extends CoreWagmiWalletImplementation {
63+
export class PolkadotWalletImplementation extends WagmiWalletImplementation {
6364
/**
6465
* Creates a new PolkadotWalletImplementation instance.
6566
*
@@ -75,6 +76,18 @@ export class PolkadotWalletImplementation extends CoreWagmiWalletImplementation
7576

7677
super(config);
7778

79+
// Inject the RainbowKit config function for creating RainbowKit-specific wagmi configs
80+
this.setRainbowKitConfigFn(
81+
async (uiKitConfiguration, chains, chainIdToNetworkIdMap, getRpcOverride) => {
82+
return getWagmiConfigForRainbowKit(
83+
uiKitConfiguration,
84+
chains as WagmiConfigChains,
85+
chainIdToNetworkIdMap,
86+
getRpcOverride
87+
);
88+
}
89+
);
90+
7891
logger.info(
7992
LOG_SYSTEM,
8093
'PolkadotWalletImplementation created with core WagmiWalletImplementation'

0 commit comments

Comments
 (0)