# WalletConnect Documentation Source: https://docs.walletconnect.com/index The Connectivity Layer for the Financial Internet
WalletConnect Ecosystem Quickstart

WalletConnect powers apps, wallets and end-users via the WalletConnect network.

Check out our quickstart guides to get started!

# Best Practices Source: https://docs.walletconnect.com/wallets/android/best-practices The purpose of this guide is to show the best practices in regards of the WalletKit client usage. The goal is to provide the best user experience that just works in every circumstances. In order to ensure the best user experience and flawless connection flow, please make sure that WalletKit is initialized immediately after your app launch, especially if launched via a WalletConnect Deep Link. It guarantees that websocket connection is opened immediately and all requests are received by your wallet ## Pairing A pairing is a connection between a wallet and a dapp that has fixed permissions to only allow a dapp to propose a session through it. Dapp can propose infinite number of sessions on one pairing. Wallet must use a pair method from WalletKit client to pair with dapp. ```kotlin theme={null} val pairingParams = Wallet.Params.Pair(pairingUri) WalletKit.pair(pairingParams, onSuccess = { //Subscribed on the pairing topic successfully. Wallet should await for a session proposal }, onError = { error -> //Some error happens while pairing - check Expected errors section } } ``` ### Pairing State A pairing state is a primitive exposed by the WalletKit client for a wallet to indicate whether it should await a session proposal. The pairing state is `true` when a wallet scans a QR and awaits a session proposal. Once the session proposal is received by the wallet, the pairing state is changed to `false`. When `true` wallet should show a loading indicator awaiting a session proposal, when changed to `false` a proposal dialog should be displayed. ```kotlin theme={null} val coreDelegate = object : CoreClient.CoreDelegate { override fun onPairingState(pairingState: Core.Model.PairingState) { //Here a pairing state is triggered } ...other callbacks } CoreClient.setDelegate(coreDelegate) ``` ### Pairing Expiry A pairing expiry event is triggered whenever a pairing is expired. The expiry for inactive pairing is 5 mins, whereas for active pairing is 30 days. A pairing becomes active when a session proposal is received and user successfully approves it. This event helps to know when given pairing expires and update UI accordingly. ```kotlin theme={null} val coreDelegate = object : CoreClient.CoreDelegate { override fun onPairingExpired(expiredPairing: Core.Model.ExpiredPairing) { //Here a pairing expiry is triggered } ...other callbacks } CoreClient.setDelegate(coreDelegate) ``` ### Expected User flow ### Pairing Flow ### Pairing Error ### Expected Errors While pairing the following errors might occur: * No Internet connection error or pairing timeout when scanning QR with no Internet connection * User should pair again with Internet connection * Pairing expired error when scanning a QR code with expired pairing * User should refresh a QR code and scan again * Pairing with existing pairing is not allowed * User should refresh a QR code and scan again. I usually happens when user scans an already paired QR code. ## Session Proposal A session proposal is a handshake sent by a dapp and it's purpose is to define a session rules. Whenever a user wants to establish a connection between a wallet and a dapp, one should approve a session proposal. ### User Action Feedback Whenever user approves or rejects a session proposal, wallet should show loading indicators in a moment of the button press until Relay acknowledgement is received for any of this actions. Session approve ```kotlin theme={null} WalletKit.approveSession(approveProposal, onSuccess = { //Session approval response was sent successfully - update your UI } onError = { error -> //Error while sending session approval - update your UI }) ``` Session reject ```kotlin theme={null} WalletKit.rejectSession(reject, onSuccess = { //Session rejection response was sent successfully - update your UI }, onError = { error -> //Error while sending session rejection - update your UI }) ``` ### Session Proposal Expiry A session proposal expiry is 5 mins. It means a given proposal is stored for 5 mins in the SDK storage and user has 5 mins for approval or rejection decision. After that time the below event is emitted and proposal modal should be removed from the app's UI. ```kotlin theme={null} val walletDelegate = object : WalletKit.WalletDelegate { override fun onProposalExpired(proposal: Wallet.Model.ExpiredProposal) { //Here this event is triggered when a proposal expires - update your UI } ...other callbacks } WalletKit.setWalletDelegate(walletDelegate) ``` ### Expected User flow ### Approve or Reject Session Proposal ### Error Handling ### Expected Errors While approving or rejecting a session proposal the following errors might occurs: * No Internet connection * It happens when a user tries to approve or reject session proposal with no Internet connection * Session proposal expired * It happens when users tries to approve or reject expired session proposal * Invalid namespaces * It happens when a validation of session namespaces fails * Timeout * It happens when Relay doesn't acknowledge session settle publish within 10s ## Session Request A session request represents the request sent by a dapp to a wallet. ### User Action Feedback Whenever user approves or rejects a session request, wallet should show loading indicators in a moment of the button press until Relay acknowledgement is received for any of this actions. ```kotlin theme={null} WalletKit.respondSessionRequest(Wallet.Params.SessionRequestResponse, onSuccess = { //Session request response was sent successfully - update your UI }, onError = { error -> //Error while sending session response - update your UI }) ``` ### Session Request Expiry A session request expiry is defined by a dapp. It's value must be between now() + 5mins and now() + 7 days. After the session request expires the below event is emitted and session request modal should be removed from the app's UI. ```kotlin theme={null} val walletDelegate = object : WalletKit.WalletDelegate { override fun onRequestExpired(request: Wallet.Model.ExpiredRequest) { //Here this event is triggered when a session request expires - update your UI } ...other callbacks } WalletKit.setWalletDelegate(walletDelegate) ``` ### Expected User flow ### Approve or Reject Session Proposal ### Error Handling ### Expected Errors While approving or rejecting a session request the following error might occur: * Invalid session * This error might happen when user approves or rejects a session request on expired session * Session request expired * This error might happen when user approves or rejects a session request that already expires * Timeout * It happens when Relay doesn't acknowledge session settle publish within 10s ## Web Socket Connection State The Web Socket connection state tracks the connection with the relay server, event is emitted whenever a connection state changes. ```kotlin theme={null} val walletDelegate = object : WalletKit.WalletDelegate { override fun onConnectionStateChange(state: Wallet.Model.ConnectionState) { //Here this event is triggered when a connection state has changed } ...other callbacks } WalletKit.setWalletDelegate(walletDelegate) ``` ### Expected User flow ### Connection State ![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/assets/connection_state.gif) # Chain Abstraction Source: https://docs.walletconnect.com/wallets/android/chain-abstraction 💡 Chain Abstraction is in early access. Chain Abstraction in WalletConnect Wallet SDK enables users with stablecoins on any network to spend them on-the-fly on a different network. Our Chain Abstraction solution provides a toolkit for wallet developers to integrate this complex functionality using Wallet SDK. For example, when an app requests a 100 USDC payment on Base network but the user only has USDC on Arbitrum, Wallet SDK offers methods to detect this mismatch, generate necessary transactions, track the cross-chain transfer, and complete the original transaction after bridging finishes. ## How It Works Apps need to pass `gas` as null, while sending a transaction to allow proper gas estimation by the wallet. Refer to this [guide](https://docs.reown.com/appkit/next/early-access/chain-abstraction) for more details. When sending a transaction, you need to: 1. Check if the required chain has enough funds to complete the transaction 2. If not, use the `prepare` method to generate necessary bridging transactions 3. Sign routing and initial transaction hashes, prepared by the prepare method 4. Use `execute` method to broadcast routing and initial transactions and wait for it to be completed The following sequence diagram illustrates the complete flow of a chain abstraction operation, from the initial dapp request to the final transaction confirmation ## Methods The following methods from Wallet SDK are used in implementing chain abstraction. 💡 Chain abstraction is currently in the early access phase and requires the `@ChainAbstractionExperimentalApi` annotation. ### Prepare This method is used to check if chain abstraction is needed. If it is, it will return a `PrepareSuccess.Available` object with the necessary transactions and funding information. If it is not, it will return a `PrepareSuccess.NotRequired` object with the original transaction. Accounts field is a list of CAIP-20 accounts you are sourcing from e.g. Solana account ```kotlin theme={null} @ChainAbstractionExperimentalApi fun prepare( initialTransaction: Wallet.Model.InitialTransaction, accounts: List, onSuccess: (Wallet.Model.PrepareSuccess) -> Unit, onError: (Wallet.Model.PrepareError) -> Unit ) ``` ### Execute This method is used to execute the chain abstraction operation. It broadcasts the bridging and initial transactions and waits for them to be completed. The method returns a `ExecuteSuccess` object with the transaction hash and receipt. ```kotlin theme={null} @ChainAbstractionExperimentalApi fun execute( prepareAvailable: Wallet.Model.PrepareSuccess.Available, prepareSignedTxs: List, initSignedTx: String, onSuccess: (Wallet.Model.ExecuteSuccess) -> Unit, onError: (Wallet.Model.Error) -> Unit ) ``` ## Usage When sending a transaction, first check if chain abstraction is needed using the `prepare` method. If it is needed, you must sign all the fulfillment transactions and use the `execute` method. If the operation is successful, use `execute` method and await the transaction hash and receipt. If the operation is unsuccessful, send the JsonRpcError to the dapp and display the error to the user. ```kotlin theme={null} val initialTransaction = Wallet.Model.Transaction(...) WalletKit.ChainAbstraction.prepare( initialTransaction, caip10Accounts, onSuccess = { prepareSuccess -> when (prepareSuccess) { is Wallet.Model.PrepareSuccess.Available -> { // If the route is available, present a CA transaction flow //sign route transactions transactionsDetails?.route?.forEach { route -> route.transactionDetails.forEach { transactionDetails -> val signedTransaction = Signer.signHash(transactionDetails.transactionHashToSign, EthAccountDelegate.privateKey) eip155Signatures.add(signedTransaction) } } } //sign initial transaction val signedInitialTx = Signer.signHash(transactionsDetails?.initialDetails.transactionHashToSign, EthAccountDelegate.privateKey) //Call the execute WalletKit.ChainAbstraction.execute(prepareSuccess, eip155Signatures, signedInitialTx onSuccess = { //The execution of the Chain Abstraction is successfull //Send the response to the Dapp or show to the user }, onError = { //Execute error - wallet should send the JsonRpcError to a dapp for given request and display error to the user } ) } is Wallet.Model.PrepareSuccess.NotRequired -> { // user does not need to move funds from other chains, sign and broadcast original transaction } } }, onError = { prepareError -> // One of the possible errors: NoRoutesAvailable, InsufficientFunds, InsufficientGasFunds - wallet should send the JsonRpcError to a dapp for given request and display error to the user } ) ``` For example, check out implementation of chain abstraction in [sample wallet](https://github.com/WalletConnect/WalletConnectKotlinV2/tree/master/sample/wallet) with Kotlin. ## Error Handling When implementing Chain Abstraction, you may encounter different types of errors. Here's how to handle them effectively: ### Application-Level Errors These errors (`PrepareError`) indicate specific issues that need to be addressed and typically require user action: * **Insufficient Gas Fees**: User needs to add more gas tokens to their wallet * **Malformed Transaction Requests**: Transaction parameters are invalid or incomplete * **Minimum Bridging Amount Not Met**: Currently set at \$0.60 * **Invalid Token or Network Selection**: Selected token or network is not supported When handling these errors, you should display clear, user-friendly error messages that provide specific guidance on how to resolve the issue. Allow users to modify their transaction parameters and consider implementing validation checks before initiating transactions. ### Retryable Errors These errors (`Result::Err`) indicate temporary issues that may be resolved by retrying the operation. Examples of these types of issues include network connection timeouts, TLS negotiation issues, service outages, or other transient errors. For retryable errors, show a generic "oops" message to users and provide a retry button. Log detailed error information to your error tracking service, but avoid displaying technical details to end users. For errors in the `execute()` method, a retry may not resolve the issue. In such cases, allow users to cancel the transaction, return them to the application, and let the application initiate a new transaction. ### Critical Errors Critical errors indicate bugs or implementation issues that should be treated as high-priority incidents: incorrect usage of WalletKit API, wrong data encoding or wrong fields passed to WalletKit, or WalletKit internal bugs. ## Testing To test Chain Abstraction, you can use the [AppKit laboratory](https://appkit-lab.reown.com/library/wagmi/) and try sending any supported [tokens](/wallets/features/chain-abstraction#what-are-the-supported-tokens-and-networks%3F) with any chain abstraction supported wallet. You can also use this [sample wallet](https://appdistribution.firebase.dev/i/076a3bc9669d3bee) for testing. ## ProGuard rules If you encounter issues with minification, add the below rules to your application: ``` -keepattributes *Annotation* -keep class com.sun.jna.** { *; } -keepclassmembers class com.sun.jna.** { native ; *; } -keep class uniffi.** { *; } # Preserve all public and protected fields and methods -keepclassmembers class ** { public *; protected *; } -dontwarn uniffi.** -dontwarn com.sun.jna.** ``` # Analytics Source: https://docs.walletconnect.com/wallets/android/cloud/analytics ## Accessing Reown Analytics To access Reown Analytics and explore these insightful features, follow these simple steps: 1. Log In to your Cloud Account [here](https://dashboard.walletconnect.com/sign-in). 2. Click on your Project. 3. Click the Analytics Tab. 4. Select the Analytics section of your choice. By following these steps, you can easily access and leverage Reown Analytics to track your project's progress and make informed decisions to take your project to the next level. ## Understanding Reown Analytics WalletConnect Dashboard now includes Analytics to help you better understand your project's performance. Let's break down some terms and explore the new analytics sections in a simple manner. ## Analytics Sections **Definitions** Refer to [Definitions](#definitions) for the meaning of terms used in Reown Analytics. ### Relay #### Overview - Wallet/Dapp Sessions Displays the total count of established connections between your project and Reown SDK. #### Overview - Clients Indicates the total number of connections established from clients (device or browser if connecting on the web). #### Overview - Messages Shows the total messages exchanged between the configured Reown SDK and the Relay Server. #### Wallet/Dapp Sessions Shows the daily trend of established sessions over a 30 day period. #### Clients Shows the daily trend of client connections over a 30 day period. #### All Messages Shows the daily trend of messages connections over a 30 day period. #### Projects Lists the top ranked wallets/Dapps connected to your project. #### Countries and Continents Provides insights into user connections by displaying the countries and continents with the most connections. Learn more about the Relay [here](./relay) ### RPC #### Overview RPC Requests Represents the total count of remote procedure calls (RPC) made to the blockchain API for the last 30 days. #### RPC Request Volumes Displays the daily trend of API requests made to the blockchain API. #### RPC Chain Shows the top chain requests made by Chain ID. #### RPC Method Highlights the top-ranked methods called by your users. #### Countries Illustrates user connections by displaying the countries with the most connections. Learn more about the Blockchain API [here](./blockchain-api) ### AppKit #### Avg. Daily Visitors Indicates the daily average of unique visitors to your app’s AppKit. #### Avg. Daily Sessions Indicates the daily average of sessions. #### Avg. Daily Connections Indicates the daily average of connections made through AppKit. #### Sessions Indicates the total count of sessions. #### Successful connections Total count of all connections made between a wallet and your app. #### Countries Ranks the top countries with the highest user connections. #### Wallets Breakdown Ranks the top wallets that your users are connecting from. #### All Events This table and chart shows the count of various events that are triggered as the users interact with AppKit. #### Platform Sessions Provides a breakdown of sessions that have been created by device platform. #### Visitors Shows the daily trend of unique visitors to your app’s AppKit. #### Sessions Shows the daily trend of sessions created when the user signs a message with their connected wallet. #### Successful connections Shows the daily trend of successful connections to your app. ### Definitions Definitions of terms used in Reown Analytics. | Term | Description | | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Relay:Session** | A session within the context of Relay analytics denotes meaningful user actions, like signing transactions for NFT sales or trades, within a wallet or dapp. It emphasizes core SDK functionality. | | **AppKit:Session** | A session within the context of AppKit analytics represents the connection established between your project and your user’s device (includes browsers). Sessions are created when the user interacts with AppKit on your app. If user events are tracked within a 30-minute range, they will be considered within the same session. | | **Message** | Messages are data exchanges between the Reown SDK and the Relay Server, facilitating communication between your project and connected clients. | | **Client** | A client is a device or browser connected to your project. | | **Blockchain API** | The interface that allows your project to interact with the blockchain. Remote Procedure Calls (RPC) are used to request information or execute operations on the blockchain through this API. | | **Chain ID** | Chain ID identifies a specific blockchain network. Different blockchain networks, such as Ethereum Mainnet or a testnet, have unique Chain IDs. | # Explorer Submission Source: https://docs.walletconnect.com/wallets/android/cloud/explorer-submission **Note** Submitting a project to the WalletConnect Dashboard Explorer is recommended but optional. You can still use WalletConnect services without submitting your project. However, doing so ensures that your project is listed under [WalletGuide](https://walletguide.walletconnect.network/?utm_source=walletconnect-docs\&utm_medium=cloud\&utm_campaign=explorer-submission) and [Cloud Explorer API](/wallets/walletguide/explorer-api). ## Creating a New Project * Head over to [dashboard.walletconnect.com](https://dashboard.walletconnect.com/) and create a new project by clicking the "New Project" button in top right corner of the dashboard. * Give a suitable name to your project, select whether its an App or Wallet and click the "Create" button. (You can change this later) ## Project Details * Go to the "Explorer" tab and fill in the details of your project. | Field | Description | Required | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -------- | | **Name** | The name to display in the explorer | Yes | | **Description** | A short description explaining your project (dapp/wallet) | Yes | | **Type** | Whether your project is a dapp or a wallet | Yes | | **Category** | Appropriate category for your project. This field is dependent on the type of your project | Yes | | **Homepage** | The URL of your project | Yes | | **Web App** | The URL of your web app. This field is only applicable for dapps | Yes | | **Chains** | Chains supported by your project | Yes | | **Logo** | The logo of your project. Further requirements are provided in the explorer submission form | Yes | | **Testing Instructions** | Instructions on how to test your WalletConnect Integration | Yes | | **Download Links** | Links to download your project (if applicable) | No | | **Mobile Linking** | Required for mobile wallets targeting AppKit. Deep Link is recommended over Universal Link | No | | **Desktop Linking** | Required for desktop wallets targeting AppKit. | No | | **Injected Wallet Identifiers** | Required for injected wallets targeting AppKit. RDNS (from EIP-6963 metadata) is recommended over Provider Flags (Legacy) | No | | **Metadata** | User-facing UI metadata for your project. Only Short Name is required. | No | ## Project Submission * Once you've filled the applicable fields, click the "Submit" button to submit your project for review. Alternatively, you can save your changes and submit later. Additional information will be visible in the modal that appears after clicking the "Submit" button. ## How do we test wallets? In order to offer a great user experience in our APIs and SDKs every Cloud submission goes through a QA process to make sure that the integration of the WalletConnect protocol is working correctly. The following list details our QA flow and how to reproduce it: | Test Case | Steps | Expected Results | | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Set Up** | 1. Download the wallet
2. Install the wallet app
3. Sign up for an account with the wallet app
4. Create one or more accounts | 1. N/A
2. The app is installed
3. I have an account
4. I have one or more accounts | | **Connect to dapp via web browser** | 1. Open the Reown connection page [https://appkit-lab.reown.com/](https://appkit-lab.reown.com/) from a PC
2. Press on the “Connect Wallet” button and select the Reown option.
3. Open the wallet app and use the scan QR option to connect.
4. Accept on the wallet the connection request | 1. The app has been correctly set-up
2. A modal with wallet options is opened
3. A QR code is shown on the website and the wallet is able to scan it.
4. The connection is successfully established. The wallet data is now shown on the website. | | **Connect to dapp via mobile browser (Deep-link)** | 1. Open [https://appkit-lab.reown.com/](https://appkit-lab.reown.com/) in your mobile device.
2. Select one of the default options (e.g. Wagmi for EVM chains). Press the "Custom Wallet" button from the navbar. Fill in the wallet’s name and its deeplink (Mobile Link) in the “Add a Custom Wallet” form. Press “Add Wallet”. After the website reloads, press the “Connect Wallet” button and select the newly created wallet.
3. Accept the connection request in the wallet application. | 1. N/A
2. A form should show up on the website to fill in the wallet’s data. After the changes are applied, the modal should show the newly created wallet on the main view.
3. The user should be redirected to the wallet application and a modal with a connection request should show up on the wallet application. The wallet should connect successfully. On Android devices, the user should be redirected back to the website after accepting the connection request. | | **Switch chains - dapp side** | 1. Once the wallet is connected, press on the modal button on the top right of the website.
2. Press the first button of the modal to switch the chain.
3. Select any available chain, close the modal, and press the “Send Transaction” button | 1. A modal with the account information should pop up on the website.
2. A new view with supported chains should show up.
3. The transaction request that pops up on the wallet should show in their information the correct chain that was previously selected. | | **Switch Chains - wallet side (if supported)** | 1. Check if the wallet supports chain switching. If so, select a different chain from the connected one. | 1. The chain change should be reflected on the website. The first card shows the current chain ID. | | **Accounts Switching - wallet side** | 1. In the wallet app, switch from one account to another. | 1. The account switch event should be reflected in the modal’s account view on the website. | | **Disconnect a wallet** | 1. Select the "Disconnect" button from the Wallet App (Ideally, wallets should have a section where users can see all their existing dApp connections and manage/disconnect from dApps in one spot—this is not always true, so if not possible, just skip this).
2. Repeat the above steps and press the "Disconnect" button from the dApp (this should always be available). | 1. The related session should disappear from the dApp and the Wallet App.
2. The related session should disappear from the dApp and the Wallet App. | | **Verify API** | 1. Open [https://malicious-app-verify-simulation.vercel.app/](https://malicious-app-verify-simulation.vercel.app/)
2. Select a supported chain by the wallet (some wallets don’t support testnets) and press the “Connect” button.
3. Scan with the wallet the generated QR code. | 1. N/A
2. A modal should show up with a QR code to scan.
3. The connection request in the wallet should flag the website as malicious. | ### Chain Specific The following test cases only apply for wallets supporting a particular set of chains. | Test Case | Steps | Expected Results | | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Supporting personal\_sign** | 1. Connect the wallet.
2. Press the “Sign Message” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. | | **Supporting eth\_signTypedData\_v4** | 1. Connect the wallet.
2. Press the “Sign Typed Data” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. | | **Supporting eth\_sendTransaction** | 1. Connect the wallet.
2. Press the “Send Transaction” button. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature. |
| Test Case | Steps | Expected Results | | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Supporting solana\_signMessage** | 1. Connect the wallet to [AppKit Lab](https://appkit-lab.reown.com/library/solana)
2. Press the “Sign Message” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. | | **Supporting solana\_signTransaction** | 1. Connect the wallet to [AppKit Lab](https://appkit-lab.reown.com/library/solana)
2. Press the “Sign Transaction” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. | | **Supporting v0 Transactions** | 1. Connect the wallet to [AppKit Lab](https://appkit-lab.reown.com/library/solana)
2. Press the “Sign Versioned Transaction” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. |
## What's Next? Now depending on whether or not your submission met all parameters, you will receive an email from the WalletConnect team with the status of your submission. This change will also be reflected with more directions in the "Explorer" tab of your project. If your submission was not accepted, you can make the necessary changes and resubmit your project for review. The reason for rejection will be mentioned in the email and in the "Explorer" tab of your project. In case of any questions, feel free to ask on [Github Discussions](https://github.com/orgs/WalletConnect/discussions/categories/explorer-support) # Relay Source: https://docs.walletconnect.com/wallets/android/cloud/relay ## Project ID The Project ID is consumed through URL parameters. URL parameters used: * `projectId`: Your Project ID can be obtained from [dashboard.walletconnect.com](https://dashboard.walletconnect.com) Example URL: `https://relay.walletconnect.com/?projectId=c4f79cc821944d9680842e34466bfbd` This can be instantiated from the client with the `projectId` in the `SignClient` constructor. ```javascript theme={null} import SignClient from '@walletconnect/sign-client' const signClient = await SignClient.init({ projectId: 'c4f79cc821944d9680842e34466bfb' }) ``` ## Allowlist To help prevent malicious use of your project ID you are strongly encouraged to set an allowlist of [origins](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin) or application/bundle ids for mobile applications where the project ID is used. Requests from other origins will be denied. * Allowlist supports a list of origins in the format `[scheme://] ## Capabilities in CAIP-25 Connection Requests CAIP-25 defines how capabilities can be expressed in wallet-to-dapp connections. These capabilities control how methods like `wallet_sendCalls` behave. ### Session Properties In a connection request, dApps can request capabilities through `sessionProperties`. These capabilities can be universal (applying to all chains) or chain-specific: ```json theme={null} "sessionProperties": { "expiry": "2022-12-24T17:07:31+00:00", "caip154": { "supported": "true" }, "flow-control": { "loose": [], "strict": [], "exoticThirdThing": [] }, "atomic": { "status": "supported" } } ``` ### Scoped Properties For chain-specific capabilities, dapps use `scopedProperties`: ```json theme={null} "scopedProperties": { "eip155:8453": { "paymasterService": { "supported": true }, "sessionKeys": { "supported": true } }, "eip155:84532": { "auxiliaryFunds": { "supported": true } } } ``` ### Wallet Response The wallet's response should specify the capabilities it supports, in accordance with EIP-5792 and CAIP-25: ```json theme={null} "sessionProperties": { "expiry": "2022-12-24T17:07:31+00:00", "caip154": { "supported": "true" }, "flow-control": { "loose": ["halt", "continue"], "strict": ["continue"] }, "atomic": { "status": "ready" } }, "scopedProperties": { "eip155:1": { "atomic": { "status": "supported" } }, "eip155:137": { "atomic": { "status": "unsupported" } }, "eip155:84532": { "eip155:83532:0x0910e12C68d02B561a34569E1367c9AAb42bd810": { "auxiliaryFunds": { "supported": false }, "atomic": { "status": "supported" } } } } ``` * Capabilities shared across all address in a namespace can be expressed at top-level * Address-specific capabilities can include exceptions to scope-wide capabilities ### Atomic Capability According to [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792), the `atomic` capability specifies how the wallet handles batches of transactions. It has three possible values: * `supported` — The wallet executes calls atomically and contiguously. * `ready` — The wallet can upgrade to support atomic execution, pending user approval. * `unsupported` — The wallet provides no atomicity guarantees. This capability is expressed per chain and is crucial for determining how `wallet_sendCalls` with `atomicRequired: true` will be handled. ### Example The `wallet_getCapabilities` method is used to request information about what capabilities a wallet supports. Following EIP-5792, here's how it should be implemented: #### Request ```json theme={null} { "id": 1, "jsonrpc": "2.0", "method": "wallet_getCapabilities", "params": ["0xd46e8dd67c5d32be8058bb8eb970870f07244567", ["0x2105", "0x14A34"]] } ``` #### Response The wallet should return a response following EIP-5792, where capabilities are organized by chain ID: ```json theme={null} { "id": 1, "jsonrpc": "2.0", "result": { "0x2105": { "atomic": { "status": "supported" } }, "0x14A34": { "atomic": { "status": "unsupported" } } } } ``` ### Implementation When implementing `wallet_sendCalls`, wallets must follow these requirements: #### Connection Approval * Only approve this method during the connection approval flow if your wallet can implement it correctly * Define the `atomic` capability per chain/account in the CAIP-25 response #### Request Format ```json theme={null} { "id": 12345, "version": "2.0", "method": "wc_sessionRequest", "params": { "chainId": "caip-2-chain-id", "request": { "method": "wallet_sendCalls", "params": { "from": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "chainId": "0x01", "atomicRequired": true, "calls": [ { "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "value": "0x9184e72a", "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" }, { "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", "value": "0x182183", "data": "0xfbadbaf01" } ] } } } } ``` #### Core Implementation Requirements * Execute calls in the exact order specified in the request * Do not wait for any calls to be finalized before completing the batch * If the user rejects the request, do not send any calls #### Atomic Execution Behavior When `atomicRequired` is `true`: * Execute all calls atomically (either all succeed or none have any effect) * Execute all calls contiguously (no other transactions between batch calls) * If your wallet can upgrade from `ready` to `supported` atomicity, do so before executing When `atomicRequired` is `false`: * You may execute calls sequentially without atomicity guarantees * You may execute atomically if your wallet supports it * You may upgrade to `supported` atomicity and execute atomically #### Response Enrichment To enhance the user experience and eliminate the need for app switching, wallets can enrich the wallet\_sendCalls response with caip2 id and transactionHash to let the Universal Provider resolve the transaction hash. ```json theme={null} { "id": "...", "capabilities": { "caip345": { "caip2": "eip155:1", "transactionHashes": ["..."], } } } ``` ### Example To enhance the user experience and eliminate the need for app switching, wallets can enrich the wallet\_sendCalls response with caip2 id and transactionHash to let the Universal Provider resolve the transaction hash. To implement this functionality, the response for wallet\_sendCalls should be enriched with capabilities: ```json theme={null} { "id": "...", "capabilities": { "caip345": { "caip2": "eip155:1", "transactionHashes": ["..."], } } } ``` ### Response Format The response format for `wallet_getCallsStatus` varies based on the execution method: For non-atomic execution, include all transactions in the receipts array, even those that were included on-chain but eventually reverted. #### For Atomic Execution ```json theme={null} { "receipts": [/* single receipt or array of receipts */], "atomic": true } ``` #### For Non-Atomic Execution ```json theme={null} { "receipts": [/* array of receipts for all transactions */], "atomic": false } ``` ## References * EIP-5792: [https://eips.ethereum.org/EIPS/eip-5792#atomicbatch-capability](https://eips.ethereum.org/EIPS/eip-5792#atomicbatch-capability) * CAIP-25 namespaces: [https://github.com/ChainAgnostic/namespaces/blob/main/eip155/caip25.md](https://github.com/ChainAgnostic/namespaces/blob/main/eip155/caip25.md) # Installation Source: https://docs.walletconnect.com/wallets/android/installation Add the `jitpack.io` Maven repository to your `root/build.gradle.kts` file. For example: ```gradle theme={null} allprojects { repositories { mavenCentral() maven { url "https://jitpack.io" } } } ``` In `app/build.gradle.kts` add the WalletKit package and its dependencies: ```gradle theme={null} implementation("com.reown:android-core:release_version") implementation("com.reown:walletkit:release_version") ``` ## ProGuard rules If you encounter issues with minification, add the below rules to your application: ``` -keepattributes *Annotation* -keep class com.sun.jna.** { *; } -keepclassmembers class com.sun.jna.** { native ; *; } -keep class uniffi.** { *; } # Preserve all public and protected fields and methods -keepclassmembers class ** { public *; protected *; } -dontwarn uniffi.** -dontwarn com.sun.jna.** ``` ## Next Steps Now that you've installed Wallet SDK, you're ready to start integrating it. The next section will walk you through the process of setting up your project to use the SDK. # Link Mode Source: https://docs.walletconnect.com/wallets/android/link-mode The Wallet SDK Link Mode is a low latency mechanism for transporting [One-Click Auth](/wallets/android/one-click-auth) requests and session requests over Universal Links, reducing the need for a WebSocket connection with the Relay. This significantly enhances the user experience when connecting native dApps to native wallets by reducing the latency associated with network connections, especially when the user has an unstable internet connection. To support Link Mode add a universal link for your wallet in Cloud project configuration dashboard, configure your AppMetaData `appLink` with a valid universal link and set the `linkMode` property to `true`: Make sure that [1-Click Auth](/wallets/android/one-click-auth) is implemented before enabling Link Mode. ```kotlin {3-4} theme={null} val appMetaData = Core.Model.AppMetaData( ... appLink = "https://example.com/example_wallet", linkMode = true ) CoreClient.initialize( metaData: appMetaData, ... ) WalletKit.initialize(Wallet.Params.Init(core = CoreClient)) ``` Once link mode and app link are properly configured and the user interacts with a link mode supporting dApp, your wallet will receive requests over app links. You must pass these requests to WalletKit so it can process them: ```kotlin theme={null} val url = intent.dataString WalletKit.dispatchEnvelope(url) { error -> //handle error } ``` Ensure to handle incoming app links in your Activity onCreate method and in onNewIntent callback. Ensure that your App Link is properly configured in your app's Manifest file with the `autoVerify` set to `true`: ``` ``` For more information on how to configure app links for your app, refer to the [Android Documentation](https://developer.android.com/training/app-links/verify-android-applinks). For enabling links to app content check [this](https://developer.android.com/training/app-links/deep-linking) documentation page. For more information on how to interact with other apps using intents, see [Android Intent Documentation](https://developer.android.com/training/basics/intents). # Mobile Linking Source: https://docs.walletconnect.com/wallets/android/mobile-linking This feature is only relevant to native platforms. ## Usage Mobile Linking allows your wallet to automatically redirect back to the Dapp allowing for less user interactions and hence a better UX for your users. ### Establishing Communication Between Mobile Wallets and Apps When integrating a wallet with a mobile application, it's essential to understand how they communicate. The process involves two main steps: 1. **QR Code Handshake:** The mobile app (Dapp) generates a unique URI (Uniform Resource Identifier) and displays it as a QR code. This URI acts like a secret handshake. When the user scans the QR code using their wallet app, they establish a connection. It's like saying, "Hey, let's chat!" 2. **Deep Links and Universal Links:** The URI from the QR code allows the wallet app to create a [deep link](https://support.google.com/google-ads/answer/10023042?hl=en#:~:text=Deep%20links%20send%20mobile%20device,%2C%20Shopping%2C%20and%20Display%20campaigns.) or [universal link](https://developer.apple.com/ios/universal-links/). These links work on both Android and iOS. They enable seamless communication between the wallet and the app. **Developers should prefer Deep Linking over Universal Linking.** Universal Linking may redirect the user to a browser, which might not provide the intended user experience. Deep Linking ensures the user is taken directly to the app. ### Key Behavior to Address In some scenarios, wallets use redirect metadata provided in session proposals to open applications. This can cause unintended behavior, such as: Redirecting to the wrong app when multiple apps share the same redirect metadata (e.g., a desktop and mobile version of the same Dapp). Opening an unrelated application if a QR code is scanned on a different device than where the wallet is installed. #### Recommended Approach To avoid this behavior, wallets should: * **Restrict Redirect Metadata to Deep Link Use Cases**: Redirect metadata should only be used when the session proposal is initiated through a deep link. QR code scans should not trigger app redirects using session proposal metadata. The connection and sign request flows are similar across platforms. ### Connection Flow * **Dapp Prompts User:** The Dapp asks the user to connect. * **User Chooses Wallet:** The user selects a wallet from a list of compatible wallets. * **Redirect to Wallet:** The user is redirected to their chosen wallet. * **Wallet Approval:** The wallet prompts the user to approve or reject the session (similar to granting permission). * **Return to Dapp:** * **Manual Return:** The wallet asks the user to manually return to the Dapp. * **Automatic Return:** Alternatively, the wallet automatically takes the user back to the Dapp. * **User Reunites with Dapp:** After all the interactions, the user ends up back in the Dapp. ![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/mobileLinking-light.png) ### Sign Request Flow When the Dapp needs the user to sign something (like a transaction), a similar pattern occurs: * **Automatic Redirect:** The Dapp automatically sends the user to their previously chosen wallet. * **Approval Prompt:** The wallet asks the user to approve or reject the request. * **Return to Dapp:** * **Manual Return:** The wallet asks the user to manually return to the Dapp. * **Automatic Return:** Alternatively, the wallet automatically takes the user back to the Dapp. * **User Reconnects:** Eventually, the user returns to the Dapp. ![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/mobileLinking_sign-light.png) ## Platform preparations In order for Dapps to be able to trigger your wallet for a connection or sign request using deep links you first need to add your own wallet to the Explorer by login to your [WalletConnect Dashboard](https://dashboard.walletconnect.com/sign-in) account, declare a deep link and define an [``](https://developer.android.com/training/app-links/deep-linking#adding-filters) in your wallet's Manifest.xml with the same deep link added in Explorer: ```xml theme={null} ``` Dapps developers must do the same for their own custom schemes if they want the wallet to be able to navigate back after a session approval or a sign request response ### How to test Before submitting your project to the Cloud Explorer you can test mobile linking in our sample Dapp: 1. On your mobile device, visit the appropriate link: * For EVM: [https://appkit-lab.reown.com/library/wagmi/](https://appkit-lab.reown.com/library/wagmi/) * For Solana: [https://appkit-lab.reown.com/library/solana/](https://appkit-lab.reown.com/library/solana/) 2. Click the "Custom Wallet" button and fill in the form with your wallet information. The website will reload and your wallet will be stored locally. 3. Click the "Connect Wallet" button and choose your mobile wallet. It *should* automatically open and redirect to your wallet. Learn more about mobile linking in the [Best Practices section](/wallets/android/best-practices#2-mobile-linking). ## Integration #### 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. To support universal native modal and WalletConnectModal register: `wc://` 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. ```kotlin theme={null} val redirect = "examplewallet://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(projectId = projectId, connectionType = connectionType, application = application, metaData = appMetaData) val init = Wallet.Params.Init(coreClient = CoreClient) WalletKit.initialize(init) ``` Redirect when responding to a session proposal: ```kotlin theme={null} WalletKit.approveSession(approveProposal, onSuccess = { // trigger deeplink: proposal.redirect } ) ``` Redirect when responding to a request: ```kotlin theme={null} val redirect = WalletKit.getActiveSessionByTopic(sessionRequest.topic)?.redirect?.toUri() WalletKit.respondSessionRequest(response, onSuccess = { // trigger deeplink: redirect } ) ``` **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](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 ```kotlin theme={null} 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. ```kotlin theme={null} val redirect = "kotlin-dapp-wc://request" //should be unique for your Dapp val appMetaData = Core.Model.AppMetaData( name = "Dapp Name", description = "Dapp Description", url = "Dapp URL", icons = listOfIconUrlStrings, redirect = redirect ) CoreClient.initialize(projectId = projectId, 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](https://github.com/WalletConnect/WalletConnectKotlinV2/tree/master/sample/dapp) #### References * [https://developer.android.com/guide/navigation/navigation-deep-link#implicit](https://developer.android.com/guide/navigation/navigation-deep-link#implicit) * [https://developer.android.com/training/app-links#deep-links](https://developer.android.com/training/app-links#deep-links) # One-click Auth Source: https://docs.walletconnect.com/wallets/android/one-click-auth ## Introduction This section outlines an innovative protocol method that facilitates the initiation of a Sign session and the authentication of a wallet through a [Sign-In with Ethereum](https://eips.ethereum.org/EIPS/eip-4361) (SIWE) message, enhanced by [ReCaps](https://eips.ethereum.org/EIPS/eip-5573) (ReCap Capabilities). This enhancement not only offers immediate authentication for dApps, paving the way for prompt user logins, but also integrates informed consent for authorization. Through this mechanism, dApps can request the delegation of specific capabilities to perform actions on behalf of the wallet user. These capabilities, encapsulated within SIWE messages as ReCap URIs, detail the scope of actions authorized by the user in an explicit and human-readable form. By incorporating ReCaps, this method extends the utility of SIWE messages, allowing dApps to combine authentication with a nuanced authorization model. This model specifies the actions a dApp is authorized to execute on the user's behalf, enhancing security and user autonomy by providing clear consent for each delegated capability. As a result, dApps can utilize these consent-backed messages to perform predetermined actions, significantly enriching the interaction between dApps, wallets, and users within the Ethereum ecosystem. ![](https://mintlify.s3.us-west-1.amazonaws.com/reown-5552f0bb/images/w3w/authenticatedSessions-light.png) ## Handling Authentication Requests To handle incoming authentication requests, set up WalletKit.WalletDelegate. The onSessionAuthenticate callback will notify you of any authentication requests that need to be processed, allowing you to either approve or reject them based on your application logic. ```kotlin theme={null} override val onSessionAuthenticate: ((Wallet.Model.SessionAuthenticate, Wallet.Model.VerifyContext) -> Unit) get() = { sessionAuthenticate, verifyContext -> // Triggered when wallet receives the session authenticate sent by a Dapp // Process the authentication request here // This involves displaying UI to the user } ``` ## Authentication Objects/Payloads #### Responding to Authentication Requests To interact with authentication requests, build authentication objects (Wallet.Model.Cacao). It involves the following steps: * **Creating an Authentication Payload Params** - Generate an authentication payload params that matches your application's supported chains and methods. * **Formatting Authentication Messages** - Format the authentication message using the payload and the user's account. * **Signing the Authentication Message** - Sign the formatted message to create a verifiable authentication object. Example: ```kotlin theme={null} override val onSessionAuthenticate: ((Wallet.Model.SessionAuthenticate, Wallet.Model.VerifyContext) -> Unit) get() = { sessionAuthenticate, verifyContext -> val auths = mutableListOf() val authPayloadParams = WalletKit.generateAuthPayloadParams( sessionAuthenticate.payloadParams, supportedChains = listOf("eip155:1", "eip155:137", "eip155:56"), // Note: Only EVM chains are supported supportedMethods = listOf("personal_sign", "eth_signTypedData", "eth_sign") ) authPayloadParams.chains.forEach { chain -> val issuer = "did:pkh:$chain:$address" val formattedMessage = WalletKit.formatAuthMessage(Wallet.Params.FormatAuthMessage(authPayloadParams, issuer)) val signature = signMessage(message: formattedMessage, privateKey: privateKey) //Note: Assume `signMessage` is a function you've implemented to sign messages. val auth = WalletKit.generateAuthObject(authPayloadParams, issuer, signature) auths.add(auth) } } ``` ## Approving Authentication Requests 1. The recommended approach for secure authentication across multiple chains involves signing a SIWE (Sign-In with Ethereum) message for each chain and account. However, at a minimum, one SIWE message must be signed to establish a session. It is possible to create a session for multiple chains with just one issued authentication object. 2. Sometimes a dapp may want to only authenticate the user without creating a session, not every approval will result with a new session. To approve an authentication request, construct Wallet.Model.Cacao instances for each supported chain, sign the authentication messages, generate AuthObjects and call approveSessionAuthenticate with the request ID and the authentication objects. ```kotlin theme={null} val approveAuthenticate = Wallet.Params.ApproveSessionAuthenticate(id = sessionAuthenticate.id, auths = auths) WalletKit.approveSessionAuthenticate(approveProposal, onSuccess = { //Redirect back to the dapp if redirect is set: sessionAuthenticate.participant.metadata?.redirect }, onError = { error -> //Handle error } ) ``` ## Rejecting Authentication Requests If the authentication request cannot be approved or if the user chooses to reject it, use the rejectSessionAuthenticate method. ```kotlin theme={null} val rejectParams = Wallet.Params.RejectSessionAuthenticate( id = sessionAuthenticate.id, reason = "Reason" ) WalletKit.rejectSessionAuthenticate(rejectParams, onSuccess = { //Success }, onError = { error -> //Handle error } ) ``` ## Testing One-click Auth You can use [AppKit Lab](https://appkit-lab.reown.com/library/ethers-siwe/) to test and verify that your wallet supports One-click Auth properly. # Resources Source: https://docs.walletconnect.com/wallets/android/resources Valuable assets for developers and users interested in integrating Wallet SDK into their applications. * [Awesome WalletConnect](https://github.com/WalletConnect/awesome-walletconnect) - Community-curated collection of WalletConnect-enabled wallets, libraries, and tools. * [AppKit Laboratory](https://appkit-lab.reown.com/) - A place to test your wallet integrations against various setups of AppKit. * [Wallet SDK GitHub](https://github.com/WalletConnect/walletconnect-monorepo/tree/v2.0/packages/web3wallet) - Wallet SDK GitHub repository. ### Wallet Resources To check more in details go and visit our [Wallet SDK Kotlin implementation app](https://github.com/reown-com/reown-kotlin/tree/develop/sample/wallet). Sample Wallet and Dapp .apk files can be found under the latest release tag in [Kotlin's V2 repository](https://github.com/reown-com/reown-kotlin/tags) If you need to test your app's integration, you can use one of our following demo dapps. **Sign** * [React dApp (with standalone client) - v2](https://github.com/WalletConnect/web-examples/tree/main/advanced/dapps/react-dapp-v2) ([Demo](https://react-app.reown.com/)) ### Dapp Resources Sample Wallet and Dapp .apk files can be found under the latest release tag in [Kotlin's V2 repository](https://github.com/reown-com/reown-kotlin/tags) **Sign** * [React dApp (with standalone client) - v2](https://github.com/WalletConnect/web-examples/tree/main/advanced/dapps/react-dapp-v2) ([Demo](https://react-app.reown.com/)) # Usage Source: https://docs.walletconnect.com/wallets/android/usage This section provides instructions on how to initialize the WalletKit client, approve sessions with supported namespaces, and respond to session requests, enabling easy integration of Web3 wallets with dapps through a simple and intuitive interface. ## Content Links to sections on this page. Some sections are platform specific and are only visible when the platform is selected. To view a summary of useful platform specific topics, check out **Extra (Platform Specific)** under this section. **[Initialization](#initialization)**: Creating a new WalletKit instance and initializing it with a projectId from [WalletConnect Dashboard](https://dashboard.walletconnect.com). **Session**: Connection between a dapp and a wallet. * [Namespace Builder](#namespace-builder): Namespace Builder is a helper utility that greatly reduces the complexity of parsing the required and optional namespaces. It accepts as parameters a session proposal along with your user's chains/methods/events/accounts and returns a ready-to-use object * [Session Approval](#session-approval): Approving a session sent from a dapp * [Session Rejection](#session-rejection): Rejecting a session sent from a dapp * [Responding to Session Requests](#responding-to-session-requests): Responding to session requests sent from a dapp * [Updating a Session](#updating-a-session): Updating a session sent between a dapp and wallet * [Extending a Session](#extending-a-session): Extending a session between a dapp and wallet * [Session Disconnect](#session-disconnect): Disconnecting a session between a dapp and wallet * [Register Device Token](#register-device-token) Enabling Wallet Push Notifications by registering a device token. * [WalletKit.WalletDelegate](#walletkitwalletdelegate) Setting and overriding functions through WalletKit delegate. Also includes instructions about VerifyContext. * [Format Message](#format-message) Receiving formatted SIWE message To check the full list of platform specific instructions for your preferred platform, go to [Extra (Platform Specific)](#extra-platform-specific) and select your platform. **Don't have a project ID?** Head over to WalletConnect Dashboard and create a new project now! ## Initialization ```kotlin theme={null} val projectId = "" // Get Project ID at https://dashboard.walletconnect.com/ val connectionType = ConnectionType.AUTOMATIC or ConnectionType.MANUAL val telemetryEnabled: Boolean = true val appMetaData = Core.Model.AppMetaData( name = "Wallet Name", description = "Wallet Description", url = "Wallet URL", icons = /*list of icon url strings*/, redirect = "kotlin-wallet-wc:/request" // Custom Redirect URI ) CoreClient.initialize(projectId = projectId, connectionType = connectionType, application = this, metaData = appMetaData, telemetryEnabled = telemetryEnabled) val initParams = Wallet.Params.Init(core = CoreClient) WalletKit.initialize(initParams) { error -> // Error will be thrown if there's an issue during initialization } ``` The WalletKit client will always be responsible for exposing accounts (CAIP10 compatible) to a Dapp and therefore is also in charge of signing. To initialize the WalletKit client, create a `Wallet.Params.Init` object in the Android Application class with the Core Client. The `Wallet.Params.Init` object will then be passed to the `WalletKit`initialize function. The telemetry feature aims to improve the reliability and observability of connection flows between decentralized applications (dapps) and wallets. It focuses solely on collecting data about code execution and error codes, without tracking any sensitive user information like amounts, accounts etc. It provides a comprehensive tracing system for three key use cases: * Subscribing to a Pairing Topic * Approving a Session * Approving an Authenticated Session Each execution trace consists of: * Trace Events: Collected to verify the proper execution of code. * Error Events: Captured when errors occur during the trace, halting the execution trace. When an error event is encountered, it is stored locally within the SDK along with all preceding trace events. These stored events are then transmitted to the server whenever the SDK is initialized. Error event tracing is enabled by default. Telemetry Enabled (telemetryEnabled = true): * The SDK stores events and sends them to the server. Telemetry Disabled (telemetryEnabled = false): * The SDK stops storing new events and deletes all unsent events from local storage upon the next initialization. Important Note: Since the SDK only stores abstract trace and error data, user identification is not possible. Example of the error events: ```json theme={null} [ { "eventId": "69e53f11-fd4b-4efc-8d36-1f60a9ac8207", "bundleId": "com.wallet.example", "timestamp": 1689611327943, "props": { "event": "ERROR", "type": "pairing_already_exists", "properties": { "topic": "topic1", "trace": [ "pairing_started", "pairing_uri_validation_success", "pairing_uri_not_expired", "existing_pairing", "pairing_not_expired", "pairing_not_expired" ] } } }, { "eventId": "69e53f11-fd4b-4efc-8d36-2321312fds", "bundleId": "com.wallet.example", "timestamp": 16896113234323, "props": { "event": "ERROR", "type": "session_approve_namespace_validation_failure", "properties": { "topic": "topic2", "trace": ["session_approve_started", "proposal_not_expired"] } } } ] ``` ## Session A session is a connection between a dapp and a wallet. It is established when a user approves a session proposal from a dapp. A session is active until the user disconnects from the dapp or the session expires. ### Namespace Builder With WalletKit 1.7.0 we've published a helper utility that greatly reduces the complexity of parsing the required and optional namespaces. It accepts as parameters a session proposal along with your wallet's chains, methods, events, and accounts (supported namespaces) and returns ready-to-use namespaces object that has to be passed into `Wallet.Params.SessionApprove` when approving a session. ```kotlin theme={null} val supportedNamespaces: Wallet.Model.Namespaces.Session = /* a map of all supported namespaces created by a wallet */ val sessionProposal: Wallet.Model.SessionProposal = /* an object received by `fun onSessionProposal(sessionProposal: Wallet.Model.SessionProposal)` in `WalletKit.WalletDelegate` */ val sessionNamespaces = WalletKit.generateApprovedNamespaces(sessionProposal, supportedNamespaces) val approveParams: Wallet.Params.SessionApprove = Wallet.Params.SessionApprove(proposerPublicKey, sessionNamespaces) WalletKit.approveSession(approveParams) { error -> /*callback for error while approving a session*/ } ``` Examples of supported namespaces: ```kotlin theme={null} val supportedNamespaces = mapOf( "eip155" to Wallet.Model.Namespace.Session( chains = listOf("eip155:1", "eip155:137", "eip155:3"), methods = listOf("personal_sign", "eth_sendTransaction", "eth_signTransaction"), events = listOf("chainChanged"), accounts = listOf("eip155:1:0x57f48fAFeC1d76B27e3f29b8d277b6218CDE6092", "eip155:137:0x57f48fAFeC1d76B27e3f29b8d277b6218CDE6092", "eip155:3:0x57f48fAFeC1d76B27e3f29b8d277b6218CDE6092") ) ) val anotherSupportedNamespaces = mapOf( "eip155" to Wallet.Model.Namespace.Session( chains = listOf("eip155:1", "eip155:2", "eip155:4"), methods = listOf("personal_sign", "eth_sendTransaction", "eth_signTransaction"), events = listOf("chainChanged", "accountsChanged"), accounts = listOf("eip155:1:0x57f48fAFeC1d76B27e3f29b8d277b6218CDE6092", "eip155:2:0x57f48fAFeC1d76B27e3f29b8d277b6218CDE6092", "eip155:4:0x57f48fAFeC1d76B27e3f29b8d277b6218CDE6092") ), "cosmos" to Wallet.Model.Namespace.Session( chains = listOf("cosmos:cosmoshub-4"), methods = listOf("cosmos_method"), events = listOf("cosmos_event"), accounts = listOf("cosmos:cosmoshub-4:cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02") ) ) ``` ### EVM methods & events In @walletconnect/ethereum-provider, (our abstracted EVM SDK for apps) we support by default the following Ethereum methods and events: ```ts theme={null} { //... methods: [ "eth_accounts", "eth_requestAccounts", "eth_sendRawTransaction", "eth_sign", "eth_signTransaction", "eth_signTypedData", "eth_signTypedData_v3", "eth_signTypedData_v4", "eth_sendTransaction", "personal_sign", "wallet_switchEthereumChain", "wallet_addEthereumChain", "wallet_getPermissions", "wallet_requestPermissions", "wallet_registerOnboarding", "wallet_watchAsset", "wallet_scanQRCode", "wallet_sendCalls", "wallet_getCallsStatus", "wallet_showCallsStatus", "wallet_getCapabilities", ], events: [ "chainChanged", "accountsChanged", "message", "disconnect", "connect", ] } ``` ### Session Approval Addresses provided in `accounts` array should follow [CAIP-10](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-10.md) semantics. ```kotlin theme={null} val proposerPublicKey: String = /*Proposer publicKey from SessionProposal object*/ val namespace: String = /*Namespace identifier, see for reference: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-2.md#syntax*/ val accounts: List = /*List of accounts on chains*/ val methods: List = /*List of methods that wallet approves*/ val events: List = /*List of events that wallet approves*/ val namespaces: Map = mapOf(namespace, Wallet.Model.Namespaces.Session(accounts, methods, events)) val approveParams: Wallet.Params.SessionApprove = Wallet.Params.SessionApprove(proposerPublicKey, namespaces) WalletKit.approveSession(approveParams) { error -> /*callback for error while approving a session*/ } ``` To send an approval, pass a Proposer's Public Key along with the map of namespaces to the `WalletKit.approveSession` function. ### Session Rejection ```kotlin theme={null} val proposerPublicKey: String = /*Proposer publicKey from SessionProposal object*/ val rejectionReason: String = /*The reason for rejecting the Session Proposal*/ val rejectionCode: String = /*The code for rejecting the Session Proposal*/ For reference use CAIP-25: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-25.md val rejectParams: Wallet.Params.SessionReject = SessionReject(proposerPublicKey, rejectionReason, rejectionCode) WalletKit.rejectSession(rejectParams) { error -> /*callback for error while rejecting a session*/ } ``` To send a rejection for the Session Proposal, pass a proposerPublicKey, rejection reason and rejection code to the `WalletKit.rejectSession` function. ### Responding to Session requests ```kotlin theme={null} val sessionTopic: String = /*Topic of Session*/ val jsonRpcResponse: Wallet.Model.JsonRpcResponse.JsonRpcResult = /*Active Session Request ID along with request data*/ val result = Wallet.Params.SessionRequestResponse(sessionTopic = sessionTopic, jsonRpcResponse = jsonRpcResponse) WalletKit.respondSessionRequest(result) { error -> /*callback for error while responding session request*/ } ``` To respond to JSON-RPC method that were sent from Dapps for a session, submit a `Wallet.Params.SessionRequestResponse` with the session's topic and request ID along with the respond data to the `WalletKit.respondSessionRequest` function. ### Updating a Session NOTE: addresses provided in `accounts` array should follow [CAIP10](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-10.md) semantics. ```kotlin theme={null} val sessionTopic: String = /*Topic of Session*/ val namespace: String = /*Namespace identifier, see for reference: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-2.md#syntax*/ val accounts: List = /*List of accounts on chains*/ val methods: List = /*List of methods that wallet approves*/ val events: List = /*List of events that wallet approves*/ val namespaces: Map = mapOf(namespace, Wallet.Model.Namespaces.Session(accounts, methods, events)) val updateParams = Wallet.Params.SessionUpdate(sessionTopic, namespaces) WalletKit.updateSession(updateParams) { error -> /*callback for error while sending session update*/ } ``` To update a session with namespaces, submit a `Wallet.Params.SessionUpdate` object with the session's topic and namespaces to update session with to `WalletKit.updateSession`. ### Extending a Session ```kotlin theme={null} val sessionTopic: String = /*Topic of Session*/ val extendParams = Wallet.Params.SessionExtend(sessionTopic = sessionTopic) WalletKit.extendSession(extendParams) { error -> /*callback for error while extending a session*/ } ``` To extend a session, create a `Wallet.Params.SessionExtend` object with the session's topic to update the session with to `WalletKit.extendSession`. Session is extended by 7 days. ### Emitting a Session To emit an event, call emitSessionEvent() as follows: ```kotlin theme={null} val sessionTopic: String = /*Topic of Session*/ val event: Wallet.Model.SessiomEvent = SessionEvent(name = "accountsChanged", data = "0x000000000") val sessionEmit = Wallet.Params.SessionEmit(topic = sessionTopic, chainId = "eip155:1", event = event) WalletKit.emitSessionEvent(sessionEmit) { error -> /*callback for error while emiting an event*/ } ``` ### Session Disconnect ```kotlin theme={null} val disconnectionReason: String = /*The reason for disconnecting the Session*/ val disconnectionCode: String = /*The code for disconnecting the Session*/ val sessionTopic: String = /*Topic from the Session*/ For reference use CAIP-25: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-25.md val disconnectParams = Wallet.Params.SessionDisconnect(sessionTopic, disconnectionReason, disconnectionCode) WalletKit.disconnectSession(disconnectParams) { error -> /*callback for error while disconnecting a session*/ } ``` To disconnect from un active session, pass a disconnection reason with code and the Session topic to the `WalletKit.disconnectSession` function. ## Extra (Platform Specific) #### WalletKit.WalletDelegate ```kotlin theme={null} val walletDelegate = object : WalletKit.WalletDelegate { override fun onSessionProposal(sessionProposal: Wallet.Model.SessionProposal, verifyContext: Wallet.Model.VerifyContext) { // Triggered when wallet receives the session proposal sent by a Dapp } fun onSessionAuthenticate(sessionAuthenticate: Wallet.Model.SessionAuthenticate, verifyContext: Wallet.Model.VerifyContext) { // Triggered when wallet receives the session authenticate sent by a Dapp } override fun onSessionRequest(sessionRequest: Wallet.Model.SessionRequest, verifyContext: Wallet.Model.VerifyContext) { // Triggered when a Dapp sends SessionRequest to sign a transaction or a message } override fun onAuthRequest(authRequest: Wallet.Model.AuthRequest, verifyContext: Wallet.Model.VerifyContext) { // Triggered when Dapp / Requester makes an authorization request } override fun onSessionDelete(sessionDelete: Wallet.Model.SessionDelete) { // Triggered when the session is deleted by the peer } override fun onSessionSettleResponse(settleSessionResponse: Wallet.Model.SettledSessionResponse) { // Triggered when wallet receives the session settlement response from Dapp } override fun onSessionUpdateResponse(sessionUpdateResponse: Wallet.Model.SessionUpdateResponse) { // Triggered when wallet receives the session update response from Dapp } override fun onConnectionStateChange(state: Wallet.Model.ConnectionState) { //Triggered whenever the connection state is changed } override fun onError(error: Wallet.Model.Error) { // Triggered whenever there is an issue inside the SDK } } WalletKit.setWalletDelegate(walletDelegate) ``` `Wallet.Event.VerifyContext` provides a domain verification information about SessionProposal, SessionRequest and AuthRequest. It consists of origin of a Dapp from where the request has been sent, validation Enum that says whether origin is VALID, INVALID or UNKNOWN and verify url server. ```kotlin theme={null} data class VerifyContext( val id: Long, val origin: String, val validation: Model.Validation, val verifyUrl: String ) enum class Validation { VALID, INVALID, UNKNOWN } ``` The WalletKit needs a `WalletKit.WalletDelegate` passed to it for it to be able to expose asynchronous updates sent from the Dapp. # #### Format message To receive formatted SIWE message, call formatMessage method with following parameters: ```kotlin theme={null} val payloadParams: Wallet.Params.PayloadParams = //PayloadParams received in the onAuthRequest callback val issuer = //MUST be the same as send with the respond methods and follows: https://github.com/w3c-ccg/did-pkh/blob/main/did-pkh-method-draft.md val formatMessage = Wallet.Params.FormatMessage(event.payloadParams, issuer) WalletKit.formatMessage(formatMessage) ``` #### Register Device Token This method enables wallets to receive push notifications from WalletConnect's Push Server via [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging). This means you will have to setup your project with Firebase before being able to call registerDeviceToken method. Make sure that a service extending the FirebaseMessagingService is added to your manifest as per the [Firebase FCM documentation](https://firebase.google.com/docs/cloud-messaging/android/client#manifest) as well as any other setup Firebase requires [Firebase setup documentation](https://firebase.google.com/docs/android/setup). To register a wallet to receive WalletConnect push notifications, call `WalletKit.registerDeviceToken` and pass the Firebase Access Token. ```kotlin theme={null} val firebaseAccessToken: String = //FCM access token received through the Firebase Messaging SDK WalletKit.registerDeviceToken( firebaseAccessToken, onSuccess = { // callback triggered once registered successfully with the Push Server }, onError = { error: Wallet.Model.Error -> // callback triggered if there's an exception thrown during the registration process }) ``` # Verify API Source: https://docs.walletconnect.com/wallets/android/verify Verify API is a security-focused feature that allows wallets to notify end-users when they may be connecting to a suspicious or malicious domain, helping to prevent phishing attacks across the industry. Once a wallet knows whether an end-user is on uniswap.com or eviluniswap.com, it can help them to detect potentially harmful connections through Verify's combined offering of WalletConnect domain registry. When a user initiates a connection with an application, Verify API enables wallets to present their users with four key states that can help them determine whether the domain they’re about to connect to might be malicious. These are: ## Disclaimer Verify API is not designed to be bulletproof but to make the impersonation attack harder and require a somewhat sophisticated attacker. We are working on a new standard with various partners to close those gaps and make it bulletproof. ## Domain risk detection The Verify security system will discriminate session proposals & session requests with distinct validations that can be either `VALID`, `INVALID` or `UNKNOWN`. * Domain match: The domain linked to this request has been verified as this application's domain. * This interface appears when the domain a user is attempting to connect to has been ‘verified’ in our domain registry as the registered domain of the application the user is trying to connect to, and the domain has not returned as suspicious from either of the security tools we work with. The `verifyContext` included in the request will have a validation of `VALID`. * Unverified: The domain sending the request cannot be verified. * This interface appears when the domain a user is attempting to connect to has not been verified in our domain registry, but the domain has not returned as suspicious from either of the security tools we work with. The `verifyContext` included in the request will have a validation of `UNKNOWN`. * Mismatch: The application's domain doesn't match the sender of this request. * This interface appears when the domain a user is attempting to connect to has been flagged as a different domain to the one this application has verified in our domain registry, but the domain has not returned as suspicious from either of the security tools we work with. The `verifyContext` included in the request will have a validation of `INVALID` * Threat: This domain is flagged as malicious and potentially harmful. * This interface appears when the domain a user is attempting to connect to has been flagged as malicious on one or more of the security tools we work with. The `verifyContext` included in the request will contain parameter `isScam` with value `true`. ### Implementation Wallet.Event.VerifyContext provides a domain verification information about SessionProposal, SessionRequest and AuthRequest. It consists of origin of an app from where the request has been sent, validation Enum that says whether origin is `VALID`, `INVALID` or `UNKNOWN` and verify url server. ```kotlin theme={null} data class VerifyContext( val id: Long, val origin: String, val validation: Model.Validation, val verifyUrl: String ) enum class Validation { VALID, INVALID, UNKNOWN } ``` # Explorer Submission Source: https://docs.walletconnect.com/wallets/c-sharp/cloud/explorer-submission **Note** Submitting a project to the WalletConnect Dashboard Explorer is recommended but optional. You can still use WalletConnect services without submitting your project. However, doing so ensures that your project is listed under [WalletGuide](https://walletguide.walletconnect.network/?utm_source=walletconnect-docs\&utm_medium=cloud\&utm_campaign=explorer-submission) and [Cloud Explorer API](/wallets/walletguide/explorer-api). ## Creating a New Project * Head over to [dashboard.walletconnect.com](https://dashboard.walletconnect.com/) and create a new project by clicking the "New Project" button in top right corner of the dashboard. * Give a suitable name to your project, select whether its an App or Wallet and click the "Create" button. (You can change this later) ## Project Details * Go to the "Explorer" tab and fill in the details of your project. | Field | Description | Required | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -------- | | **Name** | The name to display in the explorer | Yes | | **Description** | A short description explaining your project (dapp/wallet) | Yes | | **Type** | Whether your project is a dapp or a wallet | Yes | | **Category** | Appropriate category for your project. This field is dependent on the type of your project | Yes | | **Homepage** | The URL of your project | Yes | | **Web App** | The URL of your web app. This field is only applicable for dapps | Yes | | **Chains** | Chains supported by your project | Yes | | **Logo** | The logo of your project. Further requirements are provided in the explorer submission form | Yes | | **Testing Instructions** | Instructions on how to test your WalletConnect Integration | Yes | | **Download Links** | Links to download your project (if applicable) | No | | **Mobile Linking** | Required for mobile wallets targeting AppKit. Deep Link is recommended over Universal Link | No | | **Desktop Linking** | Required for desktop wallets targeting AppKit. | No | | **Injected Wallet Identifiers** | Required for injected wallets targeting AppKit. RDNS (from EIP-6963 metadata) is recommended over Provider Flags (Legacy) | No | | **Metadata** | User-facing UI metadata for your project. Only Short Name is required. | No | ## Project Submission * Once you've filled the applicable fields, click the "Submit" button to submit your project for review. Alternatively, you can save your changes and submit later. Additional information will be visible in the modal that appears after clicking the "Submit" button. ## How do we test wallets? In order to offer a great user experience in our APIs and SDKs every Cloud submission goes through a QA process to make sure that the integration of the WalletConnect protocol is working correctly. The following list details our QA flow and how to reproduce it: | Test Case | Steps | Expected Results | | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Set Up** | 1. Download the wallet
2. Install the wallet app
3. Sign up for an account with the wallet app
4. Create one or more accounts | 1. N/A
2. The app is installed
3. I have an account
4. I have one or more accounts | | **Connect to dapp via web browser** | 1. Open the Reown connection page [https://appkit-lab.reown.com/](https://appkit-lab.reown.com/) from a PC
2. Press on the “Connect Wallet” button and select the Reown option.
3. Open the wallet app and use the scan QR option to connect.
4. Accept on the wallet the connection request | 1. The app has been correctly set-up
2. A modal with wallet options is opened
3. A QR code is shown on the website and the wallet is able to scan it.
4. The connection is successfully established. The wallet data is now shown on the website. | | **Connect to dapp via mobile browser (Deep-link)** | 1. Open [https://appkit-lab.reown.com/](https://appkit-lab.reown.com/) in your mobile device.
2. Select one of the default options (e.g. Wagmi for EVM chains). Press the "Custom Wallet" button from the navbar. Fill in the wallet’s name and its deeplink (Mobile Link) in the “Add a Custom Wallet” form. Press “Add Wallet”. After the website reloads, press the “Connect Wallet” button and select the newly created wallet.
3. Accept the connection request in the wallet application. | 1. N/A
2. A form should show up on the website to fill in the wallet’s data. After the changes are applied, the modal should show the newly created wallet on the main view.
3. The user should be redirected to the wallet application and a modal with a connection request should show up on the wallet application. The wallet should connect successfully. On Android devices, the user should be redirected back to the website after accepting the connection request. | | **Switch chains - dapp side** | 1. Once the wallet is connected, press on the modal button on the top right of the website.
2. Press the first button of the modal to switch the chain.
3. Select any available chain, close the modal, and press the “Send Transaction” button | 1. A modal with the account information should pop up on the website.
2. A new view with supported chains should show up.
3. The transaction request that pops up on the wallet should show in their information the correct chain that was previously selected. | | **Switch Chains - wallet side (if supported)** | 1. Check if the wallet supports chain switching. If so, select a different chain from the connected one. | 1. The chain change should be reflected on the website. The first card shows the current chain ID. | | **Accounts Switching - wallet side** | 1. In the wallet app, switch from one account to another. | 1. The account switch event should be reflected in the modal’s account view on the website. | | **Disconnect a wallet** | 1. Select the "Disconnect" button from the Wallet App (Ideally, wallets should have a section where users can see all their existing dApp connections and manage/disconnect from dApps in one spot—this is not always true, so if not possible, just skip this).
2. Repeat the above steps and press the "Disconnect" button from the dApp (this should always be available). | 1. The related session should disappear from the dApp and the Wallet App.
2. The related session should disappear from the dApp and the Wallet App. | | **Verify API** | 1. Open [https://malicious-app-verify-simulation.vercel.app/](https://malicious-app-verify-simulation.vercel.app/)
2. Select a supported chain by the wallet (some wallets don’t support testnets) and press the “Connect” button.
3. Scan with the wallet the generated QR code. | 1. N/A
2. A modal should show up with a QR code to scan.
3. The connection request in the wallet should flag the website as malicious. | ### Chain Specific The following test cases only apply for wallets supporting a particular set of chains. | Test Case | Steps | Expected Results | | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Supporting personal\_sign** | 1. Connect the wallet.
2. Press the “Sign Message” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. | | **Supporting eth\_signTypedData\_v4** | 1. Connect the wallet.
2. Press the “Sign Typed Data” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. | | **Supporting eth\_sendTransaction** | 1. Connect the wallet.
2. Press the “Send Transaction” button. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature. |
| Test Case | Steps | Expected Results | | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Supporting solana\_signMessage** | 1. Connect the wallet to [AppKit Lab](https://appkit-lab.reown.com/library/solana)
2. Press the “Sign Message” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. | | **Supporting solana\_signTransaction** | 1. Connect the wallet to [AppKit Lab](https://appkit-lab.reown.com/library/solana)
2. Press the “Sign Transaction” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. | | **Supporting v0 Transactions** | 1. Connect the wallet to [AppKit Lab](https://appkit-lab.reown.com/library/solana)
2. Press the “Sign Versioned Transaction” button.
3. Accept the signature request on the wallet. | 1. N/A
2. A modal should pop up on the wallet app requesting a signature.
3. Once accepted and signed, the hash should show up on the website. |
## What's Next? Now depending on whether or not your submission met all parameters, you will receive an email from the WalletConnect team with the status of your submission. This change will also be reflected with more directions in the "Explorer" tab of your project. If your submission was not accepted, you can make the necessary changes and resubmit your project for review. The reason for rejection will be mentioned in the email and in the "Explorer" tab of your project. In case of any questions, feel free to ask on [Github Discussions](https://github.com/orgs/WalletConnect/discussions/categories/explorer-support) # Installation Source: https://docs.walletconnect.com/wallets/c-sharp/installation Install the Wallet SDK client package via Nuget. ```bash theme={null} dotnet add package Reown.WalletKit ``` ## Next Steps Now that you've installed Wallet SDK, you're ready to start integrating it. The next section will walk you through the process of setting up your project to use the SDK. # Usage Source: https://docs.walletconnect.com/wallets/c-sharp/usage This section provides instructions on how to initialize the WalletKit client, approve sessions with supported namespaces, and respond to session requests, enabling easy integration of Web3 wallets with dapps through a simple and intuitive interface. ## Content Links to sections on this page. Some sections are platform specific and are only visible when the platform is selected. To view a summary of useful platform specific topics, check out **Extra (Platform Specific)** under this section. **[Initialization](#initialization)**: Creating a new WalletKit instance and initializing it with a projectId from [WalletConnect Dashboard](https://dashboard.walletconnect.com). **Session**: Connection between a dapp and a wallet. * [Namespace Builder](#namespace-builder): Namespace Builder is a helper utility that greatly reduces the complexity of parsing the required and optional namespaces. It accepts as parameters a session proposal along with your user's chains/methods/events/accounts and returns a ready-to-use object * [Session Approval](#session-approval): Approving a session sent from a dapp * [Session Rejection](#session-rejection): Rejecting a session sent from a dapp * [Responding to Session Requests](#responding-to-session-requests): Responding to session requests sent from a dapp * [Updating a Session](#updating-a-session): Updating a session sent between a dapp and wallet * [Extending a Session](#extending-a-session): Extending a session between a dapp and wallet * [Session Disconnect](#session-disconnect): Disconnecting a session between a dapp and wallet **Don't have a project ID?** Head over to WalletConnect Dashboard and create a new project now! ## Initialization First you must setup a `Core` instance with a specific `Name` and `ProjectId`. You may optionally specify other `CoreOption` values, such as `RelayUrl` and `Storage` ```csharp theme={null} var options = new CoreOptions() { ProjectId = "...", Name = "my-app", } var core = new CoreClient(options); ``` Next, you must define a `Metadata` object which describes your Wallet. This includes a `Name`, `Description`, `Url` and `Icons` url. ```csharp theme={null} var metadata = new Metadata() { Description = "An example wallet to showcase Wallet SDK", Icons = new[] { "https://walletconnect.com/meta/favicon.ico" }, Name = $"wallet-csharp-test", Url = "https://walletconnect.com", }; ``` Once you have both the `Core` and `Metadata` objects, you can initialize the `WalletKitClient` ```csharp theme={null} var sdk = await WalletKitClient.Init(core, metadata, metadata.Name); ``` ## Session A session is a connection between a dapp and a wallet. It is established when a user approves a session proposal from a dapp. A session is active until the user disconnects from the dapp or the session expires. ### Namespace Builder To build a namespace mapping for either proposing a session **OR** approving a session, you can use .NET dictionary + class constructors directly, or use the built-in builder methods ### C# Constructor Style ```csharp theme={null} var TestNamespaces = new Namespaces() { { "eip155", new Namespace() { Accounts = new [] { "eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb" }, Chains = new []{ "eip155:1" }, Methods = new[] { "eth_signTransaction" }, Events = new[] { "chainChanged" } } }, }; ``` ### Builder Style ```csharp theme={null} var TestNamespaces = new Namespaces() .WithNamespace("eip155", new Namespace() .WithChain("eip155:1") .WithMethod("eth_signTransaction") .WithEvent("chainChanged") .WithAccount("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb") ); ``` The `Namespaces` mapping is required when approving a proposed session from a dApp. Because of this, you may also construct a `Namespaces` from a `RequiredNamespaces`, which auto-populates all `Methods`, `Events` and `Chains` from the given `RequiredNamespaces`. This is provided for convenience. ### RequiredNamespaces ```csharp theme={null} sdk.SessionProposed += async (sender, @event) => { var proposal = @event.Proposal; var requiredNamespaces = proposal.RequiredNamespaces; var approvedNamespaces = new Namespaces(requiredNamespaces); approvedNamespaces["eip155"].WithAccount("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"); }; ``` The `RequiredNamespaces` is required when setting up a session between a dApp and Wallet. The dApp will provide a `RequiredNamespaces` when proposing the session. The `RequiredNamespaces` and `ProposedNamespace` use the same style constructors + builder functions as `Namespaces` and `Namespace`. ### EVM methods & events In @walletconnect/ethereum-provider, (our abstracted EVM SDK for apps) we support by default the following Ethereum methods and events: ```ts theme={null} { //... methods: [ "eth_accounts", "eth_requestAccounts", "eth_sendRawTransaction", "eth_sign", "eth_signTransaction", "eth_signTypedData", "eth_signTypedData_v3", "eth_signTypedData_v4", "eth_sendTransaction", "personal_sign", "wallet_switchEthereumChain", "wallet_addEthereumChain", "wallet_getPermissions", "wallet_requestPermissions", "wallet_registerOnboarding", "wallet_watchAsset", "wallet_scanQRCode", "wallet_sendCalls", "wallet_getCallsStatus", "wallet_showCallsStatus", "wallet_getCapabilities", ], events: [ "chainChanged", "accountsChanged", "message", "disconnect", "connect", ] } ``` ### Session Approval Wallets can pair an incoming session using the session's Uri. Pairing a session lets the Wallet obtain the connection proposal which can then be approved or denied. ```csharp theme={null} var uri = "..."; await sdk.Pair(uri); ``` The wallet can then approve the proposal by constructing an approved `Namespaces`. The approved `Namespaces` should include the `RequiredNamespaces` under `proposal.RequiredNamespaces`, and may optionally include any optional namespaces specified under `proposal.OptionalNamespaces` ```csharp theme={null} sdk.SessionProposed += async (sender, @event) => { var proposal = @event.Proposal; var requiredNamespaces = proposal.RequiredNamespaces; var approvedNamespaces = new Namespaces(requiredNamespaces); approvedNamespaces["eip155"].WithAccount("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"); var sessionData = await sdk.ApproveSession(proposal.Id, approvedNamespaces); var sessionTopic = sessionData.Topic; }; ``` You may also just provide the addresses that will connect, and the SDK will create this approved `Namespaces` for you. This function **will not approve optional namespaces** ```csharp theme={null} sdk.SessionProposed += async (sender, @event) => { var proposal = @event.Proposal; var sessionData = await sdk.ApproveSession(proposal, new[] { "eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb" }); var sessionTopic = sessionData.Topic; }; ``` or ```csharp theme={null} sdk.SessionProposed += async (sender, @event) => { var proposal = @event.Proposal; var sessionData = await sdk.ApproveSession(proposal, "eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"); var sessionTopic = sessionData.Topic; }; ``` ### Session Rejection The wallet can reject the proposal using the following: ```csharp theme={null} sdk.SessionProposed += async (sender, @event) => { var proposal = @event.Proposal; await sdk.RejectSession(proposal, "User rejected"); }; ``` ### Responding to Session requests Responding to session requests is very similar to sending session requests. See dApp usage on how sending session requests works. All custom session requests requires a request class **and** response class to be created that matches the `params` field type in the custom session request. C# is a static typed language, so these types must be given whenever you do a session request (or do any querying for session requests). Currently, **WalletKit does not automatically assume the object type for `params` is an array**. This is very important, since most EVM RPC requests have `params` as an array type. **Use `List` to workaround this**. For example, for `eth_sendTransaction`, use `List` instead of `Transaction`. Newtonsoft.Json is used for JSON serialization/deserialization, therefore you can use Newtonsoft.Json attributes when defining fields in your request/response classes. ### Building a Response type Create a class for the response and populate it with the JSON properties the response object has. For this example, we will use `eth_getTransactionReceipt` The `params` field for `eth_getTransactionReceipt` has the object type ```csharp theme={null} using Newtonsoft.Json; using System.Numerics; [RpcMethod("eth_getTransactionReceipt"), RpcRequestOptions(Clock.ONE_MINUTE, 99995)] public class TransactionReceipt { [JsonProperty("transactionHash")] public string TransactionHash; [JsonProperty("transactionIndex")] public BigInteger TransactionIndex; [JsonProperty("blockHash")] public string BlockHash; [JsonProperty("blockNumber")] public BigInteger BlockNumber; [JsonProperty("from")] public string From; [JsonProperty("to")] public string To; [JsonProperty("cumulativeGasUsed")] public BigInteger CumulativeGasUsed; [JsonProperty("effectiveGasPrice ")] public BigInteger EffectiveGasPrice ; [JsonProperty("gasUsed")] public BigInteger GasUsed; [JsonProperty("contractAddress")] public string ContractAddress; [JsonProperty("logs")] public object[] Logs; [JsonProperty("logsBloom")] public string LogBloom; [JsonProperty("type")] public BigInteger Type; [JsonProperty("status")] public BigInteger Status; } ``` The `RpcMethod` class attributes defines the rpc method this response uses, this is optional. The `RpcResponseOptions` class attributes define the expiry time and tag attached to the response, **this is required**. ### Sending a response To respond to requests from a dApp, you must define the class representing the request object type. The request type for `eth_getTransactionReceipt` is the following: ```csharp theme={null} [RpcMethod("eth_getTransactionReceipt"), RpcRequestOptions(Clock.ONE_MINUTE, 99994)] public class EthGetTransactionReceipt : List { public EthGetTransactionReceipt(params string[] hashes) : base(hashes) { } // needed for proper json deserialization public EthGetTransactionReceipt() { } } ``` We can handle the `eth_getTransactionReceipt` session request by doing the following: ```csharp theme={null} walletClient.Engine.SessionRequestEvents().OnRequest += OnEthTransactionReceiptRequest; private Task OnEthTransactionReceiptRequest(RequestEventArgs e) { // logic for request goes here // set e.Response to return a response } ``` The callback function gets invoked whenever the wallet receives the `eth_getTransactionReceipt` request from a connected dApp. You may optionally filter further which requests are handled using the `FilterRequests` function ```csharp theme={null} walletClient.Engine.SessionRequestEvents() .FilterRequests(r => r.Topic == sessionTopic) .OnRequest += OnEthTransactionReceiptRequest; ``` The callback returns a `Task`, so the callback can be made async. To return a response, **you must** set the `Response` field in `RequestEventArgs` with the desired response. ```csharp theme={null} private async Task OnEthTransactionReceiptRequest(RequestEventArgs e) { var txHash = e.Request.Params[0]; var receipt = await EthGetTransactionReceipt(txHash); e.Response = receipt; } ``` ### Updating a Session Update a session, adding/removing additional namespaces in the given topic. ```csharp theme={null} var newNamespaces = new Namespaces(...); var request = await walletClient.UpdateSession(sessionTopic, newNamespaces); await request.Acknowledged(); ``` ### Extending a Session Extend a session's expiry time so the session remains open ```csharp theme={null} var request = await walletClient.Extend(sessionTopic); await request.Acknowledged(); ``` ### Session Disconnect To disconnect a session, use the `Disconnect` function. You may optional provide a reason for the disconnect. Disconnecting requires the `topic` of the session to be given. This can be found in the `SessionStruct` object given when a session has been given approval by the Wallet. ```csharp theme={null} var sessionTopic = sessionData.Topic; await walletClient.Disconnect(sessionTopic); // or await walletClient.Disconnect(sessionTopic, Error.FromErrorType(ErrorType.USER_DISCONNECTED)); ``` # Verify API Source: https://docs.walletconnect.com/wallets/c-sharp/verify Verify API is a security-focused feature that allows wallets to notify end-users when they may be connecting to a suspicious or malicious domain, helping to prevent phishing attacks across the industry. Once a wallet knows whether an end-user is on uniswap.com or eviluniswap.com, it can help them to detect potentially harmful connections through Verify's combined offering of WalletConnect's domain registry. When a user initiates a connection with an application, Verify API enables wallets to present their users with four key states that can help them determine whether the domain they’re about to connect to might be malicious. These are: ## Disclaimer Verify API is not designed to be bulletproof but to make the impersonation attack harder and require a somewhat sophisticated attacker. We are working on a new standard with various partners to close those gaps and make it bulletproof. ## Domain risk detection The Verify security system will discriminate session proposals & session requests with distinct validations that can be either `VALID`, `INVALID` or `UNKNOWN`. * Domain match: The domain linked to this request has been verified as this application's domain. * This interface appears when the domain a user is attempting to connect to has been ‘verified’ in our domain registry as the registered domain of the application the user is trying to connect to, and the domain has not returned as suspicious from either of the security tools we work with. The `verifyContext` included in the request will have a validation of `VALID`. * Unverified: The domain sending the request cannot be verified. * This interface appears when the domain a user is attempting to connect to has not been verified in our domain registry, but the domain has not returned as suspicious from either of the security tools we work with. The `verifyContext` included in the request will have a validation of `UNKNOWN`. * Mismatch: The application's domain doesn't match the sender of this request. * This interface appears when the domain a user is attempting to connect to has been flagged as a different domain to the one this application has verified in our domain registry, but the domain has not returned as suspicious from either of the security tools we work with. The `verifyContext` included in the request will have a validation of `INVALID` * Threat: This domain is flagged as malicious and potentially harmful. * This interface appears when the domain a user is attempting to connect to has been flagged as malicious on one or more of the security tools we work with. The `verifyContext` included in the request will contain parameter `isScam` with value `true`. ### Implementation `Reown.Core.Models.Verify.VerifiedContext` provides a domain verification information about `SessionProposal`, `SessionRequest` and `AuthRequest`. It consists of origin of an app from where the request has been sent, validation Enum that says whether origin is `VALID`, `INVALID` or `UNKNOWN` and verify url server. ```csharp theme={null} public class VerifiedContext { [JsonProperty("origin")] public string Origin; [JsonProperty("validation")] private string _validation; public string ValidationString => _validation; public Validation Validation { get { return FromString(); } set { _validation = AsString(value); } } [JsonProperty("verifyUrl")] public string VerifyUrl { get; set; } private Validation FromString() { switch (ValidationString.ToLowerInvariant()) { case "VALID": return Validation.Valid; case "INVALID": return Validation.Invalid; default: return Validation.Unknown; } } private string AsString(Validation str) { switch (str) { case Validation.Invalid: return "INVALID"; case Validation.Valid: return "VALID"; default: return "UNKNOWN"; } } } public enum Validation { Unknown, Valid, Invalid, } ``` # ADI Chain Source: https://docs.walletconnect.com/wallets/chains/adi Overview of ADI Chain integration with Wallet SDK. ADI Chain is a fully EVM-compatible blockchain. It uses the standard Ethereum JSON-RPC methods for all wallet interactions. ## Network / Chain Information | CAIP-2 | Chain ID | Name | RPC Endpoint | Explorer | Namespace | | -------------- | -------- | --------- | ------------------------------ | ----------------------------------- | --------- | | `eip155:36900` | `36900` | ADI Chain | `https://rpc.adifoundation.ai` | `https://explorer.adifoundation.ai` | `eip155` | ## RPC Methods As an EVM-compatible chain, ADI Chain supports all standard Ethereum JSON-RPC methods. Wallets implementing ADI Chain support should refer to the [EVM RPC documentation](/wallets/chains/evm) for the complete list of supported methods, including: * `personal_sign` - Sign a message * `eth_sign` - Sign data * `eth_signTypedData` / `eth_signTypedData_v4` - Sign typed data (EIP-712) * `eth_sendTransaction` - Send a transaction * `eth_signTransaction` - Sign a transaction without broadcasting * `eth_sendRawTransaction` - Broadcast a signed transaction For detailed method specifications and examples, see the [EVM Chain Support](/wallets/chains/evm) page. ## Additional Resources * [ADI Explorer](https://explorer.adifoundation.ai) * [ADI Bridge](https://bridge.adifoundation.ai) * [ADI RPC Endpoint](https://rpc.adifoundation.ai) # Bitcoin Source: https://docs.walletconnect.com/wallets/chains/bitcoin Bitcoin JSON-RPC methods supported by Wallet SDK. We define an account as the group of addresses derived using the same account value in their [derivation paths](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#user-content-Path_levels). We use the first address of the [external chain](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#examples) ("first external address"), as the identifier for an account. An account's total balance is defined as the sum of all unspent transaction outputs (UTXOs) belonging to its entire group of addresses. 1. Dapps **must** only display the first external address as a connected account. 2. Wallets **must** only offer to connect the first external address(es). #### Account Definition The derivation path levels in the [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#path-levels), [BIP49](https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki#user-content-Public_key_derivation), [BIP84](https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki#public-key-derivation), [BIP86](https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki#user-content-Public_key_derivation) standards are: ``` m / purpose' / coin_type' / account' / change / address_index ``` Addresses with different `purpose`, `change` and `address_index` values are considered to belong to the same account. Valid `purpose` values are 44, 49, 84 and 86. We use the first external Native SegWit (purpose = 84) address as the default account identifier. For a specific seed phrase and path `m/84'/0'/0'/0/0` we get account 0 with identifier `bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu`. Its total balance is the sum of all UTXO balances on all addresses with derivation paths: * `m/44'/0'/0'/change/address_index` * `m/49'/0'/0'/change/address_index` * `m/84'/0'/0'/change/address_index` * `m/86'/0'/0'/change/address_index` If the wallet user changes to account 1 we get path `m/84'/0'/1'/0/0` with identifier `bc1qku0qh0mc00y8tk0n65x2tqw4trlspak0fnjmfz`. Its total balance is the sum of all UTXO balances on all addresses with derivation paths: * `m/44'/0'/1'/change/address_index` * `m/49'/0'/1'/change/address_index` * `m/84'/0'/1'/change/address_index` * `m/86'/0'/1'/change/address_index` ## sendTransfer This method is used to sign and submit a transfer of any `amount` of Bitcoin to a single `recipientAddress`, optionally including a `changeAddress` for the change amount and `memo` set as an OP\_RETURN output by supporting wallets. The transaction will be signed and broadcast upon user approval. ### Parameters * `Object` * `account` : `String` - *(Required)* The connected account's first external address. * `recipientAddress` : `String` - *(Required)* The recipient's public address. * `amount` : `String` - *(Required)* The amount of Bitcoin to send, denominated in satoshis (Bitcoin base unit). * `changeAddress` : `String` - *(Optional)* The sender's public address to receive change. * `memo` : `String` - *(Optional)* The OP\_RETURN value as a hex string without 0x prefix, maximum 80 bytes. ### Returns * `Object` * `txid` : `String` - *(Required)* The transaction id as a hex string without 0x prefix. ### Example The example below specifies a simple transfer of 1.23 BTC (123000000 Satoshi). ```javascript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "sendTransfer", "params": { "account": "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", "recipientAddress": "bc1pmzfrwwndsqmk5yh69yjr5lfgfg4ev8c0tsc06e", "amount": "123000000", "memo": "636861726c6579206c6f766573206865" } } // Result { "id": 1, "jsonrpc": "2.0", "result": { "txid": "f007551f169722ce74104d6673bd46ce193c624b8550889526d1b93820d725f7" } } ``` ## getAccountAddresses This method returns all current addresses needed for a dapp to fetch all UTXOs, calculate the total balance and prepare transactions. Dapps will typically use an indexing service to query for balances and UTXOs for all addresses returned by this method, such as: * [Blockbook API](https://github.com/trezor/blockbook/blob/master/docs/api.md#get-address) * [Bitcore API](https://github.com/bitpay/bitcore/blob/master/packages/bitcore-node/docs/api-documentation.md#address) We recognize that there are two broad classes of wallets in use today: 1. Wallets that generate a new change or receive address for every transaction ("dynamic wallet"). 2. Wallets that reuse the first external address for every transaction ("static wallet"). #### Implementation Details * All wallets **should** include the first external address and all addresses with one or more UTXOs, unless they're filtered by `intentions`. * Dynamic wallets **should** include minimum 2 unused change and receive addresses. Otherwise dapps may have to request [getAccountAddresses](#getaccountaddresses) after every transaction to discover the new addresses and keep track of the user's total balance. * All wallets **must** return fewer than 20 unused change and receive addresses to avoid breaking the [gap limit](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#address-gap-limit). ### Parameters * `Object` * `account` : `String` - *(Required)* The connected account's first external address. * `intentions` : `String[]` - *(Optional)* Filter what addresses to return, e.g. "payment" or "ordinal". ### Returns * `Array` * `Object` * `address` : `String` - *(Required)* Public address belonging to the account. * `publicKey` : `String` - *(Optional)* Public key for the derivation path in hex, without 0x prefix. * `path` : `String` - *(Optional)* Derivation path of the address e.g. "m/84'/0'/0'/0/0". * `intention` : `String` - *(Optional)* Intention of the address, e.g. "payment" or "ordinal". ### Session Properties In a connection request, it is recommended to serialize the response to `getAccountAddresses` in `session.sessionProperties.bip122_getAccountAddresses`. This allows dapps to consume an active session without requiring a context switch to re-request all addresses and associated public keys from the wallet. ### Example: Dynamic Wallet The example below specifies a result from a dynamic wallet. For the sake of this example, receive and change addresses with index 3-4 are considered unused and addresses with paths `m/49'/0'/0'/0/7` and `m/84'/0'/0'/0/2` are considered to have UTXOs. Assuming the dapp monitors all returned addresses for balance changes, a new request to `getAccountAddresses` is only needed when all UTXOs in provided addresses have been spent, or when all provided `receive` addresses or `change` addresses have been used. ```javascript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "getAccountAddresses", "params": { "account": "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu" } } // Result { "id": 1, "jsonrpc": "2.0", "result": [ { "address": "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", "publicKey": "0330d54fd0dd420a6e5f8d3624f5f3482cae350f79d5f0753bf5beef9c2d91af3c", "path": "m/84'/0'/0'/0/0" }, { "address": "3KHhcgwPgYF9hE77zaKy2G36dpkcNtvQ33", "publicKey": "03b90230ca20150142bc2849a3df4517073978f32466214a0ebc00cac52f996989", "path": "m/49'/0'/0'/0/7" }, { "address": "bc1qp59yckz4ae5c4efgw2s5wfyvrz0ala7rgvuz8z", "publicKey": "038ffea936b2df76bf31220ebd56a34b30c6b86f40d3bd92664e2f5f98488dddfa", "path": "m/84'/0'/0'/0/2" }, { "address": "bc1qgl5vlg0zdl7yvprgxj9fevsc6q6x5dmcyk3cn3", "publicKey": "03de7490bcca92a2fb57d782c3fd60548ce3a842cad6f3a8d4e76d1f2ff7fcdb89", "path": "m/84'/0'/0'/0/3" }, { "address": "bc1qm97vqzgj934vnaq9s53ynkyf9dgr05rargr04n", "publicKey": "03995137c8eb3b223c904259e9b571a8939a0ec99b0717684c3936407ca8538c1b", "path": "m/84'/0'/0'/0/4" }, { "address": "bc1qv6vaedpeke2lxr3q0wek8dd7nzhut9w0eqkz9z", "publicKey": "03d0d243b6a3176fa20fa95cd7fb0e8e0829b83fc2b52053633d088c1a4ba91edf", "path": "m/84'/0'/0'/1/3" }, { "address": "bc1qetrkzfslk0d4kqjnu29fdh04tkav9vj3k36vuh", "publicKey": "02a8dee7573bcc7d3c1e9b9e267dbf0cd717343c31d322c5b074a3a97090a0d952", "path": "m/84'/0'/0'/1/4" } ] } ``` ### Example: Static Wallet The example below specifies a response from a static wallet. The returned address is used for both change and payments. It's the only address with UTXOs. ```javascript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "getAccountAddresses", "params": { "account": "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu" } } // Result { "id": 1, "jsonrpc": "2.0", "result": [ { "address": "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", "publicKey": "0330d54fd0dd420a6e5f8d3624f5f3482cae350f79d5f0753bf5beef9c2d91af3c", "path": "m/84'/0'/0'/0/0" } ] } ``` ## signPsbt This method can be used to request the signature of a Partially Signed Bitcoin Transaction (PSBT) and covers use-cases e.g. involving multiple-recipient transactions, requiring granular control over which UTXOs to spend or how to route change. ### Parameters * `Object` * `account` : `String` - *(Required)* The connected account's first external address. * `psbt` : `String` - *(Required)* Base64 encoded string of the PSBT to sign. * `signInputs` : `Array` * `Object` * `address` : `String` - *(Required)* The address whose private key to use for signing. * `index` : `Integer` - *(Required)* Specifies which input to sign. * `sighashTypes` : `Integer[]` - *(Optional)* Specifies which part(s) of the transaction the signature commits to. Default is `[1]`. * `broadcast` : `Boolean` - *(Optional)* Whether to finalize and broadcast the transaction after signing it. Default is `false`. ### Returns * `Object` * `psbt` : `String` - *(Required)* The base64 encoded signed PSBT. * `txid` : `String` - *(Optional)* The transaction ID as a hex-encoded string, without 0x prefix. This must be returned if the transaction was broadcasted. ## signMessage This method is used to sign a message with one of the connected account's addresses. ### Parameters * `Object` * `account` : `String` - *(Required)* The connected account's first external address. * `message` : `String` - *(Required)* The message to be signed by the wallet. * `address` : `String` - *(Optional)* The address whose private key to use for signing the message. * `protocol` : `"ecdsa" | "bip322"` - *(Optional)* Preferred signature type. Default is `"ecdsa"`. ### Returns * `Object` * `address` : `String` - *(Required)* The Bitcoin address used to sign the message. * `signature` : `String` - *(Required)* Hex encoded bytes of the signature, without 0x prefix. * `messageHash` : `String` - *(Optional)* Hex encoded bytes of the message hash, without 0x prefix. ## Events ### bip122\_addressesChanged This event is used by wallets to notify dapps about connected accounts' current addresses, for example all addresses with a UTXO and a few unused addresses. The event data has the same format as the [getAccountAddresses](#getaccountaddresses) result. #### Implementation Details * Wallets **should** emit a `bip122_addressesChanged` event immediately after connection approval of a BIP122 chain. * Wallets **should** emit a `bip122_addressesChanged` event whenever a UTXO is spent or created for a connected account's addresses. * Dapps **should** listen for `bip122_addressesChanged` events, collect and monitor all addresses for UTXO and balance changes. Example [session\_event](https://specs.walletconnect.com/2.0/specs/clients/sign/session-events#session_event) payload as received by a dapp: ``` { "id": 1675759795769537, "topic": "95d6aca451b8e3c6d9d176761bf786f1cc0a6d38dffd31ed896306bb37f6ae8d", "params": { "event": { "name": "bip122_addressesChanged", "data": [ { "address": "bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu", "publicKey": "0330d54fd0dd420a6e5f8d3624f5f3482cae350f79d5f0753bf5beef9c2d91af3c", "path": "m/84'/0'/0'/0/0" }, { "address": "3KHhcgwPgYF9hE77zaKy2G36dpkcNtvQ33", "publicKey": "03b90230ca20150142bc2849a3df4517073978f32466214a0ebc00cac52f996989", "path": "m/49'/0'/0'/0/7" }, { "address": "bc1qp59yckz4ae5c4efgw2s5wfyvrz0ala7rgvuz8z", "publicKey": "038ffea936b2df76bf31220ebd56a34b30c6b86f40d3bd92664e2f5f98488dddfa", "path": "m/84'/0'/0'/0/2" }, { "address": "bc1qgl5vlg0zdl7yvprgxj9fevsc6q6x5dmcyk3cn3", "publicKey": "03de7490bcca92a2fb57d782c3fd60548ce3a842cad6f3a8d4e76d1f2ff7fcdb89", "path": "m/84'/0'/0'/0/3" }, { "address": "bc1qm97vqzgj934vnaq9s53ynkyf9dgr05rargr04n", "publicKey": "03995137c8eb3b223c904259e9b571a8939a0ec99b0717684c3936407ca8538c1b", "path": "m/84'/0'/0'/0/4" }, { "address": "bc1qv6vaedpeke2lxr3q0wek8dd7nzhut9w0eqkz9z", "publicKey": "03d0d243b6a3176fa20fa95cd7fb0e8e0829b83fc2b52053633d088c1a4ba91edf", "path": "m/84'/0'/0'/1/3" }, { "address": "bc1qetrkzfslk0d4kqjnu29fdh04tkav9vj3k36vuh", "publicKey": "02a8dee7573bcc7d3c1e9b9e267dbf0cd717343c31d322c5b074a3a97090a0d952", "path": "m/84'/0'/0'/1/4" } ] }, "chainId": "bip122:000000000019d6689c085ae165831e93" } } ``` # Canton Source: https://docs.walletconnect.com/wallets/chains/canton Overview of the Canton JSON-RPC methods supported by Wallet SDK. These are the methods that wallets should implement to handle Canton transactions and messages via WalletConnect. ## Network / Chain Information * **Namespace:** `canton` * **CAIP-2:** `canton:` (e.g. `canton:devnet`, `canton:production`) * **CAIP-10 Account:** `canton::` (e.g. `canton:devnet:operator%3A%3A1220abc...`) Unlike most chains, Canton does not have fixed mainnet/testnet identifiers. Network IDs are **operator-defined** — each wallet is configured with one or more networks, and the `network-id` used in CAIP-2 identifiers comes from that configuration. dApps should **not** hardcode specific chain IDs in the session proposal. Instead, request the `canton` namespace without specifying `chains`, and work with whatever network the wallet provides in the approved session. The network ID and party ID are available directly from the session's `canton.accounts` array as CAIP-10 strings (e.g. `canton:production:operator%3A%3A1220abc...`). For full network details, use [`canton_getActiveNetwork`](#canton_getactivenetwork). ## Registered Methods & Events ```typescript theme={null} theme={null} const CANTON_WC_METHODS = [ 'canton_prepareSignExecute', 'canton_listAccounts', 'canton_getPrimaryAccount', 'canton_getActiveNetwork', 'canton_status', 'canton_ledgerApi', 'canton_signMessage', ] const CANTON_WC_EVENTS = ['accountsChanged', 'statusChanged', 'chainChanged'] ``` ### Auto-Approve vs Manual-Approve Read-only methods are auto-approved by the wallet. Methods that mutate the ledger or perform sensitive operations require explicit user approval. | Method | Approval | | --------------------------- | ------------ | | `canton_listAccounts` | Auto-approve | | `canton_getPrimaryAccount` | Auto-approve | | `canton_getActiveNetwork` | Auto-approve | | `canton_status` | Auto-approve | | `canton_ledgerApi` | Auto-approve | | `canton_prepareSignExecute` | Manual | | `canton_signMessage` | Manual | ## Method Name Mapping (dApp SDK) The dApp SDK's `WalletConnectTransport` maps SDK method names before sending over WC: | SDK method | WC method (on the wire) | | ------------------------------ | --------------------------- | | `canton_prepareExecute` | `canton_prepareSignExecute` | | `canton_prepareExecuteAndWait` | `canton_prepareSignExecute` | All other methods (`canton_listAccounts`, `canton_status`, `canton_ledgerApi`, etc.) are sent as-is. Both SDK methods resolve with the same response — over WalletConnect, every submission blocks until the transaction completes. ## RPC Methods ### canton\_prepareSignExecute Prepare, sign, and execute a Canton ledger transaction. This is the primary method for submitting commands that mutate ledger state. The wallet performs the full prepare → sign → execute cycle and responds when the transaction is complete. #### Request ```typescript theme={null} theme={null} interface CantonPrepareSignExecuteRequest { method: 'canton_prepareSignExecute'; params: CantonPrepareParams; } interface CantonPrepareParams { commandId?: string; // auto-generated (UUIDv4) if omitted commands?: { [k: string]: unknown }; actAs?: string[]; // defaults to [primaryWallet.partyId] if omitted readAs?: string[]; // defaults to [] if omitted disclosedContracts?: Array<{ templateId?: string; contractId?: string; createdEventBlob: string; synchronizerId?: string; }>; packageIdSelectionPreference?: string[]; } ``` #### Example Request ```json theme={null} theme={null} { "topic": "", "chainId": "canton:devnet", "request": { "method": "canton_prepareSignExecute", "params": { "commands": { "0": { "ExerciseCommand": { "templateId": "#:Module:Template", "contractId": "00abcdef...", "choice": "Transfer", "choiceArgument": { "receiver": "bob::1220..." } } } }, "commandId": "d290f1ee-6c54-4b01-90e6-d701748f0851", "actAs": ["operator::1220abc..."], "readAs": [], "disclosedContracts": [ { "templateId": "#:Module:Template", "contractId": "00abcdef...", "createdEventBlob": "", "synchronizerId": "wallet::1220e7b..." } ], "packageIdSelectionPreference": [""] } } } ``` #### Signing Providers Wallets support multiple signing backends. The signing provider determines the Ledger API flow used: | Provider | Flow | | --------------- | ---------------------------------------------------------------------------------------------------------------- | | `participant` | Single call to `POST /v2/commands/submit-and-wait` (participant signs internally) | | `wallet-kernel` | `POST /v2/interactive-submission/prepare` → local Ed25519 sign → `POST /v2/interactive-submission/execute` | | `blockdaemon` | `POST /v2/interactive-submission/prepare` → sign via Blockdaemon API → `POST /v2/interactive-submission/execute` | #### Success Response ```json theme={null} theme={null} { "id": 1234, "jsonrpc": "2.0", "result": { "status": "executed", "commandId": "d290f1ee-...", "payload": { "updateId": "tx-update-id", "completionOffset": 42 } } } ``` #### Error Response ```json theme={null} theme={null} { "id": 1234, "jsonrpc": "2.0", "error": { "code": 5001, "message": "Transaction execution failed: INVALID_ARGUMENT: ..." } } ``` #### User Rejected Response ```json theme={null} theme={null} { "id": 1234, "jsonrpc": "2.0", "error": { "code": 5000, "message": "User rejected" } } ``` *** ### canton\_listAccounts Retrieve all configured wallet accounts. #### Request ```json theme={null} theme={null} { "topic": "", "chainId": "canton:devnet", "request": { "method": "canton_listAccounts", "params": {} } } ``` #### Response ```json theme={null} theme={null} { "id": 1235, "jsonrpc": "2.0", "result": [ { "primary": true, "partyId": "operator::1220abc...", "status": "allocated", "hint": "operator", "publicKey": "", "namespace": "1220abc...", "networkId": "canton:production", "signingProviderId": "participant", "disabled": false } ] } ``` #### Wallet Type ```typescript theme={null} theme={null} interface Wallet { primary: boolean; partyId: string; status: 'initialized' | 'allocated' | 'removed'; hint: string; publicKey: string; namespace: string; networkId: string; signingProviderId: string; externalTxId?: string; topologyTransactions?: string; disabled?: boolean; reason?: string; } ``` *** ### canton\_getPrimaryAccount Retrieve the primary wallet account (where `primary === true`). #### Request ```json theme={null} theme={null} { "topic": "", "chainId": "canton:devnet", "request": { "method": "canton_getPrimaryAccount", "params": {} } } ``` #### Response ```json theme={null} theme={null} { "id": 1236, "jsonrpc": "2.0", "result": { "primary": true, "partyId": "operator::1220abc...", "status": "allocated", "hint": "operator", "publicKey": "", "namespace": "1220abc...", "networkId": "canton:production", "signingProviderId": "participant" } } ``` *** ### canton\_getActiveNetwork Retrieve the currently active network configuration. #### Request ```json theme={null} theme={null} { "topic": "", "chainId": "canton:devnet", "request": { "method": "canton_getActiveNetwork", "params": {} } } ``` #### Response ```json theme={null} theme={null} { "id": 1237, "jsonrpc": "2.0", "result": { "networkId": "canton:production", "ledgerApi": "http://127.0.0.1:5003" } } ``` *** ### canton\_status Check the wallet's connectivity to the Canton ledger. #### Request ```json theme={null} theme={null} { "topic": "", "chainId": "canton:devnet", "request": { "method": "canton_status", "params": {} } } ``` #### Response (ledger reachable) ```json theme={null} theme={null} { "id": 1238, "jsonrpc": "2.0", "result": { "provider": { "id": "remote-da", "version": "3.4.0", "providerType": "remote" }, "connection": { "isConnected": true, "isNetworkConnected": true }, "network": { "networkId": "canton:production", "ledgerApi": "http://127.0.0.1:5003", "accessToken": "" // optional but recommended } } } ``` #### Response (ledger unreachable) ```json theme={null} theme={null} { "id": 1238, "jsonrpc": "2.0", "result": { "provider": { "id": "remote-da", "version": "3.4.0", "providerType": "remote" }, "connection": { "isConnected": true, "isNetworkConnected": false, "reason": "Ledger unreachable" } } } ``` *** ### canton\_ledgerApi Proxy raw Canton Ledger API requests through the wallet. The wallet authenticates and forwards the request. #### Request ```typescript theme={null} theme={null} interface CantonLedgerApiRequest { method: 'canton_ledgerApi'; params: CantonLedgerApiParams; } interface CantonLedgerApiParams { requestMethod: 'GET' | 'POST'; resource: string; body?: string | object; } ``` #### Example Request ```json theme={null} theme={null} { "topic": "", "chainId": "canton:devnet", "request": { "method": "canton_ledgerApi", "params": { "requestMethod": "POST", "resource": "/v2/state/active-contracts", "body": { "filter": { "filtersByParty": { "operator::1220abc...": { "cumulative": { "templateFilters": [] } } } } } } } } ``` #### Response ```json theme={null} theme={null} { "id": 1239, "jsonrpc": "2.0", "result": {} } ``` The `result` field contains the raw Ledger API JSON response as-is. *** ### canton\_signMessage Sign an arbitrary message with the wallet's Ed25519 private key. #### Request ```typescript theme={null} theme={null} interface CantonSignMessageRequest { method: 'canton_signMessage'; params: { message: string; }; } ``` #### Example Request ```json theme={null} theme={null} { "topic": "", "chainId": "canton:devnet", "request": { "method": "canton_signMessage", "params": { "message": "Please sign this message to verify your identity" } } } ``` #### Success Response ```json theme={null} theme={null} { "id": 1240, "jsonrpc": "2.0", "result": { "signature": "", "publicKey": "" } } ``` ## Events ### accountsChanged Emitted when wallet accounts are added, removed, or modified. ```json theme={null} theme={null} { "name": "accountsChanged", "data": [ { "primary": true, "partyId": "operator::1220abc...", "status": "allocated", "hint": "operator", "publicKey": "...", "namespace": "1220abc...", "networkId": "canton:production", "signingProviderId": "participant" } ] } ``` ### statusChanged Emitted when the wallet's connectivity status changes. ```json theme={null} theme={null} { "name": "statusChanged", "data": { "provider": { "id": "remote-da", "providerType": "remote" }, "connection": { "isConnected": true, "isNetworkConnected": true }, "network": { "networkId": "canton:production" } } } ``` ### chainChanged Emitted when the wallet switches to a different network. ```json theme={null} theme={null} { "name": "chainChanged", "data": { "chainId": "canton:production" } } ``` ## Session Lifecycle ### Pairing The dApp creates a pairing URI and delivers it to the wallet: ```typescript theme={null} const { uri, approval } = await signClient.connect({ optionalNamespaces: { canton: { methods: CANTON_WC_METHODS, events: CANTON_WC_EVENTS, }, }, }) ``` ### Session Approval The wallet builds approved namespaces including the CAIP-10 account with the URL-encoded partyId: ```json theme={null} theme={null} { "canton": { "chains": ["canton:devnet"], "accounts": ["canton:devnet:operator%3A%3A1220abc..."], "methods": ["canton_prepareSignExecute", "canton_listAccounts", "canton_getPrimaryAccount", "canton_getActiveNetwork", "canton_status", "canton_ledgerApi", "canton_signMessage"], "events": ["accountsChanged", "statusChanged", "chainChanged"] } } ``` ## Error Codes | Code | Meaning | | ------ | -------------------------------------- | | `5000` | User rejected | | `5001` | Execution / handler error | | `5100` | Canton namespace not found in proposal | | `6000` | Wallet disconnected | ## Notes & Considerations * All requests and responses comply with JSON-RPC structure (`id`, `jsonrpc`, etc.). * Canton uses Ed25519 signing for transaction authentication. * The `ledgerApi` method acts as a transparent proxy — the wallet handles authentication with the Canton Ledger API. Only `GET` and `POST` are supported; other HTTP methods will return a `5001` error. * Party IDs in CAIP-10 accounts are URL-encoded (e.g. `operator::1220abc...` becomes `operator%3A%3A1220abc...`). * The `canton_prepareSignExecute` method always performs the full prepare → sign → execute cycle synchronously, responding only when the transaction is complete. * The WC session `chainId` (e.g. `canton:devnet`) may differ from the `networkId` in wallet/network records (e.g. `canton:production`). The `chainId` identifies the chain at pairing time, while `networkId` reflects the wallet's internal network configuration. # Ethereum Source: https://docs.walletconnect.com/wallets/chains/evm Overview of the Ethereum JSON-RPC methods supported by Wallet SDK. ## personal\_sign The sign method calculates an Ethereum specific signature with:`sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message)))`. By adding a prefix to the message makes the calculated signature recognizable as an Ethereum specific signature. This prevents misuse where a malicious DApp can sign arbitrary data (e.g. transaction) and use the signature to impersonate the victim. **Note** See ecRecover to verify the signature. ### Parameters message, account 1. `DATA`, N Bytes - message to sign. 2. `DATA`, 20 Bytes - address. ### Returns `DATA`: Signature ### Example ```javascript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "personal_sign", "params":["0xdeadbeaf","0x9b2055d370f73ec7d8a03e965129118dc8f5bf83"], } // Result { "id": 1, "jsonrpc": "2.0", "result": "0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b" } ``` ## eth\_sign The sign method calculates an Ethereum specific signature with: `sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message)))`. By adding a prefix to the message makes the calculated signature recognizable as an Ethereum specific signature. This prevents misuse where a malicious DApp can sign arbitrary data (e.g. transaction) and use the signature to impersonate the victim. **Note** the address to sign with must be unlocked. ### Parameters account, message 1. `DATA`, 20 Bytes - address. 2. `DATA`, N Bytes - message to sign. ### Returns `DATA`: Signature ### Example ```javascript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "eth_sign", "params": ["0x9b2055d370f73ec7d8a03e965129118dc8f5bf83", "0xdeadbeaf"], } // Result { "id": 1, "jsonrpc": "2.0", "result": "0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b" } ``` An example how to use solidity ecrecover to verify the signature calculated with `eth_sign` can be found [here](https://gist.github.com/bas-vk/d46d83da2b2b4721efb0907aecdb7ebd). The contract is deployed on the testnet Ropsten and Rinkeby. ## eth\_signTypedData Calculates an Ethereum-specific signature in the form of `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))` By adding a prefix to the message makes the calculated signature recognizable as an Ethereum specific signature. This prevents misuse where a malicious DApp can sign arbitrary data (e.g. transaction) and use the signature to impersonate the victim. **Note** the address to sign with must be unlocked. ### Parameters account, message 1. `DATA`, 20 Bytes - address. 2. `DATA`, N Bytes - message to sign containing type information, a domain separator, and data ### Example Parameters ```javascript theme={null} theme={null} [ "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", { types: { EIP712Domain: [ { name: "name", type: "string", }, { name: "version", type: "string", }, { name: "chainId", type: "uint256", }, { name: "verifyingContract", type: "address", }, ], Person: [ { name: "name", type: "string", }, { name: "wallet", type: "address", }, ], Mail: [ { name: "from", type: "Person", }, { name: "to", type: "Person", }, { name: "contents", type: "string", }, ], }, primaryType: "Mail", domain: { name: "Ether Mail", version: "1", chainId: 1, verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", }, message: { from: { name: "Cow", wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", }, to: { name: "Bob", wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", }, contents: "Hello, Bob!", }, }, ]; ``` ### Returns `DATA`: Signature ### Example ```javascript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "eth_signTypedData", "params": ["0x9b2055d370f73ec7d8a03e965129118dc8f5bf83", {see above}], } ' // Result { "id": 1, "jsonrpc": "2.0", "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c" } ``` ## eth\_sendTransaction Creates new message call transaction or a contract creation, if the data field contains code. ### Parameters 1. `Object` - The transaction object 2. `from`: `DATA`, 20 Bytes - The address the transaction is send from. 3. `to`: `DATA`, 20 Bytes - (optional when creating new contract) The address the transaction is directed to. 4. `data`: `DATA` - The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. For details see [Ethereum Contract ABI](https://docs.soliditylang.org/en/latest/abi-spec.html) 5. `gas`: `QUANTITY` - (optional, default: 90000) Integer of the gas provided for the transaction execution. It will return unused gas. 6. `gasPrice`: `QUANTITY` - (optional, default: To-Be-Determined) Integer of the gasPrice used for each paid gas 7. `value`: `QUANTITY` - (optional) Integer of the value sent with this transaction 8. `nonce`: `QUANTITY` - (optional) Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce. ### Example Parameters ```javascript theme={null} theme={null} [ { from: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", to: "0xBDE1EAE59cE082505bB73fedBa56252b1b9C60Ce", data: "0x", gasPrice: "0x029104e28c", gas: "0x5208", value: "0x00", }, ]; ``` ### Returns `DATA`, 32 Bytes - the transaction hash, or the zero hash if the transaction is not yet available. Use `eth_getTransactionReceipt` to get the contract address, after the transaction was mined, when you created a contract. ### Example ```javascript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "eth_sendTransaction", "params":[{see above}], } // Result { "id": 1, "jsonrpc": "2.0", "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" } ``` ## eth\_signTransaction Signs a transaction that can be submitted to the network at a later time using with `eth_sendRawTransaction` ### Parameters 1. `Object` - The transaction object 2. `from`: `DATA`, 20 Bytes - The address the transaction is send from. 3. `to`: `DATA`, 20 Bytes - (optional when creating new contract) The address the transaction is directed to. 4. `data`: `DATA` - The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. For details see [Ethereum Contract ABI](https://docs.soliditylang.org/en/latest/abi-spec.html) 5. `gas`: `QUANTITY` - (optional, default: 90000) Integer of the gas provided for the transaction execution. It will return unused gas. 6. `gasPrice`: `QUANTITY` - (optional, default: To-Be-Determined) Integer of the gasPrice used for each paid gas 7. `value`: `QUANTITY` - (optional) Integer of the value sent with this transaction 8. `nonce`: `QUANTITY` - (optional) Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce. ### Example Parameters ```javascript theme={null} theme={null} [ { from: "0xb60e8dd61c5d32be8058bb8eb970870f07233155", to: "0xd46e8dd67c5d32be8058bb8eb970870f07244567", data: "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675", gas: "0x76c0", // 30400 gasPrice: "0x9184e72a000", // 10000000000000 value: "0x9184e72a", // 2441406250 nonce: "0x117", // 279 }, ]; ``` ### Returns `DATA` - the signed transaction data ### Example ```javascript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "eth_signTransaction", "params":[{see above}], } // Result { "id": 1, "jsonrpc": "2.0", "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" } ``` ## eth\_sendRawTransaction Creates new message call transaction or a contract creation for signed transactions. ### Parameters 1. `DATA`, the signed transaction data. ### Returns `DATA`, 32 Bytes - the transaction hash, or the zero hash if the transaction is not yet available. Use `eth_getTransactionReceipt` to get the contract address, after the transaction was mined, when you created a contract. ### Example ```javascript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "eth_sendRawTransaction", "params":[ "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f07244567" ], } // Result { "id": 1, "jsonrpc": "2.0", "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" } ``` # Chain Support Source: https://docs.walletconnect.com/wallets/chains/overview The Wallet SDK is built to be **chain-agnostic** — it supports integrations across multiple blockchain ecosystems by working closely with each chain's foundations and developer communities to standardize namespaces, transaction flows, and JSON-RPC methods. ## Ecosystem Reference Pages * [EVM](/wallets/chains/evm) * [Solana](/wallets/chains/solana) * [Bitcoin](/wallets/chains/bitcoin) * [SUI](/wallets/chains/sui) * [Stacks](/wallets/chains/stacks) * [TON](/wallets/chains/ton) * [Tron](/wallets/chains/tron) * [ADI Chain](/wallets/chains/adi) * [Canton](/wallets/chains/canton) * [Stellar](/wallets/chains/stellar) ## Adding New Chain Support Interested in adding support for a new blockchain ecosystem? We work closely with chain foundations and developer communities to standardize integration specifications. **Contact us to start the process:** [sales@walletconnect.com](mailto:sales@walletconnect.com) # Solana Source: https://docs.walletconnect.com/wallets/chains/solana Overview of the Solana JSON-RPC methods supported by Wallet SDK. ## solana\_getAccounts This method returns an Array of public keys available to sign from the wallet. ### Parameters none ### Returns `Array` - Array of accounts: * `Object` : * `pubkey` : `String` - public key for keypair ### Example ```typescript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "solana_getAccounts", "params": {} } // Result { "id": 1, "jsonrpc": "2.0", "result": [{ "pubkey": "722RdWmHC5TGXBjTejzNjbc8xEiduVDLqZvoUGz6Xzbp" }] } ``` ## solana\_requestAccounts This method returns an Array of public keys available to sign from the wallet. ### Parameters none ### Returns `Array` - Array of accounts: * `Object` : * `pubkey` : `String` - public key for keypair ### Example ```typescript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "solana_getAccounts", "params": {} } // Result { "id": 1, "jsonrpc": "2.0", "result": [{ "pubkey": "722RdWmHC5TGXBjTejzNjbc8xEiduVDLqZvoUGz6Xzbp" }] } ``` ## solana\_signMessage This method returns a signature for the provided message from the requested signer address. ### Parameters `Object` - Signing parameters: * `message` : `String` - the message to be signed (base58 encoded) * `pubkey` : `String` - public key of the signer ### Returns `Object`: * `signature` : `String` - corresponding signature for signed message ### Example ```javascript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "solana_signMessage", "params": { "message": "37u9WtQpcm6ULa3VtWDFAWoQc1hUvybPrA3dtx99tgHvvcE7pKRZjuGmn7VX2tC3JmYDYGG7", "pubkey": "AqP3MyNwDP4L1GJKYhzmaAUdrjzpqJUZjahM7kHpgavm" } } // Result { "id": 1, "jsonrpc": "2.0", "result": { signature: "2Lb1KQHWfbV3pWMqXZveFWqneSyhH95YsgCENRWnArSkLydjN1M42oB82zSd6BBdGkM9pE6sQLQf1gyBh8KWM2c4" } } ``` ## solana\_signTransaction This method returns a signature over the provided instructions by the targeted public key. Refer always to `transaction` param. The deprecated parameters are not compatible with versioned transactions. ### Parameters `Object` - Signing parameters:
* `transaction` : `String` - base64-encoded serialized transaction
* **\[deprecated]** `feePayer` : `String | undefined` - public key of the transaction fee payer
* **\[deprecated]** `instructions` : `Array` of `Object` or `undefined` - instructions to be atomically executed:
* `Object` - instruction
* `programId` : `String` - public key of the on chain program
* `data` : `String | undefined` - encoded calldata for instruction
* `keys` : `Array` of `Object` - account metadata used to define instructions
* `Object` - key
* `isSigner` : `Boolean` - true if an instruction requires a transaction signature matching `pubkey`
* `isWritable` : `Boolean` - true if the `pubkey` can be loaded as a read-write account
* `pubkey` : `String` - public key of authorized program
* **\[deprecated]** `recentBlockhash` : `String | undefined` - a recent blockhash
* **\[deprecated]** `signatures` : `Array` of `Object` or `undefined` - (optional) previous partial signatures for this instruction set
* `Object` - partial signature
* `pubkey` : `String` - pubkey of the signer
* `signature` : `String` - signature matching `pubkey`
### Returns `Object`: * `signature`: `String` - corresponding signature for signed instructions * `transaction`?: `String | undefined` - optional: base64-encoded serialized transaction ### Example ```typescript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "solana_signTransaction", "params": { "feePayer": "AqP3MyNwDP4L1GJKYhzmaAUdrjzpqJUZjahM7kHpgavm", "instructions": [{ "programId": "Vote111111111111111111111111111111111111111", "data": "37u9WtQpcm6ULa3VtWDFAWoQc1hUvybPrA3dtx99tgHvvcE7pKRZjuGmn7VX2tC3JmYDYGG7", "keys": [{ "isSigner": true, "isWritable": true, "pubkey": "AqP3MyNwDP4L1GJKYhzmaAUdrjzpqJUZjahM7kHpgavm" }] }], "recentBlockhash": "2bUz6wu3axM8cDDncLB5chWuZaoscSjnoMD2nVvC1swe", "signatures": [{ "pubkey": "AqP3MyNwDP4L1GJKYhzmaAUdrjzpqJUZjahM7kHpgavm", "signature": "2Lb1KQHWfbV3pWMqXZveFWqneSyhH95YsgCENRWnArSkLydjN1M42oB82zSd6BBdGkM9pE6sQLQf1gyBh8KWM2c4" }], "transaction": "r32f2..FD33r" } } // Result { "id": 1, "jsonrpc": "2.0", "result": { signature: "2Lb1KQHWfbV3pWMqXZveFWqneSyhH95YsgCENRWnArSkLydjN1M42oB82zSd6BBdGkM9pE6sQLQf1gyBh8KWM2c4" } } ``` ## solana\_signAllTransactions This method is responsible for signing a list of transactions. The wallet must sign all transactions and return the signed transactions in the same order as received. Wallets must sign all transactions or return an error if it is not possible to sign any of them. ### Parameters `Object` - Signing parameters: * `transactions` : `String[]` - base64-encoded serialized list of transactions
### Returns `Object`: * `transactions` : `String[]` - base64-encoded serialized list of signed transactions in the same order as received
### Example ```typescript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "solana_signAllTransactions", "params": { "transactions": string[] } } // Response { "id": 1, "jsonrpc": "2.0", "result": { "transactions": string[] } } ``` ## solana\_signAndSendTransaction This method is responsible for signing and sending a transaction to the Solana network. The wallet must sent the transaction and return the signature that can be used as a transaction id. ### Parameters `Object` - transaction and options:
* `transaction` : `String` - the whole transaction serialized and encoded with base64
* `sendOptions` : `Object` - options for sending the transaction
* `skipPreflight` : `Boolean` - skip preflight checks
* `preflightCommitment` : `'processed' | 'confirmed' | 'finalized' | 'recent' | 'single' | 'singleGossip' | 'root' | 'max'` - preflight commitment level
* `maxRetries` : `Number` - maximum number of retries
* `minContextSlot` : `Number` - minimum context slot
### Returns `Object`: * `signature` : `String`, - the signature of the transaction encoded with base58 used as transaction id
### Example ```typescript theme={null} theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "solana_signAndSendTransaction", "params": { "transaction": string, "sendOptions": { "skipPreflight"?: boolean, "preflightCommitment"?: 'processed' | 'confirmed' | 'finalized' | 'recent' | 'single' | 'singleGossip' | 'root' | 'max', "maxRetries"?: number, "minContextSlot"?: number, } } } // Response { "id": 1, "jsonrpc": "2.0", "result": { "signature": string } } ``` # Stacks Source: https://docs.walletconnect.com/wallets/chains/stacks Overview of the Stacks JSON-RPC methods supported by Wallet SDK. These are the methods that wallets should implement to handle Stacks transfers and messages via WalletConnect. ## Core Methods (common) ### stx\_getAddresses Retrieve active account addresses; primarily Stacks-focused. #### Request ```json theme={null} { "id": 1, "jsonrpc": "2.0", "method": "stx_getAddresses", "params": {} } ``` #### Response ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "addresses": [ { "symbol": "STX", "address": "SP…" } ] } } ``` **Notes:** * Use this first to select the wallet's active address. * Filter on `symbol: "STX"` or by address prefix (SP for mainnet, ST for testnet). ## Stacks Methods ### stx\_transferStx Transfer STX. #### Request ```json theme={null} { "id": 1, "jsonrpc": "2.0", "method": "stx_transferStx", "params": { "sender": "SP3F7GQ48JY59521DZEE6KABHBF4Q33PEYJ823ZXQ", "recipient": "SP3F7GQ48JY59521DZEE6KABHBF4Q33PEYJ823ZXQ", "amount": "100000000000", "memo": "", "network": "mainnet" } } ``` #### Parameters | Parameter | Required? | Data Type | Description | | ----------- | --------- | --------- | ------------------------------------------------------------------- | | `sender` | Required | `string` | The stacks address of sender (required for multi-account scenarios) | | `recipient` | Required | `string` | Stacks address | | `amount` | Required | `string` | micro-STX (uSTX) | | `memo` | Optional | `string` | Memo string to be included with the transfer transaction | | `network` | Optional | `string` | "mainnet" \| "testnet" \| "devnet" | #### Response ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "txid": "1234567890abcdef1234567890abcdef12345678", "transaction": "0x…" } } ``` ### stx\_signTransaction Sign a Stacks transaction. Optional broadcast. #### Request ```json theme={null} { "id": 1, "jsonrpc": "2.0", "method": "stx_signTransaction", "params": { "transaction": "0x…", "broadcast": false, "network": "mainnet" } } ``` #### Parameters | Parameter | Required? | Data Type | Description | | ------------- | --------- | --------- | ---------------------------------- | | `transaction` | Required | `string` | hex transaction | | `broadcast` | Optional | `boolean` | default false | | `network` | Optional | `string` | "mainnet" \| "testnet" \| "devnet" | #### Response ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "signature": "0x…", "transaction": "0x…", "txid": "1234567890abcdef1234567890abcdef12345678" } } ``` **Note:** `txid` is present if broadcast=true ### stx\_signMessage Sign arbitrary message; supports structured (SIP-018). #### Request ```json theme={null} { "id": 1, "jsonrpc": "2.0", "method": "stx_signMessage", "params": { "address": "SP3F7GQ48JY59521DZEE6KABHBF4Q33PEYJ823ZXQ", "message": "message", "messageType": "utf8", "network": "mainnet", "domain": "example.com" } } ``` #### Parameters | Parameter | Required? | Data Type | Description | | ------------- | --------- | --------- | --------------------------------------------------------------------------------------------------------- | | `address` | Required | `string` | The stacks address of sender | | `message` | Required | `string` | Utf-8 string representing the message to be signed by the wallet | | `messageType` | Optional | `string` | Type of message for signing: `utf8` for basic string or `structured` for structured data | | `network` | Optional | `string` | Network for signing: `mainnet`, `testnet`, `signet`, `devnet` (note: redundant since chainId is provided) | | `domain` | Optional | `string` | Domain tuple per SIP-018 (for structured messages only) | #### Response ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "signature": "0x…" } } ``` ### stx\_signStructuredMessage Domain-bound structured signing (SIP-018). #### Request ```json theme={null} { "id": 1, "jsonrpc": "2.0", "method": "stx_signStructuredMessage", "params": { "message": "message", "domain": "domain" } } ``` #### Parameters | Parameter | Required? | Data Type | Description | | --------- | --------- | ------------------ | ----------------------------- | | `message` | Required | `string \| object` | message to be signed | | `domain` | Required | `string \| object` | domain for structured signing | #### Response ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "signature": "0x…", "publicKey": "0x04…" } } ``` **Note:** `publicKey` is optional ### stx\_callContract Wrapper method for `stx_signTransaction` that calls a Stacks contract. #### Request ```json theme={null} { "id": 1, "jsonrpc": "2.0", "method": "stx_callContract", "params": { "contract": "SP3F7GQ48JY59521DZEE6KABHBF4Q33PEYJ823ZXQ.my-contract", "functionName": "get-balance", "functionArgs": [] } } ``` #### Parameters | Parameter | Required? | Data Type | Description | | -------------- | --------- | ---------- | ------------------------------------------------------------------------------- | | `contract` | Required | `string` | Fully qualified contract identifier, including Stacks address and contract name | | `functionName` | Required | `string` | Name of the function to call | | `functionArgs` | Required | `string[]` | Arguments to pass to the contract function, encoded as strings | #### Response ```json theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "txid": "stack_tx_id", "transaction": "raw_tx_hex" } } ``` * `txid` - is used to identify the transaction on the explorer * `transaction` - hex-encoded raw transaction ## Session Properties In a connection request, it is recommended to serialize the response to `stx_getAddresses` in `session.sessionProperties.stacks_getAddresses`. This allows dapps to consume an active session without requiring a context switch to re-request all addresses and associated public keys from the wallet. # Stellar Source: https://docs.walletconnect.com/wallets/chains/stellar Overview of the Stellar JSON-RPC methods supported by Wallet SDK. These are the methods that wallets should implement to handle Stellar transactions and messages via WalletConnect. The Stellar RPC standard is a proposal still under review and specifications may change. Implementation details and method signatures are subject to updates. ## Network / Chain Information | Item | Form | | ------- | ------------------------------------------------------------------------------------------- | | CAIP-2 | `stellar:pubnet` OR `stellar:testnet` | | CAIP-10 | `stellar:pubnet:G…` — base32 StrKey account ID (56 chars, version byte `0x30`) | | CAIP-19 | `stellar:pubnet/slip44:148` (XLM) or `stellar:pubnet/asset:{code}-{issuer}` (issued assets) | The CAIP-2 reference (`pubnet` / `testnet`) matches the [Stellar CAIP-2 namespace draft](https://github.com/ChainAgnostic/namespaces/blob/main/stellar/caip2.md). It is **not** the network passphrase — that is a separate signing-domain constant wallets bind into every signature, derived from the chain (see [Signing semantics](#signing-semantics)). ### Account format Account IDs returned to the dApp are **CAIP-10 strings**, e.g.: ```plain theme={null} stellar:pubnet:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN ``` Wallets MUST return the **G… StrKey** form (Ed25519 public key + CRC16 checksum, base32-encoded). Wallets MUST NOT return: * Muxed account (`M…`) IDs — these require a separate spec (CAP-27) and are not universally supported. * Pre-auth (`T…`) or signer-hash (`X…`) StrKey forms — these are not accounts. * Raw 32-byte public keys without StrKey encoding. ### XDR encoding convention All transaction payloads cross the wire as **base64-encoded XDR strings**, matching SDF's reference SDKs and Horizon's `/transactions?tx=…` parameter. Specifically: * `stellar_signXDR` and `stellar_signAndSubmitXDR` accept and return a base64-encoded **`TransactionEnvelope`** XDR. * The envelope's discriminant determines tx version: `ENVELOPE_TYPE_TX_V0`, `ENVELOPE_TYPE_TX`, or `ENVELOPE_TYPE_TX_FEE_BUMP`. * Wallets MUST accept all three; wallets MAY emit signatures only on V1 and fee-bump envelopes (V0 is deprecated). ## Session Proposal A standard WalletConnect session proposal for a Stellar-enabled dApp: ```json theme={null} { "optionalNamespaces": { "stellar": { "chains": ["stellar:pubnet"], "methods": [ "stellar_signXDR", "stellar_signAndSubmitXDR", "stellar_signMessage", "stellar_signAuthEntry" ], "events": ["accountsChanged", "chainChanged"] } } } ``` | Property | Optional methods | | ---------------- | -------------------------- | | Sign transaction | `stellar_signXDR` | | Sign a message | `stellar_signMessage` | | Sign and Submit | `stellar_signAndSubmitXDR` | | Soroban | `stellar_signAuthEntry` | ## RPC Methods ### stellar\_signXDR Asks the wallet to attach a signature to a Stellar `TransactionEnvelope` and return the resulting envelope **without broadcasting it**. The dApp (or a relayer it trusts) is responsible for submission. This is the **primary method** for fee-abstracted flows: the dApp constructs an inner transaction whose `source_account` is the buyer; the wallet signs as the buyer; a separate fee-source wraps the result in a `FeeBumpTransactionEnvelope` and submits. #### Parameters `Object`: | Field | Type | Required | Description | | --------- | ------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `xdr` | `string` (base64) | yes | The unsigned (or partially-signed) `TransactionEnvelope` XDR to sign. | | `chain` | `string` (CAIP-2) | yes | Must equal the session's selected chain — `stellar:pubnet`. Wallet MUST reject signing if the encoded `network_id` inside the tx does not match this chain. | | `account` | `string` (CAIP-10) | yes | The account that should sign. Wallet MUST reject if it doesn't custody this account. | This method **signs only**. To sign and broadcast in a single round-trip, use [`stellar_signAndSubmitXDR`](#stellar-signandsubmitxdr). #### Returns `Object`: | Field | Type | Description | | --------------- | ------------------ | ------------------------------------------------------------------------------------- | | `signedXDR` | `string` (base64) | The signed `TransactionEnvelope` XDR. Existing signatures in the input are preserved. | | `signerAddress` | `string` (CAIP-10) | The account that signed (echoes `account`). | #### Example ```json theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "stellar_signXDR", "params": { "xdr": "AAAAAgAAAACz/ZNn8sJpz0r1A/8mO0wQVjEPFNG8mU3sk1Wk7TPxIQAAAGQAGYGzAAAACgAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAEsbESgFLrQc4j7yo2Up/EWNqQhgvBYGgYNCu9EuRRl+AAAAAVVTREMAAAAA...", "chain": "stellar:pubnet", "account": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ" } } // Response { "id": 1, "jsonrpc": "2.0", "result": { "signedXDR": "AAAAAgAAAACz/ZNn8sJpz0r1A/8mO0wQVjEPFNG8mU3sk1Wk7TPxIQAAAGQAGYGzAAAACgAAAAAAAAAAAAAA...AAAAAEFNB7s=", "signerAddress": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ" } } ``` ### stellar\_signAndSubmitXDR Asks the wallet to sign and submit a transaction in one step. The wallet broadcasts via its configured RPC (Horizon or Stellar RPC) and returns the resulting transaction hash. Use this method when the dApp does **not** operate a relayer (i.e. the buyer is paying their own XLM fee directly). #### Parameters `Object`: | Field | Type | Required | Description | | ------------------ | ------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `xdr` | `string` (base64) | yes | The unsigned `TransactionEnvelope` XDR. | | `chain` | `string` (CAIP-2) | yes | Must equal the session's selected chain. | | `account` | `string` (CAIP-10) | yes | Signing account. | | `waitForInclusion` | `boolean` | no | If `true`, wallet waits up to ledger-close time before responding and returns `successful`. If `false` (default), wallet responds as soon as it receives the submission ack from its RPC. | #### Returns `Object`: | Field | Type | Description | | ------------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------- | | `tx_hash` | `string` (hex, 64) | The hash of the submitted transaction. | | `signedXDR` | `string` (base64) | The final signed `TransactionEnvelope` XDR (so the dApp can independently verify the hash). | | `successful` | `boolean` | Optional. Present only when `waitForInclusion: true`. `true` if the tx landed and `successful=true` in its result envelope. | #### Example ```json theme={null} // Request { "id": 2, "jsonrpc": "2.0", "method": "stellar_signAndSubmitXDR", "params": { "xdr": "AAAAAgAAAACz/ZNn8sJpz0r1A/8mO0wQVjEPFNG8mU3sk1Wk7TPxIQAAAGQAGYGzAAAA...", "chain": "stellar:pubnet", "account": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ", "waitForInclusion": true } } // Response { "id": 2, "jsonrpc": "2.0", "result": { "tx_hash": "3389e9f0f1a54f04a78fd09a7e0fc0d44f1eecbe8c33a3d56a39c8b46d2a8b48", "signedXDR": "AAAAAgAAAACz/ZNn8sJpz0r1...AAAAAEFNB7s=", "successful": true } } ``` ### stellar\_signMessage Asks the wallet to sign an arbitrary message under a Stellar account's Ed25519 key, **outside** the context of a Stellar transaction. This enables [SEP-10 web auth](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md)-style sign-in flows and dApp session attestation. To prevent a malicious dApp from getting a wallet to sign a payload that is also a valid transaction body, wallets MUST prepend a domain-separating prefix before signing: ```plain theme={null} sign(Ed25519, sha256("Stellar Signed Message:\n" || message)) ``` The fixed UTF-8 prefix `"Stellar Signed Message:\n"` (with the trailing newline) is concatenated **directly** with the message bytes — no `0x00` separator and no length prefix — then SHA-256 hashed and Ed25519-signed. This matches the finalized [SEP-53](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md) convention (the same domain-separation approach as Bitcoin/Ethereum signed messages) and ensures cross-context replay is impossible — a SEP-10 challenge transaction would never collide with a `stellar_signMessage` payload. #### Parameters `Object`: | Field | Type | Required | Description | | ----------------- | -------------------------- | -------- | --------------------------------------------------------------------------------------------- | | `message` | `string` (utf-8 OR base64) | yes | The payload to sign. If `messageEncoding: "base64"`, decoded as raw bytes; default `"utf-8"`. | | `messageEncoding` | `"utf-8"` \| `"base64"` | no | Defaults to `"utf-8"`. | | `chain` | `string` (CAIP-2) | yes | `stellar:pubnet` (signature is network-agnostic, but the session context is). | | `account` | `string` (CAIP-10) | yes | Signing account. | #### Returns `Object`: | Field | Type | Description | | --------------- | ------------------ | ------------------------------------------ | | `signature` | `string` (base64) | 64-byte Ed25519 signature, base64-encoded. | | `signerAddress` | `string` (CAIP-10) | Signing account (echoes input). | #### Example ```json theme={null} // Request { "id": 3, "jsonrpc": "2.0", "method": "stellar_signMessage", "params": { "message": "pay-core://sign-in?nonce=8c4f1a2b&exp=1747746000", "messageEncoding": "utf-8", "chain": "stellar:pubnet", "account": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ" } } // Response { "id": 3, "jsonrpc": "2.0", "result": { "signature": "iJ7rH9N2T5q3Vh8U8a3l1eC9bD0fK6mPq9R5/4tZv1c9Ek2y0sJgPpVxT8aBhYf3LqW1uAYR7s2qNlDe6cZyAA==", "signerAddress": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ" } } ``` **Anti-pattern:** Do NOT sign raw bytes without the domain prefix. Wallets that do so MUST be considered non-compliant — they expose users to transaction-impersonation attacks. ### stellar\_signAuthEntry (Soroban) Signs a Soroban `SorobanAuthorizationEntry`, enabling Soroban contract authorizations to be co-signed by an address that is **not** the transaction's source account. This is the Stellar analogue to Ethereum's EIP-712 typed-data signing for permits / meta-transactions: a user authorizes a specific contract invocation tree, a separate party submits the transaction that consumes the authorization. The signing payload is the `HashIDPreimage::SOROBAN_AUTHORIZATION` preimage, computed as: ```plain theme={null} sign(Ed25519, sha256(xdr(HashIDPreimageSorobanAuthorization { network_id, nonce, signature_expiration_ledger, invocation, }))) ``` All four fields are pulled from the `SorobanCredentials::SOROBAN_CREDENTIALS_ADDRESS` block inside the auth entry. `network_id` is bound by the wallet from the session's CAIP-2 chain (NOT trusted from the request). #### Parameters `Object`: | Field | Type | Required | Description | | ----------- | ------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `authEntry` | `string` (base64) | yes | The unsigned `SorobanAuthorizationEntry` XDR. Its `credentials` MUST be of type `SOROBAN_CREDENTIALS_ADDRESS` with an empty `signature` SCVal. `SOROBAN_CREDENTIALS_SOURCE_ACCOUNT` entries are not signable via this method — they are authorized implicitly by signing the enclosing transaction. | | `chain` | `string` (CAIP-2) | yes | Must equal the session's selected chain. | | `account` | `string` (CAIP-10) | yes | The account that should sign. Wallet MUST verify it matches `credentials.address` inside the entry; reject with `4302` otherwise. | The wallet MUST also reject (`4304` — `AUTH_EXPIRED`) if `signature_expiration_ledger` is ≤ the current ledger sequence as known to the wallet, with a small safety margin to account for propagation. #### Returns `Object`: | Field | Type | Description | | ----------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `signedAuthEntry` | `string` (base64) | The updated `SorobanAuthorizationEntry` XDR with the `signature` SCVal populated. Other fields (nonce, expiration, invocation) MUST be byte-identical to the input. | | `signerAddress` | `string` (CAIP-10) | Echoes `account`. | The `signature` SCVal follows Stellar's account-contract signer convention: an `SCMap` with keys `"public_key"` (32-byte Ed25519 pubkey as `SCBytes`) and `"signature"` (64-byte Ed25519 signature as `SCBytes`). Wallets MUST NOT emit a raw 64-byte signature without the map wrapper — Soroban host code rejects it. #### Example ```json theme={null} // Request — dApp wants the user to authorize a transfer(from=user, to=merchant, amount=10_0000000) invocation { "id": 4, "jsonrpc": "2.0", "method": "stellar_signAuthEntry", "params": { "authEntry": "AAAAAQAAAAAAAAAAs/2TZ/LCac9K9QP/JjtMEFYxDxTRvJlN7JNVpO0z8SEAAAAAAAAAAQAGgaUAAAAAAAAAAa1uVUtkbThwc1ZmTGdEYzVlbnVsbAAAAAAAAAAIdHJhbnNmZXIAAAADAAAAEgAAAAAAAAAAs/2TZ/LCac9K9QP/JjtMEFYxDxTRvJlN7JNVpO0z8SEAAAASAAAAAAAAAABLGxEoBS60HOI+8qNlKfxFjakIYLwWBoGDQrvRLkUZfgAAAAoAAAAAAAAAAAAAAAAF9eEAAAAAAAAAAAA=", "chain": "stellar:pubnet", "account": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ" } } // Response — same entry, signature SCVal now populated with the account-contract signer map { "id": 4, "jsonrpc": "2.0", "result": { "signedAuthEntry": "AAAAAQAAAAAAAAAAs/2TZ/LCac9K9QP/JjtMEFYxDxTRvJlN7JNVpO0z8SEAAAAAAAAAAQAGgaUAAAAAAAAAAa1uVUtkbThwc1ZmTGdEYzVlbnVsbAAAAAAAAAAIdHJhbnNmZXIAAAADAAAAEgAAAAAAAAAAs/2TZ/LCac9K9QP/JjtMEFYxDxTRvJlN7JNVpO0z8SEAAAASAAAAAAAAAABLGxEoBS60HOI+8qNlKfxFjakIYLwWBoGDQrvRLkUZfgAAAAoAAAAAAAAAAAAAAAAF9eEAAAARAAAAAQAAAAIAAAAPAAAACnB1YmxpY19rZXkAAAAAAA0AAAAgs/2TZ/LCac9K9QP/JjtMEFYxDxTRvJlN7JNVpO0z8SEAAAAPAAAACXNpZ25hdHVyZQAAAAAAAA0AAABAi3hvR9N+a5q1Vh8U8a3l1eC9bD0fK6mPq9R5/4tZv1c9Ek2y0sJgPpVxT8aBhYf3LqW1uAYR7s2qNlDe6cZyAA==", "signerAddress": "stellar:pubnet:GCZ73GTH6LBGTT2K6UB76JR3JQIKMMIPCTI3ZGKN5SJVLJ65GPYSCIBQ" } } ``` ## Events | Event | Payload | Notes | | ----------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------- | | `accountsChanged` | `{ accounts: string[] }` (CAIP-10) | Emitted when the user changes the active account in their wallet, or revokes access for an account. | | `chainChanged` | `{ chainId: string }` (CAIP-2) | Emitted when the wallet switches the active network (e.g. `stellar:pubnet` ↔ `stellar:testnet`). | ## Signing semantics ### Network passphrase binding Every Stellar transaction signature is computed over: ```plain theme={null} sign(Ed25519, sha256(network_id || envelope_payload)) ``` where `network_id = sha256("Public Global Stellar Network ; September 2015")` for pubnet. This is **inside** the XDR envelope and is the protocol-level replay protection across networks. Wallets MUST: 1. Decode the envelope's signing payload, **not** the wire bytes, before signing. 2. Compute `network_id` from the network the session belongs to (`stellar:pubnet` → pubnet passphrase). Wallets MUST NOT trust a `network_id` embedded in the request — only the CAIP-2 chain identifier. 3. Refuse to sign if the decoded envelope's internal network reference (when present, e.g. on fee-bump inner txs) does not match the session chain. ### Fee-bump envelopes When the dApp passes a `FeeBumpTransactionEnvelope` to `stellar_signXDR`: * The wallet signs **only the inner tx**, not the outer fee-bump envelope. The outer envelope is signed by the `fee_source` account (typically a different party — the relayer). * Wallets MUST validate that the inner tx's `source_account` is in fact the `account` parameter. * Wallets MAY warn the user that fees are being paid by a different account (`fee_source`), and SHOULD display both the inner source and outer fee source in the signing UI. ### Computing the tx hash and explorer discoverability The transaction hash is **deterministic from the signed envelope** — signatures are computed over the hash, they are not part of it. As soon as a dApp receives `signedXDR` from `stellar_signXDR`, it can derive the same hash the network will use. ```plain theme={null} tx_hash = sha256( network_id ‖ ENVELOPE_TYPE ‖ tx_payload_xdr ) ``` | Component | Value | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `network_id` | `sha256("Public Global Stellar Network ; September 2015")` — 32 bytes, fixed for pubnet | | `ENVELOPE_TYPE` | XDR-encoded `EnvelopeType` enum (4 bytes, big-endian): `2` (`ENVELOPE_TYPE_TX`) for normal txs, `5` (`ENVELOPE_TYPE_TX_FEE_BUMP`) for fee-bump txs | | `tx_payload_xdr` | XDR-encoded **`Transaction`** struct (the inner body — NOT the full envelope with its signatures) | Adding, removing, or reordering signatures does NOT change the hash. Reference computation: ```typescript theme={null} import { TransactionBuilder, Networks } from "@stellar/stellar-sdk"; const { signedXDR } = await session.request({ topic, chainId: "stellar:pubnet", request: { method: "stellar_signXDR", params: { xdr, chain, account } }, }); const tx = TransactionBuilder.fromXDR(signedXDR, Networks.PUBLIC); const txHash = tx.hash().toString("hex"); // 64-char hex ``` **Inner hash vs. fee-bump hash.** When `stellar_signXDR` is used inside a fee-abstraction flow (the wallet signs an inner tx, a relayer wraps it in a `FeeBumpTransaction` before submitting), the hash the dApp computes from the inner tx (`H_inner`) is **not** the hash that lands on-chain (`H_fb`). Horizon resolves **either hash** to the same transaction record — a `GET /transactions/{hash}` request works whether you pass `H_inner` or `H_fb`, and both are exposed on the returned fee-bump record. For stable UX, prefer `H_fb` for explorer links once submission is confirmed; `H_inner` works as an immediate optimistic identifier between sign-time and submission. Note that Horizon will **404** on an `H_inner` lookup until the wrapping fee-bump transaction has been submitted and included in a ledger. ## Additional Resources * [CAIP-2](https://chainagnostic.org/CAIPs/caip-2) and [CAIP-10](https://chainagnostic.org/CAIPs/caip-10) — chain and account identifiers. * [Stellar CAIP-2 namespace draft](https://github.com/ChainAgnostic/namespaces/blob/main/stellar/caip2.md). * [Stellar SEP-7](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0007.md), [SEP-10](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md), [SEP-53](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0053.md). * [Freighter — WalletConnect (mobile)](https://docs.freighter.app/mobile-walletconnect/mobile). * [Stellar XDR reference](https://developers.stellar.org/docs/encyclopedia/xdr). * [Fee-bump transactions](https://developers.stellar.org/docs/learn/encyclopedia/transactions-specialized/fee-bump-transactions). # Sui Source: https://docs.walletconnect.com/wallets/chains/sui Overview of the Sui JSON-RPC methods supported by Wallet SDK. These are the methods that wallets should implement to handle Sui transactions and messages via WalletConnect. The SUI RPC standard is still under review and specifications may change. Implementation details and method signatures are subject to updates. ## sui\_getAccounts This method returns an Array of public keys and addresses available to sign from the wallet. ### Parameters none ### Returns `Array` - Array of accounts: * `Object` : * `pubkey` : `String` - public key for keypair * `address` : `String` - the Sui address ### Example ```typescript theme={null} // Request { "id": 1, "jsonrpc": "2.0", "method": "sui_getAccounts", "params": {} } // Result { "id": 1, "jsonrpc": "2.0", "result": [{ "pubkey": "AC68P56WCCTF0nUEX31/V5b1wqiD1pvfc8Fql8dPIPDA", "address":"0x3cd077f41680eebca0176baad3915b2ea26dbbdfd10161865234732bb1f2ac50" }] } ``` ### Session Properties In a connection request, it is recommended to serialize the response to `getAccounts` in `session.sessionProperties.sui_getAccounts`. This allows dapps to consume an active session without requiring a context switch to re-request all addresses and associated public keys from the wallet. ## sui\_signTransaction Sign a Sui transaction without executing it. #### Parameters 1. `transaction` (object) - The transaction to sign: * `transaction` (string) - The base64 encoded, BCS encoded, transaction data * `address` (string) - The sender's Sui address #### Returns `object` - The signed transaction: * `signature` (string) - The base64 encoded signature * `transactionBytes` (string) - The base64 encoded signed transaction bytes #### Example ```javascript theme={null} // Request { "jsonrpc": "2.0", "id": 1, "method": "sui_signTransaction", "params": { "transaction": "AAACAAhkAAAAAAAAAAAgcfGPMPqXhXLvgkjSYSgtJoBBfJN4xPm3bwZGapDhVIICAgABAQAAAQEDAAAAAAEBAHHxjzD6l4Vy74JI0mEoLSaAQXyTeMT5t28GRmqQ4VSCAq3fqx8mNL6p13BcS9bG74Gbh1dowEtQ", "address": "0xd5f647edb77d4fda31d0304506447fb3c92e55aaf77bc5ed4b77c332dd4605fa" } } // Response { "jsonrpc": "2.0", "result": { "signature": "ACRvdr3yI2mdpeOK+NsJIimdNGcE9R//jjT3HALZ17fFyu818op4jZi/64lPBjpKMDX6ZtxnCFZExTOFdpi3MwEZXLv/ORduxMYX0fw8dbHlnWC8WG0ymrlAmARpEibbhw==", "transactionBytes": "AAACAAhkAAAAAAAAAAAg1fZH7bd9T9ox0DBFBkR/s8kuVar3e8XtS3fDMt1GBfoCAgABAQAAAQEDAAAAAAEBANX2R+23fU/aMdAwRQZEf7PJLlWq93vF7Ut3wzLdRgX6At/pRJzj2VpZgqXpSvEtd3GzPvt99hR8e/yOCGz/8nbRmA7QFAAAAAAgBy5vStJizn76LmJTBlDiONdR/2rSuzzS4L+Tp/Zs4hZ8cBxYkcSlxBD6QXvgS11E6d+DNek8LiA/beba6iH3l5gO0BQAAAAAIMpdmZjiqJ5GG9di1MAgD4S3uRr2gaMC7S1WsaeBwNIx1fZH7bd9T9ox0DBFBkR/s8kuVar3e8XtS3fDMt1GBfroAwAAAAAAAECrPAAAAAAAAA==" }, "id": 1 } ``` ### sui\_signAndExecuteTransaction Sign and execute a Sui transaction. #### Parameters 1. `transaction` (object) - The transaction to sign and execute: * `transaction` (string) - The base64 encoded, BCS encoded, transaction data * `address` (string) - The sender's Sui address #### Returns `object` - The transaction result: * `digest` (string) - The transaction digest that can be used to look up the transaction in the explorer #### Example ```javascript theme={null} // Request { "jsonrpc": "2.0", "id": 1, "method": "sui_signAndExecuteTransaction", "params": { "transaction": "AAACAAhkAAAAAAAAAAAgcfGPMPqXhXLvgkjSYSgtJoBBfJN4xPm3bwZGapDhVIICAgABAQAAAQEDAAAAAAEBAHHxjzD6l4Vy74JI0mEoLSaAQXyTeMT5t28GRmqQ4VSCAq3fqx8mNL6p13BcS9bG74Gbh1dowEtQ", "address": "0xd5f647edb77d4fda31d0304506447fb3c92e55aaf77bc5ed4b77c332dd4605fa" } } // Response { "jsonrpc": "2.0", "result": { "digest": "GBqPRFR9sYfWA8rt2wCkcgZrctyYMj8Ufunxkjg5G8zt" }, "id": 1 } ``` ### sui\_signPersonalMessage Sign a personal message. #### Parameters 1. `message` (object) - The message to sign: * `message` (string) - The message to sign (plain text) * `address` (string) - The account address to sign with #### Returns `object` - The signed message: * `signature` (string) - The base64 encoded signature #### Example ```javascript theme={null} // Request { "jsonrpc": "2.0", "id": 1, "method": "sui_signPersonalMessage", "params": { "message": "This is a message to be signed for SUI", "address": "0xd5f647edb77d4fda31d0304506447fb3c92e55aaf77bc5ed4b77c332dd4605fa" } } // Response { "jsonrpc": "2.0", "result": { "signature": "APsZ7PvuAynXYxxfeo0Py4DWOnrUpwqHhJJ1F8aGB2nmS5Wv9dvVo8Gr7DKaXwPMqFaFNKsHb0Hej07R0L0NpQsZXLv/ORduxMYX0fw8dbHlnWC8WG0ymrlAmARpEibbhw==" }, "id": 1 } ``` ## Additional Resources For more information about Sui RPC methods and implementation details, please refer to the [official Sui documentation](https://docs.sui.io/sui-api-ref). # TON Source: https://docs.walletconnect.com/wallets/chains/ton Overview of the TON JSON-RPC methods supported by Wallet SDK. ## Network / Chain Information | CAIP-2 | Chain ID | Name | RPC Endpoint | Namespace | | ---------- | -------- | ----------- | ---------------------------------------------- | --------- | | `ton:-239` | `-239` | TON Mainnet | `https://toncenter.com/api/v2/jsonRPC` | `ton` | | `ton:-3` | `-3` | TON Testnet | `https://testnet.toncenter.com/api/v2/jsonRPC` | `ton` | ## RPC Methods Wallets must support the following JSON-RPC methods over WalletConnect sessions. No events are required. ## ton\_sendMessage Submit one or more transaction messages to the TON network. ### Request ```typescript theme={null} theme={null} interface TonSendMessageRequest { method: 'ton_sendMessage'; params: TonSendTransactionParams[]; } interface TonSendTransactionParams { valid_until?: number; // optional UNIX timestamp from?: string; // optional sender address (TEP-123 format) messages: TonTransactionMessage[]; } interface TonTransactionMessage { address: string; // recipient in TEP-123 format amount: number | string; // value in nanotons payload?: string; // optional base64 BoC stateInit?: string; // optional base64 BoC } ``` ### Example Request ```json theme={null} theme={null} { "id": 123, "jsonrpc": "2.0", "params": { "chainId": "ton:-239", "request": { "method": "ton_sendMessage", "params": [ { "valid_until": 1658253458, "from": "EQDmnxDMhId6v1Ofg_h5KR5coWlFG6e86Ro3pc7Tq4CA0-Jn", "messages": [ { "address": "EQBBJBB3HagsujBqVfqeDUPJ0kXjgTPLWPFFffuNXNiJL0aA", "amount": "20000000", "stateInit": "base64boc..." }, { "address": "EQDmnxDMhId6v1Ofg_h5KR5coWlFG6e86Ro3pc7Tq4CA0-Jn", "amount": "60000000", "payload": "base64boc..." } ] } ] } } } ``` ### Success Response ```json theme={null} theme={null} { "jsonrpc": "2.0", "id": 123, "result": "base64bocEncodedTransaction" } ``` ### Error Response ```json theme={null} theme={null} { "jsonrpc": "2.0", "id": 123, "error": { "code": , "message": "" } } ``` ## ton\_signData Sign an off-chain payload (text, binary, or cell) for authentication or verification by dApps. ### Request ```typescript theme={null} theme={null} interface TonSignDataRequest { method: 'ton_signData'; params: TonSignDataParams[]; } type TonSignDataParams = | { type: 'text'; text: string; from?: string } | { type: 'binary'; bytes: string; from?: string } | { type: 'cell'; schema: string; cell: string; from?: string }; ``` ### Example Request ```json theme={null} theme={null} { "id": 123, "jsonrpc": "2.0", "params": { "chainId": "ton:-239", "request": { "method": "ton_signData", "params": [ { "type": "text", "text": "Confirm new 2FA number:\\n+1 234 567 8901", "from": "EQDmnxDMhId6v1Ofg_h5KR5coWlFG6e86Ro3pc7Tq4CA0-Jn" } ] } } } ``` ### Success Response ```json theme={null} theme={null} { "jsonrpc": "2.0", "id": 123, "result": { "signature": "base64_signature", "address": "raw_wallet_address", "timestamp": 1658253458, "domain": "yourapp.com", "payload": { "type": "text", "text": "Confirm new 2FA number:\\n+1 234 567 8901" } } } ``` ### Error Response ```json theme={null} theme={null} { "jsonrpc": "2.0", "id": 123, "error": { "code": , "message": "" } } ``` ## Session Properties Wallets must include `ton_getPublicKey` and `ton_getStateInit` in the session properties when approving a session. This is mandatory for TON Connect compatibility. When approving a session, wallets must serialize the following properties into `session.sessionProperties`: * `ton_getPublicKey`: The Ed25519 public key of the wallet (hex-encoded) * `ton_getStateInit`: The StateInit of the wallet contract (base64-encoded BoC) These properties are essential for TON Connect support because: * The public key is required for signature verification * The StateInit is needed to compute and verify the wallet address, as TON addresses are derived from the contract code and initial data ### Example Session Approval ```typescript theme={null} // When approving a session, include the TON session properties const session = await walletKit.approveSession({ id: proposal.id, namespaces: approvedNamespaces, sessionProperties: { ton_getPublicKey: "a1b2c3d4e5f6...", // hex-encoded Ed25519 public key ton_getStateInit: "te6cckEBAQEA..." // base64-encoded StateInit BoC } }); ``` This allows dApps to consume an active session without requiring additional requests to retrieve the wallet's public key and state initialization data. ## Notes & Considerations * If `from` is omitted, the wallet should prompt the user to select an address. * All requests and responses must comply with JSON-RPC structure (`id`, `jsonrpc`, etc.). * Signature verification can be done using `ed25519.verify` on the original bytes. * `stateInit` support is needed when your wallet supports contract deployment flows. * The `domain` field in responses indicates the originating application (dApp) domain. # Tron Source: https://docs.walletconnect.com/wallets/chains/tron Tron JSON-RPC Methods These are the methods that wallets should implement to handle Tron transactions and messages via WalletConnect. ## Network / Chain Information | CAIP-2 | Chain ID | Name | RPC Endpoint | Namespace | | ----------------- | ------------ | ------------ | -------------------------------- | --------- | | `tron:0x2b6653dc` | `0x2b6653dc` | Tron Mainnet | `https://api.trongrid.io` | `tron` | | `tron:0xcd8690dc` | `0xcd8690dc` | Tron Shasta | `https://api.shasta.trongrid.io` | `tron` | | `tron:0x94a9059e` | `0x94a9059e` | Tron Nile | `https://nile.trongrid.io` | `tron` | ## Session Properties To enable the new simplified transaction structure, wallets should include `tron_method_version: "v1"` in their `sessionProperties` during the connection handshake: ```json theme={null} { "sessionProperties": { "tron_method_version": "v1" } } ``` When `tron_method_version` is set to `"v1"`, the transaction structure is simplified to remove the nested `transaction.transaction` format. If not set, the legacy nested format is used for backward compatibility. ### tron\_signTransaction Sign a Tron transaction without executing it. #### Parameters * The transaction to sign: * `address` (string) - The sender's Tron address * `transaction` (object) - The transaction object to sign #### Returns * The signed transaction: * `txID` (string) - The transaction ID (deterministically derived from raw transaction) * `signature` (array) - Array of signature strings * `raw_data` (object) - The raw transaction data * `raw_data_hex` (string) - The hex-encoded raw transaction data * `visible` (boolean) - Whether addresses are in visible format #### Example (New Format with tron\_method\_version: "v1") Request with the simplified format: ```json theme={null} { "request": { "method": "tron_signTransaction", "params": { "address": "TKZRPqoV7WLFvjhT4cEyBLv27Rvv1RNWGj", "transaction": { "visible": false, "txID": "539f218871fdd87e94eb03a0dd617107ba722005f37a5ddb82cb65aa4f3b73b0", "raw_data": { "contract": [ { "parameter": { "value": { "data": "095ea7b300000000000000000000000069319ea845b1c35a1f7b0e1429f4f303e8f791330000000000000000000000000000000000000000000000000000000000000000", "owner_address": "4169319ea845b1c35a1f7b0e1429f4f303e8f79133", "contract_address": "41eca9bc828a3005b9a3b909f2cc5c2a54794de05f" }, "type_url": "type.googleapis.com/protocol.TriggerSmartContract" }, "type": "TriggerSmartContract" } ], "ref_block_bytes": "7803", "ref_block_hash": "16138f9255a1db91", "expiration": 1756201572000, "fee_limit": 200000000, "timestamp": 1756201512720 }, "raw_data_hex": "0a027803220816138f9255a1db9140a0ad95ae8e335aae01081f12a9010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412740a154169319ea845b1c35a1f7b0e1429f4f303e8f79133121541eca9bc828a3005b9a3b909f2cc5c2a54794de05f2244095ea7b300000000000000000000000069319ea845b1c35a1f7b0e1429f4f303e8f79133000000000000000000000000000000000000000000000000000000000000000007090de91ae8e3390018084af5f" } }, "expiryTimestamp": 1756201811 }, "chainId": "tron:0xcd8690dc" } ``` * Response: ```json theme={null} { "visible": false, "txID": "539f218871fdd87e94eb03a0dd617107ba722005f37a5ddb82cb65aa4f3b73b0", "raw_data": { "contract": [ { "parameter": { "value": { "data": "095ea7b300000000000000000000000069319ea845b1c35a1f7b0e1429f4f303e8f791330000000000000000000000000000000000000000000000000000000000000000", "owner_address": "4169319ea845b1c35a1f7b0e1429f4f303e8f79133", "contract_address": "41eca9bc828a3005b9a3b909f2cc5c2a54794de05f" }, "type_url": "type.googleapis.com/protocol.TriggerSmartContract" }, "type": "TriggerSmartContract" } ], "ref_block_bytes": "7803", "ref_block_hash": "16138f9255a1db91", "expiration": 1756201572000, "fee_limit": 200000000, "timestamp": 1756201512720 }, "raw_data_hex": "0a027803220816138f9255a1db9140a0ad95ae8e335aae01081f12a9010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412740a154169319ea845b1c35a1f7b0e1429f4f303e8f79133121541eca9bc828a3005b9a3b909f2cc5c2a54794de05f2244095ea7b300000000000000000000000069319ea845b1c35a1f7b0e1429f4f303e8f79133000000000000000000000000000000000000000000000000000000000000000007090de91ae8e3390018084af5f", "signature": [ "1c2dd921c15fd83ca1dec4fd999b801f08c8bb073702f4bfafa4132a6e129421ed6267ec81c7dd2e4ef04ce077b101186ec2cda86d69f9f44255c216398cc9c601" ] } ``` ### tron\_signMessage Sign a personal message. #### Parameters The message to sign: * `message` (string) - The message to sign (plain text) * `address` (string) - The account address to sign with #### Returns The signed message: * `signature` (string) - The signature string #### Example * Request: ```json theme={null} { "request": { "method": "tron_signMessage", "params": { "address": "TXUEmLr...", "message": "This is a message to be signed for Tron" }, "expiryTimestamp": 1758269816 }, "chainId": "tron:0xcd8690dc" } ``` * dApp result (what client.request(...) resolves to): ```json theme={null} { "signature": "0x1ec623ee6e4716f5a116d0a2755b158ac05dfbc3e9118cca..." } ``` The methods below are not part of the required wallet surface in the Reown official Tron Wallet example. dApps may perform these directly against a Tron node or gateway. Wallets may implement them for convenience, but they're not required. ### tron\_sendTransaction (optional) Broadcast a signed transaction to the Tron network. #### Parameters The signed transaction object: * `txID` (string) - The transaction ID * `signature` (array) - Array of signature strings * `raw_data` (object) - The raw transaction data * `raw_data_hex` (string) - The hex-encoded raw transaction data #### Returns The transaction result: * `result` (boolean) - Whether the transaction was successfully broadcast * `txid` (string) - The transaction ID that can be used to look up the transaction #### Example * Request: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "tron_sendTransaction", "params": { "signedTransaction": { "txID": "66e79c6993f29b02725da54ab146ffb0453ee6a43b4083568ad9585da305374a", "signature": [ "7e760cef94bc82a7533bc1e8d4ab88508c6e13224cd50cc8da62d3f4d4e19b99514f..." ], "raw_data_hex": "0a02885b2208baa1c278fd0a309f4090c1dbe5e7325aae01081f12a9010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412740a15411cb0b7348eded93b8d0816bbeb819fc1d7a51f31121541a614f803b6fd780986a42c78ec9c7f77e6ded13c2244095ea7b30000000000000000000000001cb0b7348eded93b8d0816bbeb819fc1d7a51f3100000000000000000000000000000000000000000000000000000000000000007082f4d7e5e73290018084af5f" } } } ``` * Response: ```json theme={null} { "jsonrpc": "2.0", "result": { "result": true, "txid": "66e79c6993f29b02725da54ab146ffb0453ee6a43b4083568ad9585da305374a" }, "id": 1 } ``` ### tron\_getBalance (optional) Get the TRX balance of a Tron address. #### Parameters 1. `address` (string) - The Tron address to query #### Returns `number` - The balance in SUN (1 TRX = 1,000,000 SUN) #### Example * Request: ```json theme={null} { "jsonrpc": "2.0", "id": 1, "method": "tron_getBalance", "params": { "address": "TLyqzVGLV1srkB7dToTAEqgDSfPtXRJZYH" } } ``` * Response: ```json theme={null} { "jsonrpc": "2.0", "result": 1000000000, "id": 1 } ``` ## Additional Resources For more information about Tron RPC methods and implementation details, please refer to the [official Tron documentation](https://developers.tron.network/). # How to Control Which Apps Can Connect to Your Users’ Wallets Source: https://docs.walletconnect.com/wallets/custodians/app-access-control As a wallet provider or a custodian, you may want to restrict access to certain dapps to maintain control over which applications can connect to your users’ wallets. This can be useful for a variety of reasons, such as to prevent users from using certain apps that are not compliant with your policies or to simply block certain apps from connecting to your users' wallets. Using the Wallet SDK, you can block apps from connecting to your users' wallets by rejecting session requests from certain apps. ## Prerequisites * Please ensure you have integrated Wallet SDK into your wallet. * Please ensure that you have obtained and configured the project ID from the [WalletConnect Dashboard](https://dashboard.walletconnect.com). ## Maintaining a Blocklist of Apps Wallet SDK allows you to identify malicious apps using [Verify API](/wallets/features/verify). However, as a wallet, you will need to build your own logic for the UI and UX of blocking certain apps. If there are specific apps that you want to block (not flagged as malicious by Verify API), you will need to maintain a blocklist of apps by storing the app's metadata in a database or a file. ## Inspecting Session Requests When receiving `onSessionProposal` events, check the dapp's metadata (name, URL, description) from `proposal.proposer.metadata`. After this, you can reject unwanted connections by calling `rejectSession()` for apps you want to block. For example: ```javascript theme={null} walletKit.on('session_proposal', (event) => { const dappUrl = event.params.proposer.metadata.url; // Your blocklist logic if (isBlocked(dappUrl)) { walletKit.rejectSession({ id: event.id, reason: getSdkError('USER_REJECTED') }); return; } // Otherwise show approval UI }); ``` ## Conclusion By following the steps above, you can block apps from connecting to your users' wallets by rejecting session requests from certain apps. # How to Control Which Smart Contracts Your Users Can Interact With Source: https://docs.walletconnect.com/wallets/custodians/contract-access-control As a wallet provider or custodian, you may want to limit which smart contracts your users can interact with to maintain tighter control over onchain activity. This can be useful for enforcing compliance requirements, reducing exposure to malicious or unverified contracts, or simply restricting access to certain protocols that don’t align with your policies. Using the Wallet SDK, you can inspect and filter contract interaction requests to block or approve transactions based on your own criteria, such as contract addresses, function signatures, or network-specific rules. ## Prerequisites * Please ensure you have integrated Wallet SDK into your wallet. * Please ensure that you have obtained and configured the project ID from the [WalletConnect Dashboard](https://dashboard.walletconnect.com). ## Managing Smart Contract Access Wallet SDK does not provide a built-in way to create and manage smart contract allowlists for access control. However, you can use the Wallet SDK to inspect the `session_proposal` and `session_request` payloads and review it to approve or reject the proposal before a session is established and/or a transaction is signed respectively. ### Inspecting Session Proposals When a Web3 app is trying to establish a session or connect to your wallet, it will send a `session_proposal` payload to your wallet as shown below. After this, as a wallet, you can do the following: 1. Check `verifyContext.origin` and `validation` to confirm the dapp is trusted. 2. Approve or reject the proposal before the session is created. ```json theme={null} { "id": 1685471520923476, "topic": "proposal_topic", "params": { "requiredNamespaces": { "eip155": { "chains": ["eip155:1"], "methods": ["eth_sendTransaction", "personal_sign"], "events": ["chainChanged", "accountsChanged"] } }, "proposer": { "metadata": { "name": "Aave", "description": "Aave App", "url": "https://app.aave.com", "icons": ["https://aave.com/icon.png"] } }, "verifyContext": { "origin": "https://app.aave.com", "validation": "VALID", "verifyUrl": "https://verify.walletconnect.com/record/abc123" } } } ``` ### Inspecting Session Requests After a session is approved, Web3 apps may request to sign a transaction or a message. As a wallet, you will receive a JSON-RPC request from the Web3 app as shown below. Inside the request payload, you will find the contract address (`to: 0xContractAddress`) that is being interacted with and the function that is being called. ```json theme={null} { "id": 1685471630000123, "topic": "session_topic", "params": { "chainId": "eip155:1", "request": { "method": "eth_sendTransaction", "params": [ { "from": "0xCustodianSubAccount", "to": "0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32", "data": "0xa9059cbb0000000000000000000000000F1b5...", "value": "0x0" } ] }, "verifyContext": { "origin": "https://app.aave.com", "validation": "VALID" } } } ``` ### Enforcing Smart Contract Allowlists As a wallet or custodian, you would need to code your own logic to enforce the smart contract allowlists. Please refer to the example implementation below that works for all EVM chains. ```javascript theme={null} const ALLOWED_CONTRACTS = { "eip155:1": [ "0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32", // Lido "0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b" // OpenSea ] }; function handleRequest(payload) { const chain = payload.params.chainId; const request = payload.params.request; if (request.method === "eth_sendTransaction") { const tx = request.params[0]; if (!tx.to) { throw new Error("Contract creation transactions are not allowed."); } const contract = tx.to.toLowerCase(); const allowed = (ALLOWED_CONTRACTS[chain] || []).map(a => a.toLowerCase()); if (!allowed.includes(contract)) { throw new Error(`Blocked transaction to unapproved contract: ${contract}`); } else { // Forward to signing flow signAndBroadcast(tx); } } } // Example placeholder for your signing logic function signAndBroadcast(tx) { console.log("Signing and broadcasting transaction:", tx); } ``` ## Conclusion By following the steps above, you can block your users from interacting with certain smart contracts from certain apps. # Extended WalletConnect Sessions Request Source: https://docs.walletconnect.com/wallets/custodians/extended-sessions This guide will walk you through how a wallet developer can customize the session request expiry in WalletConnect using the **`expiry`** parameter. ## Extended Session Expiry - what does it mean? When an app sends a request through WalletConnect (for example, a signature request), it stays “active” until the expiry time is reached. If the wallet does not respond before the expiry, the request automatically fails with a timeout. By default, the expiry is short, i.e., 5 minutes. Extending the session expiry time allows the wallet and app to keep the request open longer, **up to 7 days**. This is useful for cases like off-hours approvals, delayed custody flows, or multi-party signing. ### What do wallets need to do? A wallet must: * Maintain pending state until expiry or completion. * Gracefully discard expired requests. * Verify user intent remains valid after long delays. ### Limits * **Minimum:** 300 seconds (5 min) * **Maximum:** 604,800 seconds (7 days) ## How can I extend the session request expiry as a wallet? Wallets must correctly interpret and enforce the expiry. * Parse `expiry` in seconds from incoming request metadata. * Keep pending requests active until they’re resolved or the expiry time elapses. * Notify the user of pending and expired requests. * If the expiry has passed, return an error response (`code: 4100`, “Request expired”). * Optional UX: display countdown timers or “expires in X hours”. Please refer to the [Best Practices](/wallets/more/best-practices#session-request-expiry) section to learn how you can implement this in your code. # WalletConnect for Custodians and Institutions Source: https://docs.walletconnect.com/wallets/custodians/overview **WalletConnect** enables custodians and institutions to offer curated, policy-enforced access to decentralized finance (DeFi) through a secure, modular SDK. Integrating WalletConnect and the Wallet SDK provides **institutional-grade control** while maintaining **interoperability** across thousands of dapps. Custodians can enforce granular permissions, from domain and contract verification to policy-based transaction controls, all while retaining full custody of client assets. ## Why WalletConnect? WalletConnect provides the **largest and most established gateway to DeFi**, designed for scale and institutional reliability. ### \$400 Billion Total Network Volume Total Network Volume (TNV) is the total value of all transactions routed through the WalletConnect network in a given time (annually, in this case). So this represents how much money actually flows through the WalletConnect. WalletConnect has long been the quiet backbone of Web3 and not "just a QR code". It’s the invisible glue that connects users, dApps, and wallets, and now the scale finally shows it. ### Fully Chain-Agnostic WalletConnect supports 300+ EVM chains, Bitcoin, Solana, and 70+ other networks. Any network with a CAIP-25 namespace is supported. ### Available on 70,000+ dApps WalletConnect is available on 70,000+ dApps, making it the most widely used wallet connection protocol in the world. ### Available on 500+ wallets WalletConnect is available on 500+ wallets, making it the most robust and user-friendly. You can find the list of wallets [here](https://walletguide.walletconnect.network/). # Link Mode Source: https://docs.walletconnect.com/wallets/features/link-mode WalletKit Link Mode is a low latency mechanism for transporting One-Click Auth requests and session requests over Universal Links, reducing the need for a WebSocket connection with the Relay. This significantly enhances the user experience when connecting native dApps to native wallets by reducing the latency associated with network connections, especially when the user has an unstable internet connection.