Mobile Linking
This feature is only available for iOS and Android.
Usage​
Communication between a mobile wallet and a mobile application is possible by design. Using the URI displayed by the QR-code it is possible to establish connection by sharing this URI via deep-link.
- iOS
- Android
Using the URI commonly displayed in the QRCode it is possible to establish a connection by sharing this URI through a deep link or universal link on both Android and iOS.
The pattern we chose to adhere for a consistent UX across platforms for connection establishment is the following:
-
Dapp prompts user to connect
-
User presses button to connect and is shown a list of iOS compatible wallets
-
User is redirected to the wallet of choice
-
Wallet prompts user to approve or reject session
-
Return To Dapp
5a. Wallet prompts user to return to Dapp manually
5b. Wallet automatically returns to Dapp using
WalletConnectRouter
-
User returns to Dapp
Similar pattern happens when signing requests are required from the user:
-
Dapp redirects user automatically to previously chosen wallet
-
Wallet prompts user to approve or reject request
-
Return To Dapp
3a. Wallet prompts user to return to Dapp manually
3b. Wallet automatically returns to Dapp using
WalletConnectRouter
-
User returns to Dapp
iOS Wallet Support​
iOS has some more caveats to the integration but we ensure to make it as straightforward as possible. Since its operating system is not designed to handle multiple applications subscribing to the same deep linking schema, we've designed the QRCode Modal to list supporting wallets on our Explorer and target specific deep links or universal links for each wallet.
To add your own wallet to the Explorer, login to your WalletConnect Cloud account.
We recommend that universal links are used instead of deep links for iOS since they provide smoother UX with less prompts. When a dapp triggers a mobile connection on iOS, you should expect the following links
# For deep links
examplewallet://wc?uri=wc:94caa59c77dae0dd234b5818fb7292540d017b27d41f7f387ee75b22b9738c94@2?relay-protocol=irn&symKey=ce3a2c7724c03cf1769ba8b1bdedad5414cc7b920aa3fb72112b997d1916266f
# For universal links
https://example.wallet/wc?uri=wc:94caa59c77dae0dd234b5818fb7292540d017b27d41f7f387ee75b22b9738c94@2?relay-protocol=irn&symKey=ce3a2c7724c03cf1769ba8b1bdedad5414cc7b920aa3fb72112b997d1916266f
Additionally when there is a signing request triggered by the dapp it will hit the deep link with an incomplete URI, this should be ignored and not considered valid as it's only used for automatically redirecting the users to approve or reject a signing request.
# For deep links
examplewallet://wc?uri=wc:00e46b69-d0cc-4b3e-b6a2-cee442f97188@2
# For universal links
https://example.wallet/wc?uri=wc:00e46b69-d0cc-4b3e-b6a2-cee442f97188@2
iOS App Link Constraints​
When using WalletConnect on iOS and triggering a wallet interaction (e.g. when sending a transaction or signing a message), you may experience issues where the native app is not opened as expected and a browser navigation occurs instead. For some wallets (e.g. Rainbow) this will present a fallback website, while other wallets (e.g. MetaMask) will redirect to the App Store.
This issue occurs because app links on iOS will only open the native app when the following rules are followed:
- The wallet interaction must be triggered by a user-initiated event, e.g. in a click handler rather than on page load or in an asynchronous callback.
- The wallet interaction must be triggered as soon as possible within the event handler. Any preceding asynchronous work (e.g. estimating gas, resolving an ENS name, fetching a nonce) should have already completed before the event handler fires. This may require you to design the user experience around this constraint, preventing users from initiating a wallet interaction until it's ready rather than doing the work lazily.
Note that even if your own code follows these rules, libraries you depend on may be running their own asynchronous logic before triggering a wallet interaction. For example, Ethers asynchronously populates transactions before sending them. Known workarounds are documented below, but if you're still experiencing these issues, you should raise them with the relevant library maintainers.
For Ethers v5​
These are the known workarounds for avoiding app linking issues on iOS when using Ethers v5.
When sending a transaction​
-
signer.sendTransaction
should be avoided in favor ofsigner.sendUncheckedTransaction
.This avoids an asynchronous call to retrieve the internal block number which Ethers uses to resolve a complete
TransactionResponse
object.Note that as a result of this optimization,
sendUncheckedTransaction
returns a mock transaction response that only contains thehash
property and await
method. All other properties arenull
. -
The transaction's
to
property should be a plain address rather than an ENS name.This avoids an asynchronous call to automatically resolve ENS names during the send process.
If you still want to support ENS name resolution, you should manually run
provider.resolveName
ahead of time, storing the result before the user attempts to send a transaction. Do not resolve ENS names in the event handler. -
The transaction's
gasLimit
property should be set.This avoids the asynchronous work performed in
sendTransaction
which automatically estimates the gas limit if it's missing.If you still want to use the same gas limit estimation logic from
sendTransaction
, you should manually runprovider.estimateGas
ahead of time, storing the result before the user attempts to send the transaction. Do not estimate gas in the event handler.
When calling a write method on a contract​
-
contract.METHOD_NAME
should be avoided if favor of callingcontract.populateTransaction.METHOD_NAME
ahead of time, then sending the populated transaction withsigner.sendUncheckedTransaction
. -
When sending the populated transaction, you should follow the same guidelines as regular transactions to avoid any asynchronous logic breaking the app link navigation. Do not populate the contract transaction in the event handler.
When signing a message​
- If the message depends on the result of an asynchronous call (e.g. retrieving a nonce when implementing Sign-In With Ethereum), you should do this work ahead of time, storing the result before the user attempts to sign the message. Do not perform this asynchronous work in the event handler.
Deeplinking flow works in the same way in all our products (Sign, Auth, Push, Chat)
The common pattern of establishing a connection between a mobile wallet and a mobile application is the following:
- Dapp shows user a connection button
- User presses the button and Android system shows an app chooser
- User is redirected to the wallet of choice
- Wallet prompts user to approve or reject a session
- User returns to Dapp manually
- After returning to a Dapp a connection between Dapp and wallet is established
Similar pattern happens when Dapp wants to send a signing request to wallet:
- User presses a button to send a signing request to wallet
- User is redirected automatically to already connected wallet
- Wallet prompts user to approve or reject request
- User presses approve or reject request and is redirected automatically to the already connected Dapp
Wallet Support​
Disclaimer: The below solution is designed for the communication between native Android dapps and native Android wallets. In the case of mobile browser dapps and native Android wallets communication, we recommend moving wallets into the background after both approving and rejecting sessions or approving and rejecting requests to persist smooth deep-link UX.
In order to add support for mobile linking within your wallet and receive session proposals, register following deep link in your mobile wallet using intent filters in your Activity/Fragment or deepLink tag in your navigation graph.
Deep link example: examplewallet://wc?uri={pairingUri}
To receive signing request in your Wallet, you'll need to initialize Kotlin SDK with the Redirect
object where you pass a deep link that redirects to your wallet when it comes to receiving signing request from Dapp.
val redirect = "kotlin-wallet-wc://request" //should be unique for your wallet
val appMetaData = Core.Model.AppMetaData(
name = "Wallet Name",
description = "Wallet Description",
url = "Wallet Url",
icons = listOfIconUrlStrings,
redirect = redirect
)
CoreClient.initialize(relayServerUrl = serverUrl, connectionType = connectionType, application = application, metaData = appMetaData)
val init = Wallet.Params.Init(coreClient = CoreClient)
Web3Wallet.initialize(init)
Heads-up: To make this flow working well, Wallet must register one of its Android components with the same deep link that it initialized with.
To check the flow implementation described above have a look on our sample wallet: https://github.com/WalletConnect/WalletConnectKotlinV2/tree/master/sample/wallet
Dapp Support​
To send session proposals to mobile wallet user the pairing URI as deep link that triggers a wallet to open and consume pairing URI
requireActivity().startActivity(Intent(Intent.ACTION_VIEW, deeplinkPairingUri.toUri()))
In order to add support for mobile linking within your Dapp and receive signing request responses from wallet, you'll need to initialize Kotlin SDK with the Redirect
object where you pass a deep link that redirects to your Dapp when it comes to receiving signing request responses from wallet.
val redirect = "kotlin-dapp-wc://request" //should be unique for your Dapp
val appMetaData = Core.Model.AppMetaData(
name = "Wallet Name",
description = "Wallet Description",
url = "Wallet URL",
icons = listOfIconUrlStrings,
redirect = redirect
)
CoreClient.initialize(relayServerUrl = serverUrl, connectionType = connectionType, application = application, metaData = appMetaData)
val init = Sign.Params.Init(core = CoreClient)
SignClient.initialize(init)
Heads-up: To make this flow working well, Dapp must register one of its Android components with the same deep link that it initialized with.
To check the flow implementation described above have a look on our sample Dapp: https://github.com/WalletConnect/WalletConnectKotlinV2/tree/master/sample/dapp
References​
WalletConnectRouter​
Overview​
WalletConnectRouter framework can make working with WalletConnect on a mobile device even more convenient.
For example you are interacting with a DApp from your iOS device and redirect to your wallet through a deeplink to confirm the transaction. After confirmation, you need to return to the DApp by yourself. WalletConnectRouter eliminates this unnecessary step.
By calling WalletConnectRouter.goBack(uri: "example://")
you can return to DApp automatically.
Starting from WalletConnect SDK version 1.9.5, the redirect
field in the AppMetadata
object is mandatory. Ensure that the provided value matches your app's URL scheme to prevent redirection-related issues.
Installation example​
import WalletConnectRouter
try await Sign.instance.approve(proposalId: <proposalId>, namespaces: <namespaces>)
if let uri = proposal.proposer.redirect?.native {
WalletConnectRouter.goBack(uri: uri)
} else {
// Please inform the user to return to the browser
}
Unfortunately, if you are interacting with a DApp from your browser, it's not possible to automatically redirect a user back since iOS 17. It is highly recommended to modify your app's UI to inform the user about the necessity of manually returning to the browser.
If your iOS version is lower than 17, calling WalletConnectRouter.goBack(uri: uri)
will trigger the automatic redirect. Additionally, you can manually trigger the automatic redirect using the old API call Router.goBack()
within the WalletConnectRouter
package.
Was this helpful?