diff --git a/.changeset/good-jokes-shave.md b/.changeset/good-jokes-shave.md
new file mode 100644
index 0000000000..4fa4f99c30
--- /dev/null
+++ b/.changeset/good-jokes-shave.md
@@ -0,0 +1,29 @@
+---
+'@reown/appkit-controllers': patch
+'@reown/appkit-scaffold-ui': patch
+'pay-test-exchange': patch
+'@reown/appkit-adapter-bitcoin': patch
+'@reown/appkit-adapter-ethers': patch
+'@reown/appkit-adapter-ethers5': patch
+'@reown/appkit-adapter-solana': patch
+'@reown/appkit-adapter-wagmi': patch
+'@reown/appkit': patch
+'@reown/appkit-utils': 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-siwe': patch
+'@reown/appkit-siwx': patch
+'@reown/appkit-testing': patch
+'@reown/appkit-ui': patch
+'@reown/appkit-universal-connector': patch
+'@reown/appkit-wallet': patch
+'@reown/appkit-wallet-button': patch
+---
+
+Improved "Fund Wallet" flow by adding validation checks from remote config
diff --git a/packages/controllers/src/utils/ConstantsUtil.ts b/packages/controllers/src/utils/ConstantsUtil.ts
index adbb4cb098..910e371a21 100644
--- a/packages/controllers/src/utils/ConstantsUtil.ts
+++ b/packages/controllers/src/utils/ConstantsUtil.ts
@@ -183,6 +183,10 @@ export const ConstantsUtil = {
CommonConstantsUtil.CHAIN.EVM,
CommonConstantsUtil.CHAIN.SOLANA
] as ChainNamespace[],
+ PAY_WITH_EXCHANGE_SUPPORTED_CHAIN_NAMESPACES: [
+ CommonConstantsUtil.CHAIN.EVM,
+ CommonConstantsUtil.CHAIN.SOLANA
+ ] as ChainNamespace[],
ACTIVITY_ENABLED_CHAIN_NAMESPACES: [CommonConstantsUtil.CHAIN.EVM] as ChainNamespace[],
NATIVE_TOKEN_ADDRESS: {
eip155: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
diff --git a/packages/scaffold-ui/src/partials/w3m-account-default-widget/index.ts b/packages/scaffold-ui/src/partials/w3m-account-default-widget/index.ts
index eae288a8b9..8d89e4c715 100644
--- a/packages/scaffold-ui/src/partials/w3m-account-default-widget/index.ts
+++ b/packages/scaffold-ui/src/partials/w3m-account-default-widget/index.ts
@@ -163,14 +163,18 @@ export class W3mAccountDefaultWidget extends LitElement {
return null
}
- const hasNetworkSupport = CoreConstantsUtil.ONRAMP_SUPPORTED_CHAIN_NAMESPACES.includes(
+ const isOnrampSupported = CoreConstantsUtil.ONRAMP_SUPPORTED_CHAIN_NAMESPACES.includes(
this.namespace
)
+ const isPayWithExchangeSupported =
+ CoreConstantsUtil.PAY_WITH_EXCHANGE_SUPPORTED_CHAIN_NAMESPACES.includes(this.namespace)
- const isOnrampEnabled = this.remoteFeatures?.onramp && hasNetworkSupport
const isReceiveEnabled = Boolean(this.features?.receive)
+ const isOnrampEnabled = this.remoteFeatures?.onramp && isOnrampSupported
+ const isPayWithExchangeEnabled =
+ this.remoteFeatures?.payWithExchange && isPayWithExchangeSupported
- if (!isOnrampEnabled && !isReceiveEnabled) {
+ if (!isOnrampEnabled && !isReceiveEnabled && !isPayWithExchangeEnabled) {
return null
}
diff --git a/packages/scaffold-ui/src/partials/w3m-account-wallet-features-widget/index.ts b/packages/scaffold-ui/src/partials/w3m-account-wallet-features-widget/index.ts
index de6526532a..415a519f79 100644
--- a/packages/scaffold-ui/src/partials/w3m-account-wallet-features-widget/index.ts
+++ b/packages/scaffold-ui/src/partials/w3m-account-wallet-features-widget/index.ts
@@ -183,10 +183,22 @@ export class W3mAccountWalletFeaturesWidget extends LitElement {
}
private fundWalletTemplate() {
- const isOnrampEnabled = this.remoteFeatures?.onramp
+ if (!this.namespace) {
+ return null
+ }
+
+ const isOnrampSupported = CoreConstantsUtil.ONRAMP_SUPPORTED_CHAIN_NAMESPACES.includes(
+ this.namespace
+ )
+ const isPayWithExchangeSupported =
+ CoreConstantsUtil.PAY_WITH_EXCHANGE_SUPPORTED_CHAIN_NAMESPACES.includes(this.namespace)
+
const isReceiveEnabled = this.features?.receive
+ const isOnrampEnabled = this.remoteFeatures?.onramp && isOnrampSupported
+ const isPayWithExchangeEnabled =
+ this.remoteFeatures?.payWithExchange && isPayWithExchangeSupported
- if (!isOnrampEnabled && !isReceiveEnabled) {
+ if (!isOnrampEnabled && !isReceiveEnabled && !isPayWithExchangeEnabled) {
return null
}
diff --git a/packages/scaffold-ui/src/views/w3m-deposit-from-exchange-view/index.ts b/packages/scaffold-ui/src/views/w3m-deposit-from-exchange-view/index.ts
index ca04f8a5d3..3265365d3b 100644
--- a/packages/scaffold-ui/src/views/w3m-deposit-from-exchange-view/index.ts
+++ b/packages/scaffold-ui/src/views/w3m-deposit-from-exchange-view/index.ts
@@ -75,6 +75,18 @@ export class W3mDepositFromExchangeView extends LitElement {
public override firstUpdated() {
ExchangeController.fetchExchanges()
ExchangeController.fetchTokenPrice()
+
+ if (this.network) {
+ ExchangeController.setPaymentAsset({
+ network: `${this.network.chainNamespace}:${this.network.id}`,
+ asset: 'native',
+ metadata: {
+ name: this.network.nativeCurrency.name,
+ symbol: this.network.nativeCurrency.symbol,
+ decimals: this.network.nativeCurrency.decimals
+ }
+ })
+ }
}
// -- Render -------------------------------------------- //
diff --git a/packages/scaffold-ui/src/views/w3m-fund-wallet-view/index.ts b/packages/scaffold-ui/src/views/w3m-fund-wallet-view/index.ts
index e90eeb576b..60f3caf497 100644
--- a/packages/scaffold-ui/src/views/w3m-fund-wallet-view/index.ts
+++ b/packages/scaffold-ui/src/views/w3m-fund-wallet-view/index.ts
@@ -79,9 +79,15 @@ export class W3mFundWalletView extends LitElement {
}
private depositFromExchangeTemplate() {
- const isDepositFromExchangeEnabled = this.remoteFeatures?.payWithExchange
+ if (!this.namespace) {
+ return null
+ }
+
+ const isPayWithExchangeEnabled =
+ this.remoteFeatures?.payWithExchange &&
+ CoreConstantsUtil.PAY_WITH_EXCHANGE_SUPPORTED_CHAIN_NAMESPACES.includes(this.namespace)
- if (!isDepositFromExchangeEnabled) {
+ if (!isPayWithExchangeEnabled) {
return null
}
diff --git a/packages/scaffold-ui/test/partials/w3m-account-default-widget.test.ts b/packages/scaffold-ui/test/partials/w3m-account-default-widget.test.ts
index 5d2952e74d..52c80f69b8 100644
--- a/packages/scaffold-ui/test/partials/w3m-account-default-widget.test.ts
+++ b/packages/scaffold-ui/test/partials/w3m-account-default-widget.test.ts
@@ -197,6 +197,28 @@ describe('W3mAccountDefaultWidget', () => {
expect(swapButton).not.toBeNull()
expect(sendButton).toBeNull()
})
+
+ it('should not show fund wallet if payWithExchange, onramp and receive are disabled', async () => {
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue({
+ ...OptionsController.state,
+ features: {
+ swaps: true,
+ receive: false
+ },
+ remoteFeatures: {
+ payWithExchange: false,
+ onramp: false
+ }
+ })
+
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+
+ const fundWalletButton = HelpersUtil.getByTestId(element, FUND_WALLET_BUTTON_TEST_ID)
+
+ expect(fundWalletButton).toBeNull()
+ })
})
describe('solana wallet features', () => {
@@ -281,6 +303,28 @@ describe('W3mAccountDefaultWidget', () => {
expect(swapButton).toBeNull()
expect(sendButton).toBeNull()
})
+
+ it('should not show fund wallet if payWithExchange, onramp and receive are disabled', async () => {
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue({
+ ...OptionsController.state,
+ features: {
+ swaps: true,
+ receive: false
+ },
+ remoteFeatures: {
+ payWithExchange: false,
+ onramp: false
+ }
+ })
+
+ const element: W3mAccountDefaultWidget = await fixture(
+ html``
+ )
+
+ const fundWalletButton = HelpersUtil.getByTestId(element, FUND_WALLET_BUTTON_TEST_ID)
+
+ expect(fundWalletButton).toBeNull()
+ })
})
describe('bitcoin wallet features', () => {
diff --git a/packages/scaffold-ui/test/partials/w3m-account-wallet-features-widget.test.ts b/packages/scaffold-ui/test/partials/w3m-account-wallet-features-widget.test.ts
index 265db69d5a..d31beac957 100644
--- a/packages/scaffold-ui/test/partials/w3m-account-wallet-features-widget.test.ts
+++ b/packages/scaffold-ui/test/partials/w3m-account-wallet-features-widget.test.ts
@@ -294,6 +294,31 @@ describe('wallet features visibility', () => {
expect(HelpersUtil.getByTestId(element, 'wallet-features-swaps-button')).not.toBeNull()
expect(HelpersUtil.getByTestId(element, 'wallet-features-send-button')).toBeNull()
})
+
+ it('should not show fund wallet if payWithExchange, onramp and receive are disabled', async () => {
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue({
+ ...OptionsController.state,
+ features: {
+ swaps: true,
+ receive: false
+ },
+ remoteFeatures: {
+ payWithExchange: false,
+ onramp: false
+ }
+ })
+
+ const element: W3mAccountWalletFeaturesWidget = await fixture(
+ html``
+ )
+
+ const fundWalletButton = HelpersUtil.getByTestId(
+ element,
+ 'wallet-features-fund-wallet-button'
+ )
+
+ expect(fundWalletButton).toBeNull()
+ })
})
describe('solana wallet features', () => {
@@ -403,6 +428,31 @@ describe('wallet features visibility', () => {
expect(HelpersUtil.getByTestId(element, 'wallet-features-swaps-button')).toBeNull()
expect(HelpersUtil.getByTestId(element, 'wallet-features-send-button')).toBeNull()
})
+
+ it('should not show fund wallet if payWithExchange, onramp and receive are disabled', async () => {
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue({
+ ...OptionsController.state,
+ features: {
+ swaps: true,
+ receive: false
+ },
+ remoteFeatures: {
+ payWithExchange: false,
+ onramp: false
+ }
+ })
+
+ const element: W3mAccountWalletFeaturesWidget = await fixture(
+ html``
+ )
+
+ const fundWalletButton = HelpersUtil.getByTestId(
+ element,
+ 'wallet-features-fund-wallet-button'
+ )
+
+ expect(fundWalletButton).toBeNull()
+ })
})
describe('bitcoin wallet features', () => {
diff --git a/packages/scaffold-ui/test/views/w3m-deposit-from-exchange-view.test.ts b/packages/scaffold-ui/test/views/w3m-deposit-from-exchange-view.test.ts
index c48fa10443..75fef006b5 100644
--- a/packages/scaffold-ui/test/views/w3m-deposit-from-exchange-view.test.ts
+++ b/packages/scaffold-ui/test/views/w3m-deposit-from-exchange-view.test.ts
@@ -3,11 +3,36 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { html } from 'lit'
+import type { CaipNetwork } from '@reown/appkit-common'
import { ChainController, ExchangeController } from '@reown/appkit-controllers'
import { W3mDepositFromExchangeView } from '../../src/views/w3m-deposit-from-exchange-view'
import { HelpersUtil } from '../utils/HelpersUtil'
+const mockMainnet = {
+ id: '1',
+ name: 'Mainnet',
+ chainNamespace: 'eip155',
+ nativeCurrency: { symbol: 'ETH', name: 'Ethereum', decimals: 18 },
+ rpcUrls: {
+ default: {
+ http: ['https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY']
+ }
+ }
+}
+
+const mockSolanaMainnet = {
+ id: '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
+ name: 'Solana Mainnet',
+ chainNamespace: 'solana',
+ nativeCurrency: { symbol: 'SOL', name: 'Solana', decimals: 9 },
+ rpcUrls: {
+ default: {
+ http: ['https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY']
+ }
+ }
+}
+
describe('W3mDepositFromExchangeView', () => {
beforeEach(() => {
vi.restoreAllMocks()
@@ -17,6 +42,47 @@ describe('W3mDepositFromExchangeView', () => {
vi.clearAllMocks()
})
+ it('should set network native currency as payment asset', async () => {
+ vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
+ ...ChainController.state,
+ activeCaipNetwork: mockSolanaMainnet as unknown as CaipNetwork
+ })
+
+ const setPaymentAssetSpy = vi.spyOn(ExchangeController, 'setPaymentAsset')
+
+ await fixture(html``)
+
+ expect(setPaymentAssetSpy).toHaveBeenCalledWith({
+ network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
+ asset: 'native',
+ metadata: {
+ name: 'Solana',
+ symbol: 'SOL',
+ decimals: 9
+ }
+ })
+ expect(setPaymentAssetSpy).toHaveBeenCalledTimes(1)
+
+ // Test mainnet
+ vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
+ ...ChainController.state,
+ activeCaipNetwork: mockMainnet as unknown as CaipNetwork
+ })
+
+ await fixture(html``)
+
+ expect(setPaymentAssetSpy).toHaveBeenCalledWith({
+ network: 'eip155:1',
+ asset: 'native',
+ metadata: {
+ name: 'Ethereum',
+ symbol: 'ETH',
+ decimals: 18
+ }
+ })
+ expect(setPaymentAssetSpy).toHaveBeenCalledTimes(2)
+ })
+
it('renders exchanges and asset chip, and calls controller on interactions', async () => {
// Mock active network symbol for the asset chip
vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
diff --git a/packages/scaffold-ui/test/views/w3m-fund-wallet-view.test.ts b/packages/scaffold-ui/test/views/w3m-fund-wallet-view.test.ts
index 29284a552e..dff9cbad87 100644
--- a/packages/scaffold-ui/test/views/w3m-fund-wallet-view.test.ts
+++ b/packages/scaffold-ui/test/views/w3m-fund-wallet-view.test.ts
@@ -9,6 +9,9 @@ import { ChainController, OptionsController, RouterController } from '@reown/app
import { W3mFundWalletView } from '../../src/views/w3m-fund-wallet-view'
import { HelpersUtil } from '../utils/HelpersUtil'
+// -- Constants ----------------------------------------- //
+const DEPOSIT_FROM_EXCHANGE_BUTTON_TEST_ID = 'wallet-features-deposit-from-exchange-button'
+
describe('W3mFundWalletView', () => {
beforeEach(() => {
vi.restoreAllMocks()
@@ -219,4 +222,81 @@ describe('W3mFundWalletView', () => {
)
expect(receiveFundsButton).toBeFalsy()
})
+
+ it('should not show deposit from exchange option when payWithExchange is disabled', async () => {
+ vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
+ ...ChainController.state,
+ activeChain: CommonConstantsUtil.CHAIN.EVM
+ })
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue({
+ ...OptionsController.state,
+ remoteFeatures: {
+ payWithExchange: false
+ }
+ })
+
+ const element: W3mFundWalletView = await fixture(
+ html``
+ )
+
+ await elementUpdated(element)
+
+ const depositFromExchangeButton = HelpersUtil.getByTestId(
+ element,
+ DEPOSIT_FROM_EXCHANGE_BUTTON_TEST_ID
+ )
+ expect(depositFromExchangeButton).toBeFalsy()
+ })
+
+ it('should show deposit from exchange option when payWithExchange is enabled', async () => {
+ vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
+ ...ChainController.state,
+ activeChain: CommonConstantsUtil.CHAIN.EVM
+ })
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue({
+ ...OptionsController.state,
+ remoteFeatures: {
+ payWithExchange: true
+ }
+ })
+
+ const element: W3mFundWalletView = await fixture(
+ html``
+ )
+
+ await elementUpdated(element)
+
+ const depositFromExchangeButton = HelpersUtil.getByTestId(
+ element,
+ DEPOSIT_FROM_EXCHANGE_BUTTON_TEST_ID
+ )
+
+ expect(depositFromExchangeButton).toBeTruthy()
+ })
+
+ it('should not show deposit from exchange option when chain is not supported and payWithExchange is enabled', async () => {
+ vi.spyOn(ChainController, 'state', 'get').mockReturnValue({
+ ...ChainController.state,
+ activeChain: CommonConstantsUtil.CHAIN.BITCOIN
+ })
+ vi.spyOn(OptionsController, 'state', 'get').mockReturnValue({
+ ...OptionsController.state,
+ remoteFeatures: {
+ payWithExchange: true
+ }
+ })
+
+ const element: W3mFundWalletView = await fixture(
+ html``
+ )
+
+ await elementUpdated(element)
+
+ const depositFromExchangeButton = HelpersUtil.getByTestId(
+ element,
+ DEPOSIT_FROM_EXCHANGE_BUTTON_TEST_ID
+ )
+
+ expect(depositFromExchangeButton).toBeNull()
+ })
})