diff --git a/.changeset/nine-frogs-drive.md b/.changeset/nine-frogs-drive.md new file mode 100644 index 0000000000..00b9cd3f4f --- /dev/null +++ b/.changeset/nine-frogs-drive.md @@ -0,0 +1,23 @@ +--- +'@reown/appkit-adapter-ethers5': patch +'@reown/appkit-adapter-ethers': patch +'@reown/appkit-adapter-wagmi': patch +'@reown/appkit': patch +'@reown/appkit-core': patch +'@reown/appkit-adapter-bitcoin': patch +'@reown/appkit-adapter-solana': 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-scaffold-ui': patch +'@reown/appkit-siwe': patch +'@reown/appkit-siwx': patch +'@reown/appkit-ui': patch +'@reown/appkit-wallet': patch +'@reown/appkit-wallet-button': patch +--- + +Add defaultAccountTypes option for AppKit initialization. diff --git a/packages/adapters/ethers/src/client.ts b/packages/adapters/ethers/src/client.ts index 0d220310b9..0df7f02d74 100644 --- a/packages/adapters/ethers/src/client.ts +++ b/packages/adapters/ethers/src/client.ts @@ -6,7 +6,8 @@ import { type Connector, type ConnectorType, type Provider, - CoreHelperUtil + CoreHelperUtil, + OptionsController } from '@reown/appkit-core' import { ConstantsUtil, PresetsUtil } from '@reown/appkit-utils' import { EthersHelpersUtil, type ProviderType } from '@reown/appkit-utils/ethers' @@ -346,7 +347,8 @@ export class EthersAdapter extends AdapterBlueprint { if (type === 'AUTH') { const { address } = await (selectedProvider as unknown as W3mFrameProvider).connect({ - chainId + chainId, + preferredAccountType: OptionsController.state.defaultAccountTypes.eip155 }) accounts = [address] @@ -377,7 +379,10 @@ export class EthersAdapter extends AdapterBlueprint { const connector = this.connectors.find(c => c.id === id) if (connector && connector.type === 'AUTH' && chainId) { - await (connector.provider as W3mFrameProvider).connect({ chainId }) + await (connector.provider as W3mFrameProvider).connect({ + chainId, + preferredAccountType: OptionsController.state.defaultAccountTypes.eip155 + }) } } diff --git a/packages/adapters/ethers/src/tests/client.test.ts b/packages/adapters/ethers/src/tests/client.test.ts index 5d03f8615e..c27a43ea9e 100644 --- a/packages/adapters/ethers/src/tests/client.test.ts +++ b/packages/adapters/ethers/src/tests/client.test.ts @@ -314,7 +314,10 @@ describe('EthersAdapter', () => { }) expect(mockAuthProvider.switchNetwork).toHaveBeenCalledWith('eip155:1') - expect(mockAuthProvider.getUser).toHaveBeenCalledWith({ chainId: 'eip155:1' }) + expect(mockAuthProvider.getUser).toHaveBeenCalledWith({ + chainId: 'eip155:1', + preferredAccountType: 'smartAccount' + }) }) it('should add Ethereum chain with external provider and use chain default', async () => { diff --git a/packages/adapters/ethers5/src/client.ts b/packages/adapters/ethers5/src/client.ts index 9a17003370..d67ef5732d 100644 --- a/packages/adapters/ethers5/src/client.ts +++ b/packages/adapters/ethers5/src/client.ts @@ -6,7 +6,8 @@ import { type CombinedProvider, type Connector, type ConnectorType, - type Provider + type Provider, + OptionsController } from '@reown/appkit-core' import { ConstantsUtil, PresetsUtil } from '@reown/appkit-utils' import { EthersHelpersUtil, type ProviderType } from '@reown/appkit-utils/ethers' @@ -346,7 +347,8 @@ export class Ethers5Adapter extends AdapterBlueprint { if (type === 'AUTH') { const { address } = await (selectedProvider as unknown as W3mFrameProvider).connect({ - chainId + chainId, + preferredAccountType: OptionsController.state.defaultAccountTypes.eip155 }) accounts = [address] @@ -407,7 +409,10 @@ export class Ethers5Adapter extends AdapterBlueprint { const connector = this.connectors.find(c => c.id === id) if (connector && connector.type === 'AUTH' && chainId) { - await (connector.provider as W3mFrameProvider).connect({ chainId }) + await (connector.provider as W3mFrameProvider).connect({ + chainId, + preferredAccountType: OptionsController.state.defaultAccountTypes.eip155 + }) } } diff --git a/packages/adapters/ethers5/src/tests/client.test.ts b/packages/adapters/ethers5/src/tests/client.test.ts index e2b5b2a216..4996f99c6b 100644 --- a/packages/adapters/ethers5/src/tests/client.test.ts +++ b/packages/adapters/ethers5/src/tests/client.test.ts @@ -306,7 +306,10 @@ describe('Ethers5Adapter', () => { }) expect(mockAuthProvider.switchNetwork).toHaveBeenCalledWith('eip155:1') - expect(mockAuthProvider.getUser).toHaveBeenCalledWith({ chainId: 'eip155:1' }) + expect(mockAuthProvider.getUser).toHaveBeenCalledWith({ + chainId: 'eip155:1', + preferredAccountType: 'smartAccount' + }) }) }) diff --git a/packages/adapters/wagmi/src/connectors/AuthConnector.ts b/packages/adapters/wagmi/src/connectors/AuthConnector.ts index a73a521a80..6bfd4dc8ca 100644 --- a/packages/adapters/wagmi/src/connectors/AuthConnector.ts +++ b/packages/adapters/wagmi/src/connectors/AuthConnector.ts @@ -6,7 +6,7 @@ import type { Address } from 'viem' import { ErrorUtil } from '@reown/appkit-utils' import { NetworkUtil } from '@reown/appkit-common' import { W3mFrameProviderSingleton } from '@reown/appkit/auth-provider' -import { AlertController } from '@reown/appkit-core' +import { AlertController, OptionsController } from '@reown/appkit-core' // -- Types ---------------------------------------------------------------------------------------- interface W3mFrameProviderOptions { @@ -56,7 +56,8 @@ export function authConnector(parameters: AuthParameters) { chainId: frameChainId, accounts } = await provider.connect({ - chainId + chainId, + preferredAccountType: OptionsController.state.defaultAccountTypes.eip155 }) currentAccounts = accounts?.map(a => a.address as Address) || [address as Address] @@ -126,7 +127,10 @@ export function authConnector(parameters: AuthParameters) { } const provider = await this.getProvider() // We connect instead, since changing the chain may cause the address to change as well - const response = await provider.connect({ chainId }) + const response = await provider.connect({ + chainId, + preferredAccountType: OptionsController.state.defaultAccountTypes.eip155 + }) currentAccounts = response?.accounts?.map(a => a.address as Address) || [ response.address as Address diff --git a/packages/appkit/src/adapters/ChainAdapterBlueprint.ts b/packages/appkit/src/adapters/ChainAdapterBlueprint.ts index 987601c7f7..5107fbfefb 100644 --- a/packages/appkit/src/adapters/ChainAdapterBlueprint.ts +++ b/packages/appkit/src/adapters/ChainAdapterBlueprint.ts @@ -11,7 +11,8 @@ import { type AccountControllerState, type Connector as AppKitConnector, type Tokens, - type WriteContractArgs + type WriteContractArgs, + OptionsController } from '@reown/appkit-core' import UniversalProvider from '@walletconnect/universal-provider' import { W3mFrameProvider } from '@reown/appkit-wallet' @@ -241,7 +242,9 @@ export abstract class AdapterBlueprint< const authProvider = provider as W3mFrameProvider await authProvider.switchNetwork(caipNetwork.caipNetworkId) const user = await authProvider.getUser({ - chainId: caipNetwork.caipNetworkId + chainId: caipNetwork.caipNetworkId, + preferredAccountType: + OptionsController.state.defaultAccountTypes[caipNetwork.chainNamespace] }) this.emit('switchNetwork', user) diff --git a/packages/appkit/src/client.ts b/packages/appkit/src/client.ts index 12ba63bc42..f08a5c7a08 100644 --- a/packages/appkit/src/client.ts +++ b/packages/appkit/src/client.ts @@ -72,7 +72,7 @@ import { import { W3mFrameHelpers, W3mFrameRpcConstants, - type W3mFrameProvider, + W3mFrameProvider, type W3mFrameTypes } from '@reown/appkit-wallet' import { ProviderUtil, type ProviderStoreUtilState } from './store/ProviderUtil.js' @@ -735,6 +735,7 @@ export class AppKit { OptionsController.setCustomWallets(options.customWallets) OptionsController.setFeatures(options.features) OptionsController.setAllowUnsupportedChain(options.allowUnsupportedChain) + OptionsController.setDefaultAccountTypes(options.defaultAccountTypes) const defaultMetaData = this.getDefaultMetaData() if (!options.metadata && defaultMetaData) { @@ -1242,14 +1243,15 @@ export class AppKit { this.setUser({ ...(AccountController.state.user || {}), email: user.email }) - const preferredAccountType = (user.preferredAccountType || 'eoa') as W3mFrameTypes.AccountType + const preferredAccountType = (user.preferredAccountType || + OptionsController.state.defaultAccountTypes[namespace]) as W3mFrameTypes.AccountType this.setPreferredAccountType(preferredAccountType, namespace) const userAccounts = user.accounts?.map(account => CoreHelperUtil.createAccount( namespace, account.address, - namespace === ConstantsUtil.CHAIN.EVM ? account.type : 'eoa' + account.type || OptionsController.state.defaultAccountTypes[namespace] ) ) diff --git a/packages/appkit/src/utils/TypesUtil.ts b/packages/appkit/src/utils/TypesUtil.ts index a18a3efc33..8ea8cabdd8 100644 --- a/packages/appkit/src/utils/TypesUtil.ts +++ b/packages/appkit/src/utils/TypesUtil.ts @@ -79,4 +79,9 @@ export type AppKitOptions = { * @default undefined */ universalProvider?: UniversalProvider -} & OptionsControllerState + /** + * The default account type used for each chain namespace. + * @default "{ bip122: 'payment', eip155: 'smartAccount', polkadot: 'eoa', solana: 'eoa' }" + */ + defaultAccountTypes?: Partial +} & Omit diff --git a/packages/appkit/tests/appkit.test.ts b/packages/appkit/tests/appkit.test.ts index a0c425dcae..ca96f4776d 100644 --- a/packages/appkit/tests/appkit.test.ts +++ b/packages/appkit/tests/appkit.test.ts @@ -129,6 +129,21 @@ describe('Base', () => { expect(OptionsController.setEIP6963Enabled).toHaveBeenCalledWith(false) }) + + it('should set partially defaultAccountType', () => { + new AppKit({ + ...mockOptions, + defaultAccountTypes: { + eip155: 'eoa', + bip122: 'ordinal' + } + }) + + expect(OptionsController.setDefaultAccountTypes).toHaveBeenCalledWith({ + eip155: 'eoa', + bip122: 'ordinal' + }) + }) }) describe('Base Public methods', () => { diff --git a/packages/core/src/controllers/OptionsController.ts b/packages/core/src/controllers/OptionsController.ts index 38e84023d9..130a686116 100644 --- a/packages/core/src/controllers/OptionsController.ts +++ b/packages/core/src/controllers/OptionsController.ts @@ -3,6 +3,7 @@ import { proxy, snapshot } from 'valtio/vanilla' import type { ConnectMethod, CustomWallet, + DefaultAccountTypes, Features, Metadata, ProjectId, @@ -140,6 +141,11 @@ export interface OptionsControllerStatePublic { * @default false */ allowUnsupportedChain?: boolean + /** + * Default account types for each namespace. + * @default "{ bip122: 'payment', eip155: 'smartAccount', polkadot: 'eoa', solana: 'eoa' }" + */ + defaultAccountTypes: DefaultAccountTypes } export interface OptionsControllerStateInternal { @@ -159,7 +165,8 @@ const state = proxy({ features: ConstantsUtil.DEFAULT_FEATURES, projectId: '', sdkType: 'appkit', - sdkVersion: 'html-wagmi-undefined' + sdkVersion: 'html-wagmi-undefined', + defaultAccountTypes: ConstantsUtil.DEFAULT_ACCOUNT_TYPES }) // -- Controller ---------------------------------------- // @@ -319,6 +326,17 @@ export const OptionsController = { state.useInjectedUniversalProvider = useInjectedUniversalProvider }, + setDefaultAccountTypes( + defaultAccountType: Partial = {} + ) { + Object.entries(defaultAccountType).forEach(([namespace, accountType]) => { + if (accountType) { + // @ts-expect-error - Keys are validated by the param type + state.defaultAccountTypes[namespace] = accountType + } + }) + }, + getSnapshot() { return snapshot(state) } diff --git a/packages/core/src/utils/ConstantsUtil.ts b/packages/core/src/utils/ConstantsUtil.ts index 2a65154ae4..ea440024b9 100644 --- a/packages/core/src/utils/ConstantsUtil.ts +++ b/packages/core/src/utils/ConstantsUtil.ts @@ -1,4 +1,4 @@ -import type { Features, SocialProvider } from './TypeUtil.js' +import type { DefaultAccountTypes, Features, SocialProvider } from './TypeUtil.js' import type { ChainNamespace } from '@reown/appkit-common' const SECURE_SITE = 'https://secure.walletconnect.org' @@ -238,5 +238,12 @@ export const ConstantsUtil = { collapseWallets: false, walletFeaturesOrder: ['onramp', 'swaps', 'receive', 'send'], connectMethodsOrder: undefined - } satisfies Features + } satisfies Features, + + DEFAULT_ACCOUNT_TYPES: { + bip122: 'payment', + eip155: 'smartAccount', + polkadot: 'eoa', + solana: 'eoa' + } as const satisfies DefaultAccountTypes } diff --git a/packages/core/src/utils/TypeUtil.ts b/packages/core/src/utils/TypeUtil.ts index 8edd46fd3a..c66ccef55c 100644 --- a/packages/core/src/utils/TypeUtil.ts +++ b/packages/core/src/utils/TypeUtil.ts @@ -1119,3 +1119,9 @@ export type UseAppKitNetworkReturn = { export type BadgeType = 'none' | 'certified' export type ConnectionStatus = 'connected' | 'disconnected' | 'connecting' | 'reconnecting' + +/** + * @description The default account types for each namespace. + * @default + */ +export type DefaultAccountTypes = { [Key in keyof NamespaceTypeMap]: NamespaceTypeMap[Key] } diff --git a/packages/core/tests/controllers/OptionsController.test.ts b/packages/core/tests/controllers/OptionsController.test.ts index 5de9895b1d..06a8557b33 100644 --- a/packages/core/tests/controllers/OptionsController.test.ts +++ b/packages/core/tests/controllers/OptionsController.test.ts @@ -9,7 +9,13 @@ describe('OptionsController', () => { features: ConstantsUtil.DEFAULT_FEATURES, projectId: '', sdkType: 'appkit', - sdkVersion: 'html-wagmi-undefined' + sdkVersion: 'html-wagmi-undefined', + defaultAccountTypes: { + bip122: 'payment', + eip155: 'smartAccount', + polkadot: 'eoa', + solana: 'eoa' + } }) }) @@ -27,4 +33,14 @@ describe('OptionsController', () => { OptionsController.setAllowUnsupportedChain(true) expect(OptionsController.state.allowUnsupportedChain).toEqual(true) }) + + it('should set defaultAccountType partially and not change if undefined is provided', () => { + OptionsController.setDefaultAccountTypes({ eip155: 'eoa', bip122: undefined }) + expect(OptionsController.state.defaultAccountTypes).toEqual({ + bip122: 'payment', + eip155: 'eoa', + polkadot: 'eoa', + solana: 'eoa' + }) + }) })