Describe the bug
Since 1.6.7 (#156), AppProposeService.propose routes every proposal through NamespaceMerger.mergeRequiredIntoOptional and always sends requiredNamespaces: [:] on the wire, regardless of what the dApp passes via SessionParams.requiredNamespaces or the (deprecated) SignClient.connect(requiredNamespaces:) overload. There is no public opt-out.
For dApps that transact exclusively on a specific chain, required namespaces are the only mechanism that guarantees the settled session includes an account on that chain. With all-optional proposals, wallets that do not have the chain installed legally drop it and settle without it — MetaMask being the common case (WalletConnect/WalletConnectSwiftV2#1257, MetaMask/metamask-mobile#6703). The dApp then cannot send any request pinned to that chain (rejected with invalidPermissions), cannot expand the settled session, and is forced into either a disconnect/reconnect loop or a permanently depinned request envelope (routing chain-management and signing requests over a different approved chain).
Our iOS app transacts exclusively on Arbitrum One (production) and Arbitrum Sepolia (demo/testnet), proposing the environment's chain as required and the remaining supported chains as optional. On 1.6.7+ the required chain is silently optional, so MetaMask sessions routinely settle without the only chain the app can use.
For what it's worth, the CAIP-25 implementation guidance this change implements retains defined requiredNamespaces as a supported pattern (wallet handling cases #2 and #3 in the post), and frames all-optional as a recommendation for apps ("provide the most flexibility they can afford"), not a prohibition. The equivalent JS-side change (WalletConnect/walletconnect-monorepo#6667) is likewise a silent remap.
SDK Version
To Reproduce
AppKit.set(sessionParams: SessionParams(
requiredNamespaces: [
"eip155": ProposalNamespace(
chains: [Blockchain("eip155:421614")!],
methods: ["eth_signTypedData_v4", "wallet_switchEthereumChain", "wallet_addEthereumChain"],
events: ["chainChanged", "accountsChanged"]
)
]
))
// then present the modal and connect to a wallet that does NOT have chain 421614 installed
- Physical device with MetaMask; Arbitrum Sepolia (421614) not added.
- Connect and approve.
- Observed: the session settles without any
eip155:421614 account — MetaMask's approval UI only offered networks it already had installed, so the app's only transacting chain is missing from the settled namespaces. (Source inspection confirms why: the wc_sessionPropose payload carries requiredNamespaces: {} with everything merged into optionalNamespaces.)
- Expected: the required namespace passes through (pre-1.6.7 behavior), so the wallet either grants/adds the chain or rejects at connect time.
Expected behavior
Keep the current merge as the default for compatibility, but provide an opt-in that passes requiredNamespaces through to SessionProposal unchanged (restoring the pre-1.6.7 code path) — e.g. a SessionParams flag or a non-merging connect variant. We would be happy to contribute a PR if maintainers are open to the shape.
Screenshots
n/a — wire-level behavior; happy to attach a wc_sessionPropose payload capture if useful.
Device (please complete the following information):
- Device: iPhone (physical)
- OS: iOS
- Version: reown-swift 2.3.0, MetaMask Mobile (current)
Additional context
Describe the bug
Since 1.6.7 (#156),
AppProposeService.proposeroutes every proposal throughNamespaceMerger.mergeRequiredIntoOptionaland always sendsrequiredNamespaces: [:]on the wire, regardless of what the dApp passes viaSessionParams.requiredNamespacesor the (deprecated)SignClient.connect(requiredNamespaces:)overload. There is no public opt-out.For dApps that transact exclusively on a specific chain, required namespaces are the only mechanism that guarantees the settled session includes an account on that chain. With all-optional proposals, wallets that do not have the chain installed legally drop it and settle without it — MetaMask being the common case (WalletConnect/WalletConnectSwiftV2#1257, MetaMask/metamask-mobile#6703). The dApp then cannot send any request pinned to that chain (rejected with
invalidPermissions), cannot expand the settled session, and is forced into either a disconnect/reconnect loop or a permanently depinned request envelope (routing chain-management and signing requests over a different approved chain).Our iOS app transacts exclusively on Arbitrum One (production) and Arbitrum Sepolia (demo/testnet), proposing the environment's chain as required and the remaining supported chains as optional. On 1.6.7+ the required chain is silently optional, so MetaMask sessions routinely settle without the only chain the app can use.
For what it's worth, the CAIP-25 implementation guidance this change implements retains defined
requiredNamespacesas a supported pattern (wallet handling cases #2 and #3 in the post), and frames all-optional as a recommendation for apps ("provide the most flexibility they can afford"), not a prohibition. The equivalent JS-side change (WalletConnect/walletconnect-monorepo#6667) is likewise a silent remap.SDK Version
mainas of 2.3.0)To Reproduce
eip155:421614account — MetaMask's approval UI only offered networks it already had installed, so the app's only transacting chain is missing from the settled namespaces. (Source inspection confirms why: thewc_sessionProposepayload carriesrequiredNamespaces: {}with everything merged intooptionalNamespaces.)Expected behavior
Keep the current merge as the default for compatibility, but provide an opt-in that passes
requiredNamespacesthrough toSessionProposalunchanged (restoring the pre-1.6.7 code path) — e.g. aSessionParamsflag or a non-mergingconnectvariant. We would be happy to contribute a PR if maintainers are open to the shape.Screenshots
n/a — wire-level behavior; happy to attach a
wc_sessionProposepayload capture if useful.Device (please complete the following information):
Additional context