Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
27 changes: 27 additions & 0 deletions .changeset/plain-ants-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
'@reown/appkit-adapter-ethers5': patch
'@reown/appkit-adapter-ethers': patch
'@reown/appkit-adapter-solana': patch
'@reown/appkit-adapter-wagmi': patch
'@reown/appkit-utils': patch
'@reown/appkit-controllers': patch
'@reown/appkit': patch
'@reown/appkit-wallet': patch
'@reown/appkit-adapter-bitcoin': patch
'@reown/appkit-cdn': patch
'@reown/appkit-cli': patch
'@reown/appkit-codemod': patch
'@reown/appkit-common': patch
'@reown/appkit-core': patch
'@reown/appkit-experimental': patch
'@reown/appkit-pay': patch
'@reown/appkit-polyfills': patch
'@reown/appkit-scaffold-ui': patch
'@reown/appkit-siwe': patch
'@reown/appkit-siwx': patch
'@reown/appkit-testing': patch
'@reown/appkit-ui': patch
'@reown/appkit-wallet-button': patch
---

Add forwarding of custom RPC urls to be used in embedded wallet requests
50 changes: 50 additions & 0 deletions apps/laboratory/app/flag/custom-rpc-url/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use client'

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { WagmiProvider } from 'wagmi'

import { WagmiAdapter } from '@reown/appkit-adapter-wagmi'

import { AppKitButtons } from '@/src/components/AppKitButtons'
import { AppKitConnections } from '@/src/components/AppKitConnections'
import { AppKitInfo } from '@/src/components/AppKitInfo'
import { WagmiTests } from '@/src/components/Wagmi/WagmiTests'
import { AppKitProvider } from '@/src/context/AppKitContext'
import { ConstantsUtil } from '@/src/utils/ConstantsUtil'

const queryClient = new QueryClient()

const customRpcUrls = {
'eip155:1': [{ url: 'https://ethereum-rpc.publicnode.com' }]
}

const wagmiAdapter = new WagmiAdapter({
ssr: true,
networks: ConstantsUtil.EvmNetworks,
projectId: ConstantsUtil.ProjectId,
customRpcUrls
})

const config = {
adapters: [wagmiAdapter],
networks: ConstantsUtil.EvmNetworks,
projectId: ConstantsUtil.ProjectId,
customWallets: ConstantsUtil.CustomWallets,
customRpcUrls
}
const wagmiConfig = wagmiAdapter.wagmiConfig

export default function Wagmi() {
return (
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<AppKitProvider config={config}>
<AppKitButtons />
<AppKitConnections namespace="eip155" />
<AppKitInfo />
<WagmiTests config={wagmiConfig} />
</AppKitProvider>
</QueryClientProvider>
</WagmiProvider>
)
}
2 changes: 1 addition & 1 deletion packages/adapters/ethers/src/tests/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,7 @@ describe('EthersAdapter', () => {
providerType: 'AUTH'
})

expect(mockAuthProvider.switchNetwork).toHaveBeenCalledWith('eip155:1')
expect(mockAuthProvider.switchNetwork).toHaveBeenCalledWith({ chainId: 'eip155:1' })
expect(mockAuthProvider.getUser).toHaveBeenCalledWith({
chainId: 'eip155:1',
preferredAccountType: 'smartAccount'
Expand Down
2 changes: 1 addition & 1 deletion packages/adapters/ethers5/src/tests/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,7 @@ describe('Ethers5Adapter', () => {
providerType: 'AUTH'
})

expect(mockAuthProvider.switchNetwork).toHaveBeenCalledWith('eip155:1')
expect(mockAuthProvider.switchNetwork).toHaveBeenCalledWith({ chainId: 'eip155:1' })
expect(mockAuthProvider.getUser).toHaveBeenCalledWith({
chainId: 'eip155:1',
preferredAccountType: 'smartAccount'
Expand Down
4 changes: 3 additions & 1 deletion packages/adapters/solana/src/tests/mocks/W3mFrameProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ export function mockW3mFrameProvider() {
return Promise.reject(new Error('not implemented'))
}
})
w3mFrame.switchNetwork = vi.fn((chainId: string | number) => Promise.resolve({ chainId }))
w3mFrame.switchNetwork = vi.fn((args: { chainId: string | number }) =>
Promise.resolve({ chainId: args.chainId })
)
w3mFrame.getUser = vi.fn(() => Promise.resolve(mockSession()))
w3mFrame.user = mockSession()

Expand Down
1 change: 1 addition & 0 deletions packages/adapters/wagmi/src/connectors/AuthConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export function authConnector(parameters: AuthParameters) {
chainId?: number
isReconnecting?: boolean
socialUri?: string
rpcUrl?: string
} = {}
) {
if (connectSocialPromise) {
Expand Down
2 changes: 1 addition & 1 deletion packages/adapters/wagmi/src/tests/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,7 @@ describe('WagmiAdapter', () => {
chainId: 'eip155:1',
preferredAccountType: 'smartAccount'
})
expect(mockAuthProvider.switchNetwork).toHaveBeenCalledWith('eip155:1')
expect(mockAuthProvider.switchNetwork).toHaveBeenCalledWith({ chainId: 'eip155:1' })
})
})

Expand Down
2 changes: 1 addition & 1 deletion packages/appkit/src/adapters/ChainAdapterBlueprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ export abstract class AdapterBlueprint<
if (provider && providerType === 'AUTH') {
const authProvider = provider as W3mFrameProvider
const preferredAccountType = getPreferredAccountType(caipNetwork.chainNamespace)
await authProvider.switchNetwork(caipNetwork.caipNetworkId)
await authProvider.switchNetwork({ chainId: caipNetwork.caipNetworkId })
const user = await authProvider.getUser({
chainId: caipNetwork.caipNetworkId,
preferredAccountType
Expand Down
6 changes: 5 additions & 1 deletion packages/controllers/src/utils/ConnectorControllerUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,11 @@ export const ConnectorControllerUtil = {
properties: { provider: socialProvider }
})
}
await authConnector.provider.connectSocial(uri)
const network = ChainController.state.activeCaipNetwork
await authConnector.provider.connectSocial({
uri,
chainId: network?.caipNetworkId
})

if (socialProvider) {
StorageUtil.setConnectedSocialProvider(socialProvider)
Expand Down
7 changes: 6 additions & 1 deletion packages/siwx/src/configs/DefaultSIWX.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ const DEFAULTS = {
new InformalMessenger({
domain: typeof document === 'undefined' ? 'Unknown Domain' : document.location.host,
uri: typeof document === 'undefined' ? 'Unknown URI' : document.location.href,
getNonce: async () => Promise.resolve(Math.round(Math.random() * 10000).toString())
getNonce: async () =>
Promise.resolve(
Math.round(Math.random() * 100000000)
.toString()
.padStart(8, '0')
)
}),

getDefaultVerifiers: () => [new EIP155Verifier(), new SolanaVerifier(), new BIP122Verifier()],
Expand Down
38 changes: 35 additions & 3 deletions packages/wallet/src/W3mFrame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,30 @@ interface W3mFrameConfig {
isAppClient?: boolean
chainId?: W3mFrameTypes.Network['chainId']
enableLogger?: boolean
rpcUrl?: string
}

function createSecureSiteSdkUrl({
projectId,
chainId,
version,
enableLogger,
rpcUrl = ConstantsUtil.BLOCKCHAIN_API_RPC_URL
}: {
projectId: string
chainId: W3mFrameTypes.Network['chainId']
version: string
enableLogger: boolean
rpcUrl?: string
}): string {
const url = new URL(SECURE_SITE_SDK)
url.searchParams.set('projectId', projectId)
url.searchParams.set('chainId', String(chainId))
url.searchParams.set('version', version)
url.searchParams.set('enableLogger', String(enableLogger))
url.searchParams.set('rpcUrl', rpcUrl)
Comment thread
tomiir marked this conversation as resolved.

return url.toString()
}

// -- Sdk --------------------------------------------------------------------
Expand All @@ -26,7 +50,7 @@ export class W3mFrame {

private projectId: string

private rpcUrl = ConstantsUtil.BLOCKCHAIN_API_RPC_URL
private rpcUrl: string

public frameLoadPromise: Promise<void>

Expand All @@ -41,13 +65,15 @@ export class W3mFrame {
projectId,
isAppClient = false,
chainId = 'eip155:1',
enableLogger = true
enableLogger = true,
rpcUrl = ConstantsUtil.BLOCKCHAIN_API_RPC_URL
}: W3mFrameConfig) {
this.projectId = projectId
this.frameLoadPromise = new Promise((resolve, reject) => {
this.frameLoadPromiseResolver = { resolve, reject }
})

this.rpcUrl = rpcUrl
// Create iframe only when sdk is initialised from dapp / appkit
if (isAppClient) {
this.frameLoadPromise = new Promise((resolve, reject) => {
Expand All @@ -56,7 +82,13 @@ export class W3mFrame {
if (W3mFrameHelpers.isClient) {
const iframe = document.createElement('iframe')
iframe.id = 'w3m-iframe'
iframe.src = `${SECURE_SITE_SDK}?projectId=${projectId}&chainId=${chainId}&version=${SECURE_SITE_SDK_VERSION}&enableLogger=${enableLogger}`
iframe.src = createSecureSiteSdkUrl({
projectId,
chainId,
version: SECURE_SITE_SDK_VERSION,
enableLogger,
rpcUrl: this.rpcUrl
})
iframe.name = 'w3m-secure-iframe'
iframe.style.position = 'fixed'
iframe.style.zIndex = '999999'
Expand Down
57 changes: 46 additions & 11 deletions packages/wallet/src/W3mFrameProvider.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type { ChainNamespace, EmbeddedWalletTimeoutReason } from '@reown/appkit-common'
import type { CaipNetwork } from '@reown/appkit-common'
import {
type ChainNamespace,
type EmbeddedWalletTimeoutReason,
ParseUtil
} from '@reown/appkit-common'
import type { CaipNetwork, CaipNetworkId } from '@reown/appkit-common'

import { W3mFrame } from './W3mFrame.js'
import { W3mFrameConstants, W3mFrameRpcConstants } from './W3mFrameConstants.js'
Expand Down Expand Up @@ -53,8 +57,8 @@ export class W3mFrameProvider {
}
this.abortController = abortController
this.getActiveCaipNetwork = getActiveCaipNetwork

this.w3mFrame = new W3mFrame({ projectId, isAppClient: true, chainId, enableLogger })
const rpcUrl = this.getRpcUrl(chainId)
this.w3mFrame = new W3mFrame({ projectId, isAppClient: true, chainId, enableLogger, rpcUrl })
this.onTimeout = onTimeout
if (this.getLoginEmailUsed()) {
this.createFrame()
Expand Down Expand Up @@ -315,13 +319,15 @@ export class W3mFrameProvider {
if (payload?.socialUri) {
try {
await this.init()
const rpcUrl = this.getRpcUrl(payload.chainId)
const response = await this.appEvent<'ConnectSocial'>({
type: W3mFrameConstants.APP_CONNECT_SOCIAL,
payload: {
uri: payload.socialUri,
preferredAccountType: payload.preferredAccountType,
chainId: payload.chainId,
siwxMessage: payload.siwxMessage
siwxMessage: payload.siwxMessage,
rpcUrl
}
} as W3mFrameTypes.AppEvent)

Expand All @@ -346,7 +352,8 @@ export class W3mFrameProvider {
const response = await this.getUser({
chainId,
preferredAccountType: payload?.preferredAccountType,
siwxMessage: payload?.siwxMessage
siwxMessage: payload?.siwxMessage,
rpcUrl: this.getRpcUrl(chainId)
})

this.setLoginSuccess(response.email)
Expand Down Expand Up @@ -378,12 +385,21 @@ export class W3mFrameProvider {
}
}

public async connectSocial(uri: string) {
public async connectSocial({
uri,
chainId,
preferredAccountType
}: {
uri: string
chainId?: number | string
preferredAccountType?: string
}) {
try {
await this.init()
const rpcUrl = this.getRpcUrl(chainId)
const response = await this.appEvent<'ConnectSocial'>({
type: W3mFrameConstants.APP_CONNECT_SOCIAL,
payload: { uri }
payload: { uri, chainId, rpcUrl, preferredAccountType }
} as W3mFrameTypes.AppEvent)

if (response.userName) {
Expand Down Expand Up @@ -428,11 +444,12 @@ export class W3mFrameProvider {
}
}

public async switchNetwork(chainId: number | string) {
public async switchNetwork({ chainId }: { chainId: number | string }) {
try {
const rpcUrl = this.getRpcUrl(chainId)
const response = await this.appEvent<'SwitchNetwork'>({
type: W3mFrameConstants.APP_SWITCH_NETWORK,
payload: { chainId }
payload: { chainId, rpcUrl }
} as W3mFrameTypes.AppEvent)

this.setLastUsedChainId(response.chainId)
Expand Down Expand Up @@ -478,8 +495,8 @@ export class W3mFrameProvider {
const namespace = req.chainNamespace || 'eip155'
const chainId = this.getActiveCaipNetwork(namespace)?.id
request.chainNamespace = namespace

request.chainId = chainId
request.rpcUrl = this.getRpcUrl(chainId)

this.rpcRequestHandler?.(req)
const response = await this.appEvent<'Rpc'>({
Expand Down Expand Up @@ -750,6 +767,24 @@ export class W3mFrameProvider {
private persistSmartAccountEnabledNetworks(networks: number[]) {
W3mFrameStorage.set(W3mFrameConstants.SMART_ACCOUNT_ENABLED_NETWORKS, networks.join(','))
}

private getRpcUrl(chainId?: number | string) {
let namespace: ChainNamespace | undefined = chainId === undefined ? undefined : 'eip155'

if (typeof chainId === 'string') {
if (chainId.includes(':')) {
namespace = ParseUtil.parseCaipNetworkId(chainId as CaipNetworkId)?.chainNamespace
} else if (Number.isInteger(Number(chainId))) {
namespace = 'eip155'
} else {
namespace = 'solana'
}
}

const activeNetwork = this.getActiveCaipNetwork(namespace)

return activeNetwork?.rpcUrls.default.http?.[0]
}
}

export interface W3mFrameProviderMethods {
Expand Down
14 changes: 10 additions & 4 deletions packages/wallet/src/W3mFrameSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,25 @@ export const GetTransactionByHashResponse = z.object({
v: z.string(),
value: z.string()
})
export const AppSwitchNetworkRequest = z.object({ chainId: z.string().or(z.number()) })
export const AppSwitchNetworkRequest = z.object({
chainId: z.string().or(z.number()),
rpcUrl: z.optional(z.string())
})
export const AppConnectEmailRequest = z.object({ email: z.string().email() })
export const AppConnectOtpRequest = z.object({ otp: z.string() })
export const AppConnectSocialRequest = z.object({
uri: z.string(),
preferredAccountType: z.optional(z.string()),
chainId: z.optional(z.string().or(z.number())),
siwxMessage: z.optional(SIWXMessage)
siwxMessage: z.optional(SIWXMessage),
rpcUrl: z.optional(z.string())
})
export const AppGetUserRequest = z.object({
chainId: z.optional(z.string().or(z.number())),
preferredAccountType: z.optional(z.string()),
socialUri: z.optional(z.string()),
siwxMessage: z.optional(SIWXMessage)
siwxMessage: z.optional(SIWXMessage),
rpcUrl: z.optional(z.string())
Comment thread
tomiir marked this conversation as resolved.
})
export const AppGetSocialRedirectUriRequest = z.object({
provider: z.enum(['google', 'github', 'apple', 'facebook', 'x', 'discord'])
Expand Down Expand Up @@ -568,7 +573,8 @@ export const W3mFrameSchema = {
chainId: z.string().or(z.number()).optional(),
chainNamespace: z
.enum(['eip155', 'solana', 'polkadot', 'bip122', 'cosmos'])
.optional()
.optional(),
rpcUrl: z.string().optional()
})
)
})
Expand Down
Loading