# Installation
Source: https://docs.walletconnect.network/app-sdk/javascript/installation
WalletConnect App SDK is chain agnostic and provides seamless integration with several blockchain ecosystems. WalletConnect App SDK when combined with Universal Provider library enables compatibility across any blockchain protocol.
## Pre-requisites
This section is to inform you about the pre-requisites for integrating WalletConnect as an App with JavaScript (vanilla JS).
### Cloud Configuration
Create a new project on WalletConnect Dashboard at [https://dashboard.walletconnect.com](https://dashboard.walletconnect.com) and obtain a new project ID. You will need this project ID to initialize WalletConnect in your project (app).
**Don't have a project ID?**
Head over to WalletConnect Dashboard and create a new project now!
### 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) where your project ID is used. You can manage your allowlist in the [WalletConnect Dashboard](https://dashboard.walletconnect.com) under the project settings.
The allowlist supports a list of origins in the format `[scheme://]`. Using `localhost` (or `127.0.0.1`) is always permitted, and if the allowlist is empty, all origins are allowed. Updates take 15 minutes to apply.
Examples of possible origins in the allowlist:
* `example.com` - allows `https://example.com` or `http://example.com` but not `https://www.example.com`
* `https://example.com` - allows `https://example.com` but not `http://example.com`
* `https://*.example.com` - allows `https://www.example.com` but not `https://example.com`
Requests from origins not in the allowlist will be denied. Make sure to add all domains where your app will be deployed.
## Installation
```bash npm theme={null}
npm install @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
```bash Yarn theme={null}
yarn add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
```bash Bun theme={null}
bun add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
```bash pnpm theme={null}
pnpm add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
## Implementation
Here is a sample implementation of WalletConnect App SDK with JavaScript. You can also check the example repository below.
Check the WalletConnect App SDK JavaScript example
For a quick integration of WalletConnect App SDK you can use the `UniversalConnector` class. Which simplifies the integration of WalletConnect App SDK by providing a single interface for all the blockchain protocols.
You can configure the Universal Connector with the networks you want to support.
For more information, please visit [RPC Reference](https://docs.reown.com/advanced/multichain/rpc-reference/cosmos-rpc) section from our docs.
We recommend creating a config file to establish a singleton instance for the Universal Connector:
```tsx Generic Example theme={null}
import { UniversalConnector } from '@reown/appkit-universal-connector'
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
// you can configure your own network
const suiMainnet = {
id: 784,
chainNamespace: 'sui',
caipNetworkId: 'sui:mainnet',
name: 'Sui',
nativeCurrency: { name: 'SUI', symbol: 'SUI', decimals: 9 },
rpcUrls: { default: { http: ['https://fullnode.mainnet.sui.io:443'] } }
}
export const networks = [suiMainnet]
export let universalConnector
export async function getUniversalConnector() {
if (!universalConnector) {
universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['sui_signPersonalMessage'],
chains: [suiMainnet],
events: [],
namespace: 'sui'
}
]
})
}
return universalConnector
}
```
```tsx Stacks Example theme={null}
import { UniversalConnector } from '@reown/appkit-universal-connector'
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
// you can configure your own network
const stacksMainnet = {
id: 'stacks-mainnet',
chainNamespace: 'stacks',
caipNetworkId: 'stacks:1',
name: 'Stacks Mainnet',
nativeCurrency: { name: 'STX', symbol: 'STX', decimals: 6 },
rpcUrls: { default: { http: ['https://stacks-node-api.mainnet.stacks.co'] } } // Example Stacks Mainnet RPC URL
}
export const networks = [stacksMainnet]
export let universalConnector
export async function getUniversalConnector() {
if (!universalConnector) {
universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['stx_signMessage', 'stx_signTransaction', 'stx_getAccounts', 'stx_getAddresses', 'stx_callContract', 'stx_deployContract', 'sendTransfer', 'getAddresses'],
chains: [stacksMainnet],
events: ['stx_chainChanged', 'stx_accountsChanged'],
namespace: 'stacks'
}
]
})
}
return universalConnector
}
```
```tsx TON Example theme={null}
import type { AppKitNetwork } from '@reown/appkit/networks'
import type { CustomCaipNetwork } from '@reown/appkit-common'
import { UniversalConnector } from '@reown/appkit-universal-connector'
// Get projectId from https://dashboard.walletconnect.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
const tonMainnet: CustomCaipNetwork<'ton'> = {
id: -239,
chainNamespace: 'ton' as const,
caipNetworkId: 'ton:-239',
name: 'TON',
nativeCurrency: { name: 'TON', symbol: 'TON', decimals: 9 },
rpcUrls: { default: { http: ['https://toncenter.com/api/v2/jsonRPC'] } }
}
export const networks = [tonMainnet] as [AppKitNetwork, ...AppKitNetwork[]]
export async function getUniversalConnector() {
const universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['ton_signData'],
chains: [tonMainnet as CustomCaipNetwork],
events: [],
namespace: 'ton'
}
]
})
return universalConnector
}
```
```tsx TRON Example theme={null}
import { UniversalConnector } from '@reown/appkit-universal-connector'
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
const tronMainnet = {
id: '0x2b6653dc',
chainNamespace: 'tron',
caipNetworkId: 'tron:0x2b6653dc',
name: 'Tron Mainnet',
nativeCurrency: { name: 'TRX', symbol: 'TRX', decimals: 6 },
rpcUrls: { default: { http: ['https://api.trongrid.io'] } }
}
export const networks = [tronMainnet]
export let universalConnector
export async function getUniversalConnector() {
if (!universalConnector) {
universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['tron_signTransaction', 'tron_signMessage'],
chains: [tronMainnet],
events: [],
namespace: 'tron'
}
]
})
}
return universalConnector
}
```
In the main.js file you can add:
```tsx theme={null}
import { getUniversalConnector } from './config/appKit.js'
async function setup() {
const universalConnector = await getUniversalConnector()
// check if session is already connected
if (universalConnector?.provider.session) {
session = universalConnector?.provider.session
}
}
setup()
```
## Trigger the modal
To open the WalletConnect modal you need to call the `connect` function from the Universal Connector.
For more information, please visit [RPC Reference](https://docs.reown.com/advanced/multichain/rpc-reference/cosmos-rpc) section from our docs.
```tsx theme={null}
....
```
## Smart Contract Interaction
[Wagmi hooks](https://wagmi.sh/react/api/hooks/useReadContract) can help us interact with wallets and smart contracts:
```tsx theme={null}
import { useReadContract } from "wagmi";
import { USDTAbi } from "../abi/USDTAbi";
const USDTAddress = "0x...";
function App() {
const result = useReadContract({
abi: USDTAbi,
address: USDTAddress,
functionName: "totalSupply",
});
}
```
Read more about Wagmi hooks for smart contract interaction [here](https://wagmi.sh/react/hooks/useReadContract).
[Ethers](https://docs.ethers.org/v6/) can help us interact with wallets and smart contracts:
```tsx theme={null}
import { useAppKitProvider, useAppKitAccount } from "@reown/appkit/react";
import { BrowserProvider, Contract, formatUnits } from "ethers";
const USDTAddress = "0x617f3112bf5397D0467D315cC709EF968D9ba546";
// The ERC-20 Contract ABI, which is a common contract interface
// for tokens (this is the Human-Readable ABI format)
const USDTAbi = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function balanceOf(address) view returns (uint)",
"function transfer(address to, uint amount)",
"event Transfer(address indexed from, address indexed to, uint amount)",
];
function Components() {
const { address, isConnected } = useAppKitAccount();
const { walletProvider } = useAppKitProvider("eip155");
async function getBalance() {
if (!isConnected) throw Error("User disconnected");
const ethersProvider = new BrowserProvider(walletProvider);
const signer = await ethersProvider.getSigner();
// The Contract object
const USDTContract = new Contract(USDTAddress, USDTAbi, signer);
const USDTBalance = await USDTContract.balanceOf(address);
console.log(formatUnits(USDTBalance, 18));
}
return ;
}
```
[@Solana/web3.js](https://solana.com/docs/clients/javascript) library allows for seamless interaction with wallets and smart contracts on the Solana blockchain.
For a practical example of how it works, you can refer to our [lab dApp](https://lab.reown.com/appkit/?name=solana).
```tsx theme={null}
import {
SystemProgram,
PublicKey,
Keypair,
Transaction,
TransactionInstruction,
LAMPORTS_PER_SOL
} from '@solana/web3.js'
import { useAppKitAccount, useAppKitProvider } from '@reown/appkit/react'
import { useAppKitConnection, type Provider } from '@reown/appkit-adapter-solana/react'
function deserializeCounterAccount(data?: Buffer): { count: number } {
if (data?.byteLength !== 8) {
throw Error('Need exactly 8 bytes to deserialize counter')
}
return {
count: Number(data[0])
}
}
const { address } = useAppKitAccount()
const { connection } = useAppKitConnection()
const { walletProvider } = useAppKitProvider('solana')
async function onIncrementCounter() {
const PROGRAM_ID = new PublicKey('Cb5aXEgXptKqHHWLifvXu5BeAuVLjojQ5ypq6CfQj1hy')
const counterKeypair = Keypair.generate()
const counter = counterKeypair.publicKey
const balance = await connection.getBalance(walletProvider.publicKey)
if (balance < LAMPORTS_PER_SOL / 100) {
throw Error('Not enough SOL in wallet')
}
const COUNTER_ACCOUNT_SIZE = 8
const allocIx: TransactionInstruction = SystemProgram.createAccount({
fromPubkey: walletProvider.publicKey,
newAccountPubkey: counter,
lamports: await connection.getMinimumBalanceForRentExemption(COUNTER_ACCOUNT_SIZE),
space: COUNTER_ACCOUNT_SIZE,
programId: PROGRAM_ID
})
const incrementIx: TransactionInstruction = new TransactionInstruction({
programId: PROGRAM_ID,
keys: [
{
pubkey: counter,
isSigner: false,
isWritable: true
}
],
data: Buffer.from([0x0])
})
const tx = new Transaction().add(allocIx).add(incrementIx)
tx.feePayer = walletProvider.publicKey
tx.recentBlockhash = (await connection.getLatestBlockhash('confirmed')).blockhash
await walletProvider.signAndSendTransaction(tx, [counterKeypair])
const counterAccountInfo = await connection.getAccountInfo(counter, {
commitment: 'confirmed'
})
if (!counterAccountInfo) {
throw new Error('Expected counter account to have been created')
}
const counterAccount = deserializeCounterAccount(counterAccountInfo?.data)
if (counterAccount.count !== 1) {
throw new Error('Expected count to have been 1')
}
console.log(`[alloc+increment] count is: ${counterAccount.count}`);
}
```
# Installation
Source: https://docs.walletconnect.network/app-sdk/next/installation
WalletConnect App SDK is chain agnostic and provides seamless integration with several blockchain ecosystems. WalletConnect App SDK when combined with Universal Provider library enables compatibility across any blockchain protocol.
## Pre-requisites
This section is to inform you about the pre-requisites for integrating WalletConnect as an App with Next.js.
### Cloud Configuration
Create a new project on WalletConnect Dashboard at [https://dashboard.walletconnect.com](https://dashboard.walletconnect.com) and obtain a new project ID. You will need this project ID to initialize WalletConnect in your project (app).
**Don't have a project ID?**
Head over to WalletConnect Dashboard and create a new project now!
### 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) where your project ID is used. You can manage your allowlist in the [WalletConnect Dashboard](https://dashboard.walletconnect.com) under the project settings.
The allowlist supports a list of origins in the format `[scheme://]`. Using `localhost` (or `127.0.0.1`) is always permitted, and if the allowlist is empty, all origins are allowed. Updates take 15 minutes to apply.
Examples of possible origins in the allowlist:
* `example.com` - allows `https://example.com` or `http://example.com` but not `https://www.example.com`
* `https://example.com` - allows `https://example.com` but not `http://example.com`
* `https://*.example.com` - allows `https://www.example.com` but not `https://example.com`
Requests from origins not in the allowlist will be denied. Make sure to add all domains where your app will be deployed.
## Installation
```bash npm theme={null}
npm install @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
```bash Yarn theme={null}
yarn add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
```bash Bun theme={null}
bun add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
```bash pnpm theme={null}
pnpm add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
## Implementation
For a quick integration of WalletConnect App SDK you can use the `UniversalConnector` class. Which simplifies the integration of WalletConnect App SDK by providing a single interface for all the blockchain protocols.
You can configure the Universal Connector with the networks you want to support.
For more information, please visit [RPC Reference](https://docs.reown.com/advanced/multichain/rpc-reference/cosmos-rpc) section from our docs.
We recommend creating a config file to establish a singleton instance for the Universal Connector:
```tsx Generic Example theme={null}
import type { AppKitNetwork } from '@reown/appkit/networks'
import type { CustomCaipNetwork } from '@reown/appkit-common'
import { UniversalConnector } from '@reown/appkit-universal-connector'
// Get projectId from https://dashboard.walletconnect.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
// you can configure your own network
const suiMainnet: CustomCaipNetwork<'sui'> = {
id: 784,
chainNamespace: 'sui' as const,
caipNetworkId: 'sui:mainnet',
name: 'Sui',
nativeCurrency: { name: 'SUI', symbol: 'SUI', decimals: 9 },
rpcUrls: { default: { http: ['https://fullnode.mainnet.sui.io:443'] } }
}
export const networks = [suiMainnet] as [AppKitNetwork, ...AppKitNetwork[]]
export async function getUniversalConnector() {
const universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['sui_signPersonalMessage'],
chains: [suiMainnet as CustomCaipNetwork],
events: [],
namespace: 'sui'
}
]
})
return universalConnector
}
```
```tsx Stacks Example theme={null}
import type { AppKitNetwork } from '@reown/appkit/networks'
import type { InferredCaipNetwork } from '@reown/appkit-common'
import { UniversalConnector } from '@reown/appkit-universal-connector'
// Get projectId from https://dashboard.walletconnect.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
// you can configure your own network
const stacksMainnet: InferredCaipNetwork = {
id: 'stacks-mainnet',
chainNamespace: 'stacks' as const,
caipNetworkId: 'stacks:1',
name: 'Stacks Mainnet',
nativeCurrency: { name: 'STX', symbol: 'STX', decimals: 6 },
rpcUrls: { default: { http: ['https://stacks-node-api.mainnet.stacks.co'] } } // Example Stacks Mainnet RPC URL
}
export const networks = [stacksMainnet] as [AppKitNetwork, ...AppKitNetwork[]]
export async function getUniversalConnector() {
const universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['stx_signMessage', 'stx_signTransaction', 'stx_getAccounts', 'stx_getAddresses', 'stx_callContract', 'stx_deployContract', 'sendTransfer', 'getAddresses'],
chains: [stacksMainnet as InferredCaipNetwork],
events: ['stx_chainChanged', 'stx_accountsChanged'],
namespace: 'stacks'
}
]
})
return universalConnector
}
```
```tsx TON Example theme={null}
import type { AppKitNetwork } from '@reown/appkit/networks'
import type { CustomCaipNetwork } from '@reown/appkit-common'
import { UniversalConnector } from '@reown/appkit-universal-connector'
// Get projectId from https://dashboard.walletconnect.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
const tonMainnet: CustomCaipNetwork<'ton'> = {
id: -239,
chainNamespace: 'ton' as const,
caipNetworkId: 'ton:-239',
name: 'TON',
nativeCurrency: { name: 'TON', symbol: 'TON', decimals: 9 },
rpcUrls: { default: { http: ['https://toncenter.com/api/v2/jsonRPC'] } }
}
export const networks = [tonMainnet] as [AppKitNetwork, ...AppKitNetwork[]]
export async function getUniversalConnector() {
const universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['ton_signData'],
chains: [tonMainnet as CustomCaipNetwork],
events: [],
namespace: 'ton'
}
]
})
return universalConnector
}
```
```tsx TRON Example theme={null}
import type { AppKitNetwork } from '@reown/appkit/networks'
import type { CustomCaipNetwork } from '@reown/appkit-common'
import { UniversalConnector } from '@reown/appkit-universal-connector'
// Get projectId from https://dashboard.walletconnect.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
const tronMainnet: CustomCaipNetwork<'tron'> = {
id: '0x2b6653dc',
chainNamespace: 'tron' as const,
caipNetworkId: 'tron:0x2b6653dc',
name: 'Tron Mainnet',
nativeCurrency: { name: 'TRX', symbol: 'TRX', decimals: 6 },
rpcUrls: { default: { http: ['https://api.trongrid.io'] } }
}
export const networks = [tronMainnet] as [AppKitNetwork, ...AppKitNetwork[]]
export async function getUniversalConnector() {
const universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['tron_signTransaction', 'tron_signMessage'],
chains: [tronMainnet as CustomCaipNetwork],
events: [],
namespace: 'tron'
}
]
})
return universalConnector
}
```
In the App.tsx file you can add :
```tsx theme={null}
import { useState, useEffect } from 'react'
import { getUniversalConnector } from './config' // previous config file
import { UniversalConnector } from '@reown/appkit-universal-connector'
export function App() {
const [universalConnector, setUniversalConnector] = useState()
const [session, setSession] = useState()
// Initialize the Universal Connector on component mount
useEffect(() => {
getUniversalConnector().then(setUniversalConnector)
}, [])
// Set the session state in case it changes
useEffect(() => {
setSession(universalConnector?.provider.session)
}, [universalConnector?.provider.session])
```
## Trigger the modal
To open the WalletConnect modal you need to call the `connect` function from the Universal Connector.
```tsx theme={null}
// get the session from the universal connector
const handleConnect = async () => {
if (!universalConnector) {
return
}
const { session: providerSession } = await universalConnector.connect()
setSession(providerSession)
};
// disconnect the universal connector
const handleDisconnect = async () => {
if (!universalConnector) {
return
}
await universalConnector.disconnect()
setSession(null)
};
...
return (
(
)
```
## Smart Contract Interaction
[Wagmi hooks](https://wagmi.sh/react/api/hooks/useReadContract) can help us interact with wallets and smart contracts:
```tsx theme={null}
import { useReadContract } from "wagmi";
import { USDTAbi } from "../abi/USDTAbi";
const USDTAddress = "0x...";
function App() {
const result = useReadContract({
abi: USDTAbi,
address: USDTAddress,
functionName: "totalSupply",
});
}
```
Read more about Wagmi hooks for smart contract interaction [here](https://wagmi.sh/react/hooks/useReadContract).
[Ethers](https://docs.ethers.org/v6/) can help us interact with wallets and smart contracts:
```tsx theme={null}
import { useAppKitProvider, useAppKitAccount } from "@reown/appkit/react";
import { BrowserProvider, Contract, formatUnits } from "ethers";
const USDTAddress = "0x617f3112bf5397D0467D315cC709EF968D9ba546";
// The ERC-20 Contract ABI, which is a common contract interface
// for tokens (this is the Human-Readable ABI format)
const USDTAbi = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function balanceOf(address) view returns (uint)",
"function transfer(address to, uint amount)",
"event Transfer(address indexed from, address indexed to, uint amount)",
];
function Components() {
const { address, isConnected } = useAppKitAccount();
const { walletProvider } = useAppKitProvider("eip155");
async function getBalance() {
if (!isConnected) throw Error("User disconnected");
const ethersProvider = new BrowserProvider(walletProvider);
const signer = await ethersProvider.getSigner();
// The Contract object
const USDTContract = new Contract(USDTAddress, USDTAbi, signer);
const USDTBalance = await USDTContract.balanceOf(address);
console.log(formatUnits(USDTBalance, 18));
}
return ;
}
```
[@Solana/web3.js](https://solana.com/docs/clients/javascript) library allows for seamless interaction with wallets and smart contracts on the Solana blockchain.
For a practical example of how it works, you can refer to our [lab dApp](https://lab.reown.com/appkit/?name=solana).
```tsx theme={null}
import {
SystemProgram,
PublicKey,
Keypair,
Transaction,
TransactionInstruction,
LAMPORTS_PER_SOL
} from '@solana/web3.js'
import { useAppKitAccount, useAppKitProvider } from '@reown/appkit/react'
import { useAppKitConnection, type Provider } from '@reown/appkit-adapter-solana/react'
function deserializeCounterAccount(data?: Buffer): { count: number } {
if (data?.byteLength !== 8) {
throw Error('Need exactly 8 bytes to deserialize counter')
}
return {
count: Number(data[0])
}
}
const { address } = useAppKitAccount()
const { connection } = useAppKitConnection()
const { walletProvider } = useAppKitProvider('solana')
async function onIncrementCounter() {
const PROGRAM_ID = new PublicKey('Cb5aXEgXptKqHHWLifvXu5BeAuVLjojQ5ypq6CfQj1hy')
const counterKeypair = Keypair.generate()
const counter = counterKeypair.publicKey
const balance = await connection.getBalance(walletProvider.publicKey)
if (balance < LAMPORTS_PER_SOL / 100) {
throw Error('Not enough SOL in wallet')
}
const COUNTER_ACCOUNT_SIZE = 8
const allocIx: TransactionInstruction = SystemProgram.createAccount({
fromPubkey: walletProvider.publicKey,
newAccountPubkey: counter,
lamports: await connection.getMinimumBalanceForRentExemption(COUNTER_ACCOUNT_SIZE),
space: COUNTER_ACCOUNT_SIZE,
programId: PROGRAM_ID
})
const incrementIx: TransactionInstruction = new TransactionInstruction({
programId: PROGRAM_ID,
keys: [
{
pubkey: counter,
isSigner: false,
isWritable: true
}
],
data: Buffer.from([0x0])
})
const tx = new Transaction().add(allocIx).add(incrementIx)
tx.feePayer = walletProvider.publicKey
tx.recentBlockhash = (await connection.getLatestBlockhash('confirmed')).blockhash
await walletProvider.signAndSendTransaction(tx, [counterKeypair])
const counterAccountInfo = await connection.getAccountInfo(counter, {
commitment: 'confirmed'
})
if (!counterAccountInfo) {
throw new Error('Expected counter account to have been created')
}
const counterAccount = deserializeCounterAccount(counterAccountInfo?.data)
if (counterAccount.count !== 1) {
throw new Error('Expected count to have been 1')
}
console.log(`[alloc+increment] count is: ${counterAccount.count}`);
}
```
# WalletConnect for Apps
Source: https://docs.walletconnect.network/app-sdk/overview
## **Your Gateway to the WalletConnect Network**
**We highly recommend using one of our SDK partners to integrate WalletConnect into your app.**
The **WalletConnect App SDK** is the foundational gateway for apps to access the **WalletConnect Network,** the decentralized connectivity layer for the financial internet.
With a single integration, developers can connect to **700+ wallets** and **65,000+ apps** across **EVM, Solana, Bitcoin**, and **any network with a CAIP-25 namespace**, while preserving privacy, composability, and user choice.
**Built on open standards** and powered by a decentralized relay network, WalletConnect unlocks secure and cross-chain wallet connections for apps across devices and platforms.
Whether you’re building for DeFi, gaming, payments, or identity, WalletConnect provides the essential building block for **trusted wallet-to-app experiences**, all backed by the **infrastructure that powers the financial internet**.
## **Powering leading SDKs across Web3**
The WalletConnect App SDK is already embedded in some of the most widely-used SDKs in the ecosystem - powering everything from onboarding to payments.
## **Who is it for?**
* **App developers** who want to connect wallets quickly and securely
* **SDK builders** who want a powerful foundation for wallet connectivity
* **Web3 platforms** scaling across chains and wallets
If your product needs to talk to wallets, the App SDK is your starting point.
## What does WalletConnect have to offer for Apps?
At its core, the WalletConnect handles the most critical UX layer of any onchain app: **wallet connection**.
✅ **Prebuilt modal UX** for connecting wallets
✅ **Chain-agnostic support** across EVM, Solana, Bitcoin, and more
✅ **Built-in compatibility** with 500+ wallets
✅ **Native, embedded UI** - no iframes, no redirects
✅ **Customizable and composable** - use standalone or extend with your own flows
It’s the fastest way to build reliable wallet connectivity, with none of the versioning, RPC mismatches, or fragmented logic that plague homegrown solutions.
## Demo
***
## How to Integrate WalletConnect into your App
There are **two core pathways to integrate WalletConnect** into your app:
1. Via an SDK partner that has already integrated WalletConnect into their SDK.
2. Standalone integration of WalletConnect.
Below, you can find instructions and information for both pathways.
## Integrate WalletConnect via an SDK Partner
First, we need to cover what exactly is an SDK and how projects can use them to integrate WalletConnect into their app.
### What is an SDK?
An SDK is a software development kit that provides a set of tools, libraries, and documentation for developers to build applications. It is a collection of code, tools, and resources that help developers build applications faster and easier.
In this context, SDKs are pre-packaged developer tools that abstract away the underlying protocol, i.e. WalletConnect, and provide a simplified integration path for apps and wallets, making wallet connectivity fast, reliable, and developer-friendly.
### 🛠 Most Popular SDKs built on top of WalletConnect
* [**Reown AppKit**](https://reown.com/appkit) - A modular UX engine for onboarding, payments, and wallet interaction. Used in 286M+ sessions and 10B+ RPC calls .
* [**Privy**](https://www.privy.io/) - Secure wallet infrastructure that simplifies identity, session handling, and embedded wallets.
* [**Dynamic**](https://www.dynamic.xyz/) - All-in-one authentication and wallet SDK for web3 apps across mobile and web.
* [**ConnectKit**](https://family.co/connectkit) - Beautiful React components built for WalletConnect connections.
* [**RainbowKit**](https://www.rainbowkit.com/) - Customizable wallet connection UI optimized for Ethereum and WalletConnect.
* [**Canton dApp SDK**](https://github.com/hyperledger-labs/splice-wallet-kernel/tree/main/sdk/dapp-sdk) - Browser SDK from the [Splice Wallet Kernel](https://github.com/hyperledger-labs/splice-wallet-kernel) for building dApps on the [Canton Network](https://www.canton.network/). Implements the [CIP-103](https://github.com/canton-foundation/cips/blob/main/cip-0103/cip-0103.md) dApp API with multi-transport support (HTTP, `postMessage`) and an EIP-1193-style `window.canton` provider.
These SDKs demonstrate what’s possible with the App SDK as a base layer and how it can be extended to suit your product, stack, and user flow.
### SDK Chain Compatibility
Below you can find the chain compatibility for the most popular SDKs built on top of WalletConnect.
| SDK | Networks / Chains Supported |
| ------------------- | -------------------------------------------------------------------------------------- |
| **Reown AppKit** | EVM, Solana, Bitcoin, Polkadot, Cosmos and all other networks with a CAIP-25 namespace |
| **Privy** | EVM, Solana, Bitcoin |
| **Dynamic** | EVM, Solana, Bitcoin, Flow, StarkNet, Sui, Cosmos, Algorand, Spark |
| **ConnectKit** | EVM |
| **RainbowKit** | EVM |
| **Canton dApp SDK** | Canton Network |
## Standalone Integration of WalletConnect as an App
If you do not wish to use an SDK partner, you can integrate WalletConnect directly into your app. Please refer to the corresponding installation guide for each framework given below.
Get started with WalletConnect as an App in React.
Get started with WalletConnect as an App in Next.js.
Get started with WalletConnect as an App in Vue.
Get started with WalletConnect as an App in JavaScript.
### Chains Supported by WalletConnect
Please refer to the [Chains Supported](https://docs.reown.com/cloud/chains/chain-list) page for the list of chains supported by WalletConnect.
### RPCs and Chain Specific Methods
Please refer to the **RPC Reference** dropdown under the [Multi-Chain](https://docs.reown.com/advanced/multichain/rpc-reference/) section for the list of RPCs and chain specific methods supported by WalletConnect.
# Installation
Source: https://docs.walletconnect.network/app-sdk/react/installation
WalletConnect App SDK is chain agnostic and provides seamless integration with several blockchain ecosystems. WalletConnect App SDK when combined with Universal Provider library enables compatibility across any blockchain protocol.
## Pre-requisites
This section is to inform you about the pre-requisites for integrating WalletConnect as an App with React.
### Cloud Configuration
Create a new project on WalletConnect Dashboard at [https://dashboard.walletconnect.com](https://dashboard.walletconnect.com) and obtain a new project ID. You will need this project ID to initialize WalletConnect in your project (app).
**Don't have a project ID?**
Head over to WalletConnect Dashboard and create a new project now!
### 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) where your project ID is used. You can manage your allowlist in the [WalletConnect Dashboard](https://dashboard.walletconnect.com) under the project settings.
The allowlist supports a list of origins in the format `[scheme://]`. Using `localhost` (or `127.0.0.1`) is always permitted, and if the allowlist is empty, all origins are allowed. Updates take 15 minutes to apply.
Examples of possible origins in the allowlist:
* `example.com` - allows `https://example.com` or `http://example.com` but not `https://www.example.com`
* `https://example.com` - allows `https://example.com` but not `http://example.com`
* `https://*.example.com` - allows `https://www.example.com` but not `https://example.com`
Requests from origins not in the allowlist will be denied. Make sure to add all domains where your app will be deployed.
## Installation
If you are setting up your React app, please **do not use** `npx create-react-app`, as it has been deprecated. Using it may cause dependency
issues. Instead, please use [Vite](https://vitejs.dev/guide/#scaffolding-your-first-vite-project) to
create your React app. You can set it up by running `npm create vite@latest`.
```bash npm theme={null}
npm install @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
```bash Yarn theme={null}
yarn add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
```bash Bun theme={null}
bun add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
```bash pnpm theme={null}
pnpm add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
## Implementation
Here is a sample implementation of WalletConnect App SDK with React. You can also check the example repository below.
Check the WalletConnect App SDK React example
For a quick integration of WalletConnect App SDK you can use the `UniversalConnector` class. Which simplifies the integration of WalletConnect App SDK by providing a single interface for all the blockchain protocols.
You can configure the Universal Connector with the networks you want to support.
For more information, please visit [RPC Reference](https://docs.reown.com/advanced/multichain/rpc-reference/cosmos-rpc) section from our docs.
We recommend creating a config file to establish a singleton instance for the Universal Connector:
```tsx Generic Example theme={null}
import type { AppKitNetwork } from '@reown/appkit/networks'
import type { CustomCaipNetwork } from '@reown/appkit-common'
import { UniversalConnector } from '@reown/appkit-universal-connector'
// Get projectId from https://dashboard.walletconnect.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "b56e18d47c72ab683b10814fe9495694" // this is a public projectId only to use on localhost
if (!projectId) {
throw new Error('Project ID is not defined')
}
// you can configure your own network
const suiMainnet: CustomCaipNetwork<'sui'> = {
id: 784,
chainNamespace: 'sui' as const,
caipNetworkId: 'sui:mainnet',
name: 'Sui',
nativeCurrency: { name: 'SUI', symbol: 'SUI', decimals: 9 },
rpcUrls: { default: { http: ['https://fullnode.mainnet.sui.io:443'] } }
}
export const networks = [suiMainnet] as [AppKitNetwork, ...AppKitNetwork[]]
export async function getUniversalConnector() {
const universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['sui_signPersonalMessage'],
chains: [suiMainnet as CustomCaipNetwork],
events: [],
namespace: 'sui'
}
]
})
return universalConnector
}
```
```tsx Stacks Example theme={null}
import type { AppKitNetwork } from '@reown/appkit/networks'
import type { InferredCaipNetwork } from '@reown/appkit-common'
import { UniversalConnector } from '@reown/appkit-universal-connector'
// Get projectId from https://dashboard.walletconnect.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
// you can configure your own network
const stacksMainnet: InferredCaipNetwork = {
id: 'stacks-mainnet',
chainNamespace: 'stacks' as const,
caipNetworkId: 'stacks:1',
name: 'Stacks Mainnet',
nativeCurrency: { name: 'STX', symbol: 'STX', decimals: 6 },
rpcUrls: { default: { http: ['https://stacks-node-api.mainnet.stacks.co'] } } // Example Stacks Mainnet RPC URL
}
export const networks = [stacksMainnet] as [AppKitNetwork, ...AppKitNetwork[]]
export async function getUniversalConnector() {
const universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['stx_signMessage', 'stx_signTransaction', 'stx_getAccounts', 'stx_getAddresses', 'stx_callContract', 'stx_deployContract', 'sendTransfer', 'getAddresses'],
chains: [stacksMainnet as InferredCaipNetwork],
events: ['stx_chainChanged', 'stx_accountsChanged'],
namespace: 'stacks'
}
]
})
return universalConnector
}
```
```tsx TON Example theme={null}
import type { AppKitNetwork } from '@reown/appkit/networks'
import type { CustomCaipNetwork } from '@reown/appkit-common'
import { UniversalConnector } from '@reown/appkit-universal-connector'
// Get projectId from https://dashboard.walletconnect.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
const tonMainnet: CustomCaipNetwork<'ton'> = {
id: -239,
chainNamespace: 'ton' as const,
caipNetworkId: 'ton:-239',
name: 'TON',
nativeCurrency: { name: 'TON', symbol: 'TON', decimals: 9 },
rpcUrls: { default: { http: ['https://toncenter.com/api/v2/jsonRPC'] } }
}
export const networks = [tonMainnet] as [AppKitNetwork, ...AppKitNetwork[]]
export async function getUniversalConnector() {
const universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['ton_signData'],
chains: [tonMainnet as CustomCaipNetwork],
events: [],
namespace: 'ton'
}
]
})
return universalConnector
}
```
```tsx TRON Example theme={null}
import type { AppKitNetwork } from '@reown/appkit/networks'
import type { CustomCaipNetwork } from '@reown/appkit-common'
import { UniversalConnector } from '@reown/appkit-universal-connector'
// Get projectId from https://dashboard.walletconnect.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
const tronMainnet: CustomCaipNetwork<'tron'> = {
id: '0x2b6653dc',
chainNamespace: 'tron' as const,
caipNetworkId: 'tron:0x2b6653dc',
name: 'Tron Mainnet',
nativeCurrency: { name: 'TRX', symbol: 'TRX', decimals: 6 },
rpcUrls: { default: { http: ['https://api.trongrid.io'] } }
}
export const networks = [tronMainnet] as [AppKitNetwork, ...AppKitNetwork[]]
export async function getUniversalConnector() {
const universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['tron_signTransaction', 'tron_signMessage'],
chains: [tronMainnet as CustomCaipNetwork],
events: [],
namespace: 'tron'
}
]
})
return universalConnector
}
```
In the App.tsx file you can add :
```tsx theme={null}
import { useState, useEffect } from 'react'
import { getUniversalConnector } from './config' // previous config file
import { UniversalConnector } from '@reown/appkit-universal-connector'
export function App() {
const [universalConnector, setUniversalConnector] = useState()
const [session, setSession] = useState()
// Initialize the Universal Connector on component mount
useEffect(() => {
getUniversalConnector().then(setUniversalConnector)
}, [])
// Set the session state in case it changes
useEffect(() => {
setSession(universalConnector?.provider.session)
}, [universalConnector?.provider.session])
```
## Trigger the modal
To open the WalletConnect modal you need to call the `connect` function from the Universal Connector.
```tsx theme={null}
// get the session from the universal connector
const handleConnect = async () => {
if (!universalConnector) {
return
}
const { session: providerSession } = await universalConnector.connect()
setSession(providerSession)
};
// disconnect the universal connector
const handleDisconnect = async () => {
if (!universalConnector) {
return
}
await universalConnector.disconnect()
setSession(null)
};
...
return (
(
)
```
## Smart Contract Interaction
[Wagmi hooks](https://wagmi.sh/react/api/hooks/useReadContract) can help us interact with wallets and smart contracts:
```tsx theme={null}
import { useReadContract } from "wagmi";
import { USDTAbi } from "../abi/USDTAbi";
const USDTAddress = "0x...";
function App() {
const result = useReadContract({
abi: USDTAbi,
address: USDTAddress,
functionName: "totalSupply",
});
}
```
Read more about Wagmi hooks for smart contract interaction [here](https://wagmi.sh/react/hooks/useReadContract).
[Ethers](https://docs.ethers.org/v6/) can help us interact with wallets and smart contracts:
```tsx theme={null}
import { useAppKitProvider, useAppKitAccount } from "@reown/appkit/react";
import { BrowserProvider, Contract, formatUnits } from "ethers";
const USDTAddress = "0x617f3112bf5397D0467D315cC709EF968D9ba546";
// The ERC-20 Contract ABI, which is a common contract interface
// for tokens (this is the Human-Readable ABI format)
const USDTAbi = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function balanceOf(address) view returns (uint)",
"function transfer(address to, uint amount)",
"event Transfer(address indexed from, address indexed to, uint amount)",
];
function Components() {
const { address, isConnected } = useAppKitAccount();
const { walletProvider } = useAppKitProvider("eip155");
async function getBalance() {
if (!isConnected) throw Error("User disconnected");
const ethersProvider = new BrowserProvider(walletProvider);
const signer = await ethersProvider.getSigner();
// The Contract object
const USDTContract = new Contract(USDTAddress, USDTAbi, signer);
const USDTBalance = await USDTContract.balanceOf(address);
console.log(formatUnits(USDTBalance, 18));
}
return ;
}
```
[@Solana/web3.js](https://solana.com/docs/clients/javascript) library allows for seamless interaction with wallets and smart contracts on the Solana blockchain.
For a practical example of how it works, you can refer to our [lab dApp](https://lab.reown.com/appkit/?name=solana).
```tsx theme={null}
import {
SystemProgram,
PublicKey,
Keypair,
Transaction,
TransactionInstruction,
LAMPORTS_PER_SOL
} from '@solana/web3.js'
import { useAppKitAccount, useAppKitProvider } from '@reown/appkit/react'
import { useAppKitConnection, type Provider } from '@reown/appkit-adapter-solana/react'
function deserializeCounterAccount(data?: Buffer): { count: number } {
if (data?.byteLength !== 8) {
throw Error('Need exactly 8 bytes to deserialize counter')
}
return {
count: Number(data[0])
}
}
const { address } = useAppKitAccount()
const { connection } = useAppKitConnection()
const { walletProvider } = useAppKitProvider('solana')
async function onIncrementCounter() {
const PROGRAM_ID = new PublicKey('Cb5aXEgXptKqHHWLifvXu5BeAuVLjojQ5ypq6CfQj1hy')
const counterKeypair = Keypair.generate()
const counter = counterKeypair.publicKey
const balance = await connection.getBalance(walletProvider.publicKey)
if (balance < LAMPORTS_PER_SOL / 100) {
throw Error('Not enough SOL in wallet')
}
const COUNTER_ACCOUNT_SIZE = 8
const allocIx: TransactionInstruction = SystemProgram.createAccount({
fromPubkey: walletProvider.publicKey,
newAccountPubkey: counter,
lamports: await connection.getMinimumBalanceForRentExemption(COUNTER_ACCOUNT_SIZE),
space: COUNTER_ACCOUNT_SIZE,
programId: PROGRAM_ID
})
const incrementIx: TransactionInstruction = new TransactionInstruction({
programId: PROGRAM_ID,
keys: [
{
pubkey: counter,
isSigner: false,
isWritable: true
}
],
data: Buffer.from([0x0])
})
const tx = new Transaction().add(allocIx).add(incrementIx)
tx.feePayer = walletProvider.publicKey
tx.recentBlockhash = (await connection.getLatestBlockhash('confirmed')).blockhash
await walletProvider.signAndSendTransaction(tx, [counterKeypair])
const counterAccountInfo = await connection.getAccountInfo(counter, {
commitment: 'confirmed'
})
if (!counterAccountInfo) {
throw new Error('Expected counter account to have been created')
}
const counterAccount = deserializeCounterAccount(counterAccountInfo?.data)
if (counterAccount.count !== 1) {
throw new Error('Expected count to have been 1')
}
console.log(`[alloc+increment] count is: ${counterAccount.count}`);
}
```
# Installation
Source: https://docs.walletconnect.network/app-sdk/vue/installation
WalletConnect App SDK is chain agnostic and provides seamless integration with several blockchain ecosystems. WalletConnect App SDK when combined with Universal Provider library enables compatibility across any blockchain protocol.
## Pre-requisites
This section is to inform you about the pre-requisites for integrating WalletConnect as an App with Vue
### Cloud Configuration
Create a new project on WalletConnect Dashboard at [https://dashboard.walletconnect.com](https://dashboard.walletconnect.com) and obtain a new project ID. You will need this project ID to initialize WalletConnect in your project (app).
**Don't have a project ID?**
Head over to WalletConnect Dashboard and create a new project now!
### 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) where your project ID is used. You can manage your allowlist in the [WalletConnect Dashboard](https://dashboard.walletconnect.com) under the project settings.
The allowlist supports a list of origins in the format `[scheme://]`. Using `localhost` (or `127.0.0.1`) is always permitted, and if the allowlist is empty, all origins are allowed. Updates take 15 minutes to apply.
Examples of possible origins in the allowlist:
* `example.com` - allows `https://example.com` or `http://example.com` but not `https://www.example.com`
* `https://example.com` - allows `https://example.com` but not `http://example.com`
* `https://*.example.com` - allows `https://www.example.com` but not `https://example.com`
Requests from origins not in the allowlist will be denied. Make sure to add all domains where your app will be deployed.
## Installation
```bash npm theme={null}
npm install @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
```bash Yarn theme={null}
yarn add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
```bash Bun theme={null}
bun add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
```bash pnpm theme={null}
pnpm add @reown/appkit @reown/appkit-universal-connector @reown/appkit-common ethers
```
## Implementation
Here is a sample implementation of WalletConnect App SDK with Vue. You can also check the example repository below.
Check the WalletConnect App SDK Vue example
For a quick integration of WalletConnect App SDK you can use the `UniversalConnector` class. Which simplifies the integration of WalletConnect App SDK by providing a single interface for all the blockchain protocols.
You can configure the Universal Connector with the networks you want to support.
For more information, please visit [RPC Reference](https://docs.reown.com/advanced/multichain/rpc-reference/cosmos-rpc) section from our docs.
We recommend creating a config file to establish a singleton instance for the Universal Connector:
```tsx Generic Example theme={null}
import type { AppKitNetwork } from '@reown/appkit/networks'
import type { CustomCaipNetwork } from '@reown/appkit-common'
import { UniversalConnector } from '@reown/appkit-universal-connector'
// Get projectId from https://dashboard.walletconnect.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
const suiMainnet: CustomCaipNetwork<'sui'> = {
id: 784,
chainNamespace: 'sui' as const,
caipNetworkId: 'sui:mainnet',
name: 'Sui',
nativeCurrency: { name: 'SUI', symbol: 'SUI', decimals: 9 },
rpcUrls: { default: { http: ['https://fullnode.mainnet.sui.io:443'] } }
}
export const networks = [suiMainnet] as [AppKitNetwork, ...AppKitNetwork[]]
export async function getUniversalConnector() {
const universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['sui_signPersonalMessage'],
chains: [suiMainnet as CustomCaipNetwork],
events: [],
namespace: 'sui'
}
]
})
return universalConnector
}
```
```tsx Stacks Example theme={null}
import type { AppKitNetwork } from '@reown/appkit/networks'
import type { InferredCaipNetwork } from '@reown/appkit-common'
import { UniversalConnector } from '@reown/appkit-universal-connector'
// Get projectId from https://dashboard.walletconnect.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
// you can configure your own network
const stacksMainnet: InferredCaipNetwork = {
id: 'stacks-mainnet',
chainNamespace: 'stacks' as const,
caipNetworkId: 'stacks:1',
name: 'Stacks Mainnet',
nativeCurrency: { name: 'STX', symbol: 'STX', decimals: 6 },
rpcUrls: { default: { http: ['https://stacks-node-api.mainnet.stacks.co'] } } // Example Stacks Mainnet RPC URL
}
export const networks = [stacksMainnet] as [AppKitNetwork, ...AppKitNetwork[]]
export async function getUniversalConnector() {
const universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['stx_signMessage', 'stx_signTransaction', 'stx_getAccounts', 'stx_getAddresses', 'stx_callContract', 'stx_deployContract', 'sendTransfer', 'getAddresses'],
chains: [stacksMainnet as InferredCaipNetwork],
events: ['stx_chainChanged', 'stx_accountsChanged'],
namespace: 'stacks'
}
]
})
return universalConnector
}
```
```tsx TON Example theme={null}
import type { AppKitNetwork } from '@reown/appkit/networks'
import type { CustomCaipNetwork } from '@reown/appkit-common'
import { UniversalConnector } from '@reown/appkit-universal-connector'
// Get projectId from https://dashboard.walletconnect.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
const tonMainnet: CustomCaipNetwork<'ton'> = {
id: -239,
chainNamespace: 'ton' as const,
caipNetworkId: 'ton:-239',
name: 'TON',
nativeCurrency: { name: 'TON', symbol: 'TON', decimals: 9 },
rpcUrls: { default: { http: ['https://toncenter.com/api/v2/jsonRPC'] } }
}
export const networks = [tonMainnet] as [AppKitNetwork, ...AppKitNetwork[]]
export async function getUniversalConnector() {
const universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['ton_signData'],
chains: [tonMainnet as CustomCaipNetwork],
events: [],
namespace: 'ton'
}
]
})
return universalConnector
}
```
```tsx TRON Example theme={null}
import type { AppKitNetwork } from '@reown/appkit/networks'
import type { CustomCaipNetwork } from '@reown/appkit-common'
import { UniversalConnector } from '@reown/appkit-universal-connector'
// Get projectId from https://dashboard.walletconnect.com
export const projectId = import.meta.env.VITE_PROJECT_ID || "YOUR_PROJECT_ID_HERE" // Replace with your actual project ID
if (!projectId || projectId === "YOUR_PROJECT_ID_HERE") {
throw new Error('Project ID is not defined. Please set your project ID from the WalletConnect Dashboard.')
}
const tronMainnet: CustomCaipNetwork<'tron'> = {
id: '0x2b6653dc',
chainNamespace: 'tron' as const,
caipNetworkId: 'tron:0x2b6653dc',
name: 'Tron Mainnet',
nativeCurrency: { name: 'TRX', symbol: 'TRX', decimals: 6 },
rpcUrls: { default: { http: ['https://api.trongrid.io'] } }
}
export const networks = [tronMainnet] as [AppKitNetwork, ...AppKitNetwork[]]
export async function getUniversalConnector() {
const universalConnector = await UniversalConnector.init({
projectId,
metadata: {
name: 'Universal Connector',
description: 'Universal Connector',
url: 'https://www.walletconnect.com',
icons: ['https://www.walletconnect.com/icon.png']
},
networks: [
{
methods: ['tron_signTransaction', 'tron_signMessage'],
chains: [tronMainnet as CustomCaipNetwork],
events: [],
namespace: 'tron'
}
]
})
return universalConnector
}
```
In de App.vue file you can add :
```tsx theme={null}
```
## Trigger the modal
To open the WalletConnect modal you need to call the `connect` function from the Universal Connector.
```tsx theme={null}
...
async handleConnect() {
if (!universalConnector) {
return
}
const { session: providerSession } = await universalConnector.connect()
}
```
## Smart Contract Interaction
[Wagmi actions](https://wagmi.sh/core/api/actions/readContract) can help us interact with wallets and smart contracts:
```html theme={null}
```
Read more about Wagmi actions for smart contract interaction [here](https://wagmi.sh/core/actions/readContract).
[Ethers](https://docs.ethers.org/v6/) can help us interact with wallets and smart contracts:
```html theme={null}
```
[@Solana/web3.js](https://solana.com/docs/clients/javascript) library allows for seamless interaction with wallets and smart contracts on the Solana blockchain.
For a practical example of how it works, you can refer to our [lab dApp](https://lab.reown.com/appkit/?name=solana).
```tsx theme={null}
import { ref } from 'vue';
import {
SystemProgram,
PublicKey,
Keypair,
Transaction,
TransactionInstruction,
LAMPORTS_PER_SOL
} from '@solana/web3.js';
import { useAppKitAccount, useAppKitProvider } from '@reown/appkit/vue'
import { useAppKitConnection, type Provider } from '@reown/appkit-adapter-solana/vue'
export default {
setup() {
const counterMessage = ref('');
const { address } = useAppKitAccount();
const { connection } = useAppKitConnection()
const { walletProvider } = useAppKitProvider('solana')
function deserializeCounterAccount(data) {
if (data?.byteLength !== 8) {
throw Error('Need exactly 8 bytes to deserialize counter');
}
return {
count: Number(data[0])
};
}
async function onIncrementCounter() {
try {
const PROGRAM_ID = new PublicKey('Cb5aXEgXptKqHHWLifvXu5BeAuVLjojQ5ypq6CfQj1hy');
const counterKeypair = Keypair.generate();
const counter = counterKeypair.publicKey;
const balance = await connection.getBalance(walletProvider.publicKey);
if (balance < LAMPORTS_PER_SOL / 100) {
throw Error('Not enough SOL in wallet');
}
const COUNTER_ACCOUNT_SIZE = 8;
const allocIx = SystemProgram.createAccount({
fromPubkey: walletProvider.publicKey,
newAccountPubkey: counter,
lamports: await connection.getMinimumBalanceForRentExemption(COUNTER_ACCOUNT_SIZE),
space: COUNTER_ACCOUNT_SIZE,
programId: PROGRAM_ID
});
const incrementIx = new TransactionInstruction({
programId: PROGRAM_ID,
keys: [
{
pubkey: counter,
isSigner: false,
isWritable: true
}
],
data: Buffer.from([0x0])
});
const tx = new Transaction().add(allocIx).add(incrementIx);
tx.feePayer = walletProvider.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash('confirmed')).blockhash;
await walletProvider.signAndSendTransaction(tx, [counterKeypair]);
const counterAccountInfo = await connection.getAccountInfo(counter, {
commitment: 'confirmed'
});
if (!counterAccountInfo) {
throw new Error('Expected counter account to have been created');
}
const counterAccount = deserializeCounterAccount(counterAccountInfo?.data);
if (counterAccount.count !== 1) {
throw new Error('Expected count to have been 1');
}
counterMessage.value = `[alloc+increment] count is: ${counterAccount.count}`;
} catch (error) {
console.error(error);
counterMessage.value = `Error: ${error.message}`;
}
}
return {
onIncrementCounter,
counterMessage
};
}
};
```
# WCT Smart Contracts
Source: https://docs.walletconnect.network/contracts
Below you can find all the contract addresses for the WalletConnect Token (WCT).
## Deployment Addresses
### Ethereum Mainnet (Chain ID: 1)
| Contract | Address | Description |
| --------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------- |
| WCT Token | [`0xeF4461891DfB3AC8572cCf7C794664A8DD927945`](https://etherscan.io/address/0xeF4461891DfB3AC8572cCf7C794664A8DD927945) | Main WCT token contract |
### Optimism (Chain ID: 10)
| Contract | Address | Description |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| L2WCT Token | [`0xeF4461891DfB3AC8572cCf7C794664A8DD927945`](https://optimistic.etherscan.io/address/0xeF4461891DfB3AC8572cCf7C794664A8DD927945) | WCT token on Optimism |
| Admin Timelock | [`0x61cc6aF18C351351148815c5F4813A16DEe7A7E4`](https://optimistic.etherscan.io/address/0x61cc6aF18C351351148815c5F4813A16DEe7A7E4) | Admin timelock controller |
| Manager Timelock | [`0xB5EFe3783Db55B913C79CBdB81C9d2C0a993f5f0`](https://optimistic.etherscan.io/address/0xB5EFe3783Db55B913C79CBdB81C9d2C0a993f5f0) | Manager timelock controller |
| WalletConnectConfig | [`0xd2f149fAA66DC4448176123f850C14Ff14f978B3`](https://optimistic.etherscan.io/address/0xd2f149fAA66DC4448176123f850C14Ff14f978B3) | Protocol configuration |
| Pauser | [`0x9163de7F22A9f3ad261B3dBfbB9A42886816adE7`](https://optimistic.etherscan.io/address/0x9163de7F22A9f3ad261B3dBfbB9A42886816adE7) | Emergency pause mechanism |
| StakeWeight | [`0x521B4C065Bbdbe3E20B3727340730936912DfA46`](https://optimistic.etherscan.io/address/0x521B4C065Bbdbe3E20B3727340730936912DfA46) | Manages staking positions |
| StakingRewardDistributor | [`0xF368F535e329c6d08DFf0d4b2dA961C4e7F3fCAF`](https://optimistic.etherscan.io/address/0xF368F535e329c6d08DFf0d4b2dA961C4e7F3fCAF) | Handles rewards distribution |
| Airdrop | [`0x4ee97a759AACa2EdF9c1445223b6Cd17c2eD3fb4`](https://optimistic.etherscan.io/address/0x4ee97a759AACa2EdF9c1445223b6Cd17c2eD3fb4) | Season 1 airdrop distribution |
### Base Mainnet (Chain ID: 8453)
| Contract | Address | Description |
| --------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------- |
| WCT Token | [`0xeF4461891DfB3AC8572cCf7C794664A8DD927945`](https://basescan.org/address/0xeF4461891DfB3AC8572cCf7C794664A8DD927945) | WCT token on Base |
### Solana
| Contract | Address | Description |
| --------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| WCT Token | [`WCTk5xWdn5SYg56twGj32sUF3W4WFQ48ogezLBuYTBY`](https://explorer.solana.com/address/WCTk5xWdn5SYg56twGj32sUF3W4WFQ48ogezLBuYTBY) | WCT token on Solana |
## Token Information
* Total Supply: 1,000,000,000 WCT (1e27 wei)
# How to Control Which Apps Can Connect to Your Users’ Wallets
Source: https://docs.walletconnect.network/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](/wallet-sdk/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.network/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.network/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](/wallet-sdk/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.network/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/).
# Governance
Source: https://docs.walletconnect.network/governance
The governance of the WalletConnect Network is structured to facilitate decentralization, transparency, and community participation. This section outlines the roles and responsibilities of the WalletConnect Foundation and the community governance model that guides the Network.
## Foundation
The WalletConnect Foundation is tasked with stewarding the Network by promoting its adoption, use, and growth. The Foundation's responsibilities include overview of grants to stakeholders, supporting applications, sdk and wallet development teams, and managing partnerships.
## Councils
The Councils includes several curated groups of individuals who are responsible for the different functions that are either part of the foundation, core development teams, node operator teams or work independently. Envisioned Councils include:
* **Technical Council** - responsible for the technology & infrastructure
* **Partnerships Council** - responsible for the partnerships & growth
This structure is projected to be implemented during the constitution of the community governance.
## Community Governance
The WalletConnect Network is designed for a fully decentralized governance model managed by community governance. Further decentralization is expected to be facilitated by approved proposals of WCT tokenholders participating in the Network governance.
Optimally this transition occurs through planned, multiple phases. An example of such planned multiple phases follows, though the actual transition will depend on input and approval from WCT tokenholders:
1. **Phase 1 - TGE Preparation:**
* The WalletConnect Foundation was established and began operations.
* The Foundation and reown collaborate on the Network's technical, community, partnerships, and administrative governance.
2. **Phase 2 - Foundation Transition:**
* The Foundation establishes different councils to eventually take over various governance functions.
* The Foundation expands its programs and responsibilities over community, partnerships, and administration considerations for the Network.
* The community governance arises from WCT token stakers who participate in the Network and its governance.
3. **Phase 3 - Partnerships Transition:**
* The Partnerships Council, elected by the community governance, assumes a more prominent role in community initiatives, including marketing, business development, grants programs, education, developer relations, and events.
4. **Phase 4 - Technology Transition:**
* The Technical Council, elected by the community governance, assumes responsibility for technical governance as the Network becomes permissionless.
* The Foundation coordinates this transition.
5. **Phase 5 - Administration Transition:**
* The Foundation requires community governance approval to establish annual budgets, review and elect councils, and handle other administrative responsibilities through voting by community governance delegates.
Token holders can participate in governance starting in Phase 2 after TGE by staking their WCT tokens, proposing changes, and voting on key issues, thereby shaping the future of the WalletConnect Network.
# TON Connect WalletConnect Integration
Source: https://docs.walletconnect.network/guides/tonconnect-walletconnect
Learn how to enable WalletConnect support in your TON Connect application.
This guide explains how to enable WalletConnect support in your [TON Connect](https://www.npmjs.com/package/@tonconnect/sdk) application, allowing users to connect with WalletConnect-compatible wallets.
## Prerequisites
Before enabling WalletConnect in your TON Connect application, ensure you have the following:
### 1. TON Connect Project
You should have an existing TON Connect project initialized with the [@tonconnect/sdk](https://www.npmjs.com/package/@tonconnect/sdk) package. If you haven't set up TON Connect yet, please refer to the [TON Connect documentation](https://docs.ton.org/develop/dapps/ton-connect/overview) to get started.
### 2. WalletConnect Project ID
Create a new project on the WalletConnect Dashboard at [https://dashboard.walletconnect.com](https://dashboard.walletconnect.com) and obtain a new project ID. You will need this project ID to initialize WalletConnect in your application.
**Don't have a project ID?**
Head over to WalletConnect Dashboard and create a new project now!
### 3. Allowlist Your Domains
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) where your project ID is used. You can manage your allowlist in the [WalletConnect Dashboard](https://dashboard.walletconnect.com) under the project settings.
The allowlist supports a list of origins in the format `[scheme://]`. Using `localhost` (or `127.0.0.1`) is always permitted, and if the allowlist is empty, all origins are allowed. Updates take 15 minutes to apply.
Examples of possible origins in the allowlist:
* `example.com` - allows `https://example.com` or `http://example.com` but not `https://www.example.com`
* `https://example.com` - allows `https://example.com` but not `http://example.com`
* `https://*.example.com` - allows `https://www.example.com` but not `https://example.com`
Requests from origins not in the allowlist will be denied. Make sure to add all domains where your app will be deployed.
## Enable WalletConnect
To enable WalletConnect in your TON Connect application, use the `initializeWalletConnect()` function from the `@tonconnect/sdk` package along with the `UniversalConnector` from `@reown/appkit-universal-connector`.
```typescript theme={null}
import { initializeWalletConnect } from '@tonconnect/sdk';
import { UniversalConnector } from '@reown/appkit-universal-connector';
initializeWalletConnect(UniversalConnector, {
projectId: 'YOUR_PROJECT_ID',
metadata: {
name: 'My DApp',
description: 'My awesome DApp',
url: 'https://mydapp.com',
icons: ['https://mydapp.com/icon.png']
}
});
```
### Configuration Options
The `initializeWalletConnect` function accepts the following configuration options:
| Option | Type | Required | Description |
| ---------------------- | ---------- | -------- | --------------------------------------------------------------------------------------- |
| `projectId` | `string` | Yes | Your WalletConnect project ID from the [Dashboard](https://dashboard.walletconnect.com) |
| `metadata.name` | `string` | Yes | The name of your application |
| `metadata.description` | `string` | Yes | A brief description of your application |
| `metadata.url` | `string` | Yes | The URL of your application |
| `metadata.icons` | `string[]` | Yes | An array of icon URLs for your application |
### Example Implementation
Here's a complete example of integrating WalletConnect into a TON Connect application:
```typescript theme={null}
import { TonConnect } from '@tonconnect/sdk';
import { initializeWalletConnect } from '@tonconnect/sdk';
import { UniversalConnector } from '@reown/appkit-universal-connector';
import {
TonConnectUIProvider,
TonConnectButton,
} from '@tonconnect/ui-react';
// Initialize WalletConnect support
initializeWalletConnect(UniversalConnector, {
projectId: 'YOUR_PROJECT_ID',
metadata: {
name: 'My TON DApp',
description: 'A decentralized application on TON',
url: 'https://mytonapp.com',
icons: ['https://mytonapp.com/icon.png']
}
});
export function App() {
// Create TON Connect instance
const tonConnect = new TonConnect({
manifestUrl: 'https://mytonapp.com/tonconnect-manifest.json'
});
return (
WalletConnect + TON React Example
)
}
```
## Handling `ton_proof`
When using TON Connect with WalletConnect, `ton_proof` is requested and returned as part of the connection response using the [CAIP-222](https://chainagnostic.org/CAIPs/caip-222) authentication flow. The proof payload is passed via the `authentication` parameter during `connect()`, and the signed proof is available on `session.authentication` after the connection is established.
### Requesting `ton_proof` During Connection
To request `ton_proof` during the WalletConnect session establishment, pass the `authentication` parameter when calling `connect()` on the underlying `UniversalConnector` provider:
```typescript theme={null}
import { UniversalConnector } from '@reown/appkit-universal-connector';
const connector = await UniversalConnector.init({
projectId: 'YOUR_PROJECT_ID',
metadata: {
name: 'My TON DApp',
description: 'A decentralized application on TON',
url: 'https://mytonapp.com',
icons: ['https://mytonapp.com/icon.png']
},
networks: [{
namespace: 'ton',
chains: [tonMainnet],
methods: ['ton_sendMessage', 'ton_signData'],
events: []
}]
});
const session = await connector.provider.connect({
optionalNamespaces: {
ton: {
methods: ['ton_sendMessage', 'ton_signData'],
chains: ['ton:-239'],
events: []
}
},
authentication: [{
uri: 'https://mytonapp.com',
domain: 'mytonapp.com',
chains: ['ton:-239'],
nonce: '',
ttl: 86400,
statement: ''
}]
});
```
### Reading the Proof Result
After a successful connection, the proof result is available on `session.authentication` as an array of [CAIP-222 Cacao](https://chainagnostic.org/CAIPs/caip-222) objects:
```typescript theme={null}
const authenticationResults = session?.authentication;
if (authenticationResults && authenticationResults.length > 0) {
// Each result is a Cacao object containing the signed proof
const proof = authenticationResults[0];
console.log('ton_proof result:', proof);
}
```
The `ton_proof` result is **not** available in `onStatusChange` when using WalletConnect. You must read it from `session.authentication` directly after the connection is established.
## Next Steps
After integrating WalletConnect into your TON Connect application, users will be able to connect using any WalletConnect-compatible wallet. For more information about TON-specific RPC methods supported by WalletConnect, see the [TON Chain Support](/wallet-sdk/chain-support/ton) documentation.
# Network
Source: https://docs.walletconnect.network/network
## Overview
The WalletConnect Network is the onchain UX ecosystem that facilitates users' use of any wallet across any app and platform. It is chain agnostic, working across ecosystems from EVM and its L2s, to Solana, Cosmos, Polkadot, Bitcoin and more. To date, it has facilitated more than 300+ million connections for 50+ million users between over 700+ different wallets and 70,000+ applications.
The Network's design emphasizes interoperability, connectivity, and composability, creating a user experience (UX) platform that is open and permissionless. This approach is essential for realizing the vision of the "new internet"—a decentralized and user-owned ecosystem.
The next chapter for WalletConnect is the decentralization of the Network, providing for continued resilience, privacy and censorship-resistance, which will be governed by the WalletConnect Token (WCT).
## Technology
The WalletConnect Network's services are more akin to classic web2 offchain infrastructure rather than blockchain. The core technology for the WalletConnect Network is a permissionless rendezvous-hashing based database. Key components include:
1. **Service Nodes**: These are database nodes that form the backbone of the network's storage layer. They operate on a consistent-hashing based distributed database.
2. **Gateway Nodes**: These nodes are responsible for facilitating encrypted communications and data routing between wallets and applications.
3. **Relay Service**: This service, used to connect users' wallets to dapps, is by design end-to-end encrypted. The Relay has no insight into users' addresses, transaction hashes, KYC information, or any other information passed between the dapp and wallet.
4. **End-to-End Encryption**: All messages transmitted through the network are encrypted, ensuring that only the intended recipient can access the information.
The Network is designed to operate in a fully permissionless manner, enabling any participant to run a Service Node or Gateway. It is expected that the Network will transition to a permissionless model following community consultation and technical validation.
## Permissioned Network
Currently, the WalletConnect Network operates in a permissioned environment. This means:
* Specific node operators manage the Service Nodes under service-level agreements.
* The Gateway Nodes are initially centralized and managed by reown (formerly WalletConnect Inc.).
* This setup ensures stability and reliability during the Network's early phases.
100% of WalletConnect's production traffic has been served by the Network since early 2024 with Service Nodes operated by reown. In recent months, reown has brought additional entities into the permissioned federation of Service Node operations.
The Network is designed to become more decentralized over time. This process involves a phased approach, gradually transitioning from a permissioned environment to a fully permissionless model. Throughout this evolution, the WalletConnect Network maintains its focus on user empowerment and security.
It is expected that the Network will transition to a permissionless model following community consultation and technical validation to maintain Network integrity and performance. This transition will allow broader participation while ensuring the Network's stability and efficiency.
## Network Participants
The WalletConnect Network comprises various participants, each playing a crucial role in maintaining functionality and security:
1. **Service Node Operators**: They run the service nodes (database nodes) that form the backbone of the network's storage layer. These nodes operate on a consistent-hashing based distributed database.
2. **Gateway Node Operators**: They manage the gateway nodes, which are the entry points for apps and SDKs. Gateways facilitate encrypted communications and data routing between wallets and applications.
3. **Wallets**: These allow end-users to manage their blockchain keys and interact with apps via the WalletConnect protocol. Most wallets integrate with the network using the WalletKit SDK.
4. **Apps**: These are the products and services in the web3 space that drive traffic to the network. They can integrate directly or via available SDKs.
5. **SDKs**: Software Development Kits that simplify the integration process for apps and wallets.
6. **End Users**: The consumers of all services within the network, from wallets to apps, going through the relay and database nodes.
Each of these participants contributes to the ecosystem in unique ways, ensuring the network's functionality, security, and continued growth. Their roles and interactions form the foundation of the WalletConnect Network's robust and interconnected infrastructure.
## Whitepaper
* [WalletConnect Network Whitepaper](https://whitepaper.walletconnect.network/)
# WalletConnect Network
Source: https://docs.walletconnect.network/overview
WalletConnect Network aims to become a decentralized UX platform that facilitates secure and reliable connections across wallets and applications, promoting interoperability, connectivity, standardization, and consensus.
## Introduction
The WalletConnect Network is the onchain UX ecosystem that makes web3 work by facilitating users' use of any wallet across any app and platform. As a chain-agnostic infrastructure, it operates across ecosystems from EVM and its L2s, to Solana, Cosmos, Polkadot, Bitcoin and more. The Network has played a crucial role in installing many of web3's UX standards, fostering today's composable and interoperable web3 ecosystem.
Since its inception in 2018, WalletConnect has established itself as critical infrastructure in the web3 space. The protocol's ability to provide secure, end-to-end encrypted connections has been fundamental in creating interoperability for every wallet, every app, and every chain. Based on this protocol, a network of participants and contributors are creating the WalletConnect Network.
Key milestones include:
* Expansion to over 70,000+ applications and 700+ wallets
* Facilitation of more than 300+ million connections
* Consistent growth in daily remote connections, evidencing widespread adoption
* Transition to a permissioned decentralized database supported by third-party node operators
The next chapter for WalletConnect is the decentralization of the Network, providing for continued resilience, privacy, and censorship-resistance. This evolution will be governed by the WalletConnect Token (WCT), aligning web3's incentives towards prioritizing UX.
## Resources
The WalletConnect Whitepaper outlines the network's design, architecture, and core principles.
# Service Nodes
Source: https://docs.walletconnect.network/service-nodes
Service nodes form the backbone of the WalletConnect Network, serving as crucial infrastructure for persisting and managing end-to-end encrypted network messages. These nodes employ rendezvous hashing, a sophisticated method that ensures even distribution of data across the network. This approach not only enhances reliability and fault tolerance but also maintains user privacy by design - service nodes cannot decrypt or read the content of the messages they handle.
## Technical Architecture
The network's architecture is built on the premise that clients may be offline for extended periods. To address this, a "mailbox" system persists messages, allowing clients to retrieve data upon reconnection. This system is underpinned by a database utilizing rendezvous hashing, a concept that has proven its scalability in modern databases including Cassandra, DynamoDB, MongoDB, and others.
The nodes are primarily constructed in Rust, chosen for its performance and safety features. For critical lower-level operations such as bloom-filters and I/O, the nodes integrate RocksDB, an industry-standard implementation maintained by Facebook/Meta. This hybrid approach leverages the strengths of custom-built solutions and battle-tested components.
Current research focuses on evolving the rendezvous hashing-based database into a fully permissionless system. The next milestone is to publish a comprehensive technical design for community review, a crucial step before implementation. In the interim, node access to the network remains permissioned to ensure stability and security.
## Service Node Operators
Service Node Operators play a vital role in the WalletConnect ecosystem. Their responsibilities extend beyond mere node management to include proactive maintenance of high uptime and consistent performance optimization. The barrier to entry - staking WCT tokens - serves a dual purpose: it demonstrates the operator's commitment and aligns their interests with the network's success.
The reward structure for operators is carefully designed to promote both long-term commitment and high-quality service. Staking rewards incentivize sustained participation, while performance-based incentives, calculated using key metrics like uptime and latency, encourage continuous improvement and innovation in node operation.
## Node Statuses
The WalletConnect Network implements a dynamic node status system, allowing for flexible and responsive network management:
* **Active**: These nodes are the workhorses of the network, directly processing user requests within their assigned region. The initial target of 15 active nodes balances network robustness with economic efficiency.
* **Reserve**: Operating similarly to active nodes, reserve nodes ensure network resilience. They participate in the replication process and stand ready to step into an active role when needed, maintaining a pool of qualified nodes.
* **Jailed**: This status serves as a temporary penalty for nodes that fail to meet performance standards. The 24-hour exclusion period provides operators time to address issues while protecting the network from underperforming nodes.
* **Standby**: Representing potential capacity, standby nodes have staked tokens but are not actively running. This status allows the network to scale rapidly when demand increases.
* **Deactivated**: This status accommodates operators who choose to cease support, ensuring an orderly exit process that protects both the operator's interests and the network's stability.
| Status | Target Number |
| ----------- | ------------- |
| Active | 15 |
| Reserve | 6 |
| Jailed | No target |
| Standby | No target |
| Deactivated | No target |
Target numbers are initial values and may be adjusted through governance decisions to optimize network performance and economic sustainability.
## Performance Evaluation
The sophisticated performance evaluation system is a cornerstone of maintaining the WalletConnect Network's high standards. The performance coefficient $U(i,t) \in [0, 1]$ provides a nuanced measure of each node's contribution:
$\text{Performance} = (W_u \cdot U_i) \cdot (W_l \cdot L_i)$
Where $U_i$ represents uptime, $L_i$ denotes latency, and $W_u$ and $W_l$ are adjustable weights. This formula allows for fine-tuning of performance priorities as the network evolves.
## Performance Verification
The transition from a permissioned to a permissionless verification system represents a key evolution in the network's maturity:
1. Permissioned Network (Phase 1):
* Trusted oracle nodes conduct performance verification, ensuring a controlled and stable initial environment.
* The oracle node plays a crucial role within the Network. It functions as both a data collector and a regular service operator.
2. Permissionless Network (Phase 2):
* All nodes engage in mutual performance measurement, creating a more decentralized and robust verification system.
* Every node pings and reports on every other node operator, replacing the need for a central oracle.
* This distributed approach enhances the network's resilience and reduces single points of failure.
Key Transition:
* Phase 1: Trusted nodes (oracles) verify performance
* Phase 2: Every node participates in performance measurement
This phased approach allows for gradual decentralization while maintaining network integrity throughout the transition. It enables the network to start with a controlled, easily manageable system and evolve into a more decentralized, robust structure as it matures.
## Slashing Mechanism
The slashing mechanism is a critical component in maintaining high network standards:
1. Performance threshold $\tau$ (where $0 < \tau < 1$) is set through governance.
2. Nodes with $U(i,t) < \tau$ trigger a slashing event.
3. The underperforming node is moved to "jailed" status and replaced by a reserve node.
4. Jailed nodes may face a reduction in staked tokens, with the percentage determined by governance.
5. After the jailing period, nodes return to standby status, with the opportunity to re-enter active service.
# WCT Token
Source: https://docs.walletconnect.network/token-dynamics/intro
The WCT token powers the WalletConnect ecosystem, acting as both a reward and governance mechanism.
## Token Functions
The WCT token has three primary functions within the WalletConnect Network:
1. **Rewards**: WCT tokens are distributed as cashback, staking rewards, and node operator rewards.
2. **Staking**: Participants can stake WCT tokens to earn rewards and participate in governance.
3. **Governance**: WCT holders can vote on proposals and changes, giving the community control over the Network's development through decentralized governance.
## WCT Allocation
The initial supply of WCT tokens is capped at 1 billion, with the following allocations:
* WalletConnect Foundation: 27%
* Airdrops: 18.5%
* Team: 18.5%
* Rewards: 17.5%
* Previous Backers: 11.5%
* Core Development: 7%
Tokens allocated to core development, team and previous backers will be subject to a 4-year unlock including a 1 year cliff starting at the token generation event (TGE).
## Fixed Supply
The initial design of the WalletConnect Network's tokenomics does not include token inflation. The current model focuses on utilizing existing token allocations such that inflation is not envisioned within the first 3-4 years.
Introduction of an inflationary design would follow only after the token holders' vote to approval following careful consideration of Network metrics, participant feedback, and overall ecosystem health, with specific parameters to be determined through Network governance processes.
## Token Flow
Payments represent one of the largest economic opportunities in crypto. Global card networks generate hundreds of billions in fee revenue annually — funding rewards programs, interchange, and network operations for every participant. WalletConnect Pay is designed to bring that same model onchain: generate real transaction revenue from real commerce, powered by the WalletConnect Network.
The Network is the primary engine of WalletConnect Pay. The WCT token sits at the centre, connecting payment activity to staking rewards, governance weight, and long-term token value.
### How the Network Powers Value Flows
Every payment powered by WalletConnect Pay generates transaction fees. Those fees flow back into the Network through multiple mechanisms. Additional mechanisms are expected to be implemented.
**Rewards Distribution** — Fee revenue funds rewards across all four stakeholder groups in the payment flow:
| Stakeholder | How They Earn |
| ----------- | --------------------------------------------------------------------------------------------------------- |
| Wallets | Earn interchange on every payment routed through WalletConnect Pay — modelled on card network interchange |
| End Users | Earn cashback-style rewards on every purchase |
### Why This Model Works
Most alternative payment methods have failed not for technical reasons, but because they gave users no reason to switch. UK Open Banking was technically superior to cards but offered consumers zero upside. CurrentC was backed by the largest US retailers but built solely to save merchants on fees — and died before launch because users had nothing to gain.
WalletConnect Pay is designed around the opposite principle: lead with incentives on every side. The result is a self-reinforcing flywheel — more payments generate more fees, more fees fund better rewards, better rewards drive more payments.
WCT is what holds this flywheel together. Stakers benefit as volume grows. Governance shapes how rewards and buybacks are calibrated over time. The token is not waiting for utility — it is the connective tissue between a payments network and its community of participants.
Merchants pay transaction fees — not users. The end-user experience remains frictionless by design.
# Rewards
Source: https://docs.walletconnect.network/token-dynamics/rewards
The WalletConnect Network implements a strategic reward system to incentivize network participants and ensure the network's growth and stability.
## Reward Allocation
17.5% of the initial token supply is allocated for rewards to incentivize Network participants over the first few years of operations. This allocation is strategically phased:
* **First Year**: Only 5% will be distributed to test Network assumptions
* **Subsequent Years**: The remaining 12.5% is reserved
## Phased Distribution
The reward distribution is strategically phased to allow for testing and long-term sustainability:
1. **First Year**: A conservative 5% distribution to test network assumptions and reward mechanisms.
2. **Subsequent Years**: A larger 12.5% allocation, designated as a "flexible incentive" for ongoing distribution.
:::note
The larger portion (12.5%) is a "flexible incentive" and may be subject to change to maintain the reward mechanism's support of Network goals and benefits to all participants. This flexibility ensures that the reward system can adapt to the evolving needs of the Network and its participants.
:::
## Purpose of Rewards
The reward system is designed to:
1. Incentivize active participation in the network
2. Encourage long-term commitment from participants
3. Ensure network security and efficiency
4. Support the overall growth and sustainability of the WalletConnect ecosystem
## Types of Rewards
The Network includes various types of rewards:
1. **Staking Rewards**: Participants can earn rewards by staking WCT tokens.
2. **Node Rewards**: Service Node operators receive rewards based on their performance and activity.
3. **Wallet Performance Rewards**: Wallets can earn rewards based on their performance and certification status.
4. **End-user Cashback**: Cashback rewards for using WalletConnect Pay.
## Related Topics
To learn more about specific aspects of the reward system and participation in the WalletConnect Network, please refer to the following sections:
* [Staking](../wct-staking)
* [Service Node Performance Rewards](./service-node-rewards)
# Service Node Rewards
Source: https://docs.walletconnect.network/token-dynamics/service-node-rewards
The WalletConnect Network implements a carefully structured reward system for node operators, designed to incentivize high performance and long-term commitment. The node rewards budget is split into two phases, with the first phase addressing the unique challenges of the WCT token non-transferability period.
## Phase 1: Non-Transferability Period
During this initial phase, a fixed-base reward structure is implemented, resulting in the following token-base revenues:
1. **Initial Allocation**: 100,000 WCT are distributed as initial allocation and can be staked. These tokens are locked for the entire period of non-transferability.
2. **Performance Boost (B\_n)**: A boost given to nodes to reflect their performance and activity that is not based on WCT price.
### Boost Calculation
The performance boost (B\_n) is calculated as follows:
$B_n = \alpha + (weight \cdot performance score)$
A multiplicator (M\_n) is also applied:
$M_n = \frac{100000}{T}$
Where T is the period on which the initial allocation is locked.
### Weight Distribution
The boost is based on the weight of the node relative to other nodes. Initially, if there are 15 nodes, each getting 100,000 WCT, they would each get 6.67% weight. This stake weight evolves if some nodes do not restake their boost.
## Performance-Based Rewards
Individual node rewards are conditional on the performance factor $U(i,t) \in [0, 1]$. In line with the performance factors for other groups, there is a set of KPIs defining this. Initially, it will be based on uptime and latency.
For a detailed explanation of how these factors are calculated and weighted, please refer to the [Performance Evaluation](../service-nodes/#performance-evaluation) section.
The reward structure is subject to adjustment through network governance to ensure it continues to align with the network's goals and economic sustainability.
# Best Practices
Source: https://docs.walletconnect.network/wallet-sdk/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

# Chain Abstraction
Source: https://docs.walletconnect.network/wallet-sdk/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](/wallet-sdk/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.network/wallet-sdk/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.
### Web3Inbox
#### Subscribers - All Time
Total count of all subscribers to your project.
#### Notifications - All Time
Total count of all notifications sent from your project.
#### Subscribers
Daily trend chart illustrating the growth of subscribers.
#### Notifications
Daily trend chart of total notifications received by your subscribers.
#### Messaged Accounts
Daily trend chart of unique wallets that received the notification.
#### Subscribers by notification type
This table shows the total count of subscribers by notification type over a 30 day period.
### 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.network/wallet-sdk/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](/walletguide/explorer).
## 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.network/wallet-sdk/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.network/wallet-sdk/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.network/wallet-sdk/android/link-mode
The Wallet SDK Link Mode is a low latency mechanism for transporting [One-Click Auth](/wallet-sdk/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](/wallet-sdk/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.network/wallet-sdk/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.

### 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.

## 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](/wallet-sdk/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.network/wallet-sdk/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.

## 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.network/wallet-sdk/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.network/wallet-sdk/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.network/wallet-sdk/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
}
```
# Best Practices for Wallets
Source: https://docs.walletconnect.network/wallet-sdk/best-practices
To ensure the smoothest and most seamless experience for our users, WalletConnect is committed to working closely with wallet providers to encourage the adoption of our recommended best practices.
By implementing these guidelines, we aim to optimize performance and minimize potential challenges, even in suboptimal network conditions.
We are actively partnering with wallet developers to optimize performance in scenarios such as:
1. **Success and Error Messages** - Users need to know what’s going on, at all times. Too much communication is better than too little. The less users need to figure out themselves or assume what’s going on, the better.
2. **(Perceived) Latency** - A lot of factors can influence latency (or perceived latency), e.g. network conditions, position in the boot chain, waiting on the wallet to connect or complete a transaction and not knowing if or when it has done it.
3. **Old SDK Versions** - Older versions can have known and already fixed bugs, leading to unnecessary issues to users, which can be simply and quickly solved by updating to the latest SDK.
To take all of the above into account and to make experience better for users, we've put together some key guidelines for wallet providers. These best practices focus on the most important areas for improving user experience.
Please follow these best practices and make the experience for your users and yourself a delightful and quick one.
## Checklist Before Going Live
To make sure your wallet adheres to the best practices, we recommend implementing the following checklist before going live. You can find more detailed information on each point below.
1. **Success and Error Messages**
* ✅ Display clear and concise messages for all user interactions
* ✅ Provide feedback for all user actions
* ✅ Connection success
* ✅ Connection error
* ✅ Loading indicators for waiting on connection, transaction, etc.
* ✅ Ensure that users are informed of the status of their connection and transactions
* ✅ Implement status indicators internet availability
* ✅ Make sure to provide feedback not only to users but also back to the dapp (e.g., if there's an error or a user has not enough funds to pay for gas, don't just display the info message to the user, but also send the error back to the dapp so that it can change the state accordingly)
2. **Mobile Linking**
* ✅ Implement mobile linking to allow for automatic redirection between the wallet and the dapp
* ✅ Use deep linking over universal linking for a better user experience
* ✅ Ensure that the user is redirected back to the dapp after completing a transaction
3. **Latency**
* ✅ Optimize performance to minimize latency
* ✅ Latency for connection in normal conditions: under 5 seconds
* ✅ Latency for connection in poor network (3G) conditions: under 15 seconds
* ✅ Latency for signing in normal conditions: under 5 seconds
* ✅ Latency for signing in poor network (3G) conditions: under 10 seconds
4. **Verify API**
* ✅ Present users with four key states that can help them determine whether the domain they’re about to connect to might be malicious (Domain match, Unverified, Mismatch, Threat)
5. **Latest SDK Version**
* ✅ Ensure that you are using the latest SDK version
* ✅ Update your SDK regularly to benefit from the latest features and bug fixes
* ✅ Subscribe to SDK updates to stay informed about new releases
## 1. Success and Error Messages
Users often face ambiguity in determining whether their connection or transactions were successful. They are also not guided to switching back into the dapp or automatically switched back when possible, causing unnecessary user anxiety. Additionally, wallets typically lack status indicators for connection and internet availability, leaving users in the dark.

### 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.
```jsx theme={null}
const uri = 'xxx'; // pairing uri
try {
await walletKit.pair({ uri });
} catch (error) {
// some error happens while pairing - check Expected errors section
}
```
```jsx theme={null}
const uri = 'xxx'; // pairing uri
try {
await walletKit.pair({ uri });
} catch (error) {
// some error happens while pairing - check Expected errors section
}
```
```swift theme={null}
let uri = WalletConnectURI(string: urlString)
if let uri {
Task {
try await WalletKit.instance.pair(uri: uri)
}
}
```
```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 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.
```typescript theme={null}
core.pairing.events.on("pairing_expire", (event) => {
// pairing expired before user approved/rejected a session proposal
const { topic } = topic;
});
```
```typescript theme={null}
core.pairing.events.on("pairing_expire", (event) => {
// pairing expired before user approved/rejected a session proposal
const { topic } = topic;
});
```
```Swift theme={null}
WalletKit.instance.pairingExpirationPublisher
.receive(on: DispatchQueue.main)
.sink { pairing in
guard !pairing.active else { return }
// let user know that pairing has expired
}.store(in: &publishers)
```
```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)
```
#### Pairing messages
1. Consider displaying a successful pairing message when pairing is successful. Before that happens, wallet should show a loading indicator.
2. Display an error message when a pairing fails.
#### 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. It usually happens when user scans an already paired QR code.
### Session Proposal
A session proposal is a handshake sent by a dapp and its purpose is to define session rules. Whenever a user wants to establish a connection between a wallet and a dapp, one should approve a session proposal.
Whenever user approves or rejects a session proposal, a wallet should show a loading indicator the moment the button is pressed, until Relay acknowledgement is received for any of these actions.
#### Approving session
```typescript theme={null}
try {
await walletKit.approveSession(params);
// update UI -> remove the loader
} catch (error) {
// present error to the user
}
```
```typescript theme={null}
try {
await walletKit.approveSession(params);
// update UI -> remove the loader
} catch (error) {
// present error to the user
}
```
```swift theme={null}
do {
try await WalletKit.instance.approve(proposalId: proposal.id, namespaces: sessionNamespaces, sessionProperties: proposal.sessionProperties)
// Update UI, remove loader
} catch {
// present error
}
```
```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
})
```
#### Rejecting session
```typescript theme={null}
try {
await walletKit.rejectSession(params);
// update UI -> remove the loader
} catch (error) {
// present error to the user
}
```
```typescript theme={null}
try {
await walletKit.rejectSession(params);
// update UI -> remove the loader
} catch (error) {
// present error to the user
}
```
```swift theme={null}
do {
try await WalletKit.instance.reject(proposalId: proposal.id, reason: .userRejected)
// Update UI, remove loader
} catch {
// present error
}
```
```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 minutes. It means a given proposal is stored for 5 minutes in the SDK storage and user has 5 minutes for the approval or rejection decision. After that time, the below event is emitted and proposal modal should be removed from the app's UI.
```typescript theme={null}
walletKit.on("proposal_expire", (event) => {
// proposal expired and any modal displaying it should be removed
const { id } = event;
});
```
```typescript theme={null}
walletKit.on("proposal_expire", (event) => {
// proposal expired and any modal displaying it should be removed
const { id } = event;
});
```
```swift theme={null}
WalletKit.instance.sessionProposalExpirationPublisher.sink { _ in
// let user know that session proposal has expired, update UI
}.store(in: &publishers)
```
```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)
```
#### Session Proposal messages
1. Consider displaying a successful session proposal message before redirecting back to the dapp. Before the success message is displayed, wallet should show a loading indicator.
2. Display an error message when session proposal fails.
#### Expected errors
While approving or rejecting a session proposal, the following errors might occur:
* **No Internet connection**
* It happens when a user tries to approve or reject a session proposal with no Internet connection
* **Session proposal expired**
* It happens when a user tries to approve or reject an expired session proposal
* **Invalid [namespaces](https://docs.reown.com/advanced/glossary#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.
Whenever user approves or rejects a session request, a wallet should show a loading indicator the moment the button is pressed, until Relay acknowledgement is received for any of these actions.
```typescript theme={null}
try {
await walletKit.respondSessionRequest(params);
// update UI -> remove the loader
} catch (error) {
// present error to the user
}
```
```typescript theme={null}
try {
await walletKit.respondSessionRequest(params);
// update UI -> remove the loader
} catch (error) {
// present error to the user
}
```
```swift theme={null}
do {
try await WalletKit.instance.respond(requestId: request.id, signature: signature, from: account)
// update UI -> remove the loader
} catch {
// present error to the user
}
```
```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. Its 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.
```typescript theme={null}
walletKit.on("session_request_expire", (event) => {
// request expired and any modal displaying it should be removed
const { id } = event;
});
```
```typescript theme={null}
walletKit.on("session_request_expire", (event) => {
// request expired and any modal displaying it should be removed
const { id } = event;
});
```
```swift theme={null}
WalletKit.instance.requestExpirationPublisher.sink { _ in
// let user know that request has expired
}.store(in: &publishers)
```
```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 errors
While approving or rejecting a session request, the following errors might occur:
* **Invalid session**
* This error might happen when a user approves or rejects a session request on an expired session
* **Session request expired**
* This error might happen when a user approves or rejects a session request that already expired
* **Timeout**
* It happens when Relay doesn't acknowledge session settle publish within 10 seconds
### Connection state
The Web Socket connection state tracks the connection with the Relay server. An event is emitted whenever a connection state changes.
```typescript theme={null}
core.relayer.on("relayer_connect", () => {
// connection to the relay server is established
})
core.relayer.on("relayer_disconnect", () => {
// connection to the relay server is lost
})
```
```typescript theme={null}
core.relayer.on("relayer_connect", () => {
// connection to the relay server is established
})
core.relayer.on("relayer_disconnect", () => {
// connection to the relay server is lost
})
```
```swift theme={null}
WalletKit.instance.socketConnectionStatusPublisher
.receive(on: DispatchQueue.main)
.sink { status in
switch status {
case .connected:
// ...
case .disconnected:
// ...
}
}.store(in: &publishers)
```
```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)
```
#### Connection state messages
When the connection state changes, show a message in the UI. For example, display a message when the connection is lost or re-established.
## 2. Mobile Linking
### Why use Mobile Linking?
Mobile Linking uses the mobile device’s native OS to automatically redirect between the native wallet app and a native app. This results in few user actions a better UX.
#### 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 or copy/pastes the URI 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) 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.
### Connection Flow
1. **Dapp prompts user:** The Dapp asks the user to connect.
2. **User chooses wallet:** The user selects a wallet from a list of compatible wallets.
3. **Redirect to wallet:** The user is redirected to their chosen wallet.
4. **Wallet approval:** The wallet prompts the user to approve or reject the session (similar to granting permission).
5. **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.
6. **User reunites with dapp:** After all the interactions, the user ends up back in the Dapp.

### Sign Request Flow
When the Dapp needs the user to sign something (like a transaction), a similar pattern occurs:
1. **Automatic redirect:** The Dapp automatically sends the user to their previously chosen wallet.
2. **Approval prompt:** The wallet asks the user to approve or reject the request.
3. **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.
4. **User reconnects:** Eventually, the user returns to the Dapp.

### Platform Specific Preparation
Read the specific steps for iOS here: [Platform preparations](./ios/mobile-linking#platform-preparations)
Read the specific steps for Android here: [Platform
preparations](./android/mobile-linking#platform-preparations)
Read the specific steps for Flutter here: [Platform
preparations](./flutter/mobile-linking#platform-preparations)
Read the specific steps for React Native here: [Platform preparations](./react-native/mobile-linking#platform-preparations)
### How to Test
To experience the desired behavior, try our Sample Wallet and Dapps which use our Mobile linking best practices. These are available on all platforms.
Once you have completed your integration, you can test it against our sample apps to see if it is working as expected. Download the app and and try your mobile linking integration on your device.
* [Sample Wallet](https://testflight.apple.com/join/09bTAryp) - on TestFlight
* [Sample DApp](https://testflight.apple.com/join/7S1GYcjC) - on TestFlight
* [Sample Wallet](https://appdistribution.firebase.dev/i/6f9437a5f9bf4eec) -
on Firebase - [Sample
DApp](https://appdistribution.firebase.dev/i/5e4fe4b30c8a208d) - on Firebase
* Sample Wallet: - [Sample Wallet for
iOS](https://testflight.apple.com/join/Uv0XoBuD) - [Sample Wallet for
Android](https://appdistribution.firebase.dev/i/2b8b3dce9e2831cd) - AppKit
DApp: - [AppKit Dapp for iOS](https://testflight.apple.com/join/6aRJSllc) -
[AppKit Dapp for
Android](https://appdistribution.firebase.dev/i/2c6573f6956fa7b5)
* Sample Wallet:
* [Sample Wallet for Android](https://appdistribution.firebase.dev/i/e7711e780547234e)
* Sample DApp:
* [Sample App for iOS](https://testflight.apple.com/join/Ivd8bg7s)
* [Sample App for Android](https://appdistribution.firebase.dev/i/0297fbd3de8f1e3f)
## 3. Latency
Our SDK’s position in the boot chain can lead to up to 15 seconds in throttled network conditions. Lack of loading indicators exacerbates the perceived latency issues, impacting user experience negatively. Additionally, users often do not receive error messages or codes when issues occur or timeouts happen.
### Target latency
For **connecting**, the target latency is:
* **Under 5 seconds** in normal conditions
* **Under 15 seconds** when throttled (3G network speed)
For **signing**, the target latency is:
* **Under 5 seconds** in normal conditions
* **Under 10 seconds** when throttled (3G network speed)
### How to test
To test latency under suboptimal network conditions, you can enable throttling on your mobile phone. You can simulate different network conditions to see how your app behaves in various scenarios.
For example, on iOS you need to enable Developer Mode and then go to **Settings > Developer > Network Link Conditioner**. You can then select the network condition you want to simulate. For 3G, you can select **3G** from the list, for no network or timeout simulations, choose **100% Loss**.
Check this article for how to simulate slow internet connection on iOS & Android, with multiple options for both platforms: [How to simulate slow internet connection on iOS & Android](https://www.browserstack.com/guide/how-to-simulate-slow-network-conditions).
## 4. Verify API
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.
Possible states:
* Domain match
* Unverified
* Mismatch
* Threat


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[](https://docs.reown.com/walletkit/web/verify#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`.
### Verify API Implementation
To see how to implement Verify API for your framework, see [Verify API](./features/verify) page and select your platform to see code examples.
### How to test
To test Verify API with a malicious domain, you can check out the [Malicious React dapp](https://malicious-app-verify-simulation.vercel.app/), created specifically for testing. This app is flagged as malicious and will have the `isScam` parameter set to `true` in the `verifyContext` of the request. You can use this app to test how your wallet behaves when connecting to a malicious domain.
### Error messages

*A sample error warning when trying to connect to a malicious domain*
## 5. Latest SDK
Numerous features have been introduced, bugs have been identified and fixed over time, stability has improved, but many dapps and wallets continue to use older SDK versions with known issues, affecting overall reliability.
Make sure you are using the latest version of the SDK for your platform
* **WalletConnectSwiftV2**: [Latest release](https://github.com/reown-com/reown-swift/releases/latest/)
* **WalletConnectKotlinV2**: [Latest
release](https://github.com/WalletConnect/WalletConnectKotlinV2/releases/latest)
* **WalletConnectFlutterV2**: [Latest
release](https://github.com/WalletConnect/WalletConnectFlutterV2/releases/latest)
* **AppKit for React Native**: [Latest release](https://github.com/WalletConnect/reown-react-native/releases/latest)
### Subscribe to updates
To stay up to date with the latest SDK releases, you can use GitHub's native feature to subscribe to releases. This way, you will be notified whenever a new release is published. You can find the "Watch" button on the top right of the repository page. Click on it, then select "Custom" and "Releases only". You'll get a helpful ping whenever a new release is out.

## Resources
* [React Wallet](https://react-wallet.reown.com/) - for testing dapps, features, Verify API messages, etc.
* [React dapp](https://react-app.reown.com/) - for testing wallets
* [Malicious React dapp](https://malicious-app-verify-simulation.vercel.app/) - for testing Verify API with malicious domain
# Analytics
Source: https://docs.walletconnect.network/wallet-sdk/c-sharp/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.
### Web3Inbox
#### Subscribers - All Time
Total count of all subscribers to your project.
#### Notifications - All Time
Total count of all notifications sent from your project.
#### Subscribers
Daily trend chart illustrating the growth of subscribers.
#### Notifications
Daily trend chart of total notifications received by your subscribers.
#### Messaged Accounts
Daily trend chart of unique wallets that received the notification.
#### Subscribers by notification type
This table shows the total count of subscribers by notification type over a 30 day period.
### 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.network/wallet-sdk/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](/walletguide/explorer).
## 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.network/wallet-sdk/c-sharp/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://]
**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.network/wallet-sdk/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.network/wallet-sdk/chain-support/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](/wallet-sdk/chain-support/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](/wallet-sdk/chain-support/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.network/wallet-sdk/chain-support/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.network/wallet-sdk/chain-support/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.network/wallet-sdk/chain-support/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.network/wallet-sdk/chain-support/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](/wallet-sdk/chain-support/evm)
* [Solana](/wallet-sdk/chain-support/solana)
* [Bitcoin](/wallet-sdk/chain-support/bitcoin)
* [SUI](/wallet-sdk/chain-support/sui)
* [Stacks](/wallet-sdk/chain-support/stacks)
* [TON](/wallet-sdk/chain-support/ton)
* [Tron](/wallet-sdk/chain-support/tron)
* [ADI Chain](/wallet-sdk/chain-support/adi)
* [Canton](/wallet-sdk/chain-support/canton)
## 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.network/wallet-sdk/chain-support/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.network/wallet-sdk/chain-support/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.
# Sui
Source: https://docs.walletconnect.network/wallet-sdk/chain-support/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.network/wallet-sdk/chain-support/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.network/wallet-sdk/chain-support/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/).
# Link Mode
Source: https://docs.walletconnect.network/wallet-sdk/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.
## When and How can Link Mode help?
Let's assume that a user is trying to connect their wallet to a native (mobile) dApp while commuting on a train with spotty internet.
Now, if the user wants to sign a message on the dApp using their mobile wallet and the dApp relies on typical WebSocket connections to relay the session request. Due to unstable connectivity, the connection drops or lags, causing the wallet to not receive the sign message request promptly.
**Using Link Mode, the dApp sends a Universal Link directly to the wallet app.** Since this doesn’t rely on maintaining a WebSocket session, the wallet receives the connection or signature request instantly and reliably, even in weak network conditions.
## Get Started
Get started with Link Mode in WalletKit - Android.
Get started with Link Mode in WalletKit - iOS.
Get started with Link Mode in WalletKit - Flutter.
Get started with Link Mode in WalletKit - React Native.
# One-Click Auth
Source: https://docs.walletconnect.network/wallet-sdk/features/one-click-auth
Enable your users to connect to web3 through a single tap with One-Click Auth, improving connectivity speeds and creating all-around better UX and friction-free user journeys.
With one-tap multi-chain and multi-account signing, let users authenticate multiple chains and accounts simultaneously.
## Get Started
Get started with WalletKit in Web.
Get started with WalletKit in Android.
Get started with WalletKit in iOS.
Get started with WalletKit in React Native.
Get started with WalletKit in Flutter.
# Verify API
Source: https://docs.walletconnect.network/wallet-sdk/features/verify
App Verification is a first-of-its-kind layered security solution that enables wallets to help users protect themselves from phishing attacks, with robust architecture enabling wallets to support users in better identifying the veracity of a domain they are attempting to connect to.
## Get Started
Get started with WalletKit in Android.
Get started with WalletKit in iOS.
Get started with WalletKit in Flutter.
Get started with WalletKit in React Native.
Get started with WalletKit in Web.
Get started with WalletKit in .NET.
# Chain Abstraction
Source: https://docs.walletconnect.network/wallet-sdk/flutter/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
### Prepare
This method is used to check if chain abstraction is needed. If it is, it will return a `PrepareDetailedResponseSuccessCompat` object with the necessary transactions and funding information.
If it is not, it will return a `PrepareResponseNotRequiredCompat` object with the original transaction.
```swift theme={null}
Future prepare({
required String chainId,
required String from,
required CallCompat call,
Currency? localCurrency,
});
```
### Execute
This method is used to execute the chain abstraction operation. The method will handle broadcasting all transactions in the correct order and monitor the cross-chain transfer process. It returns an `ExecuteDetails` object with the transaction status and results.
```swift theme={null}
Future execute({
required UiFieldsCompat uiFields,
required List routeTxnSigs,
required String initialTxnSig,
})
```
## Usage
When sending a transaction, first check if chain abstraction is needed using the `prepare` method. Call the `execute` method to broadcast the routing and initial transactions and wait for it to be completed.
If the operation is successful, you need to broadcast the initial transaction and await the transaction hash and receipt.
If the operation is not successful, send a JsonRpcError to the dapp and display the error to the user.
```swift theme={null}
final response = await _walletKit.prepare(
chainId: chainId, // selected chain id
from: from, // sender address
call: CallCompat(
to: to, // contract address
input: input, // calldata
),
);
response.when(
success: (PrepareDetailedResponseSuccessCompat deatailResponse) {
deatailResponse.when(
available: (UiFieldsCompat uiFieldsCompat) {
// If the route is available, present a CA transaction UX flow and sign hashes when approved
final TxnDetailsCompat initial = uiFieldsCompat.initial;
final List route = uiFieldsCompat.route;
final String initialSignature = signHashMethod(initial.transactionHashToSign);
final List routeSignatures = route.map((route) {
final String rSignature = signHashMethod(route.transactionHashToSign);
return rSignature;
}).toList();
await _walletKit.execute(
uiFields: uiFields,
initialTxnSig: initialSignature,
routeTxnSigs: routeSignatures,
);
},
notRequired: (PrepareResponseNotRequiredCompat notRequired) {
// user does not need to move funds from other chains
// proceeds as normal transaction with notRequired.initialTransaction
},
);
},
error: (PrepareResponseError prepareError) {
// Show an error
// contains prepareError.error as BridgingError and could be either:
// noRoutesAvailable, insufficientFunds, insufficientGasFunds
},
);
```
### Implementation during Session Request
If you are looking to trigger Chain Abstraction during a eth\_sendTransaction Session Request you should do it inside the session request handler as explained in [Responding to Session requests](./usage#responding-to-session-requests) section.
```swift theme={null}
Future _ethSendTransactionHandler(String topic, dynamic params) async {
final SessionRequest pendingRequest = _walletKit.pendingRequests.getAll().last;
final int requestId = pendingRequest.id;
final String chainId = pendingRequest.chainId;
final transaction = (params as List).first as Map;
// Intercept to check if Chain Abstraction is required
if (transaction.containsKey('input') || transaction.containsKey('data')) {
final inputData = transaction.containsKey('input') ?? transaction.containsKey('data');
final response = await _walletKit.prepare(
chainId: chainId,
from: transaction['from'],
call: CallCompat(
to: transaction['to'],
input: inputData,
),
);
response.when(
success: (PrepareDetailedResponseSuccessCompat deatailResponse) {
deatailResponse.when(
available: (UiFieldsCompat uiFieldsCompat) {
// Only if the route is available, present a Chain Abstraction approval modal
// and proceed with execute() method
if (approved) {
final TxnDetailsCompat initial = uiFieldsCompat.initial;
final List route = uiFieldsCompat.route;
final String initialSignature = signHashMethod(initial.transactionHashToSign);
final List routeSignatures = route.map((route) {
final String rSignature = signHashMethod(route.transactionHashToSign);
return rSignature;
}).toList();
final executeResponse = await _walletKit.execute(
uiFields: uiFields,
initialTxnSig: initialSignature,
routeTxnSigs: routeSignatures,
);
// Respond to the session request. Flow shouldn't end here as the transaction was processed
return await _walletKit.respondSessionRequest(
topic: topic,
response: JsonRpcResponse(
id: requestId,
jsonrpc: '2.0',
result: executeResponse.initialTxnReceipt,
),
);
}
},
// If deatailResponse is not `available` type
// then let the flow to continue to regular send transacrion
);
},
);
}
// display a prompt for the user to approve or reject the request
// if approved
if (approved) {
final signedTx = await sendTransaction(transaction, int.parse(chainId));
// respond to requester
await _walletKit.respondSessionRequest(
topic: topic,
response: JsonRpcResponse(
id: requestId,
jsonrpc: '2.0',
result: signedTx,
),
);
}
// if rejected
return _walletKit.respondSessionRequest(
topic: topic,
response: JsonRpcResponse(
id: id,
jsonrpc: '2.0',
error: const JsonRpcError(code: 5001, message: 'User rejected method'),
),
);
}
```
For example, check out implementation of chain abstraction in [sample wallet](https://github.com/reown-com/reown_flutter/blob/develop/packages/reown_walletkit/example/lib/dependencies/chain_services/evm_service.dart) with Flutter.
### Token Balance
You can use this method to query the token balance of the given address
```swift theme={null}
Future erc20TokenBalance({
required String chainId, // chain id
required String token, // token address
required String owner, // user address
})
```
## Android
If you didn't do it already, in your android (project's) build.gradle file add support for Jitpack:
```
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' } // <- add jipack url
}
}
```
It shouldn't happen but 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.**
```
## 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
Best way to test Chain Abstraction is to use our Sample wallet.
* [Sample Wallet for iOS](https://testflight.apple.com/join/Uv0XoBuD)
* [Sample Wallet for Android](https://appdistribution.firebase.dev/i/2b8b3dce9e2831cd)
You can also use the [AppKit laboratory](https://appkit-lab.reown.com/library/wagmi/) and try sending [USDC/USDT](/wallet-sdk/features/chain-abstraction#what-are-the-supported-tokens-and-networks%3F) with any chain abstraction-supported wallet.
# Analytics
Source: https://docs.walletconnect.network/wallet-sdk/flutter/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.
### Web3Inbox
#### Subscribers - All Time
Total count of all subscribers to your project.
#### Notifications - All Time
Total count of all notifications sent from your project.
#### Subscribers
Daily trend chart illustrating the growth of subscribers.
#### Notifications
Daily trend chart of total notifications received by your subscribers.
#### Messaged Accounts
Daily trend chart of unique wallets that received the notification.
#### Subscribers by notification type
This table shows the total count of subscribers by notification type over a 30 day period.
### 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.network/wallet-sdk/flutter/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](/walletguide/explorer).
## 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.network/wallet-sdk/flutter/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 via `sessionProperties`. These can be universal (across 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
A wallet's response should indicate which capabilities it actually supports, following 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, the `atomic` capability specifies how the wallet will execute batches of transactions. It has three possible values:
* `supported` - The wallet will execute 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": ["..."],
}
}
}
```
Specify the `scopedProperties` when approving a session:
```json theme={null}
"scopedProperties": {
"eip155": {
"walletService": [{
"url": "",
"methods": ["wallet_getCallsStatus"]
}]
}
}
```
### Response Format
The response format for `wallet_getCallsStatus` varies based on the execution method:
#### 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
}
```
For non-atomic execution, include all transactions in the receipts array, even those that were included on-chain but eventually reverted.
## 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.network/wallet-sdk/flutter/installation
* Add `reown_walletkit` as dependency in your `pubspec.yaml` and run `flutter pub get` (check out the [latest version](https://pub.dev/packages/reown_walletkit/install))
* Or simply run `flutter pub add reown_walletkit`
If you are on **Android** add jitpack support to your android (project's) build.gradle file
```
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' } // <- add jipack url
}
}
```
If you are on **MacOS** add the following to your `DebugProfile.entitlements` and `Release.entitlements` files to connect to the WebSocket server.
```xml theme={null}
com.apple.security.network.client
```
## Next Steps
Now that you've installed Wallet SDK 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.network/wallet-sdk/flutter/link-mode
WalletKit Link Mode is a low latency mechanism for transporting [One-Click Auth](/wallet-sdk/flutter/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.
By enabling it, the wallet and dapp will communicate through declared Universal Links on iOS and/or App Links on Android **even without an internet connection.**
Make sure that [One-Click Auth](/wallet-sdk/flutter/one-click-auth) is implemented before enabling Link Mode.
### How to enable it:
1. Add a Universal Link for your wallet in the **Explorer** tab of your [**Cloud project configuration**](https://dashboard.walletconnect.com/sign-in), under the **Mobile Linking** section
2. Configure your `PairingMetadata`'s `redirect:` object with that Universal Link
3. Set the `linkMode` property to `true`:
```javascript {12,13} theme={null}
final _walletKit = ReownWalletKit(
core: ReownCore(
projectId: '{YOUR_PROJECT_ID}',
),
metadata: PairingMetadata(
name: 'Example Wallet',
description: 'Example wallet description',
url: 'https://example.com/',
icons: ['https://example.com/logo.png'],
redirect: Redirect(
native: 'examplewallet://',
universal: 'https://example.com/wallet',
linkMode: true,
),
),
);
```
Once everything is properly configured, and the user interacts with a Link Mode-supporting dApp, your wallet will receive requests through it.
In Flutter, there are several plugins that can help you integrate Universal/App Links. However, regardless of which one you choose, it is crucial that, when capturing an incoming link, you pass it to WalletKit so it can process the request.
```javascript theme={null}
void _onLinkCaptured(String link) async {
await _walletKit.dispatchEnvelope(link);
}
```
### Platform specifics:
1. Ensure that you handle incoming Universal Links in the appropriate methods of `AppDelegate` or `SceneDelegate`.
2. Ensure that you have enabled the Associated Domains Capability in your XCode project and that your Universal Link is properly configured. *(Depending on the previous states of your Provisioning Profiles it may be necessary to update or create new ones)*
```xml theme={null}
com.apple.developer.associated-domainsapplinks:your_wallet_universal_link.com
```
3. Update/Create your domain's `.well-known/apple-app-site-association` file accordingly.
For more information on how to configure universal links for your app, refer to the [Apple Documentation](https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content?language=swift).
For a debugging guide, visit the [Debugging Universal Links](https://developer.apple.com/documentation/technotes/tn3155-debugging-universal-links) page.
You can check our Flutter's Wallet SDK sample [AppDelegate file](https://github.com/reown-com/reown_flutter/blob/master/packages/reown_walletkit/example/ios/Runner/AppDelegate.swift) as a reference.
1. Ensure that you handle incoming App Links in your Activity's `onCreate` method and in `onNewIntent` callback.
2. Ensure that your App Link is properly configured in your app's `AndroidManifest.xml` file with the `autoVerify` set to `true`:
```xml theme={null}
```
3. Update/Create your domains's `.well-known/assetlinks.json` file accordingly
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).
You can check our Flutter's Wallet SDK sample [MainActivity file](https://github.com/reown-com/reown_flutter/blob/master/packages/reown_walletkit/example/android/app/src/main/kotlin/com/example/wallet/MainActivity.kt) as a reference.
# Mobile Linking
Source: https://docs.walletconnect.network/wallet-sdk/flutter/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.

### 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.

## 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 custom scheme under [`CFBundleURLTypes`](https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleurltypes) key in your Info.plist file.
For instance, if your Wallet's name is Example Wallet, your custom scheme would be more likely as `examplewallet://`, therefor you will add the following in your iOS's Info.plist file:
```ruby theme={null}
CFBundleURLTypesCFBundleTypeRoleEditorCFBundleURLName$(PRODUCT_BUNDLE_IDENTIFIER)CFBundleURLSchemesexamplewallet
```
In order for Dapps to be able to trigger your wallet for a connection or sign request using deep links you first need to declare an [``](https://developer.android.com/training/app-links/deep-linking#adding-filters) in your wallet's Manifest.xml as follows:
For instance, if your Wallet's name is Example Wallet, your custom scheme would be more likely as `examplewallet://`, therefor you will add the following intent filter in your Android's Manifest.xml file:
```xml theme={null}
```
Since Flutter leverages on native APIs, you must follow iOS and Android steps for each native platform.
**Additionally**, you would have to set FlutterDeepLinkingEnabled key to true on iOS's Info.plist file.
```xml theme={null}
FlutterDeepLinkingEnabled
```
More information in official documentation: [https://docs.flutter.dev/ui/navigation/deep-linking](https://docs.flutter.dev/ui/navigation/deep-linking)
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](/wallet-sdk/android/best-practices#2-mobile-linking).
## Integration
Either you are approving a session proposal or responding to a session request, redirecting back to the Dapp is as simply as launching requester's `redirect` object in `PairingMetadata`, the same way as Dapps would call your wallet's `redirect` object on their side:
A dapp would call `examplewallet://wc?uri={pairingUri}` from their side when they request to connect with your wallet, and given the fact that `examplewallet` is your registered custom scheme then your wallet will be opened.
### Redirecting back to dapp (proposer) after session approval:
Wallet SDK exports a handy method for easy redirection back to the requester app, whether it be after a session proposal, a session authentication or a session request.
```javascript theme={null}
Future redirectToDapp({
required String topic,
required Redirect? redirect,
})
```
After Session Proposal:
```javascript theme={null}
_walletKit!.onSessionProposal.subscribe(_onSessionProposal);
//
void _onSessionProposal(SessionProposalEvent? event) async {
if (event != null) {
// Process session proposal
// ....
// Redirect back to proposer dapp
try {
await _walletKit.redirectToDapp(
topic: topic,
redirect: event.params.proposer.metadata.redirect,
);
} catch (e) {
...
}
}
}
```
After Session Authenticate:
```javascript theme={null}
// If your wallet supports One-Click Auth
_walletKit!.onSessionAuthRequest.subscribe(_onSessionAuthRequest);
//
void _onSessionAuthRequest(SessionAuthRequest? event) async {
if (event != null) {
// Process session authentication
// ....
// Redirect back to proposer dapp
try {
await _walletKit.redirectToDapp(
topic: topic,
redirect: event.params.proposer.metadata.redirect,
);
} catch (e) {
...
}
}
}
```
A dapp would call `examplewallet://` (or even better `session.peer?.metadata.redirect?.native` object) from their side when they request to sign a transaction, and given the fact that `session.peer?.metadata.redirect?.native` contains your registered custom scheme (`examplewallet://`) then your wallet will be opened.
**Redirecting back to dapp (proposer) after responding to a sign request:**
```javascript theme={null}
// Your registered request handler for the given requested method will be triggered
Future personalSignRequestHandler(String topic, dynamic parameters) async {
// Process signing requests
// ...
// With the given topic with retrieve the current session data
final session = _walletKit.sessions.get(topic);
// And we get the peer metadata to trigger dapp's redirect value
try {
await _walletKit.redirectToDapp(
topic: topic,
redirect: session!.peer.metadata.redirect,
);
} catch (e) {
...
}
}
```
`launchUrlString()` from [url\_launcher](https://pub.dev/packages/url_launcher) official package was used as an example to explain the mechanism, you can choose whatever other package you would like.
# One-click Auth
Source: https://docs.walletconnect.network/wallet-sdk/flutter/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.

## Handling Authentication Requests
To handle incoming authentication requests, subscribe to the `onSessionAuthRequest` event. This 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.
```javascript theme={null}
// subscribe to onSessionAuthRequest with a handler
_walletKit!.onSessionAuthRequest.subscribe(_onSessionAuthRequest);
//
void _onSessionAuthRequest(SessionAuthRequest? args) {
if (args != null) {
// Process the authentication request here.
// Steps include:
// 1. Populate the authentication payload with the supported chains and methods
// 2. Format the authentication message using the payload and the user's account
// 3. Present the authentication message to the user
// 4. Sign the authentication message(s) to create a verifiable authentication object(s)
// 5. Approve the authentication request with the authentication object(s)
}
}
```
## Authentication Objects/Payloads
```javascript theme={null}
final supportedChains = ['eip155:1', 'eip155:10', 'eip155:137'];
final supportedMethods = ['personal_sign', 'eth_sendTransaction'];
final SessionAuthPayload authPayload = AuthSignature.populateAuthPayload(
authPayload: args.authPayload,
chains: supportedChains,
methods: supportedMethods,
);
final cacaoRequestPayload = CacaoRequestPayload.fromSessionAuthPayload(
newAuthPayload,
);
// Prepare the user's address in CAIP10(https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-10.md) format
final iss = 'eip155:1:0x59e2f66C0E96803206B6486cDb39029abAE834c0';
// Now you can use the authPayload to format the authentication message
final message = _walletKit!.formatAuthMessage(
iss: iss,
cacaoPayload: cacaoRequestPayload,
);
// Present the authentication message to the user
...
```
## 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.
```javascript theme={null}
// Approach 1
// Sign the authentication message(s) to create a verifiable authentication object(s)
final credentials = EthPrivateKey.fromHex('$privateKey');
final signature = credentials.signPersonalMessageToUint8List(
Uint8List.fromList(message.codeUnits),
);
final hexSignature = bytesToHex(signature, include0x: true);
// Build the authentication object(s)
final cacao = AuthSignature.buildAuthObject(
requestPayload: cacaoRequestPayload,
signature: CacaoSignature(
t: CacaoSignature.EIP191,
s: hexSignature,
),
iss: iss,
);
// Approve
await _walletKit!.approveSessionAuthenticate(
id: args.id,
auths: [cacao],
);
// Approach 2
// Note that you can also sign multiple messages for every requested chain/address pair
final List cacaos = [];
for (var chain in newAuthPayload.chains) {
final message = _walletKit!.formatAuthMessage(
iss: iss,
cacaoPayload: cacaoRequestPayload,
);
final credentials = EthPrivateKey.fromHex('$privateKey');
final signature = credentials.signPersonalMessageToUint8List(
Uint8List.fromList(message.codeUnits),
);
final hexSignature = bytesToHex(signature, include0x: true);
final cacao = AuthSignature.buildAuthObject(
requestPayload: cacaoRequestPayload,
signature: CacaoSignature(
t: CacaoSignature.EIP191,
s: hexSignature,
),
iss: iss,
);
cacaos.add(cacao)
}
// Approve
await _walletKit!.approveSessionAuthenticate(
id: args.id,
auths: cacaos,
);
```
## Rejecting Authentication Requests
If the authentication request cannot be approved or if the user chooses to reject it, use the rejectSession method.
```javascript theme={null}
await _walletKit!.rejectSessionAuthenticate(
id: args.id,
reason: Errors.getSdkError(Errors.USER_REJECTED_AUTH).toSignError(),
);
```
## 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.
# Usage
Source: https://docs.walletconnect.network/wallet-sdk/flutter/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 ReownWalletKit 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
* [Formatted Errors](#formatted-errors):
A list of useful error objects to be used
**Don't have a project ID?**
Head over to WalletConnect Dashboard and create a new project now!
## Initialization
To create an instance of ReownWalletKit, you need to pass in the `core` and `metadata` parameters.
```javascript theme={null}
final _walletKit = ReownWalletKit(
core: ReownCore(
projectId: '{YOUR_PROJECT_ID}',
),
metadata: PairingMetadata(
name: 'Example Wallet',
description: 'Example wallet description',
url: 'https://example.com/',
icons: ['https://example.com/logo.png'],
redirect: Redirect(
native: 'examplewallet://',
universal: 'https://reown.com/examplewallet',
),
),
);
```
## 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
On flutter you don't need to worry about Namespace Builder as Flutter SDK would handle that for you and generate a namespace object with the supported ones for you to approve.
All you have to do is make sure you register...
1. **wallet's accounts** with `_walletKit.registerAccount()` for accounts you want events and methods to be enabled on. This is essential if you want to properly form a session object between your wallet and the requester dapp.
2. **request handlers** with `_walletKit.registerRequestHandler()` for methods you want to support on your wallet. Optional but **highly recommended** if you want to seamlessly create a session object, as we will see in the coming section.
3. **events emitters** with `_walletKit.registerEventEmitter()` for events you want to support on your wallet. Optional but recommended if you plan to send events such as `chainChanged` and `accountsChanged`.
And you'll have to do this **for every chain** you want to support on your wallet.
```dart theme={null}
// Quick example:
List supportedChains = ['eip155:1', 'eip155:10', ...];
List walletAddresses = ['0x1234......'];
List supportedEvents = ['chainChanged', 'accountsChanged', ...];
Map get _methodHandlers => {
'personal_sign': personalSignHandler,
'eth_sendTransaction': ethSendTransactionHandler,
};
for (final chainId in supportedChains) {
for (var address in walletAddresses) {
_walletKit!.registerAccount(
chainId: chainId, // CAIP-2 format chain id
accountAddress: address, // 0x.... address
);
}
for (var handler in _methodHandlers.entries) {
_walletKit.registerRequestHandler(
chainId: chainId,
method: handler.key,
handler: handler.value,
);
}
for (final event in supportedEvents) {
_walletKit.registerEventEmitter(
chainId: chainId,
event: event,
);
}
}
```
When a dApp propose a session, with declared namespaces, your wallet will be able to approve an **already generated set of namespaces** based on your registered accounts, methods and events.
You can access this object in **SessionProposalEvent** during `onSessionProposal` event by querying `event.params.generatedNamespaces`. (See [Session Approval](#session-approval) below)
You can choose **not to use** `registerRequestHandler` to configure your supported methods and rather define them during session approval (See [Session Approval](#session-approval) below)
By not using `registerRequestHandler` your methods requests are going to be sent through `onSessionRequest` event subscription.
If you do choose to use `registerRequestHandler` **(highly recommended)** then `onSessionRequest` event subscription is not going to be called.
Flutter SDK provides a handy `MethodsConstants` and `EventsConstants` for already defined set of required and optional values.
### 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
As mentioned before, the `SessionProposalEvent` is emitted when a dapp initiates a new session with your wallet. The event object will include the information about the dapp and requested namespaces. The wallet should display a prompt for the user to approve or reject the session.
To approve a session, subscribe to `onSessionProposal` event and call `approveSession()` passing in the `event.id` and the namespaces object.
```javascript theme={null}
_walletKit.onSessionProposal.subscribe((SessionProposalEvent? event) {
// display a prompt for the user to approve or reject the session
// ....
// If approved
_walletKit.approveSession(
id: event.id,
namespaces: event.params.generatedNamespaces ?? {},
);
});
```
As mentioned before, `namespaces:` should be either `event.params.generatedNamespaces!` if you decided to use `registerRequestHandler` method to configure your supported methods or a `Map` object defined by yourself if you decided **not** to use `registerRequestHandler` method
#### Pairing
The `pair` method initiates a pairing process with a dapp using the given `uri` (QR code from the dapps). To learn more about pairing, checkout out the [docs](https://specs.walletconnect.com/2.0/specs/clients/core/pairing/).
Scan the QR code and parse the URI, and pair with the dapp.\
Upon the first pairing, you will immediately receive `onSessionProposal` and `onAuthRequest` events.
```javascript theme={null}
Uri uri = Uri.parse(scannedUriString);
await _walletKit.pair(uri: uri);
```
### Session Rejection
To reject the request, pass in an error code and reason according to [protocol specs](https://specs.walletconnect.com/2.0/specs/clients/sign/error-codes). See also [Formatted Errors](#formatted-errors) section.
To reject a session:
```javascript theme={null}
_walletKit.onSessionProposal.subscribe((SessionProposalEvent? event) async {
// display a prompt for the user to approve or reject the session
// ....
// If rejected
await _walletKit.rejectSession(
id: event.id,
reason: Errors.getSdkError(Errors.USER_REJECTED).toSignError(),
);
});
```
### Responding to Session requests
To handle a session request, such as `personal_sign`, you have two ways as explained before, and they are mutually exclusive, so, either you use onSessionRequest event subscription or your methods handlers configured with `registerRequestHandler`.
1. The **recommended one** is to register a request handler for the methods and chains you want to support. So let's say your wallet supports `eip155:1` and `eip155:137`. This would translate to:
```javascript theme={null}
final supportedChains = ['eip155:1', 'eip155:137'];
Map supportedMethods = {
'personal_sign': _personalSignHandler,
'eth_sendTransaction': _ethSendTransactionHandler,
};
// Register your handlers as stated in Namespace Builder section
for (var chainId in supportedChains) {
for (var method in supportedMethods.entries) {
_walletKit.registerRequestHandler(
chainId: chainId,
method: method.key,
handler: method.value,
);
}
}
Future _personalSignHandler(String topic, dynamic params) async {
final SessionRequest pendingRequest = _walletKit.pendingRequests.getAll().last;
final int requestId = pendingRequest.id;
// message should arrive encoded
final decoded = hex.decode(params.first.substring(2));
final message = utf8.decode(decoded);
// display a prompt for the user to approve or reject the request
// if approved
if (approved) {
// Your code to sign the message here
final signature = await signMessage(message);
return _walletKit.respondSessionRequest(
topic: topic,
response: JsonRpcResponse(
id: requestId,
jsonrpc: '2.0',
result: signature,
),
);
}
// if rejected
return _walletKit.respondSessionRequest(
topic: topic,
response: JsonRpcResponse(
id: id,
jsonrpc: '2.0',
error: const JsonRpcError(code: 5001, message: 'User rejected method'),
),
);
}
Future _ethSendTransactionHandler(String topic, dynamic params) async {
final SessionRequest pendingRequest = _walletKit.pendingRequests.getAll().last;
final int requestId = pendingRequest.id;
final String chainId = pendingRequest.chainId;
final transaction = (params as List).first as Map;
// display a prompt for the user to approve or reject the request
// if approved
if (approved) {
final signedTx = await sendTransaction(transaction, int.parse(chainId));
// respond to requester
await _walletKit.respondSessionRequest(
topic: topic,
response: JsonRpcResponse(
id: requestId,
jsonrpc: '2.0',
result: signedTx,
),
);
}
// if rejected
return _walletKit.respondSessionRequest(
topic: topic,
response: JsonRpcResponse(
id: id,
jsonrpc: '2.0',
error: const JsonRpcError(code: 5001, message: 'User rejected method'),
),
);
}
```
2. The other way is subscribing to `onSessionRequest` events (if you didn't use `generatedNamespaces` object) and handle the request based on the method that is firing the event.
```javascript theme={null}
_walletKit.onSessionRequest.subscribe(_onSessionRequest);
void _onSessionRequest(SessionRequestEvent? event) async {
if (event != null) {
final id = event.id;
final topic = event.topic;
final method = event.method;
final chainId = event.chainId;
final params = event.params as List;
// message should arrive encoded
final decoded = hex.decode(params.first.substring(2));
final message = utf8.decode(decoded);
// display a prompt for the user to approve or reject the request
// if approved
if (approved) {
// Your code to sign the message here
final signature = ...
return _walletKit.respondSessionRequest(
topic: topic,
response: JsonRpcResponse(
id: id,
jsonrpc: '2.0',
result: signature,
),
);
}
// if rejected
return _walletKit.respondSessionRequest(
topic: topic,
response: JsonRpcResponse(
id: id,
jsonrpc: '2.0',
error: const JsonRpcError(code: 5001, message: 'User rejected method'),
),
);
}
}
```
Remember that if you have handlers registered these are going to be triggered **instead of** the `onSessionRequest` event.
### Updating a Session
If you wish to include new accounts, chains or methods in an existing session, `updateSession` allows you to do so.
You need pass in the `topic` and a new `Namespaces` object that contains all of the existing namespaces as well as the new data you wish to include.
After you update the session, the dapp connected to your wallet will receive a `SessionUpdate` event.
```javascript theme={null}
await _walletKit.updateSession(topic: 'topic', namespaces: '{}')
```
### Extending a Session
To extend the session, call the `extendSession` method and pass in the new `topic`. The `SessionUpdate` event will be emitted from the wallet.
```javascript theme={null}
await _walletKit.extendSession(topic: 'topic')
```
### Session Disconnect
To initiate a session disconnect, call the `disconnectSession` method and pass in the `topic` and a `reason`.
When either the dapp or the wallet disconnects from a session, a `SessionDelete` event will be emitted. It's important to subscribe to this event so you could keep your state up-to-date.
```javascript theme={null}
await _walletKit.disconnectSession(
topic: session.topic,
reason: Errors.getSdkError(Errors.USER_DISCONNECTED).toSignError(),
);
```
Using `disconnectSession()` alone will make the pairing topic persist, i.e, it can be re-used until it expires. If you want to disconnect (remove) the pairing topic as well you would have add another call as follows:
```javascript theme={null}
await _walletKit.core.pairing.disconnect(
topic: pairing.topic,
);
```
#### Supporting session events
In order to support session events, such as `chainChanged` or `accountChanged`, you would have to register an event emitter for such events, for every chain you want to emit an event for (similar to request handlers).
```javascript theme={null}
final supportedChains = ['eip155:1', 'eip155:137'];
const supportedEvents = ['chainChanged', 'accountChanged'];
for (var chainId in supportedChains) {
for (var event in supportedEvents) {
_walletKit.registerEventEmitter(
chainId: chainId,
event: event,
);
}
}
```
And to emit an event, call `emitSessionEvent()` as follows:
```javascript theme={null}
await _walletKit.emitSessionEvent(
topic: session.topic,
chainId: 'eip155:1',
event: SessionEventParams(
name: 'chainChanged',
data: 1,
),
);
```
For a better understanding please check out the [example wallet](https://github.com/reown-com/reown_flutter/tree/master/packages/reown_walletkit/example/lib) and, in particular, the [EVMService](https://github.com/reown-com/reown_flutter/blob/master/packages/reown_walletkit/example/lib/dependencies/chain_services/evm_service.dart) inside of it.
### Formatted Errors
Our SDK exports a variety of ready-made error objects for you to use in the different situations. Most commonly used are...
```javascript theme={null}
// When user rejects session proposal or method request.
final userRejectedError = Errors.getSdkError(Errors.USER_REJECTED).toSignError();
// When the request coming to your wallet can not be unparsed
final malformedRequest = Errors.getSdkError(Errors.MALFORMED_REQUEST_PARAMS).toSignError();
// When user disconnects the session
final userDisconnected = Errors.getSdkError(Errors.USER_DISCONNECTED).toSignError();
// When dapp request an unsupported method to your wallet
final unsupportedMethods = Errors.getSdkError(Errors.UNSUPPORTED_METHODS).toSignError();
```
But you can check the full list of [available errors here](https://github.com/reown-com/reown_flutter/blob/master/packages/reown_core/lib/utils/errors.dart)
# Verify API
Source: https://docs.walletconnect.network/wallet-sdk/flutter/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
To check the Verify API validations and whether or not your user is interacting with potentially malicious dapp, you can do so by accessing the `verifyContext` included in the `SessionProposalEvent`:
```javascript theme={null}
_walletKit!.onSessionProposal.subscribe((SessionProposalEvent? args) {
if (args != null) {
final scamApp = args.verifyContext?.validation.scam;
final invalidApp = args.verifyContext?.validation.invalid;
final validApp = args.verifyContext?.validation.valid;
final unknown = args.verifyContext?.validation.unknown;
}
});
```
# Best Practices
Source: https://docs.walletconnect.network/wallet-sdk/ios/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 the WalletKit client to pair with dapp.
```swift theme={null}
let uri = WalletConnectURI(string: urlString)
if let uri {
Task {
try await WalletKit.instance.pair(uri: uri)
}
}
```
### 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.
```swift theme={null}
WalletKit.instance.pairingStatePublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] isPairing in
self?.showPairingLoading = isPairing
}.store(in: &disposeBag)
```
### 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.
```Swift theme={null}
WalletKit.instance.pairingExpirationPublisher
.receive(on: DispatchQueue.main)
.sink { pairing in
guard !pairing.active else { return }
// let user know that pairing has expired
}.store(in: &publishers)
```
### 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
```swift theme={null}
do {
try await WalletKit.instance.approve(proposalId: proposal.id, namespaces: sessionNamespaces, sessionProperties: proposal.sessionProperties)
// Update UI, remove loader
} catch {
// present error
}
```
Session Reject
```swift theme={null}
do {
try await WalletKit.instance.reject(proposalId: proposal.id, reason: .userRejected)
// Update UI, remove loader
} catch {
// present error
}
```
### 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.
```swift theme={null}
WalletKit.instance.sessionProposalExpirationPublisher.sink { _ in
// let user know that session proposal has expired, update UI
}.store(in: &publishers)
```
### 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.
```swift theme={null}
do {
try await WalletKit.instance.respond(requestId: request.id, signature: signature, from: account)
// update UI -> remove the loader
} catch {
// present error to the user
}
```
### 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.
```swift theme={null}
WalletKit.instance.requestExpirationPublisher.sink { _ in
// let user know that request has expired
}.store(in: &publishers)
```
### 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.
```swift theme={null}
WalletKit.instance.socketConnectionStatusPublisher
.receive(on: DispatchQueue.main)
.sink { status in
switch status {
case .connected:
// ...
case .disconnected:
// ...
}
}.store(in: &publishers)
```
### Expected User flow
### Connection State

# Chain Abstraction
Source: https://docs.walletconnect.network/wallet-sdk/ios/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, use with careful
### Prepare
This method is used to check if chain abstraction is needed. If it is, it will return a response with the necessary transactions.
If it is not, it will return a response with the original transaction.
```swift theme={null}
@available(*, message: "This method is experimental. Use with caution.")
public func prepare(chainId: String, from: FfiAddress, call: Call, accounts: [String], localCurrency: Currency) async throws -> PrepareDetailedResponse
}
```
### Execute
This method is used to execute the chain abstraction operation. The method will handle broadcasting all transactions in the correct order and monitor the cross-chain transfer process. It returns an `ExecuteDetails` object with the transaction status and results.
```swift theme={null}
@available(*, message: "This method is experimental. Use with caution.")
public func execute(uiFields: UiFields, routeTxnSigs: [FfiPrimitiveSignature], initialTxnSig: FfiPrimitiveSignature) async throws -> ExecuteDetails {
}
```
## Usage
When sending a transaction, first check if chain abstraction is needed using the `prepare` method. Call the `execute` method to broadcast the routing and initial transactions and wait for it to be completed.
If the operation is successful, you need to broadcast the initial transaction and await the transaction hash and receipt.
If the operation is not successful, send a JsonRpcError to the dapp and display the error to the user.
```swift theme={null}
let routeResponseSuccess = try await WalletKit.instance.ChainAbstraction.prepare(
chainId: selectedNetwork.chainId.absoluteString,
from: myAccount.address,
call: call,
accounts: caip10Accounts,
localCurrency: .usd
)
switch routeResponseSuccess {
case .success(let routeResponse):
switch routeResponse {
case .available(let UiFileds):
// If the route is available, present a CA transaction flow
for txnDetails in uiFields.route {
let hash = txnDetails.transactionHashToSign
let sig = try! signer.signHash(hash)
routeTxnSigs.append(sig)
}
// sign initial transaction hash
let initialTxHash = uiFields.initial.transactionHashToSign
let initialTxnSig = try! signer.signHash(initialTxHash)
let executeDetails = try await WalletKit.instance.ChainAbstraction.execute(uiFields: uiFields, routeTxnSigs: routeTxnSigs, initialTxnSig: initialTxnSig)
case .notRequired:
// user does not need to move funds from other chains, sign and broadcast original transaction
}
case .error(let routeResponseError):
// Show an error
}
```
For example, check out implementation of chain abstraction in [sample wallet](https://github.com/reown-com/reown-swift/blob/develop/Example/WalletApp/PresentationLayer/Wallet/CATransactionModal/CATransactionPresenter.swift) with Swift.
## 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
Best way to test Chain Abstraction is to use [sample wallet](https://testflight.apple.com/join/09bTAryp).
You can also use the [AppKit laboratory](https://appkit-lab.reown.com/library/wagmi/) and try sending any supported [tokens](/wallet-sdk/features/chain-abstraction#what-are-the-supported-tokens-and-networks%3F) with any chain abstraction-supported wallet.
# Analytics
Source: https://docs.walletconnect.network/wallet-sdk/ios/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.
### Web3Inbox
#### Subscribers - All Time
Total count of all subscribers to your project.
#### Notifications - All Time
Total count of all notifications sent from your project.
#### Subscribers
Daily trend chart illustrating the growth of subscribers.
#### Notifications
Daily trend chart of total notifications received by your subscribers.
#### Messaged Accounts
Daily trend chart of unique wallets that received the notification.
#### Subscribers by notification type
This table shows the total count of subscribers by notification type over a 30 day period.
### 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.network/wallet-sdk/ios/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](/walletguide/explorer).
## 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.network/wallet-sdk/ios/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 via `sessionProperties`. These can be universal (across 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
A wallet's response should indicate which capabilities it actually supports, following 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, the `atomic` capability specifies how the wallet will execute batches of transactions. It has three possible values:
* `supported` - The wallet will execute 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": ["..."],
}
}
}
```
Specify the `scopedProperties` when approving a session:
```json theme={null}
"scopedProperties": {
"eip155": {
"walletService": [{
"url": "",
"methods": ["wallet_getCallsStatus"]
}]
}
}
```
### Response Format
The response format for `wallet_getCallsStatus` varies based on the execution method:
#### 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
}
```
For non-atomic execution, include all transactions in the receipts array, even those that were included on-chain but eventually reverted.
## 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.network/wallet-sdk/ios/installation
WalletConnect Wallet SDK is available via [Swift Package Manager](https://swift.org/package-manager/) or [Cocoapods](https://cocoapods.org/).
You can add a WalletConnect SDK to your project with Swift Package Manager. In order to do that:
1. Open XCode
2. Go to File -> Add Packages
3. Paste the repo GitHub URL: [https://github.com/reown-com/reown-swift](https://github.com/reown-com/reown-swift)
4. Tap Add Package
5. Select WalletConnect check mark
**WARNING**
Cocoapods support may be deprecated soon, use SPM instead.
1. Update Cocoapods spec repos. Type in terminal `pod repo update`
2. Initialize Podfile if needed with `pod init`
3. Add pod to your Podfile:
```ruby theme={null}
pod 'reown-swift'
```
4. Install pods with `pod install`
If you encounter any problems during package installation, you can specify the exact path to the repository
```ruby theme={null}
pod 'reown-swift', :git => 'https://github.com/reown-com/reown-swift.git', :tag => '1.0.0'
```
## 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.network/wallet-sdk/ios/link-mode
Wallet SDK Link Mode is a low latency mechanism for transporting [One-Click Auth](/wallet-sdk/ios/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.Redirect` with a valid universal link and set the `linkMode` property to `true`:
Make sure that [1-Click Auth](/wallet-sdk/ios/one-click-auth) is implemented before enabling Link Mode.
```swift {5,6} theme={null}
let metadata = AppMetadata(
...
redirect: try! AppMetadata.Redirect(
native: "exampleApp://",
universal: "https://example.com/example_wallet",
linkMode: true
)
)
WalletKit.configure(
metadata: metadata,
...
)
```
Once link mode and universal linking are properly configured and the user interacts with a link mode supporting dApp, your wallet will receive requests over universal linking. You must pass these requests to WalletKit so it can process them:
```swift theme={null}
try WalletKit.instance.dispatchEnvelope(url.absoluteString)
```
Ensure to handle incoming universal links in different methods of `AppDelegate` or `SceneDelegate`.
For more information on how to configure universal links for your app, refer to the [Apple Documentation](https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content?language=objc).
For a debugging guide, visit the [Debugging Universal Links](https://developer.apple.com/documentation/technotes/tn3155-debugging-universal-links) page.
You can also find this [article](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app?language=objc) helpful.
# Mobile Linking
Source: https://docs.walletconnect.network/wallet-sdk/ios/mobile-linking
**Note**
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.
#### 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.
#### 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.
Ensure Unique Redirect URIs for Cross-Platform Apps: Cross-platform Dapps should use distinct redirect URIs for their mobile and desktop versions to avoid conflicts.
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.

### 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.

## 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 custom scheme under [`CFBundleURLTypes`](https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleurltypes) key in your Info.plist file.
```ruby theme={null}
CFBundleURLTypesCFBundleTypeRoleEditorCFBundleURLName$(PRODUCT_BUNDLE_IDENTIFIER)CFBundleURLSchemesexamplewallet
```
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](/wallet-sdk/android/best-practices#2-mobile-linking).
## Integration
### iOS Wallet Support
iOS has some more caveats to the integration but we ensure to make it as straightforward as possible. Since its operating system is not designed to handle multiple applications subscribing to the same deep linking schema, we've designed the AppKit to list supporting wallets on our [WalletGuide](https://walletguide.walletconnect.network/) and target specific deep links or universal links for each wallet.
To add your own wallet to the Explorer, login to your [WalletConnect Dashboard](https://dashboard.walletconnect.com/sign-in) account.
```bash theme={null}
# For deep links
examplewallet://wc?uri=wc:94caa59c77dae0dd234b5818fb7292540d017b27d41f7f387ee75b22b9738c94@2?relay-protocol=irn&symKey=ce3a2c7724c03cf1769ba8b1bdedad5414cc7b920aa3fb72112b997d1916266f
# For universal links
https://example.wallet/wc?uri=wc:94caa59c77dae0dd234b5818fb7292540d017b27d41f7f387ee75b22b9738c94@2?relay-protocol=irn&symKey=ce3a2c7724c03cf1769ba8b1bdedad5414cc7b920aa3fb72112b997d1916266f
```
Additionally when there is a signing request triggered by the Dapp it will hit the deep link with an incomplete URI, this should be ignored and not considered valid as it's only used for automatically redirecting the users to approve or reject a signing request.
```bash theme={null}
# For deep links
examplewallet://wc?uri=wc:00e46b69-d0cc-4b3e-b6a2-cee442f97188@2
# For universal links
https://example.wallet/wc?uri=wc:00e46b69-d0cc-4b3e-b6a2-cee442f97188@2
```
***
### WalletConnectRouter
### Overview
WalletConnectRouter simplifies navigation by automatically redirecting users back to the DApp after they've interacted with a wallet via a deep link. This eliminates the need for users to manually navigate back after approving a session or confirming a transaction.
### Key Features
**Automatic Redirection:** By invoking WalletConnectRouter.goBack(uri: "example://")—where "example://" is the DApp's custom scheme as declared in their AppMetadata redirect field—users are seamlessly returned to the DApp.
### Important Consideration
**Mandatory redirect Field:** Starting with WalletConnect SDK version 1.9.5, specifying the redirect field in the AppMetadata object is mandatory to avoid redirection issues.
### Installation and Usage
```swift theme={null}
import WalletConnectRouter
try await Sign.instance.approve(proposalId: , namespaces: )
if let uri = proposal.proposer.redirect?.native {
WalletConnectRouter.goBack(uri: uri)
} else {
// Inform the user to manually return to the DApp
}
```
***
### Limitations
This section outlines some of the known limitations and constraints when using WalletConnect on iOS.
### Redirects on iOS 17 and Above
Automatic redirection to browser-based DApps after wallet interaction is not possible from iOS 17 onwards. Developers should adjust their app's UI to inform users about manual navigation back to the browser.
For iOS versions below 17, `WalletConnectRouter.goBack(uri: uri)` facilitates automatic redirection.
### iOS Universal Links Constraints
**Developers should prefer Deep Linking over Universal Linking.**
In the case of Universal Linking, the user may be redirected to the browser, which may not be the desired behavior. Deep Linking ensures that the user is redirected to the app, providing a seamless experience.
When using WalletConnect on iOS and triggering a wallet interaction (e.g. when sending a transaction or signing a message), you may experience issues where the native app is not opened as expected and a browser navigation occurs instead.
This issue occurs because Universal Links (app links) on iOS will only open the native app when the following rules are followed:
* **The wallet interaction must be triggered by a user-initiated event,** e.g. in a click handler rather than on page load or in an asynchronous callback.
* **The wallet interaction must be triggered as soon as possible within the event handler.** Any preceding asynchronous work (e.g. estimating gas, resolving an ENS name, fetching a nonce) should have already completed before the event handler fires. This may require you to design the user experience around this constraint, preventing users from initiating a wallet interaction until it's ready rather than doing the work lazily.
**Note that even if your own code follows these rules, libraries you depend on may be running their own asynchronous logic before triggering a wallet interaction.** For example, [Ethers asynchronously populates transactions before sending them.](https://docs.ethers.io/v5/api/signer/#Signer-sendTransaction) Known workarounds are documented below, but if you're still experiencing these issues, you should raise them with the relevant library maintainers.
### For Ethers v5 (Legacy)
These are the known workarounds for avoiding app linking issues on iOS when using [Ethers v5](https://docs.ethers.io/v5).
### When sending a transaction
1. **[`signer.sendTransaction`](https://docs.ethers.io/v5/api/signer/#Signer-sendTransaction)
should be avoided in favor of
[`signer.sendUncheckedTransaction`](https://docs.ethers.io/v5/api/providers/jsonrpc-provider/#JsonRpcSigner-sendUncheckedTransaction)**
This avoids an asynchronous call to retrieve the internal block number which
Ethers uses to resolve a complete [`TransactionResponse`](https://docs.ethers.io/v5/api/providers/types/#providers-TransactionResponse)
object.
Note that as a result of this optimization, `sendUncheckedTransaction` returns
a mock transaction response that only contains the `hash` property and a `wait`
method. All other properties are `null`.
2. **The transaction's `to` property should be a plain address rather than an ENS name**
This avoids an asynchronous call to automatically resolve ENS names during the
send process.
If you still want to support ENS name resolution, you should manually run
[`provider.resolveName`](https://docs.ethers.io/v5/api/providers/provider/#Provider-ResolveName)
ahead of time, storing the result before the user attempts to send a transaction.
Do not resolve ENS names in the event handler.
3. **The transaction's `gasLimit` property should be set**
This avoids the asynchronous work performed in `sendTransaction` which automatically
estimates the gas limit if it's missing.
If you still want to use the same gas limit estimation logic from `sendTransaction`,
you should manually run [`provider.estimateGas`](https://docs.ethers.io/v5/api/providers/provider/#Provider-estimateGas)
ahead of time, storing the result before the user attempts to send the transaction.
Do not estimate gas in the event handler.
### When calling a write method on a contract
1. **[`contract.METHOD_NAME`](https://docs.ethers.io/v5/api/contract/contract/#contract-functionsSend)
should be avoided if favor of calling
[`contract.populateTransaction.METHOD_NAME`](https://docs.ethers.io/v5/api/contract/contract/#contract-populateTransaction)
ahead of time, then sending the populated transaction with
[`signer.sendUncheckedTransaction`](https://docs.ethers.io/v5/api/providers/jsonrpc-provider/#JsonRpcSigner-sendUncheckedTransaction).**
2. When sending the populated transaction, you should [follow the same guidelines as regular
transactions](#when-sending-a-transaction) to avoid any asynchronous logic breaking the app link
navigation. Do not populate the contract transaction in the event handler.
### When signing a message
If the message depends on the result of an asynchronous call (e.g. retrieving a nonce when implementing [Sign-In With Ethereum](https://login.xyz)), you should do this work ahead of time, storing the result before the user attempts to sign the message. Do not perform this asynchronous work in the event handler.
# One-click Auth
Source: https://docs.walletconnect.network/wallet-sdk/ios/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.

## Handling Authentication Requests
To handle incoming authentication requests, subscribe to the authenticateRequestPublisher. This 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.
```swift theme={null}
WalletKit.instance.authenticateRequestPublisher
.receive(on: DispatchQueue.main)
.sink { result in
// Process the authentication request here.
// This involves displaying UI to the user.
}
.store(in: &subscriptions) // Assuming `subscriptions` is where you store your Combine subscriptions.
```
## Authentication Objects/Payloads
To interact with authentication requests, first build authentication objects (AuthObject). These objects are crucial for approving authentication requests. This involves:
* **Creating an Authentication Payload** - Generate an authentication payload 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 Implementation:
```swift theme={null}
func buildAuthObjects(request: AuthenticationRequest, account: Account, privateKey: String) throws -> [AuthObject] {
let requestedChains = Set(request.payload.chains.compactMap { Blockchain($0) })
let supportedChains: Set = [Blockchain("eip155:1")!, Blockchain("eip155:137")!, Blockchain("eip155:69")!]
let commonChains = requestedChains.intersection(supportedChains)
let supportedMethods = ["personal_sign", "eth_sendTransaction"]
var authObjects = [AuthObject]()
for chain in commonChains {
let accountForChain = Account(blockchain: chain, address: account.address)!
let supportedAuthPayload = try WalletKit.instance.buildAuthPayload(
payload: request.payload,
supportedEVMChains: Array(commonChains),
supportedMethods: supportedMethods
)
let formattedMessage = try WalletKit.instance.formatAuthMessage(payload: supportedAuthPayload, account: accountForChain)
let signature = // Assume `signMessage` is a function you've implemented to sign messages.
signMessage(message: formattedMessage, privateKey: privateKey)
let authObject = try WalletKit.instance.buildSignedAuthObject(
authPayload: supportedAuthPayload,
signature: signature,
account: accountForChain
)
authObjects.append(authObject)
}
return authObjects
}
```
## Approving Authentication Requests
**Note**
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 AuthObject instances for each supported blockchain, sign the authentication messages, build AuthObjects and call approveSessionAuthenticate with the request ID and the authentication objects.
```swift theme={null}
let session = try await WalletKit.instance.approveSessionAuthenticate(requestId: requestId, auths: authObjects)
```
## Rejecting Authentication Requests
If the authentication request cannot be approved or if the user chooses to reject it, use the rejectSession method.
```swift theme={null}
try await WalletKit.instance.rejectSession(requestId: requestId)
```
## 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.network/wallet-sdk/ios/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 Swift GitHub](https://github.com/WalletConnect/walletconnect-monorepo/tree/v2.0/packages/web3wallet) - Wallet SDK Swift GitHub repository.
### Wallet Resources
To check more in details go and visit our [Wallet SDK Swift implementation app](https://github.com/reown-com/reown-swift/tree/main/Example/WalletApp). Sample Wallet and Dapp sample apps can be found under the Example directory in [Swift's V2 repository](https://github.com/reown-com/reown-swift/tree/main/Example)
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.walletconnect.com/))
### Dapp Resources
Sample Dapp can be found under the Example directory in [Swift's V2 repository](https://github.com/reown-com/reown-swift/tree/main/Example)
You can test your integration against Swift Sample Wallet that is included in the same repo or use the following JS React Wallet:
* [React Wallet Ethers - v2](https://github.com/WalletConnect/web-examples/tree/main/advanced/wallets/react-wallet-v2) ([Demo](https://react-wallet.walletconnect.com/))
# Usage
Source: https://docs.walletconnect.network/wallet-sdk/ios/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.
* [Subscribe for WalletKit Publishers](#subscribe-for-walletkit-publishers)
Publishers available to subscribe to for WalletKit
**Don't have a project ID?**
Head over to WalletConnect Dashboard and create a new project now!
## Initialization
Confirm you have configured the [Network Client](https://docs.reown.com/advanced/api/core/relay) first.
Starting from WalletConnect SDK version 1.9.5, the `redirect` field in the `AppMetadata` object is mandatory. Ensure that the provided value matches your app's URL scheme to prevent redirection-related issues.
Once you're done, in order to initialize a client just call a `configure` method from the WalletKit instance wrapper
```swift theme={null}
let telemetryEnabled = true;
let metadata = AppMetadata(
name: "Example Wallet",
description: "Wallet description",
url: "example.wallet",
icons: ["https://avatars.githubusercontent.com/u/37784886"],
redirect: AppMetadata.Redirect(native: "example://", universal: nil)
)
WalletKit.configure(
metadata: metadata,
crypto: DefaultCryptoProvider(),
// Used for the Push: "echo.walletconnect.com" will be used by default if not provided
pushHost: "echo.walletconnect.com",
// Used for the Push: "APNSEnvironment.production" will be used by default if not provided
environment: .production,
telemetryEnabled: telemetryEnabled
)
```
In order to allow users to receive push notifications you have to communicate with Apple Push Notification service and receive unique device token. Register that token with following method:
```swift theme={null}
try await WalletKit.instance.register(deviceToken: deviceToken)
```
The telemetry feature aims to enhance the reliability and observability of connection flows between decentralized applications (dApps) and wallets. It focuses solely on collecting data related to code execution and error codes, without tracking any sensitive user information such as 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
`AutoNamespaces` 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 ready-to-use `SessionNamespace` object.
```swift theme={null}
public static func build(
sessionProposal: Session.Proposal,
chains: [Blockchain],
methods: [String],
events: [String],
accounts: [Account]
) throws -> [String: SessionNamespace]
```
Example usage
```swift theme={null}
do {
sessionNamespaces = try AutoNamespaces.build(
sessionProposal: proposal,
chains: [Blockchain("eip155:1")!, Blockchain("eip155:137")!],
methods: ["eth_sendTransaction", "personal_sign"],
events: ["accountsChanged", "chainChanged"],
accounts: [
Account(blockchain: Blockchain("eip155:1")!, address: "0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")!,
Account(blockchain: Blockchain("eip155:137")!, address: "0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")!
]
)
} catch let error as AutoNamespacesError {
// reject session proposal if AutoNamespace build function threw
try await reject(proposal: proposal, reason: RejectionReason(from: error))
return
}
// approve session with sessionNamespaces
try await WalletKit.instance.approve(proposalId: proposal.id, namespaces: sessionNamespaces)
```
### 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
```swift theme={null}
WalletKit.instance.approve(
proposalId: "proposal_id",
namespaces: sessionNamespaces
)
```
When session is successfully approved `sessionsPublishers` will publish a `Session`
```swift theme={null}
WalletKit.instance.sessionsPublishers
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.reloadSessions()
}.store(in: &publishers)
```
`Session` object represents an active session connection with a dapp. It contains dapp’s metadata (that you may want to use for displaying an active session to the user), namespaces, and expiry date. There is also a topic property that you will use for linking requests with related sessions.
You can always query settled sessions from the client later with:
```swift theme={null}
WalletKit.instance.getSessions()
```
#### Connect Clients
Your Wallet should allow users to scan a QR code generated by dapps. You are responsible for implementing it on your own.
For testing, you can use our test dapp at: [https://react-app.walletconnect.com/](https://react-app.walletconnect.com/), which is v2 protocol compliant.
Once you derive a URI from the QR code call `pair` method:
```swift theme={null}
try await WalletKit.instance.pair(uri: uri)
```
if everything goes well, you should handle following event:
```swift theme={null}
WalletKit.instance.sessionProposalPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] session in
self?.verifyDapp(session.context)
self?.showSessionProposal(session.proposal)
}.store(in: &publishers)
```
Session proposal is a handshake sent by a dapp and it's purpose is to define a session rules. Handshake procedure is defined by [CAIP-25](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-25.md).
`Session.Proposal` object conveys set of required and optional `ProposalNamespaces` that contains blockchains methods and events. Dapp requests with methods and wallet will emit events defined in namespaces.
`VerifyContext` provides a domain verification information about `Session.Proposal` and `Request`. It consists of origin of a Dapp from where the request has been sent, validation enum that says whether origin is **unknown**, **valid** or **invalid** and verify URL server.
To enable or disable verification find the **Verify SDK** toggle in your project [WalletConnect Dashboard](https://dashboard.walletconnect.com).
```swift theme={null}
public struct VerifyContext: Equatable, Hashable {
public enum ValidationStatus {
case unknown
case valid
case invalid
}
public let origin: String?
public let validation: ValidationStatus
public let verifyUrl: String
}
```
The user will either approve the session proposal (with session namespaces) or reject it. Session namespaces must at least contain requested methods, events and accounts associated with proposed blockchains.
Accounts must be provided according to [CAIP10](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-10.md) specification and be prefixed with a chain identifier. chain\_id + : + account\_address. You can find more on blockchain identifiers in [CAIP2](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-2.md). Our `Account` type meets the criteria.
```
let account = Account("eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb")!
```
Accounts sent in session approval must at least match all requested blockchains.
Example proposal namespaces request:
```json theme={null}
{
"eip155": {
"chains": ["eip155:137", "eip155:1"],
"methods": ["eth_sign"],
"events": ["accountsChanged"]
},
"cosmos": {
"chains": ["cosmos:cosmoshub-4"],
"methods": ["cosmos_signDirect"],
"events": ["someCosmosEvent"]
}
}
```
Example session namespaces response:
```json theme={null}
{
"eip155": {
"accounts": [
"eip155:137:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb",
"eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb"
],
"methods": ["eth_sign"],
"events": ["accountsChanged"]
},
"cosmos": {
"accounts": [
"cosmos:cosmoshub-4:cosmos1t2uflqwqe0fsj0shcfkrvpukewcw40yjj6hdc0"
],
"methods": ["cosmos_signDirect", "personal_sign"],
"events": ["someCosmosEvent", "proofFinalized"]
}
}
```
#### Track Sessions
When your `WalletKit` instance receives requests from a peer it will publish a related event. Set a subscription to handle them.
To track sessions subscribe to `sessionsPublisher` publisher
```swift theme={null}
WalletKit.instance.sessionsPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] sessions in
// Reload UI
}.store(in: &publishers)
```
### Session Rejection
```swift theme={null}
try await WalletKit.instance.reject(requestId: request.id)
```
### Responding to Session requests
After the session is established, a dapp will request your wallet's users to sign a transaction or a message. Requests will be delivered by the following publisher.
```swift theme={null}
WalletKit.instance.sessionRequestPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] session in
self?.verifyDapp(session.context)
self?.showSessionRequest(session.request)
}.store(in: &publishers)
```
When a wallet receives a session request, you probably want to show it to the user. It’s method will be in scope of session namespaces. And it’s params are represented by `AnyCodable` type. An expected object can be derived as follows:
```swift theme={null}
if sessionRequest.method == "personal_sign" {
let params = try! sessionRequest.params.get([String].self)
} else if method == "eth_signTypedData" {
let params = try! sessionRequest.params.get([String].self)
} else if method == "eth_sendTransaction" {
let params = try! sessionRequest.params.get([EthereumTransaction].self)
}
```
Now, your wallet (as it owns your user’s private keys) is responsible for signing the transaction. After doing it, you can send a response to a dapp.
```swift theme={null}
let response: AnyCodable = sign(request: sessionRequest) // Implement your signing method
try await WalletKit.instance.respond(topic: request.topic, requestId: request.id, response: .response(response))
```
### Updating a Session
If you want to update user session's chains, accounts, methods or events you can use session update method.
```swift theme={null}
try await WalletKit.instance.update(topic: session.topic, namespaces: newNamespaces)
```
### Extending a Session
By default, session lifetime is set for 7 days and after that time user's session will expire. But if you consider that a session should be extended you can call:
```swift theme={null}
try await WalletKit.instance.extend(topic: session.topic)
```
Above method will extend a user's session to a week.
### Session Disconnect
For good user experience your wallet should allow users to disconnect unwanted sessions. In order to terminate a session use `disconnect` method.
```swift theme={null}
try await WalletKit.instance.disconnect(topic: session.topic)
```
### Subscribe for WalletKit Publishers
The following publishers are available to subscribe:
```swift theme={null}
public var sessionProposalPublisher: AnyPublisher<(proposal: Session.Proposal, context: VerifyContext?), Never>
public var sessionRequestPublisher: AnyPublisher<(request: Request, context: VerifyContext?), Never>
public var authRequestPublisher: AnyPublisher<(request: AuthRequest, context: VerifyContext?), Never>
public var sessionPublisher: AnyPublisher<[Session], Never>
public var socketConnectionStatusPublisher: AnyPublisher
public var sessionSettlePublisher: AnyPublisher
public var sessionDeletePublisher: AnyPublisher<(String, Reason), Never>
public var sessionResponsePublisher: AnyPublisher
```
### Register Device Token
To register a wallet to receive WalletConnect push notifications, call `register` method and pass the device token received from the `didRegisterForRemoteNotificationsWithDeviceToken` method in the `AppDelegate`.
```swift theme={null}
WalletKit.instance.register(deviceToken: deviceToken, enableEncrypted: true)
```
# Verify API
Source: https://docs.walletconnect.network/wallet-sdk/ios/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
VerifyContext provides a domain verification information about Session.Proposal and Request and is relevant to the `verifyDapp` function.
It consists of origin of an app from where the request has been sent, validation enum that says whether origin is unknown, valid or invalid and verify URL server.
```swift theme={null}
public struct VerifyContext: Equatable, Hashable {
public enum ValidationStatus {
case unknown
case valid
case invalid
}
public let origin: String?
public let validation: ValidationStatus
public let verifyUrl: String
}
```
# WalletConnect Wallet SDK
Source: https://docs.walletconnect.network/wallet-sdk/overview
**Wallet SDK** is WalletConnect's modular SDK for integrating secure, multichain, policy-aligned wallet access directly into your infrastructure. Enable your wallets users to securely connect to any app, powered by the WalletConnect Network
It’s designed for apps, institutions, and custodians that need full control over key management, signing, and access without compromising UX or compliance.
## Quickstart
Get started with Wallet SDK in Android.
Get started with Wallet SDK in iOS.
Get started with Wallet SDK in React Native.
Get started with Wallet SDK in Flutter.
Get started with Wallet SDK in Web.
Get started with Wallet SDK in .NET.
## Features

Some of the key features of Wallet SDK include:
* **Sign API**: Allows dapps to request that the user sign a transaction or message.
* **Auth API**: Allows dapps to verify wallet address ownership through a single signature request, realizing login in one action.
* **Chain agnostic**: Wallet SDK is designed to work with any blockchain, so you can easily support multiple chains without having to write separate integration code.
## Use Cases
* Custom wallet infrastructure.
* Governance flows with onchain or offchain execution.
* Secure DeFi access from custody-controlled environments.
* Seamless cross-chain policy enforcement.
* In-app and in-wallet payments
* Secure in-app signature workflows.
* Access to 65,000+ onchain apps.
* Chain Agnostic by design.
* Fast, frictionless integration.
* Transparent and open source.
* Battle-tested and audit-proven security.
* No dropped connections, no interruptions.
# Best Practices
Source: https://docs.walletconnect.network/wallet-sdk/react-native/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 the WalletKit client to pair with dapp.
```typescript theme={null}
const uri = 'xxx'; // pairing uri
try {
await walletKit.pair({ uri });
} catch (error) {
// some error happens while pairing - check Expected errors section
}
```
### 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.
```typescript theme={null}
core.pairing.events.on("pairing_expire", (event) => {
// pairing expired before user approved/rejected a session proposal
const { topic } = topic;
});
```
### 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.
Approving session
```typescript theme={null}
try {
await walletKit.approveSession(params);
// update UI -> remove the loader
} catch (error) {
// present error to the user
}
```
Rejecting session
```typescript theme={null}
try {
await walletKit.rejectSession(params);
// update UI -> remove the loader
} catch (error) {
// present error to the user
}
```
### 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.
```typescript theme={null}
walletKit.on("proposal_expire", (event) => {
// proposal expired and any modal displaying it should be removed
const { id } = event;
});
```
### 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.
```typescript theme={null}
try {
await walletKit.respondSessionRequest(params);
// update UI -> remove the loader
} catch (error) {
// present error to the user
}
```
### 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.
```typescript theme={null}
walletKit.on("session_request_expire", (event) => {
// request expired and any modal displaying it should be removed
const { id } = event;
});
```
### 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.
```typescript theme={null}
core.relayer.on("relayer_connect", () => {
// connection to the relay server is established
})
core.relayer.on("relayer_disconnect", () => {
// connection to the relay server is lost
})
```
### Expected User flow
### Connection State

# Chain Abstraction
Source: https://docs.walletconnect.network/wallet-sdk/react-native/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.
Make sure you are using canary version of `@reown/walletkit` and `@walletconnect/react-native-compat`
Following are the methods from WalletKit that you will use in implementing chain abstraction.
### Prepare
This method checks if a transaction requires additional bridging transactions beforehand.
```typescript theme={null}
public abstract prepare(params: {
transaction: ChainAbstractionTypes.PartialTransaction;
}): ChainAbstractionTypes.PrepareResponse;
```
### Execute
Helper method used to broadcast the bridging and initial transactions and wait for them to be completed.
```typescript theme={null}
public abstract execute(params: {
orchestrationId: ChainAbstractionTypes.OrchestrationId;
bridgeSignedTransactions: ChainAbstractionTypes.SignedTransaction[];
initialSignedTransaction: ChainAbstractionTypes.SignedTransaction;
}): ChainAbstractionTypes.ExecuteResult;
```
## 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.
Here's a complete example:
```typescript theme={null}
// Check if chain abstraction is needed
const result = await walletKit.chainAbstraction.prepare({
transaction: {
from: transaction.from as `0x${string}`,
to: transaction.to as `0x${string}`,
// @ts-ignore - cater for both input or data
input: transaction.input || (transaction.data as `0x${string}`),
chainId: chainId,
},
});
// Handle the prepare result
if ('success' in result) {
if ('notRequired' in result.success) {
// No bridging required, proceed with normal transaction
console.log('no routing required');
} else if ('available' in result.success) {
const available = result.success.available;
// Sign all bridge transactions and initial transaction
const bridgeTxs = available.route.map(tx => tx.transactionHashToSign);
const signedBridgeTxs = bridgeTxs.map(tx => wallet.signAny(tx));
const signedInitialTx = wallet.signAny(available.initial.transactionHashToSign);
// Execute the chain abstraction
const result = await walletKit.chainAbstraction.execute({
bridgeSignedTransactions: signedBridgeTxs,
initialSignedTransaction: signedInitialTx,
orchestrationId: available.routeResponse.orchestrationId,
});
}
}
```
For example, check out implementation of chain abstraction in [sample wallet](https://github.com/reown-com/react-native-examples/tree/main/wallets/rn_cli_wallet) with React Native CLI.
## 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](/wallet-sdk/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.
# Analytics
Source: https://docs.walletconnect.network/wallet-sdk/react-native/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.
### Web3Inbox
#### Subscribers - All Time
Total count of all subscribers to your project.
#### Notifications - All Time
Total count of all notifications sent from your project.
#### Subscribers
Daily trend chart illustrating the growth of subscribers.
#### Notifications
Daily trend chart of total notifications received by your subscribers.
#### Messaged Accounts
Daily trend chart of unique wallets that received the notification.
#### Subscribers by notification type
This table shows the total count of subscribers by notification type over a 30 day period.
### 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.network/wallet-sdk/react-native/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](/walletguide/explorer).
## 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.network/wallet-sdk/react-native/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 via `sessionProperties`. These can be universal (across 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
A wallet's response should indicate which capabilities it actually supports, following 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, the `atomic` capability specifies how the wallet will execute batches of transactions. It has three possible values:
* `supported` - The wallet will execute 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": ["..."],
}
}
}
```
Specify the `scopedProperties` when approving a session:
```json theme={null}
"scopedProperties": {
"eip155": {
"walletService": [{
"url": "",
"methods": ["wallet_getCallsStatus"]
}]
}
}
```
### Response Format
The response format for `wallet_getCallsStatus` varies based on the execution method:
#### 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
}
```
For non-atomic execution, include all transactions in the receipts array, even those that were included on-chain but eventually reverted.
## 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.network/wallet-sdk/react-native/installation
Install the WalletKit package.
```sh theme={null}
yarn add @reown/walletkit @walletconnect/react-native-compat
```
Additionally add these extra packages to help with async storage, polyfills and the instance of ethers.
```sh theme={null}
yarn add @react-native-async-storage/async-storage @react-native-community/netinfo react-native-get-random-values fast-text-encoding
```
```sh theme={null}
npx expo install expo-application
```
For those using Typescript, we recommend adding these dev dependencies:
```sh theme={null}
yarn add @walletconnect/jsonrpc-types --dev
```
## 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.network/wallet-sdk/react-native/link-mode
Wallet SDK Link Mode is a low latency mechanism for transporting [One-Click Auth](/wallet-sdk/react-native/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.
Make sure that [One-Click Auth](/wallet-sdk/react-native/one-click-auth) is implemented before enabling Link Mode.
### How to enable it:
To support Link Mode add a universal link for your wallet in Cloud project configuration [dashboard](https://dashboard.walletconnect.com/sign-in), configure your Metadata with a valid universal link and set the `linkMode` property to `true`:
```ts {10-11} theme={null}
const walletKit = await WalletKit.init({
core,
metadata: {
name: "Demo React Native Wallet",
description: "Demo RN Wallet to interface with Dapps",
url: "www.reown.com/walletkit",
icons: ["https://your_wallet_icon.png"],
redirect: {
native: "yourwalletscheme://",
universal: "https://example.com/example_wallet",
linkMode: true,
},
},
});
```
### Platform specifics:
To enable universal links for your app, refer to [React Native Documentation](https://reactnative.dev/docs/linking?syntax=ios#enabling-deep-links).
After following the steps provided in the official guide:
1. Ensure that you handle incoming Universal Links in the your `AppDelegate.mm` file.
```swift theme={null}
#import
// Enable deeplinks
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
// Enable Universal Links
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler
{
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
```
2. Open your project in XCode and go to `Settings/Signing & Capabilities/Associated Domains` to add the new domain. After this, `your_project.entitlement` should look like this:
```xml theme={null}
com.apple.developer.associated-domainsapplinks:example.com
```
3. Update/Create your domain's `.well-known/apple-app-site-association` file accordingly.
For more information about supporting universal links, visit the [Supporting associated domains](https://developer.apple.com/documentation/xcode/supporting-associated-domains?language=objc) page
For a debugging guide, visit the [Debugging Universal Links](https://developer.apple.com/documentation/technotes/tn3155-debugging-universal-links) page.
Android Studio provides a tool to configure Universal Links easily, you can read the guide in [Android Documentation](https://developer.android.com/studio/write/app-link-indexing)
After following the steps provided in the guide:
1. Ensure that your Universal Link is properly configured in your app's `AndroidManifest.xml` file with the `autoVerify` set to `true`. It should look similar to this:
```xml theme={null}
```
2. Update/Create your domains's `.well-known/assetlinks.json` file accordingly
For more information on how to configure universal links for your app, refer to [Android Documentation](https://developer.android.com/studio/write/app-link-indexing).
For testing the configured universal link to app content check [this](https://developer.android.com/training/app-links/deep-linking#testing-filters) documentation page.
Once everything is properly configured, and the user interacts with a Link Mode-supporting dApp, your wallet will receive requests through it.
# Mobile Linking
Source: https://docs.walletconnect.network/wallet-sdk/react-native/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.
### 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.
## Platform preparations
Since React Native leverages on native APIs, you must follow iOS and Android steps for each native platform
More information in official documentation: [https://reactnative.dev/docs/linking?syntax=android#enabling-deep-links](https://reactnative.dev/docs/linking?syntax=android#enabling-deep-links)
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](/wallet-sdk/android/best-practices#2-mobile-linking).
## Integration
In order to redirect to the Dapp, you'll need to use `Linking` from `react-native` and call `openURL()` method with the Dapp scheme that comes in the proposal metadata.
```js theme={null}
import { Linking } from "react-native";
async function onApprove(proposal, namespaces) {
const session = await walletKit.approveSession({
id: proposal.id,
namespaces,
});
const dappScheme = session.peer.metadata.redirect?.native;
if (dappScheme) {
Linking.openURL(dappScheme);
} else {
// Inform the user to manually return to the DApp
}
}
```
# One-click Auth
Source: https://docs.walletconnect.network/wallet-sdk/react-native/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.
## Handling Authentication Requests
To handle incoming authentication requests, subscribe to the `session_authenticate` event. This 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.
```typescript theme={null}
walletKit.on("session_authenticate", async (payload) => {
// Process the authentication request here.
// Steps include:
// 1. Populate the authentication payload with the supported chains and methods
// 2. Format the authentication message using the payload and the user's account
// 3. Present the authentication message to the user
// 4. Sign the authentication message(s) to create a verifiable authentication object(s)
// 5. Approve the authentication request with the authentication object(s)
});
```
## Authentication Payload
```typescript theme={null}
import { populateAuthPayload } from "@walletconnect/utils";
// EVM chains that your wallet supports
const supportedChains = ["eip155:1", "eip155:2", 'eip155:137'];
// EVM methods that your wallet supports
const supportedMethods = ["personal_sign", "eth_sendTransaction", "eth_signTypedData"];
// Populate the authentication payload with the supported chains and methods
const authPayload = populateAuthPayload({
authPayload: payload.params.authPayload,
chains: supportedChains,
methods: supportedMethods,
});
// Prepare the user's address in CAIP10(https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-10.md) format
const iss = `eip155:1:0x0Df6d2a56F90e8592B4FfEd587dB3D5F5ED9d6ef`;
// Now you can use the authPayload to format the authentication message
const message = walletKit.formatAuthMessage({
request: authPayload,
iss
});
// Present the authentication message to the user
...
```
## 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.
```typescript theme={null}
// Approach 1
// Sign the authentication message(s) to create a verifiable authentication object(s)
const signature = await cryptoWallet.signMessage(message, privateKey);
// Build the authentication object(s)
const auth = buildAuthObject(
authPayload,
{
t: "eip191",
s: signature,
},
iss
);
// Approve
await walletKit.approveSessionAuthenticate({
id: payload.id,
auths: [auth],
});
// Approach 2
// Note that you can also sign multiple messages for every requested chain/address pair
const auths = [];
authPayload.chains.forEach(async (chain) => {
const message = walletKit.formatAuthMessage({
request: authPayload,
iss: `${chain}:${cryptoWallet.address}`,
});
const signature = await cryptoWallet.signMessage(message);
const auth = buildAuthObject(
authPayload,
{
t: "eip191", // signature type
s: signature,
},
`${chain}:${cryptoWallet.address}`
);
auths.push(auth);
});
// Approve
await walletKit.approveSessionAuthenticate({
id: payload.id,
auths,
});
```
## Rejecting Authentication Requests
If the authentication request cannot be approved or if the user chooses to reject it, use the rejectSession method.
```typescript theme={null}
import { getSdkError } from "@walletconnect/utils";
await walletKit.rejectSessionAuthenticate({
id: payload.id,
reason: getSdkError("USER_REJECTED"), // or choose a different reason if applicable
});
```
## Testing One-click Auth
You can use [AppKit Labs](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.network/wallet-sdk/react-native/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 React Native GitHub](https://github.com/WalletConnect/walletconnect-monorepo/tree/v2.0/packages/web3wallet) - Wallet SDK React Native GitHub repository.
### Wallet Resources
[Wallet SDK](https://medium.com/walletconnect/simplifying-integration-for-wallet-developers-with-the-new-web3wallet-sdk-8706b69e149c) simplifies the integration process for wallet developers by combining our Sign and Auth APIs. Please note that only V2 [WCURIs](https://specs.walletconnect.com/2.0/specs/clients/core/pairing/pairing-uri) will work with this SDK, as V1 is being deprecated by June 28th, 2023.
#### Expo
Experimental: For Expo, we have an unofficial npx starter command. `newWallet` represents the name of your project.
```bash theme={null}
npx create-wc-wallet-expo@latest newWallet
```
This downloads an Expo template with Wallet SDK installed. More information available in this [tutorial](https://medium.com/walletconnect/how-to-build-a-wallet-in-react-native-with-the-web3wallet-sdk-b6f57bf02f9a)
### Dapp Resources
If you need to test your app's integration, you can use one of our following demo wallets and/or 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.walletconnect.com/))
# Usage
Source: https://docs.walletconnect.network/wallet-sdk/react-native/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.
## Cloud Configuration
Create a new project on WalletConnect Dashboard at [https://dashboard.walletconnect.com](https://dashboard.walletconnect.com) and obtain a new project ID.
**Don't have a project ID?**
Head over to WalletConnect Dashboard and create a new project now!
## Initialization
`@walletconnect/react-native-compat` must be installed and imported before any `@reown/*` dependencies for proper React Native polyfills.
```ts theme={null}
import "@walletconnect/react-native-compat";
// Other imports
```
Create a new instance from `Core` and initialize it with your `projectId`. Next, create a WalletKit instance by calling `init` on `WalletKit`. Passing in the options object containing metadata about the app.
The `pair` function will help us pair between the dapp and wallet and will be used shortly.
```javascript theme={null}
import { Core } from "@walletconnect/core";
import { WalletKit } from "@reown/walletkit";
const core = new Core({
projectId: process.env.PROJECT_ID,
});
const walletKit = await WalletKit.init({
core, // <- pass the shared `core` instance
metadata: {
name: "Demo React Native Wallet",
description: "Demo RN Wallet to interface with Dapps",
url: "www.walletconnect.com",
icons: ["https://your_wallet_icon.png"],
redirect: {
native: "yourwalletscheme://",
},
},
});
```
### Core Instance Sharing
Starting from newer versions of WalletKit, Core instances are shared globally by default to optimize resource usage. This means that multiple Core instances with the same configuration will reuse the same underlying Core.
**For parallel testing scenarios** where you need isolated Core instances, you have two options:
#### Option 1: Use customStoragePrefix
```javascript theme={null}
const core = new Core({
projectId: process.env.PROJECT_ID,
customStoragePrefix: `test-${Date.now()}`, // Unique prefix for each test
});
```
Don't use randomly generated `customStoragePrefix` in production - this will cause the client to create new storage each time it is initialized. The client will not be able to persist/read existing data and all existing sessions will be lost after each reload.
#### Option 2: Disable global Core sharing
```javascript theme={null}
// Set environment variable before initializing Core
process.env.DISABLE_GLOBAL_CORE = "true";
const core = new Core({
projectId: process.env.PROJECT_ID,
});
```
The global Core sharing behavior was introduced to prevent resource waste when multiple SDK instances are created. If you're running parallel tests and experiencing cross-test interference, use one of the solutions above to ensure proper test isolation.
## 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 (and @walletconnect/utils) 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 user's `chains/methods/events/accounts` and returns ready-to-use `namespaces` object.
```javascript theme={null}
// util params
{
proposal: ProposalTypes.Struct; // the proposal received by `.on("session_proposal")`
supportedNamespaces: Record< // your Wallet's supported namespaces
string, // the supported namespace key e.g. eip155
{
chains: string[]; // your supported chains in CAIP-2 format e.g. ["eip155:1", "eip155:2", ...]
methods: string[]; // your supported methods e.g. ["personal_sign", "eth_sendTransaction"]
events: string[]; // your supported events e.g. ["chainChanged", "accountsChanged"]
accounts: string[] // your user's accounts in CAIP-10 format e.g. ["eip155:1:0x453d506b1543dcA64f57Ce6e7Bb048466e85e228"]
}
>;
};
```
Example usage
```javascript theme={null}
// import the builder util
import { WalletKit, WalletKitTypes } from '@reown/walletkit'
import { buildApprovedNamespaces, getSdkError } from '@walletconnect/utils'
async function onSessionProposal({ id, params }: WalletKitTypes.SessionProposal){
try{
// ------- namespaces builder util ------------ //
const approvedNamespaces = buildApprovedNamespaces({
proposal: params,
supportedNamespaces: {
eip155: {
chains: ['eip155:1', 'eip155:137'],
methods: ['eth_sendTransaction', 'personal_sign'],
events: ['accountsChanged', 'chainChanged'],
accounts: [
'eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb',
'eip155:137:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb'
]
}
}
})
// ------- end namespaces builder util ------------ //
const session = await walletKit.approveSession({
id,
namespaces: approvedNamespaces
})
}catch(error){
// use the error.message to show toast/info-box letting the user know that the connection attempt was unsuccessful
....
await walletKit.rejectSession({
id: proposal.id,
reason: getSdkError("USER_REJECTED")
})
}
}
walletKit.on('session_proposal', onSessionProposal)
```
If your wallet supports multiple namespaces e.g. `eip155`,`cosmos` & `near`
Your `supportedNamespaces` should look like the following example.
```javascript theme={null}
// ------- namespaces builder util ------------ //
const approvedNamespaces = buildApprovedNamespaces({
proposal: params,
supportedNamespaces: {
eip155: {...},
cosmos: {...},
near: {...}
},
});
// ------- end namespaces builder util ------------ //
```
### Get Active Sessions
You can get the wallet active sessions using the `getActiveSessions` function.
```js theme={null}
const activeSessions = walletKit.getActiveSessions();
```
### 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
In order to connect with a dapp, you will need to receive a WalletConnect URI (WCURI) and this will talk to our protocol to facilitate a pairing session. Therefore, you will need a test dapp in order to communicate with the wallet. We recommend testing with our [React V2 Dapp](https://react-app.walletconnect.com/) as this is the most up-to-date development site.
In order to capture the WCURI, recommend having some sort of state management you will pass through a `TextInput` or QRcode instance.
The `session_proposal` event is emitted when a dapp initiates a new session with a user's wallet. The event will include a `proposal` object with information about the dapp and requested permissions. The wallet should display a prompt for the user to approve or reject the session. If approved, call `approveSession` and pass in the `proposal.id` and requested `namespaces`.
The `pair` method initiates a WalletConnect pairing process with a dapp using the given `uri` (QR code from the dapps). To learn more about pairing, checkout out the [docs](https://specs.walletconnect.com/2.0/specs/clients/core/pairing/).
```javascript theme={null}
import { getSdkError } from "@walletconnect/utils";
// Approval: Using this listener for sessionProposal, you can accept the session
walletKit.on("session_proposal", async (proposal) => {
const session = await walletKit.approveSession({
id: proposal.id,
namespaces,
});
});
// Call this after WCURI is received
await walletKit.pair({ uri: wcuri });
```
### Session Rejection
You can use the `getSDKError` function, which is available in the `@walletconnect/utils` for the rejection function [library](https://github.com/WalletConnect/walletconnect-monorepo/tree/v2.0/packages/utils).
```javascript theme={null}
import { getSdkError } from "@walletconnect/utils";
// Reject: Using this listener for sessionProposal, you can reject the session
walletKit.on("session_proposal", async (proposal) => {
await walletKit.rejectSession({
id: proposal.id,
reason: getSdkError("USER_REJECTED_METHODS"),
});
});
```
### Responding to Session requests
The `session_request` event is triggered by a dapp when it needs the wallet to perform a specific action, such as signing a transaction. The event contains a `topic` and a `request` object, which will vary depending on the action requested.
To respond to the request, the wallet can access the `topic` and `request` object by destructuring them from the event payload. To see a list of possible `request` and `response` objects, refer to the relevant JSON-RPC Methods for [Ethereum](https://docs.reown.com/advanced/multichain/rpc-reference/ethereum-rpc), [Solana](https://docs.reown.com/advanced/multichain/rpc-reference/solana-rpc), [Cosmos](https://docs.reown.com/advanced/multichain/rpc-reference/cosmos-rpc), or [Stellar](https://docs.reown.com/advanced/multichain/rpc-reference/stellar-rpc).
As an example, if the dapp requests a `personal_sign` method, the wallet can extract the `params` array from the `request` object. The first item in the array is the hex version of the message to be signed, which can be converted to UTF-8 and assigned to a `message` variable. The second item in `params` is the user's wallet address.
To sign the message, the wallet can use the `wallet.signMessage` method and pass in the message. The signed message, along with the `id` from the event payload, can then be used to create a `response` object, which can be passed into `respondSessionRequest`.
The wallet then signs the message. `signedMessage`, along with the `id` from the event payload, can then be used to create a `response` object, which can be passed into `respondSessionRequest`.
```javascript theme={null}
walletKit.on("session_request", async (event) => {
const { topic, params, id } = event;
const { request } = params;
const requestParamsMessage = request.params[0];
// convert `requestParamsMessage` by using a method like hexToUtf8
const message = hexToUtf8(requestParamsMessage);
// sign the message
const signedMessage = await wallet.signMessage(message);
const response = { id, result: signedMessage, jsonrpc: "2.0" };
await walletKit.respondSessionRequest({ topic, response });
});
```
To reject a session request, the response should be similar to this.
```javascript theme={null}
const response = {
id,
jsonrpc: "2.0",
error: {
code: 5000,
message: "User rejected.",
},
};
```
### Updating a Session
The `session_update` event is emitted from the wallet when the session is updated by calling `updateSession`. To update a session, pass in the [topic](https://docs.reown.com/advanced/glossary#topics) and the new namespace.
```javascript theme={null}
await walletKit.updateSession({ topic, namespaces: newNs });
```
### Extending a Session
To extend the session, call the `extendSession` method and pass in the new `topic`. The `session_update` event will be emitted from the wallet.
```javascript theme={null}
await walletKit.extendSession({ topic });
```
### Session Disconnect
When either the dapp or the wallet disconnects from a session, a `session_delete` event will be emitted. It's important to subscribe to this event so you could keep your state up-to-date.
To initiate a session disconnect, call the `disconnectSession` method and pass in the `topic` and `reason`. You can use the `getSDKError` utility function, which is available in the `@walletconnect/utils` [library](https://github.com/WalletConnect/walletconnect-monorepo/tree/v2.0/packages/utils).
```javascript theme={null}
await walletKit.disconnectSession({
topic,
reason: getSdkError("USER_DISCONNECTED"),
});
```
### Emitting Session Events
To emit session events, call the `emitSessionEvent` and pass in the params. If you wish to switch to chain/account that is not approved (missing from `session.namespaces`) you will have to update the session first. In the following example, the wallet will emit `session_event` that will instruct the dapp to switch the active accounts.
```javascript theme={null}
await walletKit.emitSessionEvent({
topic,
event: {
name: "accountsChanged",
data: ["0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb"],
},
chainId: "eip155:1",
});
```
In the following example, the wallet will emit `session_event` when the wallet switches chains.
```javascript theme={null}
await walletKit.emitSessionEvent({
topic,
event: {
name: "chainChanged",
data: 1,
},
chainId: "eip155:1",
});
```
# Verify API
Source: https://docs.walletconnect.network/wallet-sdk/react-native/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
To check the Verify API validations and whether or not your user is interacting with potentially malicious app, you can do so by accessing the `verifyContext` included in the request payload.
```javascript theme={null}
...
walletKit.on("auth_request", async (authRequest) => {
const { verifyContext } = authRequest
const validation = verifyContext.verified.validation // can be VALID, INVALID or UNKNOWN
const origin = verifyContext.verified.origin // the actual verified origin of the request
const isScam = verifyContext.verified.isScam // true if the domain is flagged as malicious
// if the domain is flagged as malicious, you should warn the user as they may lose their funds - check the `Threat` case for more info
if(isScam) {
// show a warning screen to the user
// and proceed only if the user accepts the risk
}
switch(validation) {
case "VALID":
// proceed with the request - check the `Domain match` case for more info
break
case "INVALID":
// show a warning dialog to the user - check the `Mismatch` case for more info
// and proceed only if the user accepts the risk
break
case "UNKNOWN":
// show a warning dialog to the user - check the `Unverified` case for more info
// and proceed only if the user accepts the risk
break
}
})
```
For live demo examples of the intended Verify API flows, check out our demo apps:
* [Demo Web Wallet](https://react-wallet.walletconnect.com)
* [Demo React Native Wallet](https://github.com/WalletConnect/react-native-examples/tree/main/wallets/rn_cli_wallet)
* [Demo App](https://react-app.walletconnect.com/) - you can toggle between the verify states by clicking on the `gear` & selecting the decided Validation before connecting to the wallet
* [Demo Malicious App](https://malicious-app-verify-simulation.vercel.app/) - this app is flagged as malicious and will have the `isScam` parameter set to `true` in the `verifyContext` of the request
# Upgrade from Web3Wallet to WalletKit for Android
Source: https://docs.walletconnect.network/wallet-sdk/upgrade/from-web3wallet-android
## Upgrade to WalletKit
This upgrade guide helps developers transition from using the Web3Wallet library to the WalletKit within reown-kotlin. The guide involves updating imports, modifying class references and updating artefacts dependencies. Follow these steps to ensure a smooth migration.
### Step 1. Update the Repository Dependencies
The Web3Wallet library has been deprecated and moved to a new repository under the reown-com organization. Update your dependencies to use WalletKit:
```swift theme={null}
/* highlight-delete-start */
- dependencies {
- implementation(platform("com.walletconnect:android-bom:{BOM version}"))
- implementation("com.walletconnect:android-core")
- implementation("com.walletconnect:web3wallet")
- }
/* highlight-delete-end */
/* highlight-add-start */
+ dependencies {
+ implementation(platform("com.reown:android-bom:{BOM version}"))
+ implementation("com.reown:android-core")
+ implementation("com.reown:walletkit")
+ }
/* highlight-add-end */
```
### Step 2. Update Imports in Your Code
All references to Web3Wallet in your import statements should be updated to use WalletKit.
```swift theme={null}
/* highlight-delete-start */
- import com.walletconnect.android.*
- import com.walletconnect.web3.wallet.*
/* highlight-delete-end */
/* highlight-add-start */
+ import com.reown.android.*
+ import com.reown.walletkit.*
/* highlight-add-end */
```
### Step 3. Update Class Name
The singleton instance for Web3Wallet has been replaced with WalletKit. Update all instances where Web3Wallet is used with WalletKit.
```swift theme={null}
/* highlight-delete-start */
- Web3Wallet.initialize(Wallet.Params.Init(core = CoreClient), onSuccess, onError)
- Web3Wallet.approveSession(approveProposal, onSuccess, onError)
/* highlight-delete-end */
/* highlight-add-start */
+ WalletKit.initialize(Wallet.Params.Init(core = CoreClient), onSuccess, onError)
+ WalletKit.approveSession(approveProposal, onSuccess, onError)
/* highlight-add-end */
```
### Step 4. Update ProGuard file rules
If you have ProGuard rules defined remember to update
```swift theme={null}
/* highlight-delete-start */
- -keep class com.walletconnect.web3.wallet.client.Wallet$Model { *; }
- -keep class com.walletconnect.web3.wallet.client.Wallet { *; }
/* highlight-delete-end */
/* highlight-add-start */
+ -keep class com.reown.walletkit.client.Wallet$Model { *; }
+ -keep class com.reown.walletkit.client.Wallet { *; }
/* highlight-add-end */
```
### Step 5. Test Your Changes
After updating all references to Web3Wallet to use WalletKit, thoroughly test your application to ensure that all functionalities work as expected.
## Pairing Expiry
Currently, Dapps create a new pairing whenever the user selects the **"Connect Wallet"** button, instead of reusing existing pairings. Although pairings were not intended to be reused, they were being persisted for 30 days, causing unnecessary resource usage for both Dapps and wallet clients, including redundant socket connections.
This led to an accumulation of stale pairings in wallets, resulting in degraded efficiency and increased resource consumption. To address this issue, we have introduced changes to how pairings are managed to ensure more efficient connection handling.
Pairings were never intended to be listed in the wallet, and wallets should only display active sessions to users.
## WebSocket Connection Handling
We've optimized the WebSocket connection management to improve performance and resource utilization. The SDK will now establish a WebSocket connection only when there's an explicit intention to send a request or subscribe to a topic. If none of these conditions are met, the WebSocket connection will remain closed by default.
### What's Changed?
Previous Behavior: The SDK automatically initiated a WebSocket connection upon startup, regardless of active sessions or pending actions.
New Behavior: The SDK delays establishing a WebSocket connection until it's necessary based on the app's activities.
### Why This Change?
This adjustment reduces unnecessary network traffic and conserves device resources, leading to better performance and battery life, especially important for mobile applications.
### Impact on Your Application
Disconnected State on Launch: Apps without active sessions at launch will start with the WebSocket in a disconnected state.
UI Elements Depending on WebSocket: Buttons or features that rely on an active WebSocket connection may not function until the connection is established.
### Steps for Migration
Wallets are no longer expected to handle pairing-related methods. If your wallet has been listing pairings, please replace this with listing active sessions instead.
# Upgrade from Web3Wallet to WalletKit for Flutter
Source: https://docs.walletconnect.network/wallet-sdk/upgrade/from-web3wallet-flutter
## Upgrade to WalletKit
This document outlines the steps to migrate from the old `walletconnect_flutter_v2` package to the new `reown_walletkit` packages in your Flutter project.
### Step 1. Replace the corresponding dependency
Remove `walletconnect_flutter_v2` dependency from pubspec.yaml and add `reown_walletkit`:
```dart theme={null}
/* highlight-delete-start */
walletconnect_flutter_v2: ^X.Y.Z
/* highlight-delete-end */
/* highlight-add-start */
reown_walletkit: ^1.0.0
/* highlight-add-end */
```
Run `flutter clean && flutter pub get` after replacing the packages
Then replace the imports...
```dart theme={null}
/* highlight-delete-start */
import 'package:walletconnect_flutter_v2/walletconnect_flutter_v2.dart';
/* highlight-delete-end */
/* highlight-add-start */
import 'package:reown_walletkit/reown_walletkit.dart';
/* highlight-add-end */
```
### Step 2. Update main classes
### Final notes
* Ensure that you have updated all relevant configurations and imports in your project to reflect the changes from Web3Wallet to WalletKit.
* Test your application thoroughly to ensure that the migration has been successful and that all functionality is working as expected.
* Check our [WalletKit example for Flutter](https://github.com/reown-com/reown_flutter/tree/master/packages/reown_walletkit/example/) to compare with your implementation in case you are having issues
# Upgrade from Web3Wallet to WalletKit for iOS
Source: https://docs.walletconnect.network/wallet-sdk/upgrade/from-web3wallet-ios
## Upgrade to WalletKit
This upgrade guide helps developers transition from using the Web3Wallet library to the WalletKit within reown-swift. The guide involves updating import statements, modifying instance references, changing configuration methods, and updating repository URLs for CocoaPods and Swift Package Manager (SPM).
### Step 1. Update the Repository URL
The Web3Wallet library has been moved to a new repository under the reown-com organization. If you are using Swift Package Manager (SPM) to manage dependencies, update your Package.swift file to point to the new repository:
```swift theme={null}
/* highlight-delete-start */
- .package(url: "https://github.com/WalletConnect/WalletConnectSwiftV2", from: "1.0.0"),
/* highlight-delete-end */
/* highlight-add-start */
+ .package(url: "https://github.com/reown-com/reown-swift", from: "1.0.0"),
/* highlight-add-end */
```
### Step 2. Update Imports in Your Code
All references to Web3Wallet in your import statements should be updated to use WalletKit.
```swift theme={null}
/* highlight-delete-start */
- import Web3Wallet
/* highlight-delete-end */
/* highlight-add-start */
+ import WalletKit
/* highlight-add-end */
```
### Step 3. Update Instance Access and Method Calls
The singleton instance access for Web3Wallet has been replaced with WalletKit. Update all instances where Web3Wallet.instance is used to WalletKit.instance.
```swift theme={null}
/* highlight-delete-start */
- Web3Wallet.instance.authRequestPublisher.sink { (id, result) in
- // Your code here
- }
/* highlight-delete-end */
/* highlight-add-start */
+ WalletKit.instance.authRequestPublisher.sink { (id, result) in
+ // Your code here
+ }
/* highlight-add-end */
```
### Step 4. Update Configuration Method
The configure method has been updated to reflect the new branding. Replace calls to Web3Wallet.configure with WalletKit.configure.
```swift theme={null}
/* highlight-delete-start */
- Web3Wallet.configure(
- ...
- )
/* highlight-delete-end */
/* highlight-add-start */
+ WalletKit.configure(
+ ...
+ )
/* highlight-add-end */
```
### Step 5. Update CocoaPods Podspec
If you are using CocoaPods to manage dependencies, update your Podfile to use the new library name.
```swift theme={null}
/* highlight-delete-start */
- pod 'Web3Wallet', '~> 1.0'
/* highlight-delete-end */
/* highlight-add-start */
+ pod 'WalletKit', '~> 1.0'
/* highlight-add-end */
```
### Step 6. Test Your Changes
After updating all references to Web3Wallet to use WalletKit, thoroughly test your application to ensure that all functionalities work as expected.
## Pairing Expiry
Currently, Dapps create a new pairing whenever the user selects the **"Connect Wallet"** button, instead of reusing existing pairings. Although pairings were not intended to be reused, they were being persisted for 30 days, causing unnecessary resource usage for both Dapps and wallet clients, including redundant socket connections.
This led to an accumulation of stale pairings in wallets, resulting in degraded efficiency and increased resource consumption. To address this issue, we have introduced changes to how pairings are managed to ensure more efficient connection handling.
Pairings were never intended to be listed in the wallet, and wallets should only display active sessions to users.
## WebSocket Connection Handling
We've optimized the WebSocket connection management to improve performance and resource utilization. The SDK will now establish a WebSocket connection only when there's an explicit intention to send a request or subscribe to a topic. If none of these conditions are met, the WebSocket connection will remain closed by default.
### What's Changed?
Previous Behavior: The SDK automatically initiated a WebSocket connection upon startup, regardless of active sessions or pending actions.
New Behavior: The SDK delays establishing a WebSocket connection until it's necessary based on the app's activities.
### Why This Change?
This adjustment reduces unnecessary network traffic and conserves device resources, leading to better performance and battery life, especially important for mobile applications.
### Impact on Your Application
Disconnected State on Launch: Apps without active sessions at launch will start with the WebSocket in a disconnected state.
UI Elements Depending on WebSocket: Buttons or features that rely on an active WebSocket connection may not function until the connection is established.
### Steps for Migration
Wallets are no longer expected to handle pairing-related methods. If your wallet has been listing pairings, please replace this with listing active sessions instead.
# Upgrade from Web3Wallet to WalletKit for React Native
Source: https://docs.walletconnect.network/wallet-sdk/upgrade/from-web3wallet-react-native
## Upgrade to WalletKit
This document outlines the steps to migrate from the old `@walletconnect/web3wallet` package to the new `@reown/walletkit` packages in your project.
### Step 1. Update your package.json
Replace your existing `@walletconnect/web3wallet` dependency with `@reown/walletkit`:
```json theme={null}
/* highlight-delete-start */
"@walletconnect/web3wallet": "^x.y.z"
/* highlight-delete-end */
/* highlight-add-start */
"@reown/walletkit": "^1.0.0"
/* highlight-add-end */
```
### Step 2. Install `@reown/walletkit`
Run `npm install` (or your preferred package manager command) to install the new package.
### Step 3. Update your imports
Replace the imports in your project:
```javascript theme={null}
/* highlight-delete-start */
import { Web3Wallet } from "@walletconnect/web3wallet";
/* highlight-delete-end */
/* highlight-add-start */
import { WalletKit } from "@reown/walletkit";
/* highlight-add-end */
```
and your initialization to use the new package:
```javascript theme={null}
/* highlight-delete-start */
await Web3Wallet.init()
/* highlight-delete-end */
/* highlight-add-start */
await WalletKit.init()
/* highlight-add-end */
```
If you're using additional imports from `@walletconnect/web3wallet`, you can replace them with their corresponding version from `@reown/walletkit` such as:
```javascript theme={null}
/* highlight-delete-start */
import { IWeb3Wallet } from "@walletconnect/web3wallet";
/* highlight-delete-end */
/* highlight-add-start */
import { IWalletKit } from "@reown/walletkit";
/* highlight-add-end */
```
## You're all set!
### Final Notes
* public API documentation can be found [here](/wallet-sdk/web/usage)
* `auth_request` is deprecated in favor of `session_authenticate`. Docs can be found [here](/wallet-sdk/web/one-click-auth)
# Upgrade from Web3Wallet to Reown WalletKit
Source: https://docs.walletconnect.network/wallet-sdk/upgrade/from-web3wallet-to-reown
## Upgrade Platform list
Upgrade to WalletKit in Web.
Upgrade to WalletKit in React Native.
Upgrade to WalletKit in Flutter.
Upgrade to WalletKit in Android.
Migrate to WalletKit in iOS.
Upgrade to WalletKit in .NET.
# Upgrade from Web3Wallet to WalletKit for .NET
Source: https://docs.walletconnect.network/wallet-sdk/upgrade/from-web3wallet-unity
## Upgrade to WalletKit
This document outlines the steps to migrate from the old `WalletConnect.Web3Wallet` package to the new `Reown.WalletKit` package in your .NET project.
### Step 1. Replace the corresponding dependency in your project file
```xml theme={null}
/* highlight-delete-start */
/* highlight-delete-end */
/* highlight-add-start */
/* highlight-add-end */
```
Alternatively, you can use the .NET CLI:
```bash theme={null}
# Remove the old package
dotnet remove package WalletConnect.Web3Wallet
# Add the new package
dotnet add package Reown.WalletKit
```
### Step 2. Update references to the namespaces
### Step 3. Update references to the classes
### Final notes
* Ensure that you have updated all relevant configurations and imports in your project to reflect the changes from Web3Wallet to WalletKit.
* Test your application thoroughly to ensure that the migration has been successful and that all functionality is working as expected.
# Upgrade from Web3Wallet to WalletKit for Web
Source: https://docs.walletconnect.network/wallet-sdk/upgrade/from-web3wallet-web
## Upgrade to WalletKit
This document outlines the steps to migrate from the old `@walletconnect/web3wallet` package to the new `@reown/walletkit` packages in your project.
### Step 1. Update your package.json
Replace your existing `@walletconnect/web3wallet` dependency with `@reown/walletkit`:
```json theme={null}
/* highlight-delete-start */
"@walletconnect/web3wallet": "^x.y.z"
/* highlight-delete-end */
/* highlight-add-start */
"@reown/walletkit": "^1.0.0"
/* highlight-add-end */
```
### Step 2. Install `@reown/walletkit`
Run `npm install` (or your preferred package manager command) to install the new package.
### Step 3. Update your imports
Replace the imports in your project:
```javascript theme={null}
/* highlight-delete-start */
import { Web3Wallet } from "@walletconnect/web3wallet";
/* highlight-delete-end */
/* highlight-add-start */
import { WalletKit } from "@reown/walletkit";
/* highlight-add-end */
```
and your initialization to use the new package:
```javascript theme={null}
/* highlight-delete-start */
await Web3Wallet.init()
/* highlight-delete-end */
/* highlight-add-start */
await WalletKit.init()
/* highlight-add-end */
```
If you're using additional imports from `@walletconnect/web3wallet`, you can replace them with their corresponding version from `@reown/walletkit` such as:
```javascript theme={null}
/* highlight-delete-start */
import { IWeb3Wallet } from "@walletconnect/web3wallet";
/* highlight-delete-end */
/* highlight-add-start */
import { IWalletKit } from "@reown/walletkit";
/* highlight-add-end */
```
## You're all set!
### Final Notes
* public API documentation can be found [here](/wallet-sdk/web/usage)
* `auth_request` is deprecated in favor of `session_authenticate`. Docs can be found [here](/wallet-sdk/web/one-click-auth)
# Staying up to date
Source: https://docs.walletconnect.network/wallet-sdk/upgrade/staying-up-to-date
## Why Should You Keep WalletKit Updated?
Keeping your WalletKit SDK updated to the latest version is crucial for maintaining optimal performance, security, and compatibility with the evolving Web3 ecosystem. Regular updates ensure:
* **Security patches** - Protection against newly discovered vulnerabilities
* **Bug fixes** - Resolution of known issues and improved stability
* **New features** - Access to the latest WalletConnect protocol enhancements
* **Protocol compatibility** - Seamless interaction with updated dApps and wallets
* **Performance improvements** - Optimizations for better user experience
## Latest Releases by Platform
Stay current with the latest WalletKit releases for your development platform:
### Kotlin (Android)
Check the latest Kotlin releases for Android development:
* [WalletConnect Kotlin Releases](https://github.com/reown-com/reown-kotlin/releases)
### Swift (iOS)
Stay updated with the latest Swift releases for iOS development:
* [WalletConnect Swift Releases](https://github.com/reown-com/reown-swift/releases)
### JavaScript / React Native
Monitor JavaScript and React Native package updates:
* [WalletConnect WalletKit JS Releases](https://github.com/reown-com/reown-walletkit-js/releases)
### Flutter
Check the latest Flutter releases for Flutter development:
* [WalletConnect WalletKit Flutter Releases](https://pub.dev/packages/reown_walletkit)
# Best Practices
Source: https://docs.walletconnect.network/wallet-sdk/web/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.
```typescript theme={null}
const uri = 'xxx'; // pairing uri
try {
await walletKit.pair({ uri });
} catch (error) {
// some error happens while pairing - check Expected errors section
}
```
### 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.
```typescript theme={null}
core.pairing.events.on("pairing_expire", (event) => {
// pairing expired before user approved/rejected a session proposal
const { topic } = topic;
});
```
### 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.
Approving session
```typescript theme={null}
try {
await walletKit.approveSession(params);
// update UI -> remove the loader
} catch (error) {
// present error to the user
}
```
Rejecting session
```typescript theme={null}
try {
await walletKit.rejectSession(params);
// update UI -> remove the loader
} catch (error) {
// present error to the user
}
```
### 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.
```typescript theme={null}
walletKit.on("proposal_expire", (event) => {
// proposal expired and any modal displaying it should be removed
const { id } = event;
});
```
### 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.
```typescript theme={null}
try {
await walletKit.respondSessionRequest(params);
// update UI -> remove the loader
} catch (error) {
// present error to the user
}
```
### 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.
```typescript theme={null}
walletKit.on("session_request_expire", (event) => {
// request expired and any modal displaying it should be removed
const { id } = event;
});
```
### 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.
```typescript theme={null}
core.relayer.on("relayer_connect", () => {
// connection to the relay server is established
})
core.relayer.on("relayer_disconnect", () => {
// connection to the relay server is lost
})
```
### Expected User flow
### Connection State

# Chain Abstraction
Source: https://docs.walletconnect.network/wallet-sdk/web/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
Make sure you are using canary version of `@reown/walletkit`.
Following are the methods from WalletKit that you will use in implementing chain abstraction.
### Prepare
This method checks if a transaction requires additional bridging transactions beforehand.
```typescript theme={null}
public abstract prepare(params: {
transaction: ChainAbstractionTypes.PartialTransaction;
}): ChainAbstractionTypes.PrepareResponse;
```
### Execute
Helper method used to broadcast the bridging and initial transactions and wait for them to be completed.
```typescript theme={null}
public abstract execute(params: {
orchestrationId: ChainAbstractionTypes.OrchestrationId;
bridgeSignedTransactions: ChainAbstractionTypes.SignedTransaction[];
initialSignedTransaction: ChainAbstractionTypes.SignedTransaction;
}): ChainAbstractionTypes.ExecuteResult;
```
## 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.
Here's a complete example:
```typescript theme={null}
// Check if chain abstraction is needed
const result = await walletKit.chainAbstraction.prepare({
transaction: {
from: transaction.from as `0x${string}`,
to: transaction.to as `0x${string}`,
// @ts-ignore - cater for both input or data
input: transaction.input || (transaction.data as `0x${string}`),
chainId: chainId,
},
});
// Handle the prepare result
if ('success' in result) {
if ('notRequired' in result.success) {
// No bridging required, proceed with normal transaction
console.log('no routing required');
} else if ('available' in result.success) {
const available = result.success.available;
// Sign all bridge transactions and initial transaction
const bridgeTxs = available.route.map(tx => tx.transactionHashToSign);
const signedBridgeTxs = bridgeTxs.map(tx => wallet.signAny(tx));
const signedInitialTx = wallet.signAny(available.initial.transactionHashToSign);
// Execute the chain abstraction
const result = await walletKit.chainAbstraction.execute({
bridgeSignedTransactions: signedBridgeTxs,
initialSignedTransaction: signedInitialTx,
orchestrationId: available.routeResponse.orchestrationId,
});
}
}
```
For example, check out implementation of chain abstraction in [sample wallet](https://github.com/reown-com/web-examples/tree/main/advanced/wallets/react-wallet-v2) built with React.
## 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 [USDC/USDT](/wallet-sdk/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://react-wallet.walletconnect.com) for testing.
# Analytics
Source: https://docs.walletconnect.network/wallet-sdk/web/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.
### Web3Inbox
#### Subscribers - All Time
Total count of all subscribers to your project.
#### Notifications - All Time
Total count of all notifications sent from your project.
#### Subscribers
Daily trend chart illustrating the growth of subscribers.
#### Notifications
Daily trend chart of total notifications received by your subscribers.
#### Messaged Accounts
Daily trend chart of unique wallets that received the notification.
#### Subscribers by notification type
This table shows the total count of subscribers by notification type over a 30 day period.
### 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.network/wallet-sdk/web/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](/walletguide/explorer).
## 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.network/wallet-sdk/web/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 via `sessionProperties`. These can be universal (across 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
A wallet's response should indicate which capabilities it actually supports, following 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, the `atomic` capability specifies how the wallet will execute batches of transactions. It has three possible values:
* `supported` - The wallet will execute 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": ["..."],
}
}
}
```
Specify the `scopedProperties` when approving a session:
```json theme={null}
"scopedProperties": {
"eip155": {
"walletService": [{
"url": "",
"methods": ["wallet_getCallsStatus"]
}]
}
}
```
### Response Format
The response format for `wallet_getCallsStatus` varies based on the execution method:
#### 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
}
```
For non-atomic execution, include all transactions in the receipts array, even those that were included on-chain but eventually reverted.
## 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.network/wallet-sdk/web/installation
Install Wallet SDK using npm or yarn.
```bash npm theme={null}
npm install @reown/walletkit @walletconnect/utils @walletconnect/core
```
```bash Yarn theme={null}
yarn add @reown/walletkit @walletconnect/utils @walletconnect/core
```
```bash Bun theme={null}
bun add @reown/walletkit @walletconnect/utils @walletconnect/core
```
```bash pnpm theme={null}
pnpm add @reown/walletkit @walletconnect/utils @walletconnect/core
```
## Next Steps
Now that you've installed WalletKit, 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.
# One-click Auth
Source: https://docs.walletconnect.network/wallet-sdk/web/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.
## Handling Authentication Requests
To handle incoming authentication requests, subscribe to the `session_authenticate` event. This 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.
```typescript theme={null}
walletKit.on("session_authenticate", async (payload) => {
// Process the authentication request here.
// Steps include:
// 1. Populate the authentication payload with the supported chains and methods
// 2. Format the authentication message using the payload and the user's account
// 3. Present the authentication message to the user
// 4. Sign the authentication message(s) to create a verifiable authentication object(s)
// 5. Approve the authentication request with the authentication object(s)
});
```
## Authentication Objects/Payloads
```typescript theme={null}
import { populateAuthPayload } from "@walletconnect/utils";
// EVM chains that your wallet supports
const supportedChains = ["eip155:1", "eip155:2", 'eip155:137'];
// EVM methods that your wallet supports
const supportedMethods = ["personal_sign", "eth_sendTransaction", "eth_signTypedData"];
// Populate the authentication payload with the supported chains and methods
const authPayload = populateAuthPayload({
authPayload: payload.params.authPayload,
chains: supportedChains,
methods: supportedMethods,
});
// Prepare the user's address in CAIP10(https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-10.md) format
const iss = `eip155:1:0x0Df6d2a56F90e8592B4FfEd587dB3D5F5ED9d6ef`;
// Now you can use the authPayload to format the authentication message
const message = walletKit.formatAuthMessage({
request: authPayload,
iss
});
// Present the authentication message to the user
...
```
## Approving Authentication Requests
**Note**
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.
```typescript theme={null}
// Approach 1
// Sign the authentication message(s) to create a verifiable authentication object(s)
const signature = await cryptoWallet.signMessage(message, privateKey);
// Build the authentication object(s)
const auth = buildAuthObject(
authPayload,
{
t: "eip191",
s: signature,
},
iss
);
// Approve
await walletKit.approveSessionAuthenticate({
id: payload.id,
auths: [auth],
});
// Approach 2
// Note that you can also sign multiple messages for every requested chain/address pair
const auths = [];
authPayload.chains.forEach(async (chain) => {
const message = walletKit.formatAuthMessage({
request: authPayload,
iss: `${chain}:${cryptoWallet.address}`,
});
const signature = await cryptoWallet.signMessage(message);
const auth = buildAuthObject(
authPayload,
{
t: "eip191", // signature type
s: signature,
},
`${chain}:${cryptoWallet.address}`
);
auths.push(auth);
});
// Approve
await walletKit.approveSessionAuthenticate({
id: payload.id,
auths,
});
```
## Rejecting Authentication Requests
If the authentication request cannot be approved or if the user chooses to reject it, use the rejectSession method.
```typescript theme={null}
import { getSdkError } from "@walletconnect/utils";
await walletKit.rejectSessionAuthenticate({
id: payload.id,
reason: getSdkError("USER_REJECTED"), // or choose a different reason if applicable
});
```
## 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.network/wallet-sdk/web/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 JavaScript GitHub](https://github.com/reown-com/reown-walletkit-js) - Wallet SDK JavaScript GitHub repository.
### Wallet Resources
We have a set of official examples in our [web-examples](https://github.com/WalletConnect/web-examples) repository to help you get started.
**Wallet SDK**
This wallet can be used with any dapp using Sign v2 or Auth.
* [React Wallet SDK](https://github.com/reown-com/web-examples/tree/main/advanced/wallets/react-wallet-v2) ([Demo](https://react-wallet.walletconnect.com))
**Sign**
* [React Wallet Ethers - v2](https://github.com/reown-com/web-examples/tree/main/advanced/wallets/react-wallet-auth) ([Demo](https://react-auth-wallet.walletconnect.com/))
### Dapp Resources
If you need to test your app's integration, you can use one of our following demo wallets and/or 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/))
# Usage
Source: https://docs.walletconnect.network/wallet-sdk/web/usage
## Overview
WalletConnect makes distributing your Web wallet (such as Safe, Abstract Global Wallet, and many more) much faster. By integrating the Wallet SDK, your web wallet will be able to connect to any dApp that supports WalletConnect and will be available in the list of wallets on both WalletConnect, Reown AppKit, and others.
Additionally, you don't need to build SDKs in native languages such as Swift, Kotlin, Flutter, Unity, or React Native. This means your wallet can be available not just for web dApps, but also for native dApps, all from a single codebase.
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.
## User Experience
### Default Integration
This integration automatically opens a new web wallet tab when a user clicks "Connect" in a dApp. The wallet handles the WalletConnect URI automatically, meaning users don't need to manually copy and paste the URI. This provides a seamless, one-click connection experience.
### QR Code Integration
This integration only handles the session proposal and approval via the WalletConnect QR code. The user will need to manually copy and paste the WalletConnect URI into the wallet.
## Cloud Configuration
Create a new project on WalletConnect Dashboard at [https://dashboard.walletconnect.com](https://dashboard.walletconnect.com) and obtain a new project ID.
**Don't have a project ID?**
Head over to WalletConnect Dashboard and create a new project now!
## Initialization
Create a new instance from Core and initialize it with a projectId created from installation. Next, create WalletKit instance by calling init on walletKit. Passing in the options object containing metadata about the app and an optional relay URL.
Make sure you initialize `walletKit` globally and use the same instance for all your sessions. For React-based apps, you can initialize it in the root component and export it to use in other components.
```javascript theme={null}
import { Core } from "@walletconnect/core";
import { WalletKit } from "@reown/walletkit";
const core = new Core({
projectId: process.env.PROJECT_ID,
});
const walletKit = await WalletKit.init({
core, // <- pass the shared `core` instance
metadata: {
name: "Demo app",
description: "Demo Client as Wallet/Peer",
url: "https://reown.com/walletkit",
icons: [],
},
});
```
### Core Instance Sharing
Starting from newer versions of WalletKit, Core instances are shared globally by default to optimize resource usage. This means that multiple Core instances with the same configuration will reuse the same underlying Core.
**For parallel testing scenarios** where you need isolated Core instances, you have two options:
#### Option 1: Use customStoragePrefix
```javascript theme={null}
const core = new Core({
projectId: process.env.PROJECT_ID,
customStoragePrefix: `test-${Date.now()}`, // Unique prefix for each test
});
```
Don't use randomly generated `customStoragePrefix` in production - this will cause the client to create new storage each time it is initialized. The client will not be able to persist/read existing data and all existing sessions will be lost after each reload.
#### Option 2: Disable global Core sharing
```javascript theme={null}
// Set environment variable before initializing Core
process.env.DISABLE_GLOBAL_CORE = "true";
const core = new Core({
projectId: process.env.PROJECT_ID,
});
```
The global Core sharing behavior was introduced to prevent resource waste when multiple SDK instances are created. If you're running parallel tests and experiencing cross-test interference, use one of the solutions above to ensure proper test isolation.
## 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.
### Handling Session Proposals
You can connect your web wallet to a dApp in three ways:
1. Retrieve the WC URI from the dApp via query parameters
2. Implement a scanner to read the WC URI from a WalletConnect QR code
3. Allow users to manually enter the WC URI in an input field
When opening the wallet directly from the dApp, the `WC_URI` will be in the following format in the query parameters:
```
{YOUR_WALLET_URL}/wc?uri={WC_URI}
```
We recommend closing the web wallet tab after the user approves the request and redirecting back to the dApp. For web requests, redirect to the original browser tab, and for mobile requests, redirect to the native mobile dApp.
To test your web wallet connection flow, use our [Appkit Laboratory](https://appkit-lab.reown.com/) by adding a custom wallet with your web wallet URL.
### Handling Session Requests
When a dApp sends a session request to your wallet, the request will be available in the following format in the query parameters:
```
{YOUR_WALLET_URL}/wc?requestId={requestId}&sessionTopic={session.Topic}
```
This format allows your wallet to identify both the specific request and the session it belongs to.
To test your web wallet connection flow, use our [Appkit Laboratory](https://appkit-lab.reown.com/) by adding a custom wallet with your web wallet URL.
### Handling Redirects
When handling redirects after session approval or request completion, you should:
1. For web dApps:
* Redirect back to the original browser tab using `window.opener.location.href`
* Close the current wallet tab using `window.close()`
2. For native dApps:
* Check the peer metadata for a `redirect` URL in the session proposal
* If available, use deep linking to redirect to the native app
* If no redirect URL is found, display a "Return to App" message to the user
Example implementation:
```javascript theme={null}
function handleRedirect(session) {
// Check if this is a native app
const isNativeApp = session.peer.metadata.redirect !== undefined;
if (isNativeApp) {
// Redirect to native app if URL is available
if (session.peer.metadata.redirect) {
window.location.href = session.peer.metadata.redirect;
} else {
// Show "Return to App" message
showReturnToAppMessage();
}
} else {
// For web dApps, redirect back to original tab
if (window.opener) {
window.opener.location.href = session.peer.metadata.url;
window.close();
}
}
}
```
### Namespace Builder
With WalletKit (and @walletconnect/utils) 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 user's `chains/methods/events/accounts` and returns ready-to-use `namespaces` object.
```javascript theme={null}
// util params
{
proposal: ProposalTypes.Struct; // the proposal received by `.on("session_proposal")`
supportedNamespaces: Record< // your Wallet's supported namespaces
string, // the supported namespace key e.g. eip155
{
chains: string[]; // your supported chains in CAIP-2 format e.g. ["eip155:1", "eip155:2", ...]
methods: string[]; // your supported methods e.g. ["personal_sign", "eth_sendTransaction"]
events: string[]; // your supported events e.g. ["chainChanged", "accountsChanged"]
accounts: string[] // your user's accounts in CAIP-10 format e.g. ["eip155:1:0x453d506b1543dcA64f57Ce6e7Bb048466e85e228"]
}
>;
};
```
Example usage
```javascript theme={null}
// import the builder util
import { WalletKit, WalletKitTypes } from '@reown/walletkit'
import { buildApprovedNamespaces, getSdkError } from '@walletconnect/utils'
async function onSessionProposal({ id, params }: WalletKitTypes.SessionProposal){
try{
// ------- namespaces builder util ------------ //
const approvedNamespaces = buildApprovedNamespaces({
proposal: params,
supportedNamespaces: {
eip155: {
chains: ['eip155:1', 'eip155:137'],
methods: ['eth_sendTransaction', 'personal_sign'],
events: ['accountsChanged', 'chainChanged'],
accounts: [
'eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb',
'eip155:137:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb'
]
}
}
})
// ------- end namespaces builder util ------------ //
const session = await walletKit.approveSession({
id,
namespaces: approvedNamespaces
})
}catch(error){
// use the error.message to show toast/info-box letting the user know that the connection attempt was unsuccessful
....
await walletKit.rejectSession({
id: proposal.id,
reason: getSdkError("USER_REJECTED")
})
}
}
walletKit.on('session_proposal', onSessionProposal)
```
If your wallet supports multiple namespaces e.g. `eip155`,`cosmos` & `near`
Your `supportedNamespaces` should look like the following example.
```javascript theme={null}
// ------- namespaces builder util ------------ //
const approvedNamespaces = buildApprovedNamespaces({
proposal: params,
supportedNamespaces: {
eip155: {...},
cosmos: {...},
near: {...}
},
});
// ------- end namespaces builder util ------------ //
```
### Get Active Sessions
You can get the wallet active sessions using the `getActiveSessions` function.
```js theme={null}
const activeSessions = walletKit.getActiveSessions();
```
### 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
The `session_proposal` event is emitted when a dapp initiates a new session with a user's wallet. The event will include a `proposal` object with information about the dapp and requested permissions. The wallet should display a prompt for the user to approve or reject the session. If approved, call `approveSession` and pass in the `proposal.id` and requested `namespaces`.
The `pair` method initiates a WalletConnect pairing process with a dapp using the given `uri` (QR code from the dapps). To learn more about pairing, checkout out the [docs](https://docs.reown.com/advanced/api/core/pairing).
```javascript theme={null}
walletKit.on(
"session_proposal",
async (proposal: WalletKitTypes.SessionProposal) => {
const session = await walletKit.approveSession({
id: proposal.id,
namespaces,
});
}
);
await walletKit.pair({ uri });
```
### 🛠️ Usage examples
* [in a demo wallet app](https://github.com/reown-com/web-examples/blob/main/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx#L264)
* [in integration tests](https://github.com/reown-com/reown-walletkit-js/blob/main/packages/walletkit/test/sign.spec.ts#L55)
### ⚠️ Expected Errors
* `No matching key. proposal id doesn't exist: 1`
This rejection means the SDK can't find a record with the given `proposal.id` - in this example `1`.
This can happen when the proposal has expired (by default 5 minutes) or if you attempt to respond to a proposal that has already been approved/rejected.
If you are seeing this error, please make sure that you are calling `approveSession` with the correct `proposal.id` that is available within the proposal payload.
* `Error: Missing or invalid. approve(), namespaces should be an object with data`
This error means that the `namespaces` parameter passed to `approveSession` is either missing or invalid. Please check that you are passing a valid `namespaces` object that satisfies all required properties.
* `Non conforming namespaces. approve() namespaces don't satisfy required namespaces.`
This error indicates that some value(s) in your `namespaces` object do not satisfy the required namespaces requested by the dapp.
To provide additional guidance, the message might include info about the exact property that is missing or invalid e.g. `Required: eip155:1 Approved: eip155:137`.
Please check [CAIP-25](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-25.md) to familiarize yourself with the standard and it's nuances.
Additionally, we highly recommend you to use our `namespace` builder utility that would greatly simplify the process of parsing & building a valid `namespaces` object.
### Session Rejection
In the event you want to reject the session proposal, call the `rejectSession` method. The `getSDKError` function comes from the `@walletconnect/utils` [library](https://github.com/WalletConnect/walletconnect-monorepo/tree/v2.0/packages/utils).
```javascript theme={null}
walletKit.on(
"session_proposal",
async (proposal: WalletKitTypes.SessionProposal) => {
await walletKit.rejectSession({
id: proposal.id,
reason: getSdkError("USER_REJECTED_METHODS"),
});
}
);
```
### 🛠️ Usage examples
* [in a demo wallet app](https://github.com/WalletConnect/web-examples/blob/a50c8eb5a10666f25911713c5358e78f1ca576d6/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx#L287)
* [in integration tests](https://github.com/reown-com/reown-walletkit-js/blob/main/packages/walletkit/test/sign.spec.ts#L79)
### ⚠️ Expected Errors
* `No matching key. proposal id doesn't exist: 1`
This rejection means the SDK can't find a record with the given `proposal.id` - in this example `1`.
This can happen when the proposal has expired (by default 5 minutes) or if you attempt to respond to a proposal that has already been approved/rejected.
If you are seeing this error, please make sure that you are calling `rejectSession` with the correct `proposal.id` that is available within the proposal payload.
* `Error: Missing or invalid. reject() reason:`
This rejection means the `reason` parameter passed to `rejectSession` is either missing or invalid.
We recommend using the `getSDKError` function from the `@walletconnect/utils` library that will populate & format the parameter for you.
### Responding to Session requests
The `session_request` event is emitted when the SDK received a request from the peer and it needs the wallet to perform a specific action, such as signing a transaction. The event contains a `topic` and a `request` object, which will vary depending on the action requested.
To respond to the request, you can access the `topic` and `request` object by destructuring them from the event payload. To see a list of possible `request` and `response` objects, refer to the relevant JSON-RPC Methods for [Ethereum](https://docs.reown.com/advanced/multichain/rpc-reference/ethereum-rpc), [Solana](https://docs.reown.com/advanced/multichain/rpc-reference/solana-rpc), [Cosmos](https://docs.reown.com/advanced/multichain/rpc-reference/cosmos-rpc), or [Stellar](https://docs.reown.com/advanced/multichain/rpc-reference/stellar-rpc).
As an example, if the dapp requests a `personal_sign` method, you can extract the `params` array from the `request` object. The first item in the array is the hex version of the message to be signed, which can be converted to UTF-8 and assigned to a `message` variable. The second item in `params` is the user's wallet address.
To sign the message, you can use your wallet's `signMessage` method and pass in the message. The signed message, along with the `id` from the event payload, can then be used to create a `response` object, which can be passed into `respondSessionRequest`.
```javascript theme={null}
walletKit.on(
"session_request",
async (event: WalletKitTypes.SessionRequest) => {
const { topic, params, id } = event;
const { request } = params;
const requestParamsMessage = request.params[0];
// convert `requestParamsMessage` by using a method like hexToUtf8
const message = hexToUtf8(requestParamsMessage);
// sign the message
const signedMessage = await wallet.signMessage(message);
const response = { id, result: signedMessage, jsonrpc: "2.0" };
await walletKit.respondSessionRequest({ topic, response });
}
);
```
To reject a session request, the response should be similar to this.
```javascript theme={null}
const response = {
id,
jsonrpc: "2.0",
error: {
code: 5000,
message: "User rejected.",
},
};
```
### 🛠️ Usage examples
* [in a demo wallet app](https://github.com/WalletConnect/web-examples/blob/a50c8eb5a10666f25911713c5358e78f1ca576d6/advanced/wallets/react-wallet-v2/src/views/SessionSignModal.tsx#L36)
* [in integration tests](https://github.com/reown-com/reown-walletkit-js/blob/main/packages/walletkit/test/sign.spec.ts#L165)
### ⚠️ Expected Errors
* `Error: No matching key. session topic doesn't exist: 'xyz...'`
This rejection means the SDK can't find a session with the given `topic` - in this example `xyz...`.
This can happen when the session has been disconnected by either the wallet or the dapp while the session request was being processed or if a session with such topic doesn't exist.
If you are seeing this error, please make sure that you are using a correct topic that is available within the request payload.
* `Error: Missing or invalid. respond() response:`
This rejection means the `response` parameter passed to `respondSessionRequest` is either missing or invalid. The response should be a valid [JSON-RPC 2.0](https://www.jsonrpc.org/specification) response object.
We recommend you to use our `formatJsonRpcResult` utility from `"@walletconnect/jsonrpc-utils"` that will format the response for you.
Example usage:
`id` argument being the request id from the request payload.
```javascript theme={null}
import { formatJsonRpcResult } from "@walletconnect/jsonrpc-utils";
const signature = await cryptoWallet.signTransaction(signTransaction);
const response = await walletKit.respondSessionRequest({
topic: session.topic,
response: formatJsonRpcResult(id, signature),
});
```
### Updating a Session
If you wish to include new accounts or chains or methods in an existing session, `updateSession` allows you to do so.
You need pass in the `topic` and a new `Namespaces` object that contains all of the existing namespaces as well as the new data you wish to include.
After you update the session, the other peer will receive a `session_update` event.
An example adding a new account to an existing session:
```javascript theme={null}
const namespaces = session.namespaces;
const accounts = [
"eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb",
"eip155:1:0x1234567890123456789012345678901234567890",
];
const updatedNamespaces = {
...namespaces,
eip155: {
...namespaces.eip155,
accounts,
},
};
const { acknowledged } = await walletKit.updateSession({
topic: session.topic,
namespaces: updatedNamespaces,
});
// If you wish to be notified when the dapp acknowledges the update.
// note that if the dapp is offline `acknowledged` will not resolve until it comes back online
await acknowledged();
```
An example adding a new chain to an existing session:
```javascript theme={null}
const namespaces = session.namespaces;
const chains = ["eip155:1", "eip155:137"];
const accounts = [
"eip155:1:0xab16a96d359ec26a11e2c2b3d8f8b8942d5bfcdb",
"eip155:137:0x1234567890123456789012345678901234567890",
];
const updatedNamespaces = {
...namespaces,
eip155: {
...namespaces.eip155,
accounts,
chains,
},
};
await walletKit.updateSession({
topic: session.topic,
namespaces: updatedNamespaces,
});
```
### 🛠️ Usage examples
* [in a demo wallet app](https://github.com/WalletConnect/web-examples/blob/a50c8eb5a10666f25911713c5358e78f1ca576d6/advanced/wallets/react-wallet-v2/src/pages/session.tsx#L77)
* [in integration tests](https://github.com/reown-com/reown-walletkit-js/blob/main/packages/walletkit/test/sign.spec.ts#L98)
### ⚠️ Expected Errors
Note that all `namespaces` validation applies and you still have to satisfy the required namespaces requested by the dapp.
* `Error: No matching key. session topic doesn't exist: 'xyz...'`
This rejection means the SDK can't find a session with the given `topic` - in this example `xyz...`.
This can happen when the session you're trying to update has already been disconnected by either the wallet or the dapp or if a session with such topic doesn't exist.
If you are seeing this error, please make sure that you are using a correct topic of an active session.
* `Error: Missing or invalid. update(), namespaces should be an object with data`
This error means that the `namespaces` parameter passed to `updateSession` is either missing or invalid. Please check that you are passing a valid `namespaces` object that satisfies all required properties.
* `Non conforming namespaces. update() namespaces don't satisfy required namespaces.`
This error indicates that some value(s) in your `namespaces` object do not satisfy the required namespaces requested by the dapp.
To provide additional guidance, the message might include info about the exact property that is missing or invalid e.g. `Required: eip155:1 Approved: eip155:137`.
Please check [CAIP-25](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-25.md) to familiarize yourself with the standard and it's nuances.
Additionally, we highly recommend you to use our `namespace` builder utility that would greatly simplify the process of parsing & building a valid `namespaces` object.
### Extending a Session
Sessions have a default expiry of 7 days. To extend a session by an additional 7 days, call `.extendSession` method and pass in the `topic` of the session you wish to extend.
```javascript theme={null}
const { acknowledged } = await walletKit.extendSession({ topic });
// if you wish to be notified when the dapp acks the extend
// note that if the dapp is offline `acknowledged` will not resolve until it comes back online
await acknowledged();
```
### 🛠️ Usage examples
* [in integration tests](https://github.com/reown-com/reown-walletkit-js/blob/main/packages/walletkit/test/sign.spec.ts#L130)
### ⚠️ Expected Errors
* `Error: No matching key. session topic doesn't exist: 'xyz...'`
This rejection means the SDK can't find a session with the given `topic` - in this example `xyz...`.
This can happen when the session you're trying to update has already been disconnected by either the wallet or the dapp or if a session with such topic doesn't exist.
If you are seeing this error, please make sure that you are using a correct topic of an active session.
### Session Disconnect
To initiate disconnect from a session(think session delete), call `.disconnectSession` by passing a `topic` & `reason` for the disconnect.
The other peer will receive a `session_delete` and be notified that the session has been disconnected.
**Note**
It's important that you're subscribed to the `session_delete` event as well, to be notified when the other peer initiates a disconnect.
We recommend using the `getSDKError` utility function, that will provide ready-to-use `reason` payloads and is available in the `@walletconnect/utils` [library](https://github.com/WalletConnect/walletconnect-monorepo/tree/v2.0/packages/utils).
```javascript theme={null}
await walletKit.disconnectSession({
topic,
reason: getSdkError("USER_DISCONNECTED"),
});
```
### 🛠️ Usage examples
* [in integration tests](https://github.com/reown-com/reown-walletkit-js/blob/main/packages/walletkit/test/sign.spec.ts#L222)
### ⚠️ Expected Errors
* `Error: No matching key. session topic doesn't exist: 'xyz...'`
This rejection means the SDK can't find a session with the given `topic` - in this example `xyz...`.
This can happen when the session you're trying to update has already been disconnected by either the wallet or the dapp or if a session with such topic doesn't exist.
If you are seeing this error, please make sure that you are using a correct topic of an active session.
### Emitting Session Events
To emit session events, call the `emitSessionEvent` and pass in the params. If you wish to switch to chain/account that is not approved (missing from `session.namespaces`) you will have to update the session first. In the following example, the wallet will emit `session_event` that will instruct the dapp to switch the active accounts.
```javascript theme={null}
await walletKit.emitSessionEvent({
topic,
event: {
name: "accountsChanged",
data: ["0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb"],
},
chainId: "eip155:1",
});
```
In the following example, the wallet will emit `session_event` when the wallet switches chains.
```javascript theme={null}
await walletKit.emitSessionEvent({
topic,
event: {
name: "chainChanged",
data: 1,
},
chainId: "eip155:1",
});
```
# Verify API
Source: https://docs.walletconnect.network/wallet-sdk/web/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
To check the Verify API validations and whether or not your user is interacting with potentially malicious app, you can do so by accessing the `verifyContext` included in the request payload.
```javascript theme={null}
...
walletKit.on("auth_request", async (authRequest) => {
const { verifyContext } = authRequest
const validation = verifyContext.verified.validation // can be VALID, INVALID or UNKNOWN
const origin = verifyContext.verified.origin // the actual verified origin of the request
const isScam = verifyContext.verified.isScam // true if the domain is flagged as malicious
// if the domain is flagged as malicious, you should warn the user as they may lose their funds - check the `Threat` case for more info
if(isScam) {
// show a warning screen to the user
// and proceed only if the user accepts the risk
}
switch(validation) {
case "VALID":
// proceed with the request - check the `Domain match` case for more info
break
case "INVALID":
// show a warning dialog to the user - check the `Mismatch` case for more info
// and proceed only if the user accepts the risk
break
case "UNKNOWN":
// show a warning dialog to the user - check the `Unverified` case for more info
// and proceed only if the user accepts the risk
break
}
})
```
For live demo examples of the intended Verify API flows, check out our demo apps:
* [Demo Wallet](https://react-wallet.walletconnect.com)
* [Demo App](https://react-app.walletconnect.com/) - you can toggle between the verify states by clicking on the `gear` & selecting the decided Validation before connecting to the wallet
* [Demo Malicious App](https://malicious-app-verify-simulation.vercel.app/) - this app is flagged as malicious and will have the `isScam` parameter set to `true` in the `verifyContext` of the request
# Supported Chains
Source: https://docs.walletconnect.network/walletguide/chains/chain-list
## Overview
This page provides a list of chains on the [WalletGuide](https://walletguide.walletconnect.network/). WalletGuide is a tool that allows users to discover wallets and dapps that support their preferred blockchain.
On this page, you can:
* Filter chains by Mainnet / Testnet
* Search for chains by name
* Click on a chain to copy its Chain ID
# Chain Onboarding
Source: https://docs.walletconnect.network/walletguide/chains/overview
The WalletConnect protocol is multi-chain by design. By using the [CAIP-25 standard](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-25.md), WalletConnect aims to provide a standardized process for onboarding new chains into our ecosystem. To get started, follow the following steps.
## Register Chain with the Explorer
**Registering a chain with the Explorer does not impact or improve the ability for wallets and dapps to support your chain.** It is simply a way for users to discover wallets and dapps that support your chain by:
* Browsing the [Chains List](./chain-list)
* Filtering results programmatically via the [Explorer API](/walletguide/explorer)
**It is still up to wallets and dapps to provide concrete support for your chain once it is listed as part of the Explorer.**
If you don't see your chain listed in this [list](./chain-list), then you will need to create an issue in GitHub to to get the process started.
You can do so by clicking [here](https://github.com/WalletConnect/walletconnect-monorepo/issues/new?assignees=\&labels=type%3A+new+chain+request\&template=new_chain_to_explorer.md\&title=). Once your chain is added to this list, wallets & dapps will be able to indicate support for your chain via [WalletConnect Dashboard](https://dashboard.walletconnect.com).
## CASA
To register a chain, you must know both its native representation (the chainID used with that kind of blockchain) *and* its Chain Agnostic Standards Alliance representation, which can be found reading the relevant CAIP-2 profiles on the [CASA Namespaces Project Docs](https://namespaces.chainagnostic.org/). If no such profile yet exists, you can collaborate with an expert in the respective chain's tooling and submit a [namespaces PR](https://github.com/ChainAgnostic/namespaces/?tab=readme-ov-file#namespaces) to add one.
## Add RPC Methods
Integrate RPC method support into the example wallets and dapp.
**Example Wallet**
* [Demo](https://react-wallet.walletconnect.com/)
* [GitHub](https://github.com/WalletConnect/web-examples/tree/main/advanced/wallets/react-walletkit)
**Example Dapp**
* [Demo](https://react-app.walletconnect.com/)
* [GitHub](https://github.com/WalletConnect/web-examples/tree/main/advanced/dapps/react-dapp-v2)
## Promote
For a chain to benefit users, its prominent wallets and dApps must be registered in the Explorer. Encourage them to join the API, allowing users to view the wallets as options when connecting to a dApp.
## Wagmi & Viem
If the chain you are registering is EVM compliant, we highly recommend you to integrate it with [Viem](https://viem.sh/docs/clients/chains.html), an ethereum library used by Wagmi and Reown. To accomplish this you will need to open a GitHub Pull Request in the Viem repository.
* [Viem GitHub Repository](https://github.com/wagmi-dev/viem/tree/main/src/chains/definitions)
# Explorer API
Source: https://docs.walletconnect.network/walletguide/explorer
The Cloud Explorer API currently offers the following functionality:
* [Listings](#listings) - Allows for fetching of wallets and dApps listed in the [WalletGuide](https://walletguide.walletconnect.network/).
* [Logos](#logos) - Provides logo assets in different sizes for a given Cloud explorer entry.
### Listings
By default listings endpoints return all data for provided type. You can use following query params to return paginated data or search for a specific listing by its name:
| Param | Required? | Description |
| ----------- | --------- | ---------------------------------------------------------------------------------------------------------------------- |
| projectId | Required | Your WalletConnect Dashboard Project ID (from [dashboard.walletconnect.com](https://dashboard.walletconnect.com/)) |
| entries | | Specifies how many entries will be returned (must be used together with page param) |
| page | | Specifies current page (must be used with entries param) |
| search | | Returns listings whose name matches provided search query |
| ids | | Returns listings whose id matches provided ids (e.g. `&ids=LISTING_ID1,LISTING_ID2`) |
| chains | | Returns listings that support at least one of the provided chains (e.g. `?chains=eip155:1,eip155:137`) |
| platforms | | Returns listings that support at least one of the provided platforms (e.g. `?platforms=ios,android,mac,injected`) |
| sdks | | Returns listings that support at least one of the provided Reown SDKs (e.g. `?sdks=sign_v1,sign_v2,auth_v1`) |
| standards | | Returns listings that support at least one of the provided standards (e.g. `?standards=eip-712,eip-3085`) |
| ~~version~~ | | Deprecated - replaced by `sdks` param. Specifies supported Sign version (1 or 2) |
#### `GET /v3/wallets`
Returns a JSON object containing all wallets listed in the cloud explorer.
Examples:
* `GET https://explorer-api.walletconnect.com/v3/wallets?projectId=YOUR_PROJECT_ID&entries=5&page=1` (will return the first 5 wallets from the first page)
* `GET https://explorer-api.walletconnect.com/v3/wallets?projectId=YOUR_PROJECT_ID&platforms=injected` (will only return injected wallets)
#### `GET /v3/dapps`
Returns a JSON object containing all dApps listed in the public cloud explorer.
Examples:
* `GET https://explorer-api.walletconnect.com/v3/dapps?projectId=YOUR_PROJECT_ID&entries=5&page=1`
#### `GET /v3/hybrid`
Returns a JSON object containing all hybrids listed in the public cloud explorer.
Examples:
* `GET https://explorer-api.walletconnect.com/v3/hybrid?projectId=YOUR_PROJECT_ID&entries=5&page=1`
#### `GET /v3/all`
Returns a JSON object containing all entries listed in the public cloud explorer.
Examples:
* `GET https://explorer-api.walletconnect.com/v3/all?projectId=YOUR_PROJECT_ID&entries=5&page=1`
#### `GET /v3/all?projectId=YOUR_PROJECT_ID&ids=LISTING_ID1,LISTING_ID2`
Returns a JSON object containing the entry listings by ID, which can be useful for allowlisting purposes.
You can find and copy listing ids from our [WalletGuide](https://walletguide.walletconnect.network/)
Examples:
* `GET https://explorer-api.walletconnect.com/v3/all?projectId=YOUR_PROJECT_ID&ids=be49f0a78d6ea1beed3804c3a6b62ea71f568d58d9df8097f3d61c7c9baf273d,4622a2b2d6af1c9844944291e5e7351a6aa24cd7b23099efac1b2fd875da31a0`
### Chains
By default chains endpoint returns all chains registered under [CASA Namespace](https://github.com/ChainAgnostic/CASA) and that were approved by following our [Add Chain issue template](https://github.com/WalletConnect/walletconnect-monorepo/issues/new?assignees=\&labels=type%3A+new+chain+request\&template=new_chain_to_explorer.md\&title=)
#### Query Parameters
You can use following query params to query chains by its namespace and exclude testnets:
| Param | Description |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------- |
| testnets | Determines if testnets should be included in the response (e.g. `?testnets=false`, defaults to `true` if not provided) |
| namespaces | Returns chains that belong to one of the provided namespaces (e.g. `?namespaces=eip155,cosmos,solana`) |
#### `GET /v3/chains`
Returns all chains registered under [CASA Namespace](https://github.com/ChainAgnostic/CASA) and that were approved by following our [Add Chain issue template](https://github.com/WalletConnect/walletconnect-monorepo/issues/new?assignees=\&labels=type%3A+new+chain+request\&template=new_chain_to_explorer.md\&title=)
Examples:
* `GET https://explorer-api.walletconnect.com/v3/chains?projectId=YOUR_PROJECT_ID`
* `GET https://explorer-api.walletconnect.com/v3/chains?projectId=YOUR_PROJECT_ID&testnets=false`
* `GET https://explorer-api.walletconnect.com/v3/chains?projectId=YOUR_PROJECT_ID&namespaces=eip155,cosmos`
### Logos
#### Path Parameters
| Param | Description |
| ----- | ---------------------------------------------------------------------------------------- |
| size | Determines resolution of returned image can be one of: `sm`, `md` or `lg` |
| id | Corresponds to a Cloud Explorer entry's `image_id` field as returned by the Listings API |
#### Query Parameters
| Param | Required? | Description |
| --------- | --------- | ------------------------------------------------------------------------------------------------------------------ |
| projectId | Required | Your WalletConnect Dashboard Project ID (from [dashboard.walletconnect.com](https://dashboard.walletconnect.com/)) |
#### `GET /v3/logo/:size/:image_id`
Returns the image source of the logo for `image_id` sized according `size`.
Examples:
* `GET https://explorer-api.walletconnect.com/v3/logo/md/32a77b79-ffe8-42c3-61a7-3e02e019ca00?projectId=YOUR_PROJECT_ID`
# WalletGuide Submission
Source: https://docs.walletconnect.network/walletguide/explorer-submission
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=github) and [Cloud Explorer API](./explorer.md).
## Creating a New Project
First, open the WalletConnect Dashboard by navigating to [dashboard.walletconnect.com](https://dashboard.walletconnect.com/?utm_source=blog\&utm_medium=devrel\&utm_campaign=conversion) and signing in. If you don’t have an account yet, please create one before proceeding.
* Once you're logged in, navigate to your team view and click the **"+ Project"** button.
* Select **"Wallet"**, enter a project name, and click **"Add"**.
## Project Details
* From the project Dashboard, click on the **"WalletGuide"** tab in the top navigation.
* Click **"Start submission"** to begin the submission wizard.
## Project Submission
The submission is a multi-step wizard. Follow each step to complete your listing:
### Step 1 — Describe your project
Fill in your wallet's basic details:
* **Name** — This will appear in WalletGuide and other SDKs using the WalletConnect API
* **Link** — The homepage URL of your project
* **Description** — A short description of your wallet
* **Logo** — Upload your wallet's logo
Click **"Continue"** to proceed.
### Step 2 — Add wallet types
Select the wallet types that apply to your project and provide the required links for each:
* Mobile Wallet
* Desktop Wallet
* Web Wallet
* Browser Extension
Click **"+ Add"** next to each applicable type to add its details. Click **"Continue"** when done.
### Step 3 — Add chains
Select all chains your project supports. You can search by name or browse by ecosystem (EVM, Solana, Cosmos, etc.). Toggle **"My wallet supports custom chains"** if applicable.
Click **"Continue"** when done.
### Step 4 — Submit your listing
Add **Test instructions** to help the review team validate your WalletConnect integration. Clear test instructions help accelerate the review process.
Click **"Submit"** to send your listing for review.
## Review Timeline
After submitting, your listing will go through a QA review to verify your WalletConnect integration is working correctly.
* **Initial review** takes **7–10 business days** on average. You can track the status in the **WalletGuide** tab of your project — it will show as **"In Review"** while pending.
* **Once approved**, changes take approximately **24 hours** to go live on the production WalletGuide page.
If your submission is not accepted, the reason will be noted in the WalletGuide tab and in the notification email. You can make the necessary changes and resubmit at any time.
## 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 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 [appkit-lab.reown.com](https://appkit-lab.reown.com/) from a PC 2. Press "Connect Wallet" and select Reown. 3. Open the wallet app and scan QR code. 4. Accept the connection request. | 1. The app is set up correctly 2. A modal with wallet options appears 3. A QR code is shown and scanned 4. Connection established, wallet data displayed on site |
| **Connect to dapp via mobile browser (Deep-link)** | 1. Open [appkit-lab.reown.com](https://appkit-lab.reown.com/) on mobile. 2. Select a default option (e.g., Wagmi for EVM chains), click "Custom Wallet," enter wallet name and deep link, then add it. Press "Connect Wallet" and select the new wallet. 3. Accept connection request in the wallet app. | 1. N/A 2. A form appears to enter wallet data, new wallet option is visible. 3. User is redirected to the wallet app, sees a connection request, and successfully connects. On Android, user is redirected back to the website. |
| **Switch chains - dapp side** | 1. After connecting, click the modal button (top right of website). 2. Click the first button in the modal to switch chains. 3. Select a chain, close the modal, and press "Send Transaction." | 1. Modal with account info appears. 2. A new view with supported chains appears. 3. The transaction request in the wallet shows the correct chain. |
| **Switch Chains - wallet side (if supported)** | 1. Check if wallet supports chain switching. If so, switch to a different chain. | 1. The chain change is reflected on the website. The first card displays the current chain ID. |
| **Accounts Switching - wallet side** | 1. Switch accounts in the wallet app. | 1. The account switch is reflected in the modal’s account view on the website. |
| **Disconnect a wallet** | 1. Press "Disconnect" in the Wallet App (if available). 2. Alternatively, press "Disconnect" from the dApp. | 1. The session disappears from both the dApp and Wallet App. 2. The session disappears from both the dApp and Wallet App. |
| **Verify API** | 1. Open [malicious-app-verify-simulation.vercel.app](https://malicious-app-verify-simulation.vercel.app/). 2. Select a wallet-supported chain, press "Connect." 3. Scan the QR code with the wallet. | 1. N/A 2. A QR code modal appears. 3. The wallet flags the site 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 popup 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 popup 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 popup on the wallet app requesting a signature. |
| Test Case | Steps | Expected Results |
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Supporting solana\_signMessage** | 1. Connect the wallet to [appkit-lab.reown.com/appkit/?name=solana](https://appkit-lab.reown.com/appkit/?name=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.reown.com/appkit/?name=solana](https://appkit-lab.reown.com/appkit/?name=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.reown.com/appkit/?name=solana](https://appkit-lab.reown.com/appkit/?name=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. |
## FAQ
You should set the EIP-6963 RDNS (Reverse Domain Name System) value of your wallet.
This value uniquely identifies your wallet and allows us to properly detect and discover it when it is installed in the user's browser.
In the context of a browser extension, the reverse domain (RDNS):
* Serves as a unique identifier for your wallet
* Enables wallet discovery via the EIP-6963 standard
* Allows our system to detect when your wallet extension is installed
Without the correct RDNS value, your wallet may not be discoverable.
## 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. The **WalletGuide** tab of your project will also reflect the current status.
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 WalletGuide 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)
# Wallets
Source: https://docs.walletconnect.network/walletguide/wallets/wallet-list
## Overview
This page provides a list of wallets on the [WalletGuide](https://walletguide.walletconnect.network/). WalletGuide is a tool that allows users to discover wallets and all the information like WalletId, networks, supported devices, and official links.
On this page, you can:
* Search for wallets by name
* Click on a wallet to copy the WalletId
# Wallets
Source: https://docs.walletconnect.network/wallets
Wallets enable users to manage their blockchain keys and interact with applications via the WalletConnect protocol. They play a vital role in the Network by enabling end-users to securely access and utilize blockchain services on any network. Wallets are responsible for integrating with the WalletConnect Network and providing a seamless user experience for managing digital assets and performing blockchain transactions. Today, reown provides the WalletKit SDK to enable simple integrations for wallets to the Network.
## Certified Wallets
The [WalletConnect Certified](https://walletconnect.com/blog/walletguide-and-walletconnect-certified-the-future-of-digital-wallets) program offers additional incentives for wallets that meet high standards of UX and integration, further encouraging wallets to stay up-to-date with the latest network features and best practices.
## Wallet Rewards
Wallets participating in the WalletConnect network can earn rewards through staking and performance incentives. To qualify, wallets must stake WCT tokens, which enables them to participate in the Network's governance and earn staking rewards.
For a closer look at the WalletConnect network's wallet rewards system, refer to the [Wallet Rewards](/token-dynamics/wallet-rewards) documentation.
# FAQ
Source: https://docs.walletconnect.network/wct-staking/faq
## Frequently Asked Questions
### How does staking work?
When you stake WCT, you lock tokens in the protocol to earn rewards and voting power.
Your position remains fully locked and at **constant stakeweight** until you choose to **initiate an unlock**.
Once unlocking begins, your stakeweight **decays linearly** over the selected duration until your tokens become withdrawable.
***
### How do I unstake or exit?
You can exit anytime by **initiating an unlocking period**.
Select one of the available durations (e.g., 4, 26, or 52 weeks).
During this time, stakeweight and rewards gradually decrease.
When the period ends, your tokens become **fully withdrawable**.
***
### Which unlock durations are available?
You can choose from predefined options:
**4, 8, 12, 26, 52, 78, or 104 weeks** (≈ 1–24 months).
Shorter unlocks provide flexibility; longer ones grant higher rewards.
***
### How are rewards calculated and distributed?
Rewards are distributed **weekly** (Thursday–Thursday) based on your **share of total stakeweight**:
$$
\text{Reward Share} = \frac{\text{Position Stakeweight}}{\text{Total Network Stakeweight}}
$$
Positions created or updated after Thursday 00:00 GMT become eligible in the **next** reward cycle.
***
### How can I increase my rewards?
You can earn more by:
* Staking **more WCT**
* Choosing a **longer unlock duration**
* **Re-staking** your weekly rewards to compound your position
* Keeping your position locked (not unlocking) to maintain full stakeweight
***
### What happens if I don’t claim rewards immediately?
Unclaimed rewards **accumulate automatically** and can be claimed anytime.
There’s **no expiry** or penalty for delayed claiming.
***
### Can I partially unstake my position?
No. Partial unstaking is **not supported** — you must withdraw the full position once it’s fully unlocked.
***
### Can I change or extend my staking position?
Yes. While locked, you can:
* **Add more WCT** to your existing position
* **Change your unlock preset** to a longer duration
Both actions update your stakeweight accordingly.
Changes made mid-week take effect in the **next reward period**.
***
### Are there any fees?
Only regular **gas fees** apply for on-chain transactions such as staking, updating, claiming, or unstaking.
The cost depends on current network conditions.
***
### Can I have multiple staking positions?
Each address can hold **one active position**.
If you want multiple positions with different durations, use multiple wallet accounts or addresses.
***
### Can locked tokens participate in staking?
Yes. Certain locked allocations (e.g., team or contributor tokens) can be staked and earn rewards even while non-transferable, under their original vesting terms.
This applies only to long-term allocation contracts.
***
### What happens after my unlock finishes?
When the unlock period ends:
* Your stakeweight and voting power drop to **0**
* You can **withdraw** your full amount
* To continue earning, simply **stake again**
# WCT Staking
Source: https://docs.walletconnect.network/wct-staking/overview
Staking WCT is the primary mechanism through which token holders engage with and support the network by locking their tokens in the protocol's smart contracts. This alignment of interests creates a more secure and participatory ecosystem.
Staking WCT provides three key benefits: voting rights in protocol governance, eligibility for performance rewards programs, and weekly WCT token rewards distributions. These mechanisms allow token holders to participate actively in the protocol while earning additional rewards.
The staking model now uses perpetual positions with user-triggered unlocking. You select an unlock duration up front (from a discrete set of options), but your position stays fully locked and at full stakeweight until you decide to initiate unlock. When you initiate unlock, stakeweight decays linearly over the chosen duration.
## Stake Weight
Stakeweight is the measure used to determine a staker's position within the network at any given time. It is derived from two factors: the amount of WCT staked and the remaining lock time of the position. Rewards are distributed proportionally to each staker's share of total network stakeweight, and governance voting power is directly proportional to stakeweight.
### States
* **Locked (Perpetual):** Your position is active and not unlocking. While locked, the remaining lock time is fixed at your selected unlock duration, so stakeweight does not decay.
* **Unlocking:** You've initiated an unstake. Remaining lock time decreases linearly to zero over the selected unlock duration; stakeweight decays accordingly. When it reaches zero, the position becomes fully withdrawable.
### Calculating Stake Weight
The stakeweight calculation is:
$$
\text{Stakeweight} = \frac{\text{Amount of WCT} \times \text{Remaining Lock Time}}{209}
$$
Where:
* **Amount of WCT:** Number of WCT tokens staked.
* **Remaining Lock Time:**
* **Locked state:** fixed at the **selected unlock duration** (e.g., 52 weeks).
* **Unlocking state:** decays linearly from the selected duration down to 0.
* **209:** Normalization constant for maximum stakeweight.
For example, if a user stakes **1,000 WCT** and has selected a **40-week** unlock duration:
* **Locked:**
$$
\text{Stakeweight} = \frac{1000 \times 40}{209} \approx 191.39
$$
* **Unlocking (halfway through, 20 weeks remaining):**
$$
\text{Stakeweight} = \frac{1000 \times 20}{209} \approx 95.69
$$
While the stakeweight formula uses 209 weeks as the denominator, the current maximum unlock duration you can select is 104 weeks (≈ 2 years).
Due to timestamp rounding, 104 weeks is used to represent 2 years.
### Stakeweight Decay (only during Unlocking)
In the **Locked** state, stakeweight remains constant (no decay).
In the **Unstaking** state, **remaining lock time**—and thus stakeweight—decays linearly week by week until it reaches 0 at the end of the unlock duration.
## How to Stake WCT
To stake WCT, visit [**https://app.walletconnect.com/stake**](https://app.walletconnect.com/stake)
The staking flow involves:
* Connect your wallet.
* Select **Stake**.
* Enter the **amount** you want to stake.
* Set the **duration** (this sets the **unstaking period** used when you later exit. The longer the duration, the higher the APY).
* **Approve** the amount — sign the approval with your wallet.
* **Stake** — sign the staking transaction with your wallet.
Your position is perpetually locked until you initiate unstaking. Choosing the unlock duration does not exit you; it sets the duration for a future exit.
## Discrete Unlock Duration Options
To simplify decisions, the unlock duration must be one of:
* **4, 8, 12, 26, 52, 78, or 104 weeks** (≈ 1–24 months)
You can change your preset **while Locked**. The preset determines the **remaining lock time** used for stakeweight in the Locked state and the **length of the decay** once you initiate unstaking.
## Staking Rewards Eligibility
Rewards are distributed **weekly**. Each reward period **starts and ends on Thursday (00:00 GMT)**.
To be eligible for a given week:
* Your position must **exist before Thursday 00:00 GMT** of that week; and
* Your position must have **Remaining Lock Time > 0**:
* **Locked:** always eligible.
* **Unlocking:** eligible while **≥ 1 week** remains (once it hits 0, eligibility ends).
### Examples
#### Eligible (Locked)
Created on **Wednesday 23:00 GMT**, preset **4 weeks**, not unlocking yet → Eligible for the week starting Thursday 00:00 GMT (position existed before the cutoff and is Locked).
#### Eligible (Unlocking with ≥ 1 week)
Initiate unlock on **Monday** with preset **12 weeks** → For subsequent Thursdays while ≥ 1 week remains, the position is eligible (with a decaying stakeweight).
#### Ineligible (Too late)
Create a new position at **Thursday 01:00 GMT** → Not eligible for that week (created after the cutoff).
## Position Lifecycle
### Initiating Unstake (Exit)
When you're ready to exit:
1. Go to [**https://app.walletconnect.com/stake**](https://app.walletconnect.com/stake)
2. Connect your wallet
3. Click **Unstake**
4. After the unstaking duration ends, you will be able to withdraw your locked tokens.
From that point:
* Remaining lock time **decays linearly** from the unstaking duration selected when the position was created to **0**.
* Stakeweight decays accordingly.
* When it reaches **0**, the position becomes **fully withdrawable**.
### Re-Locking (Stop Decay)
* While **Unstaking**, you can **Update** your position to return to the **Locked** state.
* When updating your position, you must select a preset that is **≥ the current remaining time** (you can make it **longer**, but not shorter).
* Decay stops immediately; stakeweight snaps back to the fixed value using the new preset.
### Completed Unstake
When remaining lock time reaches **0**:
* Stakeweight and voting power become **0**.
* The position becomes **withdrawable** (full amount; partial withdrawals are not supported).
* After withdrawing, you may create a **new** staking position at any time.
## Updating Your Position
Users can update **active staking** positions at any time:
### Adding WCT
Increase your position by depositing more WCT. The added tokens adopt your position's current preset.
### Changing the Unlock Duration Preset
While **Staked**, you can change your duration to any of the discrete options **greater than or equal to** your current remaining time. This **does not** initiate unlocking; it only changes the **fixed remaining lock time** used for stakeweight in the Locked state and sets the future unlock duration.
## Claiming Rewards
Every Thursday, WCT rewards are distributed to eligible positions based on their proportional share of total network stakeweight. If your position's stakeweight represents 5% of the total, you'll receive approximately 5% of that week's distribution.
$$
\text{Reward Share} = \frac{\text{Position Stakeweight}}{\text{Total Network Stakeweight}}
$$
You can claim rewards on your dashboard at any time. When claiming, you may **re-stake** rewards back into the position to grow stakeweight.
Checking in weekly lets you claim and optionally re-stake rewards and adjust your preset, helping you maintain optimal stakeweight.
## Migration Path (Existing Positions)
To avoid disrupting existing positions at upgrade time:
* Existing positions were treated as **already unlocking** under the new contracts, so they continue to decay and become withdrawable on their original timelines without action required.
* If you prefer the new **perpetual** behavior, select **Update**, choose a duration (you'll be able to select only durations **greater than or equal to** your current remaining lock), then remain **Locked** until you choose to initiate a new unlock.
## Backwards Compatibility / Optionality
Power users can still mimic the old behavior by **initiating unlock immediately after staking**, which starts decay from day one (as before). Otherwise, you enjoy constant stakeweight until you decide to exit.