diff --git a/.changeset/silver-rockets-brush.md b/.changeset/silver-rockets-brush.md
new file mode 100644
index 0000000000..aef1633621
--- /dev/null
+++ b/.changeset/silver-rockets-brush.md
@@ -0,0 +1,22 @@
+---
+'@reown/appkit-scaffold-ui': patch
+'@reown/appkit': patch
+'@reown/appkit-core': patch
+'@reown/appkit-adapter-ethers': patch
+'@reown/appkit-adapter-ethers5': patch
+'@reown/appkit-adapter-solana': patch
+'@reown/appkit-adapter-wagmi': patch
+'@reown/appkit-utils': patch
+'@reown/appkit-cdn': patch
+'@reown/appkit-cli': patch
+'@reown/appkit-common': patch
+'@reown/appkit-experimental': patch
+'@reown/appkit-polyfills': patch
+'@reown/appkit-siwe': patch
+'@reown/appkit-siwx': patch
+'@reown/appkit-ui': patch
+'@reown/appkit-wallet': patch
+'@reown/appkit-wallet-button': patch
+---
+
+Expose a public function to get connect method order with AppKit instance
diff --git a/apps/builder/components/configuration-sections/section-connect-options.tsx b/apps/builder/components/configuration-sections/section-connect-options.tsx
index 3ca813e536..ef75048d03 100644
--- a/apps/builder/components/configuration-sections/section-connect-options.tsx
+++ b/apps/builder/components/configuration-sections/section-connect-options.tsx
@@ -27,8 +27,7 @@ const SortableConnectMethodList = dynamic(
export function SectionConnectOptions() {
const { config, updateFeatures, updateSocials, updateEnableWallets } = useAppKitContext()
const collapseWallets = config.features.collapseWallets
- const connectMethodsOrder =
- config.features.connectMethodsOrder || ConstantsUtil.DEFAULT_FEATURES.connectMethodsOrder
+ const connectMethodsOrder = config.features.connectMethodsOrder
function toggleCollapseWallets() {
updateFeatures({ collapseWallets: !collapseWallets })
diff --git a/apps/builder/components/configuration-sections/section-wallet-features.tsx b/apps/builder/components/configuration-sections/section-wallet-features.tsx
index ef7f149dfe..7d820e7277 100644
--- a/apps/builder/components/configuration-sections/section-wallet-features.tsx
+++ b/apps/builder/components/configuration-sections/section-wallet-features.tsx
@@ -9,7 +9,7 @@ const defaultWalletFeaturesOrder = ['onramp', 'swaps', 'receive', 'send']
export function SectionWalletFeatures() {
const { config, updateFeatures } = useAppKitContext()
- const connectMethodsOrder = config.features.walletFeaturesOrder || defaultWalletFeaturesOrder
+ const walletFeaturesOrder = config.features.walletFeaturesOrder || defaultWalletFeaturesOrder
function handleNewOrder(items: UniqueIdentifier[]) {
const titleValueMap = {
@@ -33,15 +33,12 @@ export function SectionWalletFeatures() {
case 'Swap':
updateFeatures({ swaps: !config.features.swaps })
return
- case 'Receive':
- case 'Send':
- return
default:
return
}
}
- const featureNameMap = connectMethodsOrder.map(name => {
+ const featureNameMap = walletFeaturesOrder.map(name => {
switch (name) {
case 'onramp':
return 'Buy'
diff --git a/apps/builder/components/preview-content.tsx b/apps/builder/components/preview-content.tsx
index aa71aa0ea8..cafe26cc5c 100644
--- a/apps/builder/components/preview-content.tsx
+++ b/apps/builder/components/preview-content.tsx
@@ -4,9 +4,14 @@ import { Button } from '@/components/ui/button'
import { toast } from 'sonner'
import { Link1Icon, ResetIcon } from '@radix-ui/react-icons'
import { useAppKitContext } from '@/hooks/use-appkit'
+import { useAppKitState } from '@reown/appkit/react'
+import { useEffect } from 'react'
+import { useState } from 'react'
export function PreviewContent() {
- const { isInitialized, resetConfigs } = useAppKitContext()
+ const [shouldRender, setShouldRender] = useState(false)
+ const { initialized } = useAppKitState()
+ const { resetConfigs } = useAppKitContext()
async function handleShare() {
try {
@@ -17,10 +22,18 @@ export function PreviewContent() {
}
}
+ useEffect(() => {
+ setShouldRender(initialized)
+ }, [initialized])
+
+ if (!shouldRender) {
+ return null
+ }
+
return (
<>
-
- {isInitialized ? (
+
+ {shouldRender ? (
<>
{/* @ts-ignore */}
({}),
getNewIndex,
handle = false,
- itemCount = 3,
items: initialItems,
measuring,
modifiers,
@@ -109,9 +105,7 @@ export function SortableConnectMethodList({
onToggleOption,
handleNewOrder
}: Props) {
- const [items, setItems] = useState(
- () => initialItems ?? createRange(itemCount, index => index)
- )
+ const [items, setItems] = useState(() => initialItems ?? [])
const [activeId, setActiveId] = useState(null)
const sensors = useSensors(
useSensor(MouseSensor, {
@@ -136,13 +130,18 @@ export function SortableConnectMethodList({
}, [activeId])
const orderString = useMemo(() => items.join(','), [items])
+ const orderFromPropsString = useMemo(() => initialItems?.join(','), [initialItems])
useEffect(() => {
- if (handleNewOrder) {
+ if (handleNewOrder && orderString) {
handleNewOrder(orderString.split(','))
}
}, [orderString])
+ useEffect(() => {
+ setItems(orderFromPropsString ? orderFromPropsString.split(',') : [])
+ }, [orderFromPropsString])
+
return (
items.join(','), [items])
+ const orderStringFromProps = useMemo(() => initialItems?.join(','), [initialItems])
useEffect(() => {
if (handleNewOrder) {
@@ -144,6 +145,12 @@ export function SortableWalletFeatureList({
}
}, [orderString])
+ useEffect(() => {
+ if (orderStringFromProps) {
+ setItems(orderStringFromProps.split(','))
+ }
+ }, [orderStringFromProps])
+
return (
- isInitialized: boolean
updateThemeMode: (mode: ThemeMode) => void
updateFeatures: (features: Partial) => void
updateSocials: (enabled: boolean) => void
diff --git a/apps/builder/providers/appkit-context-provider.tsx b/apps/builder/providers/appkit-context-provider.tsx
index 559490608e..931ce74535 100644
--- a/apps/builder/providers/appkit-context-provider.tsx
+++ b/apps/builder/providers/appkit-context-provider.tsx
@@ -1,7 +1,7 @@
'use client'
import { ReactNode, useEffect, useState } from 'react'
-import { Features, ThemeMode, ThemeVariables, type AppKit } from '@reown/appkit/react'
+import { Features, ThemeMode, ThemeVariables, useAppKitState } from '@reown/appkit/react'
import { ConnectMethod, ConstantsUtil } from '@reown/appkit-core'
import { ThemeStore } from '../lib/theme-store'
import { URLState, urlStateUtils } from '@/lib/url-state'
@@ -24,7 +24,8 @@ interface AppKitProviderProps {
const initialConfig = urlStateUtils.getStateFromURL()
export const ContextProvider: React.FC = ({ children }) => {
- const [isInitialized, setIsInitialized] = useState(false)
+ const { initialized } = useAppKitState()
+
const [features, setFeatures] = useState(
initialConfig?.features || ConstantsUtil.DEFAULT_FEATURES
)
@@ -53,10 +54,21 @@ export const ContextProvider: React.FC = ({ children }) =>
function updateFeatures(newFeatures: Partial) {
setFeatures(prev => {
- const newValue = { ...prev, ...newFeatures }
- appKit?.updateFeatures(newValue)
- urlStateUtils.updateURLWithState({ features: newValue })
- return newValue
+ // Update the AppKit state first
+ const newAppKitValue = { ...prev, ...newFeatures }
+ appKit?.updateFeatures(newAppKitValue)
+
+ // Get the connection methods order since it's calculated based on injected connectors dynamically
+ const order =
+ newFeatures?.connectMethodsOrder === undefined
+ ? appKit?.getConnectMethodsOrder()
+ : newFeatures.connectMethodsOrder
+
+ // Define and set new internal value with the order
+ const newInternalValue = { ...newAppKitValue, connectMethodsOrder: order }
+ urlStateUtils.updateURLWithState({ features: newInternalValue })
+
+ return newInternalValue
})
}
@@ -118,9 +130,16 @@ export const ContextProvider: React.FC = ({ children }) =>
useEffect(() => {
setTheme(theme as ThemeMode)
- setIsInitialized(true)
}, [])
+ useEffect(() => {
+ if (initialized) {
+ const connectMethodsOrder = appKit?.getConnectMethodsOrder()
+ const order = connectMethodsOrder
+ updateFeatures({ connectMethodsOrder: order })
+ }
+ }, [initialized])
+
const socialsEnabled = Array.isArray(features.socials)
return (
@@ -143,7 +162,6 @@ export const ContextProvider: React.FC = ({ children }) =>
socialsEnabled,
enableWallets,
isDraggingByKey,
- isInitialized,
updateFeatures,
updateThemeMode,
updateSocials,
diff --git a/packages/appkit/src/client.ts b/packages/appkit/src/client.ts
index fc62a7a222..5ea9f56203 100644
--- a/packages/appkit/src/client.ts
+++ b/packages/appkit/src/client.ts
@@ -83,6 +83,7 @@ import type { SessionTypes } from '@walletconnect/types'
import type { UniversalProviderOpts } from '@walletconnect/universal-provider'
import { W3mFrameProviderSingleton } from './auth-provider/W3MFrameProviderSingleton.js'
import { WcHelpersUtil } from './utils/HelpersUtil.js'
+import { WalletUtil } from '@reown/appkit-scaffold-ui/utils'
declare global {
interface Window {
@@ -234,6 +235,7 @@ export class AppKit {
}
}
})
+ PublicStateController.set({ initialized: true })
}
// -- Public -------------------------------------------------------------------
@@ -642,6 +644,13 @@ export class AppKit {
await this.connectionControllerClient?.disconnect()
}
+ public getConnectMethodsOrder() {
+ return WalletUtil.getConnectOrderMethod(
+ OptionsController.state.features,
+ ConnectorController.getConnectors()
+ )
+ }
+
// -- Private ------------------------------------------------------------------
private async initControllers(
options: AppKitOptions & {
diff --git a/packages/core/src/controllers/PublicStateController.ts b/packages/core/src/controllers/PublicStateController.ts
index 8139556a8a..8b42bf6ecf 100644
--- a/packages/core/src/controllers/PublicStateController.ts
+++ b/packages/core/src/controllers/PublicStateController.ts
@@ -1,12 +1,33 @@
import { proxy, subscribe as sub } from 'valtio/vanilla'
-import type { CaipNetworkId } from '@reown/appkit-common'
+import type { CaipNetworkId, ChainNamespace } from '@reown/appkit-common'
// -- Types --------------------------------------------- //
export interface PublicStateControllerState {
+ /**
+ * @description Indicates if the AppKit is loading.
+ * @type {boolean}
+ */
loading: boolean
+ /**
+ * @description Indicates if the AppKit modal is open.
+ * @type {boolean}
+ */
open: boolean
- selectedNetworkId?: CaipNetworkId
- activeChain?: string
+ /**
+ * @description Indicates the selected network id in CAIP-2 format.
+ * @type {CaipNetworkId | undefined}
+ */
+ selectedNetworkId?: CaipNetworkId | undefined
+ /**
+ * @description Indicates the active chain namespace.
+ * @type {ChainNamespace | undefined}
+ */
+ activeChain?: ChainNamespace | undefined
+ /**
+ * @description Indicates if the AppKit has been initialized. This sets to true when all controllers, adapters and internal state is ready.
+ * @type {boolean}
+ */
+ initialized: boolean
}
// -- State --------------------------------------------- //
@@ -14,7 +35,8 @@ const state = proxy({
loading: false,
open: false,
selectedNetworkId: undefined,
- activeChain: undefined
+ activeChain: undefined,
+ initialized: false
})
// -- Controller ---------------------------------------- //
diff --git a/packages/core/tests/controllers/PublicStateController.test.ts b/packages/core/tests/controllers/PublicStateController.test.ts
index 94b79ee91c..dc052aeb65 100644
--- a/packages/core/tests/controllers/PublicStateController.test.ts
+++ b/packages/core/tests/controllers/PublicStateController.test.ts
@@ -7,7 +7,9 @@ describe('PublicStateController', () => {
expect(PublicStateController.state).toEqual({
loading: false,
open: false,
- selectedNetworkId: undefined
+ selectedNetworkId: undefined,
+ activeChain: undefined,
+ initialized: false
})
})
@@ -15,14 +17,18 @@ describe('PublicStateController', () => {
PublicStateController.set({ open: true })
expect(PublicStateController.state).toEqual({
loading: false,
- open: true,
- selectedNetworkId: undefined
+ selectedNetworkId: undefined,
+ activeChain: undefined,
+ initialized: false,
+ open: true
})
PublicStateController.set({ selectedNetworkId: 'eip155:1' })
expect(PublicStateController.state).toEqual({
loading: false,
open: true,
- selectedNetworkId: 'eip155:1'
+ selectedNetworkId: 'eip155:1',
+ activeChain: undefined,
+ initialized: false
})
})
})
diff --git a/packages/scaffold-ui/exports/utils.tsx b/packages/scaffold-ui/exports/utils.tsx
new file mode 100644
index 0000000000..ab17c4c1ea
--- /dev/null
+++ b/packages/scaffold-ui/exports/utils.tsx
@@ -0,0 +1,3 @@
+export * from '../src/utils/ConnectorUtil.js'
+export * from '../src/utils/ConstantsUtil.js'
+export * from '../src/utils/WalletUtil.js'
diff --git a/packages/scaffold-ui/package.json b/packages/scaffold-ui/package.json
index 23406b04b2..e5e2a1b612 100644
--- a/packages/scaffold-ui/package.json
+++ b/packages/scaffold-ui/package.json
@@ -18,6 +18,11 @@
"types": "./dist/types/exports/w3m-modal.d.ts",
"import": "./dist/esm/exports/w3m-modal.js",
"default": "./dist/esm/exports/w3m-modal.js"
+ },
+ "./utils": {
+ "types": "./dist/types/exports/utils.d.ts",
+ "import": "./dist/esm/exports/utils.js",
+ "default": "./dist/esm/exports/utils.js"
}
},
"scripts": {
diff --git a/packages/scaffold-ui/src/utils/WalletUtil.ts b/packages/scaffold-ui/src/utils/WalletUtil.ts
index f238679422..21b63a0eba 100644
--- a/packages/scaffold-ui/src/utils/WalletUtil.ts
+++ b/packages/scaffold-ui/src/utils/WalletUtil.ts
@@ -4,7 +4,9 @@ import {
OptionsController,
StorageUtil
} from '@reown/appkit-core'
-import type { WcWallet } from '@reown/appkit-core'
+import type { ConnectMethod, Connector, Features, WcWallet } from '@reown/appkit-core'
+import { ConnectorUtil } from './ConnectorUtil.js'
+import { ConstantsUtil } from './ConstantsUtil.js'
interface AppKitWallet extends WcWallet {
installed: boolean
@@ -79,5 +81,26 @@ export const WalletUtil = {
)
return sortedWallets
+ },
+
+ getConnectOrderMethod(_features: Features | undefined, _connectors: Connector[]) {
+ const connectMethodOrder =
+ _features?.connectMethodsOrder || OptionsController.state.features?.connectMethodsOrder
+ const connectors = _connectors || ConnectorController.state.connectors
+
+ if (connectMethodOrder) {
+ return connectMethodOrder
+ }
+
+ const { injected, announced } = ConnectorUtil.getConnectorsByType(connectors)
+
+ const shownInjected = injected.filter(ConnectorUtil.showConnector)
+ const shownAnnounced = announced.filter(ConnectorUtil.showConnector)
+
+ if (shownInjected.length || shownAnnounced.length) {
+ return ['wallet', 'email', 'social'] as ConnectMethod[]
+ }
+
+ return ConstantsUtil.DEFAULT_CONNECT_METHOD_ORDER
}
}
diff --git a/packages/scaffold-ui/src/views/w3m-connect-view/index.ts b/packages/scaffold-ui/src/views/w3m-connect-view/index.ts
index 5868f41b91..055301e7e0 100644
--- a/packages/scaffold-ui/src/views/w3m-connect-view/index.ts
+++ b/packages/scaffold-ui/src/views/w3m-connect-view/index.ts
@@ -7,15 +7,13 @@ import {
CoreHelperUtil,
OptionsController,
RouterController,
- type ConnectMethod,
type WalletGuideType
} from '@reown/appkit-core'
import { state } from 'lit/decorators/state.js'
import { property } from 'lit/decorators.js'
import { classMap } from 'lit/directives/class-map.js'
import { ifDefined } from 'lit/directives/if-defined.js'
-import { ConnectorUtil } from '../../utils/ConnectorUtil.js'
-import { ConstantsUtil } from '../../utils/ConstantsUtil.js'
+import { WalletUtil } from '../../utils/WalletUtil.js'
@customElement('w3m-connect-view')
export class W3mConnectView extends LitElement {
@@ -119,7 +117,7 @@ export class W3mConnectView extends LitElement {
// -- Private ------------------------------------------- //
private renderConnectMethod(tabIndex?: number) {
- const connectMethodsOrder = this.getConnectOrderMethod()
+ const connectMethodsOrder = WalletUtil.getConnectOrderMethod(this.features, this.connectors)
return html`${connectMethodsOrder.map((method, index) => {
switch (method) {
@@ -151,7 +149,7 @@ export class W3mConnectView extends LitElement {
}
private checkIsThereNextMethod(currentIndex: number): string | undefined {
- const connectMethodsOrder = this.getConnectOrderMethod()
+ const connectMethodsOrder = WalletUtil.getConnectOrderMethod(this.features, this.connectors)
const nextMethod = connectMethodsOrder[currentIndex + 1] as
| 'wallet'
@@ -345,25 +343,6 @@ export class W3mConnectView extends LitElement {
)
}
- private getConnectOrderMethod() {
- const connectMethodOrder = this.features?.connectMethodsOrder
-
- if (connectMethodOrder) {
- return connectMethodOrder
- }
-
- const { injected, announced } = ConnectorUtil.getConnectorsByType(this.connectors)
-
- const shownInjected = injected.filter(ConnectorUtil.showConnector)
- const shownAnnounced = announced.filter(ConnectorUtil.showConnector)
-
- if (shownInjected.length || shownAnnounced.length) {
- return ['wallet', 'email', 'social'] as ConnectMethod[]
- }
-
- return ConstantsUtil.DEFAULT_CONNECT_METHOD_ORDER
- }
-
// -- Private Methods ----------------------------------- //
private onContinueWalletClick() {
RouterController.push('ConnectWallets')